line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package SCUBA::Table::NoDeco;
|
2
|
|
|
|
|
|
|
|
3
|
10
|
|
|
10
|
|
258805
|
use strict;
|
|
10
|
|
|
|
|
25
|
|
|
10
|
|
|
|
|
424
|
|
4
|
10
|
|
|
10
|
|
56
|
use Carp;
|
|
10
|
|
|
|
|
15
|
|
|
10
|
|
|
|
|
2397
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = "0.03";
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
SCUBA::Table::NoDeco - Calculate no-decompression dive times.
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use SCUBA::Table::NoDeco;
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my $table = SCUBA::Table::NoDeco->new();
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
$table->dive(metres => 15, minutes => 30);
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
print $table->group,"\n"; # Prints "E"
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$table->surface(minutes => 60);
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
print $table->group,"\n"; # Prints "D"
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
print $table->max_time(metres => 30),"\n"; # Prints 6
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 WARNING AND DISCLAIMER
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
Do B use this module as your sole source of no-decompression dive
|
31
|
|
|
|
|
|
|
times. This module is intended for use only as a guide to assist in
|
32
|
|
|
|
|
|
|
planning your dives. B calculate and verify your dive times
|
33
|
|
|
|
|
|
|
by hand once you have planned your dives. If you have a dive
|
34
|
|
|
|
|
|
|
computer, follow its instructions for use.
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
SCUBA diving involves serious risks of injury or death, and should
|
37
|
|
|
|
|
|
|
only be performed by individuals in good health and with the appropriate
|
38
|
|
|
|
|
|
|
skills and training. Even when tables are used with proper safety
|
39
|
|
|
|
|
|
|
procedures, decompression sickness may still occur.
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
The author provides ABSOLUTELY NO WARRANTY on this module, without
|
42
|
|
|
|
|
|
|
even the implied warranty of merchantability or fitness for a particular
|
43
|
|
|
|
|
|
|
purpose. Use entirely at your own risk.
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
This module provides the ability to perform useful calculations
|
48
|
|
|
|
|
|
|
using dive-tables, including calculating dive groups and maximum
|
49
|
|
|
|
|
|
|
no-decompression times for repetitive dives. A selection of tables
|
50
|
|
|
|
|
|
|
are available. The module assumes that the diver is using air as
|
51
|
|
|
|
|
|
|
their breathing gas.
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head1 METHODS
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
The following methods are provided.
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=cut
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# There's really 0.3048 feet in a metre, but all the dive tables seem
|
60
|
|
|
|
|
|
|
# to assume a flat 1 ft = 30 cm. As such, we use the same constant here,
|
61
|
|
|
|
|
|
|
# otherwise we end up with incorrect results.
|
62
|
|
|
|
|
|
|
|
63
|
10
|
|
|
10
|
|
63
|
use constant FEET2METRES => 0.3;
|
|
10
|
|
|
|
|
21
|
|
|
10
|
|
|
|
|
34675
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
our %MIN_SURFACE_TIME = (
|
66
|
|
|
|
|
|
|
SSI => 10, # Less than 10 minutes is considered same dive.
|
67
|
|
|
|
|
|
|
PADI => 0, # PADI doesn't have a minimum surface time.
|
68
|
|
|
|
|
|
|
);
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
our %LIMITS = (
|
71
|
|
|
|
|
|
|
SSI => {
|
72
|
|
|
|
|
|
|
3.0 => [60, 120, 210, 300],
|
73
|
|
|
|
|
|
|
4.5 => [35, 70, 110, 160, 225, 350],
|
74
|
|
|
|
|
|
|
6.0 => [25, 50, 75, 100, 135, 180, 240, 325],
|
75
|
|
|
|
|
|
|
7.5 => [20, 35, 55, 75, 100, 125, 160, 195, 245],
|
76
|
|
|
|
|
|
|
9.0 => [15, 30, 45, 60, 75, 95, 120, 145, 170, 205],
|
77
|
|
|
|
|
|
|
10.5 => [ 5, 15, 25, 40, 50, 60, 80, 100, 120, 140, 160],
|
78
|
|
|
|
|
|
|
12.0 => [ 5, 15, 25, 30, 40, 50, 70, 80, 100, 110, 130],
|
79
|
|
|
|
|
|
|
15.0 => [ 0, 10, 15, 25, 30, 40, 50, 60, 70],
|
80
|
|
|
|
|
|
|
18.0 => [ 0, 10, 15, 20, 25, 30, 40, 50],
|
81
|
|
|
|
|
|
|
21.0 => [ 0, 5, 10, 15, 20, 30, 35, 40],
|
82
|
|
|
|
|
|
|
24.0 => [ 0, 5, 10, 15, 20, 25, 30],
|
83
|
|
|
|
|
|
|
27.0 => [ 0, 5, 10, 12, 15, 20, 25],
|
84
|
|
|
|
|
|
|
30.0 => [ 0, 5, 7, 10, 15, 20],
|
85
|
|
|
|
|
|
|
33.0 => [ 0, 0, 5, 10, 13, 15],
|
86
|
|
|
|
|
|
|
36.0 => [ 0, 0, 5, 10],
|
87
|
|
|
|
|
|
|
39.0 => [ 0, 0, 5]
|
88
|
|
|
|
|
|
|
},
|
89
|
|
|
|
|
|
|
PADI => {
|
90
|
|
|
|
|
|
|
# 35 feet
|
91
|
|
|
|
|
|
|
10.5 => [ 10, 19, 25, 29, 32, 36, 40, 44, 48, 52, 57, 62,
|
92
|
|
|
|
|
|
|
67, 73, 79, 85, 92, 100, 108, 117, 127, 139, 152, 168,
|
93
|
|
|
|
|
|
|
188, 205],
|
94
|
|
|
|
|
|
|
# 40 feet
|
95
|
|
|
|
|
|
|
12.0 => [ 9, 16, 22, 25, 27, 31, 34, 37, 40, 44, 48, 51,
|
96
|
|
|
|
|
|
|
55, 60, 64, 69, 74, 79, 85, 91, 97, 104, 111, 120,
|
97
|
|
|
|
|
|
|
129, 140],
|
98
|
|
|
|
|
|
|
# 50 feet
|
99
|
|
|
|
|
|
|
15.0 => [ 7, 13, 17, 19, 21, 24, 26, 28, 31, 33, 36, 39,
|
100
|
|
|
|
|
|
|
41, 44, 47, 50, 53, 57, 60, 63, 67, 71, 75, 80],
|
101
|
|
|
|
|
|
|
# 60 feet
|
102
|
|
|
|
|
|
|
18.0 => [ 6, 11, 14, 16, 17, 19, 21, 23, 25, 27, 29, 31,
|
103
|
|
|
|
|
|
|
33, 35, 37, 39, 42, 44, 47, 49, 52, 54, 55],
|
104
|
|
|
|
|
|
|
# 70 feet
|
105
|
|
|
|
|
|
|
21.0 => [ 5, 9, 12, 13, 15, 16, 18, 19, 21, 22, 24, 26,
|
106
|
|
|
|
|
|
|
27, 29, 31, 33, 35, 36, 38, 40],
|
107
|
|
|
|
|
|
|
# 80 feet
|
108
|
|
|
|
|
|
|
24.0 => [ 4, 8, 10, 11, 13, 14, 15, 17, 18, 19, 21, 22,
|
109
|
|
|
|
|
|
|
23, 25, 26, 28, 29, 30],
|
110
|
|
|
|
|
|
|
# 90 feet
|
111
|
|
|
|
|
|
|
27.0 => [ 4, 7, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19,
|
112
|
|
|
|
|
|
|
21, 22, 23, 24, 25],
|
113
|
|
|
|
|
|
|
#100 feet
|
114
|
|
|
|
|
|
|
30.0 => [ 3, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
115
|
|
|
|
|
|
|
18, 19, 20],
|
116
|
|
|
|
|
|
|
#110 feet
|
117
|
|
|
|
|
|
|
33.0 => [ 3, 6, 7, 8, 9, 10, 11, 12, 13, 13, 14, 15,
|
118
|
|
|
|
|
|
|
16],
|
119
|
|
|
|
|
|
|
#120 feet
|
120
|
|
|
|
|
|
|
36.0 => [ 3, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13],
|
121
|
|
|
|
|
|
|
#130 feet
|
122
|
|
|
|
|
|
|
39.0 => [ 3, 5, 6, 7, 7, 8, 9, 10],
|
123
|
|
|
|
|
|
|
#140 feet
|
124
|
|
|
|
|
|
|
42.0 => [ 0, 4, 5, 6, 7, 8],
|
125
|
|
|
|
|
|
|
},
|
126
|
|
|
|
|
|
|
);
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
our %SURFACE = (
|
129
|
|
|
|
|
|
|
SSI => {
|
130
|
|
|
|
|
|
|
A => { 12*60+ 0 => "A" },
|
131
|
|
|
|
|
|
|
B => { 12*60+ 0 => "A", 3*60+20 => "B" },
|
132
|
|
|
|
|
|
|
C => { 12*60+ 0 => "A", 4*60+49 => "B", 1*60+39 => "C" },
|
133
|
|
|
|
|
|
|
D => { 12*60+ 0 => "A", 5*60+48 => "B", 2*60+38 => "C", 1*60+ 9 => "D"},
|
134
|
|
|
|
|
|
|
E => { 12*60+ 0 => "A", 6*60+34 => "B", 3*60+24 => "C", 1*60+57 => "D",
|
135
|
|
|
|
|
|
|
54+ 0 => "E" },
|
136
|
|
|
|
|
|
|
F => { 12*60+ 0 => "A", 7*60+ 5 => "B", 3*60+57 => "C", 2*60+28 => "D",
|
137
|
|
|
|
|
|
|
1*60+29 => "E", 0*60+45 => "F" },
|
138
|
|
|
|
|
|
|
G => { 12*60+ 0 => "A", 7*60+35 => "B", 4*60+25 => "C", 2*60+58 => "D",
|
139
|
|
|
|
|
|
|
1*60+50 => "E", 1*60+15 => "F", 0*60+40 => "G" },
|
140
|
|
|
|
|
|
|
H => { 12*60+ 0 => "A", 7*60+59 => "B", 4*60+49 => "C", 3*60+20 => "D",
|
141
|
|
|
|
|
|
|
2*60+23 => "E", 1*60+41 => "F", 1*60+06 => "G", 0*60+36 => "H"},
|
142
|
|
|
|
|
|
|
I => { 12*60+ 0 => "A", 8*60+21 => "B", 5*60+12 => "C", 3*60+43 => "D",
|
143
|
|
|
|
|
|
|
2*60+44 => "E", 2*60+02 => "F", 1*60+29 => "G", 0*60+59 => "H",
|
144
|
|
|
|
|
|
|
0*60+33 => "I" },
|
145
|
|
|
|
|
|
|
J => { 12*60+ 0 => "A", 8*60+50 => "B", 5*60+40 => "C", 4*60+02 => "D",
|
146
|
|
|
|
|
|
|
3*60+04 => "E", 2*60+20 => "F", 1*60+47 => "G", 1*60+19 => "H",
|
147
|
|
|
|
|
|
|
0*60+54 => "I", 0*60+31 => "J" },
|
148
|
|
|
|
|
|
|
K => { 12*60+ 0 => "A", 8*60+58 => "B", 5*60+48 => "C", 4*60+19 => "D",
|
149
|
|
|
|
|
|
|
3*60+21 => "E", 2*60+38 => "F", 2*60+38 => "G", 1*60+35 => "H",
|
150
|
|
|
|
|
|
|
1*60+11 => "I", 0*60+49 => "J", 0*60+28 => "K" },
|
151
|
|
|
|
|
|
|
},
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
);
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# PADI tables seem to have a consistant transition between
|
156
|
|
|
|
|
|
|
# groups (it always takes 3 hours to get out of group A, always
|
157
|
|
|
|
|
|
|
# takes 47 minutes to get out of group B). An extra minute
|
158
|
|
|
|
|
|
|
# appears to be added to each group transition beyond the first.
|
159
|
|
|
|
|
|
|
# As such, we can dynamically generate the PADI surface table.
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# Unfortunately, this doesn't hold true for everything. Some particular
|
162
|
|
|
|
|
|
|
# entries have minor variations that then dissapear. For example, D is
|
163
|
|
|
|
|
|
|
# regular, E is not (there's a minute that's gained), and F
|
164
|
|
|
|
|
|
|
# is regular. Whether these are mistakes, intentional changes to
|
165
|
|
|
|
|
|
|
# perform some sort of fingerprinting, or intentional changes with
|
166
|
|
|
|
|
|
|
# a sound backing, I'm not sure.
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# In any case, it certainly makes testing more difficult. What's
|
169
|
|
|
|
|
|
|
# more correct, the table, or the precursor tables from which it appears
|
170
|
|
|
|
|
|
|
# to have been derived? I think for the moment we have to assume that
|
171
|
|
|
|
|
|
|
# the tables themselves are considered definitive.
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
{
|
174
|
|
|
|
|
|
|
my @surface = ( 3*60, 47, 21, 8, (7) x 2, 6, (5) x 3, (4) x 3,
|
175
|
|
|
|
|
|
|
(3) x 6, (2) x 7 );
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
foreach my $start (0..25) {
|
178
|
|
|
|
|
|
|
my $accum = 0;
|
179
|
|
|
|
|
|
|
my $end = $start;
|
180
|
|
|
|
|
|
|
while ($end >= 0) {
|
181
|
|
|
|
|
|
|
$accum += $surface[$end];
|
182
|
|
|
|
|
|
|
$SURFACE{PADI}
|
183
|
|
|
|
|
|
|
{chr(ord('A')+$start)}
|
184
|
|
|
|
|
|
|
{$accum + $start - $end} = chr(ord('A')+$end);
|
185
|
|
|
|
|
|
|
$end--;
|
186
|
|
|
|
|
|
|
}
|
187
|
|
|
|
|
|
|
}
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
}
|
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Per-row tweaks. 'E' gains a minute for moves to B and A.
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
delete $SURFACE{PADI}{E}{86}; $SURFACE{PADI}{E}{87} = "B";
|
194
|
|
|
|
|
|
|
delete $SURFACE{PADI}{E}{267}; $SURFACE{PADI}{E}{268} = "A";
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# The 'O' and 'P' rows also does a funny thing at the end.
|
197
|
|
|
|
|
|
|
delete $SURFACE{PADI}{O}{142}; $SURFACE{PADI}{O}{143} = "B";
|
198
|
|
|
|
|
|
|
delete $SURFACE{PADI}{P}{146}; $SURFACE{PADI}{P}{147} = "B";
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# The PADI tables are highly consistant, in that the residual time
|
201
|
|
|
|
|
|
|
# for each group is equal to the dive times on table 1. This means
|
202
|
|
|
|
|
|
|
# that in our representation of these tables, they are exactly the same.
|
203
|
|
|
|
|
|
|
# Rather convenient.
|
204
|
|
|
|
|
|
|
#
|
205
|
|
|
|
|
|
|
# XXX - Consider what this means for repetitive dives with no surface
|
206
|
|
|
|
|
|
|
# interval. Does it work to treat these as separate dives? Yes, it
|
207
|
|
|
|
|
|
|
# appears it does.
|
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
our %RESIDUAL = (
|
210
|
|
|
|
|
|
|
SSI => {
|
211
|
|
|
|
|
|
|
3 => [39, 88, 159, 279],
|
212
|
|
|
|
|
|
|
6 => [18, 39, 62, 88, 120, 159, 208, 279, 399],
|
213
|
|
|
|
|
|
|
9 => [12, 25, 39, 54, 70, 88, 109, 132, 159, 190],
|
214
|
|
|
|
|
|
|
12 => [ 7, 17, 25, 37, 49, 61, 73, 87, 101, 116],
|
215
|
|
|
|
|
|
|
15 => [ 6, 13, 21, 29, 38, 47, 56, 66],
|
216
|
|
|
|
|
|
|
18 => [ 5, 11, 17, 24, 30, 36, 44],
|
217
|
|
|
|
|
|
|
21 => [ 4, 9, 15, 20, 26, 31, 37],
|
218
|
|
|
|
|
|
|
24 => [ 4, 8, 13, 18, 23, 28],
|
219
|
|
|
|
|
|
|
27 => [ 3, 7, 11, 16, 20, 24],
|
220
|
|
|
|
|
|
|
30 => [ 3, 7, 10, 14, 18],
|
221
|
|
|
|
|
|
|
33 => [ 3, 6, 10, 13],
|
222
|
|
|
|
|
|
|
36 => [ 3, 6, 9],
|
223
|
|
|
|
|
|
|
39 => [ 3],
|
224
|
|
|
|
|
|
|
},
|
225
|
|
|
|
|
|
|
PADI => $LIMITS{PADI},
|
226
|
|
|
|
|
|
|
);
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# Which depths appear on our limit charts, in numerically ascending order.
|
229
|
|
|
|
|
|
|
our %LIMIT_DEPTHS = (
|
230
|
|
|
|
|
|
|
SSI => [sort {$a <=> $b} keys %{$LIMITS{SSI}}],
|
231
|
|
|
|
|
|
|
PADI => [sort {$a <=> $b} keys %{$LIMITS{PADI}}],
|
232
|
|
|
|
|
|
|
);
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Same for residual depths. For SSI there are less depths on the residual
|
235
|
|
|
|
|
|
|
# table, so we must interpret some repetitve dives as deeper than they
|
236
|
|
|
|
|
|
|
# really are.
|
237
|
|
|
|
|
|
|
our %RESIDUAL_DEPTHS = (
|
238
|
|
|
|
|
|
|
SSI => [sort {$a <=> $b} keys %{$RESIDUAL{SSI}}],
|
239
|
|
|
|
|
|
|
PADI => [sort {$a <=> $b} keys %{$RESIDUAL{PADI}}],
|
240
|
|
|
|
|
|
|
);
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head2 new
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
my $stn = SCUBA::Table::NoDeco->new(table => "SSI");
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
This class method returns a SCUBA::Table::NoDeco object. It takes
|
247
|
|
|
|
|
|
|
an optional I argument, specifying which dive table should be
248
|
|
|
|
|
|
|
used.
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
If no dive table is supplied then the module will use the default
|
251
|
|
|
|
|
|
|
SSI table. This default may change in future releases, so you should
|
252
|
|
|
|
|
|
|
not rely upon this default.
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
SSI tables are the only ones supported in the present release.
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=cut
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub new {
|
259
|
14
|
|
|
14
|
1
|
1724
|
my $class = shift;
|
260
|
14
|
|
|
|
|
31
|
my $this = {};
|
261
|
14
|
|
|
|
|
37
|
bless($this,$class);
|
262
|
14
|
|
|
|
|
58
|
$this->_init(@_);
|
263
|
13
|
|
|
|
|
37
|
return $this;
|
264
|
|
|
|
|
|
|
}
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub _init {
|
267
|
51
|
|
|
51
|
|
139
|
my ($this, %args) = @_;
|
268
|
51
|
|
100
|
|
|
225
|
$this->{table} = $args{table} || "SSI"; # Tables to use.
|
269
|
51
|
|
100
|
|
|
319
|
$this->{group} = $args{group} || ""; # Initial group.
|
270
|
51
|
|
50
|
|
|
288
|
$this->{surface} = $args{surface} || 0; # Surface time.
|
271
|
|
|
|
|
|
|
|
272
|
51
|
|
|
|
|
95
|
$this->{dive_time} = 0; # Used for consequtive dives with less than...
|
273
|
51
|
|
|
|
|
79
|
$this->{last_depth}= 0; # ... MIN_SURFACE_TIME between them.
|
274
|
|
|
|
|
|
|
|
275
|
51
|
100
|
|
|
|
337
|
croak "Non-existant table $args{table} supplied" unless exists $RESIDUAL{$this->{table}};
|
276
|
|
|
|
|
|
|
|
277
|
50
|
|
|
|
|
124
|
return $this;
|
278
|
|
|
|
|
|
|
}
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=head2 list_tables
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
my @tables = SCUBA::Table::NoDeco->list_tables();
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
This method returns a list of tables that can be selected when creating
|
285
|
|
|
|
|
|
|
a new SCUBA::Table::NoDeco object.
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=cut
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub list_tables {
|
290
|
1
|
|
|
1
|
1
|
1255
|
return keys %RESIDUAL;
|
291
|
|
|
|
|
|
|
}
|
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 max_table_depth
|
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
my $max_depth_ft = $stn->max_table_depth(units => "feet");
|
296
|
|
|
|
|
|
|
my $max_depth_mt = $stn->max_table_depth(units => "metres");
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
This method provides the maximum depth for repetitive dives provided by the
|
299
|
|
|
|
|
|
|
tables currently in use. Some tables may provide a greater depth for
|
300
|
|
|
|
|
|
|
non-reptetitve dives. This function I supply the maximum safe
|
301
|
|
|
|
|
|
|
depth (however see L). The units argument is mandatory.
|
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=cut
|
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub max_table_depth {
|
306
|
5
|
|
|
5
|
1
|
791
|
my ($this, %args) = @_;
|
307
|
5
|
100
|
|
|
|
23
|
if ($args{units} eq "metres") {
|
|
|
50
|
|
|
|
|
|
308
|
3
|
|
|
|
|
9
|
return $RESIDUAL_DEPTHS{$this->table}[-1]
|
309
|
|
|
|
|
|
|
} elsif ($args{units} eq "feet") {
|
310
|
2
|
|
|
|
|
7
|
return $RESIDUAL_DEPTHS{$this->table}[-1] / FEET2METRES;
|
311
|
|
|
|
|
|
|
} else {
|
312
|
0
|
|
|
|
|
0
|
croak "max_table_depth requires units parameter of 'metres' or 'feet'";
|
313
|
|
|
|
|
|
|
}
|
314
|
|
|
|
|
|
|
}
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub _feet2metres {
|
317
|
242
|
|
|
242
|
|
443
|
my ($this, %args) = @_;
|
318
|
|
|
|
|
|
|
|
319
|
242
|
|
|
|
|
298
|
local $Carp::CarpLevel = 1; # Auto-strip one level of calls.
|
320
|
|
|
|
|
|
|
|
321
|
242
|
100
|
100
|
|
|
1149
|
if ($args{feet} and $args{metres}) {
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
322
|
1
|
|
|
|
|
126
|
croak "Both feet and metres arguments supplied to SCUBA::Table::NoDeco";
|
323
|
|
|
|
|
|
|
} elsif ($args{feet}) {
|
324
|
68
|
100
|
|
|
|
318
|
croak "Positive depth must be supplied" if $args{feet} <= 0;
|
325
|
67
|
|
|
|
|
220
|
return $args{feet} * FEET2METRES;
|
326
|
|
|
|
|
|
|
} elsif (not $args{metres}) {
|
327
|
2
|
|
|
|
|
300
|
croak "Missing mandatory 'feet' or 'metres' parameter to SCUBA::Table::NoDeco::dive";
|
328
|
|
|
|
|
|
|
}
|
329
|
171
|
100
|
|
|
|
503
|
croak "Positive depth must be supplied" if $args{metres} <= 0;
|
330
|
170
|
|
|
|
|
409
|
return $args{metres};
|
331
|
|
|
|
|
|
|
}
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# This converts the depth given into a 'standard depth' as what appears
|
334
|
|
|
|
|
|
|
# on the chart. If we're performing a repetitive dive (ie, our group
|
335
|
|
|
|
|
|
|
# is non-empty) then we'll read from RESIDUAL_DEPTHS, otherwise for
|
336
|
|
|
|
|
|
|
# a fresh dive we'll read from LIMIT_DEPTHS. This means we may treat
|
337
|
|
|
|
|
|
|
# some shallow repetitve dives as deeper than they really are. (This
|
338
|
|
|
|
|
|
|
# errs on the side of safety).
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub _std_depth {
|
341
|
238
|
|
|
238
|
|
449
|
my ($this, %args) = @_;
|
342
|
238
|
100
|
|
|
|
536
|
die "Incorrect call to SCUBA::Table::NoDeco::_std_depth, no metres arg." unless $args{metres};
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# Find the correct table to use.
|
345
|
237
|
|
|
|
|
225
|
my @depths;
|
346
|
237
|
100
|
|
|
|
423
|
if ($this->group) {
|
347
|
56
|
|
|
|
|
62
|
@depths = @{$RESIDUAL_DEPTHS{$this->{table}}};
|
|
56
|
|
|
|
|
186
|
|
348
|
|
|
|
|
|
|
} else {
|
349
|
181
|
|
|
|
|
195
|
@depths = @{$LIMIT_DEPTHS{$this->{table}}};
|
|
181
|
|
|
|
|
741
|
|
350
|
|
|
|
|
|
|
}
|
351
|
|
|
|
|
|
|
|
352
|
237
|
|
|
|
|
400
|
foreach my $depth (@depths) {
|
353
|
1817
|
100
|
|
|
|
3721
|
return $depth if $depth >= $args{metres};
|
354
|
|
|
|
|
|
|
}
|
355
|
2
|
|
|
|
|
4
|
local $Carp::CarpLevel = 1; # Auto-strip one level of calls.
|
356
|
2
|
|
|
|
|
344
|
croak "Supplied depth $args{metres} metres is not on $this->{table} charts.";
|
357
|
|
|
|
|
|
|
}
|
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=head2 clear
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
$stn->clear();
|
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
This method resets the object to its pristine state. The table used for
|
364
|
|
|
|
|
|
|
dive calculations is retained.
|
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=cut
|
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# Clear all status, except table. This is done by clearing the underlying
|
369
|
|
|
|
|
|
|
# hash entirely and recalling _init. There is almost certainly a faster
|
370
|
|
|
|
|
|
|
# and more effective way to remove all the keys without re-creating the
|
371
|
|
|
|
|
|
|
# reference.
|
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub clear {
|
374
|
37
|
|
|
37
|
1
|
11404
|
my $table = $_[0]->table;
|
375
|
37
|
|
|
|
|
51
|
foreach my $key (keys %{$_[0]}) {
|
|
37
|
|
|
|
|
112
|
|
376
|
185
|
|
|
|
|
352
|
delete $_[0]->{$key};
|
377
|
|
|
|
|
|
|
}
|
378
|
37
|
|
|
|
|
114
|
$_[0]->_init(table => $table);
|
379
|
|
|
|
|
|
|
}
|
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head2 table
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
print "You are using the ",$stn->table," tables\n";
|
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
This method simply returns the name of the dive table being used by
|
386
|
|
|
|
|
|
|
the C object.
|
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=cut
|
389
|
|
|
|
|
|
|
|
390
|
512
|
|
|
512
|
1
|
1530
|
sub table { return $_[0]->{table}; }
|
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=head2 dive
|
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my $group = $stn->dive(feet => 60, minutes => 30);
|
395
|
|
|
|
|
|
|
my $group = $stn->dive(metres => 18, minutes => 30);
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
This method determines and sets the diver's group for the dive information
|
398
|
|
|
|
|
|
|
provided. If the dive 'falls off' the tables, then an exception is
|
399
|
|
|
|
|
|
|
returned. This method takes into account the diver's current group,
|
400
|
|
|
|
|
|
|
surface interval, and residual nitrogen time.
|
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
If the diver does not have a surface interval of at least 10 minutes,
|
403
|
|
|
|
|
|
|
this will consider the dive to be a continuation of the previous
|
404
|
|
|
|
|
|
|
dive. The dive times will be added, and the maximum depth of both
|
405
|
|
|
|
|
|
|
dives will be used to calculate the diver's group.
|
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=cut
|
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub dive {
|
410
|
61
|
|
|
61
|
1
|
5253
|
my ($this, %args) = @_;
|
411
|
|
|
|
|
|
|
|
412
|
61
|
|
100
|
|
|
207
|
$args{minutes} ||= 0;
|
413
|
|
|
|
|
|
|
|
414
|
61
|
100
|
|
|
|
385
|
croak "Positive minutes argument required for SCUBA::Table::NoDeco::dive" if $args{minutes} <= 0;
|
415
|
|
|
|
|
|
|
|
416
|
58
|
|
|
|
|
311
|
$args{metres} = $this->_feet2metres(%args);
|
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# Calculate group. This is done by looping over the list
|
419
|
|
|
|
|
|
|
# of depths until we find one equal to or greater than our
|
420
|
|
|
|
|
|
|
# current dive depth.
|
421
|
|
|
|
|
|
|
|
422
|
53
|
|
|
|
|
91
|
my $group = "A";
|
423
|
53
|
|
|
|
|
131
|
my $depth = $this->_std_depth(metres => $args{metres});
|
424
|
52
|
|
|
|
|
69
|
my $tbt;
|
425
|
|
|
|
|
|
|
|
426
|
52
|
100
|
|
|
|
110
|
if ($this->surface > $MIN_SURFACE_TIME{$this->table}) {
|
427
|
5
|
|
|
|
|
17
|
$tbt = $args{minutes} + $this->rnt(metres => $depth);
|
428
|
|
|
|
|
|
|
} else {
|
429
|
47
|
|
|
|
|
83
|
$tbt = $this->{dive_time} + $args{minutes};
|
430
|
47
|
100
|
|
|
|
122
|
$depth = $depth > $this->{last_depth} ? $depth : $this->{last_depth};
|
431
|
|
|
|
|
|
|
}
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Record dive information.
|
434
|
|
|
|
|
|
|
|
435
|
52
|
|
|
|
|
85
|
$this->{surface} = 0;
|
436
|
52
|
|
|
|
|
118
|
$this->{last_depth} = $depth;
|
437
|
52
|
|
|
|
|
75
|
$this->{dive_time} = $tbt;
|
438
|
|
|
|
|
|
|
|
439
|
52
|
|
|
|
|
62
|
foreach my $time (@{$LIMITS{$this->{table}}{$depth}}) {
|
|
52
|
|
|
|
|
150
|
|
440
|
|
|
|
|
|
|
# Now walk through all our groups until
|
441
|
|
|
|
|
|
|
# we find one with a time equal to or
|
442
|
|
|
|
|
|
|
# greater than our current time.
|
443
|
|
|
|
|
|
|
|
444
|
516
|
100
|
|
|
|
881
|
if ($time >= $tbt) {
|
445
|
51
|
|
|
|
|
92
|
$this->{group} = $group;
|
446
|
51
|
|
|
|
|
286
|
return $group;
|
447
|
|
|
|
|
|
|
}
|
448
|
465
|
|
|
|
|
537
|
$group++;
|
449
|
|
|
|
|
|
|
}
|
450
|
|
|
|
|
|
|
|
451
|
1
|
|
|
|
|
78
|
croak "SCUBA::Table::NoDeco::dive called with a depth or time not listed on the $this->{table} table";
|
452
|
|
|
|
|
|
|
}
|
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 group
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
print "You are a ",$stn->group," diver\n";
|
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
The group method returns the current letter designation representing
|
459
|
|
|
|
|
|
|
the amount of residual nitrogen present in the diver. The letter
|
460
|
|
|
|
|
|
|
designation is always upper-case. A diver with no residual nitrogen
|
461
|
|
|
|
|
|
|
has no group, represented by the empty string.
|
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=cut
|
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub group {
|
466
|
410
|
|
|
410
|
1
|
517
|
my $this = shift;
|
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# If we're below the minimum surface time for this table,
|
469
|
|
|
|
|
|
|
# we're considered to be part of the same dive.
|
470
|
410
|
100
|
|
|
|
859
|
if ($this->{surface} <= $MIN_SURFACE_TIME{$this->table}) {
|
471
|
273
|
|
|
|
|
845
|
return $this->{group};
|
472
|
|
|
|
|
|
|
}
|
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Looks like we've been off-gassing for a while. Let's
|
475
|
|
|
|
|
|
|
# find what group we're in now.
|
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# Make sure that we have an entry for our current state.
|
478
|
137
|
100
|
|
|
|
169
|
keys %{$SURFACE{$this->{table}}{$this->{group}}}
|
|
137
|
|
|
|
|
688
|
|
479
|
|
|
|
|
|
|
or croak "Incomplete table exists in $this->{table} for group $this->{group}";
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# POSSIBLE OPTIMIZATION - Cache the result of this sort,
|
482
|
|
|
|
|
|
|
# or produce a pre-sorted data strucuture.
|
483
|
|
|
|
|
|
|
|
484
|
136
|
|
|
|
|
156
|
my @times = sort {$a <=> $b} keys %{$SURFACE{$this->{table}}{$this->{group}}};
|
|
2948
|
|
|
|
|
3435
|
|
|
136
|
|
|
|
|
667
|
|
485
|
|
|
|
|
|
|
|
486
|
136
|
|
|
|
|
266
|
foreach my $time (@times) {
|
487
|
|
|
|
|
|
|
# XXX - Changed from '<' to '<=' to accomodate PADI
|
488
|
|
|
|
|
|
|
# tables. Does this break the SSI tables, or did
|
489
|
|
|
|
|
|
|
# they also have an off-by-one error? Check!
|
490
|
865
|
100
|
|
|
|
2565
|
if ($this->{surface} <= $time) {
|
491
|
133
|
|
|
|
|
740
|
return $SURFACE{$this->{table}}{$this->{group}}{$time};
|
492
|
|
|
|
|
|
|
}
|
493
|
|
|
|
|
|
|
}
|
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# If we run out of times, then they must be completely off-gassed.
|
496
|
|
|
|
|
|
|
# XXX - This is a dangerous assumption if we ever have an incomplete
|
497
|
|
|
|
|
|
|
# table.
|
498
|
3
|
|
|
|
|
15
|
return "";
|
499
|
|
|
|
|
|
|
}
|
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=head2 surface
|
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
$stn->surface(minutes => 60); # Spend an hour on surface.
|
505
|
|
|
|
|
|
|
print "Total surface time ",$stn->surface," minutes\n";
|
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
This method returns the total time of the current surface interval.
|
508
|
|
|
|
|
|
|
If the optional C argument is provided, this is added to the
|
509
|
|
|
|
|
|
|
diver's current surface interval before returning the total minutes
|
510
|
|
|
|
|
|
|
elapsed.
|
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=cut
|
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# Returns total surface time.
|
515
|
|
|
|
|
|
|
sub surface {
|
516
|
115
|
|
|
115
|
1
|
728
|
my ($this, %args) = @_;
|
517
|
115
|
100
|
100
|
|
|
466
|
if (%args and ! $args{minutes}) {
|
518
|
1
|
|
|
|
|
74
|
croak "Mandatory argument 'minutes' missing from call to surface";
|
519
|
|
|
|
|
|
|
}
|
520
|
114
|
100
|
|
|
|
347
|
$args{minutes} or return $this->{surface};
|
521
|
62
|
|
|
|
|
106
|
$this->{surface} += $args{minutes};
|
522
|
62
|
|
|
|
|
162
|
return $this->{surface};
|
523
|
|
|
|
|
|
|
}
|
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=head2 max_time
|
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
print "Your maximum time at 18 metres is ",$stn->max_time(metres => 18),"\n";
|
528
|
|
|
|
|
|
|
print "Your maximum time at 60 feet is ",$stn->max_time(feet => 60),"\n";
|
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
This calculates the maximum no-decompression time for a dive to the
|
531
|
|
|
|
|
|
|
specified depth. The diver's current group is taken into account.
|
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
If the diver cannot reach the depth supplied without breaking
|
534
|
|
|
|
|
|
|
no-decompression limits then the value '0' is returned.
|
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=cut
|
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub max_time {
|
539
|
86
|
|
|
86
|
1
|
29404
|
my ($this, %args) = @_;
|
540
|
|
|
|
|
|
|
|
541
|
86
|
|
|
|
|
209
|
$args{metres} = $this->_feet2metres(%args);
|
542
|
86
|
|
|
|
|
194
|
my $depth = $this->_std_depth(metres => $args{metres});
|
543
|
|
|
|
|
|
|
|
544
|
85
|
|
|
|
|
176
|
my $rnt = $this->rnt(metres => $depth);
|
545
|
85
|
100
|
|
|
|
207
|
return 0 unless defined($rnt);
|
546
|
|
|
|
|
|
|
|
547
|
73
|
|
|
|
|
242
|
my $max_time = $LIMITS{$this->{table}}{$depth}[-1] - $rnt;
|
548
|
73
|
50
|
|
|
|
138
|
$max_time = 0 if $max_time < 0;
|
549
|
|
|
|
|
|
|
|
550
|
73
|
|
50
|
|
|
545
|
return $max_time || 0;
|
551
|
|
|
|
|
|
|
}
|
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=head2 max_depth
|
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
print "The maximum depth you may dive is ",$stn->max_depth(units => "metres")," metres\n";
|
556
|
|
|
|
|
|
|
print "Max depth for a 20 minute dive is ",$stn->max_depth(units => "feet", minutes => 20)," feet\n";
|
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
This method returns the maximum possible depth given the diver's current
|
559
|
|
|
|
|
|
|
group, or the maximum depth available on your table if no group is set.
|
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
The method takes an optional argument (minutes), in which case the
|
562
|
|
|
|
|
|
|
maximum depth for a dive of that duration will be returned.
|
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
This method returns '0' if the diver is not allowed to make *any* dive
|
565
|
|
|
|
|
|
|
for the period of time specified.
|
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=cut
|
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
sub max_depth {
|
570
|
10
|
|
|
10
|
1
|
967
|
my ($this, %args) = @_;
|
571
|
|
|
|
|
|
|
|
572
|
10
|
|
100
|
|
|
46
|
$args{minutes} ||= 1;
|
573
|
|
|
|
|
|
|
|
574
|
10
|
100
|
|
|
|
98
|
croak "Negative minutes parameter supplied" if $args{minutes} < 0;
|
575
|
9
|
100
|
100
|
|
|
320
|
croak "Mandatory argument 'units' must be 'feet' or 'metres'"
|
|
|
|
66
|
|
|
|
|
576
|
|
|
|
|
|
|
unless ($args{units} and $args{units} eq 'feet' || $args{units} eq 'metres');
|
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
# False laziness alert! Querying max_time repeatedly does
|
579
|
|
|
|
|
|
|
# provide the correct answer, but may not be particularly
|
580
|
|
|
|
|
|
|
# efficient. There could be a better way.
|
581
|
|
|
|
|
|
|
|
582
|
7
|
|
|
|
|
7
|
foreach my $depth (reverse @{$RESIDUAL_DEPTHS{$this->table}}) {
|
|
7
|
|
|
|
|
17
|
|
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
# If the diver can spend the required amount of time
|
585
|
|
|
|
|
|
|
# at that depth, then we've got a match.
|
586
|
28
|
100
|
|
|
|
48
|
if ($this->max_time(metres => $depth) >= $args{minutes}) {
|
587
|
7
|
100
|
|
|
|
38
|
return $depth if $args{units} eq "metres";
|
588
|
2
|
|
|
|
|
12
|
return $depth / FEET2METRES;
|
589
|
|
|
|
|
|
|
}
|
590
|
|
|
|
|
|
|
}
|
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# We've dropped off the end! These tables say that the diver is
|
593
|
|
|
|
|
|
|
# not allowed to dive at all with their current nitrogen levels
|
594
|
|
|
|
|
|
|
# for that period of time.
|
595
|
|
|
|
|
|
|
|
596
|
0
|
|
|
|
|
0
|
return 0;
|
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
}
|
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=head2 rnt
|
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my $rnt = $stn->rnt(metres => 12);
|
603
|
|
|
|
|
|
|
my $rnt2 = $stn->rnt(feet => 40);
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
This method returns the I for a diver, in minutes.
|
606
|
|
|
|
|
|
|
The depth argument (in either metres or feet) is mandatory.
|
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
If the depth supplied means that the diver cannot make a no-decompression
|
609
|
|
|
|
|
|
|
dive, then an undefined value is returned.
|
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=cut
|
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
sub rnt {
|
614
|
98
|
|
|
98
|
1
|
210
|
my ($this, %args) = @_;
|
615
|
|
|
|
|
|
|
|
616
|
98
|
|
|
|
|
203
|
$args{metres} = $this->_feet2metres(%args);
|
617
|
|
|
|
|
|
|
|
618
|
98
|
|
|
|
|
214
|
my $depth = $this->_std_depth(metres => $args{metres});
|
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
# Lookup group, returning 0 RNT if they're completely free
|
621
|
|
|
|
|
|
|
# of nitrogen.
|
622
|
|
|
|
|
|
|
|
623
|
98
|
100
|
|
|
|
182
|
my $group = $this->group or return 0;
|
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
# Get the group index. A is 0, B is 1, C is 2, ...
|
626
|
31
|
|
|
|
|
48
|
my $group_idx = ord($group) - ord('A');
|
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
# Now just lookup the RNT.
|
629
|
31
|
|
|
|
|
117
|
return $RESIDUAL{$this->{table}}{$depth}[$group_idx];
|
630
|
|
|
|
|
|
|
}
|
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
1;
|
633
|
|
|
|
|
|
|
__END__
|
|