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.900';
3             #ABSTRACT: Sensor stabilizer subroutine
4              
5 2     2   103917 use v5.20;
  2         18  
6              
7 2     2   11 use warnings;
  2         4  
  2         60  
8 2     2   10 use strict;
  2         6  
  2         69  
9 2     2   418 use Lab::Moose ();
  2         6  
  2         52  
10 2     2   13 use MooseX::Params::Validate 'validated_list';
  2         5  
  2         21  
11 2     2   749 use Time::HiRes qw/time sleep/;
  2         7  
  2         24  
12 2     2   862 use Lab::Moose::Countdown;
  2         6  
  2         154  
13 2     2   1086 use Statistics::Descriptive ();
  2         46345  
  2         61  
14 2     2   18 use Scalar::Util 'looks_like_number';
  2         5  
  2         107  
15 2     2   14 use Carp;
  2         6  
  2         102  
16 2     2   15 use Exporter 'import';
  2         6  
  2         833  
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 716 $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         42 my @points = ();
44              
45 1         4 my $num_points = int( $observation_time / $measurement_interval );
46 1 50       4 if ( $num_points == 0 ) {
47 0         0 $num_points = 1;
48             }
49              
50             # enable autoflush
51 1         11 my $autoflush = STDOUT->autoflush();
52 1         54 my $start_time = time();
53              
54 1         2 while (1) {
55 14         151 my $new_value = $instrument->$getter();
56 14 50       276 if ( not looks_like_number($new_value) ) {
57 0         0 croak "$new_value is not a number";
58             }
59 14         70 push @points, $new_value;
60 14 100       59 if ( @points > $num_points ) {
61 4         13 shift @points;
62             }
63              
64 14 100       71 if ( @points == $num_points ) {
65 5         16 my $crit_stddev;
66             my $crit_setpoint;
67              
68 5         49 my $stat = Statistics::Descriptive::Full->new();
69 5         651 $stat->add_data(@points);
70              
71 5         874 my $std_dev = $stat->standard_deviation();
72 5 100       286 if ( $std_dev < $tolerance_std_dev ) {
73 1         4 $crit_stddev = 1;
74             }
75              
76 5         21 my $median = $stat->median();
77 5 100       654 if ( abs( $setpoint - $median ) < $tolerance_setpoint ) {
78 4         10 $crit_setpoint = 1;
79             }
80              
81 5 50       16 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     24 if ( $crit_stddev and $crit_setpoint ) {
88 1         46 printf("reached stabilization criterion \n");
89 1         13 last;
90             }
91             else {
92 4         217 printf("\r");
93             }
94              
95             }
96             else {
97 9 50       38 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       67 if ( $measurement_interval > 5 ) {
106 0         0 countdown(
107             $measurement_interval,
108             "Measurement interval: Sleeping for "
109             );
110             }
111             else {
112 13         1302017 sleep($measurement_interval);
113             }
114              
115 13 50       168 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         11 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.900
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