API
 
Loading...
Searching...
No Matches
dmMode.hpp
Go to the documentation of this file.
1/** \file dmMode.hpp
2 * \brief The MagAO-X DM mode command header file
3 *
4 * \ingroup dmMode_files
5 */
6
7#ifndef dmMode_hpp
8#define dmMode_hpp
9
10#include <mx/improc/eigenCube.hpp>
11#include <mx/ioutils/fits/fitsFile.hpp>
12#include <mx/improc/eigenImage.hpp>
13#include <mx/ioutils/stringUtils.hpp>
14#include <mx/sys/timeUtils.hpp>
15
16#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
17#include "../../magaox_git_version.h"
18
19/** \defgroup dmMode
20 * \brief The DM mode command app, places modes on a DM channel
21 * \todo update md doc
22 * \todo the current_amps/target_amps thing is dumb. Should consider mode00.target, mode00.current maybe.
23 *
24 * <a href="../handbook/operating/software/apps/dmMode.html">Application Documentation</a>
25 *
26 * \ingroup apps
27 *
28 */
29
30/** \defgroup dmMode_files
31 * \ingroup dmMode
32 */
33
34
35namespace MagAOX
36{
37namespace app
38{
39
40/// The MagAO-X DM mode commander
41/**
42 * \ingroup dmMode
43 */
44class dmMode : public MagAOXApp<true>, public dev::telemeter<dmMode>
45{
46
47 typedef float realT;
48
50
51 friend class dev::telemeter<dmMode>;
52 friend class dmMode_test;
53
54protected:
55
56 /** \name Configurable Parameters
57 *@{
58 */
59
60 std::string m_modeCube;
61
62 int m_maxModes {50};
63
64 std::string m_dmName;
65
66 std::string m_dmChannelName;
67
68 ///@}
69
70 mx::improc::eigenCube<realT> m_modes;
71
72 std::vector<realT> m_amps;
73
74 mx::improc::eigenImage<realT> m_shape;
75
77 uint32_t m_width {0}; ///< The width of the image
78 uint32_t m_height {0}; ///< The height of the image.
79
80 uint8_t m_dataType{0}; ///< The ImageStreamIO type code.
81 size_t m_typeSize {0}; ///< The size of the type, in bytes.
82
83 bool m_opened {true};
84 bool m_restart {false};
85
86public:
87 /// Default c'tor.
88 dmMode();
89
90 /// D'tor, declared and defined for noexcept.
93
94 virtual void setupConfig();
95
96 /// Implementation of loadConfig logic, separated for testing.
97 /** This is called by loadConfig().
98 */
99 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
100
101 virtual void loadConfig();
102
103 /// Startup function
104 /**
105 *
106 */
107 virtual int appStartup();
108
109 /// Implementation of the FSM for dmMode.
110 /**
111 * \returns 0 on no critical error
112 * \returns -1 on an error requiring shutdown
113 */
114 virtual int appLogic();
115
116 /// Shutdown the app.
117 /**
118 *
119 */
120 virtual int appShutdown();
121
122
123 int sendCommand();
124
125 //INDI:
126protected:
127 //declare our properties
128 pcf::IndiProperty m_indiP_dm;
129 pcf::IndiProperty m_indiP_currAmps;
130 pcf::IndiProperty m_indiP_tgtAmps;
131
132 std::vector<std::string> m_elNames;
133public:
136
137 /** \name Telemeter Interface
138 *
139 * @{
140 */
141 int checkRecordTimes();
142
143 int recordTelem( const telem_dmmodes * );
144
145 int recordDmModes( bool force = false );
146 ///@}
147
148
149};
150
151dmMode::dmMode() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
152{
153
154 return;
155}
156
158{
159 config.add("dm.modeCube", "", "dm.modeCube", argType::Required, "dm", "modeCube", false, "string", "Full path to the FITS file containing the modes for this DM.");
160 config.add("dm.maxModes", "", "dm.maxModes", argType::Required, "dm", "maxModes", false, "int", "The maximum number of modes to use (truncates the cube). If <=0 all modes in cube are used.");
161 config.add("dm.name", "", "dm.name", argType::Required, "dm", "name", false, "string", "The descriptive name of this dm. Default is the channel name.");
162 config.add("dm.channelName", "", "dm.channelName", argType::Required, "dm", "channelName", false, "string", "The name of the DM channel to write to.");
163 config.add("dm.maxModes", "", "dm.maxModes", argType::Required, "dm", "maxModes", false, "int", "The maximum number of modes to use (truncates the cube).");
164
166}
167
168int dmMode::loadConfigImpl( mx::app::appConfigurator & _config )
169{
170
171 _config(m_modeCube, "dm.modeCube");
172 _config(m_maxModes, "dm.maxModes");
173 _config(m_dmChannelName, "dm.channelName");
174
176 _config(m_dmName, "dm.name");
177
179 {
180 log<text_log>("Error during telemeter config", logPrio::LOG_CRITICAL);
181 m_shutdown = true;
182 }
183
184 return 0;
185}
186
188{
189 loadConfigImpl(config);
190}
191
193{
194 mx::fits::fitsFile<realT> ff;
195
196 mx::error_t errc = ff.read(m_modes, m_modeCube);
197 if(errc != mx::error_t::noerror)
198 {
199 return log<text_log,-1>(std::format("Could not open mode cube file: {} "
200 "({})", mx::errorMessage(errc), mx::errorName(errc)), logPrio::LOG_ERROR);
201 }
202
203 if(m_maxModes > 0 && m_maxModes < m_modes.planes())
204 {
205 mx::improc::eigenCube<realT> modes;
206 //This probably just works as a realloc in eigenCube but I haven't looked.
207 modes.resize(m_modes.rows(), m_modes.cols(), m_maxModes);
208 for(int p =0; p < modes.planes(); ++p) modes.image(p) = m_modes.image(p);
209 m_modes.resize(m_modes.rows(), m_modes.cols(), m_maxModes);
210 for(int p =0; p < modes.planes(); ++p) m_modes.image(p) = modes.image(p);
211 }
212
213
214
215 m_amps.resize(m_modes.planes(), 0);
216 m_shape.resize(m_modes.rows(), m_modes.cols());
217
218 REG_INDI_NEWPROP_NOCB(m_indiP_dm, "dm", pcf::IndiProperty::Text);
219 m_indiP_dm.add(pcf::IndiElement("name"));
220 m_indiP_dm["name"] = m_dmName;
221 m_indiP_dm.add(pcf::IndiElement("channel"));
222 m_indiP_dm["channel"] = m_dmChannelName;
223
224 REG_INDI_NEWPROP(m_indiP_currAmps, "current_amps", pcf::IndiProperty::Number);
225 REG_INDI_NEWPROP(m_indiP_tgtAmps, "target_amps", pcf::IndiProperty::Number);
226
227 m_elNames.resize(m_amps.size());
228
229 for(size_t n=0; n < m_amps.size(); ++n)
230 {
231 //std::string el = std::to_string(n);
232 m_elNames[n] = mx::ioutils::convertToString<size_t, 4, '0'>(n);
233
234 m_indiP_currAmps.add( pcf::IndiElement(m_elNames[n]) );
236
237 m_indiP_tgtAmps.add( pcf::IndiElement(m_elNames[n]) );
238 }
239
240 if(telemeterT::appStartup() < 0)
241 {
242 return log<software_error,-1>({__FILE__,__LINE__});
243 }
244
246
247
248
249 return 0;
250}
251
253{
255 {
256 m_opened = false;
257 m_restart = false; //Set this up front, since we're about to restart.
258
260 {
261 if(m_imageStream.md[0].sem < 10) ///<\todo this is hardcoded in ImageStreamIO.c -- should be a define
262 {
264 }
265 else
266 {
267 m_opened = true;
268 }
269 }
270
271 if(m_opened)
272 {
274 }
275 }
276
278 {
279 m_dataType = m_imageStream.md[0].datatype;
281 m_width = m_imageStream.md[0].size[0];
282 m_height = m_imageStream.md[0].size[1];
283
284
286 {
287 return log<text_log,-1>("Data type of DM channel is not float.", logPrio::LOG_CRITICAL);
288 }
289
290 if(m_typeSize != sizeof(realT))
291 {
292 return log<text_log,-1>("Type-size mismatch, realT is not float.", logPrio::LOG_CRITICAL);
293 }
294
295 if(m_width != m_modes.rows())
296 {
297 return log<text_log,-1>("Size mismatch between DM and modes (rows)", logPrio::LOG_CRITICAL);
298 }
299
300 if(m_height != m_modes.cols())
301 {
302 return log<text_log,-1>("Size mismatch between DM and modes (cols)", logPrio::LOG_CRITICAL);
303 }
304
305 for(size_t n=0; n < m_amps.size(); ++n) m_amps[n] = 0;
306 sendCommand();
307
309 }
310
311 if(state() == stateCodes::READY)
312 {
313 if(telemeterT::appLogic() < 0)
314 {
316 return 0;
317 }
318 }
319
320 return 0;
321}
322
324{
326
327 return 0;
328}
329
331{
332 if(!m_opened)
333 {
334 log<text_log>("not connected to DM channel.", logPrio::LOG_WARNING);
335 return 0;
336 }
337
338 m_shape = m_amps[0]*m_modes.image(0);
339
340 for(size_t n = 1; n<m_amps.size(); ++n)
341 {
342 m_shape += m_amps[n]*m_modes.image(n);
343 }
344
345 if(m_imageStream.md[0].write)
346 {
347 while(m_imageStream.md[0].write) mx::sys::microSleep(10);
348 }
349
350 recordDmModes(true);
351 m_imageStream.md[0].write = 1;
352
354 if(m_imageStream.md[0].size[2] > 0) ///\todo change to naxis?
355 {
356 curr_image = m_imageStream.md[0].cnt1;
357 }
358 else curr_image = 0;
359
360 char* next_dest = (char *) m_imageStream.array.raw + curr_image*m_width*m_height*m_typeSize;
361
363
364 m_imageStream.md[0].cnt0++;
365
366 m_imageStream.md->write=0;
368
369 recordDmModes(true);
370
371 for(size_t n = 0; n<m_amps.size(); ++n)
372 {
374 }
375 m_indiP_currAmps.setState (pcf::IndiProperty::Ok);
376 m_indiDriver->sendSetProperty (m_indiP_currAmps);
377
378 return 0;
379
380}
381
382INDI_NEWCALLBACK_DEFN(dmMode, m_indiP_currAmps)(const pcf::IndiProperty &ipRecv)
383{
384 if (ipRecv.getName() == m_indiP_currAmps.getName())
385 {
386 size_t found = 0;
387 for(size_t n=0; n < m_amps.size(); ++n)
388 {
389 if(ipRecv.find(m_elNames[n]))
390 {
391 realT amp = ipRecv[m_elNames[n]].get<realT>();
392
393 ///\todo add bounds checks here
394
395 m_amps[n] = amp;
396 ++found;
397 }
398 }
399
400 if(found)
401 {
402 return sendCommand();
403 }
404
405 return 0;
406
407 }
408
409 return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
410}
411
412INDI_NEWCALLBACK_DEFN(dmMode, m_indiP_tgtAmps)(const pcf::IndiProperty &ipRecv)
413{
414 if (ipRecv.getName() == m_indiP_tgtAmps.getName())
415 {
416 size_t found = 0;
417 for(size_t n=0; n < m_amps.size(); ++n)
418 {
419 if(ipRecv.find(m_elNames[n]))
420 {
421 realT amp = ipRecv[m_elNames[n]].get<realT>();
422
423 ///\todo add bounds checks here
424
425 m_amps[n] = amp;
426 ++found;
427 }
428 }
429
430 if(found)
431 {
432 return sendCommand();
433 }
434
435 return 0;
436
437 }
438
439 return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
440}
441
446
448{
449 return recordDmModes(true);
450}
451
452int dmMode::recordDmModes( bool force )
453{
454 static std::vector<float> lastamps(m_amps.size(), std::numeric_limits<float>::max());
455
456 bool changed = false;
457 for(size_t p=0; p < m_amps.size(); ++p)
458 {
459 if(m_amps[p] != lastamps[p]) changed = true;
460 }
461
462 if( changed || force )
463 {
464 for(size_t p=0; p < m_amps.size(); ++p)
465 {
466 lastamps[p] = m_amps[p];
467 }
468
470 }
471
472 return 0;
473}
474
475} //namespace app
476} //namespace MagAOX
477
478#endif //dmMode_hpp
The base-class for XWCTk applications.
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.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
The MagAO-X DM mode commander.
Definition dmMode.hpp:45
std::string m_dmChannelName
Definition dmMode.hpp:66
uint32_t m_width
The width of the image.
Definition dmMode.hpp:77
mx::improc::eigenCube< realT > m_modes
Definition dmMode.hpp:70
virtual int appShutdown()
Shutdown the app.
Definition dmMode.hpp:323
int recordDmModes(bool force=false)
Definition dmMode.hpp:452
std::string m_modeCube
Definition dmMode.hpp:60
std::string m_dmName
Definition dmMode.hpp:64
INDI_NEWCALLBACK_DECL(dmMode, m_indiP_currAmps)
pcf::IndiProperty m_indiP_currAmps
Definition dmMode.hpp:129
dev::telemeter< dmMode > telemeterT
Definition dmMode.hpp:49
friend class dmMode_test
Definition dmMode.hpp:52
mx::improc::eigenImage< realT > m_shape
Definition dmMode.hpp:74
size_t m_typeSize
The size of the type, in bytes.
Definition dmMode.hpp:81
pcf::IndiProperty m_indiP_dm
Definition dmMode.hpp:128
std::vector< realT > m_amps
Definition dmMode.hpp:72
~dmMode() noexcept
D'tor, declared and defined for noexcept.
Definition dmMode.hpp:91
uint32_t m_height
The height of the image.
Definition dmMode.hpp:78
INDI_NEWCALLBACK_DECL(dmMode, m_indiP_tgtAmps)
virtual int appLogic()
Implementation of the FSM for dmMode.
Definition dmMode.hpp:252
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition dmMode.hpp:168
uint8_t m_dataType
The ImageStreamIO type code.
Definition dmMode.hpp:80
virtual int appStartup()
Startup function.
Definition dmMode.hpp:192
virtual void setupConfig()
Definition dmMode.hpp:157
virtual void loadConfig()
Definition dmMode.hpp:187
int recordTelem(const telem_dmmodes *)
Definition dmMode.hpp:447
std::vector< std::string > m_elNames
Definition dmMode.hpp:132
dmMode()
Default c'tor.
Definition dmMode.hpp:151
pcf::IndiProperty m_indiP_tgtAmps
Definition dmMode.hpp:130
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
const pcf::IndiProperty & ipRecv
Definition dm.hpp:28
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
A device base class which saves telemetry.
Definition telemeter.hpp:69
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int appLogic()
Perform telemeter application logic.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int appStartup()
Starts the telemetry log thread.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Software ERR log entry.
The type of the input message.
Log entry recording DM Mode Amplitudes.
A simple text log, a string-type log.
Definition text_log.hpp:24