API
 
Loading...
Searching...
No Matches
photonCounter.hpp
Go to the documentation of this file.
1/** \file photonCounter.hpp
2 * \brief The MagAO-X PWFS Slope Calculator
3 *
4 * \ingroup app_files
5 */
6
7#ifndef photonCounter_hpp
8#define photonCounter_hpp
9
10#include <limits>
11#include <algorithm>
12#include <mx/improc/eigenCube.hpp>
13#include <mx/improc/eigenImage.hpp>
14using namespace mx::improc;
15
16#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
17#include "../../magaox_git_version.h"
18
19namespace MagAOX
20{
21namespace app
22{
23
24/** \defgroup photonCounter PWFS Slope Calculator
25 * \brief Calculates slopes from a PWFS image.
26 *
27 * <a href="../handbook/operating/software/apps/photonCounter.html">Application Documentation</a>
28 *
29 * \ingroup apps
30 *
31 */
32
33/** \defgroup photonCounter_files PWFS Slope Calculator Files
34 * \ingroup photonCounter
35 */
36
37/** MagAO-X application to calculate slopes from PWFS images.
38 *
39 * \ingroup photonCounter
40 *
41 */
42class photonCounter : public MagAOXApp<true>, public dev::shmimMonitor<photonCounter>, public dev::frameGrabber<photonCounter>
43{
44
45 //Give the test harness access.
46 friend class photonCounter_test;
47
48 friend class dev::shmimMonitor<photonCounter>;
49 friend class dev::frameGrabber<photonCounter>;
50
51 //The base shmimMonitor type
53
54 //The base frameGrabber type
56
57 ///Floating point type in which to do all calculations.
58 typedef float realT;
59
60 static constexpr bool c_frameGrabber_flippable = false; ///< app:dev config to tell framegrabber these images can not be flipped
61
62protected:
63
64 /** \name Configurable Parameters
65 *@{
66 */
67
68 ///@}
69
70 sem_t m_smSemaphore; ///< Semaphore used to synchronize the fg thread and the sm thread.
71 realT (*pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
72 void * m_curr_src {nullptr};
73
76
77 mx::improc::eigenCube<realT> m_calibrationCube;
78 mx::improc::eigenImage<realT> m_dark_image;
79 mx::improc::eigenImage<realT> m_thresholdImage;
80 mx::improc::eigenImage<realT> m_photonCountedImage;
82
83 //
86
91
92
93public:
94 /// Default c'tor.
96
97 /// D'tor, declared and defined for noexcept.
100
101 virtual void setupConfig();
102
103 /// Implementation of loadConfig logic, separated for testing.
104 /** This is called by loadConfig().
105 */
106 int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
107
108 virtual void loadConfig();
109
110 /// Startup function
111 /**
112 *
113 */
114 virtual int appStartup();
115
116 /// Implementation of the FSM for photonCounter.
117 /**
118 * \returns 0 on no critical error
119 * \returns -1 on an error requiring shutdown
120 */
121 virtual int appLogic();
122
123 /// Shutdown the app.
124 /**
125 *
126 */
127 virtual int appShutdown();
128
129 int allocate( const dev::shmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
130
131 int processImage( void * curr_src, ///< [in] pointer to start of current frame.
132 const dev::shmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
133 );
134
135 float fps()
136 {
137 return 250;
138 }
139
140protected:
141
142 /** \name dev::frameGrabber interface
143 *
144 * @{
145 */
146
147 /// Implementation of the framegrabber configureAcquisition interface
148 /**
149 * \returns 0 on success
150 * \returns -1 on error
151 */
153
154 /// Implementation of the framegrabber startAcquisition interface
155 /**
156 * \returns 0 on success
157 * \returns -1 on error
158 */
159 int startAcquisition();
160
161 /// Implementation of the framegrabber acquireAndCheckValid interface
162 /**
163 * \returns 0 on success
164 * \returns -1 on error
165 */
167
168 /// Implementation of the framegrabber loadImageIntoStream interface
169 /**
170 * \returns 0 on success
171 * \returns -1 on error
172 */
173 int loadImageIntoStream( void * dest /**< [in] */);
174
175 /// Implementation of the framegrabber reconfig interface
176 /**
177 * \returns 0 on success
178 * \returns -1 on error
179 */
180 int reconfig();
181
182 ///@}
183 pcf::IndiProperty m_indiP_calibrateToggle;
184 pcf::IndiProperty m_indiP_calibrateSteps;
185 pcf::IndiProperty m_indiP_stackFrames;
186 pcf::IndiProperty m_indiP_quantileCut;
187
188public:
189
190 // Control states
195
196};
197
198
199inline
200photonCounter::photonCounter() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
201{
202 return;
203}
204
205inline
207{
210
211 config.add("parameters.quantile", "", "parameters.quantile", argType::Required, "parameters", "quantile", false, "float", "The quantile of the threshold.");
212 config.add("parameters.Nstack", "", "parameters.Nstack", argType::Required, "parameters", "Nstack", false, "string", "The number of frames to stack.");
213 config.add("parameters.Ncalibrate", "", "parameters.Ncalibrate", argType::Required, "parameters", "Ncalibrate", false, "string", "The number of frames for calibration.");
214}
215
216inline
217int photonCounter::loadConfigImpl( mx::app::appConfigurator & _config )
218{
219
222
223 _config(m_stack_frames, "parameters.Nstack");
224 _config(m_quantile_cut, "parameters.quantile");
225 _config(calibration_steps, "parameters.Ncalibrate");
226
227 return 0;
228}
229
230inline
232{
233 loadConfigImpl(config);
234}
235
236inline
238{
239 if(sem_init(&m_smSemaphore, 0,0) < 0)
240 {
241 log<software_critical>({__FILE__, __LINE__, errno,0, "Initializing S.M. semaphore"});
242 return -1;
243 }
244
246 {
247 return log<software_error,-1>({__FILE__, __LINE__});
248 }
249
251 {
252 return log<software_error,-1>({__FILE__, __LINE__});
253 }
254
255 // createStandardIndiToggleSw( m_indiP_calibrateToggle, "calibrate", "Control calibration state", "Calibration control");
256 // registerIndiPropertyNew( m_indiP_calibrateToggle, INDI_NEWCALLBACK(m_indiP_calibrateToggle) );
257
258 createStandardIndiRequestSw( m_indiP_calibrateToggle, "calibrate", "Start calibration", "Calibration control");
260
261 createStandardIndiNumber<int>( m_indiP_calibrateSteps, "nFrames", 0, 1000000, 1, "%d", "The calibration ", "Calibration control");
263
264 createStandardIndiNumber<int>( m_indiP_stackFrames, "stackNframes", 0, 1000000, 1, "%d", "The calibration ", "Calibration control");
266
267 createStandardIndiNumber<float>( m_indiP_quantileCut, "quantile", 0, 1.0, 0.0001, "%0.3f", "The quantile cut", "Calibration control");
269
271
272 return 0;
273}
274
275inline
277{
278 if( shmimMonitorT::appLogic() < 0)
279 {
280 return log<software_error,-1>({__FILE__,__LINE__});
281 }
282
283 if( frameGrabberT::appLogic() < 0)
284 {
285 return log<software_error,-1>({__FILE__,__LINE__});
286 }
287
288 std::unique_lock<std::mutex> lock(m_indiMutex);
289
291 {
293 }
294
296 {
298 }
299
303
304 return 0;
305}
306
307inline
309{
311
313
314 return 0;
315}
316
317inline
319{
320 static_cast<void>(dummy); //be unused
321
324
326 m_calibrationCube.setZero();
327
329 m_thresholdImage.setZero();
330
332 m_dark_image.setZero();
333
335 m_photonCountedImage.setZero();
336
337 // m_stack_frames = 1;
338 // calibration_steps = 1000;
339
340 m_calibrate = false;
341 m_calibrationSet = false;
344
345 return 0;
346}
347
348inline
349int photonCounter::processImage( void * curr_src,
350 const dev::shmimT & dummy
351 )
352{
353 static_cast<void>(dummy); //be unused
354
355 // Set the internal pointer to the new data stream
356 Eigen::Map<eigenImage<unsigned short>> camera_image( static_cast<unsigned short *>(curr_src), m_image_height, m_image_width);
357
358 if(m_calibrate){
359
361 std::cout << "Start data collection" << std::endl;
362
363 m_calibrationSet = false;
364
365 // Copy the data into the stream
368
370
371 }
372 }
373
375
377 // We have collected enough data!
379 m_calibrate = false;
380 m_calibrationSet = true;
381
382 // Measure the average dark frame
384
385 // Copy the data into the stream
388
389 if( col_i == 0){
390 std::cout << "The dark : " << m_dark_image(row_i, col_i) << " ";
391 }
392
393 // Get the time-series of the pixel of interest
394 // auto pixelVec = m_calibrationCube.pixel(row_i, col_i);
395
396 // Dark subtract the timeseries
397 // for(size_t p=0; p<pixelVec.size(); p++){
398 // pixelVec(p, 0) = pixelVec(p, 0) - m_dark_image(row_i, col_i);
399 // }
400 std::vector<float> tempVec;
401 for(int ti=0; ti < calibration_steps; ti++){
402 float val = m_calibrationCube.image(ti)(row_i, col_i);
403 tempVec.push_back(val);
404 }
405
406 // Make it a standard library vector
407 // float* start = &pixelVec(0,0);
408 // float* end = &pixelVec(0,0) + pixelVec.size() * sizeof(float);
409 //
410 // Sort the array
411 std::sort(tempVec.begin(), tempVec.end());
412
413 // Find the qth quantile
414 int q_index = (int)(m_quantile_cut * tempVec.size());
415 if( col_i == 0 )
416 std::cout <<"q0: [" << tempVec[0] << ", " << tempVec[q_index] << ", " << tempVec[tempVec.size()-1] << "]" << std::endl;
417
419
420 }
421 }
422
423 std::cout << "Calibration done!" << std::endl;
424 }
425
426
427 }else{
428
430 // if( m_stack_frames_index == 0 )
431 // std::cout << "Start collecting data for stacking..." << std::endl;
432
433 // Apply photon counting
436 float dI = (float)camera_image(row_i, col_i); // - m_dark_image(row_i, col_i);
437
440 }
441
442 }
443 }
444
446
448
449 // std::cout << "Send stacked data..." << std::endl;
451
452 //Now tell the f.g. to get going
453 if(sem_post(&m_smSemaphore) < 0)
454 {
455 log<software_critical>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
456 return -1;
457 }
458
459 }
460
461 }
462
463 }
464
465 return 0;
466}
467
468inline
470{
471 std::unique_lock<std::mutex> lock(m_indiMutex);
472
474 {
475 //This means we haven't connected to the stream to average. so wait.
476 sleep(1);
477 return -1;
478 }
479
480 // The frame grabber has the exact same size as the imagestream
484
485 return 0;
486}
487
488inline
490{
491 return 0;
492}
493
494inline
496{
497 timespec ts;
498
500 {
501 log<software_critical>({__FILE__,__LINE__,errno,0,"clock_gettime"});
502 return -1;
503 }
504
505 ts.tv_sec += 1;
506
507 if(sem_timedwait(&m_smSemaphore, &ts) == 0)
508 {
510 return 0;
511 }
512 else
513 {
514 return 1;
515 }
516}
517
518inline
520{
521 //Here is where we do it.
522 Eigen::Map<eigenImage<float>> photon_counted_out(static_cast<uint16_t*>(dest), frameGrabberT::m_width, frameGrabberT::m_height );
523
524 // Copy the data into the stream
527
529
531
532 }
533 }
534
535 return 0;
536}
537
538inline
540{
541 return 0;
542}
543
544
545INDI_NEWCALLBACK_DEFN(photonCounter, m_indiP_calibrateToggle )(const pcf::IndiProperty &ipRecv)
546{
547 if(ipRecv.getName() != m_indiP_calibrateToggle.getName())
548 {
549 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
550 return -1;
551 }
552
553 if(!ipRecv.find("request")) return 0;
554
555 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
556 {
557
558 if(!m_calibrate){
559 std::cout << "Request calibration" << std::endl;
560 m_calibrationSet = false;
561 m_calibrate = true;
562 }
563
564 updateSwitchIfChanged(m_indiP_calibrateToggle, "request", pcf::IndiElement::Off, INDI_IDLE);
565 }
566
567 return 0;
568}
569
570INDI_NEWCALLBACK_DEFN(photonCounter, m_indiP_quantileCut )(const pcf::IndiProperty &ipRecv)
571{
572 if(ipRecv.getName() != m_indiP_quantileCut.getName()){
573 log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
574 return -1;
575 }
576
577 float current = -1;
578 float target = -1;
579
580 if(ipRecv.find("current"))
581 current = ipRecv["current"].get<float>();
582
583 if(ipRecv.find("target"))
584 target = ipRecv["target"].get<float>();
585
586 if(target == -1) target = current;
587
588 if(target == -1)
589 return 0;
590
591 std::lock_guard<std::mutex> guard(m_indiMutex);
592
593 m_quantile_cut = target;
594 updateIfChanged(m_indiP_quantileCut, "target", m_quantile_cut);
595
596 return 0;
597}
598
599INDI_NEWCALLBACK_DEFN(photonCounter, m_indiP_calibrateSteps )(const pcf::IndiProperty &ipRecv)
600{
601 if(ipRecv.getName() != m_indiP_calibrateSteps.getName())
602 {
603 log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
604 return -1;
605 }
606
607 int current = -1;
608 int target = -1;
609
610 if(ipRecv.find("current"))
611 {
612 current = ipRecv["current"].get<int>();
613 }
614
615 if(ipRecv.find("target"))
616 {
617 target = ipRecv["target"].get<int>();
618 }
619
620 if(target == -1) target = current;
621
622 if(target == -1)
623 {
624 return 0;
625 }
626
627 std::lock_guard<std::mutex> guard(m_indiMutex);
628
629 calibration_steps = target;
630 m_calibrationCube.resize(m_image_width, m_image_height, calibration_steps);
631 m_calibrationCube.setZero();
632
633 updateIfChanged(m_indiP_calibrateSteps, "target", calibration_steps);
634
635 return 0;
636}
637
638INDI_NEWCALLBACK_DEFN(photonCounter, m_indiP_stackFrames )(const pcf::IndiProperty &ipRecv)
639{
640 if(ipRecv.getName() != m_indiP_stackFrames.getName())
641 {
642 log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
643 return -1;
644 }
645
646 int current = -1;
647 int target = -1;
648
649 if(ipRecv.find("current"))
650 {
651 current = ipRecv["current"].get<int>();
652 }
653
654 if(ipRecv.find("target"))
655 {
656 target = ipRecv["target"].get<int>();
657 }
658
659 if(target == -1) target = current;
660
661 if(target == -1)
662 {
663 return 0;
664 }
665
666 std::lock_guard<std::mutex> guard(m_indiMutex);
667
668 m_stack_frames = target;
669 updateIfChanged(m_indiP_stackFrames, "target", m_stack_frames);
670
671 return 0;
672}
673
674} //namespace app
675} //namespace MagAOX
676
677#endif //photonCounter_hpp
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
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.
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.
uint32_t m_width
The width of the images in the stream.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
int appShutdown()
Shuts down the shmimMonitor thread.
uint8_t m_dataType
The ImageStreamIO type code.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
int loadImageIntoStream(void *dest)
Implementation of the framegrabber loadImageIntoStream interface.
int processImage(void *curr_src, const dev::shmimT &dummy)
~photonCounter() noexcept
D'tor, declared and defined for noexcept.
INDI_NEWCALLBACK_DECL(photonCounter, m_indiP_calibrateSteps)
virtual int appStartup()
Startup function.
pcf::IndiProperty m_indiP_calibrateToggle
sem_t m_smSemaphore
Semaphore used to synchronize the fg thread and the sm thread.
INDI_NEWCALLBACK_DECL(photonCounter, m_indiP_calibrateToggle)
virtual int appLogic()
Implementation of the FSM for photonCounter.
mx::improc::eigenImage< realT > m_photonCountedImage
pcf::IndiProperty m_indiP_stackFrames
float realT
Floating point type in which to do all calculations.
int acquireAndCheckValid()
Implementation of the framegrabber acquireAndCheckValid interface.
mx::improc::eigenImage< realT > m_thresholdImage
virtual int appShutdown()
Shutdown the app.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int configureAcquisition()
Implementation of the framegrabber configureAcquisition interface.
dev::shmimMonitor< photonCounter > shmimMonitorT
int reconfig()
Implementation of the framegrabber reconfig interface.
dev::frameGrabber< photonCounter > frameGrabberT
void * m_curr_src
Pointer to a function to extract the image data as our desired type realT.
pcf::IndiProperty m_indiP_calibrateSteps
mx::improc::eigenImage< realT > m_dark_image
INDI_NEWCALLBACK_DECL(photonCounter, m_indiP_stackFrames)
mx::improc::eigenCube< realT > m_calibrationCube
realT(* pixget)(void *, size_t)
INDI_NEWCALLBACK_DECL(photonCounter, m_indiP_quantileCut)
pcf::IndiProperty m_indiP_quantileCut
static constexpr bool c_frameGrabber_flippable
app:dev config to tell framegrabber these images can not be flipped
int allocate(const dev::shmimT &dummy)
int startAcquisition()
Implementation of the framegrabber startAcquisition interface.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
@ OPERATING
The device is operating, other than homing.
#define INDI_IDLE
Definition indiUtils.hpp:28
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
Software ERR log entry.