API
baslerCtrl.hpp
Go to the documentation of this file.
1 /** \file baslerCtrl.hpp
2  * \brief The MagAO-X basler camera controller.
3  *
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  * \ingroup baslerCtrl_files
7  */
8 
9 #ifndef baslerCtrl_hpp
10 #define baslerCtrl_hpp
11 
12 
13 
14 #include <pylon/PylonIncludes.h>
15 #include <pylon/PixelData.h>
16 #include <pylon/GrabResultData.h>
17 #include <pylon/usb/BaslerUsbInstantCamera.h>
18 #include <pylon/usb/_BaslerUsbCameraParams.h>
19 #include <GenApi/IFloat.h>
20 
21 typedef Pylon::CBaslerUsbInstantCamera Camera_t;
22 typedef int16_t pixelT;
23 
24 using namespace Basler_UsbCameraParams;
25 
26 using namespace Pylon;
27 
28 //#include <ImageStruct.h>
29 #include <ImageStreamIO/ImageStreamIO.h>
30 
31 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
32 #include "../../magaox_git_version.h"
33 
34 namespace MagAOX
35 {
36 namespace app
37 {
38 
39 /** \defgroup baslerCtrl Basler USB3 Camera
40  * \brief Control of a Basler USB3 Camera
41  *
42  * <a href="../handbook/operating/software/apps/baslerCtrl.html">Application Documentation</a>
43  *
44  * \ingroup apps
45  *
46  */
47 
48 /** \defgroup baslerCtrl_files Basler USB3 Camera Files
49  * \ingroup baslerCtrl
50  */
51 
52 /** MagAO-X application to control a Basler USB3 Camera
53  *
54  * \ingroup baslerCtrl
55  *
56  */
57 class baslerCtrl : public MagAOXApp<>, public dev::stdCamera<baslerCtrl>, public dev::frameGrabber<baslerCtrl>, public dev::telemeter<baslerCtrl>
58 {
59 
60  friend class dev::stdCamera<baslerCtrl>;
61  friend class dev::frameGrabber<baslerCtrl>;
62  friend class dev::telemeter<baslerCtrl>;
63 
64 public:
65  /** \name app::dev Configurations
66  *@{
67  */
68  static constexpr bool c_stdCamera_tempControl = false; ///< app::dev config to tell stdCamera to not expose temperature controls
69 
70  static constexpr bool c_stdCamera_temp = true; ///< app::dev config to tell stdCamera to expose temperature
71 
72  static constexpr bool c_stdCamera_readoutSpeed = false; ///< app::dev config to tell stdCamera not to expose readout speed controls
73 
74  static constexpr bool c_stdCamera_vShiftSpeed = false; ///< app:dev config to tell stdCamera not to expose vertical shift speed control
75 
76  static constexpr bool c_stdCamera_emGain = false; ///< app::dev config to tell stdCamera to not expose EM gain controls
77 
78  static constexpr bool c_stdCamera_exptimeCtrl = true; ///< app::dev config to tell stdCamera to expose exposure time controls
79 
80  static constexpr bool c_stdCamera_fpsCtrl = true; ///< app::dev config to tell stdCamera to expose FPS controls
81 
82  static constexpr bool c_stdCamera_fps = true; ///< app::dev config to tell stdCamera not to expose FPS status (ignored since fpsCtrl=true)
83 
84  static constexpr bool c_stdCamera_synchro = false; ///< app::dev config to tell stdCamera to not expose synchro mode controls
85 
86  static constexpr bool c_stdCamera_usesModes = false; ///< app:dev config to tell stdCamera not to expose mode controls
87 
88  static constexpr bool c_stdCamera_usesROI = true; ///< app:dev config to tell stdCamera to expose ROI controls
89 
90  static constexpr bool c_stdCamera_cropMode = false; ///< app:dev config to tell stdCamera not to expose Crop Mode controls
91 
92  static constexpr bool c_stdCamera_hasShutter = false; ///< app:dev config to tell stdCamera to expose shutter controls
93 
94  static constexpr bool c_stdCamera_usesStateString = false; ///< app::dev confg to tell stdCamera to expose the state string property
95 
96  static constexpr bool c_frameGrabber_flippable = true; ///< app:dev config to tell framegrabber that this camera can be flipped
97 
98  ///@}
99 
100 protected:
101 
102  /** \name configurable parameters
103  *@{
104  */
105  std::string m_serialNumber; ///< The camera's identifying serial number
106 
107  int m_bits {10}; ///< The number of bits used by the camera.
108 
109  ///@}
110 
111  /** \name binning allowed values
112  * @{
113  */
114  std::vector<int> m_binXs; ///< The allowed values of binning in X (horizontal)
115  std::vector<int> m_binYs; ///< The allowed values of binning in Y (vertical)
116 
117  std::vector<int> m_incXs; ///< The allowed increment in X for each X-binning
118 
119  std::vector<int> m_minWs; ///< The minimum value of the width for each X-binning
120  std::vector<int> m_incWs; ///< The minimum value of the width for each X-binning
121  std::vector<int> m_maxWs; ///< The minimum value of the width for each X-binning
122 
123  std::vector<int> m_incYs; ///< The allowed increment in Y for each Y-binning
124 
125  std::vector<int> m_minHs; ///< The minimum value of the height for each Y-binning
126  std::vector<int> m_incHs; ///< The minimum value of the height for each Y-binning
127  std::vector<int> m_maxHs; ///< The minimum value of the height for each Y-binning
128 
129  ///@}
130 
131  CBaslerUsbInstantCamera * m_camera {nullptr}; ///< The library camera handle
132  CGrabResultPtr ptrGrabResult; ///< The result of an attempt to grab an image
133 
134 public:
135 
136  ///Default c'tor
137  baslerCtrl();
138 
139  ///Destructor
140  ~baslerCtrl() noexcept;
141 
142  /// Setup the configuration system (called by MagAOXApp::setup())
143  virtual void setupConfig();
144 
145  /// load the configuration system results (called by MagAOXApp::setup())
146  virtual void loadConfig();
147 
148  /// Startup functions
149  /** Sets up the INDI vars.
150  *
151  */
152  virtual int appStartup();
153 
154  /// Implementation of the FSM for the Siglent SDG
155  virtual int appLogic();
156 
157  /// Do any needed shutdown tasks. Currently nothing in this app.
158  virtual int appShutdown();
159 
160  int connect();
161 
162  int configureAcquisition();
163  int startAcquisition();
164  int acquireAndCheckValid();
165  int loadImageIntoStream(void * dest);
166  int reconfig();
167 
168 protected:
169 
170  /// Get the current detector temperature
171  /**
172  * \returns 0 on success
173  * \returns -1 on an error.
174  */
175  int getTemp();
176 
177  /// Get the current exposure time
178  /**
179  * \returns 0 on success
180  * \returns -1 on an error.
181  */
182  int getExpTime();
183 
184  /// Get the current framerate
185  /**
186  * \returns 0 on success
187  * \returns -1 on an error.
188  */
189  int getFPS();
190 
191  float fps();
192 
193  /** \name stdCamera Interface
194  *
195  * @{
196  */
197 
198  /// Set defaults for a power on state.
199  /**
200  * \returns 0 on success
201  * \returns -1 on error
202  */
203  int powerOnDefaults();
204 
205  /// Set the framerate.
206  /** This uses the acquistion framerate feature. If m_fpsSet is 0, acuisition framerate is disabled
207  * and the resultant framerate is based solely on exposure time and ROI. If non-zero, then the
208  * framerate will be set to m_fpsSet and the camera will maintain this (as long as exposure time
209  * and ROI allow).
210  *
211  * \returns 0 always
212  */
213  int setFPS();
214 
215  /// Set the Exposure Time. [stdCamera interface]
216  /** Sets the frame rate to m_expTimeSet.
217  *
218  * \returns 0 on success
219  * \returns -1 on error
220  */
221  int setExpTime();
222 
223  /// Check the next ROI
224  /** Checks if the target values are valid and adjusts them to the closest valid values if needed.
225  *
226  * \returns 0 always
227  */
228  int checkNextROI();
229 
230  /// Set the next ROI
231  /**
232  * \returns 0 always
233  */
234  int setNextROI();
235 
236  ///@}
237 
238  /** \name Telemeter Interface
239  *
240  * @{
241  */
242 
243  int checkRecordTimes();
244 
245  int recordTelem( const telem_stdcam * );
246 
247  ///@}
248 
249 };
250 
251 inline
252 baslerCtrl::baslerCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
253 {
254  m_powerMgtEnabled = false;
255 
256  return;
257 }
258 
259 inline
260 baslerCtrl::~baslerCtrl() noexcept
261 {
262  return;
263 }
264 
265 inline
266 void baslerCtrl::setupConfig()
267 {
269 
271 
272  config.add("camera.serialNumber", "", "camera.serialNumber", argType::Required, "camera", "serialNumber", false, "int", "The identifying serial number of the camera.");
273  config.add("camera.bits", "", "camera.bits", argType::Required, "camera", "bits", false, "int", "The number of bits used by the camera. Default is 10.");
274 
276 
277 }
278 
279 inline
280 void baslerCtrl::loadConfig()
281 {
283 
284  config(m_serialNumber, "camera.serialNumber");
285  config(m_bits, "camera.bits");
286 
288 
290 }
291 
292 
293 inline
294 int baslerCtrl::appStartup()
295 {
296 
297  //=================================
298  // Do camera configuration here
299 
300  PylonInitialize(); // Initializes pylon runtime before using any pylon methods
301 
302 
304  {
305  return log<software_critical,-1>({__FILE__,__LINE__});
306  }
307 
309  {
310  return log<software_critical,-1>({__FILE__,__LINE__});
311  }
312 
313 
315  {
316  return log<software_error,-1>({__FILE__,__LINE__});
317  }
318 
319  state(stateCodes::NOTCONNECTED);
320 
321  return 0;
322 
323 }
324 
325 inline
326 int baslerCtrl::appLogic()
327 {
328  //and run stdCamera's appLogic
330  {
331  return log<software_error, -1>({__FILE__, __LINE__});
332  }
333 
334  //and run frameGrabber's appLogic to see if the f.g. thread has exited.
336  {
337  return log<software_error, -1>({__FILE__, __LINE__});
338  }
339 
340 
341  if( state() == stateCodes::NOTCONNECTED || state() == stateCodes::NODEVICE || state() == stateCodes::ERROR)
342  {
343  std::unique_lock<std::mutex> lock(m_indiMutex);
344  if(connect() < 0)
345  {
346  log<software_error>({__FILE__, __LINE__});
347  }
348 
349  if(state() != stateCodes::CONNECTED) return 0;
350  }
351 
352  if( state() == stateCodes::CONNECTED )
353  {
354  //Get a lock
355  std::unique_lock<std::mutex> lock(m_indiMutex);
356 
357  state(stateCodes::READY);
358  }
359 
360  if( state() == stateCodes::READY || state() == stateCodes::OPERATING )
361  {
362  //Get a lock if we can
363  std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
364 
365  //but don't wait for it, just go back around.
366  if(!lock.owns_lock()) return 0;
367 
368  if(getTemp() < 0)
369  {
370  if(state() == stateCodes::READY || state() == stateCodes::OPERATING) state(stateCodes::ERROR);
371  return 0;
372  }
373 
374  if(getExpTime() < 0)
375  {
376  if(state() == stateCodes::READY || state() == stateCodes::OPERATING) state(stateCodes::ERROR);
377  return 0;
378  }
379 
380  if(getFPS() < 0)
381  {
382  if(state() == stateCodes::READY || state() == stateCodes::OPERATING) state(stateCodes::ERROR);
383  return 0;
384  }
385 
387  {
388  log<software_error>({__FILE__, __LINE__});
389  state(stateCodes::ERROR);
390  return 0;
391  }
392 
394  {
395  log<software_error>({__FILE__, __LINE__});
396  state(stateCodes::ERROR);
397  return 0;
398  }
399 
401  {
402  log<software_error>({__FILE__, __LINE__});
403  return 0;
404  }
405  }
406 
407  ///\todo Fall through check?
408 
409  return 0;
410 
411 }
412 
413 
414 inline
415 int baslerCtrl::appShutdown()
416 {
418 
420 
421  if(m_camera) m_camera->Close();
422 
423  PylonTerminate();
424 
426 
427  return 0;
428 }
429 
430 
431 inline
432 int baslerCtrl::connect()
433 {
434  CDeviceInfo info;
435  //info.SetDeviceClass(Camera_t::DeviceClass());
436  info.SetSerialNumber(m_serialNumber.c_str());
437 
438  try
439  {
440  if(m_camera)
441  {
442  m_camera->Close();
443  delete m_camera;
444  }
445  m_camera = nullptr;
446 
447  m_camera = new CBaslerUsbInstantCamera( CTlFactory::GetInstance().CreateFirstDevice(info) );
448  }
449  catch(...)
450  {
451  if(m_camera)
452  {
453  m_camera->Close();
454  delete m_camera;
455  }
456  m_camera = nullptr;
457 
458  state(stateCodes::NODEVICE);
459  if(!stateLogged())
460  {
461  log<text_log>("no camera with serial number " + m_serialNumber + " found.");
462  }
463  return 0;
464  }
465 
466 
467  try
468  {
469  if(m_shmimName == "")
470  {
471  m_shmimName = (std::string)m_camera->GetDeviceInfo().GetModelName() + "_" + (std::string)m_camera->GetDeviceInfo().GetSerialNumber(); // Gets m_camera model name and serial number
472  }
473 
474  m_camera->RegisterConfiguration( new CAcquireContinuousConfiguration , RegistrationMode_ReplaceAll, Cleanup_Delete);
475 
476  m_camera->Open(); // Opens camera parameters to grab images and set exposure time
477  }
478  catch(...)
479  {
480  if(m_camera)
481  {
482  m_camera->Close();
483  delete m_camera;
484  }
485  m_camera = nullptr;
486 
487  state(stateCodes::NODEVICE);
488  if(!stateLogged())
489  {
490  log<text_log>("error opening camera " + m_serialNumber + ".");
491  }
492  return -1;
493  }
494 
495  try
496  {
497  m_camera->ExposureAuto.SetValue(ExposureAuto_Off);
498  }
499  catch(...)
500  {
501  if(m_camera)
502  {
503  m_camera->Close();
504  delete m_camera;
505  }
506  m_camera = nullptr;
507 
508  state(stateCodes::NODEVICE);
509  if(!stateLogged())
510  {
511  log<text_log>("failed to set exposure auto off for camera " + m_serialNumber);
512  }
513  return -1;
514  }
515 
516  try
517  {
518  if(m_bits == 8)
519  {
520  m_camera->PixelFormat.SetValue(PixelFormat_Mono8);
521  }
522  else if(m_bits == 10)
523  {
524  m_camera->PixelFormat.SetValue(PixelFormat_Mono10); // Set to 10 bits
525  }
526  else if(m_bits == 12)
527  {
528  m_camera->PixelFormat.SetValue(PixelFormat_Mono12);
529  }
530  else
531  {
532  log<text_log>("unsupported bit depth for camera" + m_serialNumber + "");
533  }
534  }
535  catch(...)
536  {
537  if(m_camera)
538  {
539  m_camera->Close();
540  delete m_camera;
541  }
542  m_camera = nullptr;
543 
544  state(stateCodes::NODEVICE);
545  if(!stateLogged())
546  {
547  log<text_log>("failed to set bit depth for camera" + m_serialNumber + "");
548  }
549  return -1;
550  }
551 
552  state(stateCodes::CONNECTED);
553  if(!stateLogged())
554  {
555  log<text_log>("Found camera of type " + (std::string)m_camera->GetDeviceInfo().GetModelName() + " with serial number " + m_serialNumber + ".");
556  log<text_log>("Using shared memory name " + m_shmimName + ".");
557  }
558 
559  m_camera->BinningHorizontalMode.SetValue(BinningHorizontalMode_Sum);
560  m_camera->BinningVerticalMode.SetValue(BinningVerticalMode_Sum);
561 
562  // -- Here we interrogate the camera to find valid ROI settings -- //
563 
564  // Stop the camera and cycle through settings to get limits for each binning
565  m_camera->StopGrabbing();
566  m_camera->OffsetX.SetValue(0); //ensure that all values are valid
567  m_camera->OffsetY.SetValue(0);
568 
569  int minb = m_camera->BinningHorizontal.GetMin();
570  int incb = m_camera->BinningHorizontal.GetInc();
571  int maxb = m_camera->BinningHorizontal.GetMax();
572 
573  m_binXs.clear();
574  for(int b = minb; b<=maxb; b+=incb) m_binXs.push_back(b);
575 
576  minb = m_camera->BinningVertical.GetMin();
577  incb = m_camera->BinningVertical.GetInc();
578  maxb = m_camera->BinningVertical.GetMax();
579 
580  m_binYs.clear();
581  for(int b = minb; b<=maxb; b+=incb) m_binYs.push_back(b);
582 
583  m_incXs.clear();
584  m_minWs.clear();
585  m_incWs.clear();
586  m_maxWs.clear();
587  for(size_t b=0; b < m_binXs.size(); ++b)
588  {
589  m_camera->BinningHorizontal.SetValue(m_binXs[b]);
590  m_camera->BinningVertical.SetValue(m_binYs[0]);
591 
592  m_incXs.push_back(m_camera->OffsetX.GetInc());
593  m_minWs.push_back(m_camera->Width.GetMin());
594  m_incWs.push_back(m_camera->Width.GetInc());
595  m_maxWs.push_back(m_camera->Width.GetMax());
596 
597  /*//Leave for troubleshooting:
598  std::cerr << "--------------------\nH-binning: " << m_binXs[b] << "\n";
599  std::cerr << "OffsetX: " << 1 << " " << m_camera->OffsetX.GetInc() << " " << m_camera->Width.GetMax() - m_camera->Width.GetMin() << "\n";
600  std::cerr << "Width: " << m_camera->Width.GetMin() << " " << m_camera->Width.GetInc() << " " << m_camera->Width.GetMax() << "\n";
601  std::cerr << "OffsetY: " << 1 << " " << m_camera->OffsetY.GetInc() << " " << m_camera->Height.GetMax() - m_camera->Height.GetMin() << "\n";
602  std::cerr << "Height: " << m_camera->Height.GetMin() << " " << m_camera->Height.GetInc() << " " << m_camera->Height.GetMax() << "\n";
603  */
604  }
605 
606  m_incYs.clear();
607  m_minHs.clear();
608  m_incHs.clear();
609  m_maxHs.clear();
610  for(size_t b=0; b < m_binYs.size(); ++b)
611  {
612  m_camera->BinningHorizontal.SetValue(m_binXs[0]);
613  m_camera->BinningVertical.SetValue(m_binYs[b]);
614 
615  m_incYs.push_back(m_camera->OffsetX.GetInc());
616  m_minHs.push_back(m_camera->Height.GetMin());
617  m_incHs.push_back(m_camera->Height.GetInc());
618  m_maxHs.push_back(m_camera->Height.GetMax());
619 
620  /*//Leave for troubleshooting:
621  std::cerr << "--------------------\nV-binning: " << m_binYs[b] << "\n";
622  std::cerr << "OffsetX: " << 1 << " " << m_camera->OffsetX.GetInc() << " " << m_camera->Width.GetMax() - m_camera->Width.GetMin() << "\n";
623  std::cerr << "Width: " << m_camera->Width.GetMin() << " " << m_camera->Width.GetInc() << " " << m_camera->Width.GetMax() << "\n";
624  std::cerr << "OffsetY: " << 1 << " " << m_camera->OffsetY.GetInc() << " " << m_camera->Height.GetMax() - m_camera->Height.GetMin() << "\n";
625  std::cerr << "Height: " << m_camera->Height.GetMin() << " " << m_camera->Height.GetInc() << " " << m_camera->Height.GetMax() << "\n";
626  */
627  }
628 
629  m_full_w = m_camera->SensorWidth.GetValue();
630  m_full_h = m_camera->SensorHeight.GetValue();
631  m_full_x = 0.5*((float) m_full_w-1.0);
632  m_full_y = 0.5*((float) m_full_h-1.0);
633 
634  if(m_default_w == 0) m_default_w = m_full_w;
635  if(m_default_h == 0) m_default_h = m_full_h;
636  if(m_default_x == 0) m_default_x = m_full_x;
637  if(m_default_y == 0) m_default_y = m_full_y;
638  if(m_default_bin_x == 0) m_binXs[0];
639  if(m_default_bin_y == 0) m_binYs[0];
640 
641  m_nextROI.x = m_default_x;
642  m_nextROI.y = m_default_y;
643  m_nextROI.w = m_default_w;
644  m_nextROI.h = m_default_h;
645  m_nextROI.bin_x = m_default_bin_x;
646  m_nextROI.bin_y = m_default_bin_y;
647 
648  return 0;
649 }
650 
651 
652 int baslerCtrl::configureAcquisition()
653 {
654  if(!m_camera) return -1;
655 
656  try
657  {
658  recordCamera(true);
659  m_camera->StopGrabbing();
660  /*
661  The CenterX/Y has to be set to false otherwise the software tries to auto-center the frames.
662  See: https://docs.baslerweb.com/image-roi
663  */
664  m_camera->CenterX.SetValue(false);
665  m_camera->CenterY.SetValue(false);
666 
667  //set offsets to 0 so any valid w/h will work.
668  m_camera->OffsetX.SetValue(0);
669  m_camera->OffsetY.SetValue(0);
670 
671  if(checkNextROI() < 0)
672  {
673  log<software_error>({__FILE__, __LINE__, "error from checkNextROI()"});
674  return -1;
675  }
676 
677  //Note: assuming checkNextROI has adjusted m_nextROI to valid values, so not doing any checks
678  //First find binning indices
679  size_t bx = 0;
680  for(size_t b =0; b < m_binXs.size(); ++b)
681  {
682  if(m_nextROI.bin_x == m_binXs[b])
683  {
684  bx = b;
685  break;
686  }
687  }
688 
689  size_t by = 0;
690  for(size_t b =0; b < m_binYs.size(); ++b)
691  {
692  if(m_nextROI.bin_y == m_binYs[b])
693  {
694  by = b;
695  break;
696  }
697  }
698 
699  //Set ROI.
700  int xoff;
701  int yoff;
702  if(m_currentFlip == fgFlipLR || m_currentFlip == fgFlipUDLR)
703  {
704  xoff = (m_maxWs[bx] - 1 - m_nextROI.x) - 0.5*((float) m_nextROI.w - 1);
705  }
706  else
707  {
708  xoff = m_nextROI.x - 0.5*((float) m_nextROI.w - 1);
709  }
710 
711  if(m_currentFlip == fgFlipUD || m_currentFlip == fgFlipUDLR)
712  {
713  yoff = (m_maxHs[by] - 1 - m_nextROI.y) - 0.5*((float) m_nextROI.h - 1);
714  }
715  else
716  {
717  yoff = m_nextROI.y - 0.5*((float) m_nextROI.h - 1);
718  }
719 
720  m_camera->BinningHorizontal.SetValue(m_nextROI.bin_x);
721  m_camera->BinningVertical.SetValue(m_nextROI.bin_y);
722  //Probably not necessary to do it every time, but just in case:
723  m_camera->BinningHorizontalMode.SetValue(BinningHorizontalMode_Sum);
724  m_camera->BinningVerticalMode.SetValue(BinningVerticalMode_Sum);
725 
726  m_camera->Width.SetValue(m_nextROI.w);
727  m_camera->Height.SetValue(m_nextROI.h);
728 
729  m_camera->OffsetX.SetValue(xoff);
730  m_camera->OffsetY.SetValue(yoff);
731 
732  // Read the parameter from the camera to check if parameter change is successful
733  m_currentROI.bin_x = m_camera->BinningHorizontal.GetValue();
734  m_currentROI.bin_y = m_camera->BinningVertical.GetValue();
735 
736  bx = 0;
737  for(size_t b =0; b < m_binXs.size(); ++b)
738  {
739  if(m_nextROI.bin_x == m_binXs[b])
740  {
741  bx = b;
742  break;
743  }
744  }
745 
746  by = 0;
747  for(size_t b =0; b < m_binYs.size(); ++b)
748  {
749  if(m_nextROI.bin_y == m_binYs[b])
750  {
751  by = b;
752  break;
753  }
754  }
755 
756  m_currentROI.w = m_camera->Width.GetValue();
757  m_currentROI.h = m_camera->Height.GetValue();
758 
759  if(m_currentFlip == fgFlipLR || m_currentFlip == fgFlipUDLR)
760  {
761  m_currentROI.x = m_maxWs[bx] - 1 - (m_camera->OffsetX.GetValue() + 0.5*((float) m_currentROI.w - 1));
762  }
763  else
764  {
765  m_currentROI.x = m_camera->OffsetX.GetValue() + 0.5*((float) m_currentROI.w - 1);
766  }
767 
768  if(m_currentFlip == fgFlipUD || m_currentFlip == fgFlipUDLR)
769  {
770  m_currentROI.y = m_maxHs[by] - 1 - (m_camera->OffsetY.GetValue() + 0.5*((float) m_currentROI.h - 1));
771  }
772  else
773  {
774  m_currentROI.y = m_camera->OffsetY.GetValue() + 0.5*((float) m_currentROI.h - 1);
775  }
776 
777  //Set the full window for this binning
778  m_full_currbin_w = m_maxWs[bx];
779  m_full_currbin_x = 0.5*((float) m_full_currbin_w - 1.0);
780  m_full_currbin_h = m_maxHs[by];
781  m_full_currbin_y = 0.5*((float) m_full_currbin_h - 1.0);
782 
783  //Update binning
784  updateIfChanged( m_indiP_roi_x, "current", m_currentROI.x, INDI_OK);
785  updateIfChanged( m_indiP_roi_y, "current", m_currentROI.y, INDI_OK);
786  updateIfChanged( m_indiP_roi_w, "current", m_currentROI.w, INDI_OK);
787  updateIfChanged( m_indiP_roi_h, "current", m_currentROI.h, INDI_OK);
788  updateIfChanged( m_indiP_roi_bin_x, "current", m_currentROI.bin_x, INDI_OK);
789  updateIfChanged( m_indiP_roi_bin_y, "current", m_currentROI.bin_y, INDI_OK);
790 
791  //We also update target to the settable values
792  m_nextROI.x = m_currentROI.x;
793  m_nextROI.y = m_currentROI.y;
794  m_nextROI.w = m_currentROI.w;
795  m_nextROI.h = m_currentROI.h;
796  m_nextROI.bin_x = m_currentROI.bin_x;
797  m_nextROI.bin_y = m_currentROI.bin_y;
798 
799  updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_OK);
800  updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_OK);
801  updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_OK);
802  updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_OK);
803  updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_OK);
804  updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_OK);
805 
806  m_width = m_currentROI.w;
807  m_height = m_currentROI.h;
808  m_dataType = _DATATYPE_INT16;
809 
810  getFPS();
811 
812  recordCamera(true);
813  }
814  catch(...)
815  {
816  log<software_error>({__FILE__, __LINE__, "invalid ROI specifications"});
817  state(stateCodes::NOTCONNECTED);
818  return -1;
819  }
820 
821  return 0;
822 }
823 
824 int baslerCtrl::startAcquisition()
825 {
826  try
827  {
828  m_camera->StartGrabbing(GrabStrategy_LatestImageOnly ); // Start grabbing, and always grab just the last image.
829  }
830  catch(...)
831  {
832  state(stateCodes::NOTCONNECTED);
833  return -1;
834  }
835 
836  state(stateCodes::OPERATING);
837 
838  return 0;
839 }
840 
841 int baslerCtrl::acquireAndCheckValid()
842 {
843  try
844  {
845  m_camera->RetrieveResult(1000, ptrGrabResult, TimeoutHandling_ThrowException);
846  }
847  catch(...)
848  {
849  state(stateCodes::NOTCONNECTED);
850  return -1;
851  }
852 
853  if (ptrGrabResult->GrabSucceeded()) // If image is grabbed successfully
854  {
855  clock_gettime(CLOCK_REALTIME, &m_currImageTimestamp);
856  return 0;
857  }
858  else
859  {
860  state(stateCodes::NOTCONNECTED);
861  return -1;
862  }
863 }
864 
865 
866 int baslerCtrl::loadImageIntoStream(void * dest)
867 {
868  pixelT * src = nullptr;
869  try
870  {
871  src = (pixelT *) ptrGrabResult->GetBuffer();
872  }
873  catch(...)
874  {
875  state(stateCodes::NOTCONNECTED);
876  return -1;
877  }
878 
879  if(src == nullptr) return -1;
880 
881  if( frameGrabber<baslerCtrl>::loadImageIntoStreamCopy(dest, src, m_width, m_height, sizeof(pixelT)) == nullptr) return -1;
882 
883  return 0;
884 }
885 
886 int baslerCtrl::reconfig()
887 {
888 
889 
890  return 0;
891 }
892 
893 
894 inline
895 int baslerCtrl::getTemp()
896 {
897  if( m_camera == nullptr) return 0;
898 
899  try
900  {
901  m_ccdTemp = (float)m_camera->DeviceTemperature.GetValue();
902  recordCamera();
903  }
904  catch(...)
905  {
906  m_ccdTemp = -999;
907  recordCamera();
908  state(stateCodes::NOTCONNECTED);
909  return -1;
910  }
911 
912  return 0;
913 
914 }
915 
916 inline
917 int baslerCtrl::getExpTime()
918 {
919  if( m_camera == nullptr) return 0;
920 
921  try
922  {
923  m_expTime = (float)m_camera->ExposureTime.GetValue()/1e6;
924  recordCamera();
925  }
926  catch(...)
927  {
928  m_expTime = -999;
929  recordCamera();
930  state(stateCodes::NOTCONNECTED);
931  return -1;
932  }
933 
934  return 0;
935 
936 }
937 
938 inline
939 int baslerCtrl::getFPS()
940 {
941  if( m_camera == nullptr) return 0;
942 
943  try
944  {
945  m_fps = m_camera->ResultingFrameRate.GetValue();
946  recordCamera();
947  }
948  catch(...)
949  {
950  m_fps = -999;
951  recordCamera();
952  state(stateCodes::NOTCONNECTED);
953  return -1;
954  }
955 
956  return 0;
957 
958 }
959 
960 inline
961 float baslerCtrl::fps()
962 {
963  return m_fps;
964 
965 }
966 
967 inline
968 int baslerCtrl::powerOnDefaults()
969 {
970  m_nextROI.x = m_default_x;
971  m_nextROI.y = m_default_y;
972  m_nextROI.w = m_default_w;
973  m_nextROI.h = m_default_h;
974  m_nextROI.bin_x = m_default_bin_x;
975  m_nextROI.bin_y = m_default_bin_y;
976 
977  return 0;
978 }
979 
980 inline
981 int baslerCtrl::setFPS()
982 {
983  if( m_camera == nullptr) return 0;
984 
985  recordCamera(true);
986 
987  if(m_fpsSet == 0)
988  {
989  try
990  {
991  m_camera->AcquisitionFrameRateEnable.SetValue(false);
992  }
993  catch(...)
994  {
995  return log<software_error,-1>({__FILE__, __LINE__, "Error disabling frame rate limit."});
996  }
997  }
998  else
999  {
1000  try
1001  {
1002  m_camera->AcquisitionFrameRateEnable.SetValue(true);
1003  m_camera->AcquisitionFrameRate.SetValue(m_fpsSet);
1004  }
1005  catch(...)
1006  {
1007  return log<software_error,-1>({__FILE__, __LINE__, "Error setting frame rate limit."});
1008  }
1009  }
1010 
1011  return 0;
1012 }
1013 
1014 inline
1015 int baslerCtrl::setExpTime()
1016 {
1017  if( m_camera == nullptr) return 0;
1018 
1019  try
1020  {
1021  recordCamera(true);
1022  m_camera->ExposureTime.SetValue(m_expTimeSet*1e6);
1023  }
1024  catch(...)
1025  {
1026  log<software_error>({__FILE__, __LINE__, "Error setting exposure time"});
1027  return -1;
1028  }
1029 
1030  log<text_log>( "Set exposure time: " + std::to_string(m_expTimeSet) + " sec");
1031 
1032  return 0;
1033 }
1034 
1035 inline
1036 int baslerCtrl::checkNextROI()
1037 {
1038  std::cerr << "checkNextROI!\n";
1039 
1040  //First find binning indices
1041  size_t bx = 0;
1042  for(size_t b =0; b < m_binXs.size(); ++b)
1043  {
1044  if(m_nextROI.bin_x == m_binXs[b])
1045  {
1046  bx = b;
1047  break;
1048  }
1049  }
1050  std::cerr << "req bin_x: " << m_nextROI.bin_x << " " << "adj bin_x: " << m_binXs[bx] << "\n";
1051  m_nextROI.bin_x = m_binXs[bx]; //In case no valid value was found.
1052 
1053  size_t by = 0;
1054  for(size_t b =0; b < m_binYs.size(); ++b)
1055  {
1056  if(m_nextROI.bin_y == m_binYs[b])
1057  {
1058  by = b;
1059  break;
1060  }
1061  }
1062  std::cerr << "req bin_y: " << m_nextROI.bin_y << " " << "adj bin_y: " << m_binYs[by] << "\n";
1063  m_nextROI.bin_y = m_binYs[by]; //In case no valid value was found.
1064 
1065  //Next check width
1066  //-- round to nearest increment
1067  //-- check limits
1068  int w = m_nextROI.w;
1069  int rw = w % m_incWs[bx];
1070  if(rw < 0.5*m_incWs[bx]) w -= rw;
1071  else w += m_incWs[bx] - rw;
1072 
1073  if(w < m_minWs[bx]) w = m_minWs[bx];
1074  else if(w > m_maxWs[bx]) w = m_maxWs[bx];
1075 
1076  std::cerr << "req w: " << m_nextROI.w << " " << "adj w: " << w << "\n";
1077  m_nextROI.w = w;
1078 
1079  //Now check x
1080  //-- calculate offset from center
1081  //-- round to nearest increment
1082  //-- recalculate center
1083  int x;
1084  if(m_currentFlip == fgFlipLR || m_currentFlip == fgFlipUDLR)
1085  {
1086  x = (m_maxWs[bx] - 1 - m_nextROI.x) - 0.5*((float) w - 1);
1087  }
1088  else
1089  {
1090  x = m_nextROI.x - 0.5*((float) w - 1);
1091  }
1092 
1093  int rx = x % m_incXs[bx];
1094  if(rx < 0.5*m_incXs[bx]) x -= rx;
1095  else x += m_incXs[bx] - rx;
1096 
1097  if(x < 0) x=0;
1098  else if(x > m_maxWs[bx] - w) x = m_maxWs[bx] - w;
1099 
1100  std::cerr << "req x: " << m_nextROI.x;
1101  if(m_currentFlip == fgFlipLR || m_currentFlip == fgFlipUDLR)
1102  {
1103  m_nextROI.x = m_maxWs[bx] - 1 - (x + 0.5*((float) w - 1.0));
1104  }
1105  else
1106  {
1107  m_nextROI.x = x + 0.5*((float) w - 1.0);
1108  }
1109  std::cerr << " adj x: " << m_nextROI.x << "\n";
1110 
1111  //Next check height
1112  //-- round to nearest increment
1113  //-- check limits
1114  int h = m_nextROI.h;
1115  int rh = h % m_incHs[by];
1116  if(rh < 0.5*m_incHs[by]) h -= rh;
1117  else h += m_incHs[by] - rh;
1118 
1119  if(h < m_minHs[by]) h = m_minHs[by];
1120  else if(h > m_maxHs[by]) h = m_maxHs[by];
1121 
1122  std::cerr << "req h: " << m_nextROI.h << " " << "adj h: " << h << "\n";
1123  m_nextROI.h = h;
1124 
1125  //Now check y
1126  //-- calculate offset from center
1127  //-- round to nearest increment
1128  //-- recalculate center
1129  int y;
1130  if(m_currentFlip == fgFlipUD || m_currentFlip == fgFlipUDLR)
1131  {
1132  y = (m_maxHs[by] - 1 - m_nextROI.y) - 0.5*((float) h - 1);
1133  }
1134  else
1135  {
1136  y = m_nextROI.y - 0.5*((float) h - 1);
1137  }
1138 
1139  int ry = y % m_incYs[by];
1140  if(ry < 0.5*m_incYs[by]) y -= ry;
1141  else y += m_incYs[by] - ry;
1142 
1143  if(y < 0) y=0;
1144  else if(y > m_maxHs[by] - h) y = m_maxHs[by] - h;
1145 
1146  std::cerr << "req y: " << m_nextROI.y;
1147  if(m_currentFlip == fgFlipUD || m_currentFlip == fgFlipUDLR)
1148  {
1149  m_nextROI.y = m_maxHs[by] - 1 - (y + 0.5*((float) h - 1));
1150  }
1151  else
1152  {
1153  m_nextROI.y = y + 0.5*((float) h - 1);
1154  }
1155  std::cerr << " adj y: " << m_nextROI.y << "\n";
1156 
1157  updateIfChanged( m_indiP_roi_x, "target", m_nextROI.x, INDI_OK);
1158  updateIfChanged( m_indiP_roi_y, "target", m_nextROI.y, INDI_OK);
1159  updateIfChanged( m_indiP_roi_w, "target", m_nextROI.w, INDI_OK);
1160  updateIfChanged( m_indiP_roi_h, "target", m_nextROI.h, INDI_OK);
1161  updateIfChanged( m_indiP_roi_bin_x, "target", m_nextROI.bin_x, INDI_OK);
1162  updateIfChanged( m_indiP_roi_bin_y, "target", m_nextROI.bin_y, INDI_OK);
1163 
1164  return 0;
1165 }
1166 
1167 inline
1168 int baslerCtrl::setNextROI()
1169 {
1170  std::cerr << "setNextROI:\n";
1171  std::cerr << " m_nextROI.x = " << m_nextROI.x << "\n";
1172  std::cerr << " m_nextROI.y = " << m_nextROI.y << "\n";
1173  std::cerr << " m_nextROI.w = " << m_nextROI.w << "\n";
1174  std::cerr << " m_nextROI.h = " << m_nextROI.h << "\n";
1175  std::cerr << " m_nextROI.bin_x = " << m_nextROI.bin_x << "\n";
1176  std::cerr << " m_nextROI.bin_y = " << m_nextROI.bin_y << "\n";
1177 
1178  m_reconfig = true;
1179 
1180  updateSwitchIfChanged(m_indiP_roi_set, "request", pcf::IndiElement::Off, INDI_IDLE);
1181  updateSwitchIfChanged(m_indiP_roi_full, "request", pcf::IndiElement::Off, INDI_IDLE);
1182  updateSwitchIfChanged(m_indiP_roi_last, "request", pcf::IndiElement::Off, INDI_IDLE);
1183  updateSwitchIfChanged(m_indiP_roi_default, "request", pcf::IndiElement::Off, INDI_IDLE);
1184  return 0;
1185 }
1186 
1187 inline
1188 int baslerCtrl::checkRecordTimes()
1189 {
1191 }
1192 
1193 inline
1194 int baslerCtrl::recordTelem( const telem_stdcam * )
1195 {
1196  return recordCamera(true);
1197 }
1198 
1199 
1200 }//namespace app
1201 } //namespace MagAOX
1202 #endif
Pylon::CBaslerUsbInstantCamera Camera_t
Definition: baslerCtrl.hpp:21
int16_t pixelT
Definition: baslerCtrl.hpp:22
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
std::vector< int > m_minWs
The minimum value of the width for each X-binning.
Definition: baslerCtrl.hpp:119
std::vector< int > m_maxWs
The minimum value of the width for each X-binning.
Definition: baslerCtrl.hpp:121
CGrabResultPtr ptrGrabResult
The result of an attempt to grab an image.
Definition: baslerCtrl.hpp:132
std::vector< int > m_maxHs
The minimum value of the height for each Y-binning.
Definition: baslerCtrl.hpp:127
std::vector< int > m_incYs
The allowed increment in Y for each Y-binning.
Definition: baslerCtrl.hpp:123
std::vector< int > m_binXs
The allowed values of binning in X (horizontal)
Definition: baslerCtrl.hpp:114
std::vector< int > m_incHs
The minimum value of the height for each Y-binning.
Definition: baslerCtrl.hpp:126
std::vector< int > m_incWs
The minimum value of the width for each X-binning.
Definition: baslerCtrl.hpp:120
std::string m_serialNumber
The camera's identifying serial number.
Definition: baslerCtrl.hpp:105
std::vector< int > m_incXs
The allowed increment in X for each X-binning.
Definition: baslerCtrl.hpp:117
std::vector< int > m_binYs
The allowed values of binning in Y (vertical)
Definition: baslerCtrl.hpp:115
std::vector< int > m_minHs
The minimum value of the height for each Y-binning.
Definition: baslerCtrl.hpp:125
void * loadImageIntoStreamCopy(void *dest, void *src, size_t width, size_t height, size_t szof)
int updateINDI()
Update the INDI properties for this device controller.
MagAO-X standard camera interface.
Definition: stdCamera.hpp:287
int updateINDI()
Update the INDI properties for this device controller.
Definition: stdCamera.hpp:2794
@ info
For information only.
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:212
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition: dm.hpp:24
A device base class which saves telemetry.
Definition: telemeter.hpp:69
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:268
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:281
Software CRITICAL log entry.
Software ERR log entry.
Log entry recording stdcam stage specific status.