Instrument Neutral Distributed Interface INDI 2.2.5
lx200_proxisky.h
Go to the documentation of this file.
1/*
2 Proxisky UMi
3
4 Proxisky UMi mounts run OnStep-derived firmware, so the standard protocol is handled
5 entirely by LX200_OnStep. This driver adds the vendor-specific ":P..." extensions that
6 OnStep itself does not implement.
7
8 Copyright (C) 2026 Nico Trost
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
24*/
25
26#pragma once
27
28#include "lx200_OnStep.h"
29
31{
32 public:
34 ~LX200_Proxisky() override = default;
35
36 virtual const char *getDefaultName() override;
37 virtual bool initProperties() override;
38 virtual bool updateProperties() override;
39 virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override;
40 virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override;
41
42 protected:
43 virtual bool ReadScopeStatus() override;
44
45 // Thin wrappers over the comms helpers inherited from LX200_OnStep. The vendor
46 // protocol is line based and '#' terminated, same as OnStep, so no new serial
47 // code is needed here.
48 // requireTerminated rejects a timed-out partial read. On for the numeric parsers, where a
49 // truncated value is indistinguishable from a real one; off elsewhere, purely as
50 // tolerance. Note that off-by-default is not evidence of anything: every one of the ~30
51 // vendor commands measured on a UMi20S 1.0.6 answered with the trailing '#'.
52 // rcOut receives the raw return code from the inherited read helper. Only probeModel()
53 // needs it - that probe gates every other vendor property, so when it fails it has to
54 // say why without the user having to enable debug logging first.
55 // getVendorString() adds the probe-budget check and, while the capability probes are
56 // running, a single retry; readVendorStringOnce() is the read itself. Callers want the
57 // former - the split exists so the retry has something to call twice.
58 bool getVendorString(const char *cmd, char *out, size_t outLen, bool requireTerminated = false,
59 int *rcOut = nullptr);
60 bool readVendorStringOnce(const char *cmd, char *out, size_t outLen, bool requireTerminated,
61 int *rcOut);
62 bool getVendorBool(const char *cmd, bool &value);
63 bool getVendorPair(const char *cmd, double &first, double &second);
64 bool getVendorQuad(const char *cmd, double *out);
65 // Writes a setter and consumes its acknowledgement ("1#" accepted, "0#" refused).
66 // Returns false on a refusal or on no answer at all.
67 bool sendVendorSetter(const char *cmd);
68
69 // Capability probes, run once per connection from updateProperties().
70 void probeModel();
71 void probeRALimits();
72 void probeDecLimits();
73 void probeLed();
74 void probeAcs();
75 void probeSettings();
76 void probeSuperGoto();
77
78 // Runs every capability probe. Called only from updateProperties(), i.e. during connect,
79 // where a second or two of blocking is expected - never from the status loop.
80 void runVendorProbes();
81
82 // True once the probe sequence has spent its wall-clock budget. Every probe checks this
83 // on entry, so a mount that stops answering part way costs one timeout rather than one
84 // per remaining feature. Warns once, then stays quiet.
85 bool probeBudgetExpired();
86
87 bool probePidAxis(INDI::PropertyNumber &np, const char *readCmd, const char *label);
88
89 // Range-checks one candidate element value on the way in, against the bounds declared for
90 // that element in initProperties() rather than against a literal repeated at the call site.
91 // On failure it flags the vector and names the field; the caller only has to stop.
92 bool rangeCheck(INDI::PropertyNumber &np, int index, double value, const char *what,
93 const char *unit = "");
94
95 // Publishes mount-reported values and reports whether they all sit inside the elements'
96 // declared min/max. INDI enforces those bounds inbound (IUUpdateNumber) but not outbound,
97 // so a mount configured through the vendor tool can hand us a value the client cannot
98 // send back. We publish it regardless - hiding what the hardware actually holds would be
99 // worse - and flag the vector so the discrepancy is visible rather than silently trusted.
100 bool setReportedValues(INDI::PropertyNumber &np, const double *values, int count);
101
102 // Writes the four gains for one axis, commits with ":Pnas<axis>r#", then verifies they
103 // were stored. Refused unless the mount is idle, because the commit restarts the axis.
104 bool handlePidWrite(INDI::PropertyNumber &np, const char *axisPrefix, const char *readCmd,
105 const char *label, double values[], char *names[], int n);
106 bool refreshAcsCounters(bool stickyOnFailure = true);
107
108 // Reads a boolean feature and pre-loads its switch. Returns false when the mount does
109 // not answer, which is how every optional feature here is detected.
110 bool probeBoolSwitch(INDI::PropertySwitch &sp, const char *readCmd, const char *label);
111
112 // Write-then-verify for a setting that applies immediately. Sets IPS_OK on a matching
113 // readback, IPS_ALERT otherwise. The setter's own "1#" only says the mount parsed the
114 // command; the readback is what says the value stuck. On a refusal the switch is put back
115 // to what the mount still reports rather than left showing the change that did not happen.
116 bool handleBoolSwitch(INDI::PropertySwitch &sp, const char *setOn, const char *setOff,
117 const char *readCmd, const char *label,
118 ISState *states, char *names[], int n, bool *mirror = nullptr);
119
120 // Same, for a setting that only takes effect after a power cycle. The switch ends IPS_OK
121 // showing the requested state - the write itself is complete, and that is what the state
122 // describes; that the mount is still running the old value is said in the message.
123 bool handlePendingSwitch(INDI::PropertySwitch &sp, const char *setOn, const char *setOff,
124 const char *readCmd, const char *label,
125 ISState *states, char *names[], int n);
126
127 // Publishes whatever the probes found. Safe to call more than once.
130
131 // Re-read after a write, and publish whatever comes back. Called even when a setter was
132 // refused: these pairs are written one command at a time, so only the readback can say
133 // which half of the pair the mount actually took.
134 bool refreshRALimits(double expectedLeft, double expectedRight);
135 bool refreshDecLimits(double expectedCCW, double expectedCW);
136 bool refreshAcsThresholds(double expectedRA, double expectedDec);
137 bool refreshSuperGotoTolerance(double expected);
138
139 private:
140 bool m_HasModel = false; // :Pbvg# answered with a model string
141
142 // Probing happens once, during connect. The parent re-runs updateProperties() on detecting
143 // PEC or pier side, so this guards against a second full pass landing in the status loop.
144 bool m_ProbesDone = false;
145 bool m_PropertiesDefined = false;
146
147 // Monotonic deadline for the probe sequence; 0 until runVendorProbes() sets it.
148 double m_ProbeDeadline = 0;
149 bool m_ProbeBudgetWarned = false;
150
151 bool m_HasRALimits = false;
152 bool m_HasDecLimits = false;
153 bool m_HasLed = false;
154 bool m_HasAcs = false;
155 bool m_HasPowerLoss = false;
156 bool m_HasAsiair = false;
157 bool m_HasSuperHome = false;
158 bool m_HasDecSecHome = false;
159 bool m_HasAutoTrack = false;
160 // Two flags, not one: ":Pndgo#" (the enable switch) and ":Pndgv#" (the tolerance) are
161 // separate reads, and a mount that answers the first but not the second should still get
162 // its enable switch published.
163 bool m_HasSuperGoto = false;
164 bool m_HasSuperGotoTolerance = false;
165 bool m_HasRaPid = false;
166 bool m_HasDecPid = false;
167
168 // ACS collision counters are polled. m_AcsCountersGood is a sticky negative cache in the
169 // style of LX200_OnStep's OSCpuTemp_good: one failure stops us asking again for the rest
170 // of the session, so a mount that does not answer cannot stall every status update.
171 //
172 // m_AcsEnabled is written at probe time and by handleBoolSwitch(), and deliberately not
173 // re-read from the status loop: the firmware never disables anti-collision on its own, not
174 // even after a collision, so the only thing that can change it is a write from this driver.
175 bool m_AcsEnabled = false;
176 bool m_AcsCountersGood = true;
177 int m_AcsPollDivider = 0;
178
179
180 INDI::PropertyText ModelTP {1};
181 INDI::PropertySwitch RALimitEnableSP {2};
182 INDI::PropertyNumber RALimitNP {2};
183 INDI::PropertySwitch DecLimitEnableSP {2};
184 INDI::PropertyNumber DecLimitNP {2};
185 INDI::PropertySwitch LedSP {2};
186 INDI::PropertySwitch AcsEnableSP {2};
187 INDI::PropertyNumber AcsThresholdNP {2};
188 INDI::PropertyNumber AcsCollisionNP {2};
189 INDI::PropertySwitch AcsClearSP {1};
190 INDI::PropertySwitch PowerLossSP {2};
191 INDI::PropertySwitch AsiairSP {2};
192 INDI::PropertySwitch SuperHomeSP {2};
193 INDI::PropertySwitch DecSecHomeSP {2};
194 INDI::PropertySwitch AutoTrackSP {2};
195 INDI::PropertySwitch SuperGotoSP {2};
196 INDI::PropertyNumber SuperGotoToleranceNP {1};
197 INDI::PropertyNumber RaPidNP {4};
198 INDI::PropertyNumber DecPidNP {4};
199};
bool getVendorQuad(const char *cmd, double *out)
bool refreshRALimits(double expectedLeft, double expectedRight)
bool handlePidWrite(INDI::PropertyNumber &np, const char *axisPrefix, const char *readCmd, const char *label, double values[], char *names[], int n)
bool getVendorBool(const char *cmd, bool &value)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
void deleteVendorProperties()
bool readVendorStringOnce(const char *cmd, char *out, size_t outLen, bool requireTerminated, int *rcOut)
bool getVendorString(const char *cmd, char *out, size_t outLen, bool requireTerminated=false, int *rcOut=nullptr)
bool refreshDecLimits(double expectedCCW, double expectedCW)
bool refreshSuperGotoTolerance(double expected)
bool probePidAxis(INDI::PropertyNumber &np, const char *readCmd, const char *label)
virtual bool ReadScopeStatus() override
Read telescope status.
bool probeBoolSwitch(INDI::PropertySwitch &sp, const char *readCmd, const char *label)
bool rangeCheck(INDI::PropertyNumber &np, int index, double value, const char *what, const char *unit="")
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void defineVendorProperties()
virtual bool initProperties() override
Called to initialize basic properties required all the time.
bool sendVendorSetter(const char *cmd)
bool handleBoolSwitch(INDI::PropertySwitch &sp, const char *setOn, const char *setOff, const char *readCmd, const char *label, ISState *states, char *names[], int n, bool *mirror=nullptr)
virtual const char * getDefaultName() override
bool setReportedValues(INDI::PropertyNumber &np, const double *values, int count)
bool getVendorPair(const char *cmd, double &first, double &second)
bool refreshAcsCounters(bool stickyOnFailure=true)
bool handlePendingSwitch(INDI::PropertySwitch &sp, const char *setOn, const char *setOff, const char *readCmd, const char *label, ISState *states, char *names[], int n)
~LX200_Proxisky() override=default
bool refreshAcsThresholds(double expectedRA, double expectedDec)
ISState
Switch state.
Definition indiapi.h:148
uint8_t cmd[4]
Definition pwc-ioctl.h:2