File Coverage

blib/lib/Lab/Instrument/Source.pm
Criterion Covered Total %
statement 73 209 34.9
branch 17 106 16.0
condition 13 90 14.4
subroutine 15 23 65.2
pod 8 11 72.7
total 126 439 28.7


line stmt bran cond sub pod time code
1             package Lab::Instrument::Source;
2             #ABSTRACT: Generic voltage source base class
3             $Lab::Instrument::Source::VERSION = '3.880';
4 7     7   3777 use v5.20;
  7         28  
5              
6 7     7   53 use strict;
  7         15  
  7         159  
7 7     7   107 use warnings;
  7         16  
  7         256  
8              
9 7     7   44 use Lab::Exception;
  7         14  
  7         177  
10 7     7   1348 use Lab::Instrument;
  7         30  
  7         242  
11 7     7   2849 use Lab::Measurement::KeyboardHandling qw(labkey_check);
  7         20  
  7         238  
12              
13 7     7   69 use Time::HiRes qw(usleep gettimeofday);
  7         19  
  7         59  
14 7     7   787 use Clone qw(clone);
  7         65  
  7         7142  
15              
16             our @ISA = ('Lab::Instrument');
17              
18             our %fields = (
19             supported_connections => [],
20              
21             parent_source => undef,
22             child_sources => [],
23              
24             # supported config options
25             device_settings => {
26             gate_protect => undef,
27             gp_max_units_per_second => undef,
28             gp_max_units_per_step => undef,
29             gp_max_step_per_second => undef,
30             gp_min_units => undef,
31             gp_max_units => undef,
32              
33             gp_equal_level => 0,
34             fast_set => undef,
35             autorange => 0
36             , # silently ignored by instruments (or drivers) which don't support autorange
37              
38             read_default => 'device'
39             },
40              
41             # Config hash passed to subchannel objects, or to $self->configure()
42             default_device_settings => {},
43              
44             gpData => {},
45              
46             default_channel => 1,
47             max_channels => 1,
48              
49             device_cache => {
50             level => undef,
51             range => undef
52             }
53             );
54              
55             sub new {
56 7     7 1 16 my $proto = shift;
57 7   33     34 my $class = ref($proto) || $proto;
58              
59             #
60             # The following is for compatibility with the old syntax for subchannel object derivation.
61             # If we get some Instrument::Source object along with an integer (channel number), use it to derive a subchannel and return this.
62             #
63             # Is it an object at all? Is it a Source object?
64 7 50 33     78 if ( ref( $_[0] )
      33        
65             && UNIVERSAL::can( $_[0], 'can' )
66             && UNIVERSAL::isa( $_[0], "Lab::Instrument::Source" ) ) {
67 0 0 0     0 Lab::Exception::CorruptParameter->throw( error =>
68             "Got a valid Source object, but an invalid channel number: $_[1]. Can't create subchannel, sorry."
69             ) if !defined $_[1] || $_[1] !~ /^[0-9]*$/;
70              
71             # Use the given parent object to derive a subchannel and return it
72 0         0 my ( $parent, $channel ) = ( shift, shift );
73 0         0 my %conf = ();
74 0 0       0 if ( ref $_[0] eq 'HASH' ) { %conf = %{ ; shift } }
  0         0  
  0         0  
75 0         0 else { %conf = (@_) }
76 0         0 return $parent->create_subsource( channel => $channel, %conf );
77             }
78              
79             # compatibility mode stop, continue normally (phew)
80 7         40 my $self = $class->SUPER::new(@_);
81 7         15 $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  7         53  
82              
83             #
84             # Parameter parsing
85             #
86              
87             # checking if a valid default_device_settings hash was set by _construct.
88             # if not, initialize it with $self->device_settings
89 7 50       57 if ( defined( $self->default_device_settings() ) ) {
90 7 50       37 if ( ref( $self->default_device_settings() ) !~ /HASH/ ) {
    50          
91 0         0 Lab::Exception::CorruptParameter->throw(
92             error => 'Given default config is not a hash.' );
93             }
94 7         50 elsif ( scalar keys %{ $self->default_device_settings() } == 0 )
95             { # poor thing's empty
96 7         23 $self->default_device_settings(
97             clone( $self->device_settings() ) );
98             }
99             }
100             else {
101 0         0 $self->default_device_settings( clone( $self->device_settings() ) );
102             }
103              
104             # check max channels
105 7 50       39 if ( defined( $self->config('max_channels') ) ) {
106 0 0       0 if ( $self->config('max_channels') !~ /^[0-9]*$/ ) {
107 0         0 Lab::Exception::CorruptParameter->throw(
108             error => 'Parameter max_channels has to be an Integer' );
109             }
110 0         0 else { $self->max_channels( $self->config('max_channels') ); }
111             }
112              
113             # checking default channel number
114 7 50 33     55 if (
      33        
115             defined( $self->default_channel() )
116             && ( $self->default_channel() > $self->max_channels()
117             || $self->default_channel() < 1 )
118             ) {
119 0         0 Lab::Exception::CorruptParameter->throw( error =>
120             'Default channel number is not within the available channels.'
121             );
122             }
123              
124 7 50       73 if ( defined( $self->parent_source() ) ) {
125 0 0       0 if (
126             !UNIVERSAL::isa(
127             $self->parent_source(), "Lab::Instrument::Source"
128             )
129             ) {
130 0         0 Lab::Exception::CorruptParameter->throw( error =>
131             'Given parent_source object is not a valid Lab::Instrument::Source.'
132             );
133             }
134              
135             # instead of maintaining our own one, check if a valid reference to the gpData from the parent object was given
136 0 0 0     0 if ( !defined( $self->gpData() )
137             || !ref( $self->gpData() ) =~ /HASH/ ) {
138 0         0 Lab::Exception::CorruptParameter->throw(
139             error => 'Given gpData from parent_source is invalid.' );
140             }
141              
142             # shared connection *should* be okay, but keep this in mind
143 0         0 $self->connection( $self->parent_source()->connection() );
144             }
145             else {
146             # fill gpData
147 7         39 for ( my $i = 1; $i <= $self->max_channels(); $i++ ) {
148 7         75 $self->gpData()->{$i} = { LastSettimeMus => undef };
149             }
150             }
151              
152 7         28 return $self;
153             }
154              
155             sub configure {
156 16     16 1 29 my $self = shift;
157              
158 16         36 my $config = shift;
159 16 50       73 if ( ref($config) ne 'HASH' ) {
160 0         0 Lab::Exception::CorruptParameter->throw(
161             error => 'Given Configuration is not a hash.' );
162             }
163             else {
164             #
165             # first do the standard Instrument::configure() on $config
166             #
167 16         86 $self->SUPER::configure($config);
168              
169             #
170             # now parse in default_device_settings
171             #
172 16         25 for my $conf_name ( keys %{ $self->device_settings() } ) {
  16         60  
173             $self->device_settings()->{$conf_name}
174             = $self->default_device_settings()->{$conf_name}
175 265 100 100     959 if exists( $self->default_device_settings()->{$conf_name} )
176             && !defined( $self->device_settings($conf_name) );
177             }
178             }
179             }
180              
181             sub create_subsource
182             { # create_subsource( channel => $channel_nr, more=>options );
183 0     0 1 0 my $self = shift;
184 0         0 my $class = ref($self);
185 0         0 my $args = undef;
186 0 0       0 if ( ref $_[0] eq 'HASH' ) { $args = shift }
  0         0  
187 0         0 else { $args = {@_} }
188              
189             # we may be a subsource ourselfes, here - in this case, use our parent source instead of $self
190 0   0     0 my $parent_to_be = $self->parent_source() || $self;
191              
192             Lab::Exception::CorruptParameter->throw( error =>
193             'No channel number specified! You have to set the channel=>$number parameter.'
194 0 0       0 ) if ( !exists( $args->{'channel'} ) );
195             Lab::Exception::CorruptParameter->throw(
196             error => "Invalid channel number: "
197             . $args->{'channel'}
198             . ". Integer expected." )
199 0 0       0 if ( $args->{'channel'} !~ /^[0-9]*/ );
200              
201             my %default_device_settings
202 0         0 = %{ $parent_to_be->default_device_settings() };
  0         0  
203 0         0 delete local $default_device_settings{'channel'};
204 0         0 @default_device_settings{ keys %{$args} } = values %{$args};
  0         0  
  0         0  
205              
206 7     7   78 no strict 'refs';
  7         14  
  7         440  
207 0         0 my $subsource = $class->new(
208             {
209             parent_source => $parent_to_be,
210             gpData => $parent_to_be->gpData(),
211             %default_device_settings
212             }
213             );
214 7     7   54 use strict;
  7         15  
  7         11863  
215             $parent_to_be->child_sources(
216 0         0 [ @{ $parent_to_be->child_sources() }, $subsource ] );
  0         0  
217 0         0 return $subsource;
218             }
219              
220             sub set_level {
221 0     0 1 0 my $self = shift;
222 0         0 my ( $target, $tail ) = $self->_check_args( \@_, ['target'] );
223              
224 0         0 my $current_level = $self->get_level( { read_mode => 'cache' } );
225              
226 0 0       0 if ( $target == $current_level ) {
227 0         0 return $current_level;
228             }
229              
230 0 0 0     0 if ( $self->device_settings()->{'gate_protect'}
231             and $self->device_settings()->{'gp_max_units_per_step'}
232             < abs( $target - $current_level ) ) {
233 0         0 return $self->sweep_to_level( $target, $tail );
234             }
235             else {
236 0         0 return $self->_set_level( $target, $tail );
237             }
238             }
239              
240             sub get_value {
241 11     11 0 77 my $self = shift;
242              
243 11         36 return $self->get_level(@_);
244             }
245              
246             sub check_sweep_config {
247 0     0 0 0 my $self = shift;
248 0         0 $self->_check_gate_protect();
249 0         0 my ( $target, $rate, $time, $tail )
250             = $self->_check_args( \@_, [ 'points', 'rate', 'time' ] );
251              
252             # get current position:
253 0         0 my $start = $self->get_level( { read_mode => 'device' }, $tail );
254              
255 0         0 my $duration;
256              
257 0 0 0     0 if ( defined $rate and not defined $time ) {
    0 0        
    0 0        
258 0         0 $duration = int( abs( $start - $target ) / abs($rate) );
259             }
260             elsif ( not defined $rate and defined $time ) {
261 0         0 $duration = $time;
262 0         0 $rate = abs( $start - $target ) / $time;
263             }
264             elsif ( defined $rate and defined $time ) {
265 0         0 Lab::Exception::CorruptParameter->throw(
266             "Definition of rate and time simultanousely is inconsistent!");
267             }
268             else {
269 0 0       0 if ( $self->device_settings()->{gate_protect} ) {
270 0         0 $rate = $self->device_settings()->{gp_max_units_per_second};
271 0         0 $duration = int( abs( $start - $target ) / abs($rate) );
272             }
273             else {
274 0         0 Lab::Exception::CorruptParameter->throw(
275             "If not in gate protection mode, please define at least rate or time"
276             );
277             }
278              
279             }
280              
281             # check if the given target value and given rate are within the GATE-PROTECTION limts:
282 0 0       0 if ( $self->device_settings()->{gate_protect} ) {
283              
284 0 0 0     0 if ( $target < $self->device_settings()->{gp_min_units}
285             or $target > $self->device_settings()->{gp_max_units} ) {
286             Lab::Exception::CorruptParameter->throw( error =>
287             "SWEEP-TARGET $target exceeds GATE_PROTECTION LIMITS: "
288             . $self->device_settings()->{gp_min_volt} . " ... "
289 0         0 . $self->device_settings()->{gp_max_volt} );
290             }
291 0 0       0 if (
292             abs($rate)
293             > abs( $self->device_settings()->{gp_max_units_per_second} ) ) {
294             Lab::Exception::CorruptParameter->throw(
295             error => "SWEEP-RATE $rate exceeds GATE_PROTECTION LIMITS: "
296 0         0 . $self->device_settings()->{gp_max_units_per_second} );
297             }
298             }
299              
300             # check if rate is within limits:
301 0 0       0 if ( $rate == 0 ) {
    0          
    0          
302 0         0 print Lab::Exception::CorruptParameter->new( error =>
303             " Sweep rate too small: Maximum Sweep duration is limited to 176400 sec. "
304             );
305 0         0 $rate = abs( $start - $target ) / 176400;
306             }
307             elsif ( abs( $start - $target ) / $rate > 176400 ) {
308 0         0 print Lab::Exception::CorruptParameter->new( error =>
309             " Sweep rate too small: Maximum Sweep duration is limited to 176400 sec. "
310             );
311 0         0 $rate = abs( $start - $target ) / 176400;
312             }
313             elsif ( abs( $start - $target ) / $rate < 0.1 ) {
314              
315             #print Lab::Exception::CorruptParameter->new( error=> " Sweep rate too large: Minimum Sweep duration is limited to 0.1 sec. ");
316 0         0 $duration = 0.1;
317             }
318              
319             # calculate duration and the number of points for the sweep:
320              
321             # Test if $target in range and start programming the device:
322 0         0 my $range = $self->get_range( { read_mode => 'cache' }, $tail );
323              
324 0 0       0 if ( $target > $range ) {
    0          
325 0         0 Lab::Exception::CorruptParameter->throw( error =>
326             "SWEEP-TARGET $target exceeds selected RANGE $range. Change SWEEP-TARGET to MAX within RANGE."
327             );
328             }
329             elsif ( $target < -$range ) {
330 0         0 Lab::Exception::CorruptParameter->throw( error =>
331             "SWEEP-TARGET $target exceeds selected RANGE $range. Change SWEEP-TARGET to MAX within RANGE."
332             );
333             }
334              
335             # split sweep longer than 3600 sec into sections
336 0         0 my $sections = int( $duration / 3600 ) + 1;
337 0         0 $duration = sprintf( "%.1f", $duration / $sections );
338              
339 0 0       0 if ( $sections > 50 ) {
340 0         0 Lab::Exception::CorruptParameter->throw( error =>
341             "Configured Sweep takes too long. Sweep time is limited to 176400s."
342             );
343             }
344              
345 0         0 return ( $start, $target, $duration, $sections, $tail );
346              
347             }
348              
349             sub sweep_to_level {
350 2     2 1 5 my $self = shift;
351              
352 2         10 my ( $target, $time, $stepsize, $args )
353             = $self->_check_args( \@_, [ 'target', 'time', 'stepsize' ] );
354              
355 2 50 33     10 if ( not defined $target || ref($target) eq 'HASH' ) {
356 0         0 Lab::Exception::CorruptParameter->throw(
357             error => 'No target voltage given.' );
358             }
359              
360             # Check correct channel setup
361              
362 2         21 $self->_check_gate_protect();
363              
364             # Make sure stepsize is within gate_protect boundaries.
365              
366             # Returns undef if gate_protect is unset.
367 2         30 my $upstep = $self->get_gp_max_units_per_step();
368              
369 2 50 66     11 if ( ( not defined $upstep ) && ( not defined $stepsize ) ) {
370 0         0 Lab::Exception::CorruptParameter->throw( error =>
371             'Need either gp_max_units_per_step or stepsize parameter.' );
372             }
373              
374 2 100       6 if ( not defined $upstep ) {
375 1         3 $upstep = $stepsize;
376             }
377              
378 2 50 33     18 if ( defined $stepsize && defined $upstep && $upstep > $stepsize ) {
      33        
379 0         0 $upstep = $stepsize;
380             }
381              
382 2         31 my $apsec = $self->get_gp_max_units_per_second();
383              
384 2         26 my $spsec = $self->get_gp_max_step_per_second();
385              
386 2         12 my $current = $self->get_level();
387              
388 2 50       15 if ( $target == $current ) {
389 2         28 return $target;
390             }
391              
392 0 0 0     0 if ( $self->device_settings()->{gate_protect} && $time ) {
    0          
393 0 0       0 if ( abs( $target - $current ) > $apsec * $time ) {
394              
395             # we need to increase time to stay within the
396             # max_units_per_second limit.
397 0         0 $time = abs( $target - $current ) / $apsec;
398             }
399             }
400             elsif ( not defined($time) ) {
401 0         0 $time = abs( $target - $current ) / $apsec;
402             }
403              
404             # Sweep to current.
405              
406 0 0       0 if ( $self->can("_sweep_to_level") ) {
407 0         0 return $self->_sweep_to_level( $target, $time, $args );
408             }
409             else {
410              
411 0         0 my $steptime = $time / ( abs( $current - $target ) / $upstep );
412 0         0 while (1) {
413 0 0       0 if ( abs( $target - $current ) <= $upstep ) {
414 0         0 $self->_set_level( $target, $args );
415 0         0 last;
416             }
417              
418 0         0 my $step = $upstep;
419              
420 0 0       0 if ( $target < $current ) {
421 0         0 $step *= -1.;
422             }
423              
424 0         0 $current = $self->_set_level( $current + $step, $args );
425              
426 0         0 usleep( 1e6 * $steptime );
427             }
428 0         0 return $current;
429             }
430              
431             }
432              
433             sub is_me_channel {
434 0     0 0 0 my $self = shift;
435 0         0 my $channel = shift;
436 0 0       0 if ( $channel < 0 ) {
437 0         0 Lab::Exception::CorruptParameter->throw( error =>
438             'Channel must not be negative! Did you swap voltage and channel number?'
439             );
440             }
441 0 0       0 if ( int($channel) != $channel ) {
442 0         0 Lab::Exception::CorruptParameter->throw( error =>
443             'Channel must be an integer! Did you swap voltage and channel number?'
444             );
445             }
446              
447             }
448              
449             # Check if all gate protect variables are set correctly. And if not, calculate the missing values. Error if
450             # volt_per_step
451             # or volt_per_sec / steps_per_sec is not given
452              
453             sub _check_gate_protect {
454 2     2   4 my $self = shift;
455 2         8 my $mode = shift;
456 2         17 my $device_settings = $self->device_settings();
457              
458 2 50       8 if ( !$device_settings->{gate_protect} ) {
459 2         7 return;
460             }
461              
462             # Check if one of gp_max_units_per_second or gp_max_step_per_second is given
463              
464 0           my $apsec = $device_settings->{gp_max_units_per_second};
465 0           my $apstep = $device_settings->{gp_max_units_per_step};
466 0           my $spsec = $device_settings->{gp_max_step_per_second};
467              
468             # Make sure the gate protect vars are correctly set and consistent
469              
470 0 0 0       if ( ( !defined($apstep) || $apstep <= 0 ) ) {
471 0           Lab::Exception::CorruptParameter->throw( error =>
472             "To use gate protection, you have to gp_max_units_per_step (now: $apstep) to a positive, non-zero value."
473             );
474             }
475              
476 0 0 0       if ( ( defined($apsec) && $apsec > 0 )
    0 0        
    0 0        
      0        
      0        
      0        
      0        
      0        
      0        
477             && ( !defined($spsec) || $spsec < 0 ) ) {
478 0           $spsec = $apsec / $apstep;
479             }
480             elsif (( defined($spsec) && $spsec > 0 )
481             && ( !defined($apsec) || $apsec < 0 ) ) {
482 0           $apsec = $spsec * $apstep;
483             }
484             elsif (( !defined($apsec) || $apsec <= 0 )
485             && ( !defined($spsec) || $spsec < 0 ) ) {
486 0           Lab::Exception::CorruptParameter->throw(
487             "Please supply one of either gp_max_units_per_second or gp_max_steps_per_second."
488             );
489             }
490             else {
491 0 0         if ( $apsec <= $spsec * $apstep ) {
492 0           $spsec = $apsec / $apstep;
493             }
494             else {
495 0           $apsec = $spsec * $apstep;
496             }
497             }
498              
499 0           $device_settings->{gp_max_units_per_second} = $apsec;
500 0           $device_settings->{gp_max_units_per_step} = $apstep;
501 0           $device_settings->{gp_max_step_per_second} = $spsec;
502              
503             # gp_max_units and gp_min_units are mandatory
504 0           my $gp_max_units = $device_settings->{gp_max_units};
505 0           my $gp_min_units = $device_settings->{gp_min_units};
506              
507 0 0 0       if ( not defined $gp_max_units or not defined $gp_min_units ) {
508 0           Lab::Exception::CorruptParameter->throw(
509             "Missing gate_protect parameters for \"gp_max_units\" or \"gp_min_units\".\nYou must provide those during instrument initialization.\nIf you do not want to use the gate_protect feature,\nyou have to set gate_protect to 0."
510             );
511             }
512              
513 0 0         if ( $gp_max_units <= $gp_min_units ) {
514 0           Lab::Exception::CorruptParameter->throw(
515             "value of \"gp_max_units\" is smaller or equal to value of \"gp_min_units\""
516             );
517             }
518             }
519              
520             sub _set_level {
521 0     0     my $self = shift;
522              
523 0           Lab::Exception::DriverError->throw( "The unimplemented method stub "
524             . __PACKAGE__
525             . "::_set_level() has been called. I can't work like this.\n" );
526             }
527              
528             sub get_level {
529 0     0 1   my $self = shift;
530              
531 0           Lab::Exception::DriverError->throw( "The unimplemented method stub "
532             . __PACKAGE__
533             . "::get_level() has been called. I can't work like this.\n" );
534              
535             }
536              
537             sub get_range {
538 0     0 1   my $self = shift;
539              
540 0           Lab::Exception::DriverError->throw( "The unimplemented method stub "
541             . __PACKAGE__
542             . "::get_range() has been called. I can't work like this.\n" );
543             }
544              
545             sub set_range {
546 0     0 1   my $self = shift;
547              
548 0           Lab::Exception::DriverError->throw( "The unimplemented method stub "
549             . __PACKAGE__
550             . "::set_range() has been called. I can't work like this.\n" );
551              
552             }
553              
554             1;
555              
556             __END__
557              
558             =pod
559              
560             =encoding utf-8
561              
562             =head1 NAME
563              
564             Lab::Instrument::Source - Generic voltage source base class
565              
566             =head1 VERSION
567              
568             version 3.880
569              
570             =head1 DESCRIPTION
571              
572             This class implements a general voltage source, if necessary with several
573             channels. It is meant to be inherited by instrument classes that implement
574             real voltage sources (e.g. the L<Lab::Instrument::Yokogawa7651> class).
575              
576             The class provides a unified user interface for those voltage sources
577             to support the exchangeability of instruments.
578              
579             Additionally, this class provides a safety mechanism called C<gate_protect>
580             to protect delicate samples. It includes automatic limitations of sweep rates,
581             voltage step sizes, minimal and maximal voltages.
582              
583             There's no direct user application of this class.
584              
585             =head1 CONSTRUCTOR
586              
587             $self=new Lab::Instrument::Source(\%config);
588              
589             This constructor will only be used by instrument drivers that inherit this class,
590             not by the user. It accepts an additional configuration hash as parameter
591             'default_device_settings'. The first hash contains the parameters used by
592             default for this device and its subchannels, if any. The second hash can be used
593             to override options for this instance while still using the defaults for derived
594             objects. If \%config is missing, \%default_config is used.
595              
596             The instrument driver (e.g. L<Lab::Instrument::Yokogawa7651>)
597             has e.g. a constructor like this:
598              
599             $yoko=new Lab::Instrument::Yokogawa7651({
600             connection_type => 'LinuxGPIB',
601             gpib_board => $board,
602             gpib_address => $address,
603            
604             gate_protect => $gp,
605             [...]
606             });
607              
608             =head1 METHODS
609              
610             =head2 configure
611              
612             $self->configure(\%config);
613              
614             Supported configure options:
615              
616             In general, all parameters which can be changed by access methods of the
617             class/object can be used. In fact this is what happens, and the config hash
618             given to configure() ist just a shorthand for this. The following are equivalent:
619              
620             $source->set_gate_protect(1);
621             $source->set_gp_max_units_per_second(0.1);
622             ...
623              
624             $source->configure({ gate_protect=>1, gp_max_units_per_second=>0.1, ...)
625              
626             Options in detail:
627              
628             =over 2
629              
630             =item fast_set
631              
632             This parameter controls the return value of the set_voltage function and can be set to 0 (off,
633             default) or 1 (on). For fast_set off, set_voltage first requests the hardware to set the voltage,
634             and then reads out the actually set voltage via get_voltage. The resulting number is returned.
635             For fast_set on, set_voltage requests the hardware to set the voltage and returns without double-check
636             the requested value. This, albeit less secure, may speed up measurements a lot.
637              
638             =item gate_protect
639              
640             Whether to use the automatic sweep speed limitation. Can be set to 0 (off) or 1 (on).
641             If it is turned on, the output voltage will not be changed faster than allowed
642             by the C<gp_max_units_per_second>, C<gp_max_units_per_step> and C<gp_max_step_per_second>
643             values. These three parameters overdefine the allowed speed. Only two
644             parameters are necessary. If all three are set, the smallest allowed sweep rate
645             is chosen.
646              
647             Additionally the maximal and minimal output voltages are limited.
648              
649             This mechanism is useful to protect sensible samples that are destroyed by
650             abrupt voltage changes. One example is gate electrodes on semiconductor electronics
651             samples, hence the name.
652              
653             =item gp_max_units_per_second
654              
655             How much the output voltage is allowed to change per second.
656              
657             =item gp_max_units_per_step
658              
659             How much the output voltage is allowed to change per step.
660              
661             =item gp_max_step_per_second
662              
663             How many steps are allowed per second.
664              
665             =item gp_min_units
666              
667             The smallest allowed output voltage.
668              
669             =item gp_max_units
670              
671             The largest allowed output voltage.
672              
673             =item gp_equal_level
674              
675             Voltages with a difference less than this value are considered equal.
676              
677             =back
678              
679             =head2 set_level
680              
681             $new_volt=$self->set_level($srclvl);
682              
683             Sets the output to C<$srclvl> (in Volts or Ampere). If the configure option C<gate_protect> is set
684             to a true value, the safety mechanism takes into account the C<gp_max_units_per_step>,
685             C<gp_max_units_per_second> etc. settings, by employing the C<sweep_to_level> method.
686              
687             Returns for C<fast_set> off the actually set output source level. This can be different
688             from C<$srclvl>, due to the C<gp_max_units>, C<gp_min_units> settings. For C<fast_set> on,
689             C<set_level> returns always C<$level>.
690              
691             For a multi-channel device, add the channel number as a parameter:
692              
693             $new_volt=$self->set_voltage($voltage,$channel);
694              
695             =head2 _set_level($targetlvl)
696              
697             Function Stub. Has to be overwritten by device driver.
698              
699             The function should set the source level to $targetlvl on the device.
700             Should return the newly set source level.
701              
702             =head2 get_level()
703              
704             Function Stub. Has to be overwritten by device driver.
705              
706             The function should return the source level from the device cache.
707             If called with the option C< from_device =E<gt> 1 >, the value should be fetched from the device.
708             Should return the current source level.
709              
710             =head2 get_range()
711              
712             Function Stub. Has to be overwritten by device driver.
713              
714             The function should return the source range from the device cache.
715             If called with the option C< from_device =E<gt> 1 >, the value should be fetched from the device.
716             Should return the current source range.
717              
718             =head2 set_range()
719              
720             Function Stub. Has to be overwritten by device driver.
721              
722             The function should set the source range on the device.
723             Should return the newly set source range.
724              
725             =head2 sweep_to_level
726              
727             $new_volt=$self->sweep_to_level($srclvl);
728             $new_volt=$self->sweep_to_level($srclvl,$channel);
729              
730             This method sweeps the output source level to the desired value and only returns then.
731             If the specific Instrument implemented _sweep_to_level, this version is preferred.
732              
733             Returns the actually set output source level. This can be different from C<$srclvl>, due
734             to the C<gp_max_units>, C<gp_min_units> settings.
735              
736             =head2 get_level
737              
738             $new_volt=$self->get_level();
739             $new_volt=$self->get_level($channel);
740              
741             Returns the source level currently set.
742              
743             =head2 create_subsource
744              
745             $bigc2 = $bigsource->create_subsource( channel=>2, gp_max_units_per_second=>0.01 );
746              
747             Returns a new instrument object with its default channel set to channel $channel_nr of the parent multi-channel source.
748             The device_settings given to the parent at instantiation (or the default_device_settings if present) will be used as default
749             values, which can be overwritten by parameters to create_subsource().
750              
751             =head1 CAVEATS/BUGS
752              
753             Probably many.
754              
755             =head1 SEE ALSO
756              
757             =over 4
758              
759             =item L<Time::HiRes>
760              
761             Used internally for the sweep timing.
762              
763             =item L<Lab::Instrument::KnickS252>
764              
765             This class inherits the gate protection mechanism.
766              
767             =item L<Lab::Instrument::Yokogawa7651>
768              
769             This class inherits the gate protection mechanism.
770              
771             =back
772              
773             =head1 COPYRIGHT AND LICENSE
774              
775             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
776              
777             Copyright 2005-2006 Daniel Schroeer
778             2009 Andreas K. Huettel, Daniela Taubert
779             2010 Andreas K. Huettel, Daniel Schroeer
780             2011 Andreas K. Huettel, Florian Olbrich
781             2012 Alois Dirnaichner, Andreas K. Huettel, Florian Olbrich
782             2013 Alois Dirnaichner, Andreas K. Huettel, Christian Butschkow, Stefan Geissler
783             2014 Alexei Iankilevitch, Alois Dirnaichner, Christian Butschkow
784             2016 Simon Reinhardt
785             2017 Andreas K. Huettel
786             2020 Andreas K. Huettel
787              
788              
789             This is free software; you can redistribute it and/or modify it under
790             the same terms as the Perl 5 programming language system itself.
791              
792             =cut