File Coverage

blib/lib/Lab/Moose/Instrument/Keithley2000.pm
Criterion Covered Total %
statement 26 180 14.4
branch 0 44 0.0
condition 0 24 0.0
subroutine 9 36 25.0
pod 9 15 60.0
total 44 299 14.7


line stmt bran cond sub pod time code
1             package Lab::Moose::Instrument::Keithley2000;
2             $Lab::Moose::Instrument::Keithley2000::VERSION = '3.880';
3             #ABSTRACT: Keithley 2000 digital multimeter
4              
5 1     1   2964 use v5.20;
  1         4  
6              
7 1     1   13 use Moose;
  1         4  
  1         7  
8 1     1   7948 use MooseX::Params::Validate;
  1         2  
  1         13  
9 1     1   569 use Moose::Util::TypeConstraints qw/enum/;
  1         2  
  1         23  
10             use Lab::Moose::Instrument
11 1     1   555 qw/validated_getter validated_setter setter_params validated_no_param_setter/;
  1         3  
  1         95  
12 1     1   11 use Lab::Moose::Instrument::Cache;
  1         3  
  1         12  
13 1     1   756 use Carp;
  1         4  
  1         66  
14 1     1   7 use namespace::autoclean;
  1         14  
  1         10  
15 1     1   132 use Time::HiRes qw (usleep);
  1         6  
  1         11  
16              
17             extends 'Lab::Moose::Instrument';
18              
19             with qw(
20             Lab::Moose::Instrument::Common
21             Lab::Moose::Instrument::SCPI::Sense::Function
22             Lab::Moose::Instrument::SCPI::Sense::NPLC
23             Lab::Moose::Instrument::SCPI::Sense::Range
24             Lab::Moose::Instrument::SCPI::Format
25             Lab::Moose::Instrument::SCPI::Initiate
26             );
27              
28             # ---------------------- Init DMM ----------------------------------------------
29             sub BUILD {
30 0     0 0   my $self = shift;
31              
32 0           $self->clear();
33 0           $self->cls();
34 0           $self->initiate_continuous(value => 0);
35             }
36              
37              
38             # ----------------------- Config DMM ------------------------------------------------------
39              
40             cache sense_average_state => ( getter => 'sense_average_state_query' );
41              
42             sub sense_average_state_query {
43 0     0 0   my ( $self, %args ) = validated_getter( \@_ );
44              
45 0           my $function = $self->sense_function_query();
46              
47 0           return $self->cached_sense_average_state(
48             $self->query( command => "SENS:$function:AVER:STAT?", %args ) );
49             }
50              
51             sub sense_average_state {
52 0     0 0   my ( $self, $value, %args ) = validated_setter( \@_ );
53              
54 0           my $function = $self->sense_function_query();
55              
56 0           $self->write( command => "SENS:$function:AVER $value", %args );
57 0           return $self->cached_sense_average_state($value);
58             }
59              
60             cache sense_average_count => (
61             getter => 'sense_average_count_query',
62             isa => 'Int'
63             );
64              
65             sub sense_average_count_query {
66 0     0 0   my ( $self, %args ) = validated_getter( \@_ );
67              
68 0           my $function = $self->sense_function_query();
69              
70 0           return $self->cached_sense_average_state(
71             $self->query( command => "SENS:$function:AVER:COUN?", %args ) );
72             }
73              
74             sub sense_average_count {
75 0     0 0   my ( $self, $value, %args ) = validated_setter( \@_,
76             value => {isa => 'Int'}
77             );
78              
79 0           my $function = $self->sense_function_query();
80              
81 0           $self->write( command => "SENS:$function:AVER:COUN $value", %args );
82 0           return $self->cached_sense_average_state($value);
83             }
84              
85             # ----------------------------------------- MEASUREMENT ----------------------------------
86              
87              
88             cache value => ( getter => 'get_value' );
89              
90             sub get_value { # basic
91 0     0 1   my ( $self, %args ) = validated_getter( \@_ );
92              
93 0           my $function = $self->sense_function_query();
94              
95 0 0         if ( $function
96             =~ /\b(PERIOD|period|PER|per|FREQUENCY|frequency|FREQ|freq|TEMPERATURE|temperature|TEMP|temp|DIODE|diode|DIOD|diod|CURRENT|current|CURR|curr|CURRENT:AC|current:ac|CURR:AC|curr:ac|CURRENT:DC|current:dc|CURR:DC|curr:dc|VOLTAGE|voltage|VOLT|volt|VOLTAGE:AC|voltage:ac|VOLT:AC|volt:ac|VOLTAGE:DC|voltage:dc|VOLT:DC|volt:dc|RESISTANCE|resisitance|RES|res|FRESISTANCE|fresistance|FRES|fres)\b/
97             ) {
98 0           return $self->query(command => 'MEAS?' );
99             }
100             else {
101 0           croak "unexpected value for 'function' in sub measure. Function can be CURRENT:AC, CURRENT:DC, VOLTAGE:AC, VOLTAGE:DC, RESISTANCE, FRESISTANCE, PERIOD, FREQUENCY, TEMPERATURE, DIODE";
102             }
103             }
104              
105              
106             sub config_measurement { # basic
107 0     0 1   my ( $self, %args ) = validated_getter( \@_,
108             function => {optional => 1},
109             range => {default => 'DEF'},
110             trigger => {default => 'BUS'},
111             time => {isa => 'Num'},
112             nop => {isa => 'Int'},
113              
114             );
115              
116 0           my $function = delete $args{function};
117 0           my $nop = delete $args{nop};
118 0           my $time = delete $args{time};
119 0           my $range = delete $args{range};
120 0           my $trigger = delete $args{trigger};
121              
122              
123             # check input data
124 0 0         if ( not defined $time ) {
125 0           croak "too few arguments given in sub config_measurement. Expected arguments are FUNCTION, #POINTS, TIME, <RANGE>, <TRIGGERSOURCE>";
126             }
127              
128 0           $self->sense_function(value => $function);
129 0           print "sub config_measurement: set FUNCTION: "
130             . $self->cached_sense_function() . "\n";
131              
132 0           $self->sense_range(value => $range );
133 0           print "sub config_measurement: set RANGE: " . $self->cached_sense_range() . "\n";
134              
135 0           my $nplc = ( $time * 50 ) / $nop;
136 0 0         if ( $nplc < 0.01 ) {
137 0           croak "unexpected value for TIME in sub config_measurement. Expected values are between 0.5 ... 50000 sec.";
138             }
139 0           $self->sense_nplc(value => $nplc );
140 0           print "sub config_measurement: set NPLC: " . $self->cached_sense_nplc() . "\n";
141              
142 0           print "sub config_measurement: init BUFFER: "
143             . $self->_init_buffer(value => $nop) . "\n";
144              
145 0           print "sub config_measurement: init TRIGGER: "
146             . $self->_init_trigger(value => $trigger) . "\n";
147              
148 0           return $nplc;
149              
150             }
151              
152              
153             sub trg { # basic
154 0     0 1   my ( $self, %args ) = validated_getter( \@_ );
155 0           $self->write(command => "*TRG");
156             }
157              
158              
159             sub abort { # basic
160 0     0 1   my ( $self, %args ) = validated_getter( \@_ );
161 0           $self->write(command => "ABORT");
162             }
163              
164              
165             sub wait { # basic
166 0     0 1   my ( $self, $timeout, %args ) = validated_getter( \@_,
167             timeout => {default => 100}
168             );
169              
170 0           print "waiting for data ... \n";
171 0           while (1) {
172 0 0         if ( $self->query(command => ":STATUS:OPERATION:CONDITION?") >= 1024 ) {
173 0           last;
174             } # check if measurement has been finished
175 0           else { usleep(1e3); }
176             }
177             }
178              
179              
180             sub active { # basic
181 0     0 1   my ( $self, %args ) = validated_no_param_setter( \@_,
182             timeout => {default => 100}
183             );
184              
185 0           my $timeout = delete $args{timeout};
186              
187             # check if measurement has been finished
188 0 0         if ( $self->query(command => ":STATUS:OPERATION:CONDITION?") >= 1024 ) {
189 0           return 0;
190             }
191             else {
192 0           return 1;
193             }
194             }
195              
196              
197             sub get_data { # basic
198 0     0 1   my ( $self, %args ) = validated_getter( \@_ );
199 0           return $self->_read_buffer();
200             }
201              
202             # ------------------------------------ DATA BUFFER ----------------------------------------
203              
204             sub _clear_buffer { # internal
205 0     0     my ( $self, %args ) = validated_getter( \@_ );
206 0           $self->write(command => ":DATA:CLEAR");
207 0           return $self->query(command => ":DATA:FREE?");
208             }
209              
210             sub _init_buffer { # internal
211 0     0     my ( $self, $value, %args ) = validated_setter( \@_ );
212              
213 0           $self->_clear_buffer();
214              
215 0 0 0       if ( $value >= 2 && $value <= 1024 ) {
216 0           $self->cls();
217 0           $self->write(command => ":STATUS:OPERATION:ENABLE 16");
218             # enable status bit for measuring/idle status
219 0           $self->write(command => "INIT:CONT OFF"); # set DMM to IDLE-state
220 0           $self->_init_trigger(value => "BUS");
221             # trigger-count = 1, trigger-delay = 0s, trigger-source = IMM/EXT/TIM/MAN/BUS
222 0           $self->_set_triggercount(value => 1);
223 0           $self->_set_triggerdelay(value => 0);
224 0           my $return_nop = $self->_set_samplecount(value => $value);
225 0           $self->write(command => ":INIT"); # set DMM from IDLE to WAIT-FOR_TRIGGER status
226 0           return $return_nop;
227             }
228             else {
229 0           croak "unexpected value in sub set_nop_for_buffer. Must be between 2 and 1024.";
230             }
231             }
232              
233             sub _read_buffer { # basic
234 0     0     my ( $self, $value, %args ) = validated_setter( \@_,
235             value => {isa => 'Bool', default => 0}
236             );
237              
238             # wait until data are available
239 0           $self->wait();
240              
241             #read data
242 0           $self->write(command => ":FORMAT:DATA ASCII; :FORMAT:ELEMENTS READING");
243              
244             # select Format for reading DATA
245 0           my $data = $self->query(command => ":DATA:DATA?", read_length => 65536);
246 0           chomp $data;
247 0           my @data = split( ",", $data );
248              
249             #print data
250 0 0         if ( $value) {
251 0           foreach my $item (@data) { print $item. "\n"; }
  0            
252             }
253              
254 0           return @data;
255             }
256              
257             # -------------------------------------- TRIGGER ----------------------------------------------
258              
259             sub _init_trigger { # internal
260 0     0     my ( $self, $value, %args ) = validated_setter( \@_,
261             value => {isa => 'Str', default => 'BUS'} # set BUS as default trigger source
262             );
263              
264 0           $self->_set_triggercount(value => "DEF"); # DEF = 1
265 0           $self->_set_triggerdelay(value => "DEF"); # DEF = 0
266 0           $self->_set_triggersource(value => "BUS");
267              
268 0           return "trigger initiated";
269             }
270              
271             sub _set_triggersource { # internal
272 0     0     my ( $self, $value, %args ) = validated_setter( \@_,
273             value => {optional => 1}
274             );
275              
276             #return setting
277 0 0         if ( not defined $value ) {
278 0           $value = $self->query(command => ":TRIGGER:SOURCE?");
279 0           chomp($value);
280 0           return $value;
281             }
282              
283             #set triggersoource
284 0 0         if ( $value =~ /\b(IMM|imm|EXT|ext|TIM|tim|MAN|man|BUS|bus)\b/ ) {
285 0           return $self->query(command =>
286             sprintf( ":TRIGGER:SOURCE %s; SOURCE?", $value ) );
287             }
288             else {
289 0           croak "unexpected value in sub _init_trigger. Must be IMM, EXT, TIM, MAN or BUS.";
290             }
291             }
292              
293             sub _set_samplecount { # internal
294 0     0     my ( $self, $value, %args ) = validated_setter( \@_,
295             value => {optional => 1}
296             );
297              
298             #return setting
299 0 0         if ( not defined $value ) {
300 0           $value = $self->query(command => ":SAMPLE:COUNT?");
301 0           chomp($value);
302 0           return $value;
303             }
304              
305             #set samplecount
306 0 0 0       if ( $value >= 1 && $value <= 1024 ) {
307 0           return $self->query(command =>
308             sprintf( ":SAMPLE:COUNT %d; COUNT?", $value ) );
309             }
310             else {
311 0           croak "unexpected value in sub _set_samplecount. Must be between 1 and 1024.";
312             }
313              
314             }
315              
316             sub _set_triggercount { # internal
317 0     0     my ( $self, $value, %args ) = validated_setter( \@_,
318             value => {optional => 1}
319             );
320              
321             #return setting
322 0 0         if ( not defined $value ) {
323 0           $value = $self->query(command => ":TRIGGER:COUNT?");
324 0           chomp($value);
325 0           return $value;
326             }
327              
328             #set triggercount
329 0 0 0       if ( ( $value >= 1 && $value <= 1024 )
      0        
330             or $value =~ /\b(MIN|min|MAX|max|DEF|def)\b/ ) {
331 0           return $self->query(command => ":TRIGGER:COUNT $value; COUNT?");
332             }
333             else {
334 0           croak "unexpected value in sub _set_triggercount. Must be between 1 and 1024 or MIN/MAX/DEF.";
335             }
336             }
337              
338             sub _set_triggerdelay { # internal
339 0     0     my ( $self, $value, %args ) = validated_setter( \@_ );
340              
341             #return setting
342 0 0         if ( not defined $value ) {
343 0           $value = $self->query(command => ":TRIGGER:DELAY?");
344 0           chomp($value);
345 0           return $value;
346             }
347              
348             #set triggerdelay
349 0 0 0       if ( ( $value >= 0 && $value <= 999999.999 )
      0        
350             or $value =~ /\b(MIN|min|MAX|max|DEF|def)\b/ ) {
351 0           return $self->query(command => ":TRIGGER:DELAY $value; DELAY?");
352             }
353             else {
354 0           croak "unexpected value in sub _set_triggerdelay. Must be between 0 and 999999.999sec or MIN/MAX/DEF.";
355             }
356             }
357              
358             sub set_timer { # advanced
359 0     0 0   my ( $self, $value, %args ) = validated_setter( \@_ );
360              
361             #return setting
362 0 0         if ( not defined $value ) {
363 0           $value = $self->query(command => ":TRIGGER:TIMER?");
364 0           chomp($value);
365 0           return $value;
366             }
367              
368             #set timer
369 0 0 0       if ( ( $value >= 1e-3 && $value <= 999999.999 )
      0        
370             or $value =~ /\b(MIN|min|MAX|max|DEF|def)\b/ ) {
371 0           return $self->query(command => ":TRIGGER:TIMER $value; TIMER?");
372             }
373             else {
374 0           croak "unexpected value for TIMER in sub set_timer. Must be between 0 and 999999.999sec or MIN/MAX/DEF.";
375             }
376             }
377              
378             # -----------------------------------------DISPLAY --------------------------------
379              
380              
381             sub display { # basic
382 0     0 1   my ( $self, $value, %args ) = validated_setter( \@_ );
383              
384 0 0         if ( not defined $value ) {
    0          
    0          
    0          
385 0           return $self->_display_text();
386             }
387             elsif ( $value =~ /\b(ON|on)\b/ ) {
388 0           return $self->_display_on();
389             }
390             elsif ( $value =~ /\b(OFF|off)\b/ ) {
391 0           return $self->_display_off();
392             }
393             elsif ( $value =~ /\b(CLEAR|clear)\b/ ) {
394 0           return $self->_display_clear();
395             }
396             else {
397 0           return $self->_display_text(value => $value);
398             }
399              
400             }
401              
402             sub _display_on { # for internal/advanced use only
403 0     0     my ( $self, %args ) = validated_getter( \@_ );
404 0           $self->write(command => ":DISPLAY:ENABLE ON");
405             }
406              
407             sub _display_off { # for internal/advanced use only
408 0     0     my ( $self, %args ) = validated_getter( \@_ );
409 0           $self->write(command => ":DISPLAY:ENABLE OFF")
410             ; # when display is disabled, the instrument operates at a higher speed. Frontpanel commands are frozen.
411             }
412              
413             sub _display_text { # for internal/advanced use only
414 0     0     my ( $self, $value, %args ) = validated_setter( \@_,
415             value => {optional => 1}
416             );
417              
418 0 0         if ($value) {
419 0           chomp( $value
420             = $self->query(command => "DISPLAY:TEXT:DATA '$value'; STATE 1; DATA?") );
421 0           $value =~ s/\"//g;
422 0           return $value;
423             }
424             else {
425 0           chomp( $value = $self->query(command => "DISPLAY:TEXT:DATA?") );
426 0           $value =~ s/\"//g;
427 0           return $value;
428             }
429             }
430              
431             sub _display_clear { # for internal/advanced use only
432 0     0     my ( $self, %args ) = validated_getter( \@_ );
433 0           $self->write(command => "DISPlay:TEXT:STATE 0");
434             }
435              
436             # ----------------------------------------------------------------------------------------
437              
438              
439             sub beep {
440 0     0 1   my ( $self, %args ) = validated_getter( \@_ );
441 0           $self->write(command => "BEEP");
442             }
443              
444             __PACKAGE__->meta()->make_immutable();
445              
446             1;
447              
448             __END__
449              
450             =pod
451              
452             =encoding UTF-8
453              
454             =head1 NAME
455              
456             Lab::Moose::Instrument::Keithley2000 - Keithley 2000 digital multimeter
457              
458             =head1 VERSION
459              
460             version 3.880
461              
462             =head1 SYNOPSIS
463              
464             use Lab::Moose;
465              
466             my $DMM= instrument(
467             type => 'Keithley2000',
468             connection_type => 'LinuxGPIB',
469             connection_options => {gpib_address => 15},
470             );
471              
472             =head1 DESCRIPTION
473              
474             The Lab::Moose::Instrument::Keithley2000 class implements an interface to the
475             Keithley 2000 digital multimeter.
476              
477             =head1 METHODS
478              
479             Used roles:
480              
481             =over
482              
483             =item L<Lab::Moose::Instrument::Common>
484              
485             =item L<Lab::Moose::Instrument::SCPI::Sense::Function>
486              
487             =item L<Lab::Moose::Instrument::SCPI::Sense::NPLC>
488              
489             =item L<Lab::Moose::Instrument::SCPI::Sense::Range>
490              
491             =item L<Lab::Moose::Instrument::SCPI::Format>
492              
493             =item L<Lab::Moose::Instrument::SCPI::Initiate>
494              
495             =back
496              
497             =head2 get_value
498              
499             $DMM->get_value(value = $function);
500              
501             Make a measurement defined by $function with the previously specified range
502             and integration time.
503              
504             =over 4
505              
506             =item $function
507              
508             FUNCTION can be one of the measurement methods of the Keithley2000.
509              
510             "current:dc" --> DC current measurement
511             "current:ac" --> AC current measurement
512             "voltage:dc" --> DC voltage measurement
513             "voltage:ac" --> AC voltage measurement
514             "resisitance" --> resistance measurement (2-wire)
515             "fresistance" --> resistance measurement (4-wire)
516              
517             =back
518              
519             =head2 config_measurement
520              
521             $DMM->config_measurement(function => $function, $nop => $number_of_points, $time => time, range => $range, trigger => $trigger);
522              
523             Preset the Keithley2000 for a TRIGGERED measurement.
524              
525             WARNING: It's not recomended to perform triggered measurments with the KEITHLEY 2000 DMM due to unsolved timing problems!!!!!
526              
527             =over 4
528              
529             =item OPTIONAL $function
530              
531             FUNCTION can be one of the measurement methods of the Keithley2000.
532              
533             "current:dc" --> DC current measurement
534             "current:ac" --> AC current measurement
535             "voltage:dc" --> DC voltage measurement
536             "voltage:ac" --> AC voltage measurement
537             "resisitance" --> resistance measurement (2-wire)
538             "fresistance" --> resistance measurement (4-wire)
539              
540             =item $number_of_points
541              
542             Preset the NUMBER OF POINTS to be taken for one measurement TRACE .
543             The single measured points will be stored in the internal memory of the Keithley2000.
544             For the Keithley2000 the internal memory is limited to 1024 values.
545              
546             =item $time
547              
548             Preset the TIME duration for one full trace.
549             From TIME the integration time value for each measurement point will be derived [NPLC = (TIME *50Hz)/NOP].
550             Expected values are between 0.21 ... 20000 seconds.
551              
552             =item OPTIONAL $range
553              
554             RANGE is given in terms of amps, volts or ohms and can be 0...+3,03A | MIN | MAX | DEF | AUTO , 0...757V(AC)/1010V(DC) | MIN | MAX | DEF | AUTO or 0...101e6 | MIN | MAX | DEF | AUTO .
555             DEF is default AUTO activates the AUTORANGE-mode.
556             DEF will be set, if no value is given.
557              
558             =item OPTIONAL $trigger
559              
560             Set the TRIGGER
561              
562             =back
563              
564             =head2 trg
565              
566             $DMM->trg();
567              
568             Sends a trigger signal via the GPIB-BUS to start the predefined measurement.
569             The LabVisa-script can immediatally be continued, e.g. to start another triggered measurement using a second Keithley2000.
570              
571             =head2 abort
572              
573             $DMM->abort();
574              
575             Aborts current (triggered) measurement.
576              
577             =head2 wait
578              
579             $DMM->wait();
580              
581             WAIT until triggered measurement has been finished.
582              
583             =head2 active
584              
585             $DMM->active();
586              
587             Returns '1' if the current triggered measurement is still active and '0' if the current triggered measurement has allready been finished.
588              
589             =head2 get_data
590              
591             @data = $DMM->get_data();
592              
593             Reads all recorded values from the internal buffer and returnes them as an array of floatingpoint values.
594             Reading the buffer will start immediately after the triggered measurement has finished. The LabVisa-script cannot be continued until all requested readings have been recieved.
595              
596             =head2 display
597              
598             $DMM->display(value => 'ON');
599              
600             Control the K2000s display, $value can be
601              
602             =over 4
603              
604             =item ON
605              
606             Turn on the display
607              
608             =item OFF
609              
610             Turn off the display
611              
612             =item CLEAR
613              
614             Clear the display
615              
616             =item your text
617              
618             Display a custom text
619              
620             =back
621              
622             =head2 beep
623              
624             $DMM->beep();
625              
626             Make a beep sound
627              
628             =head1 CAVEATS/BUGS
629              
630             probably many
631              
632             =head1 SEE ALSO
633              
634             =over 4
635              
636             =item L<Lab::Instrument>
637              
638             =back
639              
640             =head1 COPYRIGHT AND LICENSE
641              
642             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
643              
644             Copyright 2021 Andreas K. Huettel, Fabian Weinelt
645              
646              
647             This is free software; you can redistribute it and/or modify it under
648             the same terms as the Perl 5 programming language system itself.
649              
650             =cut