File Coverage

blib/lib/Device/LaCrosse/WS23xx/MemoryMap.pm
Criterion Covered Total %
statement 54 60 90.0
branch 35 44 79.5
condition 3 5 60.0
subroutine 6 6 100.0
pod 2 2 100.0
total 100 117 85.4


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             ###############################################################################
4             # This file is autogenerated by $0. DO NOT EDIT!
5             ###############################################################################
6             #
7             package Device::LaCrosse::WS23xx::MemoryMap;
8              
9 3     3   22 use strict;
  3         7  
  3         127  
10 3     3   18 use warnings;
  3         7  
  3         99  
11 3     3   17 use Carp;
  3         6  
  3         19126  
12              
13             my $_memory_map = <<'END_MEMORY_MAP';
14             000F:1 Wind_unit 0=m/s, 1=knots, 2=beaufort, 3=km/h, 4=mph
15             0266:1 LCD_contrast $BCD+1
16             026B:1 Forecast 0=Rainy, 1=Cloudy, 2=Sunny
17             026C:1 Tendency 0=Steady, 1=Rising, 2=Falling
18             0346:4 Indoor_Temperature [C] $BCD / 100.0 - 30
19             034B:4 Min_Indoor_Temperature [C] $BCD / 100.0 - 30
20             0350:4 Max_Indoor_Temperature [C] $BCD / 100.0 - 30
21             0354:10 Min_Indoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
22             035E:10 Max_Indoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
23             0369:4 Low_Alarm_Indoor_Temperature [C] $BCD / 100.0 - 30
24             036E:4 High_Alarm_Indoor_Temperature [C] $BCD / 100.0 - 30
25             0373:4 Outdoor_Temperature [C] $BCD / 100.0 - 30
26             0378:4 Min_Outdoor_Temperature [C] $BCD / 100.0 - 30
27             037D:4 Max_Outdoor_Temperature [C] $BCD / 100.0 - 30
28             0381:10 Min_Outdoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
29             038B:10 Max_Outdoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
30             0396:4 Low_Alarm_Outdoor_Temperature [C] $BCD / 100.0 - 30
31             039B:4 High_Alarm_Outdoor_Temperature [C] $BCD / 100.0 - 30
32             03A0:4 Windchill [C] $BCD / 100.0 - 30
33             03A5:4 Min_Windchill [C] $BCD / 100.0 - 30
34             03AA:4 Max_Windchill [C] $BCD / 100.0 - 30
35             03AE:10 Min_Windchill_datetime [time_t] _time_convert($BCD, $field)
36             03B8:10 Max_Windchill_datetime [time_t] _time_convert($BCD, $field)
37             03C3:4 Low_Alarm_Windchill [C] $BCD / 100.0 - 30
38             03C8:4 High_Alarm_Windchill [C] $BCD / 100.0 - 30
39             03CE:4 Dewpoint [C] $BCD / 100.0 - 30
40             03D3:4 Min_Dewpoint [C] $BCD / 100.0 - 30
41             03D8:4 Max_Dewpoint [C] $BCD / 100.0 - 30
42             03DC:10 Min_Dewpoint_datetime [time_t] _time_convert($BCD, $field)
43             03E6:10 Max_Dewpoint_datetime [time_t] _time_convert($BCD, $field)
44             03F1:4 Low_Alarm_Dewpoint [C] $BCD / 100.0 - 30
45             03F6:4 High_Alarm_Dewpoint [C] $BCD / 100.0 - 30
46             03FB:2 Indoor_Humidity [%] $BCD
47             03FD:2 Min_Indoor_Humidity [%] $BCD
48             03FF:2 Max_Indoor_Humidity [%] $BCD
49             0401:10 Min_Indoor_Humidity_datetime [time_t] _time_convert($BCD, $field)
50             040B:10 Max_Indoor_Humidity_datetime [time_t] _time_convert($BCD, $field)
51             0415:2 Low_Alarm_Indoor_Humidity [%] $BCD
52             0417:2 High_Alarm_Indoor_Humidity [%] $BCD
53             0419:2 Outdoor_Humidity [%] $BCD
54             041B:2 Min_Outdoor_Humidity [%] $BCD
55             041D:2 Max_Outdoor_Humidity [%] $BCD
56             041F:10 Min_Outdoor_Humidity_datetime [time_t] _time_convert($BCD, $field)
57             0429:10 Max_Outdoor_Humidity_datetime [time_t] _time_convert($BCD, $field)
58             0433:2 Low_Alarm_Outdoor_Humidity [%] $BCD
59             0435:2 High_Alarm_Outdoor_Humidity [%] $BCD
60             0497:6 Rain_24hour [mm] $BCD / 100.0
61             049D:6 Max_Rain_24hour [mm] $BCD / 100.0
62             04A3:10 Max_Rain_24hour_datetime [time_t] _time_convert($BCD, $field)
63             04B4:6 Rain_1hour [mm] $BCD / 100.0
64             04BA:6 Max_Rain_1hour [mm] $BCD / 100.0
65             04C0:10 Max_Rain_1hour_datetime [time_t] _time_convert($BCD, $field)
66             04D2:6 Rain_Total [mm] $BCD / 100.0
67             04D8:10 Rain_Total_datetime [time_t] _time_convert($BCD, $field)
68             04EE:4 Min__wind [m/s] hex($BCD) / 360.0
69             04F4:4 Max__wind [m/s] hex($BCD) / 360.0
70             04F8:10 Min_Date/Time_wind_datetime [time_t] _time_convert($BCD, $field)
71             0502:10 Max_Date/Time_wind_datetime [time_t] _time_convert($BCD, $field)
72             0529:3 Wind_Speed [m/s] hex($BCD) / 10.0
73             052C:1 Wind_Direction [degrees] hex($BCD) * 22.5
74             0533:3 Low_wind_alarm_setting [m/s] $BCD / 10.0
75             0538:3 High_wind_alarm_setting [m/s] $BCD / 10.0
76             054D:1 Connection_Type 0=Cable, 3=lost, F=Wireless
77             054F:2 Countdown_time_to_next_datBinary [seconds] hex($BCD) / 2.0
78             05D8:5 Absolute_Pressure [hPa] $BCD / 10.0
79             05E2:5 Relative_Pressure [hPa] $BCD / 10.0
80             05EC:5 Pressure_Correction [hPa] $BCD / 10.0- 1000
81             05F6:5 Min_Absolute_Pressure [hPa] $BCD / 10.0
82             0600:5 Min_Relative_Pressure [hPa] $BCD / 10.0
83             060A:5 Max_Absolute_Pressure [hPa] $BCD / 10.0
84             0614:5 Max_Relative_Pressure [hPa] $BCD / 10.0
85             061E:10 Min_Pressure_datetime [time_t] _time_convert($BCD, $field)
86             0628:10 Max_Pressure_datetime [time_t] _time_convert($BCD, $field)
87             063C:5 Low_Alarm_Pressure [hPa] $BCD / 10.0
88             0650:5 High_Alarm_Pressure [hPa] $BCD / 10.0
89             06B2:3 History_saving_interval [minutes] hex($BCD)
90             06B5:3 Countdown_to_next_saving [minutes] hex($BCD)
91             06B8:10 Date/Time_last_record_datetime [time_t] _time_convert($BCD, $field)
92             06C2:2 Pointer_to_last_written_Record hex($BCD)
93             06C4:2 Number_of_Records hex($BCD)
94             END_MEMORY_MAP
95              
96             my $Canonical = <<'END_CANONICAL';
97             Max Maximum | Maximal
98             Min Minimum | Minimal
99              
100             Indoor Indoors | Inside | In
101             Outdoor Outdoors | Outside | Out
102              
103             Pressure Press | Air Pressure
104             Temperature Temp
105             Humidity Hum | Relative Humidity | Rel Humidity
106             Windchill Wind Chill
107             Wind_Speed Wind Speed | Windspeed
108             Dewpoint Dew Point
109             Rain Rainfall | Rain
110             END_CANONICAL
111              
112              
113              
114              
115             sub new {
116 1     1 1 4 my $proto = shift;
117 1   33     6 my $class = ref($proto) || $proto;
118              
119 1         3 my $self = {
120             fields => {},
121             };
122              
123             # Read and parse the memory map at top.
124 1         33 for my $line (split "\n", $_memory_map) {
125 80 50       320 next if $line =~ m!^\s*$!; # Skip blank lines
126 80 50       175 next if $line =~ m!^\s*#!; # Skip comments
127              
128 80 50       433 $line =~ /^(\S+):(\S+)\s+(\S+)(\s+\[(.*?)\])?\s+(\S.*\S)/
129             or die "Internal error: Cannot grok '$line'";
130 80   100     934 $self->{fields}{lc $3} = {
131             address => hex($1),
132             count => $2,
133             name => $3,
134             units => $5 || '?',
135             expr => $6,
136             };
137             }
138              
139 1         21 return bless $self, $class;
140             }
141              
142             sub find_field {
143 74     74 1 89 my $self = shift;
144 74         131 my $field = shift;
145              
146             # Canonicalize the requested field name, e.g.
147             # 'Indoor Temp Max' => Max_Indoor_Temperature
148 74         180 my $canonical_field = _canonical_name($field);
149 74 50       394 if (! exists $self->{fields}->{lc $canonical_field}) {
150 0         0 (my $re = lc $field) =~ s/[ _]+/.*/g;
151 0         0 my @match = grep { /$re/i } keys %{$self->{fields}};
  0         0  
  0         0  
152 0 0       0 if (@match == 1) {
153 0         0 $canonical_field = $match[0];
154             }
155             }
156              
157             # Get the field info.
158             # FIXME: If there's no such field, return undef instead of croaking?
159 74 0       487 return $self->{fields}->{lc $canonical_field}
160             or croak "No such value, '$field'";
161             }
162              
163              
164             ################
165             #
166             # canonical_name
167             #
168             sub _canonical_name {
169 74     74   105 my $desc = shift;
170 74         610 my $canonical_name = '';
171              
172 74         235 $desc =~ s/_/ /g;
173              
174             # Min or Max?
175 74 100       723 if ($desc =~ s/\bmin(imum)?\b/ /i) {
    100          
    100          
    100          
176 17         33 $canonical_name .= 'Min_';
177             }
178             elsif ($desc =~ s/\bmax(imum)?\b/ /i) {
179 20         48 $canonical_name .= 'Max_';
180             }
181             elsif ($desc =~ s/\b(High|Low)\s*Alarm\b/ /i) {
182 14         54 $canonical_name .= ucfirst(lc($1)) . '_Alarm_';
183             }
184             elsif ($desc =~ s/\bCurrent\b/ /i) {
185             # do nothing
186             }
187              
188             # Where?
189 74 100       548 if ($desc =~ s/\b(in|out)(doors?)?(\b|$)/ /i) {
190 27         95 $canonical_name .= ucfirst(lc($1) . 'door') . '_';
191             }
192              
193             # What: Temperature, Windchill, Pressure, ...
194 74 100       603 if ($desc =~ s/\btemp(erature)?\b/ /i) {
    100          
    100          
    100          
195 17         29 $canonical_name .= 'Temperature';
196             }
197             elsif ($desc =~ s/\bPress(ure)?\b/ /i) {
198 11         40 $desc =~ s/\bair\b/ /i;
199              
200 11 100       58 if ($desc =~ s/\bAbs(olute)?\b/ /i) {
    100          
201 3         7 $canonical_name .= 'Absolute_';
202             }
203             elsif ($desc =~ s/\bRel(ative)?\b/ /i) {
204 3         6 $canonical_name .= 'Relative_';
205             }
206 11         17 $canonical_name .= 'Pressure';
207 11 100       31 if ($desc =~ s/\bCorrection\b/ /i) {
208 1         4 $canonical_name .= '_Correction';
209             }
210             }
211             elsif ($desc =~ s/\b(Humidity|Windchill|Dewpoint)\b/ /i) {
212 25         65 $canonical_name .= ucfirst(lc($1));
213 25         64 $desc =~ s/\bRel(ative)?\b/ /i;
214             }
215             elsif ($desc =~ s/\b(Rain)\b//i) {
216 7         12 $canonical_name .= "Rain";
217 7 100       43 if ($desc =~ s/\b(1|24)(\s*h(ou)?r?)?\b//i) {
    50          
218 6         20 $canonical_name .= "_$1hour";
219             }
220             elsif ($desc =~ s/\btotal\b//i) {
221 1         3 $canonical_name .= "_Total";
222             }
223             }
224             else {
225 14         69 (my $tmp = $desc) =~ s/\s+/_/g;
226 14         28 $canonical_name .= $tmp;
227             # FIXME: warn?
228             }
229              
230             # Is this a date/time field?
231 74 100       248 if ($desc =~ s!\bDate/?Time\b! !i) {
232 17         31 $canonical_name .= '_datetime';
233             }
234              
235 74 100       474 if ($desc =~ /\S/) {
236             # warn "leftover: $desc\n";
237             }
238              
239 74         117 $canonical_name =~ s/_$//;
240              
241 74         161 return $canonical_name;
242             }
243              
244             # END canonical_name
245              
246             =head1 NAME
247              
248             Device::LaCrosse::WS23xx::MemoryMap - Weather station address meanings
249              
250             =head1 SYNOPSIS
251              
252             use Device::LaCrosse::WS23xx::MemoryMap;
253              
254             my $map = Device::LaCrosse::WS23xx::MemoryMap->new();
255              
256             This is NOT intended as a user-visible module. It is
257             used internally by Device::LaCrosse::WS23xx. This interface
258             is subject to change without notice.
259              
260             =head1 DESCRIPTION
261              
262             =head1 CONSTRUCTOR
263              
264             =over 4
265              
266             =item B()
267              
268             Parses the data table contained in the module itself.
269              
270             =back
271              
272             =head1 METHODS
273              
274             =over 4
275              
276             =item B( FIELD )
277              
278             Canonicalizes B and looks it up. If found, returns a
279             hashref with the following elements:
280              
281             =over 8
282              
283             =item name
284              
285             Canonical field name.
286              
287             =item units
288              
289             Units of the measurement. See Units below.
290              
291             =item address
292              
293             Starting address of this field in the WS-23xx memory map
294              
295             =item count
296              
297             Length, in nybbles, of the field.
298              
299             =item expr
300              
301             Perl expression used to convert data nybbles to a useful form.
302              
303             =back
304              
305             If FIELD is not found, returns undef.
306              
307             =back
308              
309             =head2 Known Fields
310              
311             The known data fields -- i.e., what you can use as an
312             argument to Device::LaCrosse::WS23xx->get() -- are:
313              
314             Wind_unit
315             LCD_contrast
316             Forecast
317             Tendency
318             Indoor_Temperature C
319             Min_Indoor_Temperature C
320             Max_Indoor_Temperature C
321             Min_Indoor_Temperature_datetime time_t
322             Max_Indoor_Temperature_datetime time_t
323             Low_Alarm_Indoor_Temperature C
324             High_Alarm_Indoor_Temperature C
325             Outdoor_Temperature C
326             Min_Outdoor_Temperature C
327             Max_Outdoor_Temperature C
328             Min_Outdoor_Temperature_datetime time_t
329             Max_Outdoor_Temperature_datetime time_t
330             Low_Alarm_Outdoor_Temperature C
331             High_Alarm_Outdoor_Temperature C
332             Windchill C
333             Min_Windchill C
334             Max_Windchill C
335             Min_Windchill_datetime time_t
336             Max_Windchill_datetime time_t
337             Low_Alarm_Windchill C
338             High_Alarm_Windchill C
339             Dewpoint C
340             Min_Dewpoint C
341             Max_Dewpoint C
342             Min_Dewpoint_datetime time_t
343             Max_Dewpoint_datetime time_t
344             Low_Alarm_Dewpoint C
345             High_Alarm_Dewpoint C
346             Indoor_Humidity %
347             Min_Indoor_Humidity %
348             Max_Indoor_Humidity %
349             Min_Indoor_Humidity_datetime time_t
350             Max_Indoor_Humidity_datetime time_t
351             Low_Alarm_Indoor_Humidity %
352             High_Alarm_Indoor_Humidity %
353             Outdoor_Humidity %
354             Min_Outdoor_Humidity %
355             Max_Outdoor_Humidity %
356             Min_Outdoor_Humidity_datetime time_t
357             Max_Outdoor_Humidity_datetime time_t
358             Low_Alarm_Outdoor_Humidity %
359             High_Alarm_Outdoor_Humidity %
360             Rain_24hour mm
361             Max_Rain_24hour mm
362             Max_Rain_24hour_datetime time_t
363             Rain_1hour mm
364             Max_Rain_1hour mm
365             Max_Rain_1hour_datetime time_t
366             Rain_Total mm
367             Rain_Total_datetime time_t
368             Min__wind m/s
369             Max__wind m/s
370             Min_Date/Time_wind_datetime time_t
371             Max_Date/Time_wind_datetime time_t
372             Wind_Speed m/s
373             Wind_Direction degrees
374             Low_wind_alarm_setting m/s
375             High_wind_alarm_setting m/s
376             Connection_Type
377             Countdown_time_to_next_datBinary seconds
378             Absolute_Pressure hPa
379             Relative_Pressure hPa
380             Pressure_Correction hPa
381             Min_Absolute_Pressure hPa
382             Min_Relative_Pressure hPa
383             Max_Absolute_Pressure hPa
384             Max_Relative_Pressure hPa
385             Min_Pressure_datetime time_t
386             Max_Pressure_datetime time_t
387             Low_Alarm_Pressure hPa
388             High_Alarm_Pressure hPa
389             History_saving_interval minutes
390             Countdown_to_next_saving minutes
391             Date/Time_last_record_datetime time_t
392             Pointer_to_last_written_Record
393             Number_of_Records
394              
395             Where applicable, units are displayed to the right of each field.
396              
397             =head2 Units
398              
399             The WS-23xx devices return data in the following units:
400              
401             =over 8
402              
403             =item B
404              
405             Degrees Centigrade (temperature)
406              
407             =item B<%>
408              
409             Percent (humidity)
410              
411             =item B
412              
413             hectoPascals (pressure)
414              
415             =item B
416              
417             Meters per Second (wind speed)
418              
419             =item B
420              
421             Millimeters (rainfall)
422              
423             =item B
424              
425             Compass degrees, 0-359, (wind direction)
426              
427             =item B
428              
429             Minutes.
430              
431             =item B
432              
433             Seconds.
434              
435             =item B
436              
437             Seconds since the Epoch; you probably want to use it as
438             an argument to B().
439              
440             =back
441              
442              
443             =head1 AUTHOR
444              
445             Ed Santiago
446              
447             =head1 SEE ALSO
448              
449             L
450              
451             =cut
452              
453              
454             1;