# -*- perl -*-
#
###############################################################################
# This file is autogenerated by $0.  DO NOT EDIT!
###############################################################################
#
package Device::LaCrosse::WS23xx::MemoryMap;

use strict;
use warnings;
use Carp;

my $_memory_map = <<'END_MEMORY_MAP';
000F:1  Wind_unit                                0=m/s, 1=knots, 2=beaufort, 3=km/h, 4=mph
0266:1  LCD_contrast                             $BCD+1
026B:1  Forecast                                 0=Rainy, 1=Cloudy, 2=Sunny
026C:1  Tendency                                 0=Steady, 1=Rising, 2=Falling
0346:4  Indoor_Temperature [C]                   $BCD / 100.0 - 30
034B:4  Min_Indoor_Temperature [C]               $BCD / 100.0 - 30
0350:4  Max_Indoor_Temperature [C]               $BCD / 100.0 - 30
0354:10 Min_Indoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
035E:10 Max_Indoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
0369:4  Low_Alarm_Indoor_Temperature [C]         $BCD / 100.0 - 30
036E:4  High_Alarm_Indoor_Temperature [C]        $BCD / 100.0 - 30
0373:4  Outdoor_Temperature [C]                  $BCD / 100.0 - 30
0378:4  Min_Outdoor_Temperature [C]              $BCD / 100.0 - 30
037D:4  Max_Outdoor_Temperature [C]              $BCD / 100.0 - 30
0381:10 Min_Outdoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
038B:10 Max_Outdoor_Temperature_datetime [time_t] _time_convert($BCD, $field)
0396:4  Low_Alarm_Outdoor_Temperature [C]        $BCD / 100.0 - 30
039B:4  High_Alarm_Outdoor_Temperature [C]       $BCD / 100.0 - 30
03A0:4  Windchill [C]                            $BCD / 100.0 - 30
03A5:4  Min_Windchill [C]                        $BCD / 100.0 - 30
03AA:4  Max_Windchill [C]                        $BCD / 100.0 - 30
03AE:10 Min_Windchill_datetime [time_t]          _time_convert($BCD, $field)
03B8:10 Max_Windchill_datetime [time_t]          _time_convert($BCD, $field)
03C3:4  Low_Alarm_Windchill [C]                  $BCD / 100.0 - 30
03C8:4  High_Alarm_Windchill [C]                 $BCD / 100.0 - 30
03CE:4  Dewpoint [C]                             $BCD / 100.0 - 30
03D3:4  Min_Dewpoint [C]                         $BCD / 100.0 - 30
03D8:4  Max_Dewpoint [C]                         $BCD / 100.0 - 30
03DC:10 Min_Dewpoint_datetime [time_t]           _time_convert($BCD, $field)
03E6:10 Max_Dewpoint_datetime [time_t]           _time_convert($BCD, $field)
03F1:4  Low_Alarm_Dewpoint [C]                   $BCD / 100.0 - 30
03F6:4  High_Alarm_Dewpoint [C]                  $BCD / 100.0 - 30
03FB:2  Indoor_Humidity [%]                      $BCD
03FD:2  Min_Indoor_Humidity [%]                  $BCD
03FF:2  Max_Indoor_Humidity [%]                  $BCD
0401:10 Min_Indoor_Humidity_datetime [time_t]    _time_convert($BCD, $field)
040B:10 Max_Indoor_Humidity_datetime [time_t]    _time_convert($BCD, $field)
0415:2  Low_Alarm_Indoor_Humidity [%]            $BCD
0417:2  High_Alarm_Indoor_Humidity [%]           $BCD
0419:2  Outdoor_Humidity [%]                     $BCD
041B:2  Min_Outdoor_Humidity [%]                 $BCD
041D:2  Max_Outdoor_Humidity [%]                 $BCD
041F:10 Min_Outdoor_Humidity_datetime [time_t]   _time_convert($BCD, $field)
0429:10 Max_Outdoor_Humidity_datetime [time_t]   _time_convert($BCD, $field)
0433:2  Low_Alarm_Outdoor_Humidity [%]           $BCD
0435:2  High_Alarm_Outdoor_Humidity [%]          $BCD
0497:6  Rain_24hour [mm]                         $BCD / 100.0
049D:6  Max_Rain_24hour [mm]                     $BCD / 100.0
04A3:10 Max_Rain_24hour_datetime [time_t]        _time_convert($BCD, $field)
04B4:6  Rain_1hour [mm]                          $BCD / 100.0
04BA:6  Max_Rain_1hour [mm]                      $BCD / 100.0
04C0:10 Max_Rain_1hour_datetime [time_t]         _time_convert($BCD, $field)
04D2:6  Rain_Total [mm]                          $BCD / 100.0
04D8:10 Rain_Total_datetime [time_t]             _time_convert($BCD, $field)
04EE:4  Min__wind [m/s]                          hex($BCD) / 360.0
04F4:4  Max__wind [m/s]                          hex($BCD) / 360.0
04F8:10 Min_Date/Time_wind_datetime [time_t]     _time_convert($BCD, $field)
0502:10 Max_Date/Time_wind_datetime [time_t]     _time_convert($BCD, $field)
0529:3  Wind_Speed [m/s]                         hex($BCD) / 10.0
052C:1  Wind_Direction [degrees]                 hex($BCD) * 22.5
0533:3  Low_wind_alarm_setting [m/s]             $BCD / 10.0
0538:3  High_wind_alarm_setting [m/s]            $BCD / 10.0
054D:1  Connection_Type                          0=Cable, 3=lost, F=Wireless
054F:2  Countdown_time_to_next_datBinary [seconds] hex($BCD) / 2.0
05D8:5  Absolute_Pressure [hPa]                  $BCD / 10.0
05E2:5  Relative_Pressure [hPa]                  $BCD / 10.0
05EC:5  Pressure_Correction [hPa]                $BCD / 10.0- 1000
05F6:5  Min_Absolute_Pressure [hPa]              $BCD / 10.0
0600:5  Min_Relative_Pressure [hPa]              $BCD / 10.0
060A:5  Max_Absolute_Pressure [hPa]              $BCD / 10.0
0614:5  Max_Relative_Pressure [hPa]              $BCD / 10.0
061E:10 Min_Pressure_datetime [time_t]           _time_convert($BCD, $field)
0628:10 Max_Pressure_datetime [time_t]           _time_convert($BCD, $field)
063C:5  Low_Alarm_Pressure [hPa]                 $BCD / 10.0
0650:5  High_Alarm_Pressure [hPa]                $BCD / 10.0
06B2:3  History_saving_interval [minutes]        hex($BCD)
06B5:3  Countdown_to_next_saving [minutes]       hex($BCD)
06B8:10 Date/Time_last_record_datetime [time_t]  _time_convert($BCD, $field)
06C2:2  Pointer_to_last_written_Record           hex($BCD)
06C4:2  Number_of_Records                        hex($BCD)
END_MEMORY_MAP

my $Canonical = <<'END_CANONICAL';
Max		Maximum | Maximal
Min		Minimum | Minimal

Indoor		Indoors  | Inside  | In
Outdoor		Outdoors | Outside | Out

Pressure	Press | Air Pressure
Temperature	Temp
Humidity	Hum   | Relative Humidity | Rel Humidity
Windchill	Wind Chill
Wind_Speed	Wind Speed | Windspeed
Dewpoint	Dew Point
Rain		Rainfall  | Rain
END_CANONICAL




sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my $self = {
	fields => {},
    };

    # Read and parse the memory map at top.
    for my $line (split "\n", $_memory_map) {
	next if $line =~ m!^\s*$!;		# Skip blank lines
	next if $line =~ m!^\s*#!;		# Skip comments

	$line =~ /^(\S+):(\S+)\s+(\S+)(\s+\[(.*?)\])?\s+(\S.*\S)/
	  or die "Internal error: Cannot grok '$line'";
	$self->{fields}{lc $3} = {
	    address => hex($1),
	    count   => $2,
	    name  => $3,
	    units => $5 || '?',
	    expr  => $6,
	};
    }

    return bless $self, $class;
}

sub find_field {
    my $self = shift;
    my $field = shift;

    # Canonicalize the requested field name, e.g.
    # 'Indoor Temp Max' => Max_Indoor_Temperature
    my $canonical_field = _canonical_name($field);
    if (! exists $self->{fields}->{lc $canonical_field}) {
	(my $re = lc $field) =~ s/[ _]+/.*/g;
	my @match = grep { /$re/i } keys %{$self->{fields}};
	if (@match == 1) {
	    $canonical_field = $match[0];
	}
    }

    # Get the field info.
    # FIXME: If there's no such field, return undef instead of croaking?
    return $self->{fields}->{lc $canonical_field}
      or croak "No such value, '$field'";
}


################
#
# canonical_name
#
sub _canonical_name {
    my $desc = shift;
    my $canonical_name = '';

    $desc =~ s/_/ /g;

    # Min or Max?
    if ($desc =~ s/\bmin(imum)?\b/ /i) {
	$canonical_name .= 'Min_';
    }
    elsif ($desc =~ s/\bmax(imum)?\b/ /i) {
	$canonical_name .= 'Max_';
    }
    elsif ($desc =~ s/\b(High|Low)\s*Alarm\b/ /i) {
	$canonical_name .= ucfirst(lc($1)) . '_Alarm_';
    }
    elsif ($desc =~ s/\bCurrent\b/ /i) {
	# do nothing
    }

    # Where?
    if ($desc =~ s/\b(in|out)(doors?)?(\b|$)/ /i) {
	$canonical_name .= ucfirst(lc($1) . 'door') . '_';
    }

    # What: Temperature, Windchill, Pressure, ...
    if ($desc =~ s/\btemp(erature)?\b/ /i) {
	$canonical_name .= 'Temperature';
    }
    elsif ($desc =~ s/\bPress(ure)?\b/ /i) {
	$desc =~ s/\bair\b/ /i;

	if ($desc =~ s/\bAbs(olute)?\b/ /i) {
	    $canonical_name .= 'Absolute_';
	}
	elsif ($desc =~ s/\bRel(ative)?\b/ /i) {
	    $canonical_name .= 'Relative_';
	}
	$canonical_name .= 'Pressure';
	if ($desc =~ s/\bCorrection\b/ /i) {
	    $canonical_name .= '_Correction';
	}
    }
    elsif ($desc =~ s/\b(Humidity|Windchill|Dewpoint)\b/ /i) {
	$canonical_name .= ucfirst(lc($1));
	$desc =~ s/\bRel(ative)?\b/ /i;
    }
    elsif ($desc =~ s/\b(Rain)\b//i) {
	$canonical_name .= "Rain";
	if ($desc =~ s/\b(1|24)(\s*h(ou)?r?)?\b//i) {
	    $canonical_name .= "_$1hour";
	}
	elsif ($desc =~ s/\btotal\b//i) {
	    $canonical_name .= "_Total";
	}
    }
    else {
	(my $tmp = $desc) =~ s/\s+/_/g;
	$canonical_name .= $tmp;
	# FIXME: warn?
    }

    # Is this a date/time field?
    if ($desc =~ s!\bDate/?Time\b! !i) {
	$canonical_name .= '_datetime';
    }

    if ($desc =~ /\S/) {
#	warn "leftover: $desc\n";
    }

    $canonical_name =~ s/_$//;

    return $canonical_name;
}

# END   canonical_name

=head1 NAME

Device::LaCrosse::WS23xx::MemoryMap - Weather station address meanings

=head1 SYNOPSIS

    use Device::LaCrosse::WS23xx::MemoryMap;

    my $map = Device::LaCrosse::WS23xx::MemoryMap->new();

This is NOT intended as a user-visible module.  It is
used internally by Device::LaCrosse::WS23xx.  This interface
is subject to change without notice.

=head1  DESCRIPTION

=head1  CONSTRUCTOR

=over 4

=item B<new>()

Parses the data table contained in the module itself.

=back

=head1  METHODS

=over 4

=item   B<find_field>( FIELD )

Canonicalizes B<FIELD> and looks it up.  If found, returns a
hashref with the following elements:

=over 8

=item	name

Canonical field name.

=item	units

Units of the measurement.  See Units below.

=item	address

Starting address of this field in the WS-23xx memory map

=item	count

Length, in nybbles, of the field.

=item	expr

Perl expression used to convert data nybbles to a useful form.

=back

If FIELD is not found, returns undef.

=back

=head2	Known Fields

The known data fields -- i.e., what you can use as an
argument to Device::LaCrosse::WS23xx->get() -- are:

   Wind_unit
   LCD_contrast
   Forecast
   Tendency
   Indoor_Temperature                      C
   Min_Indoor_Temperature                  C
   Max_Indoor_Temperature                  C
   Min_Indoor_Temperature_datetime         time_t
   Max_Indoor_Temperature_datetime         time_t
   Low_Alarm_Indoor_Temperature            C
   High_Alarm_Indoor_Temperature           C
   Outdoor_Temperature                     C
   Min_Outdoor_Temperature                 C
   Max_Outdoor_Temperature                 C
   Min_Outdoor_Temperature_datetime        time_t
   Max_Outdoor_Temperature_datetime        time_t
   Low_Alarm_Outdoor_Temperature           C
   High_Alarm_Outdoor_Temperature          C
   Windchill                               C
   Min_Windchill                           C
   Max_Windchill                           C
   Min_Windchill_datetime                  time_t
   Max_Windchill_datetime                  time_t
   Low_Alarm_Windchill                     C
   High_Alarm_Windchill                    C
   Dewpoint                                C
   Min_Dewpoint                            C
   Max_Dewpoint                            C
   Min_Dewpoint_datetime                   time_t
   Max_Dewpoint_datetime                   time_t
   Low_Alarm_Dewpoint                      C
   High_Alarm_Dewpoint                     C
   Indoor_Humidity                         %
   Min_Indoor_Humidity                     %
   Max_Indoor_Humidity                     %
   Min_Indoor_Humidity_datetime            time_t
   Max_Indoor_Humidity_datetime            time_t
   Low_Alarm_Indoor_Humidity               %
   High_Alarm_Indoor_Humidity              %
   Outdoor_Humidity                        %
   Min_Outdoor_Humidity                    %
   Max_Outdoor_Humidity                    %
   Min_Outdoor_Humidity_datetime           time_t
   Max_Outdoor_Humidity_datetime           time_t
   Low_Alarm_Outdoor_Humidity              %
   High_Alarm_Outdoor_Humidity             %
   Rain_24hour                             mm
   Max_Rain_24hour                         mm
   Max_Rain_24hour_datetime                time_t
   Rain_1hour                              mm
   Max_Rain_1hour                          mm
   Max_Rain_1hour_datetime                 time_t
   Rain_Total                              mm
   Rain_Total_datetime                     time_t
   Min__wind                               m/s
   Max__wind                               m/s
   Min_Date/Time_wind_datetime             time_t
   Max_Date/Time_wind_datetime             time_t
   Wind_Speed                              m/s
   Wind_Direction                          degrees
   Low_wind_alarm_setting                  m/s
   High_wind_alarm_setting                 m/s
   Connection_Type
   Countdown_time_to_next_datBinary        seconds
   Absolute_Pressure                       hPa
   Relative_Pressure                       hPa
   Pressure_Correction                     hPa
   Min_Absolute_Pressure                   hPa
   Min_Relative_Pressure                   hPa
   Max_Absolute_Pressure                   hPa
   Max_Relative_Pressure                   hPa
   Min_Pressure_datetime                   time_t
   Max_Pressure_datetime                   time_t
   Low_Alarm_Pressure                      hPa
   High_Alarm_Pressure                     hPa
   History_saving_interval                 minutes
   Countdown_to_next_saving                minutes
   Date/Time_last_record_datetime          time_t
   Pointer_to_last_written_Record
   Number_of_Records

Where applicable, units are displayed to the right of each field.

=head2	Units

The WS-23xx devices return data in the following units:

=over 8

=item	B<C>

Degrees Centigrade (temperature)

=item	B<%>

Percent (humidity)

=item	B<hPa>

hectoPascals (pressure)

=item	B<m/s>

Meters per Second (wind speed)

=item	B<mm>

Millimeters (rainfall)

=item	B<degrees>

Compass degrees, 0-359, (wind direction)

=item	B<minutes>

Minutes.

=item	B<seconds>

Seconds.

=item	B<time_t>

Seconds since the Epoch; you probably want to use it as
an argument to B<localtime>().

=back


=head1  AUTHOR

Ed Santiago <esm@cpan.org>

=head1	SEE ALSO

L<Device::LaCrosse::WS23xx>

=cut


1;
