File Coverage

blib/lib/Lab/Instrument/YokogawaGS200.pm
Criterion Covered Total %
statement 55 235 23.4
branch 2 48 4.1
condition 1 12 8.3
subroutine 13 42 30.9
pod 17 32 53.1
total 88 369 23.8


line stmt bran cond sub pod time code
1             package Lab::Instrument::YokogawaGS200;
2             #ABSTRACT: Yokogawa GS200 DC source
3             $Lab::Instrument::YokogawaGS200::VERSION = '3.880';
4 3     3   3059 use v5.20;
  3         14  
5              
6 3     3   17 use strict;
  3         6  
  3         61  
7 3     3   16 use warnings;
  3         12  
  3         80  
8              
9 3     3   18 use feature "switch";
  3         5  
  3         409  
10 3     3   1293 use Lab::Instrument;
  3         109  
  3         95  
11 3     3   1222 use Lab::Instrument::Source;
  3         8  
  3         105  
12 3     3   20 use Data::Dumper;
  3         6  
  3         138  
13 3     3   17 use Lab::SCPI;
  3         14  
  3         11770  
14              
15             our @ISA = ('Lab::Instrument::Source');
16              
17             our %fields = (
18             supported_connections => [ 'VISA_GPIB', 'GPIB', 'VISA' ],
19              
20             # default settings for the supported connections
21             connection_settings => {
22             gpib_board => 0,
23             gpib_address => 22,
24             },
25              
26             device_settings => {
27              
28             gate_protect => 1,
29             gp_equal_level => 1e-5,
30             gp_max_units_per_second => 0.05,
31             gp_max_units_per_step => 0.005,
32             gp_max_step_per_second => 10,
33              
34             stepsize => 0.01, # default stepsize for sweep without gate protect
35              
36             max_sweep_time => 3600,
37             min_sweep_time => 0.1,
38             },
39              
40             # If class does not provide set_$var for those, AUTOLOAD will take care.
41             device_cache => {
42             function => undef,
43             range => undef,
44             level => undef,
45             output => undef,
46             },
47              
48             device_cache_order => [ 'function', 'range' ],
49             request => 0
50             );
51              
52             sub new {
53 2     2 1 4 my $proto = shift;
54 2   33     14 my $class = ref($proto) || $proto;
55 2         15 my $self = $class->SUPER::new(@_);
56 2         4 $self->${ \( __PACKAGE__ . '::_construct' ) }(__PACKAGE__);
  2         18  
57              
58 2         10 $self->configure( $self->config() );
59              
60 2         57 return $self;
61             }
62              
63             sub set_voltage {
64 0     0 1 0 my $self = shift;
65 0         0 my $voltage = shift;
66              
67 0         0 my $function = $self->get_function( { read_mode => 'cache' } );
68              
69 0 0       0 if ( $function ne 'VOLT' ) {
70 0         0 Lab::Exception::CorruptParameter->throw( error =>
71             "Source is in mode $function. Can't set voltage level." );
72             }
73              
74 0         0 return $self->set_level( $voltage, @_ );
75             }
76              
77             sub set_voltage_auto {
78 0     0 0 0 my $self = shift;
79 0         0 my $voltage = shift;
80              
81 0         0 my $function = $self->get_function();
82              
83 0 0       0 if ( $function ne 'VOLT' ) {
84 0         0 Lab::Exception::CorruptParameter->throw( error =>
85             "Source is in mode $function. Can't set voltage level." );
86             }
87              
88 0 0       0 if ( abs($voltage) > 32. ) {
89 0         0 Lab::Exception::CorruptParameter->throw( error =>
90             "Source is not capable of voltage level > 32V. Can't set voltage level."
91             );
92             }
93              
94 0         0 $self->set_level_auto( $voltage, @_ );
95             }
96              
97             sub set_current_auto {
98 0     0 0 0 my $self = shift;
99 0         0 my $current = shift;
100              
101 0         0 my $function = $self->get_function();
102              
103 0 0       0 if ( $function ne 'CURR' ) {
104 0         0 Lab::Exception::CorruptParameter->throw( error =>
105             "Source is in mode $function. Can't set current level." );
106             }
107              
108 0 0       0 if ( abs($current) > 0.200 ) {
109 0         0 Lab::Exception::CorruptParameter->throw( error =>
110             "Source is not capable of current level > 200mA. Can't set current level."
111             );
112             }
113              
114 0         0 $self->set_level_auto( $current, @_ );
115             }
116              
117             sub set_current {
118 0     0 1 0 my $self = shift;
119 0         0 my $current = shift;
120              
121 0         0 my $function = $self->get_function();
122              
123 0 0       0 if ( $self->get_function() ne 'CURR' ) {
124 0         0 Lab::Exception::CorruptParameter->throw( error =>
125             "Source is in mode $function. Can't set current level." );
126             }
127              
128 0         0 $self->set_level( $current, @_ );
129             }
130              
131             sub _set_level {
132 11     11   21 my $self = shift;
133 11         18 my $value = shift;
134 11         32 my $srcrange = $self->get_range();
135              
136 11         113 ( my $dec, my $exp ) = ( $srcrange =~ m/(^\d+)E([-\+]\d+)$/ );
137              
138 11         982 $srcrange = eval("$dec*10**$exp+2*$dec*10**($exp-1)");
139              
140 11 50       79 if ( abs($value) <= $srcrange ) {
141 11         93 my $cmd = sprintf( ":SOURce:LEVel %f", $value );
142              
143             #print $cmd;
144 11         49 $self->write($cmd);
145 11         45 return $self->{'device_cache'}->{'level'} = $value;
146             }
147             else {
148 0         0 Lab::Exception::CorruptParameter->throw(
149             error => "Level $value is out of curren range $srcrange." );
150             }
151              
152             }
153              
154             sub set_level_auto {
155 0     0 0 0 my $self = shift;
156 0         0 my $value = shift;
157              
158 0         0 my $cmd = sprintf( ":SOURce:LEVel:AUTO %e", $value );
159              
160 0         0 $self->write($cmd);
161              
162 0         0 $self->{'device_cache'}->{'range'} = $self->get_range( from_device => 1 );
163              
164 0         0 return $self->{'device_cache'}->{'level'} = $value;
165              
166             }
167              
168             sub program_run {
169 0     0 1 0 my $self = shift;
170 0         0 my ($tail) = $self->_check_args( \@_ );
171              
172             # print "Running program\n";
173 0         0 $self->write( ":PROG:RUN", $tail );
174              
175             }
176              
177             sub program_pause {
178 0     0 1 0 my $self = shift;
179              
180 0         0 my $cmd = sprintf(":PROGram:PAUSe");
181 0         0 $self->write("$cmd");
182             }
183              
184             sub program_continue {
185 0     0 1 0 my $self = shift;
186 0         0 my $value = shift;
187 0         0 my $cmd = sprintf(":PROGram:CONTinue");
188 0         0 $self->write("$cmd");
189              
190             }
191              
192             sub program_halt {
193 2     2 0 5 my $self = shift;
194 2         8 my ($tail) = $self->_check_args( \@_ );
195              
196 2         17 $self->write( ":PROG:HALT", $tail );
197             }
198              
199             sub start_program {
200 0     0 0 0 my $self = shift;
201 0         0 my ($tail) = $self->_check_args( \@_ );
202              
203             #print "Start program\n";
204 0         0 $self->write( ":PROG:EDIT:START", $tail );
205             }
206              
207             sub end_program {
208 0     0 0 0 my $self = shift;
209 0         0 my ($tail) = $self->_check_args( \@_ );
210              
211             #print "End program\n";
212 0         0 $self->write( ":PROG:EDIT:END", $tail );
213             }
214              
215             sub set_setpoint {
216 0     0 0 0 my $self = shift;
217 0         0 my ( $value, $tail ) = $self->_check_args( \@_, ['value'] );
218 0         0 my $cmd = sprintf(":SOUR:LEV $value");
219              
220             #print "Do $cmd";
221 0         0 $self->write( $cmd, { error_check => 1 }, $tail );
222             }
223              
224             sub config_sweep {
225 0     0 0 0 my $self = shift;
226 0         0 my ( $start, $target, $duration, $sections, $tail )
227             = $self->check_sweep_config(@_);
228              
229 0         0 $self->write( ":PROG:REP 0", $tail );
230 0         0 $self->write( "*CLS", $tail );
231 0         0 $self->set_output( 1, $tail );
232              
233 0         0 $self->start_program($tail);
234              
235             #print "Program:\n";
236 0         0 for ( my $i = 1; $i <= $sections; $i++ ) {
237 0         0 $self->set_setpoint( $start + ( $target - $start ) / $sections * $i );
238 0         0 printf "sweep to setpoint: %+.4e\n",
239             $start + ( $target - $start ) / $sections * $i;
240             }
241 0         0 $self->end_program($tail);
242              
243 0         0 $self->set_time( $duration, $duration, $tail );
244              
245             }
246              
247             sub set_time { # internal use only
248 0     0 0 0 my $self = shift;
249 0         0 my ( $sweep_time, $interval_time, $tail )
250             = $self->_check_args( \@_, [ 'sweep_time', 'interval_time' ] );
251 0 0       0 if ( $sweep_time < $self->device_settings()->{min_sweep_time} ) {
    0          
252 0         0 print Lab::Exception::CorruptParameter->new( error =>
253             " Sweep Time: $sweep_time smaller than $self->device_settings()->{min_sweep_time} sec!\n Sweep time set to $self->device_settings()->{min_sweep_time} sec"
254             );
255 0         0 $sweep_time = $self->device_settings()->{min_sweep_time};
256             }
257             elsif ( $sweep_time > $self->device_settings()->{max_sweep_time} ) {
258 0         0 print Lab::Exception::CorruptParameter->new( error =>
259             " Sweep Time: $sweep_time> $self->device_settings()->{max_sweep_time} sec!\n Sweep time set to $self->device_settings()->{max_sweep_time} sec"
260             );
261 0         0 $sweep_time = $self->device_settings()->{max_sweep_time};
262             }
263 0 0       0 if ( $interval_time < $self->device_settings()->{min_sweep_time} ) {
    0          
264 0         0 print Lab::Exception::CorruptParameter->new( error =>
265             " Interval Time: $interval_time smaller than $self->device_settings()->{min_sweep_time} sec!\n Interval time set to $self->device_settings()->{min_sweep_time} sec"
266             );
267 0         0 $interval_time = $self->device_settings()->{min_sweep_time};
268             }
269             elsif ( $interval_time > $self->device_settings()->{max_sweep_time} ) {
270 0         0 print Lab::Exception::CorruptParameter->new( error =>
271             " Interval Time: $interval_time> $self->device_settings()->{max_sweep_time} sec!\n Interval time set to $self->device_settings()->{max_sweep_time} sec"
272             );
273 0         0 $interval_time = $self->device_settings()->{max_sweep_time};
274             }
275 0         0 $self->write( ":PROG:SLOP $sweep_time", $tail );
276 0         0 $self->write( ":PROG:INT $interval_time", $tail );
277              
278             }
279              
280             sub wait {
281 0     0 0 0 my $self = shift;
282 0         0 my ($tail) = $self->_check_args( \@_ );
283 0         0 my $flag = 1;
284 0         0 local $| = 1;
285              
286 0         0 while (1) {
287              
288             #my $status = $self->get_status();
289              
290 0         0 my $current_level
291             = $self->get_level( { read_mode => 'device' }, $tail );
292 0 0 0     0 if ( $flag <= 1.1 and $flag >= 0.9 ) {
    0          
293 0         0 print "\t\t\t\t\t\t\t\t\t\r";
294 0         0 print $self->get_id() . " is sweeping ($current_level )\r";
295              
296             }
297             elsif ( $flag <= 0 ) {
298 0         0 print "\t\t\t\t\t\t\t\t\t\r";
299 0         0 print $self->get_id() . " is ($current_level ) \r";
300 0         0 $flag = 2;
301             }
302 0         0 $flag -= 0.5;
303              
304 0 0       0 if ( $self->active($tail) == 0 ) {
305 0         0 print "\t\t\t\t\t\t\t\t\t\r";
306 0         0 $| = 0;
307 0         0 last;
308             }
309             }
310              
311             }
312              
313             sub _sweep_to_level {
314 0     0   0 my $self = shift;
315 0         0 my ( $target, $time, $tail )
316             = $self->_check_args( \@_, [ 'points', 'time' ] );
317              
318 0         0 $self->config_sweep( { points => $target, time => $time }, $tail );
319              
320 0         0 $self->program_run($tail);
321              
322 0         0 $self->wait($tail);
323 0         0 my $current = $self->get_level($tail);
324              
325 0         0 my $eql = $self->get_gp_equal_level();
326              
327 0 0       0 if ( abs( $current - $target ) > $eql ) {
328 0         0 print "YokogawaGS200.pm: error current neq target\n";
329 0         0 Lab::Exception::CorruptParameter->throw(
330             "Sweep failed: $target not equal to $current. \n");
331             }
332              
333 0         0 $self->{'device_cache'}->{'level'} = $target;
334              
335 0         0 return $target;
336             }
337              
338             sub trg {
339 0     0 0 0 my $self = shift;
340 0         0 my ($tail) = $self->_check_args( \@_ );
341 0         0 $self->write( "*CLS", $tail );
342 0         0 $self->program_run($tail);
343             }
344              
345             sub abort {
346 2     2 0 9 my $self = shift;
347 2         10 my ($tail) = $self->_check_args( \@_ );
348 2         17 $self->program_halt($tail);
349             }
350              
351             sub get_voltage {
352 0     0 1 0 my $self = shift;
353              
354 0         0 my $function = $self->get_function();
355              
356 0 0       0 if ( !$function eq 'VOLT' ) {
357 0         0 Lab::Exception::CorruptParameter->throw( error =>
358             "Source is in mode $function. Can't get voltage level." );
359             }
360              
361 0         0 return $self->get_level(@_);
362             }
363              
364             sub active {
365 0     0 0 0 my $self = shift;
366 0         0 my ($tail) = $self->_check_args( \@_ );
367              
368 0         0 $self->write( "STAT:ENAB 128", $tail );
369 0 0       0 if ( $self->get_status( "EES", $tail ) == 1 ) {
370 0         0 return 0;
371             }
372             else {
373 0         0 return 1;
374             }
375              
376             }
377              
378             sub get_status {
379 6     6 1 10 my $self = shift;
380 6         22 my ( $request, $tail ) = $self->_check_args( \@_, ['request'] );
381              
382             # The status byte is read
383              
384 6         20 my $status = int( $self->query( '*STB?', $tail ) );
385              
386             #printf "Status: %i",$status;
387              
388 6         27 my @flags = qw/NONE EES ESB MAV NONE EAV MSS NONE/;
389 6         12 my $result = {};
390 6         15 for ( 0 .. 7 ) {
391 48         94 $result->{ $flags[$_] } = $status & 1;
392 48         70 $status >>= 1;
393             }
394              
395             #print "EOP: $result->{'EOP'}\n";
396 6 50       16 return $result->{$request} if defined $request;
397 6         27 return $result;
398              
399             }
400              
401             sub get_current {
402 0     0 1   my $self = shift;
403              
404 0           my $function = $self->get_function();
405              
406 0 0         if ( !$self->get_function() eq 'CURR' ) {
407 0           Lab::Exception::CorruptParameter->throw( error =>
408             "Source is in mode $function. Can't get current level." );
409             }
410              
411 0           return $self->get_level(@_);
412             }
413              
414             sub get_level {
415 0     0 1   my $self = shift;
416 0           my ($tail) = $self->_check_args( \@_ );
417 0           my $cmd = ":SOUR:LEV?";
418              
419 0           return $self->request( $cmd, $tail );
420             }
421              
422             sub get_function {
423 0     0 0   my $self = shift;
424 0           my ($tail) = $self->_check_args( \@_ );
425              
426 0           my $cmd = ":SOURce:FUNCtion?";
427 0           return $self->query( $cmd, $tail );
428             }
429              
430             sub get_range {
431 0     0 1   my $self = shift;
432 0           my ($tail) = $self->_check_args( \@_ );
433              
434 0           my $cmd = ":SOUR:RANG?";
435 0           return $self->query( $cmd, $tail );
436              
437             }
438              
439             sub set_function {
440 0     0 1   my $self = shift;
441 0           my $func = shift;
442              
443 0 0         if ( scpi_match( $func, 'current|voltage' ) ) {
444 0           my $cmd = ":SOURce:FUNCtion " . $func;
445              
446             #print "$cmd\n";
447 0           $self->write($cmd);
448 0           return $self->{'device_cache'}->{'function'} = $func;
449             }
450             else {
451 0           Lab::Exception::CorruptParameter->throw(
452             error => "source function $func not defined for this device.\n" );
453             }
454              
455             }
456              
457             sub set_range {
458 0     0 1   my $self = shift;
459 0           my $range = shift;
460              
461 0           my $srcf = $self->get_function();
462              
463 0           $self->write( "SOURce:RANGe $range", error_check => 1 );
464 0           return $self->{'device_cache'}->{'range'}
465             = $self->get_range( from_device => 1 );
466              
467             }
468              
469             sub set_output {
470 0     0 0   my $self = shift;
471 0           my $value = shift;
472              
473 0 0         if ( $value =~ /(on|1)/i ) {
    0          
474 0           $value = 1;
475             }
476             elsif ( $value =~ /(off|0)/i ) {
477 0           $value = 0;
478             }
479             else {
480 0           Lab::Exception::CorruptParameter->throw( error =>
481             "set_output accepts only on or off (case non-sensitive) and 1 or 0. $value is not valid."
482             );
483             }
484              
485 0           return $self->{"device_cache"}->{"output"} = $self->write(":OUTP $value");
486             }
487              
488             sub get_output {
489 0     0 1   my $self = shift;
490 0           my ($tail) = $self->_check_args( \@_ );
491              
492 0           return $self->query( ":OUTP?", $tail );
493             }
494              
495             sub set_voltage_limit {
496 0     0 1   my $self = shift;
497 0           my $value = shift;
498 0           my $cmd = ":SOURce:PROTection:VOLTage $value";
499              
500 0 0 0       if ( $value > 30. || $value < 1. ) {
501 0           Lab::Exception::CorruptParameter->throw( error =>
502             "The voltage limit $value is not within the allowed range.\n"
503             );
504             }
505              
506 0           $self->connection()->write($cmd);
507              
508 0           return $self->device_cache()->{'voltage_limit'} = $value;
509              
510             }
511              
512             sub set_current_limit {
513 0     0 1   my $self = shift;
514 0           my $value = shift;
515 0           my $cmd = ":SOURce:PROTection:CURRent $value";
516              
517 0 0 0       if ( $value > 0.2 || $value < 0.001 ) {
518 0           Lab::Exception::CorruptParameter->throw( error =>
519             "The current limit $value is not within the allowed range.\n"
520             );
521             }
522              
523 0           $self->connection()->write($cmd);
524              
525 0           return $self->device_cache()->{'current_limit'} = $value;
526              
527             }
528              
529             sub get_error {
530 0     0 1   my $self = shift;
531              
532 0           my $cmd = ":SYSTem:ERRor?";
533              
534 0           return $self->query($cmd);
535             }
536              
537             1;
538              
539             __END__
540              
541             =pod
542              
543             =encoding utf-8
544              
545             =head1 NAME
546              
547             Lab::Instrument::YokogawaGS200 - Yokogawa GS200 DC source
548              
549             =head1 VERSION
550              
551             version 3.880
552              
553             =head1 SYNOPSIS
554              
555             use Lab::Instrument::YokogawaGS200;
556            
557             my $gate14=new Lab::Instrument::YokogawaGS200(
558             connection_type => 'LinuxGPIB',
559             gpib_address => 22,
560             function => 'VOLT',
561             level => 0.4,
562             );
563             $gate14->set_voltage(0.745);
564             print $gate14->get_voltage();
565              
566             =head1 DESCRIPTION
567              
568             The Lab::Instrument::YokogawaGS200 class implements an interface to the
569             discontinued voltage and current source GS200 by Yokogawa. This class derives from
570             L<Lab::Instrument::Source> and provides all functionality described there.
571              
572             =head1 CONSTRUCTORS
573              
574             =head2 new( %configuration_HASH )
575              
576             HASH is a list of tuples given in the format
577              
578             key => value,
579              
580             please supply at least the configuration for the connection:
581             connection_type => "LinxGPIB"
582             gpib_address =>
583              
584             you might also want to have gate protect from the start (the default values are given):
585              
586             gate_protect => 1,
587              
588             gp_equal_level => 1e-5,
589             gp_max_units_per_second => 0.05,
590             gp_max_units_per_step => 0.005,
591             gp_max_step_per_second => 10,
592             gp_max_units_per_second => 0.05,
593             gp_max_units_per_step => 0.005,
594              
595             max_sweep_time=>3600,
596             min_sweep_time=>0.1,
597              
598             Additinally there is support to set parameters for the device "on init":
599              
600             function => undef, # 'VOLT' - voltage, 'CURR' - current
601             range => undef,
602             level => undef,
603             output => undef,
604              
605             If those values are not specified, they are read from the device.
606              
607             =head1 METHODS
608              
609             =head2 sweep_to_level
610              
611             $src->sweep_to_level($lvl,$time)
612              
613             Sweep to the level $lvl in $time seconds.
614              
615             =head2 set_voltage
616              
617             $src->set_voltage($voltage)
618              
619             Sets the output voltage to $voltage.
620             Returns the newly set voltage.
621              
622             =head2 get_voltage
623              
624             Returns the currently set $voltage. The value is read from the driver cache by default. Provide the option
625              
626             device_cache => 1
627              
628             to read directly from the device.
629              
630             =head2 set_current
631              
632             $src->set_current($current)
633              
634             Sets the output current to $current.
635             Returns the newly set current.
636              
637             =head2 get_current
638              
639             Returns the currently set $current. The value is read from the driver cache by default. Provide the option
640              
641             device_cache => 1
642              
643             to read directly from the device.
644              
645             =head2 set_level
646              
647             $src->set_level($lvl)
648              
649             Sets the level $lvl in the current operation mode.
650              
651             =head2 get_level
652              
653             $lvl = $src->get_level()
654              
655             Returns the currently set level. Use
656              
657             device_cache => 1
658              
659             to enforce a reading directly from the device.
660              
661             =head2 set_range($range)
662              
663             Fixed voltage mode
664             10E-3 10mV
665             100E-3 100mV
666             1E+0 1V
667             10E+0 10V
668             30E+0 30V
669              
670             Fixed current mode
671             1E-3 1mA
672             10E-3 10mA
673             100E-3 100mA
674             200E-3 200mA
675            
676             Please use the format on the left for the range command.
677              
678             =head2 program_run($program)
679              
680             Runs a program stored on the YokogawaGS200. If no prgram name is given, the currently loaded program is executed.
681              
682             =head2 program_pause
683              
684             Pauses the currently running program.
685              
686             =head2 program_continue
687              
688             Continues the paused program.
689              
690             =head2 set_function($function)
691              
692             Sets the source function. The Yokogawa supports the values
693              
694             "CURR" for current mode and
695             "VOLT" for voltage mode.
696              
697             Returns the newly set source function.
698              
699             =head2 set_voltage_limit($limit)
700              
701             Sets a voltage limit to protect the device.
702             Returns the new voltage limit.
703              
704             =head2 set_current_limit($limit)
705              
706             See set_voltage_limit.
707              
708             =head2 output_on()
709              
710             Sets the output switch to on and returns the new value of the output status.
711              
712             =head2 output_off()
713              
714             Sets the output switch to off. The instrument outputs no voltage
715             or current then, no matter what voltage you set. Returns the new value of the output status.
716              
717             =head2 get_error()
718              
719             Queries the error code from the device. This is a very useful thing to do when you are working remote and the source is not responding.
720              
721             =head2 get_output()
722              
723             =head2 get_range()
724              
725             =head1 CAVEATS
726              
727             probably many
728              
729             =head1 SEE ALSO
730              
731             =over 4
732              
733             =item * Lab::Instrument
734              
735             The YokogawaGP200 class is a Lab::Instrument (L<Lab::Instrument>).
736              
737             =item * Lab::Instrument::Source
738              
739             The YokogawaGP200 class is a Source (L<Lab::Instrument::Source>)
740              
741             =back
742              
743             =head1 COPYRIGHT AND LICENSE
744              
745             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
746              
747             Copyright 2005-2006 Daniel Schroeer
748             2009 Andreas K. Huettel, Daniela Taubert
749             2010 Andreas K. Huettel, Daniel Schroeer
750             2011 Andreas K. Huettel, David Kalok, Florian Olbrich
751             2012 Alois Dirnaichner, Andreas K. Huettel, Florian Olbrich
752             2013 Andreas K. Huettel, Christian Butschkow
753             2014 Alois Dirnaichner, Christian Butschkow
754             2015 Alois Dirnaichner
755             2016 Simon Reinhardt
756             2017 Andreas K. Huettel
757             2020 Andreas K. Huettel
758              
759              
760             This is free software; you can redistribute it and/or modify it under
761             the same terms as the Perl 5 programming language system itself.
762              
763             =cut