API
 
Loading...
Searching...
No Matches
bmcCtrl.hpp
Go to the documentation of this file.
1/** \file bmcCtrl.hpp
2 * \brief The MagAO-X BMC DM controller header file
3 *
4 * \ingroup bmcCtrl_files
5 */
6
7
8
9/*
10Open questions:
11* Fix actuator mapping issue (don't want a bunch of if statements)
12
13
14Test:
15* bias code
16
17*/
18
19
20// #define _GLIBCXX_USE_CXX11_ABI 0
21
22
23#ifndef bmcCtrl_hpp
24#define bmcCtrl_hpp
25
26#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
27#include "../../magaox_git_version.h"
28
29
30/* BMC SDK C Header */
31#include <BMCApi.h>
32
33
34/** \defgroup bmcCtrl
35 * \brief The MagAO-X application to control a BMC DM
36 *
37 * <a href="..//apps_html/page_module_bmcCtrl.html">Application Documentation</a>
38 *
39 * \ingroup apps
40 *
41 */
42
43/** \defgroup bmcCtrl_files
44 * \ingroup bmcCtrl
45 */
46
47namespace MagAOX
48{
49namespace app
50{
51
52/// The MagAO-X BMC DM Controller
53/**
54 * \ingroup bmcCtrl
55 */
56class bmcCtrl : public MagAOXApp<true>, public dev::dm<bmcCtrl,float>, public dev::shmimMonitor<bmcCtrl>
57{
58
59 //Give the test harness access.
60 friend class bmcCtrl_test;
61
62 friend class dev::dm<bmcCtrl,float>;
63
64 friend class dev::shmimMonitor<bmcCtrl>;
65
66 typedef float realT; ///< This defines the datatype used to signal the DM using the ImageStreamIO library.
67
70
71
72protected:
73
74 /** \name Configurable Parameters
75 *@{
76 */
77
78 std::string m_serialNumber; ///< The BMC serial number used to open the correct DM profile
79
80 long m_satThresh {100000} ;///< Threshold above which to log saturation.
81
82 ///@}
83
84 long m_nsat {0};
85public:
86 /// Default c'tor.
87 bmcCtrl();
88
89 /// D'tor.
91
92 /// Setup the configuration system.
93 virtual void setupConfig();
94
95 /// Implementation of loadConfig logic, separated for testing.
96 /** This is called by loadConfig().
97 */
98 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
99
100 /// Load the configuration
101 virtual void loadConfig();
102
103 /// Startup function
104 /** Sets up INDI, and starts the shmim thread.
105 *
106 */
107 virtual int appStartup();
108
109 /// Implementation of the FSM for bmcCtrl.
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 /// Cleanup after a power off.
123 /**
124 */
125 virtual int onPowerOff();
126
127 /// Maintenace while powered off.
128 /**
129 */
131
132 /** \name DM Base Class Interface
133 *
134 *@{
135 */
136
137 /// Initialize the DM and prepare for operation.
138 /** Application is in state OPERATING upon successful conclusion.
139 *
140 * \returns 0 on success
141 * \returns -1 on error
142 */
143 int initDM();
144
145 /// Zero all commands on the DM
146 /** This does not update the shared memory buffer.
147 *
148 * \returns 0 on success
149 * \returns -1 on error
150 */
151 int zeroDM();
152
153 /// Send a command to the DM
154 /** This is called by the shmim monitoring thread in response to a semaphore trigger.
155 *
156 * \returns 0 on success
157 * \returns -1 on error
158 */
159 int commandDM(void * curr_src);
160
161 /// Release the DM, making it safe to turn off power.
162 /** The application will be state READY at the conclusion of this.
163 *
164 * \returns 0 on success
165 * \returns -1 on error
166 */
167 int releaseDM();
168
169 ///@}
170
171 /** \name BMC Interface
172 *@{
173 */
174
176 double m_act_gain {0}; ///< Actuator gain (microns/volt)
177 double m_volume_factor {0}; ///< the volume factor to convert from displacement to commands
178 uint32_t m_nbAct {0}; ///< The number of actuators
179
180 int * m_actuator_mapping {nullptr}; ///< Array containing the mapping from 2D grid position to linear index in the command vector
181
182 double * m_dminputs {nullptr}; ///< Pre-allocated command vector, used only in commandDM
183
184 DM m_dm = {}; ///< BMC SDK handle for the DM.
185
186 bool m_dmopen {false}; ///< Track whether the DM connection has been opened
187
188public:
189
190 /// Parse the BMC calibration file
191 /** \returns 0 on success
192 * \returns -1 on error
193 */
195
196 /// Read the actuator mapping from a FITS file
197 /**
198 * \todo convert this to use mxlib::fitsFile
199 *
200 * \returns 0 on success
201 * \returns -1 on error
202 */
204
205 ///@}
206
207
208};
209
210bmcCtrl::bmcCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
211{
212 m_powerMgtEnabled = true;
213 return;
214}
215
222
224{
225 config.add("dm.serialNumber", "", "dm.serialNumber", argType::Required, "dm", "serialNumber", false, "string", "The BMC serial number used to find correct DM Profile.");
226 config.add("dm.calibRelDir", "", "dm.calibRelDir", argType::Required, "dm", "calibRelDir", false, "string", "Used to find the default config directory.");
227 config.add("dm.satThresh", "", "dm.satThresh", argType::Required, "dm", "satThresh", false, "string", "Threshold above which to log saturation.");
228
230
231}
232
233int bmcCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
234{
235 config(m_calibRelDir, "dm.calibRelDir");
236 config(m_serialNumber, "dm.serialNumber");
237 config(m_satThresh, "dm.satThresh");
238
239 //m_calibRelDir = "dm/bmc_2k";
240
242
243 return 0;
244}
245
247{
248 loadConfigImpl(config);
249
250}
251
253{
254 if(parse_calibration_file() < 0)
255 {
257 return -1;
258 }
259
260 if(m_act_gain == 0 || m_volume_factor == 0)
261 {
262 log<software_critical>({__FILE__,__LINE__, "calibration not loaded properly"});
263 return -1;
264 }
265
267
269
270 return 0;
271}
272
274{
276
278
279 if(state()==stateCodes::POWEROFF) return 0;
280
282 {
283 sleep(5);
284 std::cerr << "initing DM" << std::endl;
285 return initDM();
286 }
287
288 if(m_nsat > m_satThresh)
289 {
290 log<text_log>("Saturated actuators in last second: " + std::to_string(m_nsat), logPrio::LOG_WARNING);
291 }
292
293 m_nsat = 0;
294
295 return 0;
296}
297
308
313
318
320{
321 if(m_dmopen)
322 {
323 log<text_log>("DM is already initialized. Release first.", logPrio::LOG_ERROR);
324 return -1;
325 }
326
327 std::string ser = mx::ioutils::toUpper(m_serialNumber);
328 BMCRC ret = NO_ERR;
329 ret = BMCOpen(&m_dm, ser.c_str());
330
331 if(ret == NO_ERR) m_dmopen = true; // remember that the DM connection has been opened
332
333 if(ret != NO_ERR)
334 {
335 const char *err;
337 log<text_log>(std::string("DM initialization failed: ") + err, logPrio::LOG_ERROR);
338
339 m_dm = {};
340 return -1;
341 }
342
343 if (!m_dmopen)
344 {
345 log<text_log>("DM initialization failed. Couldn't open DM handle.", logPrio::LOG_ERROR);
346 return -1;
347 }
348
349 log<text_log>("BMC " + m_serialNumber + " initialized", logPrio::LOG_NOTICE);
350
351 // Get number of actuators
352 m_nbAct = m_dm.ActCount;
353
354
355 // Load the DM map
359
360 if(ret != NO_ERR)
361 {
362 const char *err;
364 log<text_log>(std::string("DM initialization failed. Couldn't load map.") + err, logPrio::LOG_ERROR);
365
366 m_dm = {};
367 return -1;
368 }
369
370
372 m_dminputs = (double*) calloc( m_nbAct, sizeof( double ) );
373
374 if(zeroDM() < 0)
375 {
376 log<text_log>("DM initialization failed. Error zeroing DM.", logPrio::LOG_ERROR);
377 return -1;
378 }
379
380 /* get actuator mapping from 2D cacao image to 1D vector for BMC input */
382 m_actuator_mapping = (int *) malloc(m_nbAct * sizeof(int)); /* memory for actuator mapping */
383
384 /* initialize to -1 to allow for handling addressable but ignored actuators */
385 for (uint32_t idx = 0; idx < m_nbAct; ++idx)
386 {
388 }
389
390 if(get_actuator_mapping() < 0)
391 {
392 log<text_log>("DM initialization failed. Failed to get actuator mapping.", logPrio::LOG_ERROR);
393 return -1;
394 }
395
396 if(m_actuator_mapping == nullptr)
397 {
398 log<text_log>("DM initialization failed. null pointer.", logPrio::LOG_ERROR);
399 return -1;
400 }
401
403
404 return 0;
405}
406
408{
409 if(!m_dmopen)
410 {
411 log<text_log>("DM not initialized (NULL pointer)", logPrio::LOG_ERROR);
412 return -1;
413 }
414
415 if(m_nbAct == 0)
416 {
417 log<text_log>("DM not initialized (number of actuators)", logPrio::LOG_ERROR);
418 return -1;
419 }
420
421 double * dminputs = (double*) calloc( m_nbAct, sizeof( double ) );
422
423 /* Send the all 0 command to the DM */
425
426 /* Release memory */
427 free( dminputs );
428
429 if(ret != NO_ERR)
430 {
431 const char *err;
433 log<text_log>(std::string("Error zeroing DM: ") + err, logPrio::LOG_ERROR);
434 return -1;
435 }
436
437 log<text_log>("DM zeroed");
438 return 0;
439}
440
441int bmcCtrl::commandDM(void * curr_src)
442{
443 //This is based on Kyle Van Gorkoms original sendCommand function.
444
445 /*This loop performs the following steps:
446 1) converts from float to double
447 2) convert to volume-normalized displacement
448 3) convert to squared fractional voltage clamped from 0 to 1.
449 */
450
451 #ifdef XWC_DMTIMINGS
452 dmT::m_tact0 = mx::sys::get_curr_time();
453 #endif
454
455 for (uint32_t idx = 0; idx < m_nbAct; ++idx)
456 {
458 if(address == -1)
459 {
460 m_dminputs[idx] = 0.; // addressable but ignored actuators set to 0
461 }
462 else
463 {
465
466 if (m_dminputs[idx] > 1)
467 {
468 m_dminputs[idx] = 1;
469 }
470 else if (m_dminputs[idx] < 0)
471 {
472 m_dminputs[idx] = 0;
473 }
474 else
475 {
477 }
478 }
479 }
480
481 #ifdef XWC_DMTIMINGS
482 dmT::m_tact1 = mx::sys::get_curr_time();
483 #endif
484
485 /* Send the command to the DM */
487
488 #ifdef XWC_DMTIMINGS
489 dmT::m_tact2 = mx::sys::get_curr_time();
490 #endif
491
492 /* Return immediately upon error, logging the error
493 message first and then return the failure code. */
494 if(ret != NO_ERR)
495 {
496 const char *err;
498 log<text_log>(std::string("DM command failed: ") + err, logPrio::LOG_ERROR);
499 return -1;
500 }
501
502 #ifdef XWC_DMTIMINGS
503 dmT::m_tact3 = mx::sys::get_curr_time();
504 #endif
505
506 /* Now update the instantaneous sat map */
507 for (uint32_t idx = 0; idx < m_nbAct; ++idx)
508 {
510
511 if(address == -1)
512 {
513 continue;
514 }
515 else if(m_dminputs[idx] >= 1 || m_dminputs[idx] <= 0)
516 {
517 ++m_nsat;
518 m_instSatMap.data()[address] = 1;
519 }
520 else
521 {
522 m_instSatMap.data()[address] = 0;
523 }
524 }
525
526 #ifdef XWC_DMTIMINGS
527 dmT::m_tact4 = mx::sys::get_curr_time();
528 #endif
529
530 return ret;
531}
532
534{
535 // Safe DM shutdown on interrupt
536
537 if(!m_dmopen)
538 {
539 return 0;
540 }
541
543
544 if(!shutdown())
545 {
546 pthread_kill(m_smThread.native_handle(), SIGUSR1);
547 }
548
549 sleep(1);
550
551 if(zeroDM() < 0)
552 {
553 log<text_log>("DM release failed. Error zeroing DM.", logPrio::LOG_ERROR);
554 return -1;
555 }
556
557 // Zero all actuators (this is probably redundant after zeroing the DM above)
558 BMCRC ret = NO_ERR;
560
561 if(ret != NO_ERR)
562 {
563 const char *err;
565 log<text_log>(std::string("DM reset failed: ") + err, logPrio::LOG_ERROR);
566 return -1;
567 }
568
569 // Close BMC connection
570 ret = BMCClose(&m_dm);
571
572 if(ret == NO_ERR) m_dmopen = false;
573
574 if(ret != NO_ERR)
575 {
576 const char *err;
578 log<text_log>(std::string("DM release failed: ") + err, logPrio::LOG_ERROR);
579 return -1;
580 }
581
582 m_dm = {};
583
584 log<text_log>("BMC " + m_serialNumber + " reset and released", logPrio::LOG_NOTICE);
585
586 return 0;
587}
588
589/* Read in a configuration file with user-calibrated
590values to determine the conversion from physical to
591fractional stroke as well as the volume displaced by
592the influence function. */
594{
595 FILE * fp;
596 char * line = NULL;
597 size_t len = 0;
598 ssize_t read;
599 double * calibvals;
600
601 std::string calibpath = m_calibPath + "/" + "bmc_2k_userconfig.txt";
602
603 // open file
604 fp = fopen(calibpath.c_str(), "r");
605 if (fp == NULL)
606 {
607 log<text_log>("Could not read configuration file at " + calibpath, logPrio::LOG_ERROR);
608 return -1;
609 }
610
611 calibvals = (double*) malloc(2*sizeof(double));
612 int idx = 0;
613 while ((read = getline(&line, &len, fp)) != -1)
614 {
615 // grab first value from each line
616 calibvals[idx] = strtod(line, NULL);
617 idx++;
618 }
619
620 fclose(fp);
621
622 // assign stroke and volume factors
625
627
628 log<text_log>("BMC " + m_serialNumber + ": Using stroke and volume calibration from " + calibpath);
629 std::cerr << m_act_gain << " " << m_volume_factor << "\n";
630 return 0;
631}
632
633int bmcCtrl::get_actuator_mapping() //const char * serial, int nbAct, int * actuator_mapping)
634{
635 /* This function closely follows the CFITSIO imstat
636 example */
637
638 fitsfile *fptr; /* FITS file pointer */
639 int status = 0; /* CFITSIO status value MUST be initialized to zero! */
640
641
642 // get file path to actuator map
643 std::string calibpath = m_calibPath + "/" + "bmc_2k_actuator_mapping.fits";
644
645 if ( !fits_open_image(&fptr, calibpath.c_str(), READONLY, &status) )
646 {
647 int hdutype, naxis;
648 long naxes[2];
649
651 printf("Error: this program only works on images, not tables\n");
652 return(1);
653 }
654
657
658 if (status || naxis != 2) {
659 printf("Error: NAXIS = %d. Only 2-D images are supported.\n", naxis);
660 return(1);
661 }
662
663 int * pix = (int *) malloc(naxes[0] * sizeof(int)); /* memory for 1 row */
664
665 if (pix == NULL) {
666 printf("Memory allocation error\n");
667 return(1);
668 }
669
670 long fpixel[2];
671 //totpix = naxes[0] * naxes[1];
672 fpixel[0] = 1; /* read starting with first pixel in each row */
673
674 /* process image one row at a time; increment row # in each loop */
675 int ij = 0;/* actuator mapping index */
676 for (fpixel[1] = 1; fpixel[1] <= naxes[1]; fpixel[1]++)
677 {
678 /* give starting pixel coordinate and number of pixels to read */
679 if (fits_read_pix(fptr, TINT, fpixel, naxes[0],0, pix,0, &status))
680 break; /* jump out of loop on error */
681
682 // get indices of active actuators in order
683 for (int ii = 0; ii < naxes[0]; ii++) {
684 if (pix[ii] > 0) {
685 m_actuator_mapping[pix[ii] - 1] = ij;
686 }
687 ij++;
688 }
689 }
691
692 free(pix);
693 }
694
695 if (status) {
696 fits_report_error(stderr, status); /* print any error message */
697 }
698
699
700 log<text_log>("BMC " + m_serialNumber + ": Using actuator mapping from " + calibpath);
701 return 0;
702}
703
704} //namespace app
705} //namespace MagAOX
706
707#endif //bmcCtrl_hpp
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
stateCodes::stateCodeT state()
Get the current state code.
int shutdown()
Get the value of the shutdown flag.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
The MagAO-X BMC DM Controller.
Definition bmcCtrl.hpp:57
uint32_t m_nbAct
The number of actuators.
Definition bmcCtrl.hpp:178
double m_act_gain
Actuator gain (microns/volt)
Definition bmcCtrl.hpp:176
bool m_dmopen
Track whether the DM connection has been opened.
Definition bmcCtrl.hpp:186
bmcCtrl()
Default c'tor.
Definition bmcCtrl.hpp:210
virtual int whilePowerOff()
Maintenace while powered off.
Definition bmcCtrl.hpp:314
DM m_dm
BMC SDK handle for the DM.
Definition bmcCtrl.hpp:184
int commandDM(void *curr_src)
Send a command to the DM.
Definition bmcCtrl.hpp:441
dev::shmimMonitor< bmcCtrl > shmimMonitorT
Definition bmcCtrl.hpp:69
int initDM()
Initialize the DM and prepare for operation.
Definition bmcCtrl.hpp:319
virtual void setupConfig()
Setup the configuration system.
Definition bmcCtrl.hpp:223
double * m_dminputs
Pre-allocated command vector, used only in commandDM.
Definition bmcCtrl.hpp:182
std::string m_serialNumber
The BMC serial number used to open the correct DM profile.
Definition bmcCtrl.hpp:78
double m_volume_factor
the volume factor to convert from displacement to commands
Definition bmcCtrl.hpp:177
virtual int appLogic()
Implementation of the FSM for bmcCtrl.
Definition bmcCtrl.hpp:273
long m_satThresh
Threshold above which to log saturation.
Definition bmcCtrl.hpp:80
virtual int appShutdown()
Shutdown the app.
Definition bmcCtrl.hpp:298
virtual void loadConfig()
Load the configuration.
Definition bmcCtrl.hpp:246
friend class bmcCtrl_test
Definition bmcCtrl.hpp:60
int releaseDM()
Release the DM, making it safe to turn off power.
Definition bmcCtrl.hpp:533
int zeroDM()
Zero all commands on the DM.
Definition bmcCtrl.hpp:407
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition bmcCtrl.hpp:233
virtual int onPowerOff()
Cleanup after a power off.
Definition bmcCtrl.hpp:309
float realT
This defines the datatype used to signal the DM using the ImageStreamIO library.
Definition bmcCtrl.hpp:66
int * m_actuator_mapping
Array containing the mapping from 2D grid position to linear index in the command vector.
Definition bmcCtrl.hpp:180
int get_actuator_mapping()
Read the actuator mapping from a FITS file.
Definition bmcCtrl.hpp:633
virtual int appStartup()
Startup function.
Definition bmcCtrl.hpp:252
dev::dm< bmcCtrl, float > dmT
Definition bmcCtrl.hpp:68
int parse_calibration_file()
Parse the BMC calibration file.
Definition bmcCtrl.hpp:593
~bmcCtrl() noexcept
D'tor.
Definition bmcCtrl.hpp:216
std::string m_calibPath
The path to this DM's calibration files.
Definition dm.hpp:76
std::string m_calibRelDir
The directory relative to the calibPath. Set this before calling dm<derivedT,realT>::loadConfig().
Definition dm.hpp:107
int appShutdown()
DM shutdown.
Definition dm.hpp:880
mx::improc::eigenImage< uint8_t > m_instSatMap
The instantaneous saturation map, 0/1, set by the commandDM() function of the derived class.
Definition dm.hpp:313
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Definition dm.hpp:616
int appStartup()
Startup function.
Definition dm.hpp:689
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
Definition dm.hpp:574
int appLogic()
DM application logic.
Definition dm.hpp:823
std::thread m_smThread
A separate thread for the actual monitoring.
@ OPERATING
The device is operating, other than homing.
@ POWEROFF
The device power is off.
@ READY
The device is ready for operation, but is not operating.
@ POWERON
The device power is on.
Definition dm.hpp:24
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
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.