File Coverage

blib/lib/Lab/Instrument/OI_ITC503.pm
Criterion Covered Total %
statement 11 182 6.0
branch 0 32 0.0
condition 0 27 0.0
subroutine 4 31 12.9
pod 2 26 7.6
total 17 298 5.7


line stmt bran cond sub pod time code
1             package Lab::Instrument::OI_ITC503;
2             #ABSTRACT: Oxford Instruments ITC503 Intelligent Temperature Control
3             $Lab::Instrument::OI_ITC503::VERSION = '3.880';
4 1     1   1799 use v5.20;
  1         4  
5              
6 1     1   5 use strict;
  1         4  
  1         28  
7 1     1   5 use feature "switch";
  1         2  
  1         63  
8 1     1   9 use Lab::Instrument;
  1         7  
  1         2524  
9              
10             our @ISA = ("Lab::Instrument");
11              
12             our %fields = (
13             auto_pid => 1,
14             supported_connections => [ 'IsoBus', 'LinuxGPIB', 'VISA_GPIB' ],
15              
16             connection_settings => {},
17             device_settings => {
18             t_sensor => 3,
19             },
20             );
21              
22             sub new {
23 0     0 1   my $proto = shift;
24 0   0       my $class = ref($proto) || $proto;
25 0           my $self = $class->SUPER::new(@_);
26 0           $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  0            
27 0           printf "The ITC driver is work in progress. You have been warned.\n";
28 0           $self->device_settings()->{t_sensor} = 3;
29              
30 0 0         if ( $self->auto_pid() ) {
31 0           $self->itc_set_PID_auto(1);
32             }
33              
34 0           return $self;
35             }
36              
37             sub _device_init {
38 0     0     my $self = shift;
39              
40             # Dont clear the instrument since that may make it unresponsive.
41             # Instead, set the communication protocol to "Normal", which should
42             # also clear all communication buffers.
43 0           $self->connection()->SetTermChar( chr(13) );
44 0           $self->connection()->EnableTermChar(1);
45 0           $self->write("Q0\r");
46              
47 0           $self->set_control(3)
48             ; # Enable remote control, but leave the front panel unlocked
49             }
50              
51             #
52             # evaluate a command response for error conditions (leading '?', check for correct command character if supplied)
53             #
54             sub parse_error {
55 0     0 0   my $self = shift;
56 0           my $device_msg = shift;
57 0           my $cmd = shift;
58 0 0         my $cmd_char = defined $cmd ? substr( $cmd, 0, 1 ) : undef;
59              
60 0           my $status_char = substr( $device_msg, 0, 1 );
61 0 0 0       if ( $status_char eq '?' ) {
    0          
62 0           Lab::Exception::DeviceError->throw(
63             error =>
64             "ITC503 returned error '$device_msg' on command '$cmd'\n",
65             device_class => ref $self,
66             command => $cmd,
67             raw_message => $device_msg
68             );
69             }
70             elsif ( defined $cmd_char && $status_char ne $cmd_char ) {
71 0           Lab::Exception::DeviceError->throw(
72             error =>
73             "Received an unexpected answer from ITC503. Expected '$cmd_char' prefix, received '$status_char' on command '$cmd'\n",
74             device_class => ref $self,
75             command => $cmd,
76             raw_message => $device_msg
77             );
78             }
79             }
80              
81             #
82             # query wrapper with error checking for the ITC
83             #
84             sub query {
85 0     0 1   my $self = shift;
86 0           my $cmd = shift;
87              
88             # ITC query answers always start with the command character if successful with a question mark and the command char on failure
89 0           my $cmd_char = substr( $cmd, 0, 1 );
90              
91 0           my $result = $self->SUPER::query( $cmd, @_ );
92              
93 0           $self->parse_error( $result, $cmd );
94 0           chomp $result;
95              
96 0           return substr( $result, 1 );
97             }
98              
99             # old remark, relevance?
100             # don't use it if you get an error message during reading out sensors:"Cading Sensor"
101             # device modes:
102             # 0 Local & Locked front panel
103             # 1 Remote & Locked panel
104             # 2 Local & Unlocked panel
105             # 3 Remote & Unlocked panel
106             sub set_control {
107 0     0 0   my $self = shift;
108 0           my $mode = shift;
109              
110 0 0         $mode =~ /^\s*(0|1|2|3)\s*$/ ? $mode
    0          
    0          
111             = $1
112             : $mode =~ /^\s*(locked)\s*$/ ? $mode
113             = 1
114             : $mode =~ /^\s*(unlocked)\s*$/ ? $mode
115             = 3
116             : Lab::Exception::CorruptParameter->throw(
117             "Invalid control mode specified.");
118              
119 0           my $result = $self->query( "C${mode}\r", @_ );
120 0           sleep(1);
121              
122             }
123              
124             sub itc_set_communications_protocol {
125              
126             # 0 "Normal" (default)
127             # 2 Sends <LF> after each <CR>
128 0     0 0   my $self = shift;
129 0           my $mode = shift;
130 0           $self->write("Q$mode\r");
131             }
132              
133             sub set_T {
134 0     0 0   my $self = shift;
135 0           my $temp = shift;
136 0           my $t_sensor = $self->device_settings()->{t_sensor};
137 0           $DB::single = 1;
138              
139 0 0 0       if ( $temp < 1.5 && $t_sensor == 3 ) {
    0 0        
140 0           $t_sensor = 2;
141             }
142             elsif ( $temp >= 1.5 && $t_sensor == 2 ) {
143 0           $t_sensor = 3;
144             }
145              
146 0           $self->itc_set_heater_auto(0);
147 0           $self->itc_set_heater_sensor($t_sensor);
148 0           $self->itc_set_heater_auto(1);
149 0           $self->itc_T_set_point($temp);
150              
151 0           printf "Set temperature $temp with sensor $t_sensor.\n";
152 0           $self->device_settings()->{t_sensor} = $t_sensor;
153              
154             }
155              
156             sub get_value {
157 0     0 0   my $self = shift;
158 0           my $t_sensor = $self->device_settings()->{t_sensor};
159              
160 0           my $temp = $self->itc_read_parameter($t_sensor);
161 0           $temp = $self->itc_read_parameter($t_sensor);
162 0           $temp = $self->itc_read_parameter($t_sensor);
163              
164 0 0 0       if ( $temp < 1.5 && $t_sensor == 3 ) {
    0 0        
165 0           $t_sensor = 2;
166 0           $temp = $self->itc_read_parameter($t_sensor);
167 0           $temp = $self->itc_read_parameter($t_sensor);
168 0           $temp = $self->itc_read_parameter($t_sensor);
169 0           printf "Switching to sensor $t_sensor at temperature $temp\n";
170             }
171             elsif ( $temp >= 1.5 && $t_sensor == 2 ) {
172 0           $t_sensor = 3;
173 0           $temp = $self->itc_read_parameter($t_sensor);
174 0           $temp = $self->itc_read_parameter($t_sensor);
175 0           $temp = $self->itc_read_parameter($t_sensor);
176 0           printf "Switching to sensor $t_sensor at temperature $temp\n";
177             }
178              
179 0           printf "Read temperature $temp with sensor $t_sensor.\n";
180              
181 0           $self->device_settings()->{t_sensor} = $t_sensor;
182              
183 0           return $temp;
184              
185             }
186              
187             sub itc_read_parameter {
188              
189             # 0 Demand SET TEMPERATURE K
190             # 1 Sensor 1 Temperature K
191             # 2 Sensor 2 Temperature K
192             # 3 Sensor 3 Temperature K
193             # 4 Temperature Error (+ve when SET>Measured)
194             # 5 Heater O/P (as % of current limit)
195             # 6 Heater O/P (as Volts, approx)
196             # 7 Gas Flow O/P (arbitratry units)
197             # 8 Proportional Band
198             # 9 Integral Action Time
199             #10 Derivative Actionb Time
200             #11 Channel 1 Freq/4
201             #12 Channel 2 Freq/4
202             #13 Channel 3 Freq/4
203              
204 0     0 0   my $self = shift;
205 0           my $parameter = shift;
206 0           my $cmd = "R$parameter\r";
207 0           my $result = $self->query( $cmd, @_ );
208              
209 0           return sprintf( "%e", $result );
210             }
211              
212             sub itc_set_wait {
213              
214             # delay before each character is sent
215             # in millisecond
216 0     0 0   my $self = shift;
217 0           my $wait = shift;
218 0           $wait = sprintf( "%d", $wait );
219 0           $self->query("W$wait\r");
220             }
221              
222             sub itc_examine {
223              
224             # Examine Status
225 0     0 0   my $self = shift;
226 0           $self->query("X\r");
227             }
228              
229             sub set_heatercontrol {
230 0     0 0   my $self = shift;
231 0           my $mode = shift;
232              
233 0 0         if ( $mode == 'MAN' ) {
    0          
234 0           $self->itc_set_heater_auto(0);
235             }
236             elsif ( $mode == 'AUTO' ) {
237 0           $self->itc_set_heater_auto(1);
238             }
239             else {
240 0           printf "set_heatercontrol received an invalid parameter: $mode";
241             }
242              
243             }
244              
245             sub itc_set_heater_auto {
246              
247             # 0 Heater Manual, Gas Manual;
248             # 1 Heater Auto, Gas Manual
249             # 2 Heater Manual, Gas Auto
250             # 3 Heater Auto, Gas Auto
251 0     0 0   my $self = shift;
252 0           my $mode = shift;
253 0           $mode = sprintf( "%d", $mode );
254 0           return $self->query("A$mode\r");
255             }
256              
257             sub set_PID {
258 0     0 0   my $self = shift;
259 0           my $p = shift;
260 0           my $i = shift;
261 0           my $d = shift;
262              
263 0           $self->itc_set_proportional_value($p);
264 0           $self->itc_set_integral_value($i);
265 0           $self->itc_set_derivative_value($d);
266             }
267              
268             sub itc_set_proportional_value {
269 0     0 0   my $self = shift;
270 0           my $value = shift;
271              
272 0           $self->itc_set_PID_auto(0);
273 0           $value = sprintf( "%d", $value );
274 0           $self->query("P$value\r");
275             }
276              
277             sub itc_set_integral_value {
278 0     0 0   my $self = shift;
279 0           my $value = shift;
280              
281 0           $self->itc_set_PID_auto(0);
282 0           $value = sprintf( "%d", $value );
283 0           $self->query("I$value\r");
284             }
285              
286             sub itc_set_derivative_value {
287 0     0 0   my $self = shift;
288 0           my $value = shift;
289              
290 0           $self->itc_set_PID_auto(0);
291 0           $value = sprintf( "%d", $value );
292 0           $self->query("D$value\r");
293             }
294              
295             sub itc_set_heater_sensor {
296              
297             # 1 Sensor 1
298             # 2 Sensor 2
299             # 3 Sensor 3
300 0     0 0   my $self = shift;
301 0           my $value = shift;
302 0           $value = sprintf( "%d", $value );
303 0           return $self->query("H$value\r");
304             }
305              
306             sub itc_set_PID_auto {
307              
308             # 0 PID Auto Off
309             # 1 PID on
310 0     0 0   my $self = shift;
311 0           my $value = shift;
312 0           $value = sprintf( "%d", $value );
313 0           $self->query("L$value\r");
314             }
315              
316             sub itc_set_max_heater_voltage {
317              
318             # in 0.1 V
319             # 0 dynamical varying limit
320 0     0 0   my $self = shift;
321 0           my $value = shift;
322 0           $value = sprintf( "%d", $value );
323 0           $self->query("M$value\r");
324             }
325              
326             sub itc_set_heater_output {
327              
328             # from 0 to 0.999
329             # 0 dynamical varying limit
330 0     0 0   my $self = shift;
331 0           my $value = shift;
332 0           $value = sprintf( "%d", 1000 * $value );
333 0           $self->query("O$value\r");
334             }
335              
336             sub itc_T_set_point {
337              
338             # Setpoint
339 0     0 0   my $self = shift;
340 0           my $value = shift;
341 0           $value = sprintf( "%.3f", $value );
342 0           return $self->query("T$value\r");
343             }
344              
345             sub itc_sweep {
346              
347             # 0 Stop Sweep
348             # 1 Start Sweep
349             #nn=2P-1 Sweeping to step P
350             #nn=2P Sweeping to step P
351 0     0 0   my $self = shift;
352 0           my $value = shift;
353 0           $value = sprintf( "%d", $value );
354 0           $self->query("S$value\r");
355             }
356              
357             sub itc_set_pointer {
358              
359             # Sets Pointer in internal ITC memory
360 0     0 0   my $self = shift;
361 0           my $x = shift;
362 0           my $y = shift;
363 0 0 0       if ( $x < 0 or $x > 128 ) {
364 0           printf "x=$x no valid ITC Pointer value\n";
365 0           die;
366             }
367 0 0 0       if ( $y < 0 or $y > 128 ) {
368 0           printf "y=$y no valid ITC Pointer value\n";
369 0           die;
370             }
371 0           my $cmd = sprintf( "x%d\r", $x );
372 0           $self->query($cmd);
373 0           $cmd = sprintf( "y%d\r", $y );
374 0           $self->query($cmd);
375             }
376              
377             sub itc_program_sweep_table {
378 0     0 0   my $self = shift;
379 0           my $setpoint = shift; #K Sweep Stop Point
380 0           my $sweeptime = shift; #Min. Total Sweep Time
381 0           my $holdtime = shift; #sec. Hold Time
382              
383 0 0 0       if ( $setpoint < 0. or $setpoint > 9.9 ) {
384 0           printf "Cannot reach setpoint: $setpoint\n";
385 0           die;
386             }
387              
388 0           $self->itc_set_pointer( 1, 1 );
389 0           $setpoint = sprintf( "%1.4f", $setpoint );
390 0           $self->query("s$setpoint\r");
391              
392 0           $self->itc_set_pointer( 1, 2 );
393 0           $sweeptime = sprintf( "%.4f", $sweeptime );
394 0           $self->query("s$sweeptime\r");
395              
396 0           $self->itc_set_pointer( 1, 3 );
397 0           $holdtime = sprintf( "%.4f", $holdtime );
398 0           $self->query("s$holdtime\r");
399              
400 0           $self->itc_set_pointer( 0, 0 );
401             }
402              
403             sub itc_read_sweep_table {
404              
405             # Clears Sweep Program Table
406 0     0 0   my $self = shift;
407 0           $self->query("r\r");
408             }
409              
410             sub itc_clear_sweep_table {
411              
412             # Clears Sweep Program Table
413 0     0 0   my $self = shift;
414 0           $self->query("w\r");
415             }
416              
417             1;
418              
419             __END__
420              
421             =pod
422              
423             =encoding utf-8
424              
425             =head1 NAME
426              
427             Lab::Instrument::OI_ITC503 - Oxford Instruments ITC503 Intelligent Temperature Control
428              
429             =head1 VERSION
430              
431             version 3.880
432              
433             =head1 SYNOPSIS
434              
435             use Lab::Instrument::OI_ITC503;
436            
437             my $itc=new Lab::Instrument::OI_ITC503(
438             isobus_address=>3,
439             );
440              
441             =head1 DESCRIPTION
442              
443             The Lab::Instrument::OI_ITC503 class implements an interface to the Oxford Instruments
444             ITC intelligent temperature controller (tested with the ITC503). This driver is still
445             work in progress and also lacks documentation.
446              
447             =head1 CAVEATS/BUGS
448              
449             probably many
450              
451             =head1 SEE ALSO
452              
453             =over 4
454              
455             =item L<Lab::Instrument>
456              
457             =back
458              
459             =head1 COPYRIGHT AND LICENSE
460              
461             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
462              
463             Copyright 2011-2012 Andreas K. Huettel, Florian Olbrich
464             2013 Andreas K. Huettel
465             2015 Alois Dirnaichner
466             2016 Simon Reinhardt
467             2017 Andreas K. Huettel, Simon Reinhardt
468             2020 Andreas K. Huettel
469              
470              
471             This is free software; you can redistribute it and/or modify it under
472             the same terms as the Perl 5 programming language system itself.
473              
474             =cut