API
 
Loading...
Searching...
No Matches
pvcamCtrl.hpp
Go to the documentation of this file.
1/** \file pvcamCtrl.hpp
2 * \brief The MagAO-X pvcam controller header file
3 *
4 * \ingroup pvcamCtrl_files
5 */
6
7#ifndef pvcamCtrl_hpp
8#define pvcamCtrl_hpp
9
10#include <string.h>
11
12// PVCAM
13#include <master.h>
14#include <pvcam.h>
15
16#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
17#include "../../magaox_git_version.h"
18
19/** \defgroup pvcamCtrl
20 * \brief The pvcam controller application for teledyne cameras
21 *
22 * <a href="../handbook/operating/software/apps/pvcamCtrl.html">Application Documentation</a>
23 *
24 * \ingroup apps
25 *
26 */
27
28#ifndef PL_ERR_LIBRARY_NOT_INITIALIZED
29#define PL_ERR_LIBRARY_NOT_INITIALIZED (157)
30#endif
31
32/// Format an error message using pvcam facilities
33/**
34 * \returns a string of format "<func> failed: <pl_error_message>. <more>"
35 */
36std::string pvcamErrMessage( const std::string & func, ///< [in] the pvcam function which failed
37 int pec, ///< [in] the code from pl_error_code
38 const std::string & more ///< [in] extra information to include
39 )
40{
41 char pvmsg[ERROR_MSG_LEN];
42 pl_error_message(pec, pvmsg);
43
44 std::string msg = func + " failed: " + pvmsg;
45 if(more != "") msg += " " + more;
46
47 return msg;
48}
49
50/// Helper for logging an error from pvcam
51#define log_pvcam_software_error( func, /**< [in] the pvcam function which failed*/ \
52 more /**< [in] extra information to include*/ \
53 ) \
54{ \
55 int pec = pl_error_code(); \
56 log<software_error>({__FILE__, __LINE__, 0, pec , pvcamErrMessage(func, pec, more)}); \
57}
58
59
60/** \defgroup pvcamCtrl_files
61 * \ingroup pvcamCtrl
62 */
63
64namespace MagAOX
65{
66namespace app
67{
68
69/// The MagAO-X pvcam controller
70/**
71 * \ingroup pvcamCtrl
72 */
73class pvcamCtrl : public MagAOXApp<true>, public dev::stdCamera<pvcamCtrl>, public dev::frameGrabber<pvcamCtrl>,
74 public dev::dssShutter<pvcamCtrl>,
75 public dev::telemeter<pvcamCtrl>
76{
77
82
83 //Give the test harness access.
84 friend class pvcamCtrl_test;
85
86 friend class dev::stdCamera<pvcamCtrl>;
87 friend class dev::frameGrabber<pvcamCtrl>;
88 friend class dev::dssShutter<pvcamCtrl>;
89 friend class dev::telemeter<pvcamCtrl>;
90
92
93public:
94 /** \name app::dev Configurations
95 *@{
96 */
97 static constexpr bool c_stdCamera_tempControl = false; ///< app::dev config to tell stdCamera to expose temperature controls
98
99 static constexpr bool c_stdCamera_temp = false; ///< app::dev config to tell stdCamera to expose temperature
100
101 static constexpr bool c_stdCamera_readoutSpeed = true; ///< app::dev config to tell stdCamera to expose readout speed controls
102
103 static constexpr bool c_stdCamera_vShiftSpeed = false; ///< app:dev config to tell stdCamera to expose vertical shift speed control
104
105 static constexpr bool c_stdCamera_emGain = false; ///< app::dev config to tell stdCamera to expose EM gain controls
106
107 static constexpr bool c_stdCamera_exptimeCtrl = true; ///< app::dev config to tell stdCamera to expose exposure time controls
108
109 static constexpr bool c_stdCamera_fpsCtrl = true; ///< app::dev config to tell stdCamera not to expose FPS controls
110
111 static constexpr bool c_stdCamera_fps = true; ///< app::dev config to tell stdCamera not to expose FPS status
112
113 static constexpr bool c_stdCamera_synchro = false; ///< app::dev config to tell stdCamera to not expose synchro mode controls
114
115 static constexpr bool c_stdCamera_usesModes = false; ///< app:dev config to tell stdCamera not to expose mode controls
116
117 static constexpr bool c_stdCamera_usesROI = true; ///< app:dev config to tell stdCamera to expose ROI controls
118
119 static constexpr bool c_stdCamera_cropMode = false; ///< app:dev config to tell stdCamera to expose Crop Mode controls
120
121 static constexpr bool c_stdCamera_hasShutter = true; ///< app:dev config to tell stdCamera to expose shutter controls
122
123 static constexpr bool c_stdCamera_usesStateString = false; ///< app::dev confg to tell stdCamera to expose the state string property
124
125 static constexpr bool c_frameGrabber_flippable = false; ///< app:dev config to tell framegrabber this camera can be flipped
126
127 ///@}
128
129
130protected:
131
132 /** \name Configurable Parameters
133 *@{
134 */
135
136 std::string m_serialNumber; ///< The camera serial number.
137
138 ///\todo implement config-ing of this
139 float m_tempTol {0.1}; ///< Tolerance in degrees C to declare the temperature control locked.
140
141 uint32_t m_circBuffMaxBytes {536870912}; ///< Max size in bytes of the circular buffer to allocate. Default is 0.5 GB.
142
143 ///@}
144
145 int16 m_handle{ -1 }; ///< Camera handle, set when camera is opened
146
147 std::string m_camName; ///< Camera name, filled in as part of opening the camera.
148
149 struct gain
150 {
151 int index;
152 std::string name;
153
155 };
156
157 struct speed
158 {
159 int index;
161 int minG;
162 int maxG;
163 std::vector<gain> gains;
164 };
165
166 struct port
167 {
168 int index;
169 int value;
170 std::string name;
171
172 std::vector<speed> speeds;
173 };
174
175 std::vector<port> m_ports;
176
177 bool m_8bit {false};
178
180 uns8 * m_circBuff {nullptr};
181
183
184 sem_t m_frSemaphore; ///< Semaphore used to signal that a frame is ready.
185
186
187public:
188 /// Default c'tor.
189 pvcamCtrl();
190
191 /// D'tor, declared and defined for noexcept.
194
195
196 // MagAOXApp:
197
198 virtual void setupConfig();
199
200 /// Implementation of loadConfig logic, separated for testing.
201 /** This is called by loadConfig().
202 */
203 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
204
205 virtual void loadConfig();
206
207 /// Startup function
208 /**
209 *
210 */
211 virtual int appStartup();
212
213 /// Implementation of the FSM for pvcamCtrl.
214 /**
215 * \returns 0 on no critical error
216 * \returns -1 on an error requiring shutdown
217 */
218 virtual int appLogic();
219
220 /// Shutdown the app.
221 /**
222 *
223 */
224 virtual int appShutdown();
225
226
227 // stdCamera interface:
228
229 //This must set the power-on default values of
230 /* -- m_ccdTempSetpt
231 * -- m_currentROI
232 */
233 int powerOnDefaults();
234
235 int setTempControl();
236 int setTempSetPt();
237 int setReadoutSpeed();
238 int setVShiftSpeed();
239 int setEMGain();
240 int setExpTime();
241 int setFPS();
242
243 /// Check the next ROI
244 /** Checks if the target values are valid and adjusts them to the closest valid values if needed.
245 *
246 * \returns 0 if successful
247 * \returns -1 otherwise
248 */
249 int checkNextROI();
250
251 int setNextROI();
252
253 /// Sets the shutter state, via call to dssShutter::setShutterState(int) [stdCamera interface]
254 /**
255 * \returns 0 always
256 */
257 int setShutter(int sh);
258
259 //Framegrabber interface:
261 float fps();
262 int startAcquisition();
264 int loadImageIntoStream(void * dest);
265 int reconfig();
266
267 // pvcam specific:
268 int connect();
269
270 int fillSpeedTable();
271
272 void dumpEnum( uns32 paramID,
273 const std::string & paramMnem
274 );
275
276 int getTemp();
277
279 void * pvcamCtrlInst
280 );
281
283
284 /** \name Telemeter Interface
285 *
286 * @{
287 */
288 int checkRecordTimes();
289
290 int recordTelem( const telem_stdcam * );
291
292 int recordTelem( const telem_fgtimings * );
293
294 ///@}
295
296};
297
298pvcamCtrl::pvcamCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
299{
300 m_powerMgtEnabled = true;
301 m_powerOnWait = 15;
302
303 m_expTime = 0.01;
304 m_expTimeSet = 0.01;
305
306 m_default_x = 1599.5;
307 m_default_y = 1599.5;
308 m_default_w = 3200;
309 m_default_h = 3200;
310 m_default_bin_x = 1;
311 m_default_bin_y = 1;
312
313 m_full_x = 1599.5;
314 m_full_y = 1599.5;
315 m_full_w = 3200;
316 m_full_h = 3200;
317
324
329 m_nextROI.bin_x = m_currentROI.bin_x;
330 m_nextROI.bin_y = m_currentROI.bin_y;
331
332 m_defaultReadoutSpeed = "dynamic_range";
333 m_readoutSpeedNames = {"sensitivity", "speed", "dynamic_range", "sub_electron"};
334 m_readoutSpeedNameLabels = {"Sensitivity", "Speed", "Dynamic Range", "Sub-Electron"};
335
338
339
340 return;
341}
342
344{
345 config.add("camera.serialNumber", "", "camera.serialNumber", argType::Required, "camera", "serialNumber", false, "int", "The identifying serial number of the camera.");
346
347 //put this in camera because it is a camera interface config, not a framegrabber thing per se:
348 config.add("camera.circBuffMaxBytes", "", "camera.circBuffMaxBytes", argType::Required, "camera", "circBuffMaxBytes", false, "int", "Maximum size in bytes of the circular buffer to allocate. Default is 0.5 GB.");
349
352 shutterT::setupConfig(config);
354}
355
356int pvcamCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
357{
358 _config(m_serialNumber, "camera.serialNumber");
359 _config(m_circBuffMaxBytes, "camera.circBuffMaxBytes");
360
361 if(m_serialNumber == "")
362 {
363 log<text_log>("camera serial number not provided", logPrio::LOG_CRITICAL);
364 return -1;
365 }
366
368
375
380 m_nextROI.bin_x = m_currentROI.bin_x;
381 m_nextROI.bin_y = m_currentROI.bin_y;
382
386
387
388 return 0;
389}
390
392{
393 if(loadConfigImpl(config) != 0)
394 {
395 log<text_log>("error loading config", logPrio::LOG_CRITICAL);
396 m_shutdown = true;
397 }
398}
399
401{
402 if(stdCameraT::appStartup() < 0)
403 {
405 }
406
407 if(sem_init(&m_frSemaphore, 0, 0) < 0)
408 {
409 return log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Initializing frame ready semaphore"});
410 }
411
413 {
414 return log<software_critical, -1>({__FILE__, __LINE__});
415 }
416
417 if(shutterT::appStartup() < 0)
418 {
420 }
421
422 if(telemeterT::appStartup() < 0)
423 {
425 }
426
427 return 0;
428}
429
431{
432 ///\todo why do we run dev appLogics first?
433
434 if( state() == stateCodes::POWERON)
435 {
436 if(!powerOnWaitElapsed())
437 {
438 return 0;
439 }
440
442 }
443
444 //run stdCamera's appLogic
445 if(stdCameraT::appLogic() < 0)
446 {
447 return log<software_error, -1>({__FILE__, __LINE__});
448 }
449
450 // run frammerGrabbers's appLogic
452 {
453 return log<software_error, -1>({__FILE__, __LINE__});
454 }
455
456 //and run dssShutter's appLogic
457 if(shutterT::appLogic() < 0)
458 {
459 return log<software_error, -1>({__FILE__, __LINE__});
460 }
461
463 {
464 m_reconfig = true; //Trigger a f.g. thread reconfig.
465
466 //Might have gotten here because of a power off.
467 if(powerState() != 1)
468 {
469 return 0;
470 }
471
472 std::unique_lock<std::mutex> lock(m_indiMutex);
473 if(connect() < 0)
474 {
475 if(powerState() != 1 || powerStateTarget() != 1) return 0;
477 }
478
479 if(state() != stateCodes::CONNECTED) return 0;
480 }
481
483 {
486 }
487
489 {
490 if(getTemp() != 0)
491 {
492 if(powerState() != 1 || powerStateTarget() != 1) return 0;
494 return 0;
495 }
496
497 if(stdCameraT::updateINDI() < 0)
498 {
500 }
501
503 {
505 }
506
507 if(telemeterT::appLogic() < 0)
508 {
510 }
511
512 recordCamera();
513 }
514
515
516 return 0;
517}
518
520{
521 if(m_handle != -1)
522 {
524 {
525 log_pvcam_software_error("pl_cam_close", "continuing");
526 }
527 m_handle = -1;
528 }
529
530 if(!pl_pvcam_uninit())
531 {
532 if(pl_error_code() != PL_ERR_LIBRARY_NOT_INITIALIZED) /// \todo this error code is manually defined
533 {
534 log_pvcam_software_error("pl_pvcam_uninit", "continuing");
535 }
536 }
537
538 ///\todo error check these base class fxns.
540 {
541 log<software_error>({__FILE__, __LINE__, "error from frameGrabberT::appShutdown()"});
542 }
543
544 if(shutterT::appShutdown() < 0)
545 {
546 log<software_error>({__FILE__, __LINE__, "error from shutterT::appShutdown()"});
547 }
548
550 {
551 log<software_error>({__FILE__, __LINE__, "error from telemeterT::appShutdown()"});
552 }
553
554 return 0;
555}
556
581
583{
584 return 0;
585}
586
588{
589 return 0;
590}
591
593{
595
596 return 0;
597}
598
600{
601 return 0;
602}
603
605{
606 return 0;
607}
608
610{
612
613 if(!pl_get_param(m_handle, PARAM_EXPOSURE_TIME, ATTR_MIN, static_cast<void *>(&minExpTime)))
614 {
615 log_pvcam_software_error("pl_get_param", "PARAM_EXPOSURE_TIME ATTR_MIN");
616 log<software_error>({__FILE__, __LINE__, "could not set exposure time"});
617 return -1;
618 }
619
620 if(!pl_get_param(m_handle, PARAM_EXPOSURE_TIME, ATTR_MAX, static_cast<void *>(&maxExpTime)))
621 {
622 log_pvcam_software_error("pl_get_param", "PARAM_EXPOSURE_TIME ATTR_MAX");
623 log<software_error>({__FILE__, __LINE__, "could not set exposure time"});
624 return -1;
625 }
626
627 std::cerr << "Exposure time min: " << minExpTime << " max: " << maxExpTime << "\n";
628
630 {
631 m_expTimeSet = (int) (minExpTime/1e6+0.5);
632 log<text_log>("increased m_expTimeSet to: " + std::to_string(m_expTimeSet), logPrio::LOG_INFO);
633 }
634
635 if(m_expTimeSet * 1e6 > maxExpTime)
636 {
637 m_expTimeSet = (int) (maxExpTime / 1e6 - 0.5);
638 log<text_log>("decreased m_expTimeSet to: " + std::to_string(m_expTimeSet), logPrio::LOG_INFO);
639 }
640
642
643 return 0;
644}
645
647{
649 return setExpTime();
650}
651
653{
654 if(m_nextROI.w > 3200)
655 {
656 m_nextROI.w = 3200;
657 }
658
659 int x0 = m_nextROI.x - 0.5*m_nextROI.w;
660
661 if(x0 < 0)
662 {
663 m_nextROI.w += x0;
664 m_nextROI.x = 0;
665 }
666
667 int x1 = m_nextROI.x + 0.5*m_nextROI.w;
668
669 if(x1 > 3199)
670 {
671 m_nextROI.w = 3199 - x0;
672 m_nextROI.x = x0 + 0.5*m_nextROI.w;
673 }
674
675 if(m_nextROI.h > 3200)
676 {
677 m_nextROI.h = 3200;
678 }
679
680 int y0 = m_nextROI.y - 0.5 * m_nextROI.h;
681
682 if(y0 < 0)
683 {
684 m_nextROI.h += y0;
685 m_nextROI.y = 0;
686 }
687
688 int y1 = m_nextROI.y + 0.5 * m_nextROI.h;
689
690 if(y1 > 3199)
691 {
692 m_nextROI.h = 3199 - y0;
693 m_nextROI.y = y0 + 0.5 * m_nextROI.h;
694 }
695
696 return 0;
697}
698
700{
701 //This is done in setup acq
703 return 0;
704}
705
707{
708 recordCamera(true);
710}
711
713{
714
715 recordCamera(true);
716 //-- 0: register the callback
717 if(pl_cam_deregister_callback(m_handle, PL_CALLBACK_EOF) == false) // Because we registered it last time we configured acq:
718 {
719 log_pvcam_software_error("pl_cam_deregister_callback", "PL_CALLBACK_EOF");
720 }
721
722 if(pl_cam_register_callback_ex3(m_handle, PL_CALLBACK_EOF, reinterpret_cast<void *>(&st_endOfFrameCallback), static_cast<void *>(this)) != true)
723 {
724 log_pvcam_software_error("pl_cam_register_callback_ex3", "PL_CALLBACK_EOF");
725 return -1;
726 }
727
728 //-- 1: Set speed:
729 int32 value;
730
731 if(m_readoutSpeedNameSet == "sensitivity")
732 {
733 value=0;
734 m_8bit = false;
735 }
736 else if(m_readoutSpeedNameSet == "speed")
737 {
738 value=1;
739 m_8bit = true;
740 }
741 else if(m_readoutSpeedNameSet == "dynamic_range")
742 {
743 value=2;
744 m_8bit = false;
745 }
746 else if(m_readoutSpeedNameSet == "sub_electron")
747 {
748 value=3;
749 m_8bit = false;
750 }
751 else
752 {
753 value = 2;
754 m_8bit = false;
755 m_readoutSpeedNameSet = "dynamic_range";
756 }
758
759 if(pl_set_param(m_handle, PARAM_READOUT_PORT, static_cast<void *>(&value)) == false)
760 {
761 log_pvcam_software_error("pl_set_param", "PARAM_READOUT_PORT");
762 return -1;
763 }
764
765 //-- 2: Set ROI:
767 pvROI.s1 = m_nextROI.x - 0.5*m_nextROI.w;
768 pvROI.s2 = pvROI.s1 + m_nextROI.w-1;
769 pvROI.sbin = m_nextROI.bin_x;
770 pvROI.p1 = m_nextROI.y - 0.5*m_nextROI.h;
771 pvROI.p2 = pvROI.p1 + m_nextROI.h-1;
772 pvROI.pbin = m_nextROI.bin_y;
773
775
776 m_width = (pvROI.s2 - pvROI.s1 + 1) / pvROI.sbin;
777 m_height = (pvROI.p2 - pvROI.p1 + 1) / pvROI.pbin;
779
780
781 //-- 3: Setup continuous acquisition
782 //std::cerr << pvROI.s1 << " " << pvROI.s2 << " " << pvROI.sbin << " " << pvROI.p1 << " " << pvROI.p2 << " " << pvROI.pbin << "\n";
783 uns32 fsize;
784
785 uns32 exptime = m_expTimeSet * 1e6;
786 if(pl_exp_setup_cont(m_handle, 1, &pvROI, TIMED_MODE, exptime, &fsize, CIRC_OVERWRITE) == false)
787 {
788 log_pvcam_software_error("pl_exp_setup_cont", "");
789 m_shutdown = true;
790 return -1;
791 }
792
793 if (pl_get_param(m_handle, PARAM_EXPOSURE_TIME, ATTR_CURRENT, &exptime) == false)
794 {
795 log_pvcam_software_error("pl_get_param", "PARAM_EXPOSURE_TIME");
796 }
797 m_expTime = (1.0*exptime) / 1e6;
799
802 {
803 log_pvcam_software_error("pl_get_param", "PARAM_READOUT_TIME");
804 }
805
806 long64 predelay = 0;
808 {
809 log_pvcam_software_error("pl_get_param", "PARAM_PRE_TRIGGER_DELAY");
810 }
811
812 long64 postdelay = 0;
814 {
815 log_pvcam_software_error("pl_get_param", "PARAM_POST_TRIGGER_DELAY");
816 }
817
818 m_fps = 1.0/(m_expTime + predelay/1e9 + postdelay/1e9);
819 m_fpsSet = m_fps;
820
821 //-- 4: Allocate the acq circular buffer
822 if(m_circBuff != nullptr)
823 {
824 delete[] m_circBuff;
825 m_circBuffBytes = 0;
826 m_circBuff = nullptr;
827 }
828
830
831 m_circBuff = new (std::nothrow) uns8[m_circBuffBytes];
832
833 if(m_circBuff == nullptr)
834 {
835 log<software_critical>({__FILE__, __LINE__, "failed to allocate acquisition circular buffer."});
837 return -1;
838 }
839
840 recordCamera(true);
841
842 return 0;
843}
844
846{
847 return m_fps;
848}
849
851{
853 {
854 log_pvcam_software_error("pv_exp_start_cont", "");
855 return -1;
856 }
857
858 log<text_log>("continuous acquisition started", logPrio::LOG_INFO);
859 return 0;
860}
861
863{
865 if( rv == 0)
866 {
867 return 0;
868 }
869 else if(errno != EAGAIN)
870 {
871 log<software_critical>({__FILE__, __LINE__, errno, 0, "sem_trywait"});
872 return -1;
873 }
874
875 timespec ts;
876
878 {
879 log<software_critical>({__FILE__, __LINE__, errno, 0, "clock_gettime"});
880 return -1;
881 }
882
883 mx::sys::timespecAddNsec(ts, 1e9);
884
886 if(rv == 0)
887 {
888 return 0;
889 }
890 else if(errno != EAGAIN && errno != ETIMEDOUT)
891 {
892 log<software_critical>({__FILE__, __LINE__, errno, 0, "sem_timedwait"});
893 return -1;
894 }
895
896 return 1;
897}
898
900{
902
903 // Obtain a pointer to the last acquired frame
904 uns8 *frame;
905 if(pl_exp_get_latest_frame(m_handle, reinterpret_cast<void **>(&frame)) == false)
906 {
907 log_pvcam_software_error("pl_exp_get_latest_frame", "");
908 }
909
910 if(m_8bit)
911 {
912 uint16_t * dest16 = static_cast<uint16_t*>(dest);
913 uint8_t * src8 = static_cast<uint8_t*>(frame);
914 for(uint32_t i=0; i < m_width*m_height; ++i)
915 {
916 dest16[i] = src8[i];
917 }
918 }
919 else
920 {
921 memcpy(dest, frame, m_width*m_height*2);
922 }
923
924 return 0;
925}
926
928{
930 {
931 log_pvcam_software_error("pl_exp_stop_cont", "");
932 }
933 return 0;
934}
935
937{
938
939 //In picam, we had to initialize every time. We'll do that here too.
940
941 //So close handle if it's open
942 if(m_handle != -1)
943 {
945 {
946 log_pvcam_software_error("pl_cam_close", "");
947 return -1;
948 }
949 m_handle = -1;
950 }
951
952 //Uninit
953 if(!pl_pvcam_uninit())
954 {
955 if(pl_error_code() != PL_ERR_LIBRARY_NOT_INITIALIZED) /// \todo this error code is manually defined
956 {
957 log_pvcam_software_error("pl_pvcam_uninit", "continuing");
958 }
959 }
960
961 if(!pl_pvcam_init())
962 {
963 log_pvcam_software_error("pl_pvcam_init", "");
964 return -1;
965 }
966
968
969 // Read the number of cameras in the system.
970 // This will return total number of PVCAM cameras regardless of interface.
972 {
973 log_pvcam_software_error("pl_cam_get_total", "");
974 return -1;
975 }
976
977 if(nrOfCameras == 0)
978 {
979 if(!stateLogged())
980 {
981 log<text_log>("Found 0 pvcam cameras.", logPrio::LOG_INFO);
982 }
984 return 0;
985 }
986
987 log<text_log>("Found " + std::to_string(nrOfCameras) + " pvcam cameras.", logPrio::LOG_INFO);
988
989
990 for(int n=0; n < nrOfCameras; ++n)
991 {
992 char camName[CAM_NAME_LEN] {'\0'};
993
994 // Obtain PVCAM-name for this particular camera
996 {
997 log_pvcam_software_error("pl_cam_get_name", "");
998 return -1;
999 }
1000
1001 int16_t handle = -1;
1002
1003 //Open to check its serial number
1005 {
1006 log_pvcam_software_error("pl_cam_open", ""); //We log this for now, but with 2 apps running we prob want to ignore
1007 continue;
1008 }
1009
1010 // Read the version of the Device Driver
1011
1014 {
1015 log_pvcam_software_error("pl_get_param", "PARAM_HEAD_SER_NUM_ALPHA ATTR_AVAIL");
1016
1017 if(!pl_cam_close(handle))
1018 {
1019 log_pvcam_software_error("pl_cam_close", "");
1020 }
1021
1022 return -1;
1023 }
1024
1025 if(isAvailable)
1026 {
1027 char camSerial[MAX_ALPHA_SER_NUM_LEN]{ '\0' };
1028
1030 {
1031 log_pvcam_software_error("pl_get_param", "PARAM_HEAD_SER_NUM_ALPHA ATTR_CURRENT");
1032
1033 if(!pl_cam_close(handle))
1034 {
1035 log_pvcam_software_error("pl_cam_close", "");
1036 }
1037
1038 return -1;
1039 }
1040
1042 {
1043 state(stateCodes::NOTCONNECTED); //not strictly true, but logically true until m_handle is set
1044 m_camName = camName;
1045 m_handle = handle;
1046 break;
1047 }
1048 }
1049
1050 //If we're here then either it didn't have a serial number, or it didn't have the right serial number
1051 if(!pl_cam_close(handle))
1052 {
1053 log_pvcam_software_error("pl_cam_close", "");
1054 return -1;
1055 }
1056
1057 }
1058
1059 if(m_handle > -1)
1060 {
1061 log<text_log>("Opened camera " + m_serialNumber + " at " + m_camName, logPrio::LOG_INFO);
1063
1064
1065 int32 res;
1066 uns16 idx;
1067
1068 //Set exposure resolution to usec.
1069
1070 idx = 1;
1072 {
1073 log_pvcam_software_error("pl_set_param", "PARAM_EXP_RES_INDEX");
1074 }
1075
1077 {
1078 log_pvcam_software_error("pl_get_param", "PARAM_EXP_RES");
1079 }
1080
1082 {
1083 log_pvcam_software_error("pl_get_param", "PARAM_EXP_RES_INDEX");
1084 }
1085
1087 }
1088 else
1089 {
1090 if(!stateLogged())
1091 {
1092 log<text_log>("camera not found", logPrio::LOG_INFO);
1093 }
1095 }
1096
1097 return 0;
1098}
1099
1101{
1103 {
1104 return -1;
1105 }
1106
1107 uns32 nports;
1108
1109 if(pl_get_param(m_handle, PARAM_READOUT_PORT, ATTR_COUNT, static_cast<void *>(&nports)) == false)
1110 {
1111 log_pvcam_software_error("pl_get_param", "PARAM_READOUT_PORT");
1112 return -1;
1113 }
1114
1115 std::cerr << "Found " << nports << " ports\n";
1116
1117 m_ports.resize(nports);
1118
1119 for(uns32 p=0; p < nports; ++p)
1120 {
1123 {
1124 log_pvcam_software_error("pl_enum_str_length", "PARAM_READOUT_PORT");
1125 return -1;
1126 }
1127
1128 char* text = new (std::nothrow) char[strLength];
1129 if (!text)
1130 {
1131 ///\todo log this properly
1132 std::cerr << "failed to allocate string\n";
1133 return -1;
1134 }
1135
1136 int32 value;
1137 if(pl_get_enum_param(m_handle, PARAM_READOUT_PORT, p, &value, text, strLength) == false)
1138 {
1139 log_pvcam_software_error("pl_get_enum_param", "PARAM_READOUT_PORT");
1140 delete [] text;
1141 return false;
1142 }
1143
1144 m_ports[p].index = p;
1145 m_ports[p].value = value;
1146 m_ports[p].name = text;
1147
1148 std::cerr << "Port: " << p << " name: " << text << " value: " << value << "\n";
1149
1150 delete [] text;
1151
1152 if(pl_set_param(m_handle, PARAM_READOUT_PORT, static_cast<void *>(&value)) == false)
1153 {
1154 log_pvcam_software_error("pl_set_param", "PARAM_READOUT_PORT");
1155 return -1;
1156 }
1157
1158 uns32 nspeeds;
1159 if(pl_get_param(m_handle, PARAM_SPDTAB_INDEX, ATTR_COUNT, static_cast<void *>(&nspeeds)) == false)
1160 {
1161 log_pvcam_software_error("pl_get_param", "PARAM_SPDTAB_INDEX");
1162 return -1;
1163 }
1164 std::cerr << " Speeds: " << nspeeds << "\n";
1165
1166 m_ports[p].speeds.resize(nspeeds);
1167
1168 for(uns32 s = 0; s < nspeeds; ++s)
1169 {
1170 if(pl_set_param(m_handle, PARAM_SPDTAB_INDEX, static_cast<void *>(&s)) == false)
1171 {
1172 log_pvcam_software_error("pl_set_param", "PARAM_SPDTAB_INDEX");
1173 return -1;
1174 }
1175
1176 uns16 pixtime;
1177 if(pl_get_param(m_handle, PARAM_PIX_TIME, ATTR_CURRENT, static_cast<void *>(&pixtime)) == false)
1178 {
1179 log_pvcam_software_error("pl_get_param", "PARAM_PIX_TIME");
1180 return -1;
1181 }
1182
1183 m_ports[p].speeds[s].pixTime = pixtime;
1184
1185 uns32 ngains;
1186 if(pl_get_param(m_handle, PARAM_GAIN_INDEX, ATTR_COUNT, static_cast<void *>(&ngains)) == false)
1187 {
1188 log_pvcam_software_error("pl_get_param", "PARAM_GAIN_INDEX ATTR_COUNT");
1189 return -1;
1190 }
1191
1192 int16 ming;
1193 if(pl_get_param(m_handle, PARAM_GAIN_INDEX, ATTR_MIN, static_cast<void *>(&ming)) == false)
1194 {
1195 log_pvcam_software_error("pl_get_param", "PARAM_GAIN_INDEX ATTR_MIN");
1196 return -1;
1197 }
1198
1199 int16 maxg;
1200 if(pl_get_param(m_handle, PARAM_GAIN_INDEX, ATTR_MIN, static_cast<void *>(&maxg)) == false)
1201 {
1202 log_pvcam_software_error("pl_get_param", "PARAM_GAIN_INDEX ATTR_MAX");
1203 return -1;
1204 }
1205
1206 std::cerr << " Speed: " << s << " " << " pixtime: " << pixtime << " gains: " << ngains << " [" << ming << "-" << maxg << "]\n";
1207
1208 m_ports[p].speeds[s].minG = ming;
1209 m_ports[p].speeds[s].maxG = maxg;
1210
1211 m_ports[p].speeds[s].gains.resize(ngains);
1212
1213 for(uns32 g=0; g < ngains; ++g)
1214 {
1215
1216 int16 gg = ming + g;
1217 if(pl_set_param(m_handle, PARAM_GAIN_INDEX, static_cast<void *>(&gg)) == false)
1218 {
1219 log_pvcam_software_error("pl_set_param", "PARAM_GAIN_INDEX");
1220 return -1;
1221 }
1222
1224 if(pl_get_param(m_handle, PARAM_BIT_DEPTH, ATTR_CURRENT, static_cast<void *>(&bitdepth)) == false)
1225 {
1226 log_pvcam_software_error("pl_get_param", "PARAM_BIT_DEPTH ATTR_CURRENT");
1227 return -1;
1228 }
1229 std::cerr << " Gain: " << g << " bitdepth: " << bitdepth << "\n";
1230 }
1231 }
1232 }
1233
1234 return 0;
1235}
1236
1237void pvcamCtrl::dumpEnum(uns32 paramID, const std::string & paramMnem)
1238{
1239
1241 {
1242 uns32 count;
1243
1244 if(PV_OK != pl_get_param(m_handle, paramID, ATTR_COUNT, static_cast<void *>(&count)))
1245 {
1246 log_pvcam_software_error("pl_get_param", paramMnem);
1247 // TODO: Handle error
1248 return;
1249 }
1250 if(count == 0)
1251 {
1252 std::cerr << paramMnem << ": count 0\n";
1253 return;
1254 }
1255 for (uns32 n = 0; n < count; ++n)
1256 {
1259 {
1260 log_pvcam_software_error("pl_enum_str_length", paramMnem);
1261 // TODO: Handle error
1262 break;
1263 }
1264 char *text = new (std::nothrow) char[strLength];
1265 if(!text)
1266 {
1267 // TODO: Handle error
1268 break;
1269 }
1270 int32 value;
1272 {
1273 log_pvcam_software_error("pl_get_enum_param", paramMnem);
1274 // TODO: Handle error
1275 delete[] text;
1276 break;
1277 }
1278 std::cerr << paramMnem;
1279 fprintf(stderr, " item at index %u, value: %d, text: '%s'\n", n, value, text);
1280 delete[] text;
1281 }
1282 }
1283 else
1284 {
1285 std::cerr << "dumpEnum: not CONNECTED\n";
1286 }
1287}
1288
1289
1291{
1293 {
1294 return 0;
1295 }
1296
1298 if(!pl_get_param(m_handle, PARAM_TEMP_SETPOINT, ATTR_AVAIL, static_cast<void*>(&isAvailable)))
1299 {
1300 if(powerState() != 1 || powerStateTarget() != 1) return 0;
1301 log_pvcam_software_error("pl_get_param", "PARAM_TEMP ATTR_AVAIL");
1303 }
1304
1305 int16 stemp;
1306 if(isAvailable) //Maybe this is a separate check. Don't yet know what happens when acquiring
1307 {
1308 if(!pl_get_param(m_handle, PARAM_TEMP_SETPOINT, ATTR_CURRENT, static_cast<void*>(&stemp)))
1309 {
1310 if(powerState() != 1 || powerStateTarget() != 1) return 0;
1311 log_pvcam_software_error("pl_get_param", "PARAM_TEMP ATTR_AVAIL");
1313 }
1314
1315 m_ccdTempSetpt = stemp/100.0;
1316 }
1317
1318 if(!pl_get_param(m_handle, PARAM_TEMP, ATTR_AVAIL, static_cast<void*>(&isAvailable)))
1319 {
1320 if(powerState() != 1 || powerStateTarget() != 1) return 0;
1321 log_pvcam_software_error("pl_get_param", "PARAM_TEMP ATTR_AVAIL");
1323 }
1324
1325 int16 ctemp;
1326 if(isAvailable) //Maybe this is a separate check. Don't yet know what happens when acquiring
1327 {
1328 if(!pl_get_param(m_handle, PARAM_TEMP, ATTR_CURRENT, static_cast<void*>(&ctemp)))
1329 {
1330 if(powerState() != 1 || powerStateTarget() != 1) return 0;
1331 log_pvcam_software_error("pl_get_param", "PARAM_TEMP ATTR_AVAIL");
1333 }
1334
1335 m_ccdTemp = ctemp/100.0;
1336 }
1337
1339 {
1340 m_tempControlStatus = true;
1341 m_tempControlOnTarget = false;
1342 m_tempControlStatusStr = "UNLOCKED";
1343 }
1344 else
1345 {
1346 m_tempControlStatus = true;
1347 m_tempControlOnTarget = true;
1348 m_tempControlStatusStr = "LOCKED";
1349 }
1350
1351 return 0;
1352}
1353
1354void pvcamCtrl::st_endOfFrameCallback( FRAME_INFO *finfo,
1355 void *pvcamCtrlInst
1356 )
1357{
1359}
1360
1361void pvcamCtrl::endOfFrameCallback(FRAME_INFO *finfo)
1362{
1363 m_frameInfo = *finfo;
1364
1365 // Now tell the writer to get going
1366 if(sem_post(&m_frSemaphore) < 0)
1367 {
1368 log<software_critical>({__FILE__, __LINE__, errno, 0, "Error posting to frame ready semaphore"});
1369 return;
1370 }
1371}
1372
1377
1379{
1380 return recordCamera(true);
1381}
1382
1383inline
1385{
1386 return recordFGTimings(true);
1387}
1388
1389} // namespace app
1390} //namespace MagAOX
1391
1392#endif //pvcamCtrl_hpp
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
stateCodes::stateCodeT state()
Get the current state code.
int powerState()
Returns the current power state.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
int powerStateTarget()
Returns the target power state.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
bool powerOnWaitElapsed()
This method tests whether the power on wait time has elapsed.
unsigned long m_powerOnWait
Time in sec to wait for device to boot after power on.
std::mutex m_indiMutex
Mutex for locking INDI communications.
MagAO-X Uniblitz DSS Shutter interface.
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int setShutterState(int sh)
Change shutter state.
timespec m_currImageTimestamp
The timestamp of the current image.
uint32_t m_width
The width of the image, once deinterlaced etc.
int appShutdown()
Shuts down the framegrabber thread.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
int updateINDI()
Update the INDI properties for this device controller.
uint8_t m_dataType
The ImageStreamIO type code.
bool m_reconfig
Flag to set if a camera reconfiguration requires a framegrabber reset.
int appLogic()
Checks the framegrabber thread.
uint32_t m_height
The height of the image, once deinterlaced etc.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
MagAO-X standard camera interface.
std::vector< std::string > m_readoutSpeedNames
float m_fpsSet
The commanded fps, as set by user.
float m_default_x
Power-on ROI center x coordinate.
std::string m_tempControlStatusStr
Camera specific description of temperature control status.
std::vector< std::string > m_readoutSpeedNameLabels
std::string m_defaultReadoutSpeed
The default readout speed of the camera.
float m_expTime
The current exposure time, in seconds.
int m_default_bin_x
Power-on ROI x binning.
float m_expTimeSet
The exposure time, in seconds, as set by user.
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
float m_full_y
The full ROI center y coordinate.
std::string m_readoutSpeedName
The current readout speed name.
float m_ccdTempSetpt
The desired temperature, in C.
bool m_tempControlStatus
Whether or not temperature control is active.
float m_full_x
The full ROI center x coordinate.
float m_ccdTemp
The current temperature, in C.
int updateINDI()
Update the INDI properties for this device controller.
bool m_tempControlOnTarget
Whether or not the temperature control system is on its target temperature.
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
float m_default_y
Power-on ROI center y coordinate.
int m_default_bin_y
Power-on ROI y binning.
std::string m_readoutSpeedNameSet
The user requested readout speed name, to be set by derived()
The MagAO-X pvcam controller.
Definition pvcamCtrl.hpp:76
static constexpr bool c_stdCamera_exptimeCtrl
app::dev config to tell stdCamera to expose exposure time controls
int16 m_handle
Camera handle, set when camera is opened.
static constexpr bool c_stdCamera_usesROI
app:dev config to tell stdCamera to expose ROI controls
static constexpr bool c_stdCamera_emGain
app::dev config to tell stdCamera to expose EM gain controls
static constexpr bool c_stdCamera_usesStateString
app::dev confg to tell stdCamera to expose the state string property
static constexpr bool c_stdCamera_fps
app::dev config to tell stdCamera not to expose FPS status
friend class pvcamCtrl_test
Definition pvcamCtrl.hpp:84
void endOfFrameCallback(FRAME_INFO *finfo)
virtual void loadConfig()
static constexpr bool c_stdCamera_tempControl
app::dev config to tell stdCamera to expose temperature controls
Definition pvcamCtrl.hpp:97
int recordTelem(const telem_stdcam *)
static void st_endOfFrameCallback(FRAME_INFO *finfo, void *pvcamCtrlInst)
int setShutter(int sh)
Sets the shutter state, via call to dssShutter::setShutterState(int) [stdCamera interface].
static constexpr bool c_stdCamera_usesModes
app:dev config to tell stdCamera not to expose mode controls
std::vector< port > m_ports
virtual int appShutdown()
Shutdown the app.
uint32_t m_circBuffMaxBytes
Max size in bytes of the circular buffer to allocate. Default is 0.5 GB.
int checkNextROI()
Check the next ROI.
std::vector< speed > speeds
static constexpr bool c_stdCamera_hasShutter
app:dev config to tell stdCamera to expose shutter controls
static constexpr bool c_stdCamera_fpsCtrl
app::dev config to tell stdCamera not to expose FPS controls
dev::stdCamera< pvcamCtrl > stdCameraT
Definition pvcamCtrl.hpp:78
dev::frameGrabber< pvcamCtrl > frameGrabberT
Definition pvcamCtrl.hpp:79
static constexpr bool c_stdCamera_temp
app::dev config to tell stdCamera to expose temperature
Definition pvcamCtrl.hpp:99
pvcamCtrl()
Default c'tor.
virtual int appLogic()
Implementation of the FSM for pvcamCtrl.
sem_t m_frSemaphore
Semaphore used to signal that a frame is ready.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
~pvcamCtrl() noexcept
D'tor, declared and defined for noexcept.
std::string m_camName
Camera name, filled in as part of opening the camera.
virtual int appStartup()
Startup function.
int loadImageIntoStream(void *dest)
dev::dssShutter< pvcamCtrl > shutterT
Definition pvcamCtrl.hpp:80
static constexpr bool c_stdCamera_readoutSpeed
app::dev config to tell stdCamera to expose readout speed controls
void dumpEnum(uns32 paramID, const std::string &paramMnem)
static constexpr bool c_stdCamera_synchro
app::dev config to tell stdCamera to not expose synchro mode controls
dev::telemeter< pvcamCtrl > telemeterT
Definition pvcamCtrl.hpp:81
std::vector< gain > gains
static constexpr bool c_stdCamera_cropMode
app:dev config to tell stdCamera to expose Crop Mode controls
float m_tempTol
Tolerance in degrees C to declare the temperature control locked.
std::string m_serialNumber
The camera serial number.
virtual void setupConfig()
static constexpr bool c_stdCamera_vShiftSpeed
app:dev config to tell stdCamera to expose vertical shift speed control
static constexpr bool c_frameGrabber_flippable
app:dev config to tell framegrabber this camera can be flipped
@ OPERATING
The device is operating, other than homing.
@ NODEVICE
No device exists for the application to control.
@ FAILURE
The application has failed, should be used when m_shutdown is set for an error.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
#define PL_ERR_LIBRARY_NOT_INITIALIZED
Definition pvcamCtrl.hpp:29
std::string pvcamErrMessage(const std::string &func, int pec, const std::string &more)
Format an error message using pvcam facilities.
Definition pvcamCtrl.hpp:36
#define log_pvcam_software_error(func, more)
Helper for logging an error from pvcam.
Definition pvcamCtrl.hpp:51
A device base class which saves telemetry.
Definition telemeter.hpp:69
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int appLogic()
Perform telemeter application logic.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
int appStartup()
Starts the telemetry log thread.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Software CRITICAL log entry.
Software ERR log entry.
Log entry recording framegrabber timings.
Log entry recording stdcam stage specific status.