File Coverage

blib/lib/Lab/Instrument/WR640.pm
Criterion Covered Total %
statement 32 314 10.1
branch 0 156 0.0
condition 0 24 0.0
subroutine 11 30 36.6
pod 7 10 70.0
total 50 534 9.3


line stmt bran cond sub pod time code
1             package Lab::Instrument::WR640;
2             #ABSTRACT: LeCroy WaveRunner 640 digital oscilloscope
3             $Lab::Instrument::WR640::VERSION = '3.881';
4 1     1   1694 use v5.20;
  1         4  
5              
6 1     1   7 use strict;
  1         3  
  1         21  
7 1     1   5 use warnings;
  1         3  
  1         28  
8 1     1   16 use Lab::Instrument;
  1         4  
  1         24  
9 1     1   5 use Lab::SCPI;
  1         3  
  1         80  
10 1     1   7 use Carp;
  1         2  
  1         48  
11 1     1   6 use English;
  1         10  
  1         18  
12 1     1   419 use Time::HiRes qw(sleep);
  1         3  
  1         8  
13 1     1   101 use Clone 'clone';
  1         7  
  1         41  
14 1     1   7 use Data::Dumper;
  1         2  
  1         563  
15              
16             our $DEBUG = 0;
17             our @ISA = ("Lab::Instrument");
18             our %fields = (
19             supported_connections => ['VICP'],
20              
21             #default settings for connections
22              
23             connection_settings => {
24             connection_type => 'VICP',
25             remote_address => 'nulrs640',
26             },
27              
28             device_settings => {},
29              
30             # too many characteristics can easily be "messed with" on the front
31             # panel, so only allow changes when scope is "locked".
32              
33             device_cache => {},
34              
35             chan_cache => {},
36             default_chan_cache => {
37             channel => undef,
38             chan_bwlimit => undef,
39             chan_coupling => undef,
40             chan_current_probe => undef,
41             chan_invert => undef,
42             chan_position => undef,
43             chan_probe => undef,
44             chan_scale => undef,
45             chan_yunit => undef,
46             select => undef,
47             },
48              
49             # non-front-panel cache items
50             NFP => [
51             qw(
52             ID
53             HEADER
54             VERBOSE
55             LOCKED
56             )
57             ],
58              
59             shared_cache => {
60             ID => undef,
61             HEADER => undef,
62             VERBOSE => undef,
63             LOCKED => undef,
64              
65             },
66              
67             channel => undef,
68              
69             # almost all of the WR640 command suite is non-SCPI
70             scpi_override => {
71              
72             },
73             );
74              
75              
76             sub new {
77 0     0 1   my $proto = shift;
78 0   0       my $class = ref($proto) || $proto;
79              
80 0           foreach my $k ( keys( %{ $fields{default_chan_cache} } ) ) {
  0            
81 0           $fields{device_cache}->{$k} = $fields{default_chan_cache}->{$k};
82             }
83              
84 0           foreach my $k ( keys( %{ $fields{shared_cache} } ) ) {
  0            
85 0           $fields{device_cache}->{$k} = $fields{shared_cache}->{$k};
86             }
87              
88 0           my $self = $class->SUPER::new(@_);
89 0           $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  0            
90              
91 0           $self->{config}->{no_cache} = 1;
92 0           $self->{config}->{default_read_mode} = '';
93 0 0         $DEBUG = $self->{config}->{debug} if exists $self->{config}->{debug};
94              
95             # initialize channel caches
96 0           foreach my $ch (qw(C1 C2 C3 C4)) {
97 0           $self->{chan_cache}->{$ch} = {};
98 0           foreach my $k ( keys( %{ $self->{default_chan_cache} } ) ) {
  0            
99             $self->{chan_cache}->{$ch}->{$k}
100 0           = $self->{default_chan_cache}->{$k};
101             }
102 0           $self->{chan_cache}->{$ch}->{channel} = $ch;
103 0           foreach my $k ( keys( %{ $self->{shared_cache} } ) ) {
  0            
104 0           $self->{chan_cache}->{$ch}->{$k} = $self->{shared_cache}->{$k};
105             }
106             }
107              
108 0           $self->{device_cache} = $self->{chan_cache}->{C1};
109 0           $self->{channel} = "C1";
110 0           return $self;
111             }
112              
113             #initialize scope.. this means setting up status bit masking
114             #for non-destructive testing for device errors
115              
116             sub _device_init {
117 0     0     my $self = shift;
118 0           $self->write("*ESE 60")
119             ; # 0x3C -> CME+EXE+DDE+QYE to bit 5 of SBR (read with *STB?)
120 0           $self->write("*CLS"); # clear status registers
121             }
122              
123             { # keep perl from bitching about this stuff
124 1     1   9 no warnings qw(redefine);
  1         6  
  1         3496  
125              
126             # calling argument parsing; this is an extension of the
127             # _check_args and _check_args_strict routines in Instrument.pm,
128             # allowing more flexibility in how routines are called.
129             # In particular routine(a=>1,b=>2,..) and
130             # routine({a=>1,b=>2,..}) can both be used.
131              
132             # note: if this code does not properly recognize the syntax,
133             # then you have to use the {key=>value...} form.
134              
135             # calling:
136             # ($par1,$par2,$par3,$tail) = $self->_Xcheck_args(\@_,qw(par1 par2 par3));
137             # or, for compatibility:
138             # ($par1,$par2,$par3,$tail) = $self->_Xcheck_args(\@_,[qw(par1 par2 par3)]);
139              
140             sub Lab::Instrument::_check_args {
141 0     0     my $self = shift;
142 0           my $args = shift;
143 0           my $params = [@_];
144 0 0         $params = $params->[0] if ref( $params->[0] ) eq 'ARRAY';
145 0           my $arguments = {};
146              
147 0 0 0       if ( $#{$args} == 0 && ref( $args->[0] ) eq 'HASH' ) { # case 3
  0            
148 0           %{$arguments} = ( %{ $args->[0] } );
  0            
  0            
149             }
150             else {
151 0           my $simple = 1;
152 0 0         if ( $#{$args} & 1 == 1 ) { # must have even # arguments
  0            
153 0           my $found = {};
154 0           for ( my $j = 0; $j <= $#{$args}; $j += 2 ) {
  0            
155 0 0         if ( ref( $args->[$j] ) ne '' ) { # a ref for a key? no
156 0           $simple = 1;
157 0           last;
158             }
159 0           foreach my $p ( @{$params} ) { # named param
  0            
160 0 0         $simple = 0 if $p eq $args->[$j];
161             }
162 0 0         if ( exists( $found->{ $args->[$j] } ) )
163             { # key used 2x? no
164 0           $simple = 1;
165 0           last;
166             }
167 0           $found->{ $args->[$j] } = 1;
168             }
169             }
170              
171 0 0         if ($simple) { # case 1
172 0           my $i = 0;
173 0           foreach my $arg ( @{$args} ) {
  0            
174 0 0         if ( defined @{$params}[$i] ) {
  0            
175 0           $arguments->{ @{$params}[$i] } = $arg;
  0            
176             }
177 0           $i++;
178             }
179             }
180             else { # case 2
181 0           %{$arguments} = ( @{$args} );
  0            
  0            
182             }
183             }
184              
185 0           my @return_args = ();
186              
187 0           foreach my $param ( @{$params} ) {
  0            
188 0 0         if ( exists $arguments->{$param} ) {
189 0           push( @return_args, $arguments->{$param} );
190 0           delete $arguments->{$param};
191             }
192             else {
193 0           push( @return_args, undef );
194             }
195             }
196              
197 0           push( @return_args, $arguments );
198              
199 0 0         if (wantarray) {
200 0           return @return_args;
201             }
202             else {
203 0           return $return_args[0];
204             }
205             }
206              
207             sub Lab::Instrument::_check_args_strict {
208 0     0     my $self = shift;
209 0           my $args = shift;
210 0           my $params = [@_];
211 0 0         $params = $params->[0] if ref( $params->[0] ) eq 'ARRAY';
212              
213 0           my @result = $self->_check_args( $args, $params );
214              
215 0           my $num_params = @result - 1;
216              
217 0           for ( my $i = 0; $i < $num_params; ++$i ) {
218 0 0         if ( not defined $result[$i] ) {
219 0           croak("missing mandatory argument '$params->[$i]'");
220             }
221             }
222              
223 0 0         if (wantarray) {
224 0           return @result;
225             }
226             else {
227 0           return $result[0];
228             }
229             }
230              
231             }
232             #
233             # utility function: check header/verbose and parse
234             # query reply appropriately; remove quotes in present
235             # ex: $self->_parseReply('ACQ:MODE average',qw{AVE PEAK SAM})
236             # gives AVE
237             sub _parseReply {
238 0     0     my $self = shift;
239 0           my $in = shift;
240              
241 0           my $h = $self->get_header();
242 0 0         if ($h) {
243 0           my $c;
244 0           ( $c, $in ) = split( /\s+/, $in );
245 0 0 0       return '' unless defined($in) && $in ne '';
246             }
247              
248             # remove quotes on strings
249 0 0         if ( $in =~ /^\"(.*)\"$/ ) {
    0          
250 0           $in = $1;
251 0           $in =~ s/\"\"/"/g;
252             }
253             elsif ( $in =~ /^\'(.*)\'$/ ) {
254 0           $in = $1;
255 0           $in =~ s/\'\'/'/g;
256             }
257              
258 0 0         return $in unless $#_ > -1;
259 0           my $v = $self->get_verbose();
260 0 0         return $in unless $v;
261 0           return _keyword( $in, @_ );
262             }
263              
264             #
265             # select keyword
266             # example: $got = _keyword('input', qw{ IN OUT EXT } )
267             # returns $got = 'IN'
268              
269             sub _keyword {
270 0     0     my $in = shift;
271 0 0         $in = shift if ref($in) eq 'HASH'; # dispose of $self->_keyword form...
272 0           my $r;
273              
274 0           $in =~ s/^\s+//;
275 0           foreach my $k (@_) {
276 0 0         if ( $in =~ /^$k/i ) {
277 0           return $k;
278             }
279             }
280 0           Lab::Exception::CorruptParameter->throw("Invalid keyword input '$in'\n");
281             }
282              
283             # convert 'short form' keywords to long form
284              
285             sub _bloat {
286 0     0     my $in = shift;
287 0 0         $in = shift if ref($in) eq 'HASH'; # dispose of $self->_bloat
288 0           my $tr = shift; # hash of short=>long:
289              
290 0           $in =~ s/^\s+//;
291 0           $in =~ s/\s+$//;
292 0 0         return $in if $in eq '';
293              
294 0           foreach my $k ( keys( %{$tr} ) ) {
  0            
295 0 0         if ( $in =~ /^${k}/i ) {
296 0           return $tr->{$k};
297             }
298             }
299              
300 0           return uc($in); # nothing matched
301             }
302              
303             # parse a GPIB number with suffix, units
304             # $result = _parseNRf($numberstring,$unit1[,$unit2,...])
305             # _parseNRf('maximum','foo) -> 'MAX'
306             # _parseNRf('-3.7e+3kJ','j') -> -3.7e6
307             # _parseNRf('2.3ksec','s','sec') -> 2300 ('s' and 'sec' alternate units)
308             # note special cases for suffixes: MHZ, MOHM, MA
309             # also handling 'dB' -> (number)dB(magnitudesuffix)(unit V|W|etc)
310             #
311             # if problem, string returned starts 'ERR: ..message...'
312             # see IEEE std 488-2 7.7.3
313              
314             sub _parseNRf {
315 0     0     my $in = shift;
316 0 0         $in = shift if ref($in) eq 'HASH'; # $self->_parseNRf handling...
317 0           my $un = shift;
318 0 0         $un = '' unless defined $un;
319 0           my $us;
320              
321 0 0         if ( ref($un) eq 'ARRAY' ) {
    0          
    0          
322 0           $us = $un;
323             }
324             elsif ( ref($un) eq 'SCALAR' ) {
325 0           $us = [ $$un, @_ ];
326             }
327             elsif ( ref($un) eq '' ) {
328 0           $us = [ $un, @_ ];
329             }
330 0           my $str = $in;
331              
332 0           $str =~ s/^\s+//;
333 0           $str =~ s/\s+$//;
334              
335 0 0         if ( $str =~ /^MIN/i ) {
336 0           return 'MIN';
337             }
338 0 0         if ( $str =~ /^MAX/i ) {
339 0           return 'MAX';
340             }
341              
342 0           my $mant = 0;
343 0           my $exp = 0;
344 0 0         if ( $str =~ /^([+\-]?(\d+\.\d*|\d+|\d*\.\d+))\s*/i ) {
345 0           $mant = $1;
346 0           $str = $POSTMATCH;
347 0 0         return $mant if $str eq '';
348 0 0         if ( $str =~ /^e\s*([+\-]?\d+)\s*/i ) {
349 0           $exp = $1;
350 0           $str = $POSTMATCH;
351             }
352 0 0         return $mant * ( 10**$exp ) if $str eq '';
353              
354 0           my $kexp = $exp;
355 0           my $kstr = $str;
356 0           foreach my $u ( @{$us} ) {
  0            
357 0           $u =~ s/^\s+//;
358 0           $u =~ s/\s+$//;
359              
360 0           $str = $kstr;
361 0           $exp = $kexp;
362 0 0         if ( $u =~ /^db/i ) { # db(magnitude_suffix)?(V|W|... unit)?
363 0           my $dbt = $POSTMATCH;
364 0 0         if ( $str =~ /^dBex(${dbt})?$/i ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
365 0           $exp += 18;
366             }
367             elsif ( $str =~ /^dBpe(${dbt})?$/i ) {
368 0           $exp += 15;
369             }
370             elsif ( $str =~ /^dBt(${dbt})?$/i ) {
371 0           $exp += 12;
372             }
373             elsif ( $str =~ /^dBg(${dbt})?$/i ) {
374 0           $exp += 9;
375             }
376             elsif ( $str =~ /^dBma(${dbt})$/i ) {
377 0           $exp += 6;
378             }
379             elsif ( $str =~ /^dBk(${dbt})?$/i ) {
380 0           $exp += 3;
381             }
382             elsif ( $str =~ /^dBm(${dbt})?$/i ) {
383 0           $exp -= 3;
384             }
385             elsif ( $str =~ /^dBu(${dbt})?$/i ) {
386 0           $exp -= 6;
387             }
388             elsif ( $str =~ /^dBn(${dbt})?$/i ) {
389 0           $exp -= 9;
390             }
391             elsif ( $str =~ /^dBp(${dbt})?$/i ) {
392 0           $exp -= 12;
393             }
394             elsif ( $str =~ /^dBf(${dbt})?$/i ) {
395 0           $exp -= 15;
396             }
397             elsif ( $str =~ /^dB${dbt}$/i ) {
398 0           $exp += 0;
399             }
400             else {
401 0           next;
402             }
403             }
404             else { # regular units stuff: (magnitude_suffix)(unit)?
405 0 0 0       if ( $str =~ /^ex(${u})?$/i ) {
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
406 0           $exp += 18;
407             }
408             elsif ( $str =~ /^pe(${u})?$/i ) {
409 0           $exp += 15;
410             }
411             elsif ( $str =~ /^t(${u})?$/i ) {
412 0           $exp += 12;
413             }
414             elsif ( $str =~ /^g(${u})?$/i ) {
415 0           $exp += 9;
416             }
417             elsif ( $u =~ /(HZ|OHM)/i && $str =~ /^ma?(${u})$/i ) {
418 0           $exp += 6;
419             }
420             elsif ( $u =~ /A/i && $str =~ /^ma$/i ) {
421 0           $exp -= 3;
422             }
423             elsif ( $u !~ /(HZ|OHM)/i && $str =~ /^ma(${u})?$/i ) {
424 0           $exp += 6;
425             }
426             elsif ( $str =~ /^k(${u})?$/i ) {
427 0           $exp += 3;
428             }
429             elsif ( $str =~ /^m(${u})?$/i ) {
430 0           $exp -= 3;
431             }
432             elsif ( $str =~ /^u(${u})?$/i ) {
433 0           $exp -= 6;
434             }
435             elsif ( $str =~ /^n(${u})?$/i ) {
436 0           $exp -= 9;
437             }
438             elsif ( $str =~ /^p(${u})?$/i ) {
439 0           $exp -= 12;
440             }
441             elsif ( $str =~ /^f(${u})?$/i ) {
442 0           $exp -= 15;
443             }
444             elsif ( $str =~ /^${u}$/i ) {
445 0           $exp += 0;
446             }
447             else {
448 0           next;
449             }
450             }
451 0           return $mant * ( 10**$exp );
452             }
453             }
454 0           return "ERR: '$str' number parsing problem";
455              
456             }
457              
458              
459             sub reset {
460 0     0 1   my $self = shift;
461 0           $self->write("*RST");
462 0           $self->_debug();
463 0           $self->_reset_cache();
464             }
465              
466             our $_rst_state = {
467             LOCKED => 'NON',
468             HEADER => '1',
469             VERBOSE => '1',
470             };
471              
472             sub _reset_cache {
473 0     0     my $self = shift;
474              
475 0           for my $k ( keys( %{$_rst_state} ) ) {
  0            
476 0           $self->{device_cache}->{$k} = $_rst_state->{$k};
477 0           for ( my $ch = 1; $ch <= 4; $ch++ ) {
478 0 0         $self->{chan_cache}->{"CH$ch"}->{select} = ( $ch == 1 ? 1 : 0 );
479 0 0         next if "CH$ch" eq $self->{channel};
480 0           $self->{chan_cache}->{"CH$ch"}->{$k} = $_rst_state->{$k};
481             }
482             }
483 0 0         $self->{device_cache}->{select} = ( $self->{channel} eq 'CH1' ? 1 : 0 );
484 0           foreach my $wfm (qw(MATH REFA REFB REFC REFD)) {
485 0           $self->{chan_cache}->{$wfm}->{select} = 0;
486             }
487             }
488              
489             # print error queue; meant to be called at end of routine
490             # so uses 'caller' info to label the subroutine
491             sub _debug {
492 0 0   0     return unless $DEBUG;
493 0           my $self = shift;
494 0           my ( $p, $f, $l, $subr ) = caller(1);
495 0           while (1) {
496 0           my ( $code, $msg ) = $self->get_error();
497 0 0         last if $code == 0;
498 0           print "$subr\t$code: $msg\n";
499             }
500             }
501              
502              
503             sub get_error {
504 0     0 1   my $self = shift;
505              
506 0           my $err = $self->query("CHL? CLR");
507 0           $err =~ s/^(CHL\s*)?\"(.*)\"/$2/is;
508 0           my (@lines) = split( /\n/, $err );
509 0           my (@elines) = ();
510              
511 0           foreach my $x (@lines) {
512 0           $x =~ s/^\s*(.*)\s*$/$1/;
513 0 0         next if $x =~ /^connection\s/i;
514 0 0         next if $x =~ /^disconnect/i;
515 0           push( @elines, $x );
516             }
517 0           return (@elines);
518              
519             }
520              
521              
522             our $sbits = [qw(OPC RQC QYE DDE EXE CME URQ PON)];
523              
524             sub get_status {
525 0     0 1   my $self = shift;
526 0           my $bit = shift;
527 0           my $s = {};
528              
529 0           my $r = $self->query('*ESR?');
530 0           $self->_debug();
531              
532 0           for ( my $j = 0; $j < 7; $j++ ) {
533 0           $s->{ $sbits->[$j] } = ( $r >> $j ) & 0x01;
534             }
535 0           $s->{ERROR} = $s->{CME} | $s->{EXE} | $s->{DDE} | $s->{QYE};
536              
537 0 0         return $s->{ uc($bit) } if defined $bit;
538 0           return $s;
539             }
540              
541              
542             sub test_busy {
543 0     0 1   my $self = shift;
544 0 0         return 1 if $self->query('BUSY?') =~ /^(:BUSY )?\s*1/i;
545 0           return 0;
546             }
547              
548              
549             sub get_id {
550 0     0 1   my $self = shift;
551 0           my ($tail) = $self->_check_args( \@_ );
552              
553             $tail->{read_mode} = $self->{config}->{default_read_mode}
554 0 0 0       unless exists( $tail->{read_mode} ) && defined( $tail->{read_mode} );
555              
556 0 0 0       if ( $tail->{read_mode} ne 'cache'
557             || !defined( $self->{device_cache}->{ID} ) ) {
558 0           $self->{device_cache}->{ID} = $self->query('*IDN?');
559 0           $self->_debug();
560             }
561 0           return $self->{device_cache}->{ID};
562             }
563              
564              
565             sub recall {
566 0     0 1   my $self = shift;
567 0           my ( $mem, $tail ) = $self->_check_args( \@_, 'n' );
568              
569 0           my $n;
570 0 0         if ( $mem =~ /^\s*([0-6])\s/ ) {
571 0           $n = $1;
572             }
573             else {
574 0           carp("recall memory n=$mem invalid, should be 0..6");
575 0           return;
576             }
577 0           $self->write("*RCL $n");
578             }
579              
580             sub get_setup {
581 0     0 0   my $self = shift;
582 0           my (@a) = ();
583              
584 0           foreach my $ch (qw(C1 C2 C3 C4 EX EX10 ETM10 LINE)) {
585 0 0         if ( $ch =~ /C\d/ ) {
586 0           foreach my $q (qw(ATTN CPL OFST OFCT TRA TRCP VDIV)) {
587 0           push( @a, $self->query( $ch . ':' . $q . '?' ) );
588             }
589             }
590 0 0         if ( $ch ne 'LINE' ) {
591 0           push( @a, $self->query( $ch . ":TRLV?" ) );
592             }
593 0           push( @a, $self->query( $ch . ":TRSL?" ) );
594             }
595              
596 0           for ( my $j = 1; $j <= 8; $j++ ) {
597 0           my $ch = "F$j";
598 0           foreach my $q (qw(TRA VMAG VPOS)) {
599 0           push( @a, $self->query( $ch . ":" . $q . "?" ) );
600             }
601             }
602              
603 0           foreach my $ch (qw(M1 M2 M3 M4)) {
604 0           push( @a, $self->query( $ch . ":VPOS?" ) );
605             }
606              
607 0           foreach my $q (
608             qw(ALST BWL COUT CMR COMB CFMT CHDR CORD CRMS
609             ILVD RCLK SCLK SEQ TDIV TRDL TRMD TRPA
610             TRSE WFSU)
611             ) {
612 0           push( @a, $self->query( $q . '?' ) );
613             }
614              
615 0           return (@a);
616             }
617              
618             sub get_visible {
619 0     0 0   my $self = shift;
620 0           my $ch = shift;
621              
622 0           my $r = $self->query("$ch:TRA?");
623 0           $r =~ s/^.*:TRA(ce)?\s+//i;
624 0           $r = uc($r);
625 0 0         return 1 if $r eq 'ON';
626 0           return 0;
627             }
628              
629             sub get_waveform {
630 0     0 0   my $self = shift;
631 0           my $ch = shift;
632 0           return $self->query("$ch:WF?");
633             }
634              
635             1;
636              
637             __END__
638              
639             =pod
640              
641             =encoding UTF-8
642              
643             =head1 NAME
644              
645             Lab::Instrument::WR640 - LeCroy WaveRunner 640 digital oscilloscope
646              
647             =head1 VERSION
648              
649             version 3.881
650              
651             =head1 SYNOPSIS
652              
653             =over 4
654              
655             use Lab::Instrument::WR640;
656              
657             my $s = new Lab::Instrument::WR640 (
658             address => '192.168.1.1',
659             );
660              
661             =back
662              
663             Many of the 'quantities' passed to the code can use scientific
664             notation, order of magnitude suffixes ('u', 'm', etc) and unit
665             suffixes. The routines can be called using positional parameters
666             (check the documentation for order), or with keyword parameters.
667              
668             There are a few 'big' routines that let you set many parameters
669             in one call, use keyword parameters for those.
670              
671             In general, keywords passed TO these routines are case-independent,
672             with only the first few characters being significant. So, in the
673             example above: state=>'Run', state=>'running', both work. In cases
674             where the keywords distinguish an "on/off" situation (RUN vs STOP
675             for acquistion, for example) you can use a Boolean quantity, and
676             again, the Boolean values are flexible:
677              
678             =over
679              
680             TRUE = 't' or 'y' or 'on' or number!=0
681              
682             FALSE = 'f' or 'n' or 'off' or number ==0
683              
684             (only the first part of these is checked, case independent)
685              
686             =back
687              
688             The oscilloscope input 'channels' are CH1..CH4, but
689             there are also MATH, REFA..REFD that can be displayed
690             or manipulated. To perform operations on a channel, one
691             should first $s->set_channel($chan); Channel can be
692             specified as 1..4 for the input channels, and it will
693             be translated to 'CH1..CH4'.
694              
695             The state of the TDS2024B scope is cached only when the
696             front-panel is in a 'locked' state, so that it cannot be
697             changed by users fiddling with knobs.
698              
699             =head1 GENERAL/SYSTEM ROUTINES
700              
701             =head2 new
702              
703             my $s = new Lab::Instrument::TDS2024B(
704             usb_serial => '...',
705             );
706              
707             serial only needed if multiple TDS2024B scopes are attached, it
708             defaults to '*', which selects the first TDS2024B found. See
709             Lab::Bus::USBtmc.pm documentation for more information.
710              
711             =head2 reset
712              
713             $s->reset()
714              
715             Reset the oscilloscope (*RST)
716              
717             =head2 get_error
718              
719             ($code,$message) = $s->get_error();
720              
721             Fetch an error from the device error queue
722              
723             =head2 get_status
724              
725             $status = $s->get_status(['statusbit']);
726              
727             Fetches the scope status, and returns either the requested
728             status bit (if a 'statusbit' is supplied) or a reference to
729             a hash of status information. Reading the status register
730             causes it to be cleared. A status bit 'ERROR' is combined
731             from the other error bits.
732              
733             Example: $s->get_status('OPC');
734              
735             Example: $s->get_status()->{'DDE'};
736              
737             Status bit names:
738              
739             =over
740              
741             B<PON>: Power on
742              
743             B<URQ>: User Request (not used)
744              
745             B<CME>: Command Error
746              
747             B<EXE>: Execution Error
748              
749             B<DDE>: Device Error
750              
751             B<QYE>: Query Error
752              
753             B<RQC>: Request Control (not used)
754              
755             B<OPC>: Operation Complete
756              
757             B<ERROR>: CME or EXE or DDE or QYE
758              
759             =back
760              
761             =head2 test_busy
762              
763             $busy = $s->test_busy();
764              
765             Returns 1 if busy (waiting for trigger, etc), 0 if not busy.
766              
767             =head2 get_id
768              
769             $s->get_id()
770              
771             Fetch the *IDN? string from device
772              
773             =head2 recall
774              
775             $s->recall($n);
776              
777             $s->recall(n => $n);
778              
779             Recall setup 0..6
780              
781             =head1 COPYRIGHT AND LICENSE
782              
783             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
784              
785             Copyright 2016 Charles Lane
786             2017 Andreas K. Huettel
787             2020 Andreas K. Huettel
788              
789              
790             This is free software; you can redistribute it and/or modify it under
791             the same terms as the Perl 5 programming language system itself.
792              
793             =cut