API
 
Loading...
Searching...
No Matches
siglentSDG.hpp
Go to the documentation of this file.
1
2
3#ifndef siglentSDG_hpp
4#define siglentSDG_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
11
12namespace MagAOX
13{
14namespace app
15{
16
17/** MagAO-X application to control a Siglent SDG series function generator
18 *
19 * \todo need to recognize signals in tty polls and not return errors, etc.
20 * \todo need to implement an onDisconnect() to update values to unknown indicators.
21 * \todo need a frequency-dependent max amp facility.
22 * \todo convert to ioDevice
23 * \todo need telnet device, with optional username/password.
24 *
25 */
26class siglentSDG : public MagAOXApp<>, public dev::telemeter<siglentSDG>
27{
28
29 friend class siglentSDG_test;
30
31 friend class dev::telemeter<siglentSDG>;
32
33 //constexpr static double cs_MaxAmp = 0.87;//2.1;//0.87;
34 constexpr static double cs_MaxOfst = 10.0;
35 constexpr static double cs_MaxVolts = 10.0;
36 //constexpr static double cs_MaxFreq = 3622.0;//101;//3622.0;
37
38private:
39 std::vector<double> m_ampMax = {1.2801, 1.2801, 1.0201};//0.71, 0.83, 0.88, 1.05, 1.15, 3.45}; //1.5, 1.2, 1.1 };
40 std::vector<double> m_maxFreq = {0.0, 2000, 3000};//100.0, 150, 200, 250, 300, 1000}; //2999.99, 3499.99, 3500.01};
41 //todo: do we need to add max and min pulse variables?
42protected:
43
44 /** \name Configurable Parameters
45 * @{
46 */
47
48 std::string m_deviceAddr; ///< The device address
49 std::string m_devicePort; ///< The device port
50
51 double m_bootDelay {10}; ///< Time in seconds it takes the device to boot.
52
53 int m_writeTimeOut {10000}; ///< The timeout for writing to the device [msec].
54 int m_readTimeOut {10000}; ///< The timeout for reading from the device [msec].
55
56 double m_C1setVoltage {5.0}; ///< the set position voltage of Ch. 1.
57 double m_C2setVoltage {5.0}; ///< the set position voltage of Ch. 2.
58
59 bool m_C1outpOn {false}; /**< Flag controlling if C1 output is on after normalization. */
60 bool m_C2outpOn {false}; /**< Flag controlling if C2 output is on after normalization.
61 This will only have an effect if m_C1wvtp is "pulse" */
62
63 ///@}
64
65 tty::telnetConn m_telnetConn; ///< The telnet connection manager
66
67 std::string m_waveform; ///< The chosen funciton to generate
68 /// std::string m_clock; ///<INTernal or EXTernal
69
70 uint8_t m_C1outp {0}; ///< The output status channel 1
71 double m_C1frequency {0}; ///< The output frequency of channel 1
72 double m_C1vpp {0}; ///< The peak-2-peak voltage of channel 1
73 double m_C1vppDefault {0}; ///< default value for vpp of channel 1
74 double m_C1ofst {0}; ///< The offset voltage of channel 1
75 double m_C1phse {0}; ///< The phase of channel 1 (SINE only)
76 double m_C1wdth {0}; ///< The width of channel 1 (PULSE only)
77 std::string m_C1wvtp; ///< The wave type of channel 1
78 double m_C1ampMax {10.0}; ///< The maximum voltage output for channel 1
79
80 uint8_t m_C2outp {0}; ///< The output status channel 2
81 double m_C2frequency {0}; ///< The output frequency of channel 2
82 double m_C2vpp {0}; ///< The peak-2-peak voltage of channel 2
83 double m_C2vppDefault {0}; ///< default value for vpp of channel 2
84 double m_C2ofst {0}; ///< The offset voltage of channel 2
85 double m_C2phse {0}; ///< The phase of channel 2 (SINE only)
86 double m_C2wdth {0}; ///< The width of channel 2 (PULSE only)
87 std::string m_C2wvtp; ///< The wave type of channel 2
88 double m_C2ampMax {10.0}; ///< The maximum voltage output for channel 2
89
90 double m_C1frequency_tgt {-1};
91 double m_C1vpp_tgt {-1};
92
93 double m_C2frequency_tgt {-1};
94 double m_C2vpp_tgt {-1};
95
96 bool m_C1sync {false};
97 bool m_C2sync {false};
98
99private:
100
101 bool m_poweredOn {false};
102
103 double m_powerOnCounter {0}; ///< Counts the number of loops since power-on, used to control logging of connect failures.
104
105public:
106
107 /// Default c'tor.
108 siglentSDG();
109
110 /// D'tor, declared and defined for noexcept.
113
114 /// Setup the configuration system (called by MagAOXApp::setup())
115 virtual void setupConfig();
116
117 /// load the configuration system results (called by MagAOXApp::setup())
118 virtual void loadConfig();
119
120 /// Startup functions
121 /** Setsup the INDI vars.
122 *
123 */
124 virtual int appStartup();
125
126 /// Implementation of the FSM for the Siglent SDG
127 virtual int appLogic();
128
129 /// Implementation of the on-power-off FSM logic
130 virtual int onPowerOff();
131
132 /// Implementation of the while-powered-off FSM
133 virtual int whilePowerOff();
134
135 /// Do any needed shutdown tasks. Currently nothing in this app.
136 virtual int appShutdown();
137
138 /// Write a command to the device and get the response. Not mutex-ed.
139 /** We assume this is called after the m_indiMutex is locked.
140 *
141 * \returns 0 on success
142 * \returns -1 on an error. May set DISCONNECTED.
143 */
144 int writeRead( std::string & strRead, ///< [out] The string responseread in
145 const std::string & command ///< [in] The command to send.
146 );
147
148 /// Write a command to the device.
149 /**
150 * \returns 0 on success
151 * \returns -1 on error
152 */
153 int writeCommand( const std::string & commmand /**< [in] the complete command string to send to the device */);
154
155 /// Send the MDWV? query and get the response state.
156 /** This does not update internal state.
157 *
158 * \returns 0 on success
159 * \returns -1 on an error.
160 */
161 int queryMDWV( std::string & state, ///< [out] the MDWV state, ON or OFF
162 int channel ///< [in] the channel to query
163 );
164
165 /// Send the SWWV? query and get the response state.
166 /** This does not update internal state.
167 *
168 * \returns 0 on success
169 * \returns -1 on an error.
170 */
171 int querySWWV( std::string & state, ///< [out] the SWWV state, ON or OFF
172 int channel ///< [in] the channel to query
173 );
174
175 /// Send the BTWV? query and get the response state.
176 /** This does not update internal state.
177 *
178 * \returns 0 on success
179 * \returns -1 on an error.
180 */
181 int queryBTWV( std::string & state, ///< [out] the BTWV state, ON or OFF
182 int channel ///< [in] the channel to query
183 );
184
185 /// Send the ARWV? query and get the response index.
186 /** This does not update internal state.
187 *
188 * \returns 0 on success
189 * \returns -1 on an error.
190 */
191 int queryARWV( int & index, ///< [out] the ARWV index
192 int channel ///< [in] the channel to query
193 );
194
195 /// Send the BSWV? query for a channel.
196 /** This updates member variables and INDI.
197 *
198 * \returns 0 on success
199 * \returns -1 on an error.
200 */
201 int queryBSWV( int channel /** < [in] the channel to query */ );
202
203 /// Send the SYNC? query for a channel.
204 /** This updates member variables and INDI.
205 *
206 * \returns 0 on success
207 * \returns -1 on an error.
208 */
209 int querySYNC( bool & sync, /// < [in] the sync state for this channel
210 int channel /// < [in] the channel to query
211 );
212
213 /// Check the setup is correct and safe for PI TTM control.
214 /**
215 * \returns 0 if the fxn gen is setup for safe operation
216 * \returns 1 if a non-normal setup is detected.
217 * \returns -1 on an error, e.g. comms or parsing.
218 */
219 int checkSetup();
220
221 /// Normalize the setup, called during connection if checkSetup shows a problem, or on power-up.
222 int normalizeSetup();
223
224 /// Send the OUTP? query for a channel.
225 /**
226 * \returns 0 on success
227 * \returns -1 on an error.
228 */
229 int queryOUTP( int channel /**< [in] the channel to query */);
230
231 /// Change the output status (on/off) of one channel.
232 /**
233 * \returns 0 on success
234 * \returns -1 on error.
235 */
236 int changeOutp( int channel, ///< [in] the channel to send the command to.
237 const std::string & newOutp ///< [in] The requested output state [On/Off]
238 );
239
240 /// Change the output status (on/off) of one channel in response to an INDI property. This locks the mutex.
241 /**
242 * \returns 0 on success
243 * \returns -1 on error.
244 */
245 int changeOutp( int channel, ///< [in] the channel to send the command to.
246 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested output state [On/Off]
247 );
248
249 /// Send a change frequency command to the device.
250 /**
251 * \returns 0 on success
252 * \returns -1 on error
253 */
254 int changeFreq( int channel, ///< [in] the channel to send the command to.
255 double newFreq ///< [in] The requested new frequency [Hz]
256 );
257
258 /// Send a change frequency command to the device in response to an INDI property. This locks the mutex.
259 /**
260 * \returns 0 on success
261 * \returns -1 on error
262 */
263 int changeFreq( int channel, ///< [in] the channel to send the command to.
264 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new frequency [Hz]
265 );
266
267 /// Send a change amplitude command to the device.
268 /**
269 * \returns 0 on success
270 * \returns -1 on error
271 */
272 int changeAmp( int channel, ///< [in] the channel to send the command to.
273 double newAmp ///< [in] The requested new amplitude [V p2p]
274 );
275
276 /// Send a change amplitude command to the device in response to an INDI property.
277 /**
278 * \returns 0 on success
279 * \returns -1 on error
280 */
281 int changeAmp( int channel, ///< [in] the channel to send the command to.
282 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new amplitude [V p2p]
283 );
284
285 /// Send a change offset command to the device.
286 /**
287 * \returns 0 on success
288 * \returns -1 on error
289 */
290 int changeOfst( int channel, ///< [in] the channel to send the command to.
291 double newOfst ///< [in] The requested new offset [V p2p]
292 );
293
294 /// Send a change offset command to the device in response to an INDI property.
295 /**
296 * \returns 0 on success
297 * \returns -1 on error
298 */
299 int changeOfst( int channel, ///< [in] the channel to send the command to.
300 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new offset [V p2p]
301 );
302
303 /// Send a change phase command to the device.
304 /**
305 * \returns 0 on success
306 * \returns -1 on error
307 */
308 int changePhse( int channel, ///< [in] the channel to send the command to.
309 double newPhse ///< [in] The requested new phase [deg]
310 );
311
312 /// Send a change phase command to the device in response to an INDI property.
313 /**
314 * \returns 0 on success
315 * \returns -1 on error
316 */
317 int changePhse( int channel, ///< [in] the channel to send the command to.
318 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new phase [deg]
319 );
320
321 /// Send a width command to the device.
322 /**
323 * \returns 0 on success
324 * \returns -1 on error
325 */
326 int changeWdth( int channel, ///< [in] the channel to send the command to.
327 double newWdth ///< [in] The requested new width [s]
328 );
329
330 /// Send a change phase command to the device in response to an INDI property.
331 /**
332 * \returns 0 on success
333 * \returns -1 on error
334 */
335 int changeWdth( int channel, ///< [in] the channel to send the command to.
336 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new width [s]
337 );
338
339
340 /// Send a change wavetype command to the device.
341 /**
342 * \returns 0 on success
343 * \returns -1 on error
344 */
345 int changeWvtp( int channel, ///< [in] the channel to send the command to.
346 const std::string & newWvtp ///< [in] The requested new wavetype
347 );
348
349 /// Send a change wavetype command to the device in response to an INDI property.
350 /**
351 * \returns 0 on success
352 * \returns -1 on error
353 */
354 int changeWvtp( int channel, ///< [in] the channel to send the command to.
355 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new wavetype
356 );
357
358 /// Send a change sync command to the device.
359 /**
360 * \returns 0 on success
361 * \returns -1 on error
362 */
363 int changeSync( int channel, ///< [in] the channel to send the command to.
364 bool newSync ///< [in] The requested new sync state
365 );
366
367 /// Send a change sync command to the device in response to an INDI property.
368 /**
369 * \returns 0 on success
370 * \returns -1 on error
371 */
372 int changeSync( int channel, ///< [in] the channel to send the command to.
373 const pcf::IndiProperty &ipRecv ///< [in] INDI property containing the requested new sync state
374 );
375
376 /** \name INDI
377 * @{
378 */
379protected:
380
381 //declare our properties
382 pcf::IndiProperty m_indiP_status;
383
384 pcf::IndiProperty m_indiP_C1outp;
385 pcf::IndiProperty m_indiP_C1wvtp;
386 pcf::IndiProperty m_indiP_C1freq;
387 pcf::IndiProperty m_indiP_C1peri;
388 pcf::IndiProperty m_indiP_C1amp;
389 pcf::IndiProperty m_indiP_C1ampvrms;
390 pcf::IndiProperty m_indiP_C1ofst;
391 pcf::IndiProperty m_indiP_C1hlev;
392 pcf::IndiProperty m_indiP_C1llev;
393 pcf::IndiProperty m_indiP_C1phse;
394 pcf::IndiProperty m_indiP_C1wdth;
395 pcf::IndiProperty m_indiP_C1sync;
396
397 pcf::IndiProperty m_indiP_C2outp;
398 pcf::IndiProperty m_indiP_C2wvtp;
399 pcf::IndiProperty m_indiP_C2freq;
400 pcf::IndiProperty m_indiP_C2peri;
401 pcf::IndiProperty m_indiP_C2amp;
402 pcf::IndiProperty m_indiP_C2ampvrms;
403 pcf::IndiProperty m_indiP_C2ofst;
404 pcf::IndiProperty m_indiP_C2hlev;
405 pcf::IndiProperty m_indiP_C2llev;
406 pcf::IndiProperty m_indiP_C2phse;
407 pcf::IndiProperty m_indiP_C2wdth;
408 pcf::IndiProperty m_indiP_C2sync;
409
410public:
419
428 ///@}
429
430
431 /** \name Telemeter Interface
432 *
433 * @{
434 */
435
436 int checkRecordTimes();
437
438 int recordTelem( const telem_fxngen * );
439
440 int recordParams(bool force = false);
441
442 /// @}
443
444};
445
446inline
447siglentSDG::siglentSDG() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
448{
449 m_powerMgtEnabled = true;
450 m_telnetConn.m_prompt = "\n";
451 return;
452}
453
454inline
456{
457 config.add("device.address", "a", "device.address", argType::Required, "device", "address", false, "string", "The device address.");
458 config.add("device.port", "p", "device.port", argType::Required, "device", "port", false, "string", "The device port.");
459
460 config.add("timeouts.write", "", "timeouts.write", argType::Required, "timeouts", "write", false, "int", "The timeout for writing to the device [msec]. Default = 1000");
461 config.add("timeouts.read", "", "timeouts.read", argType::Required, "timeouts", "read", false, "int", "The timeout for reading the device [msec]. Default = 2000");
462
463 config.add("fxngen.waveform", "w", "fxngen.waveform", argType::Required, "fxngen", "waveform", false, "string", "The waveform to populate function.");
464
465 config.add("fxngen.C1outpOn", "", "fxngen.C1outpOn", argType::Required, "fxngen", "C1outpOn", false, "bool", "Whether (true) or not (false) C1 output is enabled at startup. Only effective wavefrom is pulse. Default is false.");
466 config.add("fxngen.C2outpOn", "", "fxngen.C2outpOn", argType::Required, "fxngen", "C2outpOn", false, "bool", "Whether (true) or not (false) C2 output is enabled at startup. Only effective wavefrom is pulse. Default is false.");
467
468 config.add("fxngen.C1ampDefault", "", "fxngen.C1ampDefault", argType::Required, "fxngen", "C1ampDefault", false, "float", "C1 Default P2V Amplitude of waveform. Default = 0.0");
469 config.add("fxngen.C2ampDefault", "", "fxngen.C2ampDefault", argType::Required, "fxngen", "C2ampDefault", false, "float", "C2 Default P2V Amplitude of waveform. Default = 0.0");
470
471 config.add("fxngen.C1ofstDefault", "", "fxngen.C1ofstDefault", argType::Required, "fxngen", "C1ofstDefault", false, "float", "C1 Default Offset Amplitude of waveform. Default = 0.0");
472 config.add("fxngen.C2ofstDefault", "", "fxngen.C2ofstDefault", argType::Required, "fxngen", "C2ofstDefault", false, "float", "C2 Default Offset Amplitude of waveform. Default = 0.0");
473
474 config.add("fxngen.C1ampMax", "", "fxngen.C1ampMax", argType::Required, "fxngen", "C1ampMax", false, "float", "C1 Maximum amplitude");
475 config.add("fxngen.C2ampMax", "", "fxngen.C2ampMax", argType::Required, "fxngen", "C2ampMax", false, "float", "C2 Maximum amplitude");
476
478}
479
480inline
482{
483 config(m_deviceAddr, "device.address");
484 config(m_devicePort, "device.port");
485
486 config(m_writeTimeOut, "timeouts.write");
487 config(m_readTimeOut, "timeouts.read");
488
489 config(m_waveform, "fxngen.waveform"); // todo: check if this is a valid waveform?
490 config(m_C1outpOn, "fxngen.C1outpOn");
491 config(m_C2outpOn, "fxngen.C2outpOn");
492
493 config(m_C1vppDefault, "fxngen.C1ampDefault");
494 config(m_C2vppDefault, "fxngen.C2ampDefault");
495
496 config(m_C1ofst, "fxngen.C1ofstDefault");
497 config(m_C2ofst, "fxngen.C2ofstDefault");
498
499 config(m_C1ampMax, "fxngen.C1ampMax");
500 config(m_C2ampMax, "fxngen.C2ampMax");
501 /// config(m_clock, "fxngen.clock");
502
504}
505
506inline
508{
509 // set up the INDI properties
510 REG_INDI_NEWPROP_NOCB(m_indiP_status, "status", pcf::IndiProperty::Text);
511 m_indiP_status.add (pcf::IndiElement("value"));
512 m_indiP_status["value"].set(0);
513
514 REG_INDI_NEWPROP(m_indiP_C1outp, "C1outp", pcf::IndiProperty::Text);
515 m_indiP_C1outp.add (pcf::IndiElement("value"));
516 m_indiP_C1outp["value"].set("");
517
518 //REG_INDI_NEWPROP(m_indiP_C1freq, "C1freq", pcf::IndiProperty::Number);
519 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_C1freq, "C1freq", -1e15, 1e15, 1, "%g", "C1freq", "C1freq");
520 //m_indiP_C1freq.add (pcf::IndiElement("value"));
521 m_indiP_C1freq["current"].set(0);
522 m_indiP_C1freq["target"].set(0);
523
524 //REG_INDI_NEWPROP(m_indiP_C1amp, "C1amp", pcf::IndiProperty::Number);
525 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_C1amp, "C1amp", -1e15, 1e15, 1, "%g", "C1amp", "C1amp");
526 //m_indiP_C1amp.add (pcf::IndiElement("value"));
527 m_indiP_C1amp["current"].set(0);
528 m_indiP_C1amp["target"].set(0);
529
530 REG_INDI_NEWPROP(m_indiP_C1ofst, "C1ofst", pcf::IndiProperty::Number);
531 m_indiP_C1ofst.add (pcf::IndiElement("value"));
532 m_indiP_C1ofst["value"].set(0);
533
534 if(m_waveform == "SINE"){
535 REG_INDI_NEWPROP(m_indiP_C1phse, "C1phse", pcf::IndiProperty::Number);
536 m_indiP_C1phse.add (pcf::IndiElement("value"));
537 m_indiP_C1phse["value"].set(0);
538 }
539
540 if(m_waveform == "PULSE"){
541 REG_INDI_NEWPROP(m_indiP_C1wdth, "C1wdth", pcf::IndiProperty::Number);
542 m_indiP_C1wdth.add (pcf::IndiElement("value"));
543 m_indiP_C1wdth["value"].set(0);
544 }
545
546 REG_INDI_NEWPROP(m_indiP_C1wvtp, "C1wvtp", pcf::IndiProperty::Text);
547 m_indiP_C1wvtp.add (pcf::IndiElement("value"));
548 m_indiP_C1wvtp["value"].set("");
549
550 REG_INDI_NEWPROP_NOCB(m_indiP_C1peri, "C1peri", pcf::IndiProperty::Number);
551 m_indiP_C1peri.add (pcf::IndiElement("value"));
552 m_indiP_C1peri["value"].set(0);
553
554 REG_INDI_NEWPROP_NOCB(m_indiP_C1ampvrms, "C1ampvrms", pcf::IndiProperty::Number);
555 m_indiP_C1ampvrms.add (pcf::IndiElement("value"));
556 m_indiP_C1ampvrms["value"].set(0);
557
558 REG_INDI_NEWPROP_NOCB(m_indiP_C1hlev, "C1hlev", pcf::IndiProperty::Number);
559 m_indiP_C1hlev.add (pcf::IndiElement("value"));
560 m_indiP_C1hlev["value"].set(0);
561
562 REG_INDI_NEWPROP_NOCB(m_indiP_C1llev, "C1llev", pcf::IndiProperty::Number);
563 m_indiP_C1llev.add (pcf::IndiElement("value"));
564 m_indiP_C1llev["value"].set(0);
565
566 createStandardIndiToggleSw( m_indiP_C1sync, "C1synchro", "C1 Sync Output", "C1 Sync Output");
568 {
570 return -1;
571 }
572
573 REG_INDI_NEWPROP(m_indiP_C2outp, "C2outp", pcf::IndiProperty::Text);
574 m_indiP_C2outp.add (pcf::IndiElement("value"));
575 m_indiP_C2outp["value"].set("");
576
577 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_C2freq, "C2freq", -1e15, 1e15, 1, "%g", "C2freq", "C2freq");
578 m_indiP_C2freq["current"].set(0);
579 m_indiP_C2freq["target"].set(0);
580
581 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_C2amp, "C2amp", -1e15, 1e15, 1, "%g", "C2amp", "C2amp");
582 m_indiP_C2amp["current"].set(0);
583 m_indiP_C2amp["target"].set(0);
584
585 REG_INDI_NEWPROP(m_indiP_C2ofst, "C2ofst", pcf::IndiProperty::Number);
586 m_indiP_C2ofst.add (pcf::IndiElement("value"));
587 m_indiP_C2ofst["value"].set(0);
588
589 if(m_waveform == "SINE")
590 {
591 REG_INDI_NEWPROP(m_indiP_C2phse, "C2phse", pcf::IndiProperty::Number);
592 m_indiP_C2phse.add (pcf::IndiElement("value"));
593 m_indiP_C2phse["value"].set(0);
594 }
595
596 if(m_waveform == "PULSE")
597 {
598 REG_INDI_NEWPROP(m_indiP_C2wdth, "C2wdth", pcf::IndiProperty::Number);
599 m_indiP_C2wdth.add (pcf::IndiElement("value"));
600 m_indiP_C2wdth["value"].set(0);
601 }
602
603 REG_INDI_NEWPROP(m_indiP_C2wvtp, "C2wvtp", pcf::IndiProperty::Text);
604 m_indiP_C2wvtp.add (pcf::IndiElement("value"));
605 m_indiP_C2wvtp["value"].set("");
606
607 REG_INDI_NEWPROP_NOCB(m_indiP_C2peri, "C2peri", pcf::IndiProperty::Number);
608 m_indiP_C2peri.add (pcf::IndiElement("value"));
609 m_indiP_C2peri["value"].set(0);
610
611 REG_INDI_NEWPROP_NOCB(m_indiP_C2ampvrms, "C2ampvrms", pcf::IndiProperty::Number);
612 m_indiP_C2ampvrms.add (pcf::IndiElement("value"));
613 m_indiP_C2ampvrms["value"].set(0);
614
615 REG_INDI_NEWPROP_NOCB(m_indiP_C2hlev, "C2hlev", pcf::IndiProperty::Number);
616 m_indiP_C2hlev.add (pcf::IndiElement("value"));
617 m_indiP_C2hlev["value"].set(0);
618
619 REG_INDI_NEWPROP_NOCB(m_indiP_C2llev, "C2llev", pcf::IndiProperty::Number);
620 m_indiP_C2llev.add (pcf::IndiElement("value"));
621 m_indiP_C2llev["value"].set(0);
622
623 createStandardIndiToggleSw( m_indiP_C2sync, "C2synchro", "C2 Sync Output", "C2 Sync Output");
625 {
627 return -1;
628 }
629
631 {
632 return log<software_error,-1>({__FILE__,__LINE__});
633 }
634
635 return 0;
636}
637
638inline
640{
641
642 if( state() == stateCodes::POWERON )
643 {
644 m_poweredOn = true; //So we reset the device.
645
648 }
649
650 //If we enter this loop in state ERROR, we wait 1 sec and then check power state.
651 if( state() == stateCodes::ERROR )
652 {
653 sleep(1);
654
655 //This allows for the case where the device powers off causing a comm error
656 //But we haven't gotten the update from the power controller before going through
657 //the main loop after the error.
658 if( (m_powerState != 1 || m_powerTargetState != 1) == true)
659 {
660 return 0;
661 }
662 }
663
665 {
667
668 if(rv == 0)
669 {
670 ///\todo the connection process in siglentSDG is a total hack. Figure out why this is needed to clear the channel, especially on a post-poweroff/on reconnect.
671
672 //The sleeps here seem to be necessary to make sure there is a good
673 //comm with device. Probably a more graceful way.
676 //sleep(1);//Wait for the connection to take.
677
679
680 m_telnetConn.m_strRead.clear();
682
684
685 int n = 0;
686 while( m_telnetConn.m_strRead != ">>")
687 {
688 if(n>9)
689 {
690 log<software_critical>({__FILE__, __LINE__, "No response from device. Time to power cycle."});
691 return -1;
692 }
694 sleep(1);
696 ++n;
697 }
698
699 if(!stateLogged())
700 {
701 std::stringstream logs;
702 logs << "Connected to " << m_deviceAddr << ":" << m_devicePort;
703 log<text_log>(logs.str());
704 }
705 return 0;//We cycle out to give connection time to settle.
706 }
707 else
708 {
709
711 {
712 std::stringstream logs;
713 logs << "Failed to connect to " << m_deviceAddr << ":" << m_devicePort;
714 log<text_log>(logs.str());
715 }
716
718
719 return 0;
720 }
721 }
722
724 {
725 //Do Initial Checks Here.
726 std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
727 if(lock.owns_lock())
728 {
729 if(m_poweredOn)
730 {
731 //This means we need to do the power-on setup.
732 if(normalizeSetup() < 0 )
733 {
735 return -1;
736 }
737
738 m_poweredOn = false;
739 }
740
741 int cs = checkSetup();
742
743 if(cs < 0) return 0; //This means we aren't really connected yet.
744
745 int rv;
746
747 rv = queryBSWV(1);
748
749 if( rv < 0 )
750 {
751 if(rv != SDG_PARSEERR_WVTP ) return 0; //This means we aren't really connected yet.
752
753 cs = 1; //Trigger normalizeSetup
754 }
755
756 rv = queryBSWV(2);
757
758 if( rv < 0 )
759 {
760 if(rv != SDG_PARSEERR_WVTP ) return 0; //This means we aren't really connected yet.
761
762 cs = 1; //Trigger normalizeSetup
763 }
764
765 if(cs > 0)
766 {
767 log<text_log>("Failed setup check, normalizing setup.", logPrio::LOG_NOTICE);
768 if(normalizeSetup() < 0)
769 {
771 return -1;
772 }
773
774 return 0;
775 }
776
777 if( queryOUTP(1) < 0 ) return 0; //This means we aren't really connected yet.
778 if( queryOUTP(2) < 0 ) return 0; //This means we aren't really connected yet.
779
780
781
782 if( m_C1outp == 1 || m_C2outp == 1)
783 {
785 }
786 else
787 {
789 }
790
791 recordParams(true);
792
793 }
794 else
795 {
796 log<text_log>("Could not get mutex after connecting.", logPrio::LOG_CRITICAL);
797 return -1;
798 }
799 }
800
802 {
803 // Do this right away to avoid a different thread updating something after we get it.
804 std::unique_lock<std::mutex> lock(m_indiMutex, std::try_to_lock);
805 if(lock.owns_lock())
806 {
807 int cs = checkSetup();
808
809 if(cs < 0)
810 {
811 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown)
812 {
815 }
816 return 0;
817 }
818
819 int rv;
820
821 rv = queryBSWV(1);
822
823 if( rv < 0 )
824 {
825 if(rv != SDG_PARSEERR_WVTP )
826 {
827 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown)
828 {
831 }
832 return 0;
833 }
834
835 cs = 1; //Trigger normalizeSetup
836 }
837
838 if(m_C1sync)
839 {
840 updateSwitchIfChanged(m_indiP_C1sync, "toggle", pcf::IndiElement::On, INDI_OK);
841 }
842 else
843 {
844 updateSwitchIfChanged(m_indiP_C1sync, "toggle", pcf::IndiElement::Off, INDI_IDLE);
845 }
846
847 rv = queryBSWV(2);
848
849 if( rv < 0 )
850 {
851 if(rv != SDG_PARSEERR_WVTP )
852 {
853 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown)
854 {
857 }
858 return 0;
859 }
860
861 cs = 1; //Trigger normalizeSetup
862 }
863
864 if(m_C2sync)
865 {
866 updateSwitchIfChanged(m_indiP_C2sync, "toggle", pcf::IndiElement::On, INDI_OK);
867 }
868 else
869 {
870 updateSwitchIfChanged(m_indiP_C2sync, "toggle", pcf::IndiElement::Off, INDI_IDLE);
871 }
872
873 if(cs > 0)
874 {
875 log<text_log>("Failed setup check, normalizing setup.", logPrio::LOG_NOTICE);
877
878 return 0;
879 }
880
881
882 if( queryOUTP(1) < 0 )
883 {
884 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown)
885 {
888 }
889 return 0;
890 }
891
892 if( queryOUTP(2) < 0 )
893 {
894 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown)
895 {
898 }
899 return 0;
900 }
901
902 if( m_C1outp == 1 || m_C2outp == 1)
903 {
905 }
906 else
907 {
909 }
910
911 recordParams(); //This will check if anything changed.
912 }
913
915 {
917 return 0;
918 }
919
920 return 0;
921
922 }
923
925 {
926 return 0;
927 }
928
929 //It's possible to get here because other threads are changing states.
930 //These are the only valid states for this APP at this point. Anything else and we'll log it.
932 {
933 return 0;
934 }
935
936
937 log<software_error>({__FILE__, __LINE__, "appLogic fell through in state " + stateCodes::codeText(state())});
938 return 0;
939
940}
941
942inline
944{
945 std::lock_guard<std::mutex> lock(m_indiMutex);
946
947 m_C1wvtp = "NONE";
948 m_C1frequency = 0.0;
949 m_C1vpp = 0.0;
950 m_C1ofst = 0.0;
951 m_C1outp = 0;
952
954 m_C1vpp_tgt = -1;
955
957
958 updateIfChanged(m_indiP_C1freq, "current", 0.0);
959 updateIfChanged(m_indiP_C1freq, "target", 0.0);
960
961 updateIfChanged(m_indiP_C1peri, "value", 0.0);
962
963 updateIfChanged(m_indiP_C1amp, "current", 0.0);
964 updateIfChanged(m_indiP_C1amp, "target", 0.0);
965
966 updateIfChanged(m_indiP_C1ampvrms, "value", 0.0);
967 updateIfChanged(m_indiP_C1ofst, "value", 0.0);
968 updateIfChanged(m_indiP_C1hlev, "value", 0.0);
969 updateIfChanged(m_indiP_C1llev, "value", 0.0);
970 if(m_waveform == "SINE"){updateIfChanged(m_indiP_C1phse, "value", 0.0);}
971 if(m_waveform == "PULSE"){updateIfChanged(m_indiP_C1wdth, "value", 0.0);}
972 updateIfChanged(m_indiP_C1outp, "value", std::string("Off"));
973 updateSwitchIfChanged(m_indiP_C1sync, "toggle", pcf::IndiElement::Off, INDI_IDLE);
974
975
976 m_C2wvtp = "NONE";
977 m_C2frequency = 0.0;
978 m_C2vpp = 0.0;
979 m_C2ofst = 0.0;
980 m_C2outp = 0;
981
983 m_C2vpp_tgt = -1;
984
986
987 updateIfChanged(m_indiP_C2freq, "current", 0.0);
988 updateIfChanged(m_indiP_C2freq, "target", 0.0);
989
990 updateIfChanged(m_indiP_C2peri, "value", 0.0);
991
992 updateIfChanged(m_indiP_C2amp, "current", 0.0);
993 updateIfChanged(m_indiP_C2amp, "target", 0.0);
994
995 updateIfChanged(m_indiP_C2ampvrms, "value", 0.0);
996 updateIfChanged(m_indiP_C2ofst, "value", 0.0);
997 updateIfChanged(m_indiP_C2hlev, "value", 0.0);
998 updateIfChanged(m_indiP_C2llev, "value", 0.0);
999 if(m_waveform == "SINE"){updateIfChanged(m_indiP_C2phse, "value", 0.0);}
1000 if(m_waveform == "PULSE"){updateIfChanged(m_indiP_C2wdth, "value", 0.0);}
1001 updateIfChanged(m_indiP_C1outp, "value", std::string("Off"));
1002 updateSwitchIfChanged(m_indiP_C2sync, "toggle", pcf::IndiElement::Off, INDI_IDLE);
1003
1004 return 0;
1005}
1006
1007inline
1009{
1010 return onPowerOff();
1011}
1012
1013inline
1015{
1017
1018 return 0;
1019}
1020
1021inline
1022int siglentSDG::writeRead( std::string & strRead,
1023 const std::string & command
1024 )
1025{
1026 int rv;
1029
1030 if(rv < 0)
1031 {
1032 std::cout << command << "\n";
1033 std::cout << "writeRead return val was " << rv << "\n";
1036 return -1;
1037 }
1038
1039 //Clear the newline
1041 if(rv < 0)
1042 {
1044 return -1;
1045 }
1046
1048 if(rv < 0)
1049 {
1051 return -1;
1052 }
1053 return 0;
1054
1055}
1056
1057inline
1058int siglentSDG::writeCommand( const std::string & command )
1059{
1060
1062 if(rv < 0)
1063 {
1065 return -1;
1066 }
1067
1068 //Clear the newline
1070 if(rv < 0)
1071 {
1073 return -1;
1074 }
1075
1077 if(rv < 0)
1078 {
1080 return -1;
1081 }
1082
1083 return 0;
1084}
1085
1086inline
1087std::string makeCommand( int channel,
1088 const std::string & afterColon
1089 )
1090{
1091 std::string command = "C";
1092 command += mx::ioutils::convertToString<int>(channel);
1093 command += ":";
1094 command += afterColon;
1095 command += "\r\n";
1096
1097 return command;
1098}
1099
1100/// Calculate the auto pulse width for a requested pulse frequency.
1101/** Returns 0 for non-positive frequency, otherwise uses the larger of:
1102 * - fixed 250 us pulse width target
1103 * - 50% duty cycle limit
1104 */
1105inline
1106double autoPulseWidthFromFrequency( const double freqHz /**< [in] requested pulse frequency [Hz] */ )
1107{
1108 if(freqHz <= 0)
1109 {
1110 return 0.0;
1111 }
1112
1113 constexpr double targetPulseS = 0.000250;
1114 constexpr double maxDutyCycle = 0.5;
1115
1116 const double wdth250 = 1.0 / freqHz - targetPulseS;
1117 const double wdthLim = maxDutyCycle / freqHz;
1118
1119 if(wdthLim > wdth250)
1120 {
1121 return wdthLim;
1122 }
1123
1124 return wdth250;
1125}
1126
1127inline
1128int siglentSDG::queryMDWV( std::string & state,
1129 int channel
1130 )
1131{
1132 int rv;
1133
1134 if(channel < 1 || channel > 2) return -1;
1135
1136 std::string strRead;
1137
1138 std::string com = makeCommand(channel, "MDWV?");
1139
1140 rv = writeRead( strRead, com);
1141
1142 if(rv < 0)
1143 {
1144 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on MDWV? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1145 return -1;
1146 }
1147
1148 int resp_channel;
1149 std::string resp_state;
1150
1152
1153 if(rv == 0)
1154 {
1155 if(resp_channel != channel)
1156 {
1157 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1158 return -1;
1159 }
1160
1161 state = resp_state;
1162 }
1163 else
1164 {
1165 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1166 return -1;
1167 }
1168
1169 return 0;
1170}
1171
1172inline
1173int siglentSDG::querySWWV( std::string & state,
1174 int channel
1175 )
1176{
1177 int rv;
1178
1179 if(channel < 1 || channel > 2) return -1;
1180
1181 std::string strRead;
1182
1183 std::string com = makeCommand(channel, "SWWV?");
1184
1185 rv = writeRead( strRead, com);
1186
1187 if(rv < 0)
1188 {
1189 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on SWWV? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1190 return -1;
1191 }
1192
1193 int resp_channel;
1194 std::string resp_state;
1195
1197
1198 if(rv == 0)
1199 {
1200 if(resp_channel != channel)
1201 {
1202 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1203 return -1;
1204 }
1205
1206 state = resp_state;
1207 }
1208 else
1209 {
1210 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1211 return -1;
1212 }
1213
1214 return 0;
1215}
1216
1217inline
1218int siglentSDG::queryBTWV( std::string & state,
1219 int channel
1220 )
1221{
1222 int rv;
1223
1224 if(channel < 1 || channel > 2) return -1;
1225
1226 std::string strRead;
1227
1228 std::string com = makeCommand(channel, "BTWV?");
1229
1230 rv = writeRead( strRead, com);
1231
1232 if(rv < 0)
1233 {
1234 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on BTWV? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1235 return -1;
1236 }
1237
1238 int resp_channel;
1239 std::string resp_state;
1240
1242
1243 if(rv == 0)
1244 {
1245 if(resp_channel != channel)
1246 {
1247 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1248 return -1;
1249 }
1250
1251 state = resp_state;
1252 }
1253 else
1254 {
1255 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1256 return -1;
1257 }
1258
1259 return 0;
1260}
1261
1262inline
1263int siglentSDG::queryARWV( int & index,
1264 int channel
1265 )
1266{
1267 int rv;
1268
1269 if(channel < 1 || channel > 2) return -1;
1270
1271 std::string strRead;
1272
1273 std::string com = makeCommand(channel, "ARWV?");
1274
1275 rv = writeRead( strRead, com);
1276
1277 if(rv < 0)
1278 {
1279 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on ARWV? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1280 return -1;
1281 }
1282
1283 int resp_channel;
1284 int resp_index;
1285
1287
1288 if(rv == 0)
1289 {
1290 if(resp_channel != channel)
1291 {
1292 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1293 return -1;
1294 }
1295
1296 index = resp_index;
1297 }
1298 else
1299 {
1300 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1301 return -1;
1302 }
1303
1304 return 0;
1305}
1306
1307inline
1308int siglentSDG::queryBSWV( int channel)
1309{
1310 int rv;
1311
1312 if(channel < 1 || channel > 2) return -1;
1313
1314 std::string strRead;
1315
1316 std::string com = makeCommand(channel, "BSWV?");
1317
1318 rv = writeRead( strRead, com);
1319
1320 if(rv < 0)
1321 {
1322 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on BSWV? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1323 return -1;
1324 }
1325
1326 int resp_channel;
1327 std::string resp_wvtp;
1329
1331
1332 if(rv == 0)
1333 {
1334 if(resp_channel != channel)
1335 {
1336 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1337 return -1;
1338 }
1339
1340 if(channel == 1)
1341 {
1344 m_C1vpp = resp_amp;
1348
1350 if(m_C1vpp_tgt == -1) m_C1vpp_tgt = m_C1vpp;
1351
1352 recordParams();
1353
1357
1359
1364 if(m_waveform == "SINE"){updateIfChanged(m_indiP_C1phse, "value", resp_phse);}
1365 if(m_waveform == "PULSE"){updateIfChanged(m_indiP_C1wdth, "value", resp_wdth);}
1366 }
1367 else if(channel == 2)
1368 {
1371 m_C2vpp = resp_amp;
1375
1377 if(m_C2vpp_tgt == -1) m_C2vpp_tgt = m_C2vpp;
1378
1379 recordParams();
1380
1389 if(m_waveform == "SINE"){updateIfChanged(m_indiP_C2phse, "value", resp_phse);}
1390 if(m_waveform == "PULSE"){updateIfChanged(m_indiP_C2wdth, "value", resp_wdth);}
1391 }
1392 }
1393 else
1394 {
1395 log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1396 return -1;
1397 }
1398
1399 return 0;
1400}
1401
1402inline
1403int siglentSDG::querySYNC( bool & sync,
1404 int channel
1405 )
1406{
1407 int rv;
1408
1409 if(channel < 1 || channel > 2) return -1;
1410
1411 std::string strRead;
1412
1413 std::string com = makeCommand(channel, "SYNC?");
1414
1415 rv = writeRead( strRead, com);
1416
1417 if(rv < 0)
1418 {
1419 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on SYNC? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1420 return -1;
1421 }
1422
1423 int resp_channel;
1424 bool resp_sync;
1425
1427
1428 if(rv == 0)
1429 {
1430 if(resp_channel != channel)
1431 {
1432 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1433 return -1;
1434 }
1435
1436 sync = resp_sync;
1437 }
1438 else
1439 {
1440 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1441 return -1;
1442 }
1443
1444 return 0;
1445}
1446
1447inline
1448int siglentSDG::queryOUTP( int channel )
1449{
1450 int rv;
1451
1452 if(channel < 1 || channel > 2) return -1;
1453
1454 std::string strRead;
1455
1456 std::string com = makeCommand(channel, "OUTP?");
1457
1458 rv = writeRead( strRead, com);
1459
1460 if(rv < 0)
1461 {
1462 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<text_log>("Error on OUTP? for channel " + mx::ioutils::convertToString<int>(channel), logPrio::LOG_ERROR);
1463 return -1;
1464 }
1465
1466 int resp_channel;
1467 int resp_output;
1468
1470
1471 if(rv == 0)
1472 {
1473 if(resp_channel != channel)
1474 {
1475 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, "wrong channel returned"});
1476 return -1;
1477 }
1478
1479 std::string ro;
1480 if(resp_output > 0) ro = "On";
1481 else if(resp_output == 0 ) ro = "Off";
1482 else ro = "UNK";
1483
1484 if(channel == 1)
1485 {
1487 recordParams();
1488 updateIfChanged(m_indiP_C1outp, "value", ro);
1489 }
1490
1491 else if(channel == 2)
1492 {
1494 recordParams();
1495 updateIfChanged(m_indiP_C2outp, "value", ro);
1496 }
1497 }
1498 else
1499 {
1500 if((m_powerState != 1 || m_powerTargetState != 1) && !m_shutdown) log<software_error>({__FILE__,__LINE__, 0, rv, "parse error"});
1501 return -1;
1502 }
1503
1504 return 0;
1505}
1506
1507inline
1509{
1510 std::string state;
1511 int index;
1512 int rv;
1513
1514 rv = queryMDWV(state, 1);
1515
1516 if(rv < 0)
1517 {
1519 return rv;
1520 }
1521
1522 if(state != "OFF")
1523 {
1524 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 1 MDWV not OFF");
1525 return 1;
1526 }
1527
1528 rv = queryMDWV(state, 2);
1529
1530 if(rv < 0)
1531 {
1533 return rv;
1534 }
1535
1536 if(state != "OFF")
1537 {
1538 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 2 MDWV not OFF");
1539 return 1;
1540 }
1541
1542 rv = querySWWV(state, 1);
1543
1544 if(rv < 0)
1545 {
1547 return rv;
1548 }
1549
1550 if(state != "OFF")
1551 {
1552 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 1 SWWV not OFF");
1553 return 1;
1554 }
1555
1556 rv = querySWWV(state, 2);
1557
1558 if(rv < 0)
1559 {
1561 return rv;
1562 }
1563
1564 if(state != "OFF")
1565 {
1566 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 2 SWWV no OFF");
1567 return 1;
1568 }
1569
1570 rv = queryBTWV(state, 1);
1571
1572 if(rv < 0)
1573 {
1575 return rv;
1576 }
1577
1578 if(state != "OFF")
1579 {
1580 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 1 BTWV not OFF");
1581 return 1;
1582 }
1583
1584 rv = queryBTWV(state, 2);
1585
1586 if(rv < 0)
1587 {
1589 return rv;
1590 }
1591
1592 if(state != "OFF")
1593 {
1594 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 2 BTWV not OFF");
1595 return 1;
1596 }
1597
1598 rv = queryARWV(index, 1);
1599
1600 if(rv < 0)
1601 {
1603 return rv;
1604 }
1605
1606 if(index != 0)
1607 {
1608 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 1 ARWV not 1");
1609 return 1;
1610 }
1611
1612 rv = queryARWV(index, 2);
1613
1614 if(rv < 0)
1615 {
1617 return rv;
1618 }
1619
1620 if(index != 0)
1621 {
1622 if((m_powerState != 1 || m_powerTargetState != 1) > 0 && !m_shutdown) log<text_log>("Channel 2 ARWV not 1");
1623 return 1;
1624 }
1625
1626 rv = querySYNC(m_C1sync, 1);
1627
1628 if(rv < 0)
1629 {
1631 return rv;
1632 }
1633
1634 rv = querySYNC(m_C2sync, 2);
1635
1636 if(rv < 0)
1637 {
1639 return rv;
1640 }
1641
1642
1643 return 0;
1644}
1645
1646inline
1648{
1649
1650 std::cerr << "Normalizing . . .";
1651
1652 recordParams(true);
1653
1654 changeOutp(1, "OFF");
1655 changeOutp(2, "OFF");
1656
1657 std::string afterColon;
1658 std::string command;
1659
1660 afterColon = "MDWV STATE,OFF";
1663
1666
1667 afterColon = "SWWV STATE,OFF";
1670
1673
1674 afterColon = "BTWV STATE,OFF";
1677
1680
1681 afterColon = "ARWV INDEX,0";
1684
1687
1690
1691 changeFreq(1, 0);
1692 changeFreq(2, 0);
1693
1696
1697 if(m_waveform == "SINE")
1698 {
1699 changePhse(1, 0);
1700 changePhse(2, 0);
1701 }
1702 else if(m_waveform == "PULSE")
1703 {
1704 changeWdth(1, 0);
1705 changeWdth(2, 0);
1706 }
1707
1708 changeOfst(1, m_C1ofst);
1709 changeOfst(2, m_C2ofst);
1710
1711 changeWvtp(1, "DC");
1712 changeWvtp(2, "DC");
1713
1714
1715 if(m_C1outpOn && m_waveform == "PULSE")
1716 {
1717 changeOutp(1, "ON");
1718 }
1719 else
1720 {
1721 changeOutp(1, "OFF");
1722 }
1723 if(m_C2outpOn && m_waveform == "PULSE")
1724 {
1725 changeOutp(2, "ON");
1726 }
1727 else
1728 {
1729 changeOutp(2, "OFF");
1730 }
1731
1734
1735 recordParams(true);
1736
1737 std::cerr << "Done\n";
1738 return 0;
1739}
1740
1741inline
1742int siglentSDG::changeOutp( int channel,
1743 const std::string & newOutp
1744 )
1745{
1746 if(channel < 1 || channel > 2) return -1;
1747
1748 std::string no;
1749
1750 if(newOutp == "Off" || newOutp == "OFF" || newOutp == "off") no = "OFF";
1751 else if(newOutp == "On" || newOutp == "ON" || newOutp == "on") no = "ON";
1752 else
1753 {
1754 log<software_error>({__FILE__, __LINE__, "Invalid OUTP spec: " + newOutp});
1755 return -1;
1756 }
1757
1758 std::string afterColon = "OUTP " + no;
1759 std::string command = makeCommand(channel, afterColon);
1760
1761 log<text_log>("Ch. " + std::to_string(channel) + " OUTP to " + newOutp, logPrio::LOG_NOTICE);
1762
1763 recordParams(true);
1764 int rv = writeCommand(command);
1765 recordParams(true);
1766
1767 if(rv < 0)
1768 {
1770 return -1;
1771 }
1772
1773
1774 if(channel == 1 && no == "ON")
1775 {
1776 if(changeSync(1, true) < 0)
1777 {
1778 return log<software_error,-1>({__FILE__, __LINE__});
1779 }
1780 }
1781 return 0;
1782}
1783
1784inline
1785int siglentSDG::changeOutp( int channel,
1786 const pcf::IndiProperty &ipRecv
1787 )
1788{
1789 if(channel < 1 || channel > 2) return -1;
1790
1791 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
1792
1793 std::string newOutp;
1794 try
1795 {
1796 newOutp = ipRecv["value"].get<std::string>();
1797 }
1798 catch(...)
1799 {
1800 log<software_error>({__FILE__, __LINE__, "Exception caught."});
1801 return -1;
1802 }
1803
1804 //Make sure we don't change things while other things are being updated.
1805 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
1806
1809
1810 int rv = changeOutp(channel, newOutp);
1812
1814
1815 return rv;
1816}
1817
1818inline
1819int siglentSDG::changeFreq( int channel,
1820 double newFreq
1821 )
1822{
1823 if(channel < 1 || channel > 2) return -1;
1824
1825 if(newFreq > m_maxFreq.back())
1826 {
1827 newFreq = m_maxFreq.back();
1828 }
1829
1830 if(newFreq < 0)
1831 {
1832 newFreq = 0;
1833 }
1834
1835 if(m_waveform != "PULSE"){
1836 // Do not limit amp if a PULSE wave
1837
1838 double amp = m_C1vpp_tgt;
1839 if(channel == 2) amp = m_C2vpp_tgt;
1840
1841 size_t i =0;
1842 while( i < m_ampMax.size())
1843 {
1844 if(m_maxFreq[i] >= newFreq) break;
1845 ++i;
1846 }
1847
1848 std::cerr << "Max Amp @ " << amp << " = " << m_ampMax[i] << " (freq)\n";
1849
1850 if( amp > m_ampMax[i] )
1851 {
1852 log<text_log>("Ch. " + std::to_string(channel) + " FREQ not set due to amplitude exceeding limit for " + std::to_string(newFreq), logPrio::LOG_WARNING);
1853 return 0;
1854 }
1855
1856 }
1857
1858 //Now we update target
1859 if(channel==1)
1860 {
1862 }
1863 else
1864 {
1866 }
1867
1868
1869 std::string afterColon = "BSWV FRQ," + mx::ioutils::convertToString<double>(newFreq);
1870 std::string command = makeCommand(channel, afterColon);
1871
1872 log<text_log>("Ch. " + std::to_string(channel) + " FREQ to " + std::to_string(newFreq), logPrio::LOG_NOTICE);
1873
1874 recordParams(true);
1875 int rv = writeCommand(command);
1876 recordParams(true);
1877
1878 if(rv < 0)
1879 {
1881 return -1;
1882 }
1883
1884 // automatically set the pulse width when setting a new frequency
1885 if(m_waveform == "PULSE")
1886 {
1888
1889 if(newFreq <= 0)
1890 {
1891 log<text_log>("Ch. " + std::to_string(channel) + " WDTH defaulting to 0.0 for invalid freq: " + std::to_string(newWdth), logPrio::LOG_NOTICE);
1892 }
1893 else if(newFreq > 2000.0)
1894 {
1895 log<text_log>("Ch. " + std::to_string(channel) + " WDTH auto-changing to duty cycle limit: " + std::to_string(newWdth), logPrio::LOG_NOTICE);
1896 }
1897 else
1898 {
1899 log<text_log>("Ch. " + std::to_string(channel) + " WDTH auto-changing to 250us ideal case: " + std::to_string(newWdth), logPrio::LOG_NOTICE);
1900 }
1901
1902 changeWdth(channel, newWdth);
1903 }
1904
1905 return 0;
1906}
1907
1908inline
1909int siglentSDG::changeFreq( int channel,
1910 const pcf::IndiProperty &ipRecv
1911 )
1912{
1913 if(channel < 1 || channel > 2) return -1;
1914
1915 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
1916
1917 double newFreq;
1918 try
1919 {
1920 newFreq = ipRecv["target"].get<double>();
1921 }
1922 catch(...)
1923 {
1924 log<software_error>({__FILE__, __LINE__, "Exception caught."});
1925 return -1;
1926 }
1927
1928 //Make sure we don't change things while other things are being updated.
1929 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
1932
1933 int rv = changeFreq(channel,newFreq);
1935
1937
1938 return rv;
1939}
1940
1941inline
1942int siglentSDG::changeAmp( int channel,
1943 double newAmp
1944 )
1945{
1946 if(channel < 1 || channel > 2) return -1;
1947
1948 double offst = m_C1ofst;
1949 if(channel == 2) offst = m_C2ofst;
1950
1951 double confAmpMax = m_C1ampMax;
1952 if(channel == 2) confAmpMax = m_C2ampMax;
1953
1954 if (0.5 * newAmp + offst > confAmpMax)
1955 {
1956 newAmp = 2 * (confAmpMax - offst);
1957 log<text_log>("Ch. " + std::to_string(channel) + " AMP max-limited by config value to " + std::to_string(newAmp), logPrio::LOG_WARNING);
1958
1959 }
1960
1961 // Do not limit freq if a PULSE wave
1962 if(m_waveform != "PULSE")
1963 {
1964
1965 //Ensure we won't excede the 0-10V range for SINE
1966 if(offst + 0.5*newAmp > 10)
1967 {
1968 newAmp = 2.*(10.0 - offst);
1969 log<text_log>("Ch. " + std::to_string(channel) + " AMP limited at 10 V by OFST to " + std::to_string(newAmp), logPrio::LOG_WARNING);
1970 }
1971
1972 if(offst - 0.5*newAmp < 0)
1973 {
1974 newAmp = 2*(offst);
1975 log<text_log>("Ch. " + std::to_string(channel) + " AMP limited at 0 V by OFST to " + std::to_string(newAmp), logPrio::LOG_WARNING);
1976 }
1977
1978 double freq = m_C1frequency_tgt;
1979 if(channel == 2) freq = m_C2frequency_tgt;
1980
1981 double ampMax;
1982 size_t i=0;
1983 while(i < m_ampMax.size())
1984 {
1985 if( m_maxFreq[i] >= freq ) break;
1986 ++i;
1987 }
1988
1989 std::cerr << "Max Amp @ " << freq << " = " << ampMax << "\n";
1990
1991 //Ensure we don't exced safe ranges for device
1992 if(newAmp > ampMax)
1993 {
1994 newAmp = ampMax;
1995 log<text_log>("Ch. " + std::to_string(channel) + " AMP max-limited to " + std::to_string(newAmp), logPrio::LOG_WARNING);
1996 }
1997
1998 if(newAmp < 0)
1999 {
2000 newAmp = 0;
2001 log<text_log>("Ch. " + std::to_string(channel) + " AMP min-limited to " + std::to_string(newAmp), logPrio::LOG_WARNING);
2002 }
2003 }
2004
2005 //Now update target
2006 if(channel==1)
2007 {
2009 }
2010 else
2011 {
2013 }
2014
2015
2016 std::string afterColon = "BSWV AMP," + mx::ioutils::convertToString<double>(newAmp);
2017 std::string command = makeCommand(channel, afterColon);
2018
2019 log<text_log>("Ch. " + std::to_string(channel) + " AMP set to " + std::to_string(newAmp), logPrio::LOG_NOTICE);
2020
2021 recordParams(true);
2022 int rv = writeCommand(command);
2023 recordParams(true);
2024
2025 if(rv < 0)
2026 {
2028 return -1;
2029 }
2030
2031 return 0;
2032}
2033
2034inline
2035int siglentSDG::changeAmp( int channel,
2036 const pcf::IndiProperty &ipRecv
2037 )
2038{
2039 if(channel < 1 || channel > 2) return -1;
2040
2041 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
2042
2043 double newAmp;
2044 try
2045 {
2046 newAmp = ipRecv["target"].get<double>();
2047 }
2048 catch(...)
2049 {
2050 log<software_error>({__FILE__, __LINE__, "Exception caught."});
2051 return -1;
2052 }
2053
2054 //Make sure we don't change things while other things are being updated.
2055 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
2056
2059
2060 int rv = changeAmp(channel, newAmp);
2062
2064
2065 return rv;
2066}
2067
2068inline
2069int siglentSDG::changeOfst( int channel,
2070 double newOfst
2071 )
2072{
2073 if(channel < 1 || channel > 2) return -1;
2074
2075 double amp = m_C1vpp;
2076 if(channel == 2) amp = m_C2vpp;
2077
2078 double ampMax = m_C1ampMax;
2079 if(channel == 2) ampMax = m_C2ampMax;
2080
2081
2082 if(newOfst + 0.5*amp > ampMax)
2083 {
2084 newOfst = ampMax - 0.5*amp;
2085 log<text_log>("Ch. " + std::to_string(channel) + " OFST limited at " + std::to_string(ampMax) + " V by AMP to " + std::to_string(newOfst), logPrio::LOG_WARNING);
2086 }
2087
2088 if(newOfst - 0.5*amp < 0)
2089 {
2090 newOfst = 0.5*amp;
2091 log<text_log>("Ch. " + std::to_string(channel) + " OFST limited at 0 V by AMP to " + std::to_string(newOfst), logPrio::LOG_WARNING);
2092 }
2093
2094 if(newOfst > cs_MaxOfst)
2095 {
2097 log<text_log>("Ch. " + std::to_string(channel) + " OFST max-limited to " + std::to_string(newOfst), logPrio::LOG_WARNING);
2098 }
2099
2100 if(newOfst < 0.0)
2101 {
2102 newOfst = 0.0;
2103 log<text_log>("Ch. " + std::to_string(channel) + " OFST min-limited to " + std::to_string(newOfst), logPrio::LOG_WARNING);
2104 }
2105
2106 std::string afterColon = "BSWV OFST," + mx::ioutils::convertToString<double>(newOfst);
2107 std::string command = makeCommand(channel, afterColon);
2108
2109 log<text_log>("Ch. " + std::to_string(channel) + " OFST set to " + std::to_string(newOfst), logPrio::LOG_NOTICE);
2110
2111 int rv = writeCommand(command);
2112
2113 if(rv < 0)
2114 {
2116 return -1;
2117 }
2118
2119 return 0;
2120}
2121
2122inline
2123int siglentSDG::changeOfst( int channel,
2124 const pcf::IndiProperty &ipRecv
2125 )
2126{
2127 if(channel < 1 || channel > 2) return -1;
2128
2129 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
2130
2131 double newOfst;
2132 try
2133 {
2134 newOfst = ipRecv["value"].get<double>();
2135 }
2136 catch(...)
2137 {
2138 log<software_error>({__FILE__, __LINE__, "Exception caught."});
2139 return -1;
2140 }
2141
2142 //Make sure we don't change things while other things are being updated.
2143 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
2144
2147
2148 int rv = changeOfst(channel, newOfst);
2150
2152
2153 return rv;
2154}
2155
2156inline
2157int siglentSDG::changePhse( int channel,
2158 double newPhse
2159 )
2160{
2161 if(channel < 1 || channel > 2) return -1;
2162
2163 if(m_waveform == "PULSE"){
2164 log<text_log>("Ch. " + std::to_string(channel) + " PHSE not set for PULSE waveform.", logPrio::LOG_WARNING);
2165 return 0;
2166 }
2167
2168 std::string afterColon = "BSWV PHSE," + mx::ioutils::convertToString<double>(newPhse);
2169 std::string command = makeCommand(channel, afterColon);
2170
2171 log<text_log>("Ch. " + std::to_string(channel) + " PHSE to " + std::to_string(newPhse), logPrio::LOG_NOTICE);
2172
2173 int rv = writeCommand(command);
2174
2175 if(rv < 0)
2176 {
2178 return -1;
2179 }
2180
2181 return 0;
2182}
2183
2184inline
2185int siglentSDG::changePhse( int channel,
2186 const pcf::IndiProperty &ipRecv
2187 )
2188{
2189 if(channel < 1 || channel > 2) return -1;
2190
2191 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
2192
2193 double newPhse;
2194 try
2195 {
2196 newPhse = ipRecv["value"].get<double>();
2197 }
2198 catch(...)
2199 {
2200 log<software_error>({__FILE__, __LINE__, "Exception caught."});
2201 return -1;
2202 }
2203
2204 //Make sure we don't change things while other things are being updated.
2205 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
2206
2209
2210 int rv = changePhse(channel, newPhse);
2212
2214
2215 return rv;
2216}
2217
2218inline
2219int siglentSDG::changeWdth( int channel,
2220 double newWdth
2221 )
2222{
2223 if(channel < 1 || channel > 2) return -1;
2224
2225 if(m_waveform != "PULSE"){
2226 log<text_log>("Ch. " + std::to_string(channel) + " WDTH can not be set, waveforem not PULSE.", logPrio::LOG_WARNING);
2227 return 0;
2228 }
2229
2230 std::string afterColon = "BSWV WIDTH," + mx::ioutils::convertToString<double>(newWdth);
2231 std::string command = makeCommand(channel, afterColon);
2232
2233 log<text_log>("Ch. " + std::to_string(channel) + " WDTH to " + std::to_string(newWdth), logPrio::LOG_NOTICE);
2234
2235 int rv = writeCommand(command);
2236
2237 if(rv < 0)
2238 {
2240 return -1;
2241 }
2242
2243 return 0;
2244}
2245
2246inline
2247int siglentSDG::changeWdth( int channel,
2248 const pcf::IndiProperty &ipRecv
2249 )
2250{
2251 if(channel < 1 || channel > 2) return -1;
2252
2253 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
2254
2255 double newWdth;
2256 try
2257 {
2258 newWdth = ipRecv["value"].get<double>();
2259 }
2260 catch(...)
2261 {
2262 log<software_error>({__FILE__, __LINE__, "Exception caught."});
2263 return -1;
2264 }
2265
2266 //Make sure we don't change things while other things are being updated.
2267 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
2268
2271
2272 int rv = changeWdth(channel, newWdth);
2274
2276
2277 return rv;
2278}
2279
2280
2281inline
2282int siglentSDG::changeWvtp( int channel,
2283 const std::string & newWvtp
2284 )
2285{
2286 if(channel < 1 || channel > 2) return -1;
2287
2288 std::string afterColon = "BSWV WVTP," + newWvtp;
2289 std::string command = makeCommand(channel, afterColon);
2290
2291 log<text_log>("Ch. " + std::to_string(channel) + " WVTP to " + newWvtp, logPrio::LOG_NOTICE);
2292
2293 int rv = writeCommand(command);
2294
2295 if(rv < 0)
2296 {
2298 return -1;
2299 }
2300
2301 return 0;
2302}
2303
2304inline
2305int siglentSDG::changeWvtp( int channel,
2306 const pcf::IndiProperty &ipRecv
2307 )
2308{
2309 if(channel < 1 || channel > 2) return -1;
2310
2311 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
2312
2313 std::string newWvtp;
2314 try
2315 {
2316 newWvtp = ipRecv["value"].get<std::string>();
2317 }
2318 catch(...)
2319 {
2320 log<software_error>({__FILE__, __LINE__, "Exception caught."});
2321 return -1;
2322 }
2323
2324 //Make sure we don't change things while other things are being updated.
2325 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
2326
2329
2330 int rv = changeWvtp(channel, newWvtp);
2332
2334
2335 return rv;
2336}
2337
2338inline
2339int siglentSDG::changeSync( int channel,
2340 const bool newSync
2341 )
2342{
2343 if(channel < 1 || channel > 2) return -1;
2344
2345 std::string afterColon = "SYNC ";
2346 if(newSync) afterColon += "ON";
2347 else afterColon += "OFF";
2348
2349 std::string command = makeCommand(channel, afterColon);
2350
2351 if(newSync) log<text_log>("Ch. " + std::to_string(channel) + " SYNC to ON", logPrio::LOG_NOTICE);
2352 else log<text_log>("Ch. " + std::to_string(channel) + " SYNC to OFF", logPrio::LOG_NOTICE);
2353
2354 recordParams(true);
2355 int rv = writeCommand(command);
2356 recordParams(true);
2357
2358 if(rv < 0)
2359 {
2361 return -1;
2362 }
2363
2364 return 0;
2365}
2366
2367inline
2368int siglentSDG::changeSync( int channel,
2369 const pcf::IndiProperty &ipRecv
2370 )
2371{
2372 if(channel < 1 || channel > 2) return -1;
2373
2374 if(state() != stateCodes::READY && state() != stateCodes::OPERATING) return 0;
2375
2376 bool newSync;
2377
2378 if(!ipRecv.find("toggle")) return 0;
2379
2380 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
2381 {
2382 newSync = false;
2383 }
2384
2385 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
2386 {
2387 newSync = true;
2388 }
2389
2390 //Make sure we don't change things while other things are being updated.
2391 std::lock_guard<std::mutex> guard(m_indiMutex); //Lock the mutex before conducting any communications.
2392
2395
2396 int rv = changeSync(channel, newSync);
2398
2400
2401 return rv;
2402}
2403
2404INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1outp)(const pcf::IndiProperty &ipRecv)
2405{
2406 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1outp, ipRecv);
2407
2408 return changeOutp(1, ipRecv);
2409
2410}
2411
2412INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1freq)(const pcf::IndiProperty &ipRecv)
2413{
2414 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1freq, ipRecv);
2415
2416 return changeFreq(1, ipRecv);
2417}
2418
2419INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1amp)(const pcf::IndiProperty &ipRecv)
2420{
2421 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1amp, ipRecv);
2422
2423 return changeAmp(1, ipRecv);
2424}
2425
2426INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1ofst)(const pcf::IndiProperty &ipRecv)
2427{
2428 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1ofst, ipRecv);
2429
2430 return changeOfst(1, ipRecv);
2431}
2432
2433INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1phse)(const pcf::IndiProperty &ipRecv)
2434{
2435 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1phse, ipRecv);
2436
2437 return changePhse(1, ipRecv);
2438}
2439
2440INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1wdth)(const pcf::IndiProperty &ipRecv)
2441{
2442 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1wdth, ipRecv);
2443
2444 return changeWdth(1, ipRecv);
2445}
2446
2447INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1wvtp)(const pcf::IndiProperty &ipRecv)
2448{
2449 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1wvtp, ipRecv);
2450
2451 return changeWvtp(1, ipRecv);
2452}
2453
2454INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C1sync)(const pcf::IndiProperty &ipRecv)
2455{
2456 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C1sync, ipRecv);
2457
2458 return changeSync(1, ipRecv);
2459}
2460
2461INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2outp)(const pcf::IndiProperty &ipRecv)
2462{
2463 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2outp, ipRecv);
2464
2465 return changeOutp(2, ipRecv);
2466}
2467
2468INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2freq)(const pcf::IndiProperty &ipRecv)
2469{
2470 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2freq, ipRecv);
2471
2472 return changeFreq(2, ipRecv);
2473}
2474
2475INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2amp)(const pcf::IndiProperty &ipRecv)
2476{
2477 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2amp, ipRecv);
2478
2479 return changeAmp(2, ipRecv);
2480}
2481
2482INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2ofst)(const pcf::IndiProperty &ipRecv)
2483{
2484 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2ofst, ipRecv);
2485
2486 return changeOfst(2, ipRecv);
2487}
2488
2489INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2phse)(const pcf::IndiProperty &ipRecv)
2490{
2491 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2phse, ipRecv);
2492
2493 return changePhse(2, ipRecv);
2494}
2495
2496INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2wdth)(const pcf::IndiProperty &ipRecv)
2497{
2498 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2wdth, ipRecv);
2499
2500 return changeWdth(2, ipRecv);
2501}
2502
2503INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2wvtp)(const pcf::IndiProperty &ipRecv)
2504{
2505 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2wvtp, ipRecv);
2506
2507 return changeWvtp(2, ipRecv);
2508}
2509
2510INDI_NEWCALLBACK_DEFN(siglentSDG, m_indiP_C2sync)(const pcf::IndiProperty &ipRecv)
2511{
2512 INDI_VALIDATE_CALLBACK_PROPS(m_indiP_C2sync, ipRecv);
2513
2514 return changeSync(2, ipRecv);
2515}
2516
2517// todo: add change width INDI
2518
2519// todo: add change edge INDI
2520
2521inline
2526
2527inline
2529{
2530 return recordParams(true);
2531}
2532
2533inline
2535{
2536 static double old_C1outp = -1e30; //Ensure first time writes
2537 static double old_C1frequency = m_C1frequency;
2538 static double old_C1vpp = m_C1vpp;
2539 static double old_C1ofst = m_C1ofst;
2540 static double old_C1phse = m_C1phse;
2541 static double old_C1wdth = m_C1wdth;
2542 static std::string old_C1wvtp = m_C1wvtp;
2543 static bool old_C1sync = m_C1sync;
2544 static double old_C2outp = m_C2outp;
2545 static double old_C2frequency = m_C2frequency;
2546 static double old_C2vpp = m_C2vpp;
2547 static double old_C2ofst = m_C2ofst;
2548 static double old_C2phse = m_C2phse;
2549 static double old_C2wdth = m_C2wdth;
2550 static std::string old_C2wvtp = m_C2wvtp;
2551 static bool old_C2sync = m_C2sync;
2552
2553 bool write = false;
2554
2555 if(!force)
2556 {
2557 if( old_C1outp != m_C1outp ) write = true;
2558 else if( old_C1frequency != m_C1frequency ) write = true;
2559 else if( old_C1vpp != m_C1vpp ) write = true;
2560 else if( old_C1ofst != m_C1ofst ) write = true;
2561 else if( old_C1phse != m_C1phse ) write = true;
2562 else if( old_C1wdth != m_C1wdth ) write = true;
2563 else if( old_C1wvtp != m_C1wvtp ) write = true;
2564 else if( old_C1sync != m_C1sync ) write = true;
2565 else if( old_C2outp != m_C2outp ) write = true;
2566 else if( old_C2frequency != m_C2frequency ) write = true;
2567 else if( old_C2vpp != m_C2vpp ) write = true;
2568 else if( old_C2ofst != m_C2ofst ) write = true;
2569 else if( old_C2phse != m_C2phse ) write = true;
2570 else if( old_C2wdth != m_C2wdth ) write = true;
2571 else if( old_C2wvtp != m_C2wvtp ) write = true;
2572 else if( old_C2sync != m_C2sync ) write = true;
2573 }
2574
2575 // todo: add if statement for all of the write??
2576
2577 if(force || write)
2578 {
2579 uint8_t C1wvtp = 3;
2580 if(m_C1wvtp == "DC") C1wvtp = TELEM_FXNGEN_WVTP_DC;
2581 else if(m_C1wvtp == "SINE") C1wvtp = TELEM_FXNGEN_WVTP_SINE;
2582 else if(m_C1wvtp == "PULSE") C1wvtp = TELEM_FXNGEN_WVTP_PULSE;
2583
2584 uint8_t C2wvtp = 3;
2585 if(m_C2wvtp == "DC") C2wvtp = TELEM_FXNGEN_WVTP_DC;
2586 else if(m_C2wvtp == "SINE") C2wvtp = TELEM_FXNGEN_WVTP_SINE;
2587 else if(m_C2wvtp == "PULSE") C2wvtp = TELEM_FXNGEN_WVTP_PULSE;
2588
2592
2601
2610 }
2611
2612 return 0;
2613}
2614
2615} //namespace app
2616} //namespace MagAOX
2617
2618#endif //siglentSDG_hpp
The base-class for XWCTk applications.
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.
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.
unsigned long m_loopPause
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
int m_powerState
Current power state, 1=On, 0=Off, -1=Unk.
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.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
int m_powerTargetState
Current target power state, 1=On, 0=Off, -1=Unk.
static constexpr double cs_MaxVolts
int writeCommand(const std::string &commmand)
Write a command to the device.
int querySYNC(bool &sync, int channel)
Send the SYNC? query for a channel.
int writeRead(std::string &strRead, const std::string &command)
Write a command to the device and get the response. Not mutex-ed.
double m_C1ofst
The offset voltage of channel 1.
double m_C1vpp
The peak-2-peak voltage of channel 1.
int changePhse(int channel, double newPhse)
Send a change phase command to the device.
virtual int onPowerOff()
Implementation of the on-power-off FSM logic.
int queryOUTP(int channel)
Send the OUTP? query for a channel.
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1outp)
int recordParams(bool force=false)
std::vector< double > m_ampMax
int querySWWV(std::string &state, int channel)
Send the SWWV? query and get the response state.
int checkSetup()
Check the setup is correct and safe for PI TTM control.
double m_bootDelay
Time in seconds it takes the device to boot.
tty::telnetConn m_telnetConn
The telnet connection manager.
double m_C1frequency
The output frequency of channel 1.
std::string m_devicePort
The device port.
uint8_t m_C2outp
The output status channel 2.
double m_C1vppDefault
default value for vpp of channel 1
virtual int appStartup()
Startup functions.
int changeAmp(int channel, double newAmp)
Send a change amplitude command to the device.
pcf::IndiProperty m_indiP_C2sync
pcf::IndiProperty m_indiP_C2wvtp
double m_C2phse
The phase of channel 2 (SINE only)
pcf::IndiProperty m_indiP_C2freq
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1wvtp)
std::string m_C1wvtp
The wave type of channel 1.
int changeWdth(int channel, double newWdth)
Send a width command to the device.
double m_powerOnCounter
Counts the number of loops since power-on, used to control logging of connect failures.
~siglentSDG() noexcept
D'tor, declared and defined for noexcept.
virtual void loadConfig()
load the configuration system results (called by MagAOXApp::setup())
int changeWvtp(int channel, const std::string &newWvtp)
Send a change wavetype command to the device.
static constexpr double cs_MaxOfst
pcf::IndiProperty m_indiP_C1amp
pcf::IndiProperty m_indiP_C2wdth
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2phse)
pcf::IndiProperty m_indiP_status
pcf::IndiProperty m_indiP_C1peri
pcf::IndiProperty m_indiP_C2ofst
int queryMDWV(std::string &state, int channel)
Send the MDWV? query and get the response state.
double m_C2wdth
The width of channel 2 (PULSE only)
pcf::IndiProperty m_indiP_C2llev
double m_C2vppDefault
default value for vpp of channel 2
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1sync)
pcf::IndiProperty m_indiP_C1ofst
virtual int whilePowerOff()
Implementation of the while-powered-off FSM.
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1phse)
int changeOfst(int channel, double newOfst)
Send a change offset command to the device.
pcf::IndiProperty m_indiP_C2amp
pcf::IndiProperty m_indiP_C1wdth
pcf::IndiProperty m_indiP_C1wvtp
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2sync)
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2wdth)
int queryBSWV(int channel)
Send the BSWV? query for a channel.
pcf::IndiProperty m_indiP_C2peri
std::vector< double > m_maxFreq
pcf::IndiProperty m_indiP_C1freq
double m_C1phse
The phase of channel 1 (SINE only)
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2outp)
double m_C2setVoltage
the set position voltage of Ch. 2.
virtual int appLogic()
Implementation of the FSM for the Siglent SDG.
pcf::IndiProperty m_indiP_C2ampvrms
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2wvtp)
pcf::IndiProperty m_indiP_C1hlev
siglentSDG()
Default c'tor.
virtual void setupConfig()
Setup the configuration system (called by MagAOXApp::setup())
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1amp)
std::string m_deviceAddr
The device address.
pcf::IndiProperty m_indiP_C2hlev
int changeSync(int channel, bool newSync)
Send a change sync command to the device.
pcf::IndiProperty m_indiP_C1phse
pcf::IndiProperty m_indiP_C1outp
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1freq)
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2ofst)
int changeOutp(int channel, const std::string &newOutp)
Change the output status (on/off) of one channel.
pcf::IndiProperty m_indiP_C2outp
int recordTelem(const telem_fxngen *)
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2amp)
double m_C1wdth
The width of channel 1 (PULSE only)
int m_readTimeOut
The timeout for reading from the device [msec].
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1wdth)
int queryARWV(int &index, int channel)
Send the ARWV? query and get the response index.
pcf::IndiProperty m_indiP_C1llev
double m_C2frequency
The output frequency of channel 2.
virtual int appShutdown()
Do any needed shutdown tasks. Currently nothing in this app.
int m_writeTimeOut
The timeout for writing to the device [msec].
int normalizeSetup()
Normalize the setup, called during connection if checkSetup shows a problem, or on power-up.
friend class siglentSDG_test
uint8_t m_C1outp
std::string m_clock; ///<INTernal or EXTernal
double m_C1ampMax
The maximum voltage output for channel 1.
pcf::IndiProperty m_indiP_C2phse
int changeFreq(int channel, double newFreq)
Send a change frequency command to the device.
double m_C2vpp
The peak-2-peak voltage of channel 2.
double m_C2ofst
The offset voltage of channel 2.
double m_C1setVoltage
the set position voltage of Ch. 1.
std::string m_C2wvtp
The wave type of channel 2.
pcf::IndiProperty m_indiP_C1sync
int queryBTWV(std::string &state, int channel)
Send the BTWV? query and get the response state.
pcf::IndiProperty m_indiP_C1ampvrms
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C1ofst)
INDI_NEWCALLBACK_DECL(siglentSDG, m_indiP_C2freq)
double m_C2ampMax
The maximum voltage output for channel 2.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
#define CREATE_REG_INDI_NEW_NUMBERF(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition ttyErrors.cpp:15
#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_OK
Definition indiUtils.hpp:28
int parseOUTP(int &channel, int &output, const std::string &strRead)
Parse the SDG response to the OUTP query.
const pcf::IndiProperty & ipRecv
int parseSWWV(int &channel, std::string &state, const std::string &strRead)
Parse the SDG response to the SWWV query.
int parseMDWV(int &channel, std::string &state, const std::string &strRead)
Parse the SDG response to the MDWV query.
std::string makeCommand(int channel, const std::string &afterColon)
int parseBSWV(int &channel, std::string &wvtp, double &freq, double &peri, double &amp, double &ampvrms, double &ofst, double &hlev, double &llev, double &phse, double &wdth, const std::string &strRead)
Parse the SDG response to the BSWV query.
int parseBTWV(int &channel, std::string &state, const std::string &strRead)
Parse the SDG response to the BTWV query.
int parseARWV(int &channel, int &index, const std::string &strRead)
Parse the SDG response to the ARWV query.
double autoPulseWidthFromFrequency(const double freqHz)
Calculate the auto pulse width for a requested pulse frequency.
int parseSYNC(int &channel, bool &sync, const std::string &strRead)
Parse the SDG response to the SYNC query.
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:19
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
#define SDG_PARSEERR_WVTP
A device base class which saves telemetry.
Definition telemeter.hpp:75
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
@ OPERATING
The device is operating, other than homing.
@ CONFIGURING
The application is configuring the device.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
@ POWERON
The device power is on.
int16_t stateCodeT
The type of the state code.
static std::string codeText(const stateCodeT &stateCode)
Get an ASCII string corresponding to an application stateCode.
Software ERR log entry.
Log entry recording the function generator parameters.
A Telnet connection manager, wrapping libtelnet.
int write(const std::string &buffWrite, int timeoutWrite)
Write to a telnet connection.
std::string m_strRead
The accumulated string read from the device.
int noLogin()
Set flags as if we're logged in, used when device doesn't require it.
std::string m_prompt
The device's prompt, used for detecting end of transmission.
int writeRead(const std::string &strWrite, bool swallowEcho, int timeoutWrite, int timeoutRead)
Write to a telnet connection, then get the reply.
int read(const std::string &eot, int timeoutRead, bool clear=true)
Read from a telnet connection, until end-of-transmission string is read.
int connect(const std::string &host, const std::string &port)
Connect to the device.
#define TELEM_FXNGEN_WVTP_DC
#define TELEM_FXNGEN_WVTP_PULSE
#define TELEM_FXNGEN_WVTP_SINE