File Coverage

blib/lib/Lab/Moose/Stabilizer.pm
Criterion Covered Total %
statement 64 72 88.8
branch 16 24 66.6
condition 2 3 66.6
subroutine 12 12 100.0
pod 1 1 100.0
total 95 112 84.8


line stmt bran cond sub pod time code
1             package Lab::Moose::Stabilizer;
2             $Lab::Moose::Stabilizer::VERSION = '3.881';
3             #ABSTRACT: Sensor stabilizer subroutine
4              
5 2     2   105623 use v5.20;
  2         19  
6              
7 2     2   12 use warnings;
  2         5  
  2         61  
8 2     2   12 use strict;
  2         6  
  2         38  
9 2     2   448 use Lab::Moose ();
  2         7  
  2         61  
10 2     2   14 use MooseX::Params::Validate 'validated_list';
  2         5  
  2         23  
11 2     2   759 use Time::HiRes qw/time sleep/;
  2         5  
  2         20  
12 2     2   1000 use Lab::Moose::Countdown;
  2         15  
  2         122  
13 2     2   634 use Statistics::Descriptive ();
  2         23324  
  2         46  
14 2     2   19 use Scalar::Util 'looks_like_number';
  2         6  
  2         97  
15 2     2   35 use Carp;
  2         4  
  2         110  
16 2     2   14 use Exporter 'import';
  2         6  
  2         1014  
17             our @EXPORT = qw/stabilize/;
18              
19             # inspired by old Lab::XPRESS stabilization routines
20              
21              
22             sub stabilize {
23             my (
24 1     1 1 710 $instrument, $setpoint, $getter, $tolerance_setpoint,
25             $tolerance_std_dev,
26             $measurement_interval, $observation_time, $max_stabilization_time,
27             $verbose
28             )
29             = validated_list(
30             \@_,
31             instrument => { isa => 'Object' },
32             setpoint => { isa => 'Num' },
33             getter => { isa => 'CodeRef | Str' },
34             tolerance_setpoint => { isa => 'Lab::Moose::PosNum' },
35             tolerance_std_dev => { isa => 'Lab::Moose::PosNum' },
36             measurement_interval => { isa => 'Lab::Moose::PosNum' },
37             observation_time => { isa => 'Lab::Moose::PosNum' },
38             max_stabilization_time =>
39             { isa => 'Maybe[Lab::Moose::PosNum]', optional => 1 },
40             verbose => { isa => 'Bool' },
41             );
42              
43 1         142 my @points = ();
44              
45 1         7 my $num_points = int( $observation_time / $measurement_interval );
46 1 50       5 if ( $num_points == 0 ) {
47 0         0 $num_points = 1;
48             }
49              
50             # enable autoflush
51 1         13 my $autoflush = STDOUT->autoflush();
52 1         68 my $start_time = time();
53              
54 1         3 while (1) {
55 14         291 my $new_value = $instrument->$getter();
56 14 50       454 if ( not looks_like_number($new_value) ) {
57 0         0 croak "$new_value is not a number";
58             }
59 14         144 push @points, $new_value;
60 14 100       94 if ( @points > $num_points ) {
61 4         36 shift @points;
62             }
63              
64 14 100       97 if ( @points == $num_points ) {
65 5         30 my $crit_stddev;
66             my $crit_setpoint;
67              
68 5         79 my $stat = Statistics::Descriptive::Full->new();
69 5         1186 $stat->add_data(@points);
70              
71 5         1378 my $std_dev = $stat->standard_deviation();
72 5 100       502 if ( $std_dev < $tolerance_std_dev ) {
73 1         6 $crit_stddev = 1;
74             }
75              
76 5         42 my $median = $stat->median();
77 5 100       1009 if ( abs( $setpoint - $median ) < $tolerance_setpoint ) {
78 4         16 $crit_setpoint = 1;
79             }
80              
81 5 50       44 if ($verbose) {
82 0         0 printf(
83             "Setpoint: %.6e, Value: %.6e, std_dev: %.6e, median: %.6e ",
84             $setpoint, $new_value, $std_dev, $median
85             );
86             }
87 5 100 66     45 if ( $crit_stddev and $crit_setpoint ) {
88 1         739 printf("reached stabilization criterion \n");
89 1         20 last;
90             }
91             else {
92 4         673 printf("\r");
93             }
94              
95             }
96             else {
97 9 50       39 if ($verbose) {
98 0         0 printf(
99             "Setpoint: %.6e, Value: %.6e, need more points... \r",
100             $setpoint, $new_value
101             );
102             }
103             }
104              
105 13 50       79 if ( $measurement_interval > 5 ) {
106 0         0 countdown(
107             $measurement_interval,
108             "Measurement interval: Sleeping for "
109             );
110             }
111             else {
112 13         1302979 sleep($measurement_interval);
113             }
114              
115 13 50       282 if ( defined $max_stabilization_time ) {
116 0 0       0 if ( time() - $start_time > $max_stabilization_time ) {
117 0         0 printf(
118             "Reached maximum stabilization time \n"
119             );
120 0         0 last;
121             }
122             }
123             }
124              
125             # reset autoflush to previous value
126 1         38 STDOUT->autoflush($autoflush);
127             }
128              
129             1;
130              
131             __END__
132              
133             =pod
134              
135             =encoding UTF-8
136              
137             =head1 NAME
138              
139             Lab::Moose::Stabilizer - Sensor stabilizer subroutine
140              
141             =head1 VERSION
142              
143             version 3.881
144              
145             =head1 DESCRIPTION
146              
147             Routine for sensor (temperature, magnetic field, ...) stabilization.
148              
149             =head1 SUBROUTINES
150              
151             =head2 stabilize
152              
153             stabilize(
154             instrument => $OI_ITC,
155             setpoint => 10,
156             getter => sub { ...; return $number}, # or method name like 'get_T'
157             # will call '$instrument->$getter()'
158             tolerance_setpoint => 0.1, # max. allowed median
159             tolerance_std_dev => 0.1, # max. allowed standard deviation
160             measurement_interval => 2, # time (s) between calls of getter
161             observation_time => 20, # length of window (s) for median/std_dev
162             max_stabilization_time => 100, # abort stabilization after (s, optional)
163             verbose => 1
164             );
165              
166             Call the C<getter> method repeatedly. As soon as enough points have been measured,
167             start calculating median and standard deviation and repeat until convergence.
168             All times are given in seconds. Print status messages if C<verbose> is true.
169              
170             =head1 COPYRIGHT AND LICENSE
171              
172             This software is copyright (c) 2023 by the Lab::Measurement team; in detail:
173              
174             Copyright 2018 Andreas K. Huettel, Simon Reinhardt
175             2019 Simon Reinhardt
176             2020 Andreas K. Huettel
177              
178              
179             This is free software; you can redistribute it and/or modify it under
180             the same terms as the Perl 5 programming language system itself.
181              
182             =cut