File Coverage

blib/lib/Device/LaCrosse/WS23xx/MemoryMap.pm
Criterion Covered Total %
statement 56 62 90.3
branch 37 46 80.4
condition 3 5 60.0
subroutine 6 6 100.0
pod 2 2 100.0
total 104 121 85.9


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   16 use strict;
  3         6  
  3         86  
10 3     3   16 use warnings;
  3         6  
  3         78  
11 3     3   16 use Carp;
  3         6  
  3         3500  
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 2 my $proto = shift;
117 1   33     6 my $class = ref($proto) || $proto;
118              
119 1         4 my $self = {
120             fields => {},
121             };
122              
123             # Read and parse the memory map at top.
124 1         38 for my $line (split "\n", $_memory_map) {
125 80 50       225 next if $line =~ m!^\s*$!; # Skip blank lines
126 80 50       162 next if $line =~ m!^\s*#!; # Skip comments
127              
128 80 50       400 $line =~ /^(\S+):(\S+)\s+(\S+)(\s+\[(.*?)\])?\s+(\S.*\S)/
129             or die "Internal error: Cannot grok '$line'";
130 80   100     704 $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         11 return bless $self, $class;
140             }
141              
142             sub find_field {
143 74     74 1 99 my $self = shift;
144 74         92 my $field = shift;
145              
146             # Canonicalize the requested field name, e.g.
147             # 'Indoor Temp Max' => Max_Indoor_Temperature
148 74         135 my $canonical_field = _canonical_name($field);
149 74 50       248 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             # If field doesn't exist at this point, our caller is requesting
158             # something we just can't handle. The only solution is for our caller
159             # to change the requested field name in their code. Abort noisily so
160             # they can do so.
161 74 50       147 defined $canonical_field
162             or croak "Could not translate '$field' to a known field name";
163 74 50       181 exists $self->{fields}->{lc $canonical_field}
164             or croak "'$field' is not a valid WS23xx data field";
165              
166 74         315 return $self->{fields}->{lc $canonical_field};
167             }
168              
169              
170             ################
171             #
172             # canonical_name
173             #
174             sub _canonical_name {
175 74     74   93 my $desc = shift;
176 74         85 my $canonical_name = '';
177              
178 74         157 $desc =~ s/_/ /g;
179              
180             # Min or Max?
181 74 100       583 if ($desc =~ s/\bmin(imum)?\b/ /i) {
    100          
    100          
    100          
182 17         31 $canonical_name .= 'Min_';
183             }
184             elsif ($desc =~ s/\bmax(imum)?\b/ /i) {
185 20         35 $canonical_name .= 'Max_';
186             }
187             elsif ($desc =~ s/\b(High|Low)\s*Alarm\b/ /i) {
188 14         44 $canonical_name .= ucfirst(lc($1)) . '_Alarm_';
189             }
190             elsif ($desc =~ s/\bCurrent\b/ /i) {
191             # do nothing
192             }
193              
194             # Where?
195 74 100       339 if ($desc =~ s/\b(in|out)(doors?)?(\b|$)/ /i) {
196 27         78 $canonical_name .= ucfirst(lc($1) . 'door') . '_';
197             }
198              
199             # What: Temperature, Windchill, Pressure, ...
200 74 100       482 if ($desc =~ s/\btemp(erature)?\b/ /i) {
    100          
    100          
    100          
201 17         27 $canonical_name .= 'Temperature';
202             }
203             elsif ($desc =~ s/\bPress(ure)?\b/ /i) {
204 11         36 $desc =~ s/\bair\b/ /i;
205              
206 11 100       50 if ($desc =~ s/\bAbs(olute)?\b/ /i) {
    100          
207 3         7 $canonical_name .= 'Absolute_';
208             }
209             elsif ($desc =~ s/\bRel(ative)?\b/ /i) {
210 3         4 $canonical_name .= 'Relative_';
211             }
212 11         15 $canonical_name .= 'Pressure';
213 11 100       29 if ($desc =~ s/\bCorrection\b/ /i) {
214 1         3 $canonical_name .= '_Correction';
215             }
216             }
217             elsif ($desc =~ s/\b(Humidity|Windchill|Dewpoint)\b/ /i) {
218 25         52 $canonical_name .= ucfirst(lc($1));
219 25         61 $desc =~ s/\bRel(ative)?\b/ /i;
220             }
221             elsif ($desc =~ s/\b(Rain)\b//i) {
222 7         11 $canonical_name .= "Rain";
223 7 100       42 if ($desc =~ s/\b(1|24)(\s*h(ou)?r?)?\b//i) {
    50          
224 6         16 $canonical_name .= "_$1hour";
225             }
226             elsif ($desc =~ s/\btotal\b//i) {
227 1         2 $canonical_name .= "_Total";
228             }
229             }
230             else {
231 14         59 (my $tmp = $desc) =~ s/\s+/_/g;
232 14         28 $canonical_name .= $tmp;
233             # FIXME: warn?
234             }
235              
236             # Is this a date/time field?
237 74 100       225 if ($desc =~ s!\bDate/?Time\b! !i) {
238 17         25 $canonical_name .= '_datetime';
239             }
240              
241 74 100       198 if ($desc =~ /\S/) {
242             # warn "leftover: $desc\n";
243             }
244              
245 74         97 $canonical_name =~ s/_$//;
246              
247 74         148 return $canonical_name;
248             }
249              
250             # END canonical_name
251              
252             =head1 NAME
253              
254             Device::LaCrosse::WS23xx::MemoryMap - Weather station address meanings
255              
256             =head1 SYNOPSIS
257              
258             use Device::LaCrosse::WS23xx::MemoryMap;
259              
260             my $map = Device::LaCrosse::WS23xx::MemoryMap->new();
261              
262             This is NOT intended as a user-visible module. It is
263             used internally by Device::LaCrosse::WS23xx. This interface
264             is subject to change without notice.
265              
266             =head1 DESCRIPTION
267              
268             =head1 CONSTRUCTOR
269              
270             =over 4
271              
272             =item B()
273              
274             Parses the data table contained in the module itself.
275              
276             =back
277              
278             =head1 METHODS
279              
280             =over 4
281              
282             =item B( FIELD )
283              
284             Canonicalizes B and looks it up. If found, returns a
285             hashref with the following elements:
286              
287             =over 8
288              
289             =item name
290              
291             Canonical field name.
292              
293             =item units
294              
295             Units of the measurement. See Units below.
296              
297             =item address
298              
299             Starting address of this field in the WS-23xx memory map
300              
301             =item count
302              
303             Length, in nybbles, of the field.
304              
305             =item expr
306              
307             Perl expression used to convert data nybbles to a useful form.
308              
309             =back
310              
311             If FIELD is not found, returns undef.
312              
313             =back
314              
315             =head2 Known Fields
316              
317             The known data fields -- i.e., what you can use as an
318             argument to Device::LaCrosse::WS23xx->get() -- are:
319              
320             Wind_unit
321             LCD_contrast
322             Forecast
323             Tendency
324             Indoor_Temperature C
325             Min_Indoor_Temperature C
326             Max_Indoor_Temperature C
327             Min_Indoor_Temperature_datetime time_t
328             Max_Indoor_Temperature_datetime time_t
329             Low_Alarm_Indoor_Temperature C
330             High_Alarm_Indoor_Temperature C
331             Outdoor_Temperature C
332             Min_Outdoor_Temperature C
333             Max_Outdoor_Temperature C
334             Min_Outdoor_Temperature_datetime time_t
335             Max_Outdoor_Temperature_datetime time_t
336             Low_Alarm_Outdoor_Temperature C
337             High_Alarm_Outdoor_Temperature C
338             Windchill C
339             Min_Windchill C
340             Max_Windchill C
341             Min_Windchill_datetime time_t
342             Max_Windchill_datetime time_t
343             Low_Alarm_Windchill C
344             High_Alarm_Windchill C
345             Dewpoint C
346             Min_Dewpoint C
347             Max_Dewpoint C
348             Min_Dewpoint_datetime time_t
349             Max_Dewpoint_datetime time_t
350             Low_Alarm_Dewpoint C
351             High_Alarm_Dewpoint C
352             Indoor_Humidity %
353             Min_Indoor_Humidity %
354             Max_Indoor_Humidity %
355             Min_Indoor_Humidity_datetime time_t
356             Max_Indoor_Humidity_datetime time_t
357             Low_Alarm_Indoor_Humidity %
358             High_Alarm_Indoor_Humidity %
359             Outdoor_Humidity %
360             Min_Outdoor_Humidity %
361             Max_Outdoor_Humidity %
362             Min_Outdoor_Humidity_datetime time_t
363             Max_Outdoor_Humidity_datetime time_t
364             Low_Alarm_Outdoor_Humidity %
365             High_Alarm_Outdoor_Humidity %
366             Rain_24hour mm
367             Max_Rain_24hour mm
368             Max_Rain_24hour_datetime time_t
369             Rain_1hour mm
370             Max_Rain_1hour mm
371             Max_Rain_1hour_datetime time_t
372             Rain_Total mm
373             Rain_Total_datetime time_t
374             Min__wind m/s
375             Max__wind m/s
376             Min_Date/Time_wind_datetime time_t
377             Max_Date/Time_wind_datetime time_t
378             Wind_Speed m/s
379             Wind_Direction degrees
380             Low_wind_alarm_setting m/s
381             High_wind_alarm_setting m/s
382             Connection_Type
383             Countdown_time_to_next_datBinary seconds
384             Absolute_Pressure hPa
385             Relative_Pressure hPa
386             Pressure_Correction hPa
387             Min_Absolute_Pressure hPa
388             Min_Relative_Pressure hPa
389             Max_Absolute_Pressure hPa
390             Max_Relative_Pressure hPa
391             Min_Pressure_datetime time_t
392             Max_Pressure_datetime time_t
393             Low_Alarm_Pressure hPa
394             High_Alarm_Pressure hPa
395             History_saving_interval minutes
396             Countdown_to_next_saving minutes
397             Date/Time_last_record_datetime time_t
398             Pointer_to_last_written_Record
399             Number_of_Records
400              
401             Where applicable, units are displayed to the right of each field.
402              
403             =head2 Units
404              
405             The WS-23xx devices return data in the following units:
406              
407             =over 8
408              
409             =item B
410              
411             Degrees Centigrade (temperature)
412              
413             =item B<%>
414              
415             Percent (humidity)
416              
417             =item B
418              
419             hectoPascals (pressure)
420              
421             =item B
422              
423             Meters per Second (wind speed)
424              
425             =item B
426              
427             Millimeters (rainfall)
428              
429             =item B
430              
431             Compass degrees, 0-359, (wind direction)
432              
433             =item B
434              
435             Minutes.
436              
437             =item B
438              
439             Seconds.
440              
441             =item B
442              
443             Seconds since the Epoch; you probably want to use it as
444             an argument to B().
445              
446             =back
447              
448              
449             =head1 AUTHOR
450              
451             Ed Santiago
452              
453             =head1 SEE ALSO
454              
455             L
456              
457             =cut
458              
459              
460             1;