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 mx::error_t errc = ff.read(m_wZModes, m_wZModesPath);
177 if(errc != mx::error_t::noerror)
178 {
179 return log<text_log,-1>("Could not open mode cube file", logPrio::LOG_ERROR);
180 }
181
182 m_zCoeffs.resize(m_wZModes.planes(), 0);
183
184 errc = ff.read(m_wMask, m_wMaskPath);
185 if( errc != mx::error_t::noerror)
186 {
187 return log<text_log,-1>("Could not open mode mask file", logPrio::LOG_ERROR);
188 }
189
190 m_norm = m_wMask.sum();
191
192 createStandardIndiNumber<unsigned>( m_indiP_gain, "gain", 0, 1, 0, "%0.2f");
193 m_indiP_gain["current"] = m_gain;
194 m_indiP_gain["target"] = m_gain;
195
197 {
199 return -1;
200 }
201
202 createStandardIndiNumber<unsigned>( m_indiP_nModes, "nModes", 1, std::numeric_limits<unsigned>::max(), 1, "%u");
203 m_indiP_nModes["current"] = m_nModes;
204
206 {
208 return -1;
209 }
210
211 REG_INDI_NEWPROP(m_indiP_zCoeffs, "zCoeffs", pcf::IndiProperty::Number);
212
213
214 m_elNames.resize(m_zCoeffs.size());
215 for(size_t n=0; n < m_zCoeffs.size(); ++n)
216 {
217 //std::string el = std::to_string(n);
218 m_elNames[n] = mx::ioutils::convertToString<size_t, 2, '0'>(n);
219
220 m_indiP_zCoeffs.add( pcf::IndiElement(m_elNames[n]) );
221 m_indiP_zCoeffs[m_elNames[n]].set(0);
222 }
223
225 {
226 return log<software_error,-1>({__FILE__, __LINE__});
227 }
228
229
230 createStandardIndiRequestSw( m_indiP_zero, "zero", "zero loop");
232 {
234 return -1;
235 }
236
238
239 return 0;
240}
241
242inline
244{
245 if( shmimMonitorT::appLogic() < 0)
246 {
247 return log<software_error,-1>({__FILE__,__LINE__});
248 }
249
250
251 std::unique_lock<std::mutex> lock(m_indiMutex);
252
254 {
256 }
257
258
259 return 0;
260}
261
262inline
264{
266
267
268 return 0;
269}
270
271inline
273{
274 static_cast<void>(dummy); //be unused
275
276 //std::unique_lock<std::mutex> lock(m_indiMutex);
277
279
280 //state(stateCodes::OPERATING);
281
282 return 0;
283}
284
285inline
286int w2tcsOffloader::processImage( void * curr_src,
287 const dev::shmimT & dummy
288 )
289{
290 static_cast<void>(dummy); //be unused (what is this?)
291
292 // Replace this:
293 // project zernikes onto avg image
294 // update INDI properties with coeffs
295
296 for(size_t i=0; i < m_zCoeffs.size(); ++i)
297 {
298 /* update requested nModes and explicitly zero out any
299 modes that shouldn't be offloaded (but might have been
300 previously set)
301 */
302 if(i < m_nModes)
303 {
304 float coeff;
305 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;
307 }
308 else
309 {
311 }
312 }
313
314 m_indiP_zCoeffs.setState (pcf::IndiProperty::Ok);
315 m_indiDriver->sendSetProperty (m_indiP_zCoeffs);
316
317
318 // loop over something like this
319 //z0 = (im * basis.image(0)*mask).sum()/norm;
320
321
322 return 0;
323}
324
325// update this: mode coefficients (maybe they shouldn't be settable. How to handle?)
326
327INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_gain)(const pcf::IndiProperty &ipRecv)
328{
330
331 float target;
332
333 if( indiTargetUpdate( m_indiP_gain, target, ipRecv, true) < 0)
334 {
335 log<software_error>({__FILE__,__LINE__});
336 return -1;
337 }
338
339 m_gain = target;
340
341 updateIfChanged(m_indiP_gain, "current", m_gain);
342 updateIfChanged(m_indiP_gain, "target", m_gain);
343
344 log<text_log>("set gain to " + std::to_string(m_gain), logPrio::LOG_NOTICE);
345
346 return 0;
347}
348
349INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_nModes)(const pcf::IndiProperty &ipRecv)
350{
351 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_nModes, ipRecv);
352
353 unsigned target;
354
355 if( indiTargetUpdate( m_indiP_nModes, target, ipRecv, true) < 0)
356 {
357 log<software_error>({__FILE__,__LINE__});
358 return -1;
359 }
360
361 m_nModes = target;
362
363 updateIfChanged(m_indiP_nModes, "current", m_nModes);
364 updateIfChanged(m_indiP_nModes, "target", m_nModes);
365
366 log<text_log>("set nModes to " + std::to_string(m_nModes), logPrio::LOG_NOTICE);
367
368 return 0;
369}
370
371INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zCoeffs)(const pcf::IndiProperty &ipRecv)
372{
373 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zCoeffs, ipRecv);
374
375
376 for(size_t n=0; n < m_zCoeffs.size(); ++n)
377 {
378 if(ipRecv.find(m_elNames[n]))
379 {
380 realT zcoeff = ipRecv[m_elNames[n]].get<realT>();
381 m_zCoeffs[n] = zcoeff;
382 }
383 }
384 return 0;
385
386
387 return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
388}
389
390INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zero)(const pcf::IndiProperty &ipRecv)
391{
393
394 float target;
395
396 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
397 {
398 m_woofer.setZero();
399 log<text_log>("set zero", logPrio::LOG_NOTICE);
400 }
401 return 0;
402}
403
404} //namespace app
405} //namespace MagAOX
406
407#endif //w2tcsOffloader_hpp
408
The base-class for XWCTk applications.
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:28
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