File Coverage

blib/lib/Lab/Instrument/IPS.pm
Criterion Covered Total %
statement 14 380 3.6
branch 0 120 0.0
condition 0 144 0.0
subroutine 5 38 13.1
pod 7 24 29.1
total 26 706 3.6


line stmt bran cond sub pod time code
1             # Please note: The Oxford IPS expects a \r after all commands, no matter
2             # which connection is used. For rs232 connection, additionally \n has to be added.
3             # Hence termchar => \n. For Isobus connection, no additional termchar must be added.
4             # Hence IsoEnableTermChar => 0.
5              
6             package Lab::Instrument::IPS;
7             #ABSTRACT: Oxford Instruments IPS Magnet Power Supply
8             $Lab::Instrument::IPS::VERSION = '3.881';
9 1     1   15 use v5.20;
  1         3  
10              
11 1     1   5 use strict;
  1         2  
  1         25  
12 1     1   6 use Time::HiRes qw/usleep/, qw/time/;
  1         3  
  1         6  
13              
14             #use Lab::VISA;
15 1     1   107 use Lab::Instrument;
  1         18  
  1         37  
16 1     1   6 use Carp;
  1         1  
  1         4743  
17              
18             our @ISA = ('Lab::Instrument');
19              
20             my $default_config = {
21             can_reverse => 1,
22             can_use_negative_current => 1,
23             };
24              
25             our %fields = (
26             supported_connections => [
27             'VISA', 'VISA_GPIB', 'GPIB', 'VISA_RS232', 'RS232', 'IsoBus', 'DEBUG'
28             ],
29              
30             # default settings for the supported connections
31             connection_settings => {
32             gpib_board => undef,
33             gpib_address => undef,
34             baudrate => 9600,
35             databits => 8,
36             stopbits => 2,
37             parity => 'none',
38             handshake => 'none',
39             termchar => "\r",
40             IsoEnableTermChar => 0,
41             timeout => 2,
42             },
43              
44             device_settings => {
45             id => 'Oxford IPS',
46             has_switchheater => 0,
47             read_default => 'device'
48             },
49              
50             device_cache => {
51             targetfield => undef,
52             rate => undef,
53             field => undef,
54             persistent_mode => 0
55              
56             }
57              
58             );
59              
60             sub new {
61 0     0 1   my $proto = shift;
62 0   0       my $class = ref($proto) || $proto;
63 0           my $self = $class->SUPER::new(@_);
64 0           $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  0            
65              
66             $self->{LIMITS} = {
67 0           'magneticfield' => 0,
68             'field_intervall_limits' => [ 0, 0, 0, 0 ],
69             'rate_intervall_limits' => [ 0, 0, 0, 0 ]
70             };
71              
72 0           return $self;
73             }
74              
75             sub get_version { # internal only
76             # returns the VERSION of the POWERSUPPLY, e.g. IPS180-20 or IPS120-10
77 0     0 0   my $self = shift;
78 0           my $version = $self->query("V\r");
79              
80 0           return $version;
81             }
82              
83             sub _device_init { # internal only
84 0     0     my $self = shift;
85 0           my $magnet = shift;
86              
87 0           $self->connection()->SetTermChar( chr(13) );
88 0           $self->connection()->EnableTermChar(1);
89 0           $self->{SWEEP_CONFIG_ARMED} = 0;
90 0           $self->_set_control(3);
91              
92 0           my $device_settings = $self->device_settings();
93 0 0         if ( $device_settings->{has_switchheater} ) {
94              
95             #print "Try to switch on the SWITCHHEATER ...";
96 0           $self->set_persistent_mode(0);
97              
98 0           my $switchheater = $self->get_switchheater();
99 0 0 0       if ( $switchheater == 0 || $switchheater == 2 ) {
100 0           Lab::Exception::CorruptParameter->throw( error =>
101             "PSU != Magnet --> SWITCHHEATER cannot be switched on." );
102             }
103              
104             #print "done\n!";
105             }
106              
107             #print "Set Communication Protocol to Extended Resolution...";
108 0           $self->_set_communicationsprotocol(4);
109              
110             #print "done!\n";
111             #print "Set Magnet to Remote and Unlocked...";
112              
113 0           $self->_set_mode(9);
114              
115             #print "done!\n";
116              
117             #print "Clamp Magnet and Set to Hold...";
118 0           $self->abort(0);
119 0           usleep(5e5);
120              
121             #print "done!\n";
122             }
123              
124             # sub _set_limits { # internal only
125             # my $self = shift;
126             # my $magnet = shift;
127              
128             # # set limits
129             # if( $magnet =~ /\b(KRYO1|kryo1)\b/ )
130             # {
131             # %LIMITS = ( 'magneticfield' => 14, 'field_intervall_limits' => [0, 9, 11.5, 13], 'rate_intervall_limits' => [1.98, 0.66, 0.36, 0.18]);
132             # }
133             # elsif( $magnet =~ /\b(KRYO2|kryo2)\b/ )
134             # {
135             # %LIMITS = ( 'magneticfield' => 10, 'field_intervall_limits' => [0, 10], 'rate_intervall_limits' => [1.98, 1.98]);
136             # }
137             # elsif( $magnet =~ /\b(MISCHER|mischer)\b/ )
138             # {
139             # %LIMITS = ( 'magneticfield' => 17, 'field_intervall_limits' => [0, 10.99, 13.73, 16.48], 'rate_intervall_limits' => [0.660, 0.552, 0.276, 0.138]);
140             # }
141             # elsif( $magnet =~ /\b(VECTRO|vector|VECTORMAGNET|vectormagnet|3D|3d)\b/ )
142             # {
143             # %LIMITS = ( 'magneticfield' => 1.01, 'field_intervall_limits' => [0, 1.01], 'rate_intervall_limits' => [0.6, 0.6]);
144             # }
145             # else
146             # {
147             # die "unexpected value for MAGNET in sub _set_limits";
148             # }
149              
150             # }
151              
152             sub set_switchheater { # internal only
153             # 0 Heater Off (close switch)
154             # 1 Heater On if PSU=Magnet (open switch)
155             # (only perform operation
156             # if recorded magnet current==present power supply output current)
157             # 2 Heater On, no Checks (open switch)
158 0     0 0   my $self = shift;
159 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['value'] );
160              
161             warn "Try to use switchheater: No switchheater installed!"
162 0 0         if not $self->{device_settings}->{has_switchheater};
163              
164             #print "Trying to switch switchheater to mode $mode\n";
165              
166 0 0         if ( $mode == 0 ) {
    0          
167 0           my $switchheater = $self->get_switchheater();
168 0   0       while ( !( $switchheater == 0 || $switchheater == 2 ) ) {
169 0           $self->query( "H$mode\r", $tail );
170 0           sleep(1);
171             }
172             }
173             elsif ( $mode == 1 ) {
174 0           while ( not $self->get_switchheater() == 1 ) {
175              
176 0           $self->query( "H$mode\r", $tail );
177 0           sleep(1);
178             }
179             }
180             else {
181 0           print Lab::Exception::Warning->new(
182             "Mode $mode is not allowed for the switchheater. Select 0 (off) or 1 (on)."
183             );
184             }
185 0           sleep(10); # wait for heater to open the switch
186             }
187              
188             sub get_switchheater { # internal only
189 0     0 0   my $self = shift;
190             warn "Try to use switchheater: No switchheater installed!"
191 0 0         if not $self->{device_settings}->{has_switchheater};
192              
193 0           my $result = $self->query( "X\r", @_ );
194 0           $result =~ /X[0-9][0-9]A[0-9]C[0-9]H(.)/;
195 0           return $1;
196             }
197              
198             sub _set_control { # internal only
199              
200             # 0 Local & Locked
201             # 1 Remote & Locked
202             # 2 Local & Unlocked
203             # 3 Remote & Unlocked
204 0     0     my $self = shift;
205 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
206              
207 0           $self->query( "C$mode\r", $tail );
208             }
209              
210             sub _set_mode { # internal only
211              
212             # Display Magnet Sweep
213             # 0 Amps Fast
214             # 1 Tesla Fast
215             # 4 Amps Slow
216             # 5 Tesla Slow
217             # 8 Amps Unaffected
218             # 9 Tesla Unaffected
219 0     0     my $self = shift;
220 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
221              
222 0 0 0       if ( $mode != 0
      0        
      0        
      0        
      0        
223             and $mode != 1
224             and $mode != 4
225             and $mode != 5
226             and $mode != 8
227             and $mode != 9 ) {
228 0           Lab::Exception::CorruptParameter->throw( error =>
229             "unexpected value for MODE in sub _set_mode. Expected values are:\n\n\tDisplay\tMagnet Sweep\n 0\tAmps\tFast\n 1\tTesla\tFast\n 4\tAmps\tSlow\n 5\tTesla\tSlow\n 8\tAmps\tUnaffected\n 9\tTesla\tUnaffected"
230             );
231             }
232              
233 0           $self->query("M$mode\r");
234             }
235              
236             sub _set_communicationsprotocol { # internal only
237              
238             # 0 "Normal" (default)
239             # 2 Sends <LF> after each <CR>
240             # 4 Extended Resolution
241             # 6 Extended Resolution. Sends <LF> after each <CR>.
242 0     0     my $self = shift;
243 0           my $mode = shift;
244              
245 0 0         if ( ref($mode) eq "HASH" ) {
246 0           $mode = $mode->{mode};
247             }
248              
249 0 0 0       if ( $mode != 0 and $mode != 2 and $mode != 4 and $mode != 6 ) {
      0        
      0        
250 0           Lab::Exception::CorruptParameter->throw( error =>
251             "unexpected value for MODE in sub _set_communicationsprotocol. Expected values are:\n\n 0 --> Normal (default)\n 2 --> Sends <LF> after each <CR>\n 4 --> Extended Resolution\n 6 --> Extended Resolution. Sends <LF> after each <CR>."
252             );
253             }
254              
255 0           $self->write("Q$mode\r"); #no aswer from IPS expected
256             }
257              
258             sub hold {
259 0     0 0   my $self = shift;
260              
261 0           $self->_set_activity(0);
262             }
263              
264             sub tosetpoint {
265 0     0 0   my $self = shift;
266              
267 0           $self->_set_activity(1);
268              
269 0           sleep(1) while $self->active();
270             }
271              
272             sub tozero {
273 0     0 0   my $self = shift;
274              
275 0           $self->_set_activity(2);
276              
277 0           while ( $self->active() ) {
278 0           sleep(1);
279             }
280              
281             }
282              
283             sub _set_activity { # internal only
284              
285             # 0 Hold
286             # 1 To Set Point
287             # 2 To Zero
288             # 4 Clamp (clamp the power supply output)
289 0     0     my $self = shift;
290 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
291              
292 0 0 0       if ( not 0 <= $mode && $mode <= 4 ) {
293 0           Lab::Exception::CorruptParameter->throw( error =>
294             "unexpected value for MODE in sub _set_activity. Expected values are:\n\n 0 --> Hold\n 1 --> To Set Point\n 2 --> To Zero\n 4 --> Clamp (clamp the power supply output)"
295             );
296             }
297              
298 0           $self->query("A$mode\r");
299             }
300              
301             sub set_rate {
302 0     0 0   my $self = shift;
303 0           my ($targetrate) = $self->_check_args( \@_, ['value'] );
304              
305 0 0         if ( $targetrate < 0.0001 ) {
306 0           $targetrate = 0.0001;
307             }
308              
309 0           $self->query( sprintf( "T%.5f\r", $targetrate ) );
310              
311             #printf("$self->{ID}: T%.5f\r\n", $targetrate);
312              
313 0           return;
314              
315             }
316              
317             sub get_rate {
318 0     0 0   my $self = shift;
319              
320 0           my ($tail) = $self->_check_args( \@_ );
321              
322 0           return $self->get_parameter( 9, $tail );
323             }
324              
325             sub set_targetfield {
326 0     0 0   my $self = shift;
327 0           my ($targetfield) = $self->_check_args( \@_, ['value'] );
328              
329 0           $self->query( sprintf( "J%.5f\r", $targetfield ) );
330              
331             #printf("$self->{ID}: J%.5f\r\n", $targetfield);
332              
333 0           return;
334              
335             }
336              
337             sub get_targetfield {
338 0     0 0   my $self = shift;
339              
340 0           my ($tail) = $self->_check_args( \@_ );
341              
342 0           return $self->get_parameter( 8, $tail );
343             }
344              
345             sub get_parameter { # advanced
346              
347             # 0 --> Demand current (output current) amp
348             # 1 --> Measured power supply voltage volt
349             # 2 --> Measured magnet current amp
350             # 3 --> -
351             # 4 --> -
352             # 5 --> Set point (target current) amp
353             # 6 --> Current sweep rate amp/min
354             # 7 --> Demand field (output field) tesla
355             # 8 --> Set point (target field) tesla
356             # 9 --> Field sweep rate tesla/minute
357             #10 --> - 14 -
358             #15 --> Software voltage limit volt
359             #16 --> Persistent magnet current amp
360             #17 --> Trip current amp
361             #18 --> Persistent magnet field tesla
362             #19 --> Trip field tesla
363             #20 --> Switch heater current milliamp
364             #21 --> Safe current limit, most negative amp
365             #22 --> Safe current limit, most positive amp
366             #23 --> Lead resistance milliohm
367             #24 --> Magnet inductance henry
368 0     0 0   my $self = shift;
369 0           my ($parameter) = $self->_check_args( \@_, ['param'] );
370              
371 0 0 0       if ( $parameter != 0
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
372             and $parameter != 1
373             and $parameter != 2
374             and $parameter != 3
375             and $parameter != 4
376             and $parameter != 5
377             and $parameter != 6
378             and $parameter != 7
379             and $parameter != 8
380             and $parameter != 9
381             and $parameter != 10
382             and $parameter != 15
383             and $parameter != 16
384             and $parameter != 17
385             and $parameter != 18
386             and $parameter != 19
387             and $parameter != 20
388             and $parameter != 21
389             and $parameter != 22
390             and $parameter != 23
391             and $parameter != 24 ) {
392 0           Lab::Exception::CorruptParameter->throw( error =>
393             "\n 0 --> Demand current (output current) amp\n 1 --> Measured power supply voltage volt\n 2 --> Measured magnet current amp\n 3 --> -\n 4 --> -\n 5 --> Set point (target current) amp\n 6 --> Current sweep rate amp/min\n 7 --> Demand field (output field) tesla\n 8 --> Set point (target field) tesla\n 9 --> Field sweep rate tesla/minute\n10 --> - 14 -\n15 --> Software voltage limit volt\n16 --> Persistent magnet current amp\n17 --> Trip current amp\n18 --> Persistent magnet field tesla\n19 --> Trip field tesla\n20 --> Switch heater current milliamp\n21 --> Safe current limit, most negative amp\n22 --> Safe current limit, most positive amp\n23 --> Lead resistance milliohm\n24 --> Magnet inductance henry"
394             );
395             }
396              
397 0           my $result = $self->query("R$parameter\r");
398 0           $result =~ s/^R//;
399 0           return $result;
400             }
401              
402             sub get_value {
403 0     0 0   my $self = shift;
404 0           my ($tail) = $self->_check_args( \@_ );
405 0           return $self->get_field($tail);
406             }
407              
408             sub get_field { # basic
409             # returns the current value of the magnetic field
410              
411 0     0 1   my $self = shift;
412              
413 0           my ($tail) = $self->_check_args( \@_ );
414              
415 0           my $persistent_mode
416             = $self->get_persistent_mode( { read_mode => 'cache' } );
417              
418 0 0         if ($persistent_mode) {
419 0           return $self->get_persistent_field();
420             }
421              
422 0           my $result = $self->query( "R7\r", $tail );
423 0           $result =~ s/R//g;
424 0           return $result;
425              
426             }
427              
428             sub set_persistent_mode {
429 0     0 0   my $self = shift;
430              
431 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
432              
433 0 0         return 0 if not $self->{device_settings}->{has_switchheater};
434              
435 0           my $switch = $self->get_switchheater();
436              
437             #print "We are in mode $current_mode \n";
438              
439 0 0 0       if ( $mode == 1 ) {
    0 0        
    0          
440              
441 0           $self->hold();
442 0           $self->set_switchheater(0);
443              
444 0           $self->tozero();
445              
446             #$current_mode = 1;
447              
448             }
449             elsif ( $mode == 0 and $switch == 2 ) {
450              
451 0           my $setpoint = $self->get_persistent_field();
452              
453 0           $self->set_targetfield($setpoint);
454              
455 0           $self->tosetpoint();
456              
457             #print "Try to start switchheater...\n";
458 0           $self->set_switchheater(1);
459              
460             #print "Switchheater has status ".$self->get_switchheater();
461              
462             }
463             elsif ( $mode == 0 and $switch == 0 ) {
464 0           print "Zero magnetic field. Switch on switchheater.\n";
465 0           $self->set_switchheater(1);
466              
467             }
468              
469             }
470              
471             sub get_persistent_mode {
472 0     0 0   my $self = shift;
473 0           my ($tail) = $self->_check_args( \@_ );
474              
475 0 0         return 0 if not $self->{device_settings}->{has_switchheater};
476              
477 0           my $sh = $self->get_switchheater();
478 0           my $field = $self->get_field( { read_mode => 'cache' } );
479              
480             # Are we really in persistent mode?
481              
482 0 0 0       if ( $field == 0 && $sh == 2 ) {
483 0           return 1;
484             }
485             else {
486 0           return 0;
487             }
488              
489             }
490              
491             sub get_persistent_field {
492 0     0 0   my $self = shift;
493 0           my ($tail) = $self->_check_args( \@_ );
494              
495             # Are we really in persistent mode?
496              
497 0           return $self->get_parameter(18);
498              
499             }
500              
501             sub wait { # basic
502              
503             # waits during magnet is sweeping
504 0     0 1   my $self = shift;
505              
506 0           my $flag = 1;
507 0           local $| = 1;
508              
509 0           while (1) {
510              
511 0           my $current_field = $self->get_field();
512 0 0 0       if ( $flag <= 1.1 and $flag >= 0.9 ) {
    0          
513 0           print "\t\t\t\t\t\t\t\t\t\r";
514 0           print $self->get_id() . " is sweeping ($current_field )\r";
515 0           usleep(5e5);
516             }
517             elsif ( $flag <= 0 ) {
518 0           print "\t\t\t\t\t\t\t\t\t\r";
519 0           print $self->get_id() . " is ($current_field ) \r";
520 0           $flag = 2;
521             }
522 0           $flag -= 0.5;
523 0 0         if ( not $self->active() ) {
524 0           print "\t\t\t\t\t\t\t\t\t\r";
525 0           $| = 0;
526 0           last;
527             }
528             }
529 0           return 0;
530              
531             }
532              
533             sub active { # basic
534             # returns a value > 0 if MAGNET is SWEEPING. Else MAGNET is not sweeping.
535 0     0 1   my $self = shift;
536              
537 0           $self->_check_magnet();
538              
539 0           my $status = $self->query("X\r");
540              
541             #print "status is $status\n";
542 0           my $sweepstatus
543             = substr( $status, 11, 1 ); # MAGNET is SWEEPING if $sweepstatus > 0
544              
545 0 0 0       if ( !$sweepstatus and ( @{ $self->{SWEEP_QUEUE} }[1] ) ) {
  0 0          
546 0           shift( @{ $self->{SWEEP_QUEUE} } );
  0            
547 0           $self->trg();
548 0           $sweepstatus = 1;
549             }
550             elsif ( !$sweepstatus ) {
551 0           $self->query("A0\r")
552             ; #Set Magnet-status to hold when no more sweeps in queue.
553             }
554              
555             #print "Sweepstatus is $sweepstatus\n";
556 0           return $sweepstatus;
557              
558             }
559              
560             sub _check_limits { # for internal use only
561 0     0     my $self = shift;
562 0           my $current_field = shift;
563 0           my $target_field = shift;
564 0           my $rate = shift;
565              
566             #print "CF=$current_field\n TF = $target_field\n RATE = $rate\n";
567              
568             # check field limits:
569 0 0 0       if ( not defined $target_field
      0        
570             or abs($target_field) > $self->{LIMITS}{'magneticfield'}
571             or not $target_field =~ /\b\d+(e\d+|E\d+|exp\d+|EXP\d+)?\b/ ) {
572             return
573 0           "unexpected value for FIELD in sub ips_set_target_field. Expected values are between -/+ $self->{LIMITS}->{'magneticfield'} (Tesla)";
574             }
575              
576             # check limits for sweeprate
577 0           my $max_rate;
578             my $check_field;
579 0 0         if ( abs($target_field) > abs($current_field) ) {
580 0           $check_field = abs($target_field);
581             }
582 0           else { $check_field = abs($current_field); }
583              
584 0           my @len = @{ $self->{LIMITS}->{'field_intervall_limits'} };
  0            
585 0           my $len = @len;
586 0           $max_rate = 0;
587 0           for ( my $i = $len; $i < 0; $i-- ) {
588 0 0         if ( $check_field > $self->{LIMITS}->{'field_intervall_limits'}[$i] )
589             {
590 0           print "maxrate = $i\n";
591 0           $max_rate = $i;
592             }
593             }
594              
595 0 0 0       if ( not defined $rate
      0        
      0        
596             or $rate < 0
597             or $rate > $self->{LIMITS}->{'rate_intervall_limits'}[$max_rate]
598             or not $rate =~ /\b\d+(e\d+|E\d+|exp\d+|EXP\d+)?\b/ ) {
599             return
600 0           "unexpected value for RATE ($rate) in sub config_sweep. Look up individual limits for the sweeping rates for different fieldranges.";
601             }
602              
603 0           return 0;
604              
605             }
606              
607       0     sub _calculate_trace {
608              
609             }
610              
611             sub _check_magnet { # for internal use only
612 0     0     my $self = shift;
613              
614             # get current field
615 0           my $current_field = $self->get_field();
616              
617 0 0         if ( @{ $self->{SWEEP_QUEUE} }[0] != 0 ) {
  0            
618             my $sweepdirection
619 0 0         = ( $current_field >= @{ $self->{SWEEP_QUEUE}[0][0] }[-1] )
  0            
620             ? -1
621             : 1;
622              
623 0 0         if ( ( @{ $self->{SWEEP_QUEUE}[0][0] }[0] - $current_field )
  0            
624             * $sweepdirection < 0 ) {
625 0           shift( @{ $self->{SWEEP_QUEUE}[0][1] } );
  0            
626 0           shift( @{ $self->{SWEEP_QUEUE}[0][0] } );
  0            
627              
628 0           $self->set_rate( @{ $self->{SWEEP_QUEUE}[0][1] }[0] );
  0            
629             print $self->{ID}
630             . ": Changed rate to "
631 0           . ( @{ $self->{SWEEP_QUEUE}[0][1] }[0] )
  0            
632             . " T/min at "
633             . $current_field . " T\n";
634             }
635             }
636              
637             }
638              
639             sub trg { # basic
640             # start configurated sweep
641 0     0 1   my $self = shift;
642 0 0         if ( @{ $self->{SWEEP_QUEUE} }[0] == 0 ) {
  0            
643 0           print "\nIPS: Sweep is not configured. Can't start sweeping.\n";
644             }
645             else {
646 0           $self->set_targetfield( @{ $self->{SWEEP_QUEUE}[0][0] }[-1] );
  0            
647 0           $self->set_rate( @{ $self->{SWEEP_QUEUE}[0][1] }[0] );
  0            
648              
649 0           $self->query("A1\r"); # go to setpoint
650 0           my $current_field = $self->get_field();
651             print $self->{ID}
652             . ": New target-field set "
653 0           . ( @{ $self->{SWEEP_QUEUE}[0][0] }[-1] )
654             . " with rate "
655 0           . ( @{ $self->{SWEEP_QUEUE}[0][1] }[0] )
  0            
656             . "T/min at "
657             . $current_field . " T\n";
658              
659             }
660              
661             }
662              
663             sub abort { # basic
664             # stop sweep
665 0     0 1   my $self = shift;
666 0           $self->query("A0\r");
667 0           $self->{SWEEP_CONFIG_ARMED} = 0;
668             }
669              
670             sub _prepare_sweep_sequence {
671 0     0     my $self = shift;
672 0           my $sweep_points = shift;
673 0           my $sweep_rates = shift;
674              
675 0 0         if ( ref($sweep_points) eq "HASH" ) {
676 0           my $parameters = $sweep_points;
677 0           $sweep_points = $parameters->{points};
678 0           $sweep_rates = $parameters->{rates};
679             }
680              
681 0           my @sweep_points = @$sweep_points;
682 0           my @sweep_rates = @$sweep_rates;
683              
684 0           print "prepare_sweep_sequence\n";
685 0           my $j = 0;
686 0           my $len = @sweep_points;
687 0 0         if ( $len > 2 ) {
688 0           @{ $self->{SWEEP_QUEUE} }[$j] = ( [], [] );
  0            
689 0           for ( my $i = 1; $i < $len - 1; $i++ ) {
690              
691 0 0         my $sign_1
692             = ( ( @sweep_points[$i] - @sweep_points[ $i - 1 ] ) >= 0 )
693             ? -1
694             : 1;
695 0 0         my $sign_2
696             = ( ( @sweep_points[ $i + 1 ] - @sweep_points[$i] ) >= 0 )
697             ? -1
698             : 1;
699 0 0         if ( $sign_1 == $sign_2 ) {
700 0           push( @{ $self->{SWEEP_QUEUE}[$j][0] }, @sweep_points[$i] );
  0            
701             push(
702 0           @{ $self->{SWEEP_QUEUE}[$j][1] },
  0            
703             @sweep_rates[ $i - 1 ]
704             );
705             }
706             else {
707 0           push( @{ $self->{SWEEP_QUEUE}[$j][0] }, @sweep_points[$i] );
  0            
708             push(
709 0           @{ $self->{SWEEP_QUEUE}[$j][1] },
  0            
710             @sweep_rates[ $i - 1 ]
711             );
712 0           $j++;
713 0           @{ $self->{SWEEP_QUEUE} }[$j] = ( [], [] );
  0            
714             }
715             }
716              
717             # take care of the last sweep_point ...
718 0           push( @{ $self->{SWEEP_QUEUE}[$j][0] }, @sweep_points[-1] );
  0            
719 0           push( @{ $self->{SWEEP_QUEUE}[$j][1] }, @sweep_rates[-1] );
  0            
720              
721             }
722             else {
723 0           shift(@sweep_points);
724 0           @{ $self->{SWEEP_QUEUE}[0][0] } = @sweep_points;
  0            
725 0           @{ $self->{SWEEP_QUEUE}[0][1] } = @sweep_rates;
  0            
726             }
727              
728 0           $len = @{ $self->{SWEEP_QUEUE} };
  0            
729 0           for ( my $i = 0; $i < $len; $i++ ) {
730 0           print("Sequence Sweep $i: \n");
731 0           print("SP:\t");
732              
733 0           foreach my $item ( @{ $self->{SWEEP_QUEUE}[$i][0] } ) {
  0            
734 0           print $item. "\t";
735             }
736 0           print("\n");
737 0           print("SR:\t");
738              
739 0           foreach my $item ( @{ $self->{SWEEP_QUEUE}[$i][1] } ) {
  0            
740 0           print $item. "\t";
741             }
742 0           print("\n");
743             }
744              
745             }
746              
747             sub config_sweep { # basic
748 0     0 1   my $self = shift;
749              
750 0           my ( $field, $rate, $interval )
751             = $self->_check_args( \@_, [ 'points', 'rates', 'interval' ] );
752              
753 0           my @sweep_points;
754             my @sweep_rates;
755              
756 0           print "$self->{ID}: config_sweep\n";
757              
758 0 0         if ( not defined $interval ) {
759 0           $interval = 1;
760             }
761              
762 0 0         if ( not defined $rate ) {
763 0           Lab::Exception::CorruptParameter->throw( error =>
764             "too view parameters given in sub config_sweep. Expected parameters are FIELD, RATE, <INTERVAL>."
765             );
766             }
767              
768 0 0         if ( ref($field) eq "ARRAY" ) {
769              
770 0           @sweep_points = @$field;
771              
772 0 0         if ( ref($rate) eq "ARRAY" ) {
773 0           @sweep_rates = @$rate;
774             }
775             }
776              
777             # split and check $field and $rate
778             else {
779 0           @sweep_points = split( ',', $field );
780 0           @sweep_rates = split( ',', $rate );
781             }
782              
783             # rounding of the received values.
784 0           my $len = @sweep_points;
785 0           for ( my $i; $i < $len; $i++ ) {
786 0           @sweep_points[$i] = sprintf( "%.5f", @sweep_points[$i] );
787 0           @sweep_rates[$i] = sprintf( "%.5f", @sweep_rates[$i] );
788 0 0         if ( @sweep_rates[$i] == 0 ) {
789 0           @sweep_rates[$i] = 0.01;
790             }
791             }
792              
793 0 0         if ( ( my $i = @sweep_points ) != ( my $j = @sweep_rates ) ) {
794 0           Lab::Exception::CorruptParameter->throw( error =>
795             "Sweep-points-list and Sweep-rates-list must have the same length!.\n"
796             );
797             }
798              
799             # get current field
800 0           my $current_field = $self->get_field();
801 0           unshift( @sweep_points, $current_field );
802              
803             # check sequence
804 0           my $sequences = @sweep_rates;
805 0           for my $i ( 0 .. $sequences - 1 ) {
806 0 0         if (
807             my $status = $self->_check_limits(
808             @sweep_points[$i], @sweep_points[ $i + 1 ],
809             @sweep_rates[$i]
810             )
811             ) {
812 0           Lab::Exception::CorruptParameter->throw( error => $status );
813             }
814             }
815              
816 0           $self->_prepare_sweep_sequence( \@sweep_points, \@sweep_rates );
817              
818             # calculate trace
819 0           my @trace;
820 0           foreach my $item ( @{ $self->{SWEEP_QUEUE} } ) {
  0            
821 0           my @item = @$item;
822 0           my @points = @{ $item[0] };
  0            
823 0           my @rates = @{ $item[1] };
  0            
824 0 0         my $sweepdirection = ( $points[-1] - $current_field ) >= 0 ? 1 : -1;
825 0           my $len_points = @points;
826 0           while ($len_points) {
827 0           for (
828             $current_field;
829             ( $points[0] - $current_field ) * $sweepdirection > 0;
830             $current_field
831             += ( $rates[0] / 60 ) * $interval * $sweepdirection
832             ) {
833 0           push( @trace, $current_field );
834             }
835 0           $current_field -= $rates[0] * 60 * $interval * $sweepdirection;
836 0           shift(@points);
837 0           shift(@rates);
838 0           $len_points = @points;
839             }
840             }
841 0 0         if ( ( my $len_trace = @trace ) == 0 ) {
842 0           push( @trace, $current_field );
843             }
844              
845 0           return @trace;
846              
847             }
848              
849             sub sweep_to_field {
850 0     0 0   my $self = shift;
851              
852 0           my ( $target, $rate ) = $self->_check_args( \@_, [ 'target', 'rate' ] );
853              
854 0           my $current_field = $self->get_value();
855              
856 0           my $current_field_interval = 0;
857 0           my $target_field_interval = 0;
858              
859 0           my @targets;
860             my @rates;
861              
862 0 0         if ( abs($target) > $self->{LIMITS}->{magneticfield} ) {
863 0           croak("Target-Field exceeds maximum field value! \n");
864             }
865 0 0         if ( not defined $rate ) {
866 0           $rate = @{ $self->{LIMITS}->{rate_intervall_limits} }[0];
  0            
867             }
868 0 0         my $sweep_direction = ( $current_field < $target ) ? 1 : -1;
869              
870 0           foreach
871 0           my $field_limit ( @{ $self->{LIMITS}->{field_intervall_limits} } ) {
872 0 0 0       if ( abs($current_field) > $field_limit and $field_limit != 0 ) {
873 0           $current_field_interval++;
874             }
875 0 0 0       if ( abs($target) > $field_limit and $field_limit != 0 ) {
876 0           $target_field_interval++;
877             }
878             }
879              
880             $current_field_interval
881 0 0         = ( $current_field < 0 )
882             ? $current_field_interval * (-1)
883             : $current_field_interval;
884 0 0         $target_field_interval
885             = ( $target < 0 )
886             ? $target_field_interval * (-1)
887             : $target_field_interval;
888              
889             # add interval limits:
890 0           my $interval = $current_field_interval;
891              
892 0           while (1) {
893             my $vergleichswert
894             = ( ( $interval >= 0 ) ? 1 : -1 )
895 0 0         * @{ $self->{LIMITS}->{field_intervall_limits} }[
  0 0          
896             ( $interval * $sweep_direction >= 0 )
897             ? abs($interval) + 1
898             : abs($interval)
899             ];
900 0 0         if ( $interval == 0 ) {
901 0           $vergleichswert *= $sweep_direction;
902             }
903              
904 0           my $index = abs($interval);
905              
906 0 0 0       if (
      0        
907             $target * $sweep_direction <= $vergleichswert * $sweep_direction
908             or (
909             abs($interval)
910 0           >= @{ $self->{LIMITS}->{field_intervall_limits} } - 1
911             and $interval != $current_field_interval )
912             ) {
913 0           push( @targets, $target );
914              
915 0 0 0       if ( $rate > @{ $self->{LIMITS}->{rate_intervall_limits} }[$index]
  0            
916             or not defined $rate ) {
917             push(
918             @rates,
919 0           @{ $self->{LIMITS}->{rate_intervall_limits} }[$index]
  0            
920             );
921             }
922             else {
923 0           push( @rates, $rate );
924             }
925              
926 0           last;
927              
928             }
929              
930 0 0         if ( $vergleichswert != $current_field ) {
931 0           push( @targets, $vergleichswert );
932 0 0 0       if ( $rate > @{ $self->{LIMITS}->{rate_intervall_limits} }
  0            
933             [ abs($interval) ]
934             or not defined $rate ) {
935             push(
936             @rates,
937 0           @{ $self->{LIMITS}->{rate_intervall_limits} }
  0            
938             [ abs($interval) ]
939             );
940             }
941             else {
942 0           push( @rates, $rate );
943             }
944             }
945              
946 0           $interval += $sweep_direction;
947             }
948              
949 0           $self->config_sweep( \@targets, \@rates );
950 0           $self->trg();
951 0           $self->wait();
952              
953             }
954              
955             sub sweep_to_level {
956              
957 0     0 0   my $self = shift;
958 0           return $self->sweep_to_field(@_);
959             }
960              
961             1;
962              
963             __END__
964              
965             =pod
966              
967             =encoding UTF-8
968              
969             =head1 NAME
970              
971             Lab::Instrument::IPS - Oxford Instruments IPS Magnet Power Supply
972              
973             =head1 VERSION
974              
975             version 3.881
976              
977             =head1 SYNOPSIS
978              
979             use Lab::Instrument::IPS;
980             my $ips=new Lab::Instrument::IPS($isobus,2);
981             print $ips->get_field();
982              
983             .
984              
985             =head1 DESCRIPTION
986              
987             The Lab::Instrument::IPS class implements an interface to the Oxford Instruments
988             IPS magnet power supply.
989              
990             .
991              
992             =head1 CONSTRUCTOR
993              
994             my $ips=new Lab::Instrument::IPS($isobus,$addr);
995              
996             Instantiates a new IPS object, for example attached to the IsoBus device
997             (of type Lab::Instrument::IsoBus ) $IsoBus , with IsoBus address $addr .
998             All constructor forms of Lab::Instrument are available.
999              
1000             .
1001              
1002             =head1 METHODS
1003              
1004             =head2 get_field
1005              
1006             $field=$ips->get_field();
1007              
1008             reads out the current applied magnetic field.
1009              
1010             .
1011              
1012             =head2 config_sweep
1013              
1014             @sweep = $ips->config_sweep($targetfield, $rate, <$interval>)
1015              
1016             Predefine the target value, the sweeprate and optional the measurement interval for a magnetic field sweep.
1017             Returns the calculated sweep TRACE in steps of $rate*$interval as an array.
1018              
1019             =over 4
1020              
1021             =item $targetfield
1022              
1023             TARGETFIELD is the target magnetic tield value to sweep to. It must be within the magnet's limits.
1024              
1025             =item $rate
1026              
1027             RATE is the sweep rate in TESLA per MINUTE . It must be within the magnet's limits.
1028              
1029             =item $interval
1030              
1031             INTERVAL defines the planed measurement interval in seconds. This parameter is necessary to calculate the TRACE correctly.
1032             Default is 1 second.
1033              
1034             =item ADVANCED SWEEP
1035              
1036             $targetfield and $rate can also be a series of values to define a multiple step sweep.
1037             Note: The multiple step sweep cannot reverse sweeping direction.
1038              
1039             Example:
1040             starting at 0 tesla: $targetfield = "1.0, 1.5, 3" and $rate = "0.1, 0.5, 1"
1041              
1042             --> this defines a sweep from 0T --> 3T with sweeprates of
1043             0.1T/m for 0T->1T,
1044             0.5T/m for 1T->1.5T and
1045             1T/m for 1.5T->3T.
1046              
1047             Important: This kinde of 'advanced' sweep works only in combination with the subroutines wait() or active().
1048              
1049             --> wait() will simply wait until the sweep has been finished.
1050             --> active() can be used as the condition-parameter within a 'while-loop'.
1051              
1052             Example:
1053              
1054             $ips->config_sweep("1.0, 1.5, 3", "0.1, 0.5, 1"); # define an advanced sweep
1055             $ips->trg(); # start sweep
1056             while($ips->active())
1057             {
1058             # do something while the sweep is running
1059             }
1060              
1061             =back
1062              
1063             .
1064              
1065             =head2 trg
1066              
1067             $ips->trg();
1068              
1069             starts a configured sweep.
1070              
1071             .
1072              
1073             =head2 abort
1074              
1075             $ips->abort();
1076              
1077             aborts the current sweep.
1078              
1079             .
1080              
1081             =head2 wait
1082              
1083             $ips->wait(<$seconds>);
1084              
1085             Waits ...
1086              
1087             =over 4
1088              
1089             =item $seconds
1090              
1091             SECONDS is an optional paramter.
1092             Wait until $seconds have been passed or if $seconds is not defined until the current sweep has been finished.
1093              
1094             =back
1095              
1096             =head2 active
1097              
1098             $ips->active();
1099              
1100             Returns a value > 0 if magnet is currently sweeping and '0' if magnet is not sweeping.
1101              
1102             =head1 CAVEATS/BUGS
1103              
1104             probably many
1105              
1106             .
1107              
1108             =head1 SEE ALSO
1109              
1110             =over 4
1111              
1112             =item L<Lab::Instrument>
1113              
1114             =back
1115              
1116             =head1 COPYRIGHT AND LICENSE
1117              
1118             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
1119              
1120             Copyright 2012 Christian Butschkow, Stefan Geissler
1121             2013 Alois Dirnaichner, Andreas K. Huettel, Christian Butschkow, Stefan Geissler
1122             2014-2015 Christian Butschkow
1123             2016 Simon Reinhardt
1124             2017 Andreas K. Huettel
1125             2020 Andreas K. Huettel
1126              
1127              
1128             This is free software; you can redistribute it and/or modify it under
1129             the same terms as the Perl 5 programming language system itself.
1130              
1131             =cut