API
 
Loading...
Searching...
No Matches
w2tcsOffloader.hpp
Go to the documentation of this file.
1/** \file w2tcsOffloader.hpp
2 * \brief The MagAO-X Woofer To Telescope Control System (TCS) offloading manager
3 *
4 * \ingroup app_files
5 */
6
7#ifndef w2tcsOffloader_hpp
8#define w2tcsOffloader_hpp
9
10#include <limits>
11
12#include <mx/improc/eigenCube.hpp>
13#include <mx/improc/eigenImage.hpp>
14
15#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
16#include "../../magaox_git_version.h"
17
18namespace MagAOX
19{
20namespace app
21{
22
23/** \defgroup w2tcsOffloader Woofer to TCS Offloading
24 * \brief Monitors the averaged woofer shape, fits Zernikes, and sends it to INDI.
25 *
26 * <a href="../handbook/operating/software/apps/w2tcsOffloader.html">Application Documentation</a>
27 *
28 * \ingroup apps
29 *
30 */
31
32/** \defgroup w2tcsOffloader_files Woofer to TCS Offloading
33 * \ingroup w2tcsOffloader
34 */
35
36/** MagAO-X application to control offloading the woofer to the TCS.
37 *
38 * \ingroup w2tcsOffloader
39 *
40 */
41class w2tcsOffloader : public MagAOXApp<true>, public dev::shmimMonitor<w2tcsOffloader>
42{
43
44 //Give the test harness access.
45 friend class w2tcsOffloader_test;
46
48
49 //The base shmimMonitor type
51
52 ///Floating point type in which to do all calculations.
53 typedef float realT;
54
55protected:
56
57 /** \name Configurable Parameters
58 *@{
59 */
60
61 std::string m_wZModesPath;
62 std::string m_wMaskPath;
63 std::vector<std::string> m_elNames;
64 std::vector<realT> m_zCoeffs;
65 float m_gain {0.1};
66 int m_nModes {2};
67 float m_norm {1.0};
68
69 ///@}
70
71 mx::improc::eigenCube<realT> m_wZModes;
72 mx::improc::eigenImage<realT> m_woofer;
73 mx::improc::eigenImage<realT> m_wMask;
74
75public:
76 /// Default c'tor.
78
79 /// D'tor, declared and defined for noexcept.
82
83 virtual void setupConfig();
84
85 /// Implementation of loadConfig logic, separated for testing.
86 /** This is called by loadConfig().
87 */
88 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
89
90 virtual void loadConfig();
91
92 /// Startup function
93 /**
94 *
95 */
96 virtual int appStartup();
97
98 /// Implementation of the FSM for w2tcsOffloader.
99 /**
100 * \returns 0 on no critical error
101 * \returns -1 on an error requiring shutdown
102 */
103 virtual int appLogic();
104
105 /// Shutdown the app.
106 /**
107 *
108 */
109 virtual int appShutdown();
110
111
112
113
114 int allocate( const dev::shmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
115
116 int processImage( void * curr_src, ///< [in] pointer to start of current frame.
117 const dev::shmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
118 );
119
120
121protected:
122
123 pcf::IndiProperty m_indiP_gain;
124 pcf::IndiProperty m_indiP_nModes;
125 pcf::IndiProperty m_indiP_zCoeffs;
126
127 pcf::IndiProperty m_indiP_zero;
128
133};
134
135inline
136w2tcsOffloader::w2tcsOffloader() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
137{
138 return;
139}
140
141inline
143{
145
146 config.add("offload.wZModesPath", "", "offload.wZModesPath", argType::Required, "offload", "wZModesPath", false, "string", "The path to the woofer Zernike modes.");
147 config.add("offload.wMaskPath", "", "offload.wMaskPath", argType::Required, "offload", "wMaskPath", false, "string", "Path to the woofer Zernike mode mask.");
148 config.add("offload.gain", "", "offload.gain", argType::Required, "offload", "gain", false, "float", "The starting offload gain. Default is 0.1.");
149 config.add("offload.nModes", "", "offload.nModes", argType::Required, "offload", "nModes", false, "int", "Number of modes to offload to the TCS.");
150}
151
152inline
153int w2tcsOffloader::loadConfigImpl( mx::app::appConfigurator & _config )
154{
155
157
158 _config(m_wZModesPath, "offload.wZModesPath");
159 _config(m_wMaskPath, "offload.wMaskPath");
160 _config(m_gain, "offload.gain");
161 _config(m_nModes, "offload.nModes");
162
163 return 0;
164}
165
166inline
168{
169 loadConfigImpl(config);
170}
171
172inline
174
175 mx::fits::fitsFile<float> ff;
176 if(ff.read(m_wZModes, m_wZModesPath) < 0)
177 {
178 return log<text_log,-1>("Could not open mode cube file", logPrio::LOG_ERROR);
179 }
180
181 m_zCoeffs.resize(m_wZModes.planes(), 0);
182
183 if(ff.read(m_wMask, m_wMaskPath) < 0)
184 {
185 return log<text_log,-1>("Could not open mode mask file", logPrio::LOG_ERROR);
186 }
187
188 m_norm = m_wMask.sum();
189
190 createStandardIndiNumber<unsigned>( m_indiP_gain, "gain", 0, 1, 0, "%0.2f");
191 m_indiP_gain["current"] = m_gain;
192 m_indiP_gain["target"] = m_gain;
193
195 {
197 return -1;
198 }
199
200 createStandardIndiNumber<unsigned>( m_indiP_nModes, "nModes", 1, std::numeric_limits<unsigned>::max(), 1, "%u");
201 m_indiP_nModes["current"] = m_nModes;
202
204 {
206 return -1;
207 }
208
209 REG_INDI_NEWPROP(m_indiP_zCoeffs, "zCoeffs", pcf::IndiProperty::Number);
210
211
212 m_elNames.resize(m_zCoeffs.size());
213 for(size_t n=0; n < m_zCoeffs.size(); ++n)
214 {
215 //std::string el = std::to_string(n);
216 m_elNames[n] = mx::ioutils::convertToString<size_t, 2, '0'>(n);
217
218 m_indiP_zCoeffs.add( pcf::IndiElement(m_elNames[n]) );
219 m_indiP_zCoeffs[m_elNames[n]].set(0);
220 }
221
223 {
224 return log<software_error,-1>({__FILE__, __LINE__});
225 }
226
227
228 createStandardIndiRequestSw( m_indiP_zero, "zero", "zero loop");
230 {
232 return -1;
233 }
234
236
237 return 0;
238}
239
240inline
242{
243 if( shmimMonitorT::appLogic() < 0)
244 {
245 return log<software_error,-1>({__FILE__,__LINE__});
246 }
247
248
249 std::unique_lock<std::mutex> lock(m_indiMutex);
250
252 {
254 }
255
256
257 return 0;
258}
259
260inline
262{
264
265
266 return 0;
267}
268
269inline
271{
272 static_cast<void>(dummy); //be unused
273
274 //std::unique_lock<std::mutex> lock(m_indiMutex);
275
277
278 //state(stateCodes::OPERATING);
279
280 return 0;
281}
282
283inline
284int w2tcsOffloader::processImage( void * curr_src,
285 const dev::shmimT & dummy
286 )
287{
288 static_cast<void>(dummy); //be unused (what is this?)
289
290 // Replace this:
291 // project zernikes onto avg image
292 // update INDI properties with coeffs
293
294 for(size_t i=0; i < m_zCoeffs.size(); ++i)
295 {
296 /* update requested nModes and explicitly zero out any
297 modes that shouldn't be offloaded (but might have been
298 previously set)
299 */
300 if(i < m_nModes)
301 {
302 float coeff;
303 coeff = ( Eigen::Map<mx::improc::eigenImage<realT>>((float *)curr_src, shmimMonitorT::m_width, shmimMonitorT::m_height) * m_wZModes.image(i) * m_wMask).sum() / m_norm;
305 }
306 else
307 {
309 }
310 }
311
312 m_indiP_zCoeffs.setState (pcf::IndiProperty::Ok);
313 m_indiDriver->sendSetProperty (m_indiP_zCoeffs);
314
315
316 // loop over something like this
317 //z0 = (im * basis.image(0)*mask).sum()/norm;
318
319
320 return 0;
321}
322
323// update this: mode coefficients (maybe they shouldn't be settable. How to handle?)
324
325INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_gain)(const pcf::IndiProperty &ipRecv)
326{
328
329 float target;
330
331 if( indiTargetUpdate( m_indiP_gain, target, ipRecv, true) < 0)
332 {
333 log<software_error>({__FILE__,__LINE__});
334 return -1;
335 }
336
337 m_gain = target;
338
339 updateIfChanged(m_indiP_gain, "current", m_gain);
340 updateIfChanged(m_indiP_gain, "target", m_gain);
341
342 log<text_log>("set gain to " + std::to_string(m_gain), logPrio::LOG_NOTICE);
343
344 return 0;
345}
346
347INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_nModes)(const pcf::IndiProperty &ipRecv)
348{
349 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_nModes, ipRecv);
350
351 unsigned target;
352
353 if( indiTargetUpdate( m_indiP_nModes, target, ipRecv, true) < 0)
354 {
355 log<software_error>({__FILE__,__LINE__});
356 return -1;
357 }
358
359 m_nModes = target;
360
361 updateIfChanged(m_indiP_nModes, "current", m_nModes);
362 updateIfChanged(m_indiP_nModes, "target", m_nModes);
363
364 log<text_log>("set nModes to " + std::to_string(m_nModes), logPrio::LOG_NOTICE);
365
366 return 0;
367}
368
369INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zCoeffs)(const pcf::IndiProperty &ipRecv)
370{
371 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zCoeffs, ipRecv);
372
373
374 for(size_t n=0; n < m_zCoeffs.size(); ++n)
375 {
376 if(ipRecv.find(m_elNames[n]))
377 {
378 realT zcoeff = ipRecv[m_elNames[n]].get<realT>();
379 m_zCoeffs[n] = zcoeff;
380 }
381 }
382 return 0;
383
384
385 return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
386}
387
388INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zero)(const pcf::IndiProperty &ipRecv)
389{
391
392 float target;
393
394 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
395 {
396 m_woofer.setZero();
397 log<text_log>("set zero", logPrio::LOG_NOTICE);
398 }
399 return 0;
400}
401
402} //namespace app
403} //namespace MagAOX
404
405#endif //w2tcsOffloader_hpp
406
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
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.
std::mutex m_indiMutex
Mutex for locking INDI communications.
uint32_t m_width
The width of the images in the stream.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
int appShutdown()
Shuts down the shmimMonitor thread.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_nModes)
virtual int appLogic()
Implementation of the FSM for w2tcsOffloader.
mx::improc::eigenImage< realT > m_wMask
std::vector< realT > m_zCoeffs
mx::improc::eigenCube< realT > m_wZModes
int allocate(const dev::shmimT &dummy)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
dev::shmimMonitor< w2tcsOffloader > shmimMonitorT
mx::improc::eigenImage< realT > m_woofer
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_gain)
virtual int appStartup()
Startup function.
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_zero)
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_zCoeffs)
~w2tcsOffloader() noexcept
D'tor, declared and defined for noexcept.
float realT
Floating point type in which to do all calculations.
pcf::IndiProperty m_indiP_zCoeffs
std::vector< std::string > m_elNames
virtual int appShutdown()
Shutdown the app.
int processImage(void *curr_src, const dev::shmimT &dummy)
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Software ERR log entry.
A simple text log, a string-type log.
Definition text_log.hpp:24