File Coverage

blib/lib/Lab/Instrument/HP33120A.pm
Criterion Covered Total %
statement 20 860 2.3
branch 0 488 0.0
condition 0 204 0.0
subroutine 7 97 7.2
pod 85 85 100.0
total 112 1734 6.4


line stmt bran cond sub pod time code
1             package Lab::Instrument::HP33120A;
2             #ABSTRACT: HP 33120A 15MHz function/arbitrary waveform generator
3             $Lab::Instrument::HP33120A::VERSION = '3.881';
4 1     1   1874 use v5.20;
  1         5  
5              
6 1     1   6 use strict;
  1         3  
  1         21  
7 1     1   5 use warnings;
  1         2  
  1         24  
8 1     1   5 use Lab::Instrument;
  1         2  
  1         33  
9 1     1   7 use Try::Tiny;
  1         2  
  1         53  
10 1     1   6 use Carp;
  1         2  
  1         61  
11 1     1   7 use English;
  1         4  
  1         7  
12              
13             our @ISA = ("Lab::Instrument");
14              
15             our %fields = (
16             supported_connections => ['GPIB'],
17              
18             #default settings for connections
19              
20             connection_settings => {
21             gpib_board => 0,
22             gpib_address => undef,
23             },
24              
25             device_settings => {},
26              
27             scpi_override => {},
28              
29             device_cache => {
30             id => undef,
31             shape => undef,
32             frequency => undef,
33             amplitude => undef,
34             offset => undef,
35             duty_cycle => undef,
36             load => undef,
37             sync => undef,
38             vunit => undef,
39              
40             user_waveform => undef,
41             trigger_source => undef,
42             trigger_slope => undef,
43             display => undef,
44             am_depth => undef,
45             am_shape => undef,
46             am_frequency => undef,
47             am_source => undef,
48              
49             fm_deviation => undef,
50             fm_shape => undef,
51             fm_frequency => undef,
52              
53             burst_cycles => undef,
54             burst_phase => undef,
55             burst_rate => undef,
56             burst_source => undef,
57              
58             fsk_frequency => undef,
59             fsk_rate => undef,
60             fsk_source => undef,
61              
62             sweep_start_frequency => undef,
63             sweep_stop_frequency => undef,
64             sweep_spacing => undef,
65             sweep_time => undef,
66              
67             modulation => undef,
68             },
69              
70             waveforms => {
71             user => [],
72             },
73             );
74              
75              
76             sub new {
77 0     0 1   my $proto = shift;
78 0   0       my $class = ref($proto) || $proto;
79 0           my $self = $class->SUPER::new(@_);
80 0           $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  0            
81              
82 0           return $self;
83             }
84              
85             sub _device_init {
86              
87             # when NI-GPIB-USB-HS initially plugged in, first write
88             # fails with timeout, so try a 'write nothing significant', let it
89             # fail, after that it should be okay
90              
91 0     0     my $self = shift;
92             try {
93 0     0     $self->write( command => ' ', );
94             }
95 0     0     catch { 1; };
  0            
96              
97             }
98              
99              
100             sub get_id {
101 0     0 1   my $self = shift;
102 0           return $self->query('*IDN?');
103             }
104              
105              
106             sub get_status {
107 0     0 1   my $self = shift;
108 0           my $stb = $self->query("*STB?");
109 0           my $esr = $self->query("*ESR?");
110              
111 0           my (%status);
112 0 0         $status{'DATA'} = ( $stb & 0x10 ) == 0 ? 0 : 1;
113 0 0         $status{'ERROR'} = ( $esr & 0x3C ) == 0 ? 0 : 1;
114 0 0         $status{'QERR'} = ( $esr & 0x04 ) == 0 ? 0 : 1;
115 0 0         $status{'DERR'} = ( $esr & 0x08 ) == 0 ? 0 : 1;
116 0 0         $status{'EERR'} = ( $esr & 0x10 ) == 0 ? 0 : 1;
117 0 0         $status{'CERR'} = ( $esr & 0x20 ) == 0 ? 0 : 1;
118 0 0         $status{'PON'} = ( $esr & 0x80 ) == 0 ? 0 : 1;
119 0 0         $status{'OPC'} = ( $esr & 0x01 ) == 0 ? 0 : 1;
120 0           return %status;
121             }
122              
123              
124             sub get_error {
125 0     0 1   my $self = shift;
126 0           my $err = $self->query("SYST:ERR?");
127 0 0         if ( $err =~ /^\s*\+?0+\s*,/ ) {
128 0           return ( 0, '' ); # no error
129             }
130              
131 0           my ( $code, $msg ) = split( /,/, $err );
132 0           return ( $code, $msg );
133             }
134              
135              
136             our $rst_cache = {
137             shape => 'SIN',
138             frequency => 1000,
139             amplitude => 0.1,
140             offset => 0,
141             load => 50,
142             sync => 1,
143             vunit => 'VPP',
144              
145             trigger_source => 'IMM',
146             display => 1,
147             am_depth => 100,
148             am_shape => 'SIN',
149             am_frequency => 100,
150             am_source => 'INT',
151              
152             fm_deviation => 100,
153             fm_shape => 'SIN',
154             fm_frequency => 10,
155              
156             burst_cycles => 1,
157             burst_phase => 0,
158             burst_rate => 100,
159             burst_source => 'INT',
160              
161             fsk_frequency => 100,
162             fsk_rate => 10,
163             fsk_source => 'INT',
164              
165             sweep_start_frequency => 100,
166             sweep_stop_frequency => 1000,
167             sweep_spacing => 'LIN',
168             sweep_time => 1,
169              
170             modulation => 'NONE',
171             };
172              
173             sub reset {
174 0     0 1   my $self = shift;
175              
176 0           my $mod = $self->get_modulation( { read_mode => 'cache' } );
177              
178 0           $self->write('*RST');
179 0           $self->write('*CLS');
180 0           $self->wait_complete();
181              
182             # set cache to *RST values
183              
184 0           foreach my $k ( keys( %{$rst_cache} ) ) {
  0            
185 0           $self->{device_cache}->{$k} = $rst_cache->{$k};
186             }
187              
188 0 0         if ( $mod =~ /^SWE/i ) {
189 0           $self->{device_cache}->{sweep_start_frequency} = 0.01;
190 0           $self->{device_cache}->{sweep_stop_frequency} = 15000000;
191             }
192              
193             }
194              
195              
196             sub get_trigger_slope {
197 0     0 1   my $self = shift;
198 0           return $self->query('TRIG:SLOP?');
199             }
200              
201              
202             sub set_trigger_slope {
203 0     0 1   my $self = shift;
204 0           my $in = shift;
205 0           my $sl;
206 0 0         if ( $in =~ /^\s*[p+]/i ) {
    0          
207 0           $sl = 'POS';
208             }
209             elsif ( $in =~ /^\s*[n\-]/i ) {
210 0           $sl = 'NEG';
211             }
212             else {
213 0           Lab::Exception::CorruptParameter->throw(
214             "Invalid trigger slope '$in' [POS|NEG]\n");
215 0           return;
216             }
217 0           $self->write("TRIG:SLOP $sl");
218             }
219              
220              
221             sub wait_complete {
222 0     0 1   my $self = shift;
223 0           $self->write('*WAI');
224             }
225              
226              
227             sub trigger {
228 0     0 1   my $self = shift;
229 0           $self->write('*TRG');
230 0           $self->wait_complete();
231             }
232              
233              
234             sub get_trigger_source {
235 0     0 1   my $self = shift;
236 0           return $self->query("TRIG:SOUR?");
237             }
238              
239              
240             sub set_trigger_source {
241 0     0 1   my $self = shift;
242 0           my $in = shift;
243 0           my $s;
244 0 0         if ( $in =~ /^\s*IMM/i ) {
    0          
    0          
245 0           $s = 'IMM';
246             }
247             elsif ( $in =~ /^\s*BUS/i ) {
248 0           $s = 'BUS';
249             }
250             elsif ( $in =~ /^\s*EXT/i ) {
251 0           $s = 'EXT';
252             }
253             else {
254 0           Lab::Exception::CorruptParameter->throw(
255             "Set_trigger_source invalid input '$in' [IMM|BUS|EXT]\n");
256 0           return;
257             }
258 0           $self->write("TRIG:SOUR $s");
259             }
260              
261              
262             sub set_display {
263 0     0 1   my $self = shift;
264 0           my $in = shift;
265 0           my $state;
266 0 0         if ( $in =~ /^\s*(1|on|t|y)/i ) {
    0          
267 0           $state = 1;
268             }
269             elsif ( $in =~ /^\s*(0|of|f|n)/i ) {
270 0           $state = 0;
271             }
272             else {
273 0           Lab::Exception::CorruptParameter->throw(
274             "Invalid display setting '$in' [ON|OFF]\n");
275 0           return;
276             }
277 0           $self->write("DISP $state");
278             }
279              
280              
281             sub get_display {
282 0     0 1   my $self = shift;
283 0           return $self->query("DISP?");
284             }
285              
286              
287             sub set_text {
288 0     0 1   my $self = shift;
289 0           my $in = shift;
290 0           $in =~ s/\'/''/g;
291 0           $self->write("DISP:TEXT '$in'");
292             }
293              
294              
295             sub get_text {
296 0     0 1   my $self = shift;
297 0           my $txt = $self->query('DISP:TEXT?');
298 0           my (@s) = _parseStrings($txt);
299 0           return $s[0];
300             }
301              
302              
303             sub clear_text {
304 0     0 1   my $self = shift;
305 0           $self->write("DISP:TEXT:CLE");
306             }
307              
308              
309             sub beep {
310 0     0 1   my $self = shift;
311 0           $self->write("SYST:BEEP");
312             }
313              
314              
315             sub get_sync {
316 0     0 1   my $self = shift;
317 0           return $self->query("OUTP:SYNC?");
318             }
319              
320              
321             sub set_sync {
322 0     0 1   my $self = shift;
323 0           my $in = shift;
324 0           my $sync;
325 0 0         if ( $in =~ /^\s*(1|on|t|y)/i ) {
    0          
326 0           $sync = 1;
327             }
328             elsif ( $in =~ /^\s*(0|of|f|n)/i ) {
329 0           $sync = 0;
330             }
331             else {
332 0           Lab::Exception::CorruptParameter->throw(
333             "Sync '$in' not recognized as boolean \n");
334 0           return;
335             }
336 0           $self->write("OUTP:SYNC $sync");
337             }
338              
339              
340             sub save_setup {
341 0     0 1   my $self = shift;
342 0           my $n = shift;
343 0           $n = int( $n + 0.5 );
344 0 0 0       if ( $n < 0 || $n > 3 ) {
345 0           Lab::Exception::CorruptParameter->throw(
346             "Save '$n' out of range (0..3)\n");
347 0           return;
348             }
349 0           $self->write("*SAV $n");
350             }
351              
352              
353             sub recall_setup {
354 0     0 1   my $self = shift;
355 0           my $n = shift;
356 0           $n = int( $n + 0.5 );
357 0 0 0       if ( $n < 0 || $n > 3 ) {
358 0           Lab::Exception::CorruptParameter->throw(
359             "Recall '$n' out of range (0..3)\n");
360 0           return;
361             }
362 0           $self->write("*RCL $n");
363              
364             # invalidate the cache
365 0           $self->reset_device_cache();
366             }
367              
368              
369             sub delete_setup {
370 0     0 1   my $self = shift;
371 0           my $n = shift;
372 0           $n = int( $n + 0.5 );
373              
374 0 0 0       if ( $n < 0 || $n > 3 ) {
375 0           Lab::Exception::CorruptParameter->throw(
376             "Delete '$n' out of range (0..3)\n");
377 0           return;
378             }
379 0           $self->write("MEM:STAT:DEL $n");
380             }
381              
382              
383             sub get_load {
384 0     0 1   my $self = shift;
385 0           my $z = $self->query("OUTP:LOAD?");
386 0 0         $z = 'INF' if $z > 1000;
387 0           return $z;
388             }
389              
390              
391             sub set_load {
392 0     0 1   my $self = shift;
393 0           my $in = shift;
394 0           my $z;
395              
396 0 0         if ( $in =~ /^\s*inf/i ) {
397 0           $z = 'INF';
398             }
399             else {
400 0           my $zin = _parseNRf( $in, 'ohm' );
401 0 0         if ( $zin =~ /^ERR/i ) {
402 0           Lab::Exception::CorruptParameter->throw(
403             "Parse error in load impedance '$in': $zin\n");
404 0           return;
405             }
406              
407 0 0 0       if ( $zin ne 'MIN' && $zin ne 'MAX' ) {
408 0 0 0       if ( $zin > 40 && $zin < 60 ) {
    0          
409 0           $z = 50;
410             }
411             elsif ( $zin > 50e3 ) {
412 0           $z = 'INF';
413             }
414             else {
415 0           Lab::Exception::CorruptParameter->throw(
416             "Invalid load impedance '$in' [MIN,MAX,INF,50]\n");
417 0           return;
418             }
419             }
420             else {
421 0           $z = $zin;
422             }
423             }
424 0           $self->write("OUTP:LOAD $z");
425             }
426              
427              
428             sub get_shape {
429 0     0 1   my $self = shift;
430 0           return $self->query('FUNC:SHAP?');
431             }
432              
433              
434             sub set_shape {
435 0     0 1   my $self = shift;
436 0           my $shape = shift;
437 0           my $s;
438 0 0         if ( $shape =~ /^SIN/i ) {
    0          
    0          
    0          
    0          
    0          
    0          
439 0           $s = 'SIN';
440             }
441             elsif ( $shape =~ /^SQU/i ) {
442 0           $s = 'SQU';
443             }
444             elsif ( $shape =~ /^TRI/i ) {
445 0           $s = 'TRI';
446             }
447             elsif ( $shape =~ /^RAMP/i ) {
448 0           $s = 'RAMP';
449             }
450             elsif ( $shape =~ /^NOIS/i ) {
451 0           $s = 'NOIS';
452             }
453             elsif ( $shape =~ /^DC/i ) {
454 0           $s = 'DC';
455             }
456             elsif ( $shape =~ /^USER/i ) {
457 0           $s = 'USER';
458             }
459             else {
460 0           Lab::Exception::CorruptParameter->throw(
461             "Invalid function shape '$shape'\n");
462 0           return;
463             }
464 0           $self->write("FUNC:SHAP $s");
465             }
466              
467              
468             sub get_frequency {
469 0     0 1   my $self = shift;
470 0           return $self->query("FREQ?");
471             }
472              
473              
474             sub set_frequency {
475 0     0 1   my $self = shift;
476 0           my $freq = shift;
477 0           my $f = _parseNRf( $freq, 'hz' );
478              
479 0 0         if ( $f =~ /^ERR/i ) {
480 0           Lab::Exception::CorruptParameter->throw(
481             "Error parsing frequency '$freq': $f\n");
482 0           return;
483             }
484              
485 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
486 0           $f = sprintf( '%.11e', $f );
487 0           my $shape = $self->get_shape( { read_mode => 'cache' } );
488 0           my $maxf = 15e6;
489 0 0 0       $maxf = 100e3 if $shape eq 'RAMP' || $shape eq 'TRI';
490 0 0         $maxf = 5e6 if $shape eq 'USER'; # fix, points dependent
491              
492 0 0 0       if ( $f < 100e-6 || $f > $maxf ) {
493 0           Lab::Exception::CorruptParameter->throw(
494             "Frequency '$freq' out of valid range 100uHz..${maxf}Hz\n");
495 0           return;
496             }
497             }
498 0           $self->write("FREQ $f");
499             }
500              
501              
502             sub get_duty_cycle {
503 0     0 1   my $self = shift;
504 0           return $self->query('PULS:DCYC?');
505             }
506              
507              
508             sub set_duty_cycle {
509 0     0 1   my $self = shift;
510 0           my $in = shift;
511 0           my $dc = _parseNRf( $in, '' );
512              
513 0 0         if ( $dc =~ /^ERR/i ) {
514 0           Lab::Exception::CorruptParameter->throw(
515             "Error parsing duty-cycle '$in': $dc\n");
516 0           return;
517             }
518 0 0 0       if ( $dc ne 'MIN' && $dc ne 'MAX' ) {
519 0           my $f = $self->get_frequency( { read_mode => 'cache' } );
520              
521 0           $dc = int( $dc + 0.5 );
522 0           my $dcmin = 20;
523 0           my $dcmax = 80;
524 0 0         if ( $f > 5e6 ) {
525 0           $dcmin = 40;
526 0           $dcmax = 60;
527             }
528 0 0 0       if ( $dc < $dcmin || $dc > $dcmax ) {
529 0           Lab::Exception::CorruptParameter->throw(
530             "Duty-cycle '$in' outside valid range ($dcmin..$dcmax)\n");
531 0           return;
532             }
533             }
534 0           $self->write("PULS:DCYC $dc");
535             }
536              
537              
538             sub get_amplitude {
539 0     0 1   my $self = shift;
540 0           return $self->query("volt?");
541             }
542              
543              
544             sub set_amplitude {
545 0     0 1   my $self = shift;
546 0           my $in = shift;
547 0           my $v = _parseNRf( $in, 'v', 'db', 'dbv' );
548              
549 0 0         if ( $v =~ /^ERR/i ) {
550 0           Lab::Exception::CorruptParameter->throw(
551             "Error parsing amplitude '$in': $v\n");
552 0           return;
553             }
554 0 0 0       if ( $v ne 'MIN' && $v ne 'MAX' ) {
555 0           $v = sprintf( '%.4e', $v );
556 0           my $z = $self->get_load( { read_mode => 'cache' } );
557 0           my $u = $self->get_vunit( { read_mode => 'cache' } );
558 0           my $s = $self->get_shape( { read_mode => 'cache' } );
559 0           my $voff = $self->get_offset( { read_mode => 'cache' } );
560              
561 0           my $vpp;
562 0 0 0       if ( $u eq 'VPP' ) {
    0          
563 0           $vpp = $v;
564             }
565             elsif ( $u eq 'VRMS' || $u eq 'DBM' ) {
566 0           my $vrms = $v;
567 0           $vrms = 0.224 * ( 10**( $v / 20 ) );
568 0 0 0       if ( $s eq 'SQU' ) {
    0          
    0          
    0          
    0          
    0          
569 0           $vpp = 2 * $vrms;
570             }
571             elsif ( $s eq 'DC' ) {
572 0           $vpp = $vrms;
573             }
574             elsif ( $s eq 'SIN' ) {
575 0           $vpp = 2 * sqrt(2) * $vrms;
576             }
577             elsif ( $s eq 'TRI' || $s eq 'RAMP' ) {
578 0           $vpp = 2 * sqrt(3) * $vrms;
579             }
580             elsif ( $s eq 'NOIS' ) {
581 0           $vpp = 6.6 * $vrms; # a guess, 99.9% of the time
582             }
583             elsif ( $s eq 'USER' ) {
584 0           $vpp = 2 * sqrt(2) * $vrms; # a guess, fix later
585             }
586             }
587              
588 0           my $vmin = 100e-3;
589 0           my $vmax = 20;
590 0 0         $vmin = 50e-3 if $z == 50;
591 0 0         $vmax = 10 if $z == 50;
592              
593 0 0 0       if ( $vpp < $vmin || $vpp > $vmax ) {
594 0           Lab::Exception::CorruptParameter->throw(
595             "Amplitude '$in' out of range ($vmin..$vmax)\n");
596 0           return;
597             }
598              
599 0 0         if ( abs($voff) > 2 * $vpp ) {
600 0           Lab::Exception::CorruptParameter->throw(
601             "Amplitude '$in' gives |Voff| > 2Vpp\n");
602 0           return;
603             }
604 0 0         if ( abs($voff) + 0.5 * $vpp > $vmax ) {
605 0           Lab::Exception::CorruptParameter->throw(
606             "Amplitude '$in' gives |Voff|+Vpp/2 > Vmax\n");
607 0           return;
608             }
609              
610             }
611 0           $self->write("VOLT $v");
612             }
613              
614              
615             sub get_vunit {
616 0     0 1   my $self = shift;
617 0           return $self->query("VOLT:UNIT?");
618             }
619              
620              
621             sub set_vunit {
622 0     0 1   my $self = shift;
623 0           my $in = shift;
624 0           $in =~ s/^\s+//;
625 0           $in =~ s/\s+$//;
626 0           my $u;
627 0 0         if ( $in =~ /pp/i ) {
    0          
    0          
    0          
628 0           $u = 'VPP';
629             }
630             elsif ( $in =~ /rms/i ) {
631 0           $u = 'VRMS';
632             }
633             elsif ( $in =~ /dbm/i ) {
634 0           $u = 'DBM';
635             }
636             elsif ( $in =~ /def/i ) {
637 0           $u = 'DEF';
638             }
639             else {
640 0           Lab::Exception::CorruptParameter->throw(
641             "Invalid vunit '$in' [VPP,VRMS,DBM,DEFAULT]\n");
642 0           return;
643             }
644 0           $self->write("VOLT:UNIT $u");
645             }
646              
647              
648             sub get_offset {
649 0     0 1   my $self = shift;
650 0           return $self->query("VOLT:OFFS?");
651             }
652              
653              
654             sub set_offset {
655 0     0 1   my $self = shift;
656 0           my $in = shift;
657              
658 0           my $voff = _parseNRf( $in, 'v' );
659 0 0         if ( $voff =~ /^ERR/ ) {
660 0           Lab::Exception::CorruptParameter->throw(
661             "Error parsing '$in': $voff\n");
662 0           return;
663             }
664 0 0 0       if ( $voff ne 'MIN' && $voff ne 'MAX' ) {
665 0           $voff = sprintf( '%.4e', $voff );
666 0           my $u = $self->get_vunit( { read_mode => 'cache' } );
667 0           my $vpp;
668 0 0         if ( $u ne 'VPP' ) {
669 0           $self->set_vunit('VPP');
670 0           $vpp = $self->get_amplitude( { read_mode => 'device' } );
671 0           $self->set_vunit($u);
672 0           $self->get_amplitude( { read_mode => 'device' } ); # reset cache
673             }
674             else {
675 0           $vpp = $self->get_amplitude( { read_mode => 'cache' } );
676             }
677 0           my $z = $self->get_load( { read_mode => 'cache' } );
678 0           my $vmax = 20;
679 0 0         $vmax = 10 if $z == 50;
680 0 0         if ( abs($voff) > 2 * $vpp ) {
681 0           Lab::Exception::CorruptParameter->throw(
682             "|Voffset| > 2*Vpp: $voff\n");
683 0           return;
684             }
685 0 0         if ( abs($voff) + 0.5 * $vpp > $vmax ) {
686 0           Lab::Exception::CorruptParameter->throw(
687             "|Voffset|+Vpp/2 > $vmax: Voffset = $voff\n");
688 0           return;
689             }
690             }
691 0           $self->write("VOLT:OFFS $voff");
692             }
693              
694             # use a "special" cache for this, because we need to store
695             # an array of names
696              
697              
698             sub get_waveform_list {
699 0     0 1   my $self = shift;
700              
701 0           my ($read_mode) = $self->_check_args( \@_, ['read_mode'] );
702 0 0         $read_mode = 'device' unless defined($read_mode);
703              
704 0 0 0       if ( $read_mode eq 'cache'
      0        
705 0           && $#{ $self->{waveform}->{user} } >= 0
706             && !$self->{config}->{no_cache} ) {
707 0           return ( @{ $self->{waveform}->{user} } );
  0            
708             }
709              
710 0           my $wfs = $self->query("DATA:CAT?");
711 0           $self->{waveform}->{user} = [ _parseStrings($wfs) ];
712 0           return ( @{ $self->{waveform}->{user} } );
  0            
713             }
714              
715              
716             sub get_user_waveform {
717 0     0 1   my $self = shift;
718 0           return $self->query("FUNC:USER?");
719             }
720              
721              
722             sub set_user_waveform {
723 0     0 1   my $self = shift;
724 0           my $in = shift;
725 0           $in =~ s/^\s+//;
726 0           $in =~ s/\s+$//;
727              
728 0 0 0       if ( $in !~ /^[a-z]\w+$/i || length($in) > 8 ) {
729 0           Lab::Exception::CorruptParameter->throw(
730             "Invalid arbitrary waveform name '$in' (a-z,0-9,_; len < 9) \n");
731 0           return;
732             }
733 0           $in = uc($in);
734              
735 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
736              
737 0           my $got = 0;
738 0           foreach my $wf ( @w, 'VOLATILE' ) {
739 0 0         $got++ if $wf eq $in;
740             }
741 0 0         if ( $got == 0 ) {
742 0           Lab::Exception::CorruptParameter->throw(
743             "Unknown USER waveform '$in' for set_user_waveform\n");
744 0           return;
745             }
746 0           $self->write("FUNC:USER $in");
747             }
748              
749              
750             sub load_waveform {
751 0     0 1   my $self = shift;
752 0           my $arg = shift;
753 0           my $fwfd;
754             my $dac;
755              
756 0 0         if ( ref($arg) eq 'HASH' ) {
757 0 0 0       if ( exists( $arg->{waveform} )
    0 0        
758             && ref( $arg->{waveform} ) eq 'ARRAY' ) {
759 0           $fwfd = $arg->{waveform};
760             }
761             elsif ( exists( $arg->{dac} ) && ref( $arg->{dac} ) eq 'ARRAY' ) {
762 0           $dac = $arg->{dac};
763             }
764             }
765             else {
766 0 0         if ( ref($arg) eq 'ARRAY' ) {
    0          
767 0           $fwfd = $arg;
768             }
769             elsif ( ref($arg) eq '' ) {
770 0           $fwfd = [ $arg, @_ ];
771             }
772              
773 0 0         if ( defined($fwfd) ) {
774 0           my ( $minv, $maxv );
775 0           foreach my $v ( @{$fwfd} ) {
  0            
776 0 0 0       $minv = $v unless defined($minv) && $minv < $v;
777 0 0 0       $maxv = $v unless defined($maxv) && $maxv > $v;
778             }
779 0 0 0       if ( $minv < -1 && $maxv > 1 ) {
780 0           $dac = $fwfd;
781 0           $fwfd = undef;
782             }
783             }
784             }
785              
786 0 0 0       if ( !defined($fwfd) && !defined($dac) ) {
787 0           Lab::Exception::CorruptParameter->throw("No waveform data\n");
788 0           return;
789             }
790              
791 0           my $npts;
792             my $cmd;
793 0 0         if ( defined($dac) ) {
794 0           $cmd = 'DATA:DAC VOLATILE'; # maybe use gpib data block?
795 0           $npts = $#{$dac} + 1;
  0            
796 0           for ( my $j = 0; $j < $npts; $j++ ) {
797 0           my $d = int( $dac->[$j] + 0.5 );
798 0 0         if ( abs($d) > 2047 ) {
799 0           Lab::Exception::CorruptParameter->throw(
800             "Waveform DAC data point $j ($) out of range -2047..2047 \n"
801             );
802 0           return;
803             }
804 0           $cmd .= ',' . $d;
805             }
806              
807             }
808              
809 0 0         if ( defined($fwfd) ) {
810 0           $cmd = 'DATA VOLATILE';
811 0           $npts = $#{$fwfd} + 1;
  0            
812 0           for ( my $j = 0; $j < $npts; $j++ ) {
813 0           my $v = sprintf( '%.3f', $fwfd->[$j] );
814 0 0         if ( abs($v) > 1 ) {
815 0           Lab::Exception::CorruptParameter->throw(
816             "Waveform data point $j ($v) out of range -1..1 \n");
817 0           return;
818             }
819 0           $cmd .= "," . $v;
820             }
821             }
822              
823 0 0 0       if ( $npts < 8 || $npts > 16000 ) {
824 0           Lab::Exception::CorruptParameter->throw(
825             "Number of Waveform data points $npts out of range 8..16000 \n");
826 0           return;
827             }
828 0           $self->write($cmd);
829             }
830              
831              
832             sub get_waveform_average {
833 0     0 1   my $self = shift;
834 0           my $name = shift;
835 0 0         if ( !defined($name) ) {
836 0           return $self->query("DATA:ATTR:AVER?");
837             }
838 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
839              
840 0           my $got = 0;
841 0           $name = uc($name);
842 0           foreach my $s (@w) {
843 0 0         $got++ if uc($s) eq $name;
844 0 0         last if $got;
845             }
846 0 0         if ( !$got ) {
847 0           Lab::Exception::CorruptParameter->throw(
848             "No such stored waveform '$name' \n");
849 0           return;
850             }
851 0           return $self->query("DATA:ATTR:AVER? $name");
852             }
853              
854              
855             sub get_waveform_crestfactor {
856 0     0 1   my $self = shift;
857 0           my $name = shift;
858 0 0         if ( !defined($name) ) {
859 0           return $self->query("DATA:ATTR:CFAC?");
860             }
861 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
862              
863 0           my $got = 0;
864 0           $name = uc($name);
865 0           foreach my $s (@w) {
866 0 0         $got++ if uc($s) eq $name;
867 0 0         last if $got;
868             }
869 0 0         if ( !$got ) {
870 0           Lab::Exception::CorruptParameter->throw(
871             "No such stored waveform '$name' \n");
872 0           return;
873             }
874 0           return $self->query("DATA:ATTR:CFAC? $name");
875             }
876              
877              
878             sub get_waveform_points {
879 0     0 1   my $self = shift;
880 0           my $name = shift;
881 0 0         if ( !defined($name) ) {
882 0           return $self->query("DATA:ATTR:POIN?");
883             }
884 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
885              
886 0           my $got = 0;
887 0           $name = uc($name);
888 0           foreach my $s (@w) {
889 0 0         $got++ if uc($s) eq $name;
890 0 0         last if $got;
891             }
892 0 0         if ( !$got ) {
893 0           Lab::Exception::CorruptParameter->throw(
894             "No such stored waveform '$name' \n");
895 0           return;
896             }
897 0           return $self->query("DATA:ATTR:POIN? $name");
898             }
899              
900              
901             sub get_waveform_peak2peak {
902 0     0 1   my $self = shift;
903 0           my $name = shift;
904 0 0         if ( !defined($name) ) {
905 0           return $self->query("DATA:ATTR:PTP?");
906             }
907 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
908              
909 0           my $got = 0;
910 0           $name = uc($name);
911 0           foreach my $s (@w) {
912 0 0         $got++ if uc($s) eq $name;
913 0 0         last if $got;
914             }
915 0 0         if ( !$got ) {
916 0           Lab::Exception::CorruptParameter->throw(
917             "No such stored waveform '$name' \n");
918 0           return;
919             }
920 0           return $self->query("DATA:ATTR:PTP? $name");
921             }
922              
923              
924             sub store_waveform {
925 0     0 1   my $self = shift;
926 0           my $name = shift;
927 0           $name =~ s/^\s+//;
928 0           $name =~ s/\s+$//;
929 0 0         if ( $name !~ /^[a-z]\w+$/i ) {
930 0           Lab::Exception::CorruptParameter->throw(
931             "Invalid waveform name '$name' [a-z][a-z,0-9,_]+\n");
932 0           return;
933             }
934 0 0         if ( length($name) > 8 ) {
935 0           Lab::Exception::CorruptParameter->throw(
936             "Invalid waveform name '$name' length > 8\n");
937 0           return;
938             }
939 0           $name = uc($name);
940 0 0 0       if ( $name eq 'SINC'
      0        
      0        
      0        
      0        
941             || $name eq 'NEG_RAMP'
942             || $name eq 'EXP_RISE'
943             || $name eq 'EXP_FALL'
944             || $name eq 'CARDIAC'
945             || $name eq 'VOLATILE' ) {
946 0           Lab::Exception::CorruptParameter->throw(
947             "Invalid waveform name '$name' for copy\n");
948 0           return;
949             }
950              
951 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
952 0           my $got = 0;
953 0           foreach my $s (@w) {
954 0 0         $got++ if uc($s) eq $name;
955 0 0         last if $got;
956             }
957 0 0 0       if ( !$got && $#w == 9 ) {
958 0           Lab::Exception::CorruptParameter->throw(
959             "Waveform storage is full, delete something \n");
960 0           return;
961             }
962 0           $self->write("DATA:COPY $name");
963             }
964              
965              
966             sub delete_waveform {
967 0     0 1   my $self = shift;
968 0           my $name = shift;
969 0           $name =~ s/^\s+//;
970 0           $name =~ s/\s+$//;
971 0 0         if ( $name !~ /^[a-z]\w+$/i ) {
972 0           Lab::Exception::CorruptParameter->throw(
973             "Invalid waveform name '$name' [a-z][a-z,0-9,_]+\n");
974 0           return;
975             }
976 0 0         if ( length($name) > 8 ) {
977 0           Lab::Exception::CorruptParameter->throw(
978             "Invalid waveform name '$name' length > 8\n");
979 0           return;
980             }
981 0           $name = uc($name);
982 0 0 0       if ( $name eq 'SINC'
      0        
      0        
      0        
983             || $name eq 'NEG_RAMP'
984             || $name eq 'EXP_RISE'
985             || $name eq 'EXP_FALL'
986             || $name eq 'CARDIAC' ) {
987 0           Lab::Exception::CorruptParameter->throw(
988             "Built-in waveform '$name' , not deletable\n");
989 0           return;
990             }
991              
992 0           my (@w) = $self->get_waveform_list( { read_mode => 'cache' } );
993 0           my $got = 0;
994 0           foreach my $s ( @w, 'VOLATILE' ) {
995 0 0         $got++ if uc($s) eq $name;
996 0 0         last if $got;
997             }
998 0 0         if ( !$got ) {
999 0           Lab::Exception::CorruptParameter->throw("No such waveform '$name'\n");
1000 0           return;
1001             }
1002 0           $self->write("DATA:DEL $name");
1003             }
1004              
1005              
1006             sub get_waveform_free {
1007 0     0 1   my $self = shift;
1008 0           return $self->query('DATA:NVOL:FREE?');
1009             }
1010              
1011              
1012             sub get_modulation {
1013 0     0 1   my $self = shift;
1014 0           my $mod = 'NONE';
1015              
1016 0 0         $mod = 'AM' if $self->query("AM:STAT?");
1017 0 0         $mod = 'FM' if $self->query("FM:STAT?");
1018 0 0         $mod = 'BURST' if $self->query("BM:STAT?");
1019 0 0         $mod = 'FSK' if $self->query("FSK:STAT?");
1020 0 0         $mod = 'SWEEP' if $self->query("SWE:STAT?");
1021              
1022 0           return $mod;
1023             }
1024              
1025              
1026             sub set_modulation {
1027 0     0 1   my $self = shift;
1028 0           my $in = shift;
1029 0 0         $in = 'NONE' unless defined($in);
1030 0 0         $in = 'NONE' if $in eq '';
1031 0           $in =~ s/^\s+//;
1032 0           my $m;
1033              
1034 0 0 0       if ( $in =~ /^NO/i || $in =~ /^OF/i ) {
    0          
    0          
    0          
    0          
    0          
1035 0           $m = 'NONE';
1036             }
1037             elsif ( $in =~ /^AM/i ) {
1038 0           $m = 'AM';
1039             }
1040             elsif ( $in =~ /^FM/i ) {
1041 0           $m = 'FM';
1042             }
1043             elsif ( $in =~ /^BUR/i ) {
1044 0           $m = 'BM';
1045             }
1046             elsif ( $in =~ /^FSK/i ) {
1047 0           $m = 'FSK';
1048             }
1049             elsif ( $in =~ /^SWE/i ) {
1050 0           $m = 'SWE';
1051             }
1052             else {
1053 0           Lab::Exception::CorruptParameter->throw(
1054             "Invalid modulation type '$in', should be NONE|AM|FM|BURST|FSK|SWEEP\n"
1055             );
1056 0           return;
1057             }
1058              
1059 0           my $cm = $self->get_modulation( { read_mode => 'cache' } );
1060 0 0         $cm = 'BM' if $cm eq 'BURST';
1061 0 0         $cm = 'SWE' if $cm eq 'SWEEP';
1062 0 0         if ( $m eq 'NONE' ) {
1063 0           $self->write("$cm:STAT 0");
1064             }
1065             else {
1066 0 0         if ( $cm ne 'NONE' ) {
1067 0           $self->write("$cm:STAT 0");
1068             }
1069 0           $self->write("$m:STAT 1");
1070             }
1071             }
1072              
1073              
1074             sub set_am_depth {
1075 0     0 1   my $self = shift;
1076 0           my $in = shift;
1077 0           my $d = _parseNRf($in);
1078 0 0         if ( $d =~ /^ERR:/ ) {
1079 0           Lab::Exception::CorruptParameter->throw(
1080             "Invalid AM modulation depth '$in' $d\n");
1081 0           return;
1082             }
1083 0 0 0       if ( $d ne 'MIN' && $d ne 'MAX' ) {
1084 0           $d = sprintf( '%.1f', $d );
1085 0 0 0       if ( $d < 0 || $d > 120 ) {
1086 0           Lab::Exception::CorruptParameter->throw(
1087             "Invalid AM modulation depth '$in' [0..120|MIN|MAX]\n");
1088 0           return;
1089             }
1090             }
1091 0           $self->write("AM:DEPT $d");
1092             }
1093              
1094              
1095             sub get_am_depth {
1096 0     0 1   my $self = shift;
1097 0           return $self->query("AM:DEPT?");
1098             }
1099              
1100              
1101             sub get_am_shape {
1102 0     0 1   my $self = shift;
1103 0           return $self->query("AM:INT:FUNC?");
1104             }
1105              
1106              
1107             sub set_am_shape {
1108 0     0 1   my $self = shift;
1109 0           my $in = shift;
1110 0           $in =~ s/^\s+//;
1111 0           my $s;
1112              
1113 0 0         if ( $in =~ /^sin/i ) {
    0          
    0          
    0          
    0          
    0          
1114 0           $s = 'SIN';
1115             }
1116             elsif ( $in =~ /^squ/i ) {
1117 0           $s = 'SQU';
1118             }
1119             elsif ( $in =~ /^tri/i ) {
1120 0           $s = 'TRI';
1121             }
1122             elsif ( $in =~ /^ram/i ) {
1123 0           $s = 'RAMP';
1124             }
1125             elsif ( $in =~ /^noi/i ) {
1126 0           $s = 'NOIS';
1127             }
1128             elsif ( $in =~ /^use/i ) {
1129 0           $s = 'USER';
1130             }
1131             else {
1132 0           Lab::Exception::CorruptParameter->throw(
1133             "Invalid AM modulation shape '$in' [SIN|SQU|TRI|RAMP|NOIS|USER]\n"
1134             );
1135 0           return;
1136             }
1137 0           $self->write("AM:INT:FUNC $s");
1138             }
1139              
1140              
1141             sub get_am_frequency {
1142 0     0 1   my $self = shift;
1143 0           return $self->query("AM:INT:FREQ?");
1144             }
1145              
1146              
1147             sub set_am_frequency {
1148 0     0 1   my $self = shift;
1149 0           my $in = shift;
1150              
1151 0           my $f = _parseNRf( $in, 'Hz' );
1152              
1153 0 0         if ( $f =~ /^ERR:/ ) {
1154 0           Lab::Exception::CorruptParameter->throw(
1155             "Invalid AM modulation frequency '$in' $f\n");
1156 0           return;
1157             }
1158              
1159 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1160 0 0 0       if ( $f < 10e-3 || $f > 20e3 ) {
1161 0           Lab::Exception::CorruptParameter->throw(
1162             "AM modulation frequency '$in' out of range 10mHz..20kHz\n");
1163 0           return;
1164             }
1165             }
1166 0           $self->write("AM:INT:FREQ $f");
1167             }
1168              
1169              
1170             sub get_am_source {
1171 0     0 1   my $self = shift;
1172 0           return $self->query("AM:SOUR?");
1173             }
1174              
1175              
1176             sub set_am_source {
1177 0     0 1   my $self = shift;
1178 0           my $in = shift;
1179 0           my $s;
1180              
1181 0 0         if ( $in =~ /^\s*BOTH/i ) {
    0          
    0          
1182 0           $s = 'BOTH';
1183             }
1184             elsif ( $in =~ /^\s*INT/i ) {
1185 0           $s = 'BOTH';
1186             }
1187             elsif ( $in =~ /^\s*EXT/i ) {
1188 0           $s = 'EXT';
1189             }
1190             else {
1191 0           Lab::Exception::CorruptParameter->throw(
1192             "Invalid AM modulation source '$in' [BOTH|EXT]\n");
1193 0           return;
1194             }
1195 0           $self->write("AM:SOUR $s");
1196             }
1197              
1198              
1199             sub get_fm_deviation {
1200 0     0 1   my $self = shift;
1201 0           return $self->query("FM:DEV?");
1202             }
1203              
1204              
1205             sub set_fm_deviation {
1206 0     0 1   my $self = shift;
1207 0           my $in = shift;
1208 0           my $d = _parseNRf( $in, 'hz' );
1209              
1210 0 0 0       if ( $d ne 'MIN' && $d ne 'MAX' ) {
1211 0           my $s = $self->get_shape( { read_mode => 'cache' } );
1212 0           my $f = $self->get_frequency( { read_mode => 'cache' } );
1213 0           my $fmax = 15.1e6;
1214 0 0 0       $fmax = 200e3 if $s eq 'TRI' || $s eq 'RAMP';
1215 0 0         $fmax = 5.1e6 if $s eq 'USER';
1216              
1217 0 0 0       if ( $d < 10e-3 || $d > $f || $d + $f > $fmax ) {
      0        
1218 0           Lab::Exception::CorruptParameter->throw(
1219             "FM modulation '$in' out of range\n");
1220 0           return;
1221             }
1222             }
1223 0           $self->write("FM:DEV $d");
1224             }
1225              
1226              
1227             sub get_fm_shape {
1228 0     0 1   my $self = shift;
1229 0           return $self->query("FM:INT:FUNC?");
1230             }
1231              
1232              
1233             sub set_fm_shape {
1234 0     0 1   my $self = shift;
1235 0           my $in = shift;
1236 0           $in =~ s/^\s+//;
1237 0           my $s;
1238              
1239 0 0         if ( $in =~ /^sin/i ) {
    0          
    0          
    0          
    0          
    0          
1240 0           $s = 'SIN';
1241             }
1242             elsif ( $in =~ /^squ/i ) {
1243 0           $s = 'SQU';
1244             }
1245             elsif ( $in =~ /^tri/i ) {
1246 0           $s = 'TRI';
1247             }
1248             elsif ( $in =~ /^ram/i ) {
1249 0           $s = 'RAMP';
1250             }
1251             elsif ( $in =~ /^noi/i ) {
1252 0           $s = 'NOIS';
1253             }
1254             elsif ( $in =~ /^use/i ) {
1255 0           $s = 'USER';
1256             }
1257             else {
1258 0           Lab::Exception::CorruptParameter->throw(
1259             "Invalid FM modulation shape '$in' [SIN|SQU|TRI|RAMP|NOIS|USER]\n"
1260             );
1261 0           return;
1262             }
1263 0           $self->write("FM:INT:FUNC $s");
1264             }
1265              
1266              
1267             sub get_fm_frequency {
1268 0     0 1   my $self = shift;
1269 0           return $self->query("FM:INT:FREQ?");
1270             }
1271              
1272              
1273             sub set_fm_frequency {
1274 0     0 1   my $self = shift;
1275 0           my $in = shift;
1276              
1277 0           my $f = _parseNRf( $in, 'Hz' );
1278              
1279 0 0         if ( $f =~ /^ERR:/ ) {
1280 0           Lab::Exception::CorruptParameter->throw(
1281             "Invalid FM modulation frequency '$in' $f\n");
1282 0           return;
1283             }
1284              
1285 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1286 0 0 0       if ( $f < 10e-3 || $f > 10e3 ) {
1287 0           Lab::Exception::CorruptParameter->throw(
1288             "FM modulation frequency '$in' out of range 10mHz..10kHz\n");
1289 0           return;
1290             }
1291             }
1292 0           $self->write("FM:INT:FREQ $f");
1293             }
1294              
1295              
1296             sub get_burst_cycles {
1297 0     0 1   my $self = shift;
1298 0           return $self->query("BM:NCYC?");
1299             }
1300              
1301              
1302             sub set_burst_cycles {
1303 0     0 1   my $self = shift;
1304 0           my $in = shift;
1305 0           my $ncyc;
1306 0 0         if ( $in =~ /^\s*min/i ) {
    0          
    0          
    0          
1307 0           $ncyc = 'MIN';
1308             }
1309             elsif ( $in =~ /\s*max/i ) {
1310 0           $ncyc = 'MAX';
1311             }
1312             elsif ( $in =~ /\s*inf/i ) {
1313 0           $ncyc = 'INF';
1314             }
1315             elsif ( $in =~ /\s*(\d+)/ ) {
1316 0           $ncyc = $1;
1317              
1318 0           my $f = $self->get_frequency( { read_mode => 'cache' } );
1319 0           my $s = $self->get_shape( { read_mode => 'cache' } );
1320              
1321 0           my $nmax = 50000;
1322 0 0         $nmax = int( 500 * $f ) if $f <= 100;
1323 0           my $nmin = 1;
1324 0 0 0       if ( $s eq 'SIN' || $s eq 'SQU' || $s eq 'USER' ) {
      0        
1325 0 0         if ( $f <= 1e6 ) {
    0          
    0          
    0          
    0          
1326 0           $nmin = 1;
1327             }
1328             elsif ( $f <= 2e6 ) {
1329 0           $nmin = 2;
1330             }
1331             elsif ( $f <= 3e6 ) {
1332 0           $nmin = 3;
1333             }
1334             elsif ( $f <= 4e6 ) {
1335 0           $nmin = 4;
1336             }
1337             elsif ( $f < 5e6 ) {
1338 0           $nmin = 5;
1339             }
1340             }
1341              
1342 0 0 0       if ( $ncyc < $nmin || $ncyc > $nmax ) {
1343 0           Lab::Exception::CorruptParameter->throw(
1344             "Burst count '$in' out of range\n");
1345 0           return;
1346             }
1347             }
1348             else {
1349 0           Lab::Exception::CorruptParameter->throw(
1350             "Error parsing burst count '$in'\n");
1351 0           return;
1352             }
1353              
1354 0           $self->write("BM:NCYC $ncyc");
1355             }
1356              
1357              
1358             sub get_burst_phase {
1359 0     0 1   my $self = shift;
1360 0           return $self->query("BM:PHAS?");
1361             }
1362              
1363              
1364             sub set_burst_phase {
1365 0     0 1   my $self = shift;
1366 0           my $in = shift;
1367 0           my $ph = _parseNRf( $in, 'deg' );
1368              
1369 0 0         if ( $ph =~ /^ERR:/ ) {
1370 0           Lab::Exception::CorruptParameter->throw(
1371             "Error parsing burst phase '$in' $ph\n");
1372 0           return;
1373             }
1374              
1375 0 0 0       if ( $ph ne 'MIN' && $ph ne 'MAX' ) {
1376 0           $ph = sprintf( "%.3f", $ph );
1377 0 0 0       if ( $ph < -360 || $ph > 360 ) {
1378 0           Lab::Exception::CorruptParameter->throw(
1379             "Burst phase '$in' out of range -360..360\n");
1380 0           return;
1381             }
1382             }
1383              
1384 0           $self->write("BM:PHAS $ph");
1385             }
1386              
1387              
1388             sub get_burst_rate {
1389 0     0 1   my $self = shift;
1390 0           return $self->query("BM:INT:RATE?");
1391             }
1392              
1393              
1394             sub set_burst_rate {
1395 0     0 1   my $self = shift;
1396 0           my $in = shift;
1397              
1398 0           my $f = _parseNRf( $in, 'Hz' );
1399              
1400 0 0         if ( $f =~ /^ERR:/ ) {
1401 0           Lab::Exception::CorruptParameter->throw(
1402             "Burst rate parse '$in' $f\n");
1403 0           return;
1404             }
1405              
1406 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1407 0 0 0       if ( $f < 10e-3 || $f > 50e3 ) {
1408 0           Lab::Exception::CorruptParameter->throw(
1409             "Burst rate '$in' out of range 10mHz..50kHz\n");
1410 0           return;
1411             }
1412             }
1413 0           $self->write("BM:INT:RATE $f");
1414             }
1415              
1416              
1417             sub get_burst_source {
1418 0     0 1   my $self = shift;
1419 0           return $self->query("BM:SOUR?");
1420             }
1421              
1422              
1423             sub set_burst_source {
1424 0     0 1   my $self = shift;
1425 0           my $in = shift;
1426 0           my $s;
1427 0 0         if ( $in =~ /^\s*IN/i ) {
    0          
1428 0           $s = 'INT';
1429             }
1430             elsif ( $in =~ /^\s*EX/i ) {
1431 0           $s = 'EXT';
1432             }
1433             else {
1434 0           Lab::Exception::CorruptParameter->throw(
1435             "Invalid burst source '$in', should be INT or EXT\n");
1436 0           return;
1437             }
1438 0           $self->write("BM:SOUR $s");
1439             }
1440              
1441              
1442             sub get_fsk_frequency {
1443 0     0 1   my $self = shift;
1444 0           return $self->query("FSK:FREQ?");
1445             }
1446              
1447              
1448             sub set_fsk_frequency {
1449 0     0 1   my $self = shift;
1450 0           my $in = shift;
1451              
1452 0           my $f = _parseNRf( $in, 'Hz' );
1453              
1454 0 0         if ( $f =~ /^ERR:/ ) {
1455 0           Lab::Exception::CorruptParameter->throw(
1456             "Invalid FSK hop frequency '$in' $f\n");
1457 0           return;
1458             }
1459              
1460 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1461 0           my $s = $self->get_shape( { read_mode => 'cache' } );
1462 0           my $fmax = 15e6;
1463 0 0 0       $fmax = 100e3 if $s eq 'TRI' || $s eq 'RAMP';
1464 0 0 0       if ( $f < 10e-3 || $f > $fmax ) {
1465 0           Lab::Exception::CorruptParameter->throw(
1466             "FSK hop frequency '$in' out of range\n");
1467 0           return;
1468             }
1469             }
1470 0           $self->write("FSK:FREQ $f");
1471             }
1472              
1473              
1474             sub get_fsk_rate {
1475 0     0 1   my $self = shift;
1476 0           return $self->query("FSK:INT:RATE?");
1477             }
1478              
1479              
1480             sub set_fsk_rate {
1481 0     0 1   my $self = shift;
1482 0           my $in = shift;
1483              
1484 0           my $f = _parseNRf( $in, 'Hz' );
1485              
1486 0 0         if ( $f =~ /^ERR:/ ) {
1487 0           Lab::Exception::CorruptParameter->throw("FSK rate parse '$in' $f\n");
1488 0           return;
1489             }
1490              
1491 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1492 0 0 0       if ( $f < 10e-3 || $f > 50e3 ) {
1493 0           Lab::Exception::CorruptParameter->throw(
1494             "FSK rate '$in' out of range 10mHz..50kHz\n");
1495 0           return;
1496             }
1497             }
1498 0           $self->write("FSK:INT:RATE $f");
1499             }
1500              
1501              
1502             sub get_fsk_source {
1503 0     0 1   my $self = shift;
1504 0           return $self->query("FSK:SOUR?");
1505             }
1506              
1507              
1508             sub set_fsk_source {
1509 0     0 1   my $self = shift;
1510 0           my $in = shift;
1511 0           my $s;
1512 0 0         if ( $in =~ /^\s*IN/i ) {
    0          
1513 0           $s = 'INT';
1514             }
1515             elsif ( $in =~ /^\s*EX/i ) {
1516 0           $s = 'EXT';
1517             }
1518             else {
1519 0           Lab::Exception::CorruptParameter->throw(
1520             "Invalid FSK source '$in', should be INT or EXT\n");
1521 0           return;
1522             }
1523 0           $self->write("FSK:SOUR $s");
1524             }
1525              
1526              
1527             sub get_sweep_start_frequency {
1528 0     0 1   my $self = shift;
1529 0           return $self->query("FREQ:STAR?");
1530             }
1531              
1532              
1533             sub get_sweep_stop_frequency {
1534 0     0 1   my $self = shift;
1535 0           return $self->query("FREQ:STOP?");
1536             }
1537              
1538              
1539             sub set_sweep_start_frequency {
1540 0     0 1   my $self = shift;
1541 0           my $in = shift;
1542              
1543 0           my $f = _parseNRf( $in, 'Hz' );
1544              
1545 0 0         if ( $f =~ /^ERR:/ ) {
1546 0           Lab::Exception::CorruptParameter->throw(
1547             "Invalid SWEEP start frequency '$in' $f\n");
1548 0           return;
1549             }
1550              
1551 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1552 0 0 0       if ( $f < 10e-3 || $f > 15e6 ) {
1553 0           Lab::Exception::CorruptParameter->throw(
1554             "SWEEP start frequency '$in' out of range (10mHz..15MHz)\n");
1555 0           return;
1556             }
1557             }
1558 0           $self->write("FREQ:STAR $f");
1559             }
1560              
1561              
1562             sub set_sweep_stop_frequency {
1563 0     0 1   my $self = shift;
1564 0           my $in = shift;
1565              
1566 0           my $f = _parseNRf( $in, 'Hz' );
1567              
1568 0 0         if ( $f =~ /^ERR:/ ) {
1569 0           Lab::Exception::CorruptParameter->throw(
1570             "Invalid SWEEP stop frequency '$in' $f\n");
1571 0           return;
1572             }
1573              
1574 0 0 0       if ( $f ne 'MIN' && $f ne 'MAX' ) {
1575 0 0 0       if ( $f < 10e-3 || $f > 15e6 ) {
1576 0           Lab::Exception::CorruptParameter->throw(
1577             "SWEEP stop frequency '$in' out of range (10mHz..15MHz)\n");
1578 0           return;
1579             }
1580             }
1581 0           $self->write("FREQ:STOP $f");
1582             }
1583              
1584              
1585             sub get_sweep_spacing {
1586 0     0 1   my $self = shift;
1587 0           return $self->query("SWE:SPAC?");
1588             }
1589              
1590              
1591             sub set_sweep_spacing {
1592 0     0 1   my $self = shift;
1593 0           my $in = shift;
1594 0           my $s;
1595              
1596 0 0         if ( $in =~ /^\s*LIN/i ) {
    0          
1597 0           $s = 'LIN';
1598             }
1599             elsif ( $in =~ /^\s*LOG/i ) {
1600 0           $s = 'LOG';
1601             }
1602             else {
1603 0           Lab::Exception::CorruptParameter->throw(
1604             "invalid SWEEP spacing '$in', should be LIN or LOG\n");
1605 0           return;
1606             }
1607 0           $self->write("SWE:SPAC $s");
1608             }
1609              
1610              
1611             sub get_sweep_time {
1612 0     0 1   my $self = shift;
1613 0           return $self->query("SWE:TIME?");
1614             }
1615              
1616              
1617             sub set_sweep_time {
1618 0     0 1   my $self = shift;
1619 0           my $in = shift;
1620              
1621 0           my $t = _parseNRf( $in, 's' );
1622 0 0         if ( $t =~ /ERR/i ) {
1623 0           Lab::Exception::CorruptParameter->throw(
1624             "Parse error in sweep time '$in': $t\n");
1625 0           return;
1626             }
1627 0 0 0       if ( $t ne 'MIN' && $t ne 'MAX' ) {
1628 0 0 0       if ( $t < 1e3 || $t > 500 ) {
1629 0           Lab::Exception::CorruptParameter->throw(
1630             "SWEEP time '$in' out of range (1ms..500s)\n");
1631 0           return;
1632             }
1633             }
1634 0           $self->write("SWE:TIME $t");
1635             }
1636              
1637             # parse a delimited set of strings, return an array of the strings
1638              
1639             sub _parseStrings($) {
1640 0     0     my $str = shift;
1641 0           $str =~ s/^\s+//;
1642 0 0         $str .= ' ,' if $str !~ /,\s*$/;
1643 0           my $x;
1644 0           my (@results) = ();
1645              
1646 0           while ( $str ne '' ) {
1647 0 0         if ( $str =~ /^\"(([^\"]|\"\")+)\"\s*,/i ) {
    0          
    0          
1648 0           $x = $1;
1649 0           $x =~ s/\"\"/"/g;
1650 0           $str = $POSTMATCH;
1651 0           push( @results, $x );
1652             }
1653             elsif ( $str =~ /^\'(([^\']|\'\')+)\'\s*,/i ) {
1654 0           $x = $1;
1655 0           $x =~ s/\'\'/'/g;
1656 0           $str = $POSTMATCH;
1657 0           push( @results, $x );
1658             }
1659             elsif ( $str =~ /^([^,]*[^,\s])\s*,/i ) {
1660 0           $x = $1;
1661 0           $str = $POSTMATCH;
1662 0           push( @results, $x );
1663             }
1664             else {
1665 0           carp("problems parsing strings '$str'");
1666 0           last;
1667             }
1668 0           $str =~ s/^\s+//;
1669             }
1670 0           return (@results);
1671             }
1672              
1673             # parse a GPIB number with suffix, units
1674             # $result = _parseNRf($numberstring,$unit1[,$unit2,...])
1675             # _parseNRf('maximum','foo) -> 'MAX'
1676             # _parseNRf('-3.7e+3kJ','j') -> -3.7e6
1677             # _parseNRf('2.3ksec','s','sec') -> 2300 ('s' and 'sec' alternate units)
1678             # note special cases for suffixes: MHZ, MOHM, MA
1679             # if problem, string returned starts 'ERR: ..message...'
1680             # see IEEE std 488-2 7.7.3
1681              
1682             sub _parseNRf($\[$@];@) {
1683 0     0     my $in = shift;
1684 0 0         $in = shift if ref($in) eq 'HASH'; # $self->_parseNRf handling...
1685 0           my $un = shift;
1686 0 0         $un = '' unless defined $un;
1687 0           my $us;
1688              
1689 0 0         if ( ref($un) eq 'ARRAY' ) {
    0          
    0          
1690 0           $us = $un;
1691             }
1692             elsif ( ref($un) eq 'SCALAR' ) {
1693 0           $us = [ $$un, @_ ];
1694             }
1695             elsif ( ref($un) eq '' ) {
1696 0           $us = [ $un, @_ ];
1697             }
1698 0           my $str = $in;
1699              
1700 0           $str =~ s/^\s+//;
1701 0           $str =~ s/\s+$//;
1702              
1703 0 0         if ( $str =~ /^MIN/i ) {
1704 0           return 'MIN';
1705             }
1706 0 0         if ( $str =~ /^MAX/i ) {
1707 0           return 'MAX';
1708             }
1709              
1710 0           my $mant = 0;
1711 0           my $exp = 0;
1712 0 0         if ( $str =~ /^([+\-]?(\d+\.\d*|\d+|\d*\.\d+))\s*/i ) {
1713 0           $mant = $1;
1714 0           $str = $POSTMATCH;
1715 0 0         return $mant if $str eq '';
1716 0 0         if ( $str =~ /^e\s*([+\-]?\d+)\s*/i ) {
1717 0           $exp = $1;
1718 0           $str = $POSTMATCH;
1719             }
1720 0 0         return $mant * ( 10**$exp ) if $str eq '';
1721              
1722 0           my $kexp = $exp;
1723 0           my $kstr = $str;
1724 0           foreach my $u ( @{$us} ) {
  0            
1725 0           $u =~ s/^\s+//;
1726 0           $u =~ s/\s+$//;
1727              
1728 0           $str = $kstr;
1729 0           $exp = $kexp;
1730 0 0         if ( $u =~ /^db/i ) { # db(magnitude_suffix)?(V|W|... unit)?
1731 0           my $dbt = $POSTMATCH;
1732 0 0         if ( $str =~ /^dBex(${dbt})?$/i ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1733 0           $exp += 18;
1734             }
1735             elsif ( $str =~ /^dBpe(${dbt})?$/i ) {
1736 0           $exp += 15;
1737             }
1738             elsif ( $str =~ /^dBt(${dbt})?$/i ) {
1739 0           $exp += 12;
1740             }
1741             elsif ( $str =~ /^dBg(${dbt})?$/i ) {
1742 0           $exp += 9;
1743             }
1744             elsif ( $str =~ /^dBma(${dbt})$/i ) {
1745 0           $exp += 6;
1746             }
1747             elsif ( $str =~ /^dBk(${dbt})?$/i ) {
1748 0           $exp += 3;
1749             }
1750             elsif ( $str =~ /^dBm(${dbt})?$/i ) {
1751 0           $exp -= 3;
1752             }
1753             elsif ( $str =~ /^dBu(${dbt})?$/i ) {
1754 0           $exp -= 6;
1755             }
1756             elsif ( $str =~ /^dBn(${dbt})?$/i ) {
1757 0           $exp -= 9;
1758             }
1759             elsif ( $str =~ /^dBp(${dbt})?$/i ) {
1760 0           $exp -= 12;
1761             }
1762             elsif ( $str =~ /^dBf(${dbt})?$/i ) {
1763 0           $exp -= 15;
1764             }
1765             elsif ( $str =~ /^dB${dbt}$/i ) {
1766 0           $exp += 0;
1767             }
1768             else {
1769 0           next;
1770             }
1771             }
1772             else { # regular units stuff: (magnitude_suffix)(unit)?
1773 0 0 0       if ( $str =~ /^ex(${u})?$/i ) {
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1774 0           $exp += 18;
1775             }
1776             elsif ( $str =~ /^pe(${u})?$/i ) {
1777 0           $exp += 15;
1778             }
1779             elsif ( $str =~ /^t(${u})?$/i ) {
1780 0           $exp += 12;
1781             }
1782             elsif ( $str =~ /^g(${u})?$/i ) {
1783 0           $exp += 9;
1784             }
1785             elsif ( $u =~ /(HZ|OHM)/i && $str =~ /^ma?(${u})$/i ) {
1786 0           $exp += 6;
1787             }
1788             elsif ( $u =~ /A/i && $str =~ /^ma$/i ) {
1789 0           $exp -= 3;
1790             }
1791             elsif ( $u !~ /(HZ|OHM)/i && $str =~ /^ma(${u})?$/i ) {
1792 0           $exp += 6;
1793             }
1794             elsif ( $str =~ /^k(${u})?$/i ) {
1795 0           $exp += 3;
1796             }
1797             elsif ( $str =~ /^m(${u})?$/i ) {
1798 0           $exp -= 3;
1799             }
1800             elsif ( $str =~ /^u(${u})?$/i ) {
1801 0           $exp -= 6;
1802             }
1803             elsif ( $str =~ /^n(${u})?$/i ) {
1804 0           $exp -= 9;
1805             }
1806             elsif ( $str =~ /^p(${u})?$/i ) {
1807 0           $exp -= 12;
1808             }
1809             elsif ( $str =~ /^f(${u})?$/i ) {
1810 0           $exp -= 15;
1811             }
1812             elsif ( $str =~ /^${u}$/i ) {
1813 0           $exp += 0;
1814             }
1815             else {
1816 0           next;
1817             }
1818             }
1819 0           return $mant * ( 10**$exp );
1820             }
1821             }
1822 0           return "ERR: '$str' number parsing problem";
1823              
1824             }
1825              
1826             1; # End of Lab::Instrument::HP33120A
1827              
1828             __END__
1829              
1830             =pod
1831              
1832             =encoding UTF-8
1833              
1834             =head1 NAME
1835              
1836             Lab::Instrument::HP33120A - HP 33120A 15MHz function/arbitrary waveform generator
1837              
1838             =head1 VERSION
1839              
1840             version 3.881
1841              
1842             =head1 SYNOPSIS
1843              
1844             use Lab::Instrument::HP33120A;
1845              
1846             my $g = new Lab::Instrument::HP33120A (
1847             connection_type => 'LinuxGPIB',
1848             gpib_address => 10
1849             );
1850             $g->set_frequency('3.78kHz');
1851             $g->set_shape('square');
1852              
1853             ...
1854              
1855             =head1 Getting started, system control
1856              
1857             =head2 new
1858              
1859             $g = new Lab::Instrument::HP33120A->(%options);
1860              
1861             options: gpib_board => 0,
1862             gpib_address => 10,
1863             connection_type => 'LinuxGPIB',
1864             no_cache => 1, # turn off cache
1865              
1866             =head2 get_id
1867              
1868             $id = $g->get_id();
1869              
1870             reads the *IDN? string from device
1871              
1872             =head2 get_status
1873              
1874             %status = $g->get_status();
1875              
1876             return a hash with status bits
1877             { ERROR => .., DATA=> ..
1878              
1879             =head2 get_error
1880              
1881             $errmsg = $g->get_error();
1882              
1883             Fetch the first error in the error queue. Returns
1884             ($code,$message); code == 0 means 'no error'
1885              
1886             =head2 reset
1887              
1888             $g->reset();
1889              
1890             reset the function generator (*RST, *CLS)
1891              
1892             =head2 get_trigger_slope
1893              
1894             $slope = $g->get_trigger_slope();
1895              
1896             fetch the trigger slope, returns POS or NEG
1897              
1898             =head2 set_trigger_slope
1899              
1900             $g->set_trigger_slope($slope);
1901              
1902             set the slope of the signal used to trigger
1903             $slope = 'POS','+' or 'NEG','-'
1904              
1905             =head2 wait_complete
1906              
1907             $g->wait_complete();
1908              
1909             Wait for operations to be completed
1910              
1911             TODO: probably need to revise, with a *OPC? checking loop
1912              
1913             =head2 trigger
1914              
1915             $g->trigger();
1916              
1917             Send a bus trigger to the function generator, wait
1918             until trigger complete.
1919              
1920             =head2 get_trigger_source
1921              
1922             $src = $g->get_trigger_source();
1923              
1924             fetch the 'trigger source' from the function generator.
1925             Possible values are 'IMM', 'BUS' or 'EXT'. IMM => immediate
1926             self-triggering; BUS => gpib/serial trigger input, such as *TRG;
1927             EXT => external trigger input.
1928              
1929             =head2 set_trigger_source
1930              
1931             $g->set_trigger_source($src);
1932              
1933             Set the trigger source for the function generator. Possible
1934             values are 'IMM' (immediate, i.e., internal free-running self-trigger)
1935             'BUS' GPIB *TRG type triggering; 'EXT' trigger from external input.
1936              
1937             =head2 set_display
1938              
1939             $g->set_display(BOOL);
1940              
1941             turn the display off (BOOL = false) or on (BOOL = true)
1942              
1943             =head2 get_display
1944              
1945             $display_on = $g->get_display();
1946              
1947             get the state of the display (boolean)
1948              
1949             =head2 set_text
1950              
1951             $g->set_text("text to show");
1952              
1953             display text on the function generator, in the place
1954             of the usual voltage/frequency/etc. Text is truncated
1955             at 11 chars, comma, semicolon, period are combined with
1956             char, so not counted in length
1957              
1958             =head2 get_text
1959              
1960             $mytext = $g->get_text();
1961              
1962             fetches the text shown on the display with set_text
1963              
1964             =head2 clear_text
1965              
1966             $g->clear_text();
1967              
1968             remove the text from the display
1969              
1970             =head2 beep
1971              
1972             $g->beep();
1973              
1974             Cause the function generator to 'beep'
1975              
1976             =head2 get_sync
1977              
1978             $sync = $g->get_sync();
1979              
1980             fetch boolean value indicating whether 'sync' output on the
1981             front panel is enabled
1982              
1983             =head2 set_sync
1984              
1985             $g->set_sync($sync);
1986              
1987             enable or disable SYNC output on front panel. $sync is
1988             a boolean (1/true/yes/on) => sync output enabled
1989              
1990             =head2 save_setup
1991              
1992             $g->save_setup($n);
1993              
1994             save function generator setup to internal non-volatile
1995             memory. $n = 0..3.
1996              
1997             NOTE: $n=0 is overwritten by the 'current
1998             setup' when the generator is turned off.
1999              
2000             =head2 recall_setup
2001              
2002             $g->recall_setup($n);
2003              
2004             restore function generator configuration from internal
2005             non-volatile memory. $n=0..3
2006              
2007             =head2 delete_setup
2008              
2009             $g->delete_setup($n);
2010              
2011             delete one of the internal non-volatile setups
2012             $n=0..3
2013              
2014             =head2 get_load
2015              
2016             $zload = $g->get_load();
2017              
2018             fetch the output load impedance of the generator. Possible
2019             values are '50' and 'INF'. This does NOT make any physical
2020             changes in the generator, but affects the internal calculation
2021             of amplitudes.
2022              
2023             =head2 set_load
2024              
2025             $g->set_load($z);
2026              
2027             Tell the function generator what load impedance the output
2028             is being terminated to, so that other characteristics can be
2029             correctly calculated. Possible values are '50', 'INF', 'MIN', 'MAX'
2030             (can also use '50ohm', '0.05kohm', etc)
2031              
2032             =head1 Basic waveform output routines
2033              
2034             =head2 get_shape
2035              
2036             $shape = $g->get_shape();
2037              
2038             returns the waveform shape = SIN|SQU|TRI|RAMP|USER
2039              
2040             =head2 set_shape
2041              
2042             $g=>set_shape($shape);
2043              
2044             Sets the output function shape = SIN|SQU|TRI|RAMP|USER
2045             USER = arbitary waveform, separately selected
2046              
2047             =head2 get_frequency
2048              
2049             $f = $g->get_frequency();
2050              
2051             reads the function generator frequency, in Hz
2052              
2053             =head2 set_frequency
2054              
2055             $g->set_frequency($f);
2056              
2057             sets the function generator frequency in Hz. The
2058             frequency limits are 10mHz to 15MHz . The frequency
2059             can be specified as a simple number (in Hz), MIN, MAX
2060             or a string in standard IEEE488-2 NRf format.
2061             NOTE: if you use the Hz unit, the standard is
2062             to interpret mHz as megahertz.
2063              
2064             =over
2065              
2066             set_frequency(10) 10Hz
2067              
2068             set_frequency('0.01kHz') 10Hz
2069              
2070             set_frequency('1mHz') 1E6 Hz
2071              
2072             set_frequency('10m') 10e-3 Hz (note, without Hz, `m' means `milli')
2073              
2074             =back
2075              
2076             The upper frequency limit depends on the function shape
2077              
2078             =head2 get_duty_cycle
2079              
2080             $dc = $g->get_duty_cycle()'
2081              
2082             fetch the duty cycle, in percent; only relevent for
2083             square waves
2084              
2085             =head2 set_duty_cycle
2086              
2087             $g->set_duty_cycle(percent);
2088              
2089             sets the square wave duty cycle, in percent. The available
2090             range depends on frequency, so percent = 20..80 for <= 5MHz
2091             and percent = 40..60 for higher frequencies
2092              
2093             =head2 get_amplitude
2094              
2095             $vamp = $g->get_amplitude();
2096              
2097             fetch the function amplitude, default is amplitude in volts
2098             peak-to-peak (Vpp), but depending on the units setting
2099             [see get_vunit()], so might be Vrms or dBm.
2100              
2101             =head2 set_amplitude
2102              
2103             $g->set_amplitude($vamp);
2104              
2105             sets the function amplitude, in units from the
2106             set_vunit() call, defaults to Vpp.
2107              
2108             The amplitude can be either a number,
2109             or string with magnitude (and optionally, units),
2110             MAX or MIN.
2111              
2112             Examples: `100uV', `50mV', `123E-3', `20dBm', `5.5E1dBmV'.
2113              
2114             NOTE: attaching units with $vamp does not change vunit,
2115             so if vunit=`VPP' and you set $vamp=`5.5e1dBmV', you'll
2116             get 55mVpp.
2117              
2118             The minimum and maximum
2119             amplitudes depend on the output load selection,
2120             the function shape, and the DC offset.
2121              
2122             Max output voltage is +-20V into a high-Z load,
2123             +-10V into 50 ohm load.
2124              
2125             TODO: automatically adjust units based on
2126             input text: 4Vpp, 3.5Vrms, 7.3dbm ...
2127              
2128             Since limits are rather hard to determine, you should
2129             check for errors after setting.
2130              
2131             =head2 get_vunit
2132              
2133             $unit = $g->get_vunit()
2134              
2135             Fetch the units that are being used to specify the output amplitude
2136             Possible values are VPP, VRMS, DBM, or DEF (default, VPP)
2137              
2138             =head2 set_vunit
2139              
2140             $g->set_vunit($unit);
2141              
2142             Set the way that amplitudes are specified. Possible
2143             values are Vpp, Vrms, dBm or DEF (default = Vpp)
2144              
2145             =head2 get_offset
2146              
2147             $voff = $g->get_offset();
2148              
2149             Get the DC offset in volts (not affected by vunit)
2150              
2151             =head2 set_offset
2152              
2153             $g->set_offset($voff);
2154              
2155             Set the DC offset, either as a number (volts), as a string
2156             '100mV', '0.01kV' '1e3u', MIN or MAX. The specification of
2157             the DC offset is not affected by the selection of vunit.
2158              
2159             Note that the DC offset is limited in combination with the
2160             output load, amplitude, and function shape.
2161              
2162             =head1 Arbitrary 'user' waveforms
2163              
2164             =head2 get_waveform_list
2165              
2166             @list = $g->get_waveform_list();
2167              
2168             Get a list of the available 'user' waveforms. Five of these
2169             are built-in, up to four are user-storable in non-volatile
2170             memory, and possibly VOLATILE for a waveform in volatile memory
2171              
2172             The names of the five built-in arbitrary waveforms are:
2173             SINC, NEG_RAMP, EXP_RISE, EXP_FALL, and CARDIAC.
2174              
2175             =head2 get_user_waveform
2176              
2177             $wname = $g->get_user_waveform();
2178              
2179             Fetches the name of the currently selected 'user' waveform.
2180              
2181             =head2 set_user_waveform
2182              
2183             $g->set_user_waveform($wname);
2184              
2185             Sets the name of the current 'user' waveform. This
2186             should be a name from the $g->get_waveform_list()
2187             set of nonvolatile waveforms, or 'VOLATILE'.
2188              
2189             =head2 load_waveform
2190              
2191             $g->load_waveform(...);
2192              
2193             store waveform as 'volatile' data (can be used by selecting 'volatile'
2194             user waveform) perhaps for persistant storage.
2195              
2196             =over
2197              
2198             load_waveform(v1,v2,v3...) voltages |v(j)| <= 1
2199              
2200             load_waveform(d1,d2,d3...) DAC values |d(j)| < 2048
2201              
2202             load_waveform(\@array) voltages or DAC values
2203              
2204             load_waveform(waveform=>[voltage array ref]);
2205              
2206             load_waveform(dac=>[DAC array ref]);
2207              
2208             =back
2209              
2210             number of data points 8..16000
2211              
2212             In the first three cases above, where it is not specified "voltage"
2213             or "DAC" values, it is assumed to be voltages if the quantities are
2214             within the range -1..+1, and otherwise assumed to be DAC values.
2215              
2216             =head2 get_waveform_average
2217              
2218             $vavg = $g->get_waveform_average($name);
2219              
2220             calculates and returns the 'average voltage' of
2221             waveform $name (nonvolatile stored waveform, or VOLATILE)
2222              
2223             =head2 get_waveform_crestfactor
2224              
2225             $vcr = $g->get_waveform_crestfactor($name);
2226              
2227             calculates and returns the voltage 'crest factor'
2228             (ratio of Vpeak/Vrms) for the waveform stored in $name.
2229              
2230             =head2 get_waveform_points
2231              
2232             $npts = $g->get_waveform_points($name)
2233             Returns the number of points in the waveform $name
2234              
2235             =head2 get_waveform_peak2peak
2236              
2237             $vpp = $g->get_waveform_peak2peak($name);
2238              
2239             calculates and returns the peak-to-peak voltage
2240             of waveform $name
2241              
2242             =head2 store_waveform
2243              
2244             $g->store_waveform($name);
2245              
2246             Stores the waveform in VOLATILE to non-volatile
2247             memory as $name. Note that $name cannot be one
2248             of the 'hard-coded' names, is a maximum of 8 characters
2249             in length, must start with a-z, and contain only
2250             alphanumeric and underscore (_) characters. All
2251             names are converted to uppercase.
2252              
2253             There is memory for 4 user waveforms to be stored,
2254             after which some must be deleted to allow further
2255             storage.
2256              
2257             =head2 delete_waveform
2258              
2259             $g->delete_waveform($name);
2260              
2261             Delete one of the non-volatile user waveforms (or VOLATILE).
2262             Note that the 5 'built-in' user waveforms cannot be deleted.
2263              
2264             =head2 get_waveform_free
2265              
2266             $n = $g->get_waveform_free();
2267              
2268             returns the number of 'free' user waveform storage
2269             areas (0..4) that can be used for $g->store_waveform
2270              
2271             =head1 Modulation
2272              
2273             =head2 get_modulation
2274              
2275             $mod = $g->get_modulation();
2276              
2277             Fetch the type of modulation being used: NONE,AM,FM,BURST,FSK,SWEEP
2278              
2279             =head2 set_modulation
2280              
2281             $g->set_modulation($mod);
2282              
2283             Set the type of modulation to use: NONE,AM,FM,BURST,FSK,SWEEP
2284             if $mod='' or 'off', selects NONE.
2285              
2286             =head2 set_am_depth
2287              
2288             $g->set_am_depth(percent);
2289              
2290             set AM modulation depth percent: 0..120, MIN, MAX
2291              
2292             =head2 get_am_depth
2293              
2294             $depth = $g->get_am_depth();
2295              
2296             get the AM modulation depth, in percent
2297              
2298             =head2 get_am_shape
2299              
2300             $shape = $g->get_am_shape();
2301              
2302             gets the waveform used for AM modulation
2303             returns $shape = (SIN|SQU|TRI|RAMP|NOIS|USER)
2304              
2305             =head2 set_am_shape
2306              
2307             $g->set_am_shape($shape);
2308              
2309             sets the waveform used for AM modulation
2310             $shape = (SIN|SQU|TRI|RAMP|NOIS|USER)
2311              
2312             =head2 get_am_frequency
2313              
2314             $freq = $g->get_am_frequency();
2315              
2316             get the frequency of the AM modulation
2317              
2318             =head2 set_am_frequency
2319              
2320             $g->set_am_frequency($f);
2321              
2322             sets the frequency of AM modulation
2323             $f = value in Hz, 10mHz..20kHz, MIN, MAX
2324              
2325             Note that $f can be a string, with suffixes, and that
2326             'mHz' suffix -> MEGAHz 'm' suffix with no 'Hz' -> millihertz
2327              
2328             =head2 get_am_source
2329              
2330             $source = $g->get_am_source();
2331              
2332             get the source of the AM modulation signal: BOTH|EXT
2333              
2334             =head2 set_am_source
2335              
2336             $g->set_am_source(BOTH|EXT);
2337              
2338             set the source of the AM modulation; BOTH = internal+external
2339             EXT = external only. INT = translated to BOTH
2340              
2341             =head2 get_fm_deviation
2342              
2343             $dev = $g->get_fm_deviation();
2344              
2345             fetch the FM modulation deviation, in Hz
2346              
2347             =head2 set_fm_deviation
2348              
2349             $g->set_fm_deviation($dev);
2350              
2351             Set the FM modulation deviation in Hz. $dev can be a simple
2352             number, in Hz, or a string with suffixes, or MIN or MAX.
2353              
2354             Ex: $dev='10.3kHz' $dev='1.2MHZ' $dev='200m'
2355             NOTE: MHZ -> megahertz (case independent). A simple 'm' suffix => millihertz
2356              
2357             dev range 10mHz .. 7.5MHz
2358             carrier frequency must be >= deviation frequency
2359             carrier + deviation < peak frequency for carrier waveform + 100kHz
2360             So: 15.1MHz for sine and square
2361             200kHz for triangle and ramp
2362             5.1MHz for 'user' waveforms
2363              
2364             =head2 get_fm_shape
2365              
2366             $shape = $g->get_fm_shape();
2367              
2368             gets the waveform used for FM modulation
2369             returns $shape = (SIN|SQU|TRI|RAMP|NOIS|USER)
2370              
2371             =head2 set_fm_shape
2372              
2373             $g->set_fm_shape($shape);
2374              
2375             sets the waveform used for FM modulation
2376             $shape = (SIN|SQU|TRI|RAMP|NOIS|USER)
2377              
2378             NOTE: NOISE and DC cannot be used as FM carrier
2379              
2380             =head2 get_fm_frequency
2381              
2382             $freq = $g->get_fm_frequency();
2383              
2384             get the frequency of the FM modulation, in Hz
2385              
2386             =head2 set_fm_frequency
2387              
2388             $g->set_fm_frequency($f);
2389              
2390             sets the frequency of AM modulation
2391             $f = value in Hz, 10mHz..10kHz, MIN, MAX
2392              
2393             Note that $f can be a string with the usual suffixes,
2394             but XmHz -> X megahz Xm-> X millihz
2395              
2396             =head2 get_burst_cycles
2397              
2398             $ncyc = $g->get_burst_cycles();
2399              
2400             Fetch the number of cycles in burst modulation
2401              
2402             =head2 set_burst_cycles
2403              
2404             $g->set_burst_cycles($ncyc);
2405              
2406             Set the number of cycles in burst modulation.
2407             $ncyc is an integer 1..50,000 or MIN or MAX or INF
2408              
2409             For SIN, SQU, or USER waveform shapes, the minumim number of cycles
2410             is related to the carrier frequency.
2411             <= 1MHz min 1 cycle
2412             1..2MHz min 2 cycles
2413             2..3MHz min 3 cycles
2414             3..4MHz min 4 cycles
2415             4..5MHz min 5 cycles
2416              
2417             For carrier frequency <= 100Hz, cycles <= 500sec * carrier freq
2418              
2419             =head2 get_burst_phase
2420              
2421             $ph = $g->get_burst_phase();
2422              
2423             Fetches the starting phase of the burst, in degrees, when
2424             bursts are triggered.
2425              
2426             =head2 set_burst_phase
2427              
2428             $g->set_burst_phase($ph);
2429              
2430             Sets the starting phase of burst, in degrees (or MIN or MAX)
2431             from -360 to 360 in 0.001 degree increments.
2432              
2433             phase examples: 30.1, '20deg', 'min', 'max'
2434              
2435             =head2 get_burst_rate
2436              
2437             $rate = $g->get_burst_rate();
2438              
2439             Fetch the burst rate (in Hz) for internally triggered bursts
2440              
2441             =head2 set_burst_rate
2442              
2443             $g->set_burst_rate($rate);
2444              
2445             Set the burst rate (in Hz) for internally triggered bursts.
2446             $rate can be a simple number, or a string with the usual
2447             suffixes. Note that 'mHz' (case independent) -> megahertz
2448             while 'm' -> millihertz. Rate 10mHz .. 50kHz or MIN or MAX
2449              
2450             If the burst rate is too large for the carrier frequency and
2451             burst count, the function generator will (silently) adjust to
2452             continually retrigger.
2453              
2454             =head2 get_burst_source
2455              
2456             $source = $g->get_burst_source();
2457              
2458             Fetch the source of the burst modulation: INT or EXT
2459              
2460             =head2 set_burst_source
2461              
2462             $g->set_burst_source($source);
2463              
2464             Set the source of burst modulation: $source = 'INT' or 'EXT'.
2465             If source is external, burst cycle count, rate, are ignored.
2466              
2467             =head2 get_fsk_frequency
2468              
2469             $freq = $g->get_fsk_frequency();
2470              
2471             get the FSK 'hop' frequency, in Hz
2472              
2473             =head2 set_fsk_frequency
2474              
2475             $g->set_fsk_frequency($f);
2476              
2477             sets the FSK 'hop' frequency
2478             $f = value in Hz, 10mHz..15MHz, MIN, MAX
2479             (max freq 100kHz for TRIANGLE and RAMP shapes)
2480              
2481             Note that $f can be a string with the usual suffixes,
2482             but XmHz -> X megahz Xm-> X millihz
2483              
2484             =head2 get_fsk_rate
2485              
2486             $rate = $g->get_fsk_rate();
2487              
2488             Fetch the rate at which fsk shifts between frequencies (in Hz) for
2489             internally triggered modulation.
2490              
2491             =head2 set_fsk_rate
2492              
2493             $g->set_fsk_rate($rate);
2494              
2495             Set the rate for fsk shifting between frequencies (in Hz) for
2496             internally triggered modulation.
2497              
2498             $rate can be a simple number, or a string with the usual
2499             suffixes. Note that 'mHz' (case independent) -> megahertz
2500             while 'm' -> millihertz. Rate 10mHz .. 50kHz or MIN or MAX
2501              
2502             =head2 get_fsk_source
2503              
2504             $source = $g->get_fsk_source();
2505              
2506             Fetch the source of the FSK modulation: INT or EXT
2507              
2508             =head2 set_fsk_source
2509              
2510             $g->set_fsk_source($source);
2511              
2512             Set the source of FSK modulation: $source = 'INT' or 'EXT'.
2513             If source is external, FSK rate is ignored.
2514              
2515             =head2 get_sweep_start_frequency
2516              
2517             $g->get_sweep_start_frequency();
2518              
2519             Fetch the starting frequency of the sweep, in Hz
2520              
2521             =head2 get_sweep_stop_frequency
2522              
2523             $g->get_sweep_stop_frequency();
2524              
2525             Fetch the stopping frequency of the sweep, in Hz
2526              
2527             =head2 set_sweep_start_frequency
2528              
2529             $g->set_sweep_start_frequency($f);
2530              
2531             sets the frequency sweep starting frequency
2532             $f = value in Hz, 10mHz..15MHz, MIN, MAX
2533              
2534             Note that $f can be a string with the usual suffixes,
2535             but XmHz -> X megahz Xm-> X millihz
2536              
2537             if fstart>fstop, sweep decreases in frequency;
2538             if fstart<fstop, sweep increases in frequency.
2539              
2540             =head2 set_sweep_stop_frequency
2541              
2542             $g->set_sweep_stop_frequency($f);
2543              
2544             sets the frequency sweep stopping frequency
2545             $f = value in Hz, 10mHz..15MHz, MIN, MAX
2546              
2547             Note that $f can be a string with the usual suffixes,
2548             but XmHz -> X megahz Xm-> X millihz
2549              
2550             if fstart>fstop, sweep decreases in frequency;
2551             if fstart<fstop, sweep increases in frequency.
2552              
2553             =head2 get_sweep_spacing
2554              
2555             $spc = $g->get_sweep_spacing();
2556              
2557             Fetches the sweep 'spacing', returns 'LIN' or 'LOG' for linear
2558             or logarithmic spacing.
2559              
2560             =head2 set_sweep_spacing
2561              
2562             $g->set_sweep_spacing($spc);
2563              
2564             Sets sweep to either LIN or LOG spacing
2565              
2566             =head2 get_sweep_time
2567              
2568             $time = $g->get_sweep_time();
2569              
2570             Fetch the time (in seconds) to sweep from starting to stopping frequency.
2571              
2572             =head2 set_sweep_time
2573              
2574             $g->set_sweep_time($time);
2575              
2576             Sets the time to sweep between starting and stopping frequencies. The
2577             number of frequencies steps is internally calculated by the function
2578             generator.
2579              
2580             $time can be a simple number (in seconds) or a string with
2581             suffices such as "5ms" "0.03ks", or MIN or MAX. The range of sweep
2582             times is 1ms .. 500s
2583              
2584             =head1 COPYRIGHT AND LICENSE
2585              
2586             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
2587              
2588             Copyright 2016 Charles Lane, Simon Reinhardt
2589             2017 Andreas K. Huettel
2590             2020 Andreas K. Huettel
2591              
2592              
2593             This is free software; you can redistribute it and/or modify it under
2594             the same terms as the Perl 5 programming language system itself.
2595              
2596             =cut