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.880';
3             #ABSTRACT: Sensor stabilizer subroutine
4              
5 2     2   106701 use v5.20;
  2         17  
6              
7 2     2   19 use warnings;
  2         4  
  2         62  
8 2     2   11 use strict;
  2         3  
  2         45  
9 2     2   462 use Lab::Moose ();
  2         6  
  2         64  
10 2     2   15 use MooseX::Params::Validate 'validated_list';
  2         9  
  2         31  
11 2     2   827 use Time::HiRes qw/time sleep/;
  2         7  
  2         21  
12 2     2   963 use Lab::Moose::Countdown;
  2         6  
  2         104  
13 2     2   550 use Statistics::Descriptive ();
  2         22021  
  2         48  
14 2     2   16 use Scalar::Util 'looks_like_number';
  2         5  
  2         97  
15 2     2   12 use Carp;
  2         4  
  2         109  
16 2     2   11 use Exporter 'import';
  2         4  
  2         944  
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 616 $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         270 my @points = ();
44              
45 1         6 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         14 my $autoflush = STDOUT->autoflush();
52 1         64 my $start_time = time();
53              
54 1         2 while (1) {
55 14         245 my $new_value = $instrument->$getter();
56 14 50       402 if ( not looks_like_number($new_value) ) {
57 0         0 croak "$new_value is not a number";
58             }
59 14         116 push @points, $new_value;
60 14 100       73 if ( @points > $num_points ) {
61 4         17 shift @points;
62             }
63              
64 14 100       94 if ( @points == $num_points ) {
65 5         26 my $crit_stddev;
66             my $crit_setpoint;
67              
68 5         66 my $stat = Statistics::Descriptive::Full->new();
69 5         877 $stat->add_data(@points);
70              
71 5         1180 my $std_dev = $stat->standard_deviation();
72 5 100       375 if ( $std_dev < $tolerance_std_dev ) {
73 1         5 $crit_stddev = 1;
74             }
75              
76 5         31 my $median = $stat->median();
77 5 100       786 if ( abs( $setpoint - $median ) < $tolerance_setpoint ) {
78 4         37 $crit_setpoint = 1;
79             }
80              
81 5 50       26 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     36 if ( $crit_stddev and $crit_setpoint ) {
88 1         81 printf("reached stabilization criterion \n");
89 1         21 last;
90             }
91             else {
92 4         234 printf("\r");
93             }
94              
95             }
96             else {
97 9 50       42 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       61 if ( $measurement_interval > 5 ) {
106 0         0 countdown(
107             $measurement_interval,
108             "Measurement interval: Sleeping for "
109             );
110             }
111             else {
112 13         1302749 sleep($measurement_interval);
113             }
114              
115 13 50       319 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         45 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.880
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