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 // The incoming stream name
73 uint32_t m_modevalWidth {0}; ///< The width of the shmim
74 uint32_t m_modevalHeight {0}; ///< The height of the shmim
76
77 long long frame_counter {0};
78
79 // The predictive control parameters
80 float m_gainCtrl {0.0};
82 float m_gammaCtrl {1.00};
83 float m_covarianceCtrl {100000.0};
84
85 int m_num_modes {1};
86 int m_history {5};
87 int m_future {3};
88
92
94
95 // Process control parameters
96 bool is_learning {false};
98
99 // Learning variables
101 std::vector<int> m_exploration_steps_01;
102 std::vector<float> m_regularization_steps_01;
103
105 std::vector<int> m_exploration_steps_02;
106 std::vector<float> m_regularization_steps_02;
107
108 bool switch_exploration {false};
109 bool use_set_01 {true};
110
111 //
112 std::default_random_engine generator;
113 std::normal_distribution<DDSPC::realT> distribution;
114
115 std::string m_exploration_sequence {""};
116
117 pcf::IndiProperty m_indiP_exploration;
118 pcf::IndiProperty m_indiP_learningToggle;
119 pcf::IndiProperty m_indiP_predictingToggle;
120 pcf::IndiProperty m_indiP_resetToggle;
121
122 public:
123
128
129 /// Default c'tor.
130 loPredCtrl();
131
132 /// D'tor, declared and defined for noexcept.
134 {
135 }
136
137 virtual void setupConfig();
138
139 /// Implementation of loadConfig logic, separated for testing.
140 /** This is called by loadConfig().
141 */
142 int loadConfigImpl(
143 mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
144
145 virtual void loadConfig();
146
147 /// Startup function
148 /**
149 *
150 */
151 virtual int appStartup();
152
153 /// Implementation of the FSM for loPredCtrl.
154 /**
155 * \returns 0 on no critical error
156 * \returns -1 on an error requiring shutdown
157 */
158 virtual int appLogic();
159
160 /// Shutdown the app.
161 /**
162 *
163 */
164 virtual int appShutdown();
165
166 // Custom functions
167 int send_to_shmim();
168
169 protected:
170 int allocate( const dev::shmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
171
172 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
173 const dev::shmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
174 );
175
176 // TODO ::: ADD SAVE AND LOAD FUNCTIONALITY
177 void save(std::string directory);
178 void load(std::string directory);
179 };
180
182 {
183 // Check if processImage is running
184 // while(m_outputStream.md[0].write == 1);
185
186 m_outputStream.md[0].write = 1;
188 m_outputStream.md[0].cnt0++;
189 m_outputStream.md[0].write = 0;
190
192
193 return 0;
194 }
195
196 inline loPredCtrl::loPredCtrl() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
197 {
198 return;
199 }
200
202 {
204 config.add("outputShmim.shmimName", "", "outputShmim.shmimName", argType::Required, "outputShmim", "shmimName", false, "string", "The output shmim to write to.");
205
206 config.add("parameters.gain", "", "parameters.gain", argType::Required, "parameters", "gain", false, "float", "The initial feedback gain.");
207 config.add("parameters.regularization", "", "parameters.regularization", argType::Required, "parameters", "regularization", false, "float", "The regularization parameter.");
208 config.add("parameters.gamma", "", "parameters.gamma", argType::Required, "parameters", "gamma", false, "float", "The forgetting factor.");
209 config.add("parameters.covariance", "", "parameters.covariance", argType::Required, "parameters", "covariance", false, "float", "The initial covariance.");
210
211 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.");
212 config.add("parameters.history", "", "parameters.history", argType::Required, "parameters", "history", false, "int", "The number of past measurements for the prediction.");
213 config.add("parameters.future", "", "parameters.future", argType::Required, "parameters", "future", false, "int", "The number of future steps that are predicted.");
214 }
215
216 inline int loPredCtrl::loadConfigImpl( mx::app::appConfigurator &_config )
217 {
219
220 _config(m_gainCtrl, "parameters.gain");
221 _config(m_regularizationCtrl, "parameters.regularization");
222 _config(m_gammaCtrl, "parameters.gamma");
223 _config(m_covarianceCtrl, "parameters.covariance");
224
225 _config(m_num_modes, "parameters.num_modes");
226 _config(m_history, "parameters.history");
227 _config(m_future, "parameters.future");
228
229 _config(m_outputName, "outputShmim.shmimName");
230
231 std::cout << "Open output channel at " << m_outputName << std::endl;
232 std::cout << "Gain " << m_gainCtrl << std::endl;
233 std::cout << "Regularization " << m_regularizationCtrl << std::endl;
234 std::cout << "Gamma " << m_gammaCtrl << std::endl;
235
236 std::cout << "History " << m_history << std::endl;
237 std::cout << "Future " << m_future << std::endl;
238
239 std::cout << "Done reading config Impl." << std::endl;
240
241 return 0;
242 }
243
245 {
246 loadConfigImpl( config );
247 }
248
250 {
251 if( shmimMonitorT::appStartup() < 0 )
252 {
253 return log<software_error, -1>( { __FILE__, __LINE__ } );
254 }
255
256 CREATE_REG_INDI_NEW_TEXT( m_indiP_exploration, "exploration_sequence", "", "");
257
258 createStandardIndiToggleSw( m_indiP_learningToggle, "learn", "Learning State", "Learn Controls");
260
261 createStandardIndiToggleSw( m_indiP_predictingToggle, "predict", "Predict State", "Predictive Controls");
263
264 createStandardIndiRequestSw( m_indiP_resetToggle, "reset_model", "Reset the RLS model", "Reset Model");
266
267 // state(stateCodes::READY);
269 return 0;
270 }
271
273 {
274 if( shmimMonitorT::appLogic() < 0 )
275 {
276 return log<software_error, -1>( { __FILE__, __LINE__ } );
277 }
278
279 std::unique_lock<std::mutex> lock( m_indiMutex );
280
281 if( shmimMonitorT::updateINDI() < 0 )
282 {
284 }
285
287
288 if(is_learning){
289 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::On, INDI_OK);
290 }else{
291 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
292 }
293
295 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::On, INDI_OK);
296 }else{
297 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
298 }
299
300 return 0;
301 }
302
304 {
306
307 if(controller)
308 delete controller;
309
310 return 0;
311 }
312
313 inline int loPredCtrl::allocate( const dev::shmimT &dummy )
314 {
315 static_cast<void>( dummy ); // be unused
316
319 m_modevalTypeSize = sizeof(realT);
320 std::cout << "m_modevalWidth: " << m_modevalWidth << std::endl;
321 std::cout << "m_modevalHeight: " << m_modevalHeight << std::endl;
322
324 new_command.resize(m_num_modes, 1);
325 new_measurement.resize(m_num_modes, 1);
326
327 generator = std::default_random_engine();
328 distribution = std::normal_distribution<DDSPC::realT>(0.0, 1.0);
329
330 // Allocate the DM
331 if(m_outputOpened){
333 }
334
335 m_outputOpened = false;
336 m_outputRestart = false; //Set this up front, since we're about to restart.
337
339 if(m_outputStream.md[0].sem < 10){
341 }else{
342 m_outputOpened = true;
343 }
344 }
345
346 if(!m_outputOpened){
348 return -1;
349 }else{
350 m_outputWidth = m_outputStream.md->size[0];
351 m_outputHeight = m_outputStream.md->size[1];
352
353 m_outputDataType = m_outputStream.md->datatype;
354 m_outputTypeSize = sizeof(float);
355
356 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);
357 }
358
359
361
362 return 0;
363 }
364
365 inline int loPredCtrl::processImage( void *curr_src, const dev::shmimT &dummy )
366 {
367 // static_cast<void>( dummy ); // be unused
368 // This could be made more efficient by doing only a single copy statement.
369 Eigen::Map<eigenImage<realT>> m_modeval( static_cast<realT *>(curr_src), m_modevalWidth, m_modevalHeight);
370
372 exp_noise.resize(m_num_modes, 1);
373 exp_noise.setZero();
374
377 switch_exploration = false;
378
379 if(use_set_01){
381 }else{
383 }
384 }
385
386 if(use_set_01){
388 for(int i=0; i < m_num_modes; i++){
390 }
391
392 // If no more steps are left pop it!
394 if(m_exploration_steps_01[0] == 0){
397
398 // Erase and apply the next regularization step?
400 if(!m_regularization_steps_01.empty())
402 }
403 }
404 }else{
406 for(int i=0; i < m_num_modes; i++){
408 }
409
410 // If no more steps are left pop it!
412 if(m_exploration_steps_02[0] == 0){
415
416 // Erase and apply the next regularization step?
418 if(!m_regularization_steps_02.empty())
420 }
421 }
422 }
423
424 for(int i=0; i < m_num_modes; i++){
425 new_measurement(i, 0) = m_modeval(i,0);
426 }
427
430 }
431
432 for(int i=0; i < m_modevalWidth; i++){
433 if(i < m_num_modes){
434 full_command(i, 0) = new_command(i, 0);
435 }else{
436 full_command(i, 0) = m_modeval(i,0);
437 }
438 }
439
441
442 if(is_learning){
445 }
446
447 if(frame_counter % 2000 == 0){
448 std::cout << "HOWDY" << std::endl;
449 }
450
452 return 0;
453 }
454
455 INDI_NEWCALLBACK_DEFN( loPredCtrl, m_indiP_exploration )( const pcf::IndiProperty &ipRecv )
456{
457 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_exploration, ipRecv );
458
459 std::string target;
460
461 std::unique_lock<std::mutex> lock( m_indiMutex );
462
463 if( indiTargetUpdate( m_indiP_exploration, target, ipRecv, true ) < 0 )
464 {
465 log<software_error>( { __FILE__, __LINE__ } );
466 return -1;
467 }
468
469 // Now we need to parse the string!
470 m_exploration_sequence = target;
471 std::cout << target << std::endl;
472
473 std::stringstream csvStringStream(m_exploration_sequence);
474 std::string entry;
475
476 int k = 0;
477 while (getline(csvStringStream, entry, ',')){
478 if(k % 3 == 0){
479 // std::cout << std::stoi(entry) << std::endl;
480 if(use_set_01){
481 m_exploration_steps_02.push_back(std::stoi(entry));
482 }else{
483 m_exploration_steps_01.push_back(std::stoi(entry));
484 }
485 }else if(k % 3 == 1){
486 // std::cout << static_cast<DDSPC::realT>() << std::endl;
487 if(use_set_01){
488 m_exploration_noise_strength_02.push_back(std::stod(entry));
489 }else{
490 m_exploration_noise_strength_01.push_back(std::stod(entry));
491 }
492 }else{
493 if(use_set_01){
494 m_regularization_steps_02.push_back(std::stof(entry));
495 }else{
496 m_regularization_steps_01.push_back(std::stof(entry));
497 }
498 }
499 k++;
500 }
501 switch_exploration = true;
502
503 return 0;
504}
505
506INDI_NEWCALLBACK_DEFN(loPredCtrl, m_indiP_learningToggle )(const pcf::IndiProperty &ipRecv)
507{
508 if(ipRecv.getName() != m_indiP_learningToggle.getName())
509 {
510 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
511 return -1;
512 }
513
514 //switch is toggled to on
515 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
516 {
517 if(!is_learning) //is actively learning so change it
518 {
519 is_learning = true;
520 log<text_log>("started learning", logPrio::LOG_NOTICE);
521 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::On, INDI_BUSY);
522
523 }
524 return 0;
525 }
526
527 //switch is toggle to off
528 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
529 {
530 if(is_learning) //is actively learning so change it
531 {
532 is_learning = false;
533 log<text_log>("stopped learning", logPrio::LOG_NOTICE);
534 updateSwitchIfChanged(m_indiP_learningToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
535 }
536 return 0;
537 }
538
539 return 0;
540}
541
542INDI_NEWCALLBACK_DEFN(loPredCtrl, m_indiP_predictingToggle )(const pcf::IndiProperty &ipRecv)
543{
544 if(ipRecv.getName() != m_indiP_predictingToggle.getName())
545 {
546 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
547 return -1;
548 }
549
550 //switch is toggled to on
551 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
552 {
553 if(!is_predictive_control) //is actively learning so change it
554 {
555 is_predictive_control = true;
556 log<text_log>("started predicting", logPrio::LOG_NOTICE);
557 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::On, INDI_BUSY);
558
559 }
560 return 0;
561 }
562
563 //switch is toggle to off
564 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
565 {
566 if(is_predictive_control) //is actively learning so change it
567 {
568 is_predictive_control = false;
569 log<text_log>("stopped predicting", logPrio::LOG_NOTICE);
570 updateSwitchIfChanged(m_indiP_predictingToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
571 }
572 return 0;
573 }
574
575 return 0;
576}
577
578
579INDI_NEWCALLBACK_DEFN(loPredCtrl, m_indiP_resetToggle )(const pcf::IndiProperty &ipRecv)
580{
581 if(ipRecv.getName() != m_indiP_resetToggle.getName())
582 {
583 log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
584 return -1;
585 }
586
587 if(!ipRecv.find("request")) return 0;
588
589 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
590 {
591 std::lock_guard<std::mutex> guard(m_indiMutex);
592 controller->reset();
593 updateSwitchIfChanged(m_indiP_resetToggle, "request", pcf::IndiElement::Off, INDI_IDLE);
594 }
595
596 return 0;
597}
598
599 } // namespace app
600 } // namespace MagAOX
601
602 #endif // loPredCtrl_hpp
603
void set_regularization(realT new_regularization)
Matrix calculate_command(Matrix new_measurement, Matrix exploration_noise)
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
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:26
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
Software ERR log entry.