API
 
Loading...
Searching...
No Matches
closedLoopIndi.hpp
Go to the documentation of this file.
1/** \file closedLoopIndi.hpp
2 * \brief The MagAO-X INDI Closed Loop header file
3 *
4 * \ingroup closedLoopIndi_files
5 */
6
7#ifndef closedLoopIndi_hpp
8#define closedLoopIndi_hpp
9
10
11#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
12#include "../../magaox_git_version.h"
13
14/** \defgroup closedLoopIndi
15 * \brief The MagAO-X application to do closed-loop control using INDI properties
16 *
17 * <a href="../handbook/operating/software/apps/closedLoopIndi.html">Application Documentation</a>
18 *
19 * \ingroup apps
20 *
21 */
22
23/** \defgroup closedLoopIndi_files
24 * \ingroup closedLoopIndi
25 */
26
27namespace MagAOX
28{
29namespace app
30{
31
32/// The MagAO-X application to do closed-loop control using INDI properties
33/**
34 * \ingroup closedLoopIndi
35 */
36class closedLoopIndi : public MagAOXApp<true>
37{
38 //Give the test harness access.
39 friend class closedLoopIndi_test;
40
41protected:
42
43 /** \name Configurable Parameters
44 *@{
45 */
46
47 std::string m_inputDevice; ///< The device with the input disturbances and frame counter.
48 std::string m_inputProperty; ///< The property with the input disturbances and frame counter.
49 std::vector<std::string> m_inputElements {"x", "y"}; ///< The elements with the input disturbances. Must be two, defaults are "x" and "y".
50 std::string m_inputCounterElement {"counter"}; ///< The element with the frame counter, a monotonically increasing integer. Default is "counter".
51
52 std::vector<std::string> m_ctrlDevices; ///< Device names of the controller(s). If only one, it's used for both properties. Max two.
53 std::vector<std::string> m_ctrlProperties; ///< Properties of the ctrl device(s) to which to give the commands. Must specify two.
54 std::vector<std::string> m_ctrlCurrents {"current", "current"}; ///< current elements of the properties on which to base the commands. Must specify 0 or 2. Default is 'current'.
55 std::vector<std::string> m_ctrlTargets {"target", "target"}; ///< target elements of the properties to which to send the commands. Must specify 0 or 2. Default is 'target'.
56
57 mx::improc::eigenImage<float> m_references; ///< The reference values of the disturbances
58
59 bool m_operatingOK {false}; ///< If true then it's ok for the ctrl device to be OPERATING when a command is sent
60
61 std::unordered_map<std::string, std::string> m_fsmStates; ///< The FSM states of the control devices.
62
63 std::vector<float> m_currents; ///< The current commands
64
65 mx::improc::eigenImage<float> m_intMat; ///< The interaction matrix. Default is [1 0][0 1].
66
67 std::vector<float> m_defaultGains; ///< The default gains, per-axis
68
69 std::string m_upstreamDevice; ///< The upstream device to monitor to automatically open this loop if it's loop opens
70 std::string m_upstreamProperty {"loop_state"}; ///< The name of the toggle switch to monitor
71
72 ///@}
73
74 int64_t m_counter = -1; ///< The latest value of the loop counter
75 mx::improc::eigenImage<float> m_measurements; ///< The latest value of the measurements
76 float m_delta0 {0};
77 float m_delta1 {0};
78
79 mx::improc::eigenImage<float> m_commands; ///< The latest commands
80
81 float m_ggain {0}; ///< The global gain
82
83 std::vector<float> m_gains; ///< The axis gains
84
85 bool m_loopClosed {false}; ///< Whether or not the loop is closed
86
87public:
88 /// Default c'tor.
90
91 /// D'tor, declared and defined for noexcept.
94
95 virtual void setupConfig();
96
97 /// Implementation of loadConfig logic, separated for testing.
98 /** This is called by loadConfig().
99 */
100 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
101
102 virtual void loadConfig();
103
104 /// Startup function
105 /**
106 *
107 */
108 virtual int appStartup();
109
110 /// Implementation of the FSM for closedLoopIndi.
111 /**
112 * \returns 0 on no critical error
113 * \returns -1 on an error requiring shutdown
114 */
115 virtual int appLogic();
116
117 /// Shutdown the app.
118 /**
119 *
120 */
121 virtual int appShutdown();
122
123 /// Change the loop state
124 int toggleLoop( bool onoff );
125
126 /// Update the loop with a new command
127 int updateLoop();
128
129 /// Send commands to the control devices
130 int sendCommands(std::vector<float> & commands);
131
132 //INDI
133
134 pcf::IndiProperty m_indiP_deltas;
135
136 pcf::IndiProperty m_indiP_reference0;
138
139 pcf::IndiProperty m_indiP_reference1;
141
142 pcf::IndiProperty m_indiP_inputs;
144
145 pcf::IndiProperty m_indiP_ggain;
147
148 pcf::IndiProperty m_indiP_ctrlEnabled;
150
151 pcf::IndiProperty m_indiP_counterReset;
153
154 pcf::IndiProperty m_indiP_ctrl0_fsm; ///< The INDI property for fsm state of axis 0
156
157 pcf::IndiProperty m_indiP_ctrl0; ///< The INDI property used for control of axis 0
159
160 pcf::IndiProperty m_indiP_ctrl1_fsm; ///< The INDI property for fsm state of axis 1
162
163 pcf::IndiProperty m_indiP_ctrl1; ///< The INDI property used for control of axis 1
165
166 pcf::IndiProperty m_indiP_upstream; ///< Property used to report the upstream loop state
168};
169
170closedLoopIndi::closedLoopIndi() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
171{
172 return;
173}
174
176{
177 config.add("input.device", "", "input.device", argType::Required, "input", "device", false, "string", "The device with the input disturbances and frame counter.");
178 config.add("input.property", "", "input.property", argType::Required, "input", "property", false, "string", "The property with the input disturbances and counter.");
179 config.add("input.elements", "", "input.elements", argType::Required, "input", "elements", false, "vector<string>", " The elements with the input disturbances. Must be two, defaults are 'x' and 'y'.");
180 config.add("input.counterElement", "", "input.counterElement", argType::Required, "input", "counterElement", false, "string", "The element with the frame counter, a monotonically increasing integer. Default is 'counter'.");
181
182 config.add("input.references", "", "input.references", argType::Required, "input", "references", false, "vector<float>", "The reference values for the input disturbances.");
183
184 config.add("ctrl.devices", "", "ctrl.devices", argType::Required, "ctrl", "devices", false, "string", "Device names of the controller(s). If only one, it's used for both properties. Max two.");
185 config.add("ctrl.properties", "", "ctrl.properties", argType::Required, "ctrl", "properties", false, "string", "Properties of the ctrl device(s) to which to give the commands. Must specify two.");
186 config.add("ctrl.currents", "", "ctrl.currents", argType::Required, "ctrl", "currents", false, "vector<string>", "current elements of the properties on which to base the commands. Must specify 0 or 2. Default is 'current'");
187 config.add("ctrl.targets", "", "ctrl.targets", argType::Required, "ctrl", "targets", false, "vector<string>", "target elements of the properties to which to send the commands. Must specify 0 or 2. Default is 'target'.");
188 config.add("ctrl.operatingOK", "", "ctrl.operatingOK", argType::Required, "ctrl", "operatingOK", false, "bool", "If true then it's ok for the ctrl device to be OPERATING when a command is sent. Default is false.");
189
190
191 config.add("loop.intMat00", "", "loop.intMat00", argType::Required, "loop", "intMat00", false, "float", "element (0,0) of the interaction matrix. Default is 1.");
192 config.add("loop.intMat01", "", "loop.intMat01", argType::Required, "loop", "intMat01", false, "float", "element (0,1) of the interaction matrix. Default is 0.");
193 config.add("loop.intMat10", "", "loop.intMat10", argType::Required, "loop", "intMat10", false, "float", "element (1,0) of the interaction matrix. Default is 0.");
194 config.add("loop.intMat11", "", "loop.intMat11", argType::Required, "loop", "intMat11", false, "float", "element (1,1) of the interaction matrix. Default is 1.");
195
196 config.add("loop.gain", "", "loop.gain", argType::Required, "loop", "gain", false, "float", "default global loop gain.");
197 config.add("loop.gains", "", "loop.gains", argType::Required, "loop", "gains", false, "vector<float>", "default loop gains. If single number, it is applied to all axes.");
198 config.add("loop.upstream", "", "loop.upstream", argType::Required, "loop", "upstream", false, "string", "Upstream loop device name. This loop will open, and optionally close, with the upstream loop. Default none.");
199 config.add("loop.upstreamProperty", "", "loop.upstreamProperty", argType::Required, "loop", "upstreamProperty", false, "string", "Property of upstream loop device to follow. Must be a toggle. Default is loop_state.");
200
201}
202
203int closedLoopIndi::loadConfigImpl( mx::app::appConfigurator & _config )
204{
205 _config(m_inputDevice, "input.device");
206 _config(m_inputProperty, "input.property");
207 _config(m_inputElements, "input.elements");
208 _config(m_inputCounterElement, "input.counterElement");
209
210 if(m_inputDevice == "")
211 {
212 m_shutdown = 1;
213 return log<software_error, -1>({__FILE__, __LINE__, "no input device specified"});
214 }
215
216 if(m_inputProperty == "")
217 {
218 m_shutdown = 1;
219 return log<software_error, -1>({__FILE__, __LINE__, "no input property specified"});
220 }
221
222 if(m_inputElements.size() != 2)
223 {
224 m_shutdown = 1;
225 return log<software_error, -1>({__FILE__, __LINE__, "must specify only two input.elements"});
226 }
227
228 std::vector<float> refs({0,0});
229 _config(refs, "input.references");
230 if(refs.size() != 2)
231 {
232 m_shutdown = 1;
233 return log<software_error, -1>({__FILE__, __LINE__, "input.references must have 2 elements"});
234 }
235
236 m_references.resize(2,1);
237 m_references(0,0) = refs[0];
238 m_references(1,0) = refs[1];
239
240 _config(m_ctrlDevices, "ctrl.devices");
241 _config(m_ctrlProperties, "ctrl.properties");
242 _config(m_ctrlCurrents, "ctrl.currents");
243 _config(m_ctrlTargets, "ctrl.targets");
244 _config(m_operatingOK, "ctrl.operatingOK");
245
246 if(m_ctrlDevices.size() == 1)
247 {
248 m_ctrlDevices.push_back(m_ctrlDevices[0]);
249 }
250 else if(m_ctrlDevices.size() != 2)
251 {
252 m_shutdown = 1;
253 return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.devices"});
254 }
255
256 if(m_ctrlProperties.size() != 2)
257 {
258 m_shutdown = 1;
259 return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.properties"});
260 }
261
262 if(m_ctrlTargets.size() != 2)
263 {
264 m_shutdown = 1;
265 return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
266 }
267
268 if(m_ctrlCurrents.size() != 2)
269 {
270 m_shutdown = 1;
271 return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.currents"});
272 }
273
274 if(m_ctrlTargets.size() != 2)
275 {
276 m_shutdown = 1;
277 return log<software_error, -1>({__FILE__, __LINE__, "must specify two ctrl.targets"});
278 }
279
280 float im00 = 1;
281 float im01 = 0;
282 float im10 = 0;
283 float im11 = 1;
284
285 _config(im00, "loop.intMat00");
286 _config(im01, "loop.intMat01");
287 _config(im10, "loop.intMat10");
288 _config(im11, "loop.intMat11");
289
290 m_intMat.resize(2, 2);
291 m_intMat(0,0) = im00;
292 m_intMat(0,1) = im01;
293 m_intMat(1,0) = im10;
294 m_intMat(1,1) = im11;
295
296 _config(m_ggain, "loop.gain");
297
298 _config(m_defaultGains, "loop.gains");
299 _config(m_upstreamDevice, "loop.upstream");
300 _config(m_upstreamProperty, "loop.upstreamProperty");
301
302 return 0;
303}
304
306{
307 loadConfigImpl(config);
308}
309
311{
313
314 CREATE_REG_INDI_RO_NUMBER( m_indiP_deltas, "deltas", "Deltas", "Deltas");
315 m_indiP_deltas.add(pcf::IndiElement("delta0"));
316 m_indiP_deltas["delta0"] = 0;
317 m_indiP_deltas.add(pcf::IndiElement("delta1"));
318 m_indiP_deltas["delta1"] = 0;
319
320 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference0, "reference0", -1e15, 1e15, 1, "%g", "reference0", "references");
321 m_indiP_reference0["current"] = m_references(0,0);
322 m_indiP_reference0["target"] = m_references(0,0);
323
324 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_reference1, "reference1", -1e15, 1e15, 1, "%g", "reference1", "references");
325 m_indiP_reference1["current"] = m_references(1,0);
326 m_indiP_reference1["target"] = m_references(1,0);
327
328 if(m_ctrlTargets.size() != m_defaultGains.size())
329 {
330 if(m_defaultGains.size()==1)
331 {
332 m_defaultGains.push_back(m_defaultGains[0]);
333 log<text_log>("Setting loop.gains gains to be same size as ctrl.Targets", logPrio::LOG_NOTICE);
334 }
335 else
336 {
337 return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and loop.gains are not the same size"});
338 }
339 }
340
341 m_gains.resize(m_defaultGains.size());
342 for(size_t n=0; n < m_defaultGains.size(); ++n) m_gains[n] = m_defaultGains[n];
343
344 CREATE_REG_INDI_NEW_NUMBERU( m_indiP_ggain, "loop_gain", 0, 1, 0, "%0.2f", "gain", "loop");
345 m_indiP_ggain["current"] = m_ggain;
346 m_indiP_ggain["target"] = m_ggain;
347
348
350
352
353 m_measurements.resize(2,1);
354 m_measurements.setZero();
355
356 m_currents.resize(m_ctrlDevices.size(), -1e15);
357
359
361
362 if(m_ctrlDevices[1] != m_ctrlDevices[0])
363 {
365 }
366
368
369 m_commands.resize(2, 2);
370 m_commands.setZero();
371
372 //Get the loop state for managing offloading
373 if(m_upstreamDevice != "")
374 {
376 }
377
378 return 0;
379}
380
382{
383 if(m_loopClosed)
384 {
386 }
387 else
388 {
390 }
391
392 return 0;
393}
394
396{
397 return 0;
398}
399
401{
402 if(!m_loopClosed && onoff) //not enabled so change
403 {
404 m_loopClosed = true;
406 updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::On, INDI_OK);
407 return 0;
408 }
409
410 if(m_loopClosed && !onoff)
411 {
412 m_loopClosed = false;
414 updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::Off, INDI_IDLE);
415
416 return 0;
417 }
418
419 return 0;
420}
421
422
423inline
425{
426 bool ready = false;
427
428 //This should only give ready == true if all devices exist and are ready
429 for(size_t n = 0; n < m_ctrlDevices.size(); ++n)
430 {
431 if(m_fsmStates.count(m_ctrlDevices[n]) > 0)
432 {
433 if(m_fsmStates[m_ctrlDevices[n]] == "READY" || (m_operatingOK && m_fsmStates[m_ctrlDevices[n]] == "OPERATING") )
434 {
435 ready = true;
436 }
437 else
438 {
439 ready = false;
440 break;
441 }
442 }
443 else
444 {
445 ready = false;
446 break;
447 }
448 }
449
450 if(ready != true)
451 {
452 return 0;
453 }
454
457
458 m_commands.matrix() = m_intMat.matrix() * (m_measurements - m_references).matrix();
459
460 std::vector<float> commands;
461 commands.resize(m_measurements.rows());
462
463 for(int cc = 0; cc < m_measurements.rows(); ++cc)
464 {
466 }
467
468 //And send commands.
469 int rv;
470 if(m_loopClosed)
471 {
473 }
474 else
475 {
476 rv = 0;
477 }
478
479 updateIfChanged(m_indiP_deltas, std::vector<std::string>({"delta0", "delta1"}), std::vector<float>({m_delta0, m_delta1}));
480 return rv;
481}
482
483inline
484int closedLoopIndi::sendCommands(std::vector<float> & commands)
485{
486 for(size_t n=0; n < m_ctrlDevices.size(); ++n)
487 {
488 pcf::IndiProperty ip(pcf::IndiProperty::Number);
489
490 ip.setDevice(m_ctrlDevices[n]);
491 ip.setName(m_ctrlProperties[n]);
492 ip.add(pcf::IndiElement(m_ctrlTargets[n]));
494
496 }
497
498 return 0;
499}
500
501INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference0)(const pcf::IndiProperty &ipRecv)
502{
503 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_reference0);
504
505 float target;
506
507 if( indiTargetUpdate( m_indiP_reference0, target, ipRecv, true) < 0)
508 {
509 return log<software_error, -1>({__FILE__,__LINE__});
510 }
511
512 m_references(0,0) = target;
513
514 updateIfChanged(m_indiP_reference0, std::vector<std::string>({"current", "target"}), std::vector<float>({m_references(0,0), m_references(0,0)}));
515
516 log<text_log>("set reference0 to " + std::to_string(m_references(0,0)), logPrio::LOG_NOTICE);
517
518 return 0;
519}
520
521INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_reference1)(const pcf::IndiProperty &ipRecv)
522{
523 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_reference1);
524
525 float target;
526
527 if( indiTargetUpdate( m_indiP_reference1, target, ipRecv, true) < 0)
528 {
529 return log<software_error, -1>({__FILE__,__LINE__});
530 }
531
532 m_references(1,0) = target;
533
534 updateIfChanged(m_indiP_reference1, std::vector<std::string>({"current", "target"}), std::vector<float>({m_references(1,0), m_references(1,0)}));
535
536 log<text_log>("set reference1 to " + std::to_string(m_references(1,0)), logPrio::LOG_NOTICE);
537
538 return 0;
539}
540
541INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_inputs)(const pcf::IndiProperty &ipRecv)
542{
543 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_inputs)
544
545 if(!ipRecv.find(m_inputElements[0])) return -1;
546 if(!ipRecv.find(m_inputElements[1])) return -1;
547 if(!ipRecv.find(m_inputCounterElement)) return -1;
548
549 int counter = ipRecv[m_inputCounterElement].get<int>();
550
551 if(counter != m_counter)
552 {
553 m_counter = counter;
554 m_measurements(0,0) = ipRecv[m_inputElements[0]].get<float>();
555 m_measurements(1,0) = ipRecv[m_inputElements[1]].get<float>();
556
557 return updateLoop();
558 }
559
560 return 0;
561}
562
563INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ggain)(const pcf::IndiProperty &ipRecv)
564{
565 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ggain);
566
567 float target;
568
569 if( indiTargetUpdate( m_indiP_ggain, target, ipRecv, true) < 0)
570 {
571 log<software_error>({__FILE__,__LINE__});
572 return -1;
573 }
574
575 m_ggain = target;
576
577 updateIfChanged(m_indiP_ggain, "current", m_ggain);
578 updateIfChanged(m_indiP_ggain, "target", m_ggain);
579
580 log<text_log>("set global gain to " + std::to_string(m_ggain), logPrio::LOG_NOTICE);
581
582 return 0;
583}
584
585INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrlEnabled)(const pcf::IndiProperty &ipRecv)
586{
587 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrlEnabled);
588
589 //switch is toggled to on
590 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
591 {
592 return toggleLoop(true);
593 }
594
595 //switch is toggle to off
596 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
597 {
598 return toggleLoop(false);
599 }
600
601 return 0;
602}
603
604INDI_NEWCALLBACK_DEFN(closedLoopIndi, m_indiP_counterReset)(const pcf::IndiProperty &ipRecv)
605{
606 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_counterReset);
607
608 //switch is toggled to on
609 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
610 {
611 m_counter = -1;
612 }
613
614
615 return 0;
616}
617
618INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0_fsm)(const pcf::IndiProperty &ipRecv)
619{
620 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl0_fsm);
621
622 if(ipRecv.find("state"))
623 {
624 m_fsmStates[ipRecv.getDevice()] = ipRecv["state"].get();
625 }
626
627 return 0;
628}
629
630INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl0)(const pcf::IndiProperty &ipRecv)
631{
632 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl0);
633
634 if(ipRecv.find(m_ctrlCurrents[0]))
635 {
636 m_currents[0] = ipRecv[m_ctrlCurrents[0]].get<float>();
637 }
638
639 return 0;
640}
641
642INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1_fsm)(const pcf::IndiProperty &ipRecv)
643{
644 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl1_fsm);
645
646 if(ipRecv.find("state"))
647 {
648 m_fsmStates[ipRecv.getDevice()] = ipRecv["state"].get();
649 }
650
651 return 0;
652}
653
654INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_ctrl1)(const pcf::IndiProperty &ipRecv)
655{
656 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_ctrl1);
657
658 if(ipRecv.find(m_ctrlCurrents[1]))
659 {
660 m_currents[1] = ipRecv[m_ctrlCurrents[1]].get<float>();
661 }
662
663 return 0;
664}
665
666INDI_SETCALLBACK_DEFN(closedLoopIndi, m_indiP_upstream)(const pcf::IndiProperty &ipRecv)
667{
668 INDI_VALIDATE_CALLBACK_PROPS(ipRecv, m_indiP_upstream);
669
670 if(!ipRecv.find("toggle")) return 0;
671
672 if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
673 {
674 std::cerr << "upstream on\n";
675 return toggleLoop(true);
676 }
677 else if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
678 {
679 std::cerr << "upstream off\n";
680 return toggleLoop(false);
681 }
682
683 return 0;
684}
685
686
687} //namespace app
688} //namespace MagAOX
689
690#endif //closedLoopIndi_hpp
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
The MagAO-X application to do closed-loop control using INDI properties.
int toggleLoop(bool onoff)
Change the loop state.
~closedLoopIndi() noexcept
D'tor, declared and defined for noexcept.
pcf::IndiProperty m_indiP_upstream
Property used to report the upstream loop state
float m_ggain
The global gain.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_upstream)
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl0)
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference1)
pcf::IndiProperty m_indiP_ctrlEnabled
virtual int appShutdown()
Shutdown the app.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_inputs)
std::vector< float > m_gains
The axis gains.
std::string m_inputCounterElement
The element with the frame counter, a monotonically increasing integer. Default is "counter".
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_ctrlEnabled)
pcf::IndiProperty m_indiP_reference1
mx::improc::eigenImage< float > m_commands
The latest commands.
int64_t m_counter
The latest value of the loop counter.
std::string m_inputProperty
The property with the input disturbances and frame counter.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_counterReset)
virtual int appStartup()
Startup function.
bool m_operatingOK
If true then it's ok for the ctrl device to be OPERATING when a command is sent.
bool m_loopClosed
Whether or not the loop is closed.
pcf::IndiProperty m_indiP_ctrl1_fsm
The INDI property for fsm state of axis 1.
pcf::IndiProperty m_indiP_reference0
mx::improc::eigenImage< float > m_measurements
The latest value of the measurements.
std::string m_upstreamProperty
The name of the toggle switch to monitor.
pcf::IndiProperty m_indiP_ctrl1
The INDI property used for control of axis 1.
std::vector< std::string > m_ctrlTargets
target elements of the properties to which to send the commands. Must specify 0 or 2....
std::vector< std::string > m_inputElements
The elements with the input disturbances. Must be two, defaults are "x" and "y".
pcf::IndiProperty m_indiP_counterReset
std::string m_upstreamDevice
The upstream device to monitor to automatically open this loop if it's loop opens.
std::vector< std::string > m_ctrlProperties
Properties of the ctrl device(s) to which to give the commands. Must specify two.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int sendCommands(std::vector< float > &commands)
Send commands to the control devices.
std::vector< float > m_defaultGains
The default gains, per-axis.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl0_fsm)
std::string m_inputDevice
The device with the input disturbances and frame counter.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl1_fsm)
int updateLoop()
Update the loop with a new command.
pcf::IndiProperty m_indiP_ctrl0_fsm
The INDI property for fsm state of axis 0.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_ggain)
mx::improc::eigenImage< float > m_intMat
The interaction matrix. Default is [1 0][0 1].
std::vector< std::string > m_ctrlCurrents
current elements of the properties on which to base the commands. Must specify 0 or 2....
std::vector< float > m_currents
The current commands.
virtual int appLogic()
Implementation of the FSM for closedLoopIndi.
INDI_NEWCALLBACK_DECL(closedLoopIndi, m_indiP_reference0)
mx::improc::eigenImage< float > m_references
The reference values of the disturbances.
std::unordered_map< std::string, std::string > m_fsmStates
The FSM states of the control devices.
pcf::IndiProperty m_indiP_ctrl0
The INDI property used for control of axis 0.
std::vector< std::string > m_ctrlDevices
Device names of the controller(s). If only one, it's used for both properties. Max two.
INDI_SETCALLBACK_DECL(closedLoopIndi, m_indiP_ctrl1)
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define CREATE_REG_INDI_NEW_NUMBERU(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as unsigned int, using the standard call...
#define CREATE_REG_INDI_NEW_TOGGLESWITCH(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
#define CREATE_REG_INDI_NEW_REQUESTSWITCH(prop, name)
Create and register a NEW INDI property as a standard request switch, using the standard callback nam...
#define CREATE_REG_INDI_NEW_NUMBERF(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
#define CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
@ READY
The device is ready for operation, but is not operating.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:28
#define INDI_OK
Definition indiUtils.hpp:29
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
Definition dm.hpp:24
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
Software ERR log entry.