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