File Coverage

blib/lib/Lab/Instrument/Source.pm
Criterion Covered Total %
statement 102 192 53.1
branch 31 80 38.7
condition 9 27 33.3
subroutine 8 15 53.3
pod 5 10 50.0
total 155 324 47.8


line stmt bran cond sub pod time code
1             #$Id: Source.pm 650 2010-04-22 19:09:27Z schroeer $
2             package Lab::Instrument::Source;
3 2     2   23252 use strict;
  2         4  
  2         74  
4 2     2   821 use Time::HiRes qw(usleep gettimeofday);
  2         1942  
  2         12  
5            
6             our $VERSION = sprintf("1.%04d", q$Revision: 650 $ =~ / (\d+) /);
7             our $maxchannels = 16;
8            
9             sub new {
10 2     2 0 22 my $proto = shift;
11 2   33     16 my $class = ref($proto) || $proto;
12            
13 2         7 my $self = bless {}, $class;
14            
15 2         8 my $type=ref $_[0];
16            
17 2 50 0     12 if ($type =~ /HASH/) {
    0          
18            
19             # Source gets as parameters 1) the default config of a particular
20             # source class and 2) the config with which the source was instantiated
21            
22 2         5 %{$self->{default_config}}=%{shift @_};
  2         21  
  2         10  
23 2         6 %{$self->{config}}=%{$self->{default_config}};
  2         10  
  2         8  
24 2         16 $self->configure(@_);
25            
26 2         10 for (my $i=1; $i<=$maxchannels; $i++) {
27 32         50 my $tmp="last_voltage_$i";
28 32         69 $self->{_gp}->{$tmp}=undef;
29 32         42 $tmp="last_settime_mus_$i";
30 32         951 $self->{_gp}->{$tmp}=undef;
31             }
32            
33 2         9 $self->{subsource}=0;
34            
35             } elsif (($type =~ /^Lab::Instrument/) && ($_[0]->{IamaSource})) {
36            
37             # Whenever the first parameter is not a default config hash but a
38             # class object inherited from Lab::Instrument with IamaSource set,
39             # we are instantiating a subsource of a multichannel source.
40            
41 0         0 print "Hey great! Someone is testing subchannel sources...\n";
42 0         0 $self->{multisource}=shift;
43 0         0 $self->{channel}=shift;
44            
45             # the default config is in this case the actual config of the
46             # multisource object
47 0         0 %{$self->{default_config}}=%{$self->{multisource}->{config}};
  0         0  
  0         0  
48 0         0 %{$self->{config}}=%{$self->{default_config}};
  0         0  
  0         0  
49 0         0 $self->configure(@_);
50            
51 0         0 $self->{subsource}=1;
52            
53             };
54            
55 2         4 $self->{IamaSource}=1;
56 2         15 return $self;
57             }
58            
59             sub configure {
60 22     22 1 36 my $self=shift;
61             #supported config options are (so far)
62             # gate_protect
63             # gp_max_volt_per_second
64             # gp_max_volt_per_step
65             # gp_max_step_per_second
66             # gp_min_volt
67             # gp_max_volt
68             # qp_equal_level
69             # fast_set
70 22         33 my $config=shift;
71 22 100       76 if ((ref $config) =~ /HASH/) {
    100          
72 9         16 for my $conf_name (keys %{$self->{default_config}}) {
  9         40  
73             #print "Key: $conf_name, default: ",$self->{default_config}->{$conf_name},", old config: ",$self->{config}->{$conf_name},", new config: ",$config->{$conf_name},"\n";
74 55 50 33     413 unless ((defined($self->{config}->{$conf_name})) || (defined($config->{$conf_name}))) {
    100          
75 0         0 $self->{config}->{$conf_name}=$self->{default_config}->{$conf_name};
76             } elsif (defined($config->{$conf_name})) {
77 15         38 $self->{config}->{$conf_name}=$config->{$conf_name};
78             }
79             }
80 9         35 return $self;
81             } elsif($config) {
82 5         27 return $self->{config}->{$config};
83             } else {
84 8         39 return $self->{config};
85             }
86             }
87            
88             sub set_voltage {
89 5     5 1 161 my $self=shift;
90 5         9 my $voltage=shift;
91 5         8 my $channel=shift;
92            
93 5 50       16 $channel = 1 unless defined($channel);
94            
95 5 50       14 die "Channel must not be negative! Did you swap voltage and channel number? Aborting..." if $channel < 0;
96 5 50       12 die "Channel must be an integer! Did you swap voltage and channel number? Aborting..." if int($channel) != $channel;
97            
98 5 100       18 if ($self->{config}->{gate_protect}) {
99 3         8 $voltage=$self->sweep_to_voltage($voltage,$channel);
100             } else {
101 2         7 $self->_set_voltage($voltage,$channel);
102             }
103            
104 5         12 my $result;
105 5 50       19 if ($self->{config}->{fast_set}) {
106 0         0 $result=$voltage;
107             } else {
108 5         34 $result=$self->get_voltage($channel);
109             };
110            
111 5         11 my $tmp="last_voltage_$channel";
112 5         9 $self->{_gp}->{$tmp}=$result;
113 5         27 return $result;
114             }
115            
116             sub set_voltage_auto {
117 0     0 0 0 my $self=shift;
118 0         0 my $voltage=shift;
119 0         0 my $channel=shift;
120            
121 0 0       0 $channel = 1 unless defined($channel);
122            
123 0 0       0 die "Channel must not be negative! Did you swap voltage and channel number? Aborting..." if $channel < 0;
124 0 0       0 die "Channel must be an integer! Did you swap voltage and channel number? Aborting..." if int($channel) != $channel;
125            
126 0 0       0 if ($self->{config}->{gate_protect}) {
127 0         0 $voltage=$self->sweep_to_voltage_auto($voltage,$channel);
128             } else {
129 0         0 $self->_set_voltage_auto($voltage,$channel);
130             }
131            
132 0         0 my $result;
133 0 0       0 if ($self->{config}->{fast_set}) {
134 0         0 $result=$voltage;
135             } else {
136 0         0 $result=$self->get_voltage($channel);
137             };
138            
139 0         0 my $tmp="last_voltage_$channel";
140 0         0 $self->{_gp}->{$tmp}=$result;
141 0         0 return $result;
142             }
143            
144            
145             sub step_to_voltage {
146 1222     1222 1 1520 my $self=shift;
147 1222         1317 my $voltage=shift;
148 1222         1646 my $channel=shift;
149 1222         2688 my $voltpersec=abs($self->{config}->{gp_max_volt_per_second});
150 1222         1705 my $voltperstep=abs($self->{config}->{gp_max_volt_per_step});
151 1222         1621 my $steppersec=abs($self->{config}->{gp_max_step_per_second});
152            
153             #read output voltage from instrument (only at the beginning)
154 1222         2141 my $last_voltage_channel="last_voltage_$channel";
155            
156 1222         1806 my $last_v=$self->{_gp}->{$last_voltage_channel};
157 1222 100       2487 unless (defined $last_v) {
158 1         4 $last_v=$self->get_voltage($channel);
159 1         4 $self->{_gp}->{$last_voltage_channel}=$last_v;
160             }
161            
162 1222 50 33     5753 if (defined($self->{config}->{gp_max_volt}) && ($voltage > $self->{config}->{gp_max_volt})) {
163 0         0 $voltage = $self->{config}->{gp_max_volt};
164             }
165 1222 50 33     5143 if (defined($self->{config}->{gp_min_volt}) && ($voltage < $self->{config}->{gp_min_volt})) {
166 0         0 $voltage = $self->{config}->{gp_min_volt};
167             }
168            
169             #already there
170 1222 100       2972 return $voltage if (abs($voltage - $last_v) < $self->{config}->{gp_equal_level});
171            
172             #are we already close enough? if so, screw the waiting time...
173 1215 100 66     4701 if ((defined $voltperstep) && (abs($voltage - $last_v) < $voltperstep)) {
174 1         10 $self->_set_voltage($voltage,$channel);
175 1         3 $self->{_gp}->{$last_voltage_channel}=$voltage;
176 1         3 return $voltage;
177             }
178            
179             #do the magic step calculation
180 1214 100       2717 my $wait = ($voltpersec < $voltperstep * $steppersec) ?
181             $voltperstep/$voltpersec : # ignore $steppersec
182             1/$steppersec; # ignore $voltpersec
183 1214         1557 my $step=$voltperstep * ($voltage <=> $last_v);
184            
185             #wait if necessary
186 1214         3311 my ($ns,$nmu)=gettimeofday();
187 1214         1923 my $now=$ns*1e6+$nmu;
188            
189 1214         2064 my $last_settime_mus_channel="last_settime_mus_$channel";
190            
191 1214 50       2929 unless (defined (my $last_t=$self->{_gp}->{last_settime_mus})) {
    0          
192 1214         2004 $self->{_gp}->{$last_settime_mus_channel}=$now;
193             } elsif ( $now-$last_t < 1e6*$wait ) {
194 0         0 usleep ( ( 1e6*$wait+$last_t-$now ) );
195 0         0 ($ns,$nmu)=gettimeofday();
196 0         0 $now=$ns*1e6+$nmu;
197             }
198 1214         1756 $self->{_gp}->{$last_settime_mus_channel}=$now;
199            
200             #do one step
201 1214 100       2861 if (abs($voltage-$last_v) > abs($step)) {
202 1213         1472 $voltage=$last_v+$step;
203             }
204 1214         5690 $voltage=0+sprintf("%.10f",$voltage);
205            
206 1214         4367 $self->_set_voltage($voltage,$channel);
207 1214         2353 $self->{_gp}->{$last_voltage_channel}=$voltage;
208 1214         2678 return $voltage;
209             }
210            
211             sub step_to_voltage_auto {
212 0     0 0 0 my $self=shift;
213 0         0 my $voltage=shift;
214 0         0 my $channel=shift;
215 0         0 my $voltpersec=abs($self->{config}->{gp_max_volt_per_second});
216 0         0 my $voltperstep=abs($self->{config}->{gp_max_volt_per_step});
217 0         0 my $steppersec=abs($self->{config}->{gp_max_step_per_second});
218            
219             #read output voltage from instrument (only at the beginning)
220 0         0 my $last_voltage_channel="last_voltage_$channel";
221            
222 0         0 my $last_v=$self->{_gp}->{$last_voltage_channel};
223 0 0       0 unless (defined $last_v) {
224 0         0 $last_v=$self->get_voltage($channel);
225 0         0 $self->{_gp}->{$last_voltage_channel}=$last_v;
226             }
227            
228 0 0 0     0 if (defined($self->{config}->{gp_max_volt}) && ($voltage > $self->{config}->{gp_max_volt})) {
229 0         0 $voltage = $self->{config}->{gp_max_volt};
230             }
231 0 0 0     0 if (defined($self->{config}->{gp_min_volt}) && ($voltage < $self->{config}->{gp_min_volt})) {
232 0         0 $voltage = $self->{config}->{gp_min_volt};
233             }
234            
235             #already there
236 0 0       0 return $voltage if (abs($voltage - $last_v) < $self->{config}->{gp_equal_level});
237            
238             #do the magic step calculation
239 0 0       0 my $wait = ($voltpersec < $voltperstep * $steppersec) ?
240             $voltperstep/$voltpersec : # ignore $steppersec
241             1/$steppersec; # ignore $voltpersec
242 0         0 my $step=$voltperstep * ($voltage <=> $last_v);
243            
244             #wait if necessary
245 0         0 my ($ns,$nmu)=gettimeofday();
246 0         0 my $now=$ns*1e6+$nmu;
247            
248 0         0 my $last_settime_mus_channel="last_settime_mus_$channel";
249            
250 0 0       0 unless (defined (my $last_t=$self->{_gp}->{last_settime_mus})) {
    0          
251 0         0 $self->{_gp}->{last_settime_mus_channel}=$now;
252             } elsif ( $now-$last_t < 1e6*$wait ) {
253 0         0 usleep ( ( 1e6*$wait+$last_t-$now ) );
254 0         0 ($ns,$nmu)=gettimeofday();
255 0         0 $now=$ns*1e6+$nmu;
256             }
257 0         0 $self->{_gp}->{$last_settime_mus_channel}=$now;
258            
259             #do one step
260 0 0       0 if (abs($voltage-$last_v) > abs($step)) {
261 0         0 $voltage=$last_v+$step;
262             }
263 0         0 $voltage=0+sprintf("%.10f",$voltage);
264            
265 0         0 $self->_set_voltage_auto($voltage,$channel);
266 0         0 $self->{_gp}->{$last_voltage_channel}=$voltage;
267 0         0 return $voltage;
268             }
269            
270            
271             sub sweep_to_voltage {
272 7     7 1 13 my $self=shift;
273 7         20 my $voltage=shift;
274 7         11 my $channel=shift;
275            
276 7         7 my $last;
277 7         11 my $cont=1;
278 7         16 while($cont) {
279 1212         1310 $cont=0;
280 1212         2410 my $this=$self->step_to_voltage($voltage,$channel);
281 1212 100 100     6383 unless ((defined $last) && (abs($last-$this) < $self->{config}->{gp_equal_level})) {
282 1205         1459 $last=$this;
283 1205         2538 $cont++;
284             }
285             }; #ugly
286 7         37 return $voltage;
287             }
288            
289             sub _set_voltage {
290 0     0   0 my $self=shift;
291 0         0 my $voltage=shift;
292            
293 0 0       0 if ($self->{subsource}) {
294 0         0 return $self->{multisource}->_set_voltage($voltage, $self->{channel});
295             } else {
296 0         0 warn '_set_voltage not implemented for this instrument';
297             };
298             }
299            
300             sub _set_voltage_auto {
301 0     0   0 my $self=shift;
302 0         0 my $voltage=shift;
303            
304 0 0       0 if ($self->{subsource}) {
305 0         0 return $self->{multisource}->_set_voltage_auto($voltage, $self->{channel});
306             } else {
307 0         0 warn '_set_voltage_auto not implemented for this instrument';
308             };
309             }
310            
311             sub get_voltage {
312 8     8 1 14 my $self=shift;
313 8         12 my $channel=shift;
314 8 100       21 $channel = 1 unless defined($channel);
315 8         29 my $voltage=$self->_get_voltage($channel);
316 8         16 my $tmp="last_voltage_$channel";
317 8         17 $self->{_gp}->{$tmp}=$voltage;
318 8         21 return $voltage;
319             }
320            
321             sub _get_voltage {
322 0     0     my $self=shift;
323            
324 0 0         if ($self->{subsource}) {
325 0           return $self->{multisource}->_get_voltage($self->{channel});
326             } else {
327 0           warn '_get_voltage not implemented for this instrument';
328             };
329             }
330            
331             sub get_range() {
332 0     0 0   my $self=shift;
333 0 0         if ($self->{subsource}) {
334 0           return $self->{multisource}->get_range($self->{channel});
335             } else {
336 0           warn 'get_range not implemented for this instrument';
337             };
338             }
339            
340             sub set_range() {
341 0     0 0   my $self=shift;
342 0           my $range=shift;
343 0 0         if ($self->{subsource}) {
344 0           return $self->{multisource}->set_range($range, $self->{channel});
345             } else {
346 0           warn 'set_range not implemented for this instrument';
347             };
348             }
349            
350             1;
351            
352             =head1 NAME
353            
354             Lab::Instrument::Source - Base class for voltage source instruments
355            
356             =head1 SYNOPSIS
357            
358             =head1 DESCRIPTION
359            
360             This class implements a general voltage source, if necessary with several channels.
361             It is meant to be inherited by instrument classes (virtual instruments) that implement
362             real voltage sources (e.g. the L class).
363            
364             The class provides a unified user interface for those virtual voltage sources
365             to support the exchangeability of instruments.
366            
367             Additionally, this class provides a safety mechanism called C
368             to protect delicate samples. It includes automatic limitations of sweep rates,
369             voltage step sizes, minimal and maximal voltages.
370            
371             The only user application of this class is to define a voltage source object
372             which represents a single channel of a multi-channel voltage source.
373             Otherwise, you will always have to instantiate classes derived from Lab::Instrument::Source.
374            
375             =head1 CONSTRUCTOR
376            
377             $self=new Lab::Instrument::Source($multisource, $channel);
378             $self=new Lab::Instrument::Source($multisource, $channel, \%config);
379            
380             This constructor can be used to create a source object which represents
381             channel C<$channel> of the multi-channel voltage source C<$multisource>.
382             The default configuration of this source is the configuration of C<$multisource>;
383             it can be partially or entirely overridden with an additional C<\%config> hash.
384            
385            
386             $self=new Lab::Instrument::Source(\%default_config,\%config);
387            
388             This constructor will only be used by instrument drivers that inherit this class,
389             not by the user.
390            
391             The instrument driver (e.g. L)
392             has a constructor like this:
393            
394             $knick=new Lab::Instrument::KnickS252({
395             GPIB_board => $board,
396             GPIB_address => $address,
397            
398             gate_protect => $gp,
399             [...]
400             });
401            
402             =head1 METHODS
403            
404             =head2 configure
405            
406             $self->configure(\%config);
407            
408             Supported configure options:
409            
410             =over 2
411            
412             =item fast_set
413            
414             This parameter controls the return value of the set_voltage function and can be set to 0 (off,
415             default) or 1 (on). For fast_set off, set_voltage first requests the hardware to set the voltage,
416             and then reads out the actually set voltage via get_voltage. The resulting number is returned.
417             For fast_set on, set_voltage requests the hardware to set the voltage and returns without double-check
418             the requested value. This, albeit less secure, may speed up measurements a lot.
419            
420             =item gate_protect
421            
422             Whether to use the automatic sweep speed limitation. Can be set to 0 (off) or 1 (on).
423             If it is turned on, the output voltage will not be changed faster than allowed
424             by the C, C and C
425             values. These three parameters overdefine the allowed speed. Only two
426             parameters are necessary. If all three are set, the smallest allowed sweep rate
427             is chosen.
428            
429             Additionally the maximal and minimal output voltages are limited.
430            
431             This mechanism is useful to protect sensible samples that are destroyed by
432             abrupt voltage changes. One example is gate electrodes on semiconductor electronics
433             samples, hence the name.
434            
435             =item gp_max_volt_per_second
436            
437             How much the output voltage is allowed to change per second.
438            
439             =item gp_max_volt_per_step
440            
441             How much the output voltage is allowed to change per step.
442            
443             =item gp_max_step_per_second
444            
445             How many steps are allowed per second.
446            
447             =item gp_min_volt
448            
449             The smallest allowed output voltage.
450            
451             =item gp_max_volt
452            
453             The largest allowed output voltage.
454            
455             =item qp_equal_level
456            
457             Voltages with a difference less than this value are considered equal.
458            
459             =back
460            
461             =head2 set_voltage
462            
463             $new_volt=$self->set_voltage($voltage);
464            
465             Sets the output to C<$voltage> (in Volts). If the configure option C is set
466             to a true value, the safety mechanism takes into account the C,
467             C etc. settings, by employing the C method.
468            
469             Returns for C off the actually set output voltage. This can be different
470             from C<$voltage>, due to the C, C settings. For C on,
471             C returns always C<$voltage>.
472            
473             For a multi-channel device, add the channel number as a parameter:
474            
475             $new_volt=$self->set_voltage($voltage,$channel);
476            
477            
478             =head2 step_to_voltage
479            
480             $new_volt=$self->step_to_voltage($voltage);
481             $new_volt=$self->step_to_voltage($voltage,$channel);
482            
483             Makes one safe step in direction to C<$voltage>. The output voltage is not changed by more
484             than C. Before the voltage is changed, the methods waits if not
485             enough times has passed since the last voltage change. For step voltage and waiting time
486             calculation, the larger of C or C is ignored
487             (see code).
488            
489             Returns the actually set output voltage. This can be different from C<$voltage>, due
490             to the C, C settings.
491            
492             =head2 sweep_to_voltage
493            
494             $new_volt=$self->sweep_to_voltage($voltage);
495             $new_volt=$self->sweep_to_voltage($voltage,$channel);
496            
497             This method sweeps the output voltage to the desired value and only returns then.
498             Uses the L method internally, so all discussions of config options
499             from there apply too.
500            
501             Returns the actually set output voltage. This can be different from C<$voltage>, due
502             to the C, C settings.
503            
504             =head2 get_voltage
505            
506             $new_volt=$self->get_voltage();
507             $new_volt=$self->get_voltage($channel);
508            
509             Returns the voltage currently set.
510            
511             =head1 CAVEATS/BUGS
512            
513             Probably many.
514            
515             =head1 SEE ALSO
516            
517             =over 4
518            
519             =item L
520            
521             Used internally for the sweep timing.
522            
523             =item L
524            
525             This class inherits the gate protection mechanism.
526            
527             =item L
528            
529             This class inherits the gate protection mechanism.
530            
531             =back
532            
533             =head1 AUTHOR/COPYRIGHT
534            
535             This is $Id: Source.pm 650 2010-04-22 19:09:27Z schroeer $
536            
537             Copyright 2004-2008 Daniel Schröer ()
538             2009-2010 Daniel Schröer, Andreas K. Hüttel (L) and Daniela Taubert
539            
540             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
541            
542             =cut