API
ttmModulator.hpp
Go to the documentation of this file.
1 
2 
3 #ifndef ttmModulator_hpp
4 #define ttmModulator_hpp
5 
6 
7 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
8 #include "../../magaox_git_version.h"
9 
10 
11 #define MODSTATE_UNKNOWN (-1)
12 #define MODSTATE_OFF (0)
13 #define MODSTATE_REST (1)
14 #define MODSTATE_MIDSET (2)
15 #define MODSTATE_SET (3)
16 #define MODSTATE_MODULATING (4)
17 
18 namespace MagAOX
19 {
20 namespace app
21 {
22 
23 /** MagAO-X application to control TTM modulation
24  *
25  * \todo need tests fo ttmModulator
26  */
27 class ttmModulator : public MagAOXApp<>
28 {
29 
30 protected:
31 
32  /** \name Configurable Parameters
33  * @{
34  */
35 
36  double m_maxFreq {3000.0}; ///< The maximum modulation frequency settable by this program
37  double m_maxVolt {1.2801}; ///< The maximum modulation voltage settable by this program
38 
39  double m_setVoltage_1 {5.0}; ///< the set position voltage of Ch. 1.
40  double m_setVoltage_2 {5.0}; ///< the set position voltage of Ch. 2.
41 
42  double m_setDVolts {1.0}; ///< The setting ramp step size [volts].
43 
44  double m_modDFreq {500}; ///< The modulation ramp frequency step size [Hz].
45  double m_modDVolts {0.5}; ///< The modulation ramp voltage step size [Volts].
46 
47  double m_rotAngle {0};
48  double m_rotParity {1};
49 
50  ///@}
51 
52  int m_modState {MODSTATE_UNKNOWN}; ///< -1 = unknown, 0 = off, 1 = rest, 2 = midset, 3 = set, 4 = modulating
53  int m_modStateRequested {MODSTATE_UNKNOWN}; ///< The requested TTM state
54  double m_modRad {0}; ///< The current modulation radius, in lam/D.
55  double m_modRadRequested {-1}; ///< The requested modulation radius, in lam/D.
56  double m_modFreq {0}; ///< The current modulation frequency, in Hz.
57  double m_modFreqRequested {-1}; ///< The requested modulation frequency, in Hz.
58 
59 
60  int m_C1outp {-1}; ///< Output state of fxn gen channel 1.
61  double m_C1freq {-1}; ///< Frequency of fxn gen channel 1.
62  double m_C1volts {-1}; ///< Voltage p2p of fxn gen channel 1.
63  double m_C1ofst {-1}; ///< DC offset of fxn gen channel 1.
64  double m_C1phse {-1}; ///< Phase of fxn gen channel 1.
65 
66  int m_C2outp {-1}; ///< Output state of fxn gen channel 2
67  double m_C2freq {-1}; ///< Frequency of fxn gen channel 2.
68  double m_C2volts {-1}; ///< Voltage p2p of fxn gen channel 2.
69  double m_C2ofst {-1}; ///< DC offset of fxn gen channel 2.
70  double m_C2phse {-1}; ///< Phase of fxn gen channel 2.
71 
72  double m_calRadius {1.0};
73 
74  /* Old Cal:
75  std::vector<double> m_calFreqs = {100,400,500,600,700,800,900,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000};
76  std::vector<double> m_calC1Amps = {0.61,0.61,0.57,0.55,0.53,0.51,0.51,0.49,0.46,0.44,0.44,0.43,0.44,0.46,0.49,0.54,0.58,0.62};
77  std::vector<double> m_calC2Amps = {0.6,0.6,0.6,0.57,0.55,0.53,0.51,0.49,0.49,0.49,0.49,0.5,0.52,0.54,0.59,0.64,0.70,0.77};
78  std::vector<double> m_calC2Phse = { 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 72, 72, 70, 70, 70} ;*/
79 
80  /* Cal on 2022-09-18:
81  std::vector<double> m_calFreqs = { 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, 3500};
82  std::vector<double> m_calC1Amps = {0.66, 0.62, 0.58, 0.49, 0.44, 0.41, 0.43, 0.41, 0.35, 0.75, 1.0, 1.03, 1.08, 1.58 };
83  std::vector<double> m_calC2Amps = {0.64, 0.61, 0.56, 0.54, 0.55, 0.57, 0.65, 0.78, 0.95, 1.15, 1.15, 2.25, 2.15, 1.97};
84  std::vector<double> m_calC2Phse = { 75, 75, 75, 75, 75, 75, 72, 67, 63, 35, 5, 18, 10, -40} ; */
85 
86  /* Cal on 2023-12-03 (w. strain gauges ON):*/
87  /*std::vector<double> m_calFreqs = { 100, 250, 500, 750, 1000, 1250, 1500, 1750, 2000};
88  std::vector<double> m_calC1Amps = {0.22, 0.28, 0.43, 0.61, 0.82, 1.06, 1.35, 1.70, 2.045};
89  std::vector<double> m_calC2Amps = {0.23, 0.23, 0.56, 0.85, 1.16, 1.58, 1.96, 2.60, 3.63};
90  std::vector<double> m_calC2Phse = { 79, 82, 82, 82, 82, 82, 84, 88, 93}; */
91 
92  /* Cal on 2023-12-03 (w. strain gauges OFF):*/
93  std::vector<double> m_calFreqs = { 100, 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000};
94  std::vector<double> m_calC1Amps = {0.310, 0.317, 0.327, 0.327, 0.333, 0.333, 0.343, 0.350, 0.373, 0.39, 0.405, 0.425, 0.435};
95  std::vector<double> m_calC2Amps = {0.313, 0.317, 0.327, 0.340, 0.357, 0.363, 0.380, 0.407, 0.426667, 0.45, 0.475, 0.490, 0.510}; //have to go to this many sig-figs for max voltage reasons
96  std::vector<double> m_calC2Phse = { 74, 74, 74, 74, 74, 71, 71, 68, 68, 68, 68, 68, 68};
97 
98 public:
99 
100  /// Default c'tor.
101  ttmModulator();
102 
103  /// D'tor, declared and defined for noexcept.
104  ~ttmModulator() noexcept
105  {}
106 
107  /// Setup the configuration system (called by MagAOXApp::setup())
108  virtual void setupConfig();
109 
110  /// load the configuration system results (called by MagAOXApp::setup())
111  virtual void loadConfig();
112 
113  /// Startup functions
114  /** Setsup the INDI vars.
115  *
116  * \returns 0 on success
117  * \returns -1 on error.
118  */
119  virtual int appStartup();
120 
121  /// Implementation of the FSM for the TTM Modulator
122  /**
123  * \returns 0 on success
124  * \returns -1 on error.
125  */
126  virtual int appLogic();
127 
128  /// Do any needed shutdown tasks. Currently nothing in this app.
129  /**
130  * \returns 0 on success
131  * \returns -1 on error.
132  */
133  virtual int appShutdown();
134 
135 
136  /// Calculate the state of the modulator from the fxn gen params.
137  /**
138  * \returns 0 on success
139  * \returns -1 on error.
140  */
141  int calcState();
142 
143  /// Rest the TTM
144  /**
145  * \returns 0 on success
146  * \returns -1 on error.
147  */
148  int restTTM();
149 
150  /// Set the TTM
151  /**
152  * \returns 0 on success
153  * \returns -1 on error.
154  */
155  int setTTM();
156 
157  /// Begin modulating or modify current modulation parameters.
158  /**
159  * \returns 0 on success
160  * \returns -1 on error.
161  */
162  int modTTM( double newRad, ///< The new radius for modulation [lam/D]
163  double newFreq ///< The new frequency for modulation [Hz]
164  );
165 
166  int offset12( double d1,
167  double d2
168  );
169 
170  int offsetXY( double dx,
171  double dy
172  );
173 
174 protected:
175 
176  //declare our properties
177  pcf::IndiProperty m_indiP_modState;
178 
179  pcf::IndiProperty m_indiP_modRadius;
180  pcf::IndiProperty m_indiP_modFrequency;
181 
182  pcf::IndiProperty m_indiP_offset12;
183  pcf::IndiProperty m_indiP_offset;
184 
185 
186  pcf::IndiProperty m_indiP_FGState;
187 
188  pcf::IndiProperty m_indiP_C1outp;
189  pcf::IndiProperty m_indiP_C1freq;
190  pcf::IndiProperty m_indiP_C1volts;
191  pcf::IndiProperty m_indiP_C1ofst;
192  pcf::IndiProperty m_indiP_C1phse;
193 
194  pcf::IndiProperty m_indiP_C2outp;
195  pcf::IndiProperty m_indiP_C2freq;
196  pcf::IndiProperty m_indiP_C2volts;
197  pcf::IndiProperty m_indiP_C2ofst;
198  pcf::IndiProperty m_indiP_C2phse;
199 
200 public:
206 
212 
218 
219 };
220 
221 inline
222 ttmModulator::ttmModulator() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
223 {
224  m_powerMgtEnabled = true;
225  return;
226 }
227 
228 inline
230 {
231  config.add("limits.maxfreq", "", "limits.maxfreq", argType::Required, "limits", "maxfreq", false, "real", "The maximum frequency [Hz] which can be set through this program.");
232  config.add("limits.maxamp", "", "limits.maxamp", argType::Required, "limits", "maxamp", false, "real", "The maximum amplitude [lam/D] which can be set throught this program.");
233 
234  config.add("cal.voltsperld1", "", "cal.voltsperld1", argType::Required, "cal", "voltsperld1", false, "real", "The voltage per lam/D for channel 1.");
235  config.add("cal.voltsperld2", "", "cal.voltsperld2", argType::Required, "cal", "voltsperld2", false, "real", "The voltage per lam/D for channel 2.");
236  config.add("cal.phase", "", "cal.phase", argType::Required, "cal", "phase", false, "real", "The axis phase offset, which is applied to channel 2.");
237 
238  config.add("cal.setv1", "", "cal.setv1", argType::Required, "cal", "setv1", false, "real", "The set position voltage of chaannel 1.");
239  config.add("cal.setv2", "", "cal.setv2", argType::Required, "cal", "setv2", false, "real", "The set position voltage of chaannel 2.");
240 
241  config.add("cal.setDvolts", "", "cal.setDvolts", argType::Required, "cal", "setDvolts", false, "real", "The setting ramp step size [Volts]");
242 
243  config.add("cal.modDfreq", "", "cal.modDfreq", argType::Required, "cal", "modDfreq", false, "real", "The modulation ramp frequency step size [Hz]");
244  config.add("cal.modDvolts", "", "cal.modDvolts", argType::Required, "cal", "modDvolts", false, "real", "The modulation ramp voltage step size [Volts]");
245 
246  config.add("cal.rotAngle", "", "cal.rotAngle", argType::Required, "cal", "rotAngle", false, "real", "The offset rotation matrix angle in degrees.");
247  config.add("cal.rotParity", "", "cal.rotParity", argType::Required, "cal", "rotParity", false, "real", "The offset rotation matrix parity, +1 or -1.");
248 }
249 
250 inline
252 {
253  config(m_maxFreq, "limits.maxfreq");
254 
255  config(m_setVoltage_1, "cal.setv1");
256  config(m_setVoltage_2, "cal.setv2");
257 
258  config(m_setDVolts, "cal.setDvolts");
259  config(m_modDFreq, "cal.modDfreq");
260  config(m_modDVolts, "cal.modDvolts");
261 
262  config(m_rotAngle, "cal.rotAngle");
263  m_rotAngle = m_rotAngle*3.14159/180.;
264 
265  config(m_rotParity, "cal.rotParity");
266  if(m_rotParity < 0) m_rotParity = -1;
267  else m_rotParity = 1;
268 
269 
270 }
271 
272 inline
274 {
275  // set up the INDI properties
276  REG_INDI_NEWPROP(m_indiP_modState, "modState", pcf::IndiProperty::Number);
277  m_indiP_modState.add (pcf::IndiElement("current"));
278  m_indiP_modState.add (pcf::IndiElement("target"));
279  m_indiP_modState["current"].set(m_modState);
280  m_indiP_modState["target"].set(m_modStateRequested);
281 
282  REG_INDI_NEWPROP(m_indiP_modFrequency, "modFrequency", pcf::IndiProperty::Number);
283  m_indiP_modFrequency.add (pcf::IndiElement("current"));
284  m_indiP_modFrequency.add (pcf::IndiElement("target"));
285  m_indiP_modFrequency["current"].set(m_modFreq);
287 
288  REG_INDI_NEWPROP(m_indiP_modRadius, "modRadius", pcf::IndiProperty::Number);
289  m_indiP_modRadius.add (pcf::IndiElement("current"));
290  m_indiP_modRadius.add (pcf::IndiElement("target"));
291  m_indiP_modRadius["current"].set(m_modRad);
292  m_indiP_modRadius["target"].set(m_modRadRequested);
293 
294  REG_INDI_NEWPROP(m_indiP_offset12, "offset12", pcf::IndiProperty::Number);
295  m_indiP_offset12.add (pcf::IndiElement("dC1"));
296  m_indiP_offset12.add (pcf::IndiElement("dC2"));
297 
298  REG_INDI_NEWPROP(m_indiP_offset, "offset", pcf::IndiProperty::Number);
299  m_indiP_offset.add (pcf::IndiElement("x"));
300  m_indiP_offset.add (pcf::IndiElement("y"));
301 
302  REG_INDI_SETPROP(m_indiP_C1outp, "fxngenmodwfs", "C1outp");
303  REG_INDI_SETPROP(m_indiP_C1freq, "fxngenmodwfs", "C1freq");
304  REG_INDI_SETPROP(m_indiP_C1volts, "fxngenmodwfs", "C1amp");
305  REG_INDI_SETPROP(m_indiP_C1ofst, "fxngenmodwfs", "C1ofst");
306  REG_INDI_SETPROP(m_indiP_C1phse, "fxngenmodwfs", "C1phse");
307 
308  REG_INDI_SETPROP(m_indiP_C2outp, "fxngenmodwfs", "C2outp");
309  REG_INDI_SETPROP(m_indiP_C2freq, "fxngenmodwfs", "C2freq");
310  REG_INDI_SETPROP(m_indiP_C2volts, "fxngenmodwfs", "C2amp");
311  REG_INDI_SETPROP(m_indiP_C2ofst, "fxngenmodwfs", "C2ofst");
312  REG_INDI_SETPROP(m_indiP_C2phse, "fxngenmodwfs", "C2phse");
313 
314  return 0;
315 }
316 
317 inline
319 {
320  if(state()==stateCodes::POWEROFF) return 0;
321 
323  {
324  sleep(2);
325  }
326 
327  if( calcState() < 0 )
328  {
329  //application failure if we can't determine state
330  log<software_critical>({__FILE__,__LINE__});
331  return -1;
332  }
333 
335  {
337  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
338  }
339  else if(m_modState == MODSTATE_SET)
340  {
342  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
343  }
344  else if(m_modState == MODSTATE_MIDSET)
345  {
347  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
348  }
349  else if(m_modState == MODSTATE_MODULATING)
350  {
352  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
353  }
354 
355  { //mutex scope
356  std::lock_guard<std::mutex> lock(m_indiMutex);
363  }
364 
365  //This is set by an INDI newProperty
366  if(m_modStateRequested > 0)
367  {
368  //Step 0: change the requested state to match, so a new request while we're
369  // processing gets handled.
370 
371  std::unique_lock<std::mutex> lock(m_indiMutex);
372  int newState = m_modStateRequested;
373  double newRad = m_modRadRequested;
374  double newFreq = m_modFreqRequested;
375 
377 
378  lock.unlock();
379 
381  if(newState == MODSTATE_REST) restTTM();
382  if(newState == MODSTATE_SET) setTTM();
383  if(newState == MODSTATE_MODULATING)
384  {
385  if(newRad <= 0.1 || newFreq <= 1)
386  {
387  log<text_log>("radius or frequency too low", logPrio::LOG_ERROR);
388  }
389  else
390  {
391  modTTM(newRad, newFreq);
392  }
393  }
394  calcState();
395 
396  //Do this now for responsiveness.
398  {
400  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
401  }
402  else if(m_modState == MODSTATE_SET)
403  {
405  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
406  }
407  else if(m_modState == MODSTATE_MIDSET)
408  {
410  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
411  }
412  else if(m_modState == MODSTATE_MODULATING)
413  {
415  if(!stateLogged()) log<ttmmod_params>({(uint8_t) m_modState, m_modFreq, m_modRad, 0,0});
416  }
417 
418  }
419  return 0;
420 
421 }
422 
423 
424 
425 inline
427 {
428  //don't bother
429  return 0;
430 }
431 
432 inline
434 {
435  //Need TTM power state here.
436 
437  if( m_C1outp < 1 || m_C2outp < 1 ) //At least one channel off
438  {
439  //Need to also check fxn gen pwr state here
441  }
442  else if( (m_C1freq == 0 || m_C1volts <= 0.002) && (m_C2freq == 0 || m_C2volts <= 0.002) )
443  {
444  //To be set:
445  // -- sine wave freq is 0 or amp is 0.002
446  // -- offset V is at setVoltage
447  // -- phase is 0
448  if(/*m_C1ofst == m_setVoltage_1 && m_C2ofst == m_setVoltage_2 &&*/ m_C1phse == 0 && m_C2phse == 0 )
449  {
451  }
452  else
453  {
454  m_modState = MODSTATE_MIDSET; //must be setting
455  }
456  }
457  else
458  {
459  if(m_C1freq != m_C2freq)
460  {
462  }
463  else
464  {
465  //Possibly some more checks
467 
468  //Interpolate on C1.
469  size_t ngt = 0;
470 
471  for(ngt = 0; ngt < m_calFreqs.size(); ++ngt)
472  {
473  if( m_calFreqs[ngt] >= m_modFreq) break;
474  }
475 
476  double terpC1Amp;
477  if(ngt == 0 || m_calFreqs[ngt] == m_modFreq)
478  {
479  terpC1Amp = m_calC1Amps[ngt];
480  }
481  else
482  {
483  size_t nlt = ngt -1;
484  double dfreq = (m_modFreq - m_calFreqs[nlt])/(m_calFreqs[ngt]-m_calFreqs[nlt]);
485  terpC1Amp = m_calC1Amps[nlt] + (m_calC1Amps[ngt]-m_calC1Amps[nlt])*dfreq;
486 
487  }
488 
489  m_modRad = m_C1volts/terpC1Amp * m_calRadius;
490 
492  }
493  }
494 
495  return 0;
496 }
497 
498 void nanoSleep( unsigned long nsec )
499 {
500  std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>(nsec));
501 }
502 
503 template<typename T>
504 int waitValue( const T & var,
505  const T & tgtVal,
506  unsigned long timeout = 5000000000,
507  unsigned long pauseWait = 1000000
508  )
509 {
510  if(var == tgtVal) return 0;
511 
512  struct timespec ts0, ts1;
513  clock_gettime(CLOCK_REALTIME, &ts0);
514  ts1 = ts0;
515 
516 
517  while( (ts1.tv_sec - ts0.tv_sec)*1e9 + (ts1.tv_nsec - ts0.tv_nsec) < timeout)
518  {
519  if(var == tgtVal) return 0;
520 
521  nanoSleep(pauseWait);
522 
523  clock_gettime(CLOCK_REALTIME, &ts1);
524  }
525 
526  if(var == tgtVal) return 0;
527 
528  std::cerr << "Timeout: " << (ts1.tv_sec - ts0.tv_sec)*1e9 + (ts1.tv_nsec - ts0.tv_nsec) << "\n";
529 
530  return -1;
531 
532 }
533 
534 template<typename T>
535 int waitValue( const T & var,
536  const T & tgtVal,
537  double tol,
538  unsigned long timeout = 5000000000,
539  unsigned long pauseWait = 1000000
540  )
541 {
542  if(fabs(tgtVal - var) <= tol) return 0;
543 
544  struct timespec ts0, ts1;
545  clock_gettime(CLOCK_REALTIME, &ts0);
546  ts1 = ts0;
547 
548  while( (ts1.tv_sec - ts0.tv_sec)*1e9 + (ts1.tv_nsec - ts0.tv_nsec) < timeout)
549  {
550  if(fabs(tgtVal - var) <= tol) return 0;
551 
552  nanoSleep(pauseWait);
553 
554  clock_gettime(CLOCK_REALTIME, &ts1);
555  }
556 
557  if(fabs(tgtVal - var) <= tol) return 0;
558 
559  std::cerr << "Timeout: " << (ts1.tv_sec - ts0.tv_sec)*1e9 + (ts1.tv_nsec - ts0.tv_nsec) << "\n";
560  return -1;
561 
562 }
563 
564 inline
566 {
567  //Steps:
568  //1) Set freqs to 0
569  if( sendNewProperty(m_indiP_C1freq, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
570  if( sendNewProperty(m_indiP_C2freq, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
571 
572  //2) Set amps to 0 (really 0.002)
573  if( sendNewProperty(m_indiP_C1volts, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
574  if( sendNewProperty(m_indiP_C2volts, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
575 
576  //3) Set phase to 0
577  if( sendNewProperty(m_indiP_C1phse, "value", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
578  if( sendNewProperty(m_indiP_C2phse, "value", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
579 
580  //4) Set offset to 0
581  if( sendNewProperty(m_indiP_C1ofst, "value", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
582  if( sendNewProperty(m_indiP_C2ofst, "value", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
583 
584  //5) Set outputs to off
585  if( sendNewProperty(m_indiP_C1outp, "value", "Off") < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
586  if( sendNewProperty(m_indiP_C2outp, "value", "Off") < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
587 
588  //Now check if values have changed.
589  if( waitValue(m_C1freq, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
590  if( waitValue(m_C2freq, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
591  if( waitValue(m_C1volts, 0.002, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
592  if( waitValue(m_C2volts, 0.002, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
593  if( waitValue(m_C1phse, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
594  if( waitValue(m_C2phse, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
595  if( waitValue(m_C1ofst, 0.001, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
596  if( waitValue(m_C2ofst, 0.001, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
597  if( waitValue(m_C1outp, 0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
598  if( waitValue(m_C2outp, 0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
599 
600  log<text_log>("The PyWFS TTM is rested.", logPrio::LOG_NOTICE);
601 
602  return 0;
603 }
604 
605 inline
607 {
608  if(m_modState == MODSTATE_SET) //already Set.
609  {
610  return 0;
611  }
612 
613  if(m_modState == MODSTATE_MODULATING) //Modulating
614  {
615  log<text_log>("Stopping modulation.", logPrio::LOG_INFO);
616 
617  //Steps:
618  //1) Set freqs to 0
619  if( sendNewProperty(m_indiP_C1freq, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
620 
621  if( sendNewProperty(m_indiP_C2freq, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
622 
623  //2) Set amps to 0 (really 0.002)
624  if( sendNewProperty(m_indiP_C1volts, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
625 
626  if( sendNewProperty(m_indiP_C2volts, "target", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
627 
628  //3) Set phase to 0
629  if( sendNewProperty(m_indiP_C1phse, "value", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
630 
631  if( sendNewProperty(m_indiP_C2phse, "value", 0.0) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
632 
633  //Now check if values have changed.
634  if( waitValue(m_C1freq, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
635  if( waitValue(m_C2freq, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
636  if( waitValue(m_C1volts, 0.002, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
637  if( waitValue(m_C2volts, 0.002,1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
638  if( waitValue(m_C1phse, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
639  if( waitValue(m_C2phse, 0.0) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
640 
641  m_modFreq = 0;
642  m_modFreqRequested = 0;
643  m_modRad = 0;
644  m_modRadRequested = 0;
645 
646  log<text_log>("PyWFS TTM is set.", logPrio::LOG_NOTICE);
647  return 0;
648  }
649 
650  //Ok, we're in not set or modulating. Possibly rested, or in a partially set state.
651 
652  //Steps:
653  //1) Make sure we're fully rested:
654  if( m_modState != MODSTATE_REST)
655  {
656  if( restTTM() < 0 ) return log<software_error, -1>({__FILE__, __LINE__});
657 
658  sleep(1);
659  }
660 
661  log<text_log>("Setting the PyWFS TTM.", logPrio::LOG_INFO);
662 
663  //2) Set outputs to on
664  if( sendNewProperty(m_indiP_C1outp, "value", "On") < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
665  if( sendNewProperty(m_indiP_C2outp, "value", "On") < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
666 
667  if( waitValue(m_C1outp, 1) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
668  if( waitValue(m_C2outp, 1) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
669 
670  //3) Now we begin ramp . . .
671  size_t N1 = m_setVoltage_1/m_setDVolts;
672  size_t N2 = m_setVoltage_2/m_setDVolts;
673 
674  size_t N = N1;
675  if(N2 < N1) N = N2;
676 
677  log<text_log>("Ramping with " + std::to_string(N) + " steps. [" + std::to_string(N1) + " " + std::to_string(N2) + "]", logPrio::LOG_DEBUG);
678 
679  for(size_t i=1; i< N ; ++i)
680  {
681  double nv = i*m_setDVolts;
682 
683  if(nv < 0 || nv > 10) return log<software_error,-1>({__FILE__, __LINE__, "Bad voltage calculated. Refusing."});
684 
685  if( sendNewProperty(m_indiP_C1ofst, "value", nv) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
686 
687  if( waitValue(m_C1ofst, nv, 1e-10) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
688 
689  sleep(1);
690 
691  if( sendNewProperty(m_indiP_C2ofst, "value", nv) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
692 
693  if( waitValue(m_C2ofst, nv, 1e-6) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
694 
695  sleep(1);
696  }
697 
698  for(size_t j=N; j< N1;++j)
699  {
700  double nv = j*m_setDVolts;
701 
702  if(nv < 0 || nv > 10) return log<software_error,-1>({__FILE__, __LINE__, "Bad voltage calculated. Refusing."});
703 
704  if( sendNewProperty(m_indiP_C1ofst, "value", nv) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
705 
706  if( waitValue(m_C1ofst, nv, 1e-6) < 0 ) return log<software_error, -1>({__FILE__,__LINE__, "fxngen timeout"});
707 
708  sleep(1);
709  }
710 
711  for(size_t j=N; j< N2;++j)
712  {
713  double nv = j*m_setDVolts;
714 
715  if(nv < 0 || nv > 10) return log<software_error,-1>({__FILE__, __LINE__, "Bad voltage calculated. Refusing."});
716 
717  if( sendNewProperty(m_indiP_C2ofst, "value", nv) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
718 
719  if( waitValue(m_C2ofst, nv, 1e-6) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
720 
721  sleep(1);
722  }
723 
725  {
726  if( m_setVoltage_1 < 0 || m_setVoltage_1 > 10) return log<software_error,-1>({__FILE__, __LINE__, "Bad voltage calculated. Refusing."});
727 
728  if( (sendNewProperty(m_indiP_C1ofst, "value", m_setVoltage_1) < 0 ) ) return log<software_error,-1>({__FILE__,__LINE__});
729 
730  if(waitValue(m_C1ofst, m_setVoltage_1, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
731 
732  }
733 
735  {
736  if( m_setVoltage_2 < 0 || m_setVoltage_2 > 10) return log<software_error,-1>({__FILE__, __LINE__, "Bad voltage calculated. Refusing."});
737 
738  if( (sendNewProperty(m_indiP_C2ofst, "value", m_setVoltage_2) < 0 ) ) return log<software_error,-1>({__FILE__,__LINE__});
739 
740  if( waitValue(m_C2ofst, m_setVoltage_2, 1e-6) < 0) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
741 
742  }
743 
744  log<text_log>("PyWFS TTM is set.", logPrio::LOG_NOTICE);
745 
746  return 0;
747 }
748 
749 /*double maxRadAtFrequency( const std::vector<double> freqs,
750  const std::vector<double> amps,
751  double modrad
752  )
753 {
754 }*/
755 
756 inline
757 int ttmModulator::modTTM( double newRad,
758  double newFreq
759  )
760 {
761  /// \todo log this
762  if(newRad < 0 || newFreq < 0) return 0;
763 
764  /// \todo logging in these steps
765 
766  //For now: if we enter modulating, we stop modulating.
767  /// \todo Implement changing modulation without setting first.
769  {
770  if(newRad == m_modRad && newFreq == m_modFreq) return 0;
771 
772  if( setTTM() < 0 ) return log<software_error, -1>({__FILE__, __LINE__});
773 
774  if( calcState() < 0) return log<software_error, -1>({__FILE__,__LINE__});
775 
776  }
777 
778  //If not set, we first check if we are fully rested.
779  if( m_modState < MODSTATE_SET )
780  {
781  if( setTTM() < 0 ) return log<software_error, -1>({__FILE__, __LINE__});
782 
783  if( calcState() < 0 ) return log<software_error, -1>({__FILE__,__LINE__});
784 
785  if( m_modState < MODSTATE_SET) return log<software_error, -1>({__FILE__,__LINE__, "TTM not set/setable."});
786  }
787 
788  //Check frequency for safety.
789  if(newFreq > m_maxFreq)
790  {
791  log<text_log>("Requested frequency " + std::to_string(newFreq) + " Hz exceeds limit (" + std::to_string(m_maxFreq) + " Hz). Limiting.", logPrio::LOG_WARNING);
792  newFreq = m_maxFreq;
793  }
794 
795  //Calculate voltage, and normalize and safety-check Parameters
796  double voltageC1, voltageC2;
797 
798  ///\todo here maximum radius should be frequency dependent.
799 
800 
801 
802  double terpC1Amp = 0;
803  double terpC2Amp = 0;
804  double terpC2Phse = 0;
805 
806  size_t ngt = 0;
807 
808  for(ngt = 0; ngt < m_calFreqs.size(); ++ngt)
809  {
810  if( m_calFreqs[ngt] >= newFreq) break;
811  }
812 
813  if(ngt == 0 || m_calFreqs[ngt] == newFreq)
814  {
815  terpC1Amp = m_calC1Amps[ngt];
816  terpC2Amp = m_calC2Amps[ngt];
817  terpC2Phse = m_calC2Phse[ngt];
818  }
819  else
820  {
821  size_t nlt = ngt -1;
822  double dfreq = (newFreq - m_calFreqs[nlt])/(m_calFreqs[ngt]-m_calFreqs[nlt]);
823 
824  terpC1Amp = m_calC1Amps[nlt] + (m_calC1Amps[ngt]-m_calC1Amps[nlt])*dfreq;
825  terpC2Amp = m_calC2Amps[nlt] + (m_calC2Amps[ngt]-m_calC2Amps[nlt])*dfreq;
826  terpC2Phse = m_calC2Phse[nlt] + (m_calC2Phse[ngt]-m_calC2Phse[nlt])*dfreq;
827  }
828 
829 
830  voltageC1 = terpC1Amp*(newRad/m_calRadius);
831  voltageC2 = terpC2Amp*(newRad/m_calRadius);
832 
833 
834  if(voltageC1 > m_maxVolt)
835  {
836  log<text_log>("Requested ch-1 voltge " + std::to_string(voltageC1) + " V exceeds limit (" + std::to_string(m_maxVolt) + " V). Limiting.", logPrio::LOG_WARNING);
837  voltageC1 = m_maxVolt;
838  }
839 
840  if(voltageC2 > m_maxVolt)
841  {
842  log<text_log>("Requested ch-2 voltge " + std::to_string(voltageC2) + " V exceeds limit (" + std::to_string(m_maxVolt) + " V). Limiting.", logPrio::LOG_WARNING);
843  voltageC2 = m_maxVolt;
844  }
845 
846 
847  //At this point we have safe calibrated voltage for the frequency.
848 
849  if( m_modState == MODSTATE_SET)
850  {
851  // 0) set phase
852  if( sendNewProperty(m_indiP_C2phse, "value", terpC2Phse) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
853 
854  /// \todo should we set the offset here just to be sure?
855 
856  //Now check if values have changed.
857  if( waitValue(m_C2phse, terpC2Phse, 1e-4) < 0 ) return log<software_error, -1>({__FILE__,__LINE__, "fxngen timeout"});
858 
859  // 1) set freq to 100 Hz (or requested if < 100 Hz)
860  double nextFreq = 100.0;
861  if(nextFreq > newFreq) nextFreq = newFreq;
862 
863  //send to device
864  if( sendNewProperty(m_indiP_C1freq, "target", nextFreq) < 0 ) log<software_error,-1>({__FILE__,__LINE__});
865  if( sendNewProperty(m_indiP_C2freq, "target", nextFreq) < 0 ) log<software_error,-1>({__FILE__,__LINE__});
866 
867  //Now check if values have changed.
868  if( waitValue(m_C1freq, nextFreq, 1e-6) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
869  if( waitValue(m_C2freq, nextFreq, 1e-6) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
870 
871  // 2) set amp to 0.1 V (or requested if < 0.1 V)
872  double nextVolts1 = 0.1;
873  if(nextVolts1 > voltageC1) nextVolts1 = voltageC1;
874 
875  double nextVolts2 = 0.1;
876  if(nextVolts2 > voltageC2) nextVolts2 = voltageC2;
877 
878  //send to device
879  if( sendNewProperty(m_indiP_C1volts, "target", nextVolts1) < 0 ) return log<software_error, -1>({__FILE__,__LINE__});
880  if( sendNewProperty(m_indiP_C2volts, "target", nextVolts2) < 0 ) return log<software_error, -1>({__FILE__,__LINE__});
881 
882 
883  //Now check if values have changed.
884  if( waitValue(m_C1volts, nextVolts1, 1e-6) < 0 ) log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
885  if( waitValue(m_C2volts, nextVolts2, 1e-6) < 0 ) log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
886 
887  // 3) Increase freq to 500 Hz, then in 500 Hz increments
888  double currFreq = m_C1freq;
889 
890  nextFreq = 500.0;
891  if(nextFreq > newFreq) nextFreq = newFreq;
892 
893  ///\todo make frequency tolerance a configurable
894  while( fabs(currFreq - newFreq) > 1e-4)
895  {
896  if( sendNewProperty(m_indiP_C1freq, "target", nextFreq) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
897  if( sendNewProperty(m_indiP_C2freq, "target", nextFreq) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
898 
899  //Now check if values have changed.
900  if( waitValue(m_C1freq, nextFreq, 1e-6) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
901  if( waitValue(m_C2freq, nextFreq, 1e-6) < 0 ) return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout"});
902 
903  ///\todo make sleep-time configurable
904  sleep(1);
905  currFreq = m_C1freq;
906  nextFreq = currFreq + m_modDFreq;
907  if(nextFreq > newFreq) nextFreq = newFreq;
908  }
909 
910  // 4) Now increase amplitude in 0.1 V increments.
911  double currVolts1 = m_C1volts;
912  double currVolts2 = m_C2volts;
913 
914  nextVolts1 = 0.2;
915  if(nextVolts1 > voltageC1) nextVolts1 = voltageC1;
916 
917  nextVolts2 = 0.2;
918  if(nextVolts2 > voltageC2) nextVolts2 = voltageC2;
919  ///\todo make voltage tolerance a configurable.
920  while( fabs(currVolts1 - voltageC1) > 1e-4 || fabs(currVolts2 - voltageC2) > 1e-4)
921  {
922  if( sendNewProperty(m_indiP_C1volts, "target", nextVolts1) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
923  if( sendNewProperty(m_indiP_C2volts, "target", nextVolts2) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
924 
925  //Now check if values have changed.
926  if( waitValue(m_C1volts, nextVolts1, 1e-3) < 0 )
927  {
928  return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout C1"});
929  }
930 
931  if( waitValue(m_C2volts, nextVolts2, 1e-3) < 0 )
932  {
933  return log<software_error,-1>({__FILE__,__LINE__, "fxngen timeout C2"});
934  }
935 
936  sleep(1);
937  currVolts1 = m_C1volts;
938  nextVolts1 = currVolts1 + m_modDVolts;
939  if(nextVolts1 > voltageC1) nextVolts1 = voltageC1;
940 
941  currVolts2 = m_C2volts;
942  nextVolts2 = currVolts2 + m_modDVolts;
943  if(nextVolts2 > voltageC2) nextVolts2 = voltageC2;
944 
945  }
946 
947  m_modRad = newRad;
948  m_modFreq = newFreq;
949  }
950  else return log<software_error,-1>({__FILE__,__LINE__, "TTM not set but should be by now."});
951 
952 
953  return 0;
954 }
955 
956 inline
957 int ttmModulator::offset12( double d1,
958  double d2
959  )
960 {
961 
962  if( sendNewProperty(m_indiP_C1ofst, "value", m_C1ofst + d1) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
963  if( sendNewProperty(m_indiP_C2ofst, "value", m_C2ofst + d2) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
964 
965  return 0;
966 
967 }
968 
969 inline
970 int ttmModulator::offsetXY( double dx,
971  double dy
972  )
973 {
974  double cs = cos(m_rotAngle);
975  double ss = sin(m_rotAngle);
976 
977  double rdx = dx * cs - dy * ss;
978  double rdy = m_rotParity*(dx * ss + dy * cs);
979 
980  if( sendNewProperty(m_indiP_C1ofst, "value", m_C1ofst + rdx) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
981  if( sendNewProperty(m_indiP_C2ofst, "value", m_C2ofst + rdy) < 0 ) return log<software_error,-1>({__FILE__,__LINE__});
982 
983  return 0;
984 
985 }
986 
987 INDI_NEWCALLBACK_DEFN(ttmModulator, m_indiP_modState)(const pcf::IndiProperty &ipRecv)
988 {
989  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_modState, ipRecv);
990 
991  int target;
992 
993  if( indiTargetUpdate( m_indiP_modState, target, ipRecv, false) < 0)
994  {
995  return log<software_error, -1>({__FILE__,__LINE__});
996  }
997 
998  m_modStateRequested = target;
999 
1000  /*m_references(0,0) = target;
1001 
1002  int state = 0;
1003  try
1004  {
1005  state = ipRecv["target"].get<int>();
1006  }
1007  catch(...)
1008  {
1009  log<software_error>({__FILE__, __LINE__, "exception caught"});
1010  return -1;
1011  }
1012 
1013  m_modStateRequested = state;*/
1014 
1015  return 0;
1016 }
1017 
1018 INDI_NEWCALLBACK_DEFN(ttmModulator, m_indiP_modFrequency)(const pcf::IndiProperty &ipRecv)
1019 {
1020  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_modFrequency, ipRecv);
1021 
1022  double target;
1023  if( indiTargetUpdate( m_indiP_modFrequency, target, ipRecv, false) < 0)
1024  {
1025  return log<software_error, -1>({__FILE__,__LINE__});
1026  }
1027 
1028  if(target > 0)
1029  {
1030  m_modFreqRequested = target;
1031  }
1032  /*
1033  ///\todo use find to test
1034  try
1035  {
1036  double nf = -1;
1037  nf = ipRecv["target"].get<double>();
1038  if(nf > 0) m_modFreqRequested = nf;
1039  }
1040  catch(...)
1041  {
1042  //do nothing, just means no requested in command.
1043  }
1044  */
1045 
1046  return 0;
1047 
1048 }
1049 
1050 INDI_NEWCALLBACK_DEFN(ttmModulator, m_indiP_modRadius)(const pcf::IndiProperty &ipRecv)
1051 {
1052  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_modRadius, ipRecv);
1053 
1054  double target;
1055  if( indiTargetUpdate( m_indiP_modRadius, target, ipRecv, false) < 0)
1056  {
1057  return log<software_error, -1>({__FILE__,__LINE__});
1058  }
1059 
1060  if(target > 0)
1061  {
1062  m_modRadRequested = target;
1063  }
1064 
1065 /*
1066  ///\todo use find to test
1067  try
1068  {
1069  double nr = -1;
1070  nr = ipRecv["target"].get<double>();
1071  if(nr > 0) m_modRadRequested = nr;
1072  }
1073  catch(...)
1074  {
1075  //do nothing, just means no requested in command.
1076  }
1077  */
1078 
1079  return 0;
1080 
1081 }
1082 
1083 INDI_NEWCALLBACK_DEFN(ttmModulator, m_indiP_offset12)(const pcf::IndiProperty &ipRecv)
1084 {
1085  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offset12, ipRecv);
1086 
1087  double dx = 0;
1088  if(ipRecv.find("dC1"))
1089  {
1090  dx = ipRecv["dC1"].get<double>();
1091  }
1092  std::cerr << "dC1: " << dx << "\n";
1093 
1094  double dy = 0;
1095  if(ipRecv.find("dC2"))
1096  {
1097  dy = ipRecv["dC2"].get<double>();
1098  }
1099 
1100  std::cerr << "dC2: " << dy << "\n\n";
1101 
1102 
1103  return offset12(dx, dy);
1104 
1105 }
1106 
1107 INDI_NEWCALLBACK_DEFN(ttmModulator, m_indiP_offset)(const pcf::IndiProperty &ipRecv)
1108 {
1109  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offset, ipRecv);
1110 
1111  double dx = 0;
1112  if(ipRecv.find("x"))
1113  {
1114  dx = ipRecv["x"].get<double>();
1115  }
1116  std::cerr << "dx: " << dx << "\n";
1117 
1118  double dy = 0;
1119  if(ipRecv.find("y"))
1120  {
1121  dy = ipRecv["y"].get<double>();
1122  }
1123 
1124  std::cerr << "dy: " << dy << "\n\n";
1125 
1126 
1127  return offsetXY(dx, dy);
1128 
1129 }
1130 
1131 
1132 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C1outp)(const pcf::IndiProperty &ipRecv)
1133 {
1134  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1outp, ipRecv);
1135 
1136  ///\todo use find to test
1137  try
1138  {
1139  m_indiP_C1outp = ipRecv;
1140  std::string outp = ipRecv["value"].getValue();
1141 
1142  if( outp == "Off" )
1143  {
1144  m_C1outp = 0;
1145  }
1146  else if (outp == "On")
1147  {
1148  m_C1outp = 1;
1149  }
1150  else
1151  {
1152  m_C1outp = -1;
1153  }
1154 
1155  return 0;
1156  }
1157  catch(...)
1158  {
1159  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1160  return -1;
1161  }
1162 
1163 }
1164 
1165 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C1freq)(const pcf::IndiProperty &ipRecv)
1166 {
1167  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1freq, ipRecv);
1168 
1169  ///\todo use find to test
1170  try
1171  {
1172  m_indiP_C1freq = ipRecv;
1173  double nv = ipRecv["current"].get<double>();
1174 
1175  m_C1freq = nv;
1176 
1177  return 0;
1178  }
1179  catch(...)
1180  {
1181  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1182  return -1;
1183  }
1184 
1185 }
1186 
1187 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C1volts)(const pcf::IndiProperty &ipRecv)
1188 {
1189  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1volts, ipRecv);
1190 
1191  ///\todo use find to test
1192  try
1193  {
1194  m_indiP_C1volts = ipRecv;
1195  double nv = ipRecv["current"].get<double>();
1196 
1197  m_C1volts = nv;
1198  return 0;
1199  }
1200  catch(...)
1201  {
1202  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1203  return -1;
1204  }
1205 
1206 }
1207 
1208 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C1ofst)(const pcf::IndiProperty &ipRecv)
1209 {
1210  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1ofst, ipRecv);
1211 
1212  ///\todo use find to test
1213  try
1214  {
1215  m_indiP_C1ofst = ipRecv;
1216  double nv = ipRecv["value"].get<double>();
1217 
1218  m_C1ofst = nv;
1219 
1220  return 0;
1221  }
1222  catch(...)
1223  {
1224  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1225  return -1;
1226  }
1227 
1228 }
1229 
1230 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C1phse)(const pcf::IndiProperty &ipRecv)
1231 {
1232 
1233  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1phse, ipRecv);
1234 
1235  ///\todo use find to test
1236  try
1237  {
1238  m_indiP_C1phse = ipRecv;
1239  double nv = ipRecv["value"].get<double>();
1240 
1241  m_C1phse = nv;
1242 
1243  return 0;
1244  }
1245  catch(...)
1246  {
1247  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1248  return -1;
1249  }
1250 
1251 }
1252 
1253 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C2outp)(const pcf::IndiProperty &ipRecv)
1254 {
1255  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2outp, ipRecv);
1256 
1257  ///\todo use find to test
1258  try
1259  {
1260  m_indiP_C2outp = ipRecv;
1261  std::string outp = ipRecv["value"].getValue();
1262 
1263  if( outp == "Off" )
1264  {
1265  m_C2outp = 0;
1266  }
1267  else if (outp == "On")
1268  {
1269  m_C2outp = 1;
1270  }
1271  else
1272  {
1273  m_C2outp = -1;
1274  }
1275 
1276  return 0;
1277  }
1278  catch(...)
1279  {
1280  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1281  return -1;
1282  }
1283 
1284 }
1285 
1286 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C2freq)(const pcf::IndiProperty &ipRecv)
1287 {
1288  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2freq, ipRecv);
1289 
1290  ///\todo use find to test
1291  try
1292  {
1293  m_indiP_C2freq = ipRecv;
1294  double nv = ipRecv["current"].get<double>();
1295 
1296  m_C2freq = nv;
1297 
1298  return 0;
1299  }
1300  catch(...)
1301  {
1302  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1303  return -1;
1304  }
1305 
1306 }
1307 
1308 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C2volts)(const pcf::IndiProperty &ipRecv)
1309 {
1310  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2volts, ipRecv);
1311 
1312  ///\todo use find to test
1313  try
1314  {
1315  m_indiP_C2volts = ipRecv;
1316  double nv = ipRecv["current"].get<double>();
1317 
1318  m_C2volts = nv;
1319  return 0;
1320  }
1321  catch(...)
1322  {
1323  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1324  return -1;
1325  }
1326 
1327 }
1328 
1329 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C2ofst)(const pcf::IndiProperty &ipRecv)
1330 {
1331  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2ofst, ipRecv);
1332 
1333  ///\todo use find to test
1334  try
1335  {
1336  m_indiP_C2ofst = ipRecv;
1337 
1338  double nv = ipRecv["value"].get<double>();
1339 
1340  m_C2ofst = nv;
1341 
1342  return 0;
1343  }
1344  catch(...)
1345  {
1346  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1347  return -1;
1348  }
1349 
1350 }
1351 
1352 INDI_SETCALLBACK_DEFN(ttmModulator, m_indiP_C2phse)(const pcf::IndiProperty &ipRecv)
1353 {
1354  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2phse, ipRecv);
1355 
1356  ///\todo use find to test
1357  try
1358  {
1359  m_indiP_C2phse = ipRecv;
1360  double nv = ipRecv["value"].get<double>();
1361 
1362  m_C2phse = nv;
1363 
1364  return 0;
1365  }
1366  catch(...)
1367  {
1368  log<software_error>({__FILE__, __LINE__, "exception from libcommon"});
1369  return -1;
1370  }
1371 }
1372 
1373 } //namespace app
1374 } //namespace MagAOX
1375 
1376 #endif //ttmModulator_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.
Definition: MagAOXApp.hpp:3120
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2361
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:545
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
Definition: MagAOXApp.hpp:3268
double m_C1volts
Voltage p2p of fxn gen channel 1.
virtual int appStartup()
Startup functions.
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C2volts)
pcf::IndiProperty m_indiP_C1phse
pcf::IndiProperty m_indiP_C2ofst
std::vector< double > m_calC2Amps
int m_C1outp
Output state of fxn gen channel 1.
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C2ofst)
double m_setDVolts
The setting ramp step size [volts].
double m_maxFreq
The maximum modulation frequency settable by this program.
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C1volts)
pcf::IndiProperty m_indiP_C1ofst
int m_modState
-1 = unknown, 0 = off, 1 = rest, 2 = midset, 3 = set, 4 = modulating
int modTTM(double newRad, double newFreq)
Begin modulating or modify current modulation parameters.
virtual int appLogic()
Implementation of the FSM for the TTM Modulator.
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C1phse)
double m_modRadRequested
The requested modulation radius, in lam/D.
pcf::IndiProperty m_indiP_offset
int restTTM()
Rest the TTM.
pcf::IndiProperty m_indiP_C2outp
double m_C1phse
Phase of fxn gen channel 1.
double m_C2freq
Frequency of fxn gen channel 2.
double m_modFreq
The current modulation frequency, in Hz.
double m_C2volts
Voltage p2p of fxn gen channel 2.
pcf::IndiProperty m_indiP_C2volts
pcf::IndiProperty m_indiP_C2phse
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C1freq)
pcf::IndiProperty m_indiP_offset12
double m_maxVolt
The maximum modulation voltage settable by this program.
pcf::IndiProperty m_indiP_modState
int m_C2outp
Output state of fxn gen channel 2.
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C1ofst)
double m_modRad
The current modulation radius, in lam/D.
INDI_NEWCALLBACK_DECL(ttmModulator, m_indiP_modRadius)
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C1outp)
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C2freq)
pcf::IndiProperty m_indiP_C2freq
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C2phse)
int setTTM()
Set the TTM.
virtual void loadConfig()
load the configuration system results (called by MagAOXApp::setup())
pcf::IndiProperty m_indiP_modFrequency
ttmModulator()
Default c'tor.
double m_setVoltage_1
the set position voltage of Ch. 1.
double m_C1freq
Frequency of fxn gen channel 1.
pcf::IndiProperty m_indiP_C1outp
int calcState()
Calculate the state of the modulator from the fxn gen params.
double m_C1ofst
DC offset of fxn gen channel 1.
int m_modStateRequested
The requested TTM state.
INDI_NEWCALLBACK_DECL(ttmModulator, m_indiP_modFrequency)
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
double m_modDVolts
The modulation ramp voltage step size [Volts].
pcf::IndiProperty m_indiP_C1freq
double m_C2ofst
DC offset of fxn gen channel 2.
int offsetXY(double dx, double dy)
~ttmModulator() noexcept
D'tor, declared and defined for noexcept.
double m_setVoltage_2
the set position voltage of Ch. 2.
pcf::IndiProperty m_indiP_C1volts
int offset12(double d1, double d2)
std::vector< double > m_calC2Phse
std::vector< double > m_calFreqs
INDI_NEWCALLBACK_DECL(ttmModulator, m_indiP_modState)
pcf::IndiProperty m_indiP_modRadius
INDI_NEWCALLBACK_DECL(ttmModulator, m_indiP_offset)
double m_C2phse
Phase of fxn gen channel 2.
pcf::IndiProperty m_indiP_FGState
INDI_SETCALLBACK_DECL(ttmModulator, m_indiP_C2outp)
std::vector< double > m_calC1Amps
double m_modDFreq
The modulation ramp frequency step size [Hz].
INDI_NEWCALLBACK_DECL(ttmModulator, m_indiP_offset12)
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
double m_modFreqRequested
The requested modulation frequency, in Hz.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:230
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:282
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
@ POWEROFF
The device power is off.
Definition: stateCodes.hpp:47
@ NOTHOMED
The device has not been homed.
Definition: stateCodes.hpp:53
@ CONFIGURING
The application is configuring the device.
Definition: stateCodes.hpp:52
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
Definition: stateCodes.hpp:43
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:56
@ POWERON
The device power is on.
Definition: stateCodes.hpp:48
std::ostream & cerr()
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
void nanoSleep(unsigned long nsec)
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
int waitValue(const T &var, const T &tgtVal, unsigned long timeout=5000000000, unsigned long pauseWait=1000000)
INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf
Definition: adcTracker.hpp:461
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition: dm.hpp:24
constexpr static logPrioT LOG_DEBUG
Used for debugging.
Definition: logPriority.hpp:52
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
Definition: logPriority.hpp:49
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.
#define MODSTATE_REST
#define MODSTATE_MODULATING
#define MODSTATE_OFF
#define MODSTATE_MIDSET
#define MODSTATE_UNKNOWN
#define MODSTATE_SET