API
 
Loading...
Searching...
No Matches
loPredCtrl.hpp
Go to the documentation of this file.
1/** \file loPredCtrl.hpp
2 * \brief The MagAO-X generic ImageStreamIO stream integrator
3 *
4 * \ingroup app_files
5 */
6
7#ifndef loPredCtrl_hpp
8#define loPredCtrl_hpp
9
10#include <iostream>
11#include <fstream>
12#include <vector>
13#include <limits>
14#include <chrono>
15#include <thread>
16#include <random>
17
18#include <Eigen/Dense>
19#include <mx/improc/eigenCube.hpp>
20#include <mx/improc/eigenImage.hpp>
21using namespace mx::improc;
22
23#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
24#include "../../magaox_git_version.h"
25
26#include "ar_controller.hpp"
27
28 // #define MAGAOX_CURRENT_SHA1 0
29 // #define MAGAOX_REPO_MODIFIED 0
30 namespace MagAOX
31 {
32 namespace app
33 {
34
35 class loPredCtrl : public MagAOXApp<true>, public dev::shmimMonitor<loPredCtrl>
36 {
37 // Give the test harness access.
38 friend class loPredCtrl_test;
39
40 friend class dev::shmimMonitor<loPredCtrl>;
41
42 // The base shmimMonitor type
44
45 /// Floating point type in which to do all calculations.
46 typedef float realT;
47
48 public:
49 /** \name app::dev Configurations
50 *@{
51 */
52
53 ///@}
54
55 protected:
56 /** \name Configurable Parameters
57 *@{
58 */
59
60 // variables for sending the output to an output shmim.
61 std::string m_outputName;
63 uint32_t m_outputWidth {0}; ///< The width of the image
64 uint32_t m_outputHeight {0}; ///< The height of the image.
65
66 uint8_t m_outputDataType{0}; ///< The ImageStreamIO type code.
67 size_t m_outputTypeSize {0}; ///< The size of the type, in bytes.
68
69 bool m_outputOpened {false};
70 bool m_outputRestart {false};
71
72
73 // The incoming stream name
74 uint32_t m_modevalWidth {0}; ///< The width of the shmim
75 uint32_t m_modevalHeight {0}; ///< The height of the shmim
77
78 long long frame_counter {0};
79
80 // The predictive control parameters
81 float m_gainCtrl {0.0};
83 float m_gammaCtrl {1.00};
84 float m_covarianceCtrl {100000.0};
85
86 int m_num_modes {1};
87 int m_history {5};
88 int m_future {3};
89
93
95
96 // Process control parameters
97 bool is_learning {false};
99
100 // Learning variables
102 std::vector<int> m_exploration_steps_01;
103 std::vector<float> m_regularization_steps_01;
104
106 std::vector<int> m_exploration_steps_02;
107 std::vector<float> m_regularization_steps_02;
108
109 bool switch_exploration {false};
110 bool use_set_01 {true};
111 bool do_reset_model {false};
112
113 //
114 std::default_random_engine generator;
115 std::normal_distribution<DDSPC::realT> distribution;
116
117 std::string m_exploration_sequence {""};
118
119 pcf::IndiProperty m_indiP_exploration;
120 pcf::IndiProperty m_indiP_learningToggle;
121 pcf::IndiProperty m_indiP_predictingToggle;
122 pcf::IndiProperty m_indiP_resetToggle;
123
124 public:
125
130
131 /// Default c'tor.
132 loPredCtrl();
133
134 /// D'tor, declared and defined for noexcept.
136 {
137 }
138
139 virtual void setupConfig();
140
141 /// Implementation of loadConfig logic, separated for testing.
142 /** This is called by loadConfig().
143 */
144 int loadConfigImpl(
145 mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
146
147 virtual void loadConfig();
148
149 /// Startup function
150 /**
151 *
152 */
153 virtual int appStartup();
154
155 /// Implementation of the FSM for loPredCtrl.
156 /**
157 * \returns 0 on no critical error
158 * \returns -1 on an error requiring shutdown
159 */
160 virtual int appLogic();
161
162 /// Shutdown the app.
163 /**
164 *
165 */
166 virtual int appShutdown();
167
168 // Custom functions
169 int send_to_shmim();
170
171 protected:
172 int allocate( const dev::shmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
173
174 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
175 const dev::shmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
176 );
177
178 // TODO ::: ADD SAVE AND LOAD FUNCTIONALITY
179 void save(std::string directory);
180 void load(std::string directory);
181 };
182
184 {
185 // Check if processImage is running
186 // while(m_outputStream.md[0].write == 1);
187
188 // m_outputStream.md[0].write = 1;
189 // memcpy( m_outputStream.array.raw, full_command.data(), m_modevalWidth * m_modevalTypeSize );
190 // m_outputStream.md[0].cnt0++;
191 // m_outputStream.md[0].write = 0;
192
193 //ImageStreamIO_sempost( &m_outputStream, -1 );
194
195 return 0;
196 }
197
198 inline loPredCtrl::loPredCtrl() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
199 {
200 return;
201 }
202
204 {
206 config.add("outputShmim.shmimName", "", "outputShmim.shmimName", argType::Required, "outputShmim", "shmimName", false, "string", "The output shmim to write to.");
207
208 config.add("parameters.gain", "", "parameters.gain", argType::Required, "parameters", "gain", false, "float", "The initial feedback gain.");
209 config.add("parameters.regularization", "", "parameters.regularization", argType::Required, "parameters", "regularization", false, "float", "The regularization parameter.");
210 config.add("parameters.gamma", "", "parameters.gamma", argType::Required, "parameters", "gamma", false, "float", "The forgetting factor.");
211 config.add("parameters.covariance", "", "parameters.covariance", argType::Required, "parameters", "covariance", false, "float", "The initial covariance.");
212
213 config.add("parameters.num_modes", "", "parameters.num_modes", argType::Required, "parameters", "num_modes", false, "int", "The number of modes that will be controlled through predictive control.");
214 config.add("parameters.history", "", "parameters.history", argType::Required, "parameters", "history", false, "int", "The number of past measurements for the prediction.");
215 config.add("parameters.future", "", "parameters.future", argType::Required, "parameters", "future", false, "int", "The number of future steps that are predicted.");
216 }
217
218 inline int loPredCtrl::loadConfigImpl( mx::app::appConfigurator &_config )
219 {
221
222 _config(m_gainCtrl, "parameters.gain");
223 _config(m_regularizationCtrl, "parameters.regularization");
224 _config(m_gammaCtrl, "parameters.gamma");
225 _config(m_covarianceCtrl, "parameters.covariance");
226
227 _config(m_num_modes, "parameters.num_modes");
228 _config(m_history, "parameters.history");
229 _config(m_future, "parameters.future");
230
231 _config(m_outputName, "outputShmim.shmimName");
232
233 std::cout << "Open output channel at " << m_outputName << std::endl;
234 std::cout << "Gain " << m_gainCtrl << std::endl;
235 std::cout << "Regularization " << m_regularizationCtrl << std::endl;
236 std::cout << "Gamma " << m_gammaCtrl << std::endl;
237
238 std::cout << "History " << m_history << std::endl;
239 std::cout << "Future " << m_future << std::endl;
240
241 std::cout << "Done reading config Impl." << std::endl;
242
243 return 0;
244 }
245
247 {
248 loadConfigImpl( config );
249 }
250
252 {
253 if( shmimMonitorT::appStartup() < 0 )
254 {
255 return log<software_error, -1>( { __FILE__, __LINE__ } );
256 }
257
258 CREATE_REG_INDI_NEW_TEXT( m_indiP_exploration, "exploration_sequence", "", "");
259
260 createStandardIndiToggleSw( m_indiP_learningToggle, "learn", "Learning State", "Learn Controls");
262
263 createStandardIndiToggleSw( m_indiP_predictingToggle, "predict", "Predict State", "Predictive Controls");
265
266 createStandardIndiRequestSw( m_indiP_resetToggle, "reset_model", "Reset the RLS model", "Reset Model");
268
269 // state(stateCodes::READY);
271 return 0;
272 }
273
275 {
276 if( shmimMonitorT::appLogic() < 0 )
277 {
278 return log<software_error, -1>( { __FILE__, __LINE__ } );
279 }
280
281 std::unique_lock<std::mutex> lock( m_indiMutex );
282
283 if( shmimMonitorT::updateINDI() < 0 )
284 {
286 }
287
289
290 if(is_learning){
291 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::On, INDI_OK);
292 }else{
293 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
294 }
295
297 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::On, INDI_OK);
298 }else{
299 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
300 }
301
302 return 0;
303 }
304
306 {
308
309 if(controller)
310 delete controller;
311
312 return 0;
313 }
314
315 inline int loPredCtrl::allocate( const dev::shmimT &dummy )
316 {
317 static_cast<void>( dummy ); // be unused
318
321 m_modevalTypeSize = sizeof(realT);
322 std::cout << "m_modevalWidth: " << m_modevalWidth << std::endl;
323 std::cout << "m_modevalHeight: " << m_modevalHeight << std::endl;
324
326 new_command.resize(m_num_modes, 1);
327 new_measurement.resize(m_num_modes, 1);
328
329 generator = std::default_random_engine();
330 distribution = std::normal_distribution<DDSPC::realT>(0.0, 1.0);
331
332 /*
333 // Allocate the DM
334 if(m_outputOpened){
335 ImageStreamIO_closeIm(&m_outputStream);
336 }
337
338 m_outputOpened = false;
339 m_outputRestart = false; //Set this up front, since we're about to restart.
340
341 if( ImageStreamIO_openIm(&m_outputStream, m_outputName.c_str()) == 0){
342 if(m_outputStream.md[0].sem < 10){
343 ImageStreamIO_closeIm(&m_outputStream);
344 }else{
345 m_outputOpened = true;
346 }
347 }
348
349 if(!m_outputOpened){
350 log<text_log>( m_outputName + " not opened.", logPrio::LOG_NOTICE);
351 return -1;
352 }else{
353 m_outputWidth = m_outputStream.md->size[0];
354 m_outputHeight = m_outputStream.md->size[1];
355
356 m_outputDataType = m_outputStream.md->datatype;
357 m_outputTypeSize = sizeof(float);
358
359 log<text_log>( "Opened " + m_outputName + " " + std::to_string(m_outputWidth) + " x " + std::to_string(m_outputHeight) + " with data type: " + std::to_string(m_outputDataType), logPrio::LOG_NOTICE);
360 }
361 */
362
364
365 return 0;
366 }
367
368 inline int loPredCtrl::processImage( void *curr_src, const dev::shmimT &dummy )
369 {
370 // static_cast<void>( dummy ); // be unused
371 // This could be made more efficient by doing only a single copy statement.
372 Eigen::Map<eigenImage<realT>> m_modeval( static_cast<realT *>(curr_src), m_modevalWidth, m_modevalHeight);
373
375 exp_noise.resize(m_num_modes, 1);
376 exp_noise.setZero();
377
378 if(do_reset_model){
379 controller->reset();
380 do_reset_model = false;
381 }
382
383
386 switch_exploration = false;
387
388 if(use_set_01){
390 }else{
392 }
393 }
394
395 if(use_set_01){
397 for(int i=0; i < m_num_modes; i++){
399 }
400
401 // If no more steps are left pop it!
403 if(m_exploration_steps_01[0] == 0){
406
407 // Erase and apply the next regularization step?
409 if(!m_regularization_steps_01.empty())
411 }
412 }
413 }else{
415 for(int i=0; i < m_num_modes; i++){
417 }
418
419 // If no more steps are left pop it!
421 if(m_exploration_steps_02[0] == 0){
424
425 // Erase and apply the next regularization step?
427 if(!m_regularization_steps_02.empty())
429 }
430 }
431 }
432
433
434 for(int i=0; i < m_num_modes; i++){
435 new_measurement(i, 0) = m_modeval(i,0);
436 }
437
440 }
441
442 for(int i=0; i < m_modevalWidth; i++){
443 if(i < m_num_modes){
444 full_command(i, 0) = new_command(i, 0);
445 }else{
446 full_command(i, 0) = m_modeval(i,0);
447 }
448 }
449
450 // send_to_shmim();
451
452 if(is_learning){
455 }
456
457 if(frame_counter % 20 == 0){
458 std::cout << "HOWDY" << std::endl;
459 }
460
462 return 0;
463 }
464
465 INDI_NEWCALLBACK_DEFN( loPredCtrl, m_indiP_exploration )( const pcf::IndiProperty &ipRecv )
466{
467 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_exploration, ipRecv );
468 // Called in indi like: num_explore, std, regularization, num_explore, std, regularization, ....
469
470 std::string target;
471
472 std::unique_lock<std::mutex> lock( m_indiMutex );
473
474 if( indiTargetUpdate( m_indiP_exploration, target, ipRecv, true ) < 0 )
475 {
476 log<software_error>( { __FILE__, __LINE__ } );
477 return -1;
478 }
479
480 // Now we need to parse the string!
481 m_exploration_sequence = target;
482 std::cout << target << std::endl;
483
484 std::stringstream csvStringStream(m_exploration_sequence);
485 std::string entry;
486
487 int k = 0;
488 while (getline(csvStringStream, entry, ',')){
489 if(k % 3 == 0){
490 // std::cout << std::stoi(entry) << std::endl;
491 if(use_set_01){
492 m_exploration_steps_02.push_back(std::stoi(entry));
493 }else{
494 m_exploration_steps_01.push_back(std::stoi(entry));
495 }
496 }else if(k % 3 == 1){
497 // std::cout << static_cast<DDSPC::realT>() << std::endl;
498 if(use_set_01){
499 m_exploration_noise_strength_02.push_back(std::stod(entry));
500 }else{
501 m_exploration_noise_strength_01.push_back(std::stod(entry));
502 }
503 }else{
504 if(use_set_01){
505 m_regularization_steps_02.push_back(std::stof(entry));
506 }else{
507 m_regularization_steps_01.push_back(std::stof(entry));
508 }
509 }
510 k++;
511 }
512 switch_exploration = true;
513
514 return 0;
515}
516
517INDI_NEWCALLBACK_DEFN(loPredCtrl, m_indiP_learningToggle )(const pcf::IndiProperty &ipRecv)
518{
519 if(ipRecv.getName() != m_indiP_learningToggle.getName())
520 {
521 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
522 return -1;
523 }
524
525 //switch is toggled to on
526 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
527 {
528 if(!is_learning) //is actively learning so change it
529 {
530 is_learning = true;
531 log<text_log>("started learning", logPrio::LOG_NOTICE);
532 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::On, INDI_BUSY);
533 }
534 return 0;
535 }
536
537 //switch is toggle to off
538 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
539 {
540 if(is_learning) //is actively learning so change it
541 {
542 is_learning = false;
543 log<text_log>("stopped learning", logPrio::LOG_NOTICE);
544 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
545 }
546 return 0;
547 }
548
549 return 0;
550}
551
552INDI_NEWCALLBACK_DEFN(loPredCtrl, m_indiP_predictingToggle )(const pcf::IndiProperty &ipRecv)
553{
554 if(ipRecv.getName() != m_indiP_predictingToggle.getName())
555 {
556 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
557 return -1;
558 }
559
560 //switch is toggled to on
561 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
562 {
563 if(!is_predictive_control) //is actively learning so change it
564 {
565 is_predictive_control = true;
566 log<text_log>("started predicting", logPrio::LOG_NOTICE);
567 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::On, INDI_BUSY);
568
569 }
570 return 0;
571 }
572
573 //switch is toggle to off
574 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
575 {
576 if(is_predictive_control) //is actively learning so change it
577 {
578 is_predictive_control = false;
579 log<text_log>("stopped predicting", logPrio::LOG_NOTICE);
580 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
581 }
582 return 0;
583 }
584
585 return 0;
586}
587
588
589INDI_NEWCALLBACK_DEFN(loPredCtrl, m_indiP_resetToggle )(const pcf::IndiProperty &ipRecv)
590{
591 if(ipRecv.getName() != m_indiP_resetToggle.getName())
592 {
593 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
594 return -1;
595 }
596
597 if(!ipRecv.find("request")) return 0;
598
599 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
600 {
601 std::lock_guard<std::mutex> guard(m_indiMutex);
602
603 //controller->reset();
604 do_reset_model = true;
605 log<text_log>("request reset.", logPrio::LOG_NOTICE);
606 updateSwitchIfChanged(m_indiP_resetToggle, "request", pcf::IndiElement::Off, INDI_IDLE);
607 }
608
609 return 0;
610}
611
612 } // namespace app
613 } // namespace MagAOX
614
615 #endif // loPredCtrl_hpp
void set_regularization(realT new_regularization)
Matrix calculate_command(Matrix new_measurement, Matrix exploration_noise)
The base-class for XWCTk applications.
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.
int createStandardIndiToggleSw(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 toggle element.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
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.
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.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
INDI_NEWCALLBACK_DECL(loPredCtrl, m_indiP_resetToggle)
friend class loPredCtrl_test
pcf::IndiProperty m_indiP_exploration
DDSPC::PredictiveController * controller
INDI_NEWCALLBACK_DECL(loPredCtrl, m_indiP_learningToggle)
std::string m_exploration_sequence
std::vector< float > m_exploration_noise_strength_01
pcf::IndiProperty m_indiP_learningToggle
std::vector< float > m_regularization_steps_02
void load(std::string directory)
~loPredCtrl() noexcept
D'tor, declared and defined for noexcept.
float realT
Floating point type in which to do all calculations.
loPredCtrl()
Default c'tor.
size_t m_outputTypeSize
The size of the type, in bytes.
uint8_t m_outputDataType
The ImageStreamIO type code.
INDI_NEWCALLBACK_DECL(loPredCtrl, m_indiP_exploration)
std::vector< float > m_regularization_steps_01
virtual int appLogic()
Implementation of the FSM for loPredCtrl.
DDSPC::Matrix new_command
std::normal_distribution< DDSPC::realT > distribution
pcf::IndiProperty m_indiP_predictingToggle
int processImage(void *curr_src, const dev::shmimT &dummy)
virtual void loadConfig()
dev::shmimMonitor< loPredCtrl > shmimMonitorT
void save(std::string directory)
std::vector< float > m_exploration_noise_strength_02
std::default_random_engine generator
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
uint32_t m_modevalWidth
The width of the shmim.
virtual int appShutdown()
Shutdown the app.
INDI_NEWCALLBACK_DECL(loPredCtrl, m_indiP_predictingToggle)
virtual void setupConfig()
uint32_t m_modevalHeight
The height of the shmim.
virtual int appStartup()
Startup function.
std::vector< int > m_exploration_steps_01
uint32_t m_outputWidth
The width of the image.
DDSPC::Matrix new_measurement
uint32_t m_outputHeight
The height of the image.
int allocate(const dev::shmimT &dummy)
std::vector< int > m_exploration_steps_02
pcf::IndiProperty m_indiP_resetToggle
DDSPC::Matrix full_command
#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.
#define CREATE_REG_INDI_NEW_TEXT(prop, name, label, group)
Create and register a NEW INDI property as a standard text, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:27
#define INDI_BUSY
Definition indiUtils.hpp:29
#define INDI_OK
Definition indiUtils.hpp:28
Eigen::Matrix< realT, Eigen::Dynamic, Eigen::Dynamic > Matrix
Definition utils.hpp:12
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:19
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
Software ERR log entry.