File Coverage

blib/lib/Lab/Instrument/ITC.pm
Criterion Covered Total %
statement 11 126 8.7
branch 0 28 0.0
condition 0 101 0.0
subroutine 4 27 14.8
pod 7 16 43.7
total 22 298 7.3


line stmt bran cond sub pod time code
1             package Lab::Instrument::ITC;
2             #ABSTRACT: Oxford Instruments ITC Intelligent Temperature Control
3             $Lab::Instrument::ITC::VERSION = '3.880';
4 1     1   1943 use v5.20;
  1         4  
5              
6 1     1   6 use strict;
  1         3  
  1         21  
7 1     1   11 use Lab::Instrument;
  1         4  
  1         18  
8 1     1   6 use Lab::MultiChannelInstrument;
  1         6  
  1         2132  
9              
10             our @ISA = ( 'Lab::MultiChannelInstrument', 'Lab::Instrument' );
11              
12             our %fields = (
13             supported_connections => [
14             'VISA', 'VISA_GPIB', 'GPIB', 'VISA_RS232', 'RS232', 'IsoBus', 'DEBUG'
15             ],
16              
17             # default settings for the supported connections
18             connection_settings => {
19             gpib_board => undef,
20             gpib_address => undef,
21             baudrate => 9600,
22             databits => 8,
23             stopbits => 2,
24             parity => 'none',
25             handshake => 'none',
26             termchar => "\r",
27             timeout => 2,
28             },
29              
30             device_settings => {
31             id => 'Oxford ITC',
32             read_default => 'device',
33             channels => {
34             Ch1 => 1,
35             Ch2 => 2,
36             Ch3 => 3
37             },
38             channel_default => 'Ch1',
39             channel => undef
40             },
41              
42             device_cache => {
43             T => undef,
44             proportional => undef,
45             integral => undef,
46             derivative => undef
47              
48             },
49              
50             multichannel_shared_cache =>
51             [ "id", "proportional", "integral", "derivative" ],
52              
53             );
54              
55             sub new {
56 0     0 1   my $proto = shift;
57 0   0       my $class = ref($proto) || $proto;
58 0           my $self = $class->SUPER::new(@_);
59 0           $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  0            
60              
61 0           return $self;
62             }
63              
64             sub _device_init {
65 0     0     my $self = shift;
66              
67 0           $self->_set_control(3); # REMOTE & unlocked
68 0           $self->clear();
69             }
70              
71             sub _get_parameter { # internal only
72              
73             # 0 Demand SET TEMPERATURE
74             # 1 Sensor 1 Temperature
75             # 2 Sensor 2 Temperature
76             # 3 Sensor 3 Temperature
77             # 4 Temperature Error (+ve when SET>Measured)
78             # 5 Heater O/P (as % of current limit)
79             # 6 Heater O/P (as Volts, approx)
80             # 7 Gas Flow O/P (arbitratry units)
81             # 8 Proportional Band
82             # 9 Integral Action Time
83             #10 Derivative Actionb Time
84             #11 Channel 1 Freq/4
85             #12 Channel 2 Freq/4
86             #13 Channel 3 Freq/4
87              
88 0     0     my $self = shift;
89 0           my ( $parameter, $tail ) = $self->_check_args( \@_, ['parameter'] );
90              
91 0 0 0       if ( $parameter != 0
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
92             and $parameter != 1
93             and $parameter != 2
94             and $parameter != 3
95             and $parameter != 4
96             and $parameter != 5
97             and $parameter != 6
98             and $parameter != 7
99             and $parameter != 8
100             and $parameter != 9
101             and $parameter != 10
102             and $parameter != 11
103             and $parameter != 12
104             and $parameter != 13 ) {
105 0           Lab::Exception::CorruptParameter->throw( error =>
106             "unexpected value for MODE in sub read_parameter. Expected values are:\n 0 --> Demand SET TEMPERATURE\n 1 --> Sensor 1 Temperature\n 2 --> Sensor 2 Temperature\n 3 --> Sensor 3 Temperature\n 4 --> Temperature Error (+ve when SET>Measured)\n 5 --> Heater O/P (as % of current limit)\n 6 --> Heater O/P (as Volts, approx)\n 7 --> Gas Flow O/P (arbitratry units)\n 8 --> Proportional Band\n 9 --> Integral Action Time\n10 --> Derivative Actionb Time\n11 --> Channel 1 Freq/4\n12 --> Channel 2 Freq/4\n13 --> Channel 3 Freq/4"
107             );
108             }
109              
110 0           my $cmd = sprintf( "R%d\r", $parameter );
111 0           my $result = $self->query( $cmd, $tail );
112 0           chomp $result;
113 0           $result =~ s/^R//;
114 0           return sprintf( "%e", $result );
115             }
116              
117             sub get_value {
118 0     0 0   my $self = shift;
119 0           return $self->get_T(@_);
120             }
121              
122             sub get_T { # basic
123 0     0 1   my $self = shift;
124 0           my ( $sensor, $tail ) = $self->_check_args( \@_, ['channel'] );
125              
126 0 0 0       if ( $sensor != 1 and $sensor != 2 and $sensor != 3 ) {
      0        
127 0   0       $sensor = $self->{channel} || 1;
128             }
129              
130 0           my $cmd = sprintf( "R%d\r", $sensor );
131              
132 0           my $result = $self->request($cmd);
133 0           chomp $result;
134 0           $result =~ s/^R//;
135 0           return $result;
136              
137             }
138              
139             sub set_T { # basic
140              
141             # Setpoint
142 0     0 1   my $self = shift;
143 0           my ( $value, $tail ) = $self->_check_args( \@_, ['value'] );
144              
145 0 0 0       if ( not defined $value or $value > 200 or $value < 0 ) {
      0        
146 0           Lab::Exception::CorruptParameter->throw( error =>
147             "unexpected value for SETPOINT in sub set_T. Expected values are between 0 .. 200 K"
148             );
149             }
150              
151 0           $value = sprintf( "%.3f", $value );
152 0           $self->query( "T$value\r", $tail );
153             }
154              
155             sub _get_version { # internal only
156 0     0     my $self = shift;
157 0           my ($tail) = $self->_check_args( \@_, [] );
158 0           return $self->query( "V\r", $tail );
159             }
160              
161             sub _get_status { # internal only
162              
163             # Examine Status
164 0     0     my $self = shift;
165 0           my ($tail) = $self->_check_args( \@_, [] );
166              
167 0           my $result = $self->query("X\r");
168              
169 0           $result =~ m/^X([0-9])A([0-9])C([0-9])S([0-9]{1,2})H([0-9])L([0-9])$/;
170              
171 0           $result = {
172             status => $1,
173             auto => $2,
174             control => $3,
175             sweep_status => $4,
176             heater_control => $5,
177             auto_pid => $6
178             };
179              
180 0 0         if ( wantarray() ) {
181 0           return ( $1, $2, $3, $4, $5, $6 );
182             }
183             else {
184 0           return $result;
185             }
186              
187             }
188              
189             sub _set_control
190             { # don't use it if you get an error message during reading out sensors:"Cading Sensor"; # internal only
191              
192             # 0 Local & Locked
193             # 1 Remote & Locked
194             # 2 Local & Unlocked
195             # 3 Remote & Unlocked
196 0     0     my $self = shift;
197 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
198              
199 0 0 0       if ( $mode != 0 and $mode != 1 and $mode != 2 and $mode != 3 ) {
      0        
      0        
200 0           Lab::Exception::CorruptParameter->throw( error =>
201             "unexpected value for MODE in sub _set_control. Expected values are:\n 0 --> Local & Locked\n 1 --> Remote & Locked\n 2 --> Local & Unlocked\n 3 --> Remote & Unlocked"
202             );
203             }
204 0           my $cmd = sprintf( "C%d\r", $mode );
205 0           $self->query( $cmd, $tail );
206              
207             #sleep(1);
208             }
209              
210             #
211             sub _set_communicationsprotocol { # internal only
212              
213             # 0 "Normal" (default)
214             # 2 Sends <LF> after each <CR>
215 0     0     my $self = shift;
216 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
217              
218 0 0 0       if ( $mode != 0 and $mode != 2 ) {
219 0           Lab::Exception::CorruptParameter->throw( error =>
220             "unexpected value for MODE in sub set_comunicationsprotocol. Expected values are:\n 0 --> Normal (default)\n 2 --> Sends <LF> after each <CR>"
221             );
222             }
223              
224 0           $self->write( "Q$mode\r", $tail ); #no aswer from ITC expected
225             }
226              
227             sub set_heatercontrol { # basic
228              
229             # 0 Heater Manual, Gas Manual;
230             # 1 Heater Auto, Gas Manual
231             # 2 Heater Manual, Gas Auto
232             # 3 Heater Auto, Gas Auto
233 0     0 1   my $self = shift;
234 0           my ( $mode, $tail ) = $self->_check_args( \@_, ['mode'] );
235              
236 0 0         if ( $mode =~ /\b(MANUAL|manual|MAN|man)\b/ ) {
    0          
237 0           $self->query( "A0\r", $tail );
238             }
239             elsif ( $mode =~ /\b(AUTOMATIC|automatic|AUTO|auto)\b/ ) {
240 0           $self->query( "A1\r", $tail );
241             }
242             else {
243 0           Lab::Exception::CorruptParameter->throw( error =>
244             "unexpected value for MODE in sub set_heatercontrol. Expected values are:\n 0 --> Heater Manual, Gas Manual\n 1 --> Heater Auto"
245             );
246             }
247              
248             }
249              
250             sub set_proportional { # internal only
251 0     0 0   my $self = shift;
252 0           my ( $value, $tail ) = $self->_check_args( \@_, ['value'] );
253              
254 0           $value = sprintf( "%.3f", $value );
255 0           $self->query( "P$value\r", $tail );
256             }
257              
258             sub get_proportional {
259 0     0 0   my $self = shift;
260 0           my ($tail) = $self->_check_args( \@_, [] );
261              
262 0           return $self->_get_parameter( 8, $tail );
263             }
264              
265             sub set_integral { # internal only
266 0     0 0   my $self = shift;
267 0           my ( $value, $tail ) = $self->_check_args( \@_, ['value'] );
268              
269 0           $value = sprintf( "%.1f", $value );
270 0           $self->query( "I$value\r", $tail );
271             }
272              
273             sub get_integral {
274 0     0 0   my $self = shift;
275 0           my ($tail) = $self->_check_args( \@_, [] );
276              
277 0           return $self->_get_parameter( 9, $tail );
278             }
279              
280             sub set_derivative { # internal only
281 0     0 0   my $self = shift;
282 0           my ( $value, $tail ) = $self->_check_args( \@_, ['value'] );
283              
284 0           $value = sprintf( "%.1f", $value );
285 0           $self->query( "D$value\r", $tail );
286             }
287              
288             sub get_derivative {
289 0     0 0   my $self = shift;
290 0           my ($tail) = $self->_check_args( \@_, [] );
291              
292 0           return $self->_get_parameter( 10, $tail );
293             }
294              
295             sub set_PID { # basic
296 0     0 1   my $self = shift;
297 0           my ( $P, $I, $D ) = $self->_check_args( \@_, [ 'P', 'I', 'D' ] );
298              
299 0 0 0       if ( ( defined $P ) and ( $P eq "auto" or $P eq "AUTO" ) ) {
    0 0        
    0 0        
      0        
      0        
300 0           $self->query("L1\r"); # enable AUTO-PID
301             }
302             elsif ( ( defined $P ) and ( $P eq "man" or $P eq "MAN" ) ) {
303 0           $self->query("L0\r"); # disable AUTO-PID
304             }
305             elsif ( ( not defined $P or not defined $I or not defined $D ) ) {
306 0           Lab::Exception::CorruptParameter->throw( error =>
307             "unexpected values for PID in sub set_PID. Exactly three arguments are required."
308             );
309             }
310             else {
311 0           $self->query("L0\r"); # disable AUTO-PID
312 0           $self->set_proportional($P);
313 0           $self->set_integral($I);
314 0           $self->set_derivative($D);
315             }
316              
317             }
318              
319             sub set_heatersensor { # basic
320              
321             # 1 Sensor 1
322             # 2 Sensor 2
323             # 3 Sensor 3
324 0     0 1   my $self = shift;
325 0           my ( $sensor, $tail ) = $self->_check_args( \@_, ['channel'] );
326              
327 0 0 0       if ( $sensor != 1 and $sensor != 2 and $sensor != 3 ) {
      0        
328 0           Lab::Exception::CorruptParameter->throw( error =>
329             "unexpected value for SENSOR in sub set_heatersensor. Expected values are:\n 1 --> Sensor #1\n 2 --> Sensor #2\n 3 --> Sensor #3"
330             );
331             }
332              
333 0           $self->query( "H$sensor\r", $tail );
334             }
335              
336             sub get_heatersensor {
337              
338 0     0 0   my $self = shift;
339 0           my ($tail) = $self->_check_args( \@_, [] );
340              
341 0           return $self->_get_parameter( 10, $tail );
342             }
343              
344             sub _set_heaterlimit { # internal only
345              
346             # in steps of 0.1 V; MAX = 40V --> 80W at 20 Ohm load
347             # 0 dynamical varying limit
348 0     0     my $self = shift;
349 0           my ( $limit, $tail ) = $self->_check_args( \@_, ['value'] );
350              
351 0 0 0       if ( not defined $limit or $limit > 40 or $limit < 0 ) {
      0        
352 0           Lab::Exception::CorruptParameter->throw( error =>
353             "unexpected value for LIMIT in sub _set_heaterlimit. Expected values are between 0 .. 40 V"
354             );
355             }
356              
357 0           $self->query( "M$limit\r", $tail );
358             }
359              
360             sub set_heateroutput { # basic
361              
362             # from 0 to 99.9 % of HEATERLIMIT.
363 0     0 1   my $self = shift;
364 0           my ( $value, $tail ) = $self->_check_args( \@_, ['value'] );
365              
366 0 0 0       if ( not defined $value or $value > 99.9 or $value < 0 ) {
      0        
367 0           Lab::Exception::CorruptParameter->throw( error =>
368             "unexpected value for OUTPUT in sub set_heateroutput. Expected values are between 0 .. 999. (100 == 10.0% * HEATERLIMIT.)"
369             );
370             }
371              
372 0           $self->query( "O$value\r", $tail );
373             }
374              
375             sub get_heateroutput {
376 0     0 0   my $self = shift;
377 0           my ($tail) = $self->_check_args( \@_, [] );
378              
379 0           return $self->_get_parameter(5);
380             }
381              
382             ####################################################################################################
383             #### TEMPERATURE-SWEEP not implemented yet ######################################################
384             ####################################################################################################
385              
386             #sub itc_sweep {
387             # 0 Stop Sweep
388             # 1 Start Sweep
389             #nn=2P-1 Sweeping to step P
390             #nn=2P Sweeping to step P
391             # my $self=shift;
392             # my $value=shift;
393             # $value=sprintf("%d",$value);
394             # $self->query("S$value\r");
395             #}
396              
397             #sub itc_set_pointer{
398             # Sets Pointer in internal ITC memory
399             # my $self=shift;
400             # my $x=shift;
401             # my $y=shift;
402             # if ($x<0 or $x>128){ printf "x=$x no valid ITC Pointer value\n";die };
403             # if ($y<0 or $y>128){ printf "y=$y no valid ITC Pointer value\n";die };
404             # my $cmd=sprintf("x%d\r",$x);
405             # $self->query($cmd);
406             # $cmd=sprintf("y%d\r",$y);
407             # $self->query($cmd);
408             #}
409              
410             #sub itc_program_sweep_table{
411             # my $self=shift;
412             # my $setpoint=shift; #K Sweep Stop Point
413             # my $sweeptime=shift; #Min. Total Sweep Time
414             # my $holdtime=shift; #sec. Hold Time
415              
416             # if ($setpoint<0. or $setpoint >9.9){printf "Cannot reach setpoint: $setpoint\n";die};
417              
418             # $self->itc_set_pointer(1,1);
419             # $setpoint=sprintf("%1.4f",$setpoint);
420             # $self->query("s$setpoint\r");
421              
422             # $self->itc_set_pointer(1,2);
423             # $sweeptime=sprintf("%.4f",$sweeptime);
424             # $self->query("s$sweeptime\r");
425              
426             # $self->itc_set_pointer(1,3);
427             # $holdtime=sprintf("%.4f",$holdtime);
428             # $self->query("s$holdtime\r");
429              
430             # $self->itc_set_pointer(0,0);
431             #}
432              
433             #sub itc_read_sweep_table {
434             # Clears Sweep Program Table
435             # my $self=shift;
436             # $self->query("r\r");
437             #}
438              
439             #sub itc_clear_sweep_table {
440             # Clears Sweep Program Table
441             # my $self=shift;
442             # $self->query("w\r");
443             #}
444              
445             1;
446              
447             __END__
448              
449             =pod
450              
451             =encoding UTF-8
452              
453             =head1 NAME
454              
455             Lab::Instrument::ITC - Oxford Instruments ITC Intelligent Temperature Control
456              
457             =head1 VERSION
458              
459             version 3.880
460              
461             =head1 SYNOPSIS
462              
463             use Lab::Instrument::ITC;
464             my $irc=new Lab::Instrument::ILM($gpibadaptor,3);
465              
466             .
467              
468             =head1 DESCRIPTION
469              
470             The Lab::Instrument::ITC class implements an interface to the Oxford Instruments
471              
472             =head1 CONSTRUCTOR
473              
474             my $itc=new Lab::Instrument::ITC($isobus,$addr);
475              
476             Instantiates a new ITC object, for example attached to the IsoBus device
477             (of type C<Lab::Instrument::IsoBus>) C<$IsoBus>, with IsoBus address C<$addr>.
478             All constructor forms of C<Lab::Instrument> are available.
479              
480             .
481              
482             =head1 METHODS
483              
484             =head2 get_T
485              
486             $temperature=$itc->get_T(<$sensor>);
487              
488             Returns the value of the selected temperature sensor.
489              
490             =over 4
491              
492             =item $sensor
493              
494             SENSOR is an optional parameter and can be 1, 2 or 3.
495             If not defined DEF = 1.
496              
497             =back
498              
499             .
500              
501             =head2 set_T
502              
503             $temperature=$itc->set_T($temperature);
504              
505             Set target value for the temperature control circuit.
506              
507             =over 4
508              
509             =item $temperature
510              
511             TEMPERATURE can be between 0 ... 200 K.
512              
513             =back
514              
515             .
516              
517             =head2 set_heatercontrol
518              
519             $temperature=$itc->set_heatercontrol($mode);
520              
521             Set HEATER CONTROL to MANUAL or AUTOMATIC.
522              
523             =over 4
524              
525             =item $mode
526              
527             MODE can be MANUAL, MAN, AUTOMATIC or AUTO.
528              
529             =back
530              
531             .
532              
533             =head2 set_PID
534              
535             $temperature=$itc->set_PID($P, $I, $D);
536              
537             Set the PID-values for the temperature control circuit.
538              
539             =over 4
540              
541             =item $P
542              
543             PROPORTIONAL element
544              
545             =item $I
546              
547             INTEGRAL element
548              
549             =item $D
550              
551             DERIVATIVE element
552              
553             =back
554              
555             .
556              
557             =head2 set_heatersensor
558              
559             $temperature=$itc->set_heatersensor($sensor);
560              
561             Select the C<SENSOR> to be used for the temperature control circuit.
562              
563             =over 4
564              
565             =item $sensor
566              
567             SENSOR can be 1, 2 or 3. DEFAULT = 1.
568              
569             =back
570              
571             .
572              
573             =head2 set_heateroutput
574              
575             $temperature=$itc->set_heateroutput($percent);
576              
577             Set the power for the C<HEATER OUTPUT> in percent of full range defined by C<HEATER LIMIT>.
578              
579             =over 4
580              
581             =item $percent
582              
583             PERCENT can be 0 ... 99.9.
584              
585             =back
586              
587             .
588              
589             =head1 CAVEATS/BUGS
590              
591             probably many
592              
593             .
594              
595             =head1 SEE ALSO
596              
597             =over 4
598              
599             =item L<Lab::Instrument>
600              
601             =back
602              
603             =head1 COPYRIGHT AND LICENSE
604              
605             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
606              
607             Copyright 2013 Andreas K. Huettel, Christian Butschkow, Stefan Geissler
608             2014 Christian Butschkow
609             2016 Christian Butschkow, Simon Reinhardt
610             2017 Andreas K. Huettel
611             2020 Andreas K. Huettel
612              
613              
614             This is free software; you can redistribute it and/or modify it under
615             the same terms as the Perl 5 programming language system itself.
616              
617             =cut