edu.hawaii.soest.kilonalu.dvp2.DavisWxParser.java Source code

Java tutorial

Introduction

Here is the source code for edu.hawaii.soest.kilonalu.dvp2.DavisWxParser.java

Source

/**
 *  Copyright: 2009 Regents of the University of Hawaii and the
 *             School of Ocean and Earth Science and Technology
 *    Purpose: A class that represents a single LOOP sample of data produced by
 *            a Davis Scientific Vantage Pro 2 Weather station as described in
 *            the Davis Vantage Serial Protocol document (Vantage Pro and 
 *            Vantage Pro2 Serial Support 2.2 - 01-25-2005)
 *   Authors: Christopher Jones
 *
 * $HeadURL$
 * $LastChangedDate$
 * $LastChangedBy$
 * $LastChangedRevision$
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package edu.hawaii.soest.kilonalu.dvp2;

import edu.hawaii.soest.kilonalu.dvp2.DavisWxParser;

import java.io.File;
import java.io.FileInputStream;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.codec.binary.Hex;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.PropertyConfigurator;

/**
 *  A class that represents a single Rev "B" sample of data produced by
 *  an Davis Scientific Vantage Pro 2 Weather station in the
 *  default LOOP format.  This class includes fields for each of the stated
 *  byte fields in the LOOP format documentation, along with getter and setter
 *  methods for accessing those fields.
 * 
 *  Note: LOOP Multi-byte binary values are generally stored and sent least 
 *  significant byte first. Negative numbers use 2's complement notation. 
 *  CRC values are sent and received most significant byte first.
 */
public class DavisWxParser {

    /**
     * The default log configuration file location
     */
    private final String DEFAULT_LOG_CONFIGURATION_FILE = "lib/log4j.properties";

    /**
     * The log configuration file location
     */
    private String logConfigurationFile = DEFAULT_LOG_CONFIGURATION_FILE;

    /**
     * The Logger instance used to log system messages 
     */
    private static Logger logger = Logger.getLogger(DavisWxParser.class);

    /*
     *  A field that stores the binary LOOP packet input as a ByteBuffer
     */
    private ByteBuffer packetBuffer = ByteBuffer.allocate(99);

    /**
     *  A field that stores the LOOP packet identifier (three bytes) in a ByteBuffer
     *  which contains the three letters 'LOO'.  The 'P' slot is now used in Rev 'B'
     *  packets to store the barometric trend.
     */
    private ByteBuffer loopID = ByteBuffer.allocate(3);

    /**
     *  A field that stores the signed byte that indicates the current 3-hour 
     *  barometer trend.  It is one of these values:
     *
     *  -60 = Falling Rapidly  = 196 (as an unsigned byte) 
     *  -20 = Falling Slowly   = 236 (as an unsigned byte) 
     *    0 = Steady 
     *   20 = Rising Slowly 
     *   60 = Rising Rapidly 
     *   80 = ASCII "P" = Rev A firmware, no trend info is available 
     *  Any other value means that the Vantage does not have the 3 
     *  hours of bar data needed to determine the bar trend. 
         
     *  */
    private ByteBuffer barTrend = ByteBuffer.allocate(1);

    /**
     *  A field that stores the LOOP packet type (one byte) in a ByteBuffer
     *  which contains the value zero.  In the future Davis may define new LOOP 
     *  packet formats and assign a different value to this field.  
     */
    private ByteBuffer packetType = ByteBuffer.allocate(1);

    /**
     *  A field that stores the next record value (two bytes) in a ByteBuffer
     *  which is the location in the archive memory where the next data packet 
     *  will be written. This can be monitored to detect when a new record is 
     *  created. 
     */
    private ByteBuffer nextRecord = ByteBuffer.allocate(2);

    /**
     *  A field that stores the current barometer value (two bytes) in a ByteBuffer.
     *  Units are (in Hg / 1000). The barometric value should be between 20 
     *  inches and 32.5 inches in Vantage Pro and between 20 inches and 32.5 
     *  inches in both Vantatge Pro Vantage Pro2.  Values outside these ranges 
     *  will not be logged.
     */
    private ByteBuffer barometer = ByteBuffer.allocate(2);

    /**
     *  A field that stores the inside temperature value (two bytes) in a ByteBuffer.
     *  The value is sent as 10th of a degree in F.  For example, 795 is 
     *  returned for 79.5F.
     */
    private ByteBuffer insideTemperature = ByteBuffer.allocate(2);

    /**
     *  A field that stores the inside humidity value (one byte) in a ByteBuffer.
     *  This is the relative humidity in %, such as 50 is returned for 50%.
     */
    private ByteBuffer insideHumidity = ByteBuffer.allocate(1);

    /**
     *  A field that stores the outside temperature value (two bytes) in a ByteBuffer.
     *  The value is sent as 10th of a degree in F.  For example, 795 is 
     *  returned for 79.5F.
     */
    private ByteBuffer outsideTemperature = ByteBuffer.allocate(2);

    /**
     *  A field that stores the wind speed value (one byte) in a ByteBuffer.
     *  It is a byte unsigned value in mph.  If the wind speed is dashed 
     *  because it lost synchronization with the radio or due to some 
     *  other reason, the wind speed is forced to be 0. 
     */
    private ByteBuffer windSpeed = ByteBuffer.allocate(1);

    /**
     *  A field that stores the ten minute averagewind speed value (one byte) in a ByteBuffer.
     *  It is a byte unsigned value in mph.
     */
    private ByteBuffer tenMinuteAverageWindSpeed = ByteBuffer.allocate(1);

    /**
     *  A field that stores the wind direction value (two bytes) in a ByteBuffer.
     *  It is a two byte unsigned value from 0 to 360 degrees.  (0 is 
     *  North, 90 is East, 180 is South and 270 is West.) 
     */
    private ByteBuffer windDirection = ByteBuffer.allocate(2);

    /**
     *  A field that stores seven extra temperature values (seven bytes) in a ByteBuffer.
     *  This field supports seven extra temperature stations. 
     *  Each byte is one extra temperature value in whole degrees F with 
     *  an offset of 90 degrees.  For example, a value of 0 = -90F ; a 
     *  value of 100 = 10F ; and a value of 169 = 79F.
     */
    private ByteBuffer extraTemperatures = ByteBuffer.allocate(7);

    /**
     *  A field that stores soil temperature values (four bytes) in a ByteBuffer.
     *  This field supports four soil temperature sensors.
     *  Each byte is one temperature value in whole degrees F with 
     *  an offset of 90 degrees.  For example, a value of 0 = -90F ; a 
     *  value of 100 = 10F ; and a value of 169 = 79F.
     */
    private ByteBuffer soilTemperatures = ByteBuffer.allocate(4);

    /**
     *  A field that stores leaf temperature values (four bytes) in a ByteBuffer.
     *  This field supports four soil temperature sensors.
     *  Each byte is one temperature value in whole degrees F with 
     *  an offset of 90 degrees.  For example, a value of 0 = -90F ; a 
     *  value of 100 = 10F ; and a value of 169 = 79F.
     */
    private ByteBuffer leafTemperatures = ByteBuffer.allocate(4);

    /**
     *  A field that stores the outside humidity value (one byte) in a ByteBuffer.
     *  This is the relative humidity in %, such as 50 is returned for 50%.
     */
    private ByteBuffer outsideHumidity = ByteBuffer.allocate(1);

    /**
     *  A field that stores seven extra humidity values (seven bytes) in a ByteBuffer.
     *  Relative humidity in % for extra seven humidity stations.  
     */
    private ByteBuffer extraHumidities = ByteBuffer.allocate(7);

    /**
     *  A field that stores the rain rate value (two bytes) in a ByteBuffer.
     *  This value is sent as 100 of a inch per hour.  For example, 256 
     *  represent 2.56 inches/hour. 
     */
    private ByteBuffer rainRate = ByteBuffer.allocate(2);

    /**
     *  A field that stores the UV radiation value (one byte) in a ByteBuffer.
     *  The unit is in UV index.
     */
    private ByteBuffer uvRadiation = ByteBuffer.allocate(1);

    /**
     *  A field that stores the solar radiation value (two bytes) in a ByteBuffer.
     *  The unit is in watt/meter^2.
     */
    private ByteBuffer solarRadiation = ByteBuffer.allocate(2);

    /**
     *  A field that stores the storm rain value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer stormRain = ByteBuffer.allocate(2);

    /**
     *  A field that stores the current storm start date (two bytes) in a ByteBuffer.
     *  Bit 15 to bit 12 is the month, bit 11 to bit 7 is the day and bit 6 to 
     *  bit 0 is the year offseted by 2000.
     */
    private ByteBuffer currentStormStartDate = ByteBuffer.allocate(2);

    /**
     *  A field that stores the daily rain value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer dailyRain = ByteBuffer.allocate(2);

    /**
     *  A field that stores the monthly rain value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer monthlyRain = ByteBuffer.allocate(2);

    /**
     *  A field that stores the yearly rain value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer yearlyRain = ByteBuffer.allocate(2);

    /**
     *  A field that stores the daily evapotranspiration value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer dailyEvapoTranspiration = ByteBuffer.allocate(2);

    /**
     *  A field that stores the monthly evapotranspiration value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer monthlyEvapoTranspiration = ByteBuffer.allocate(2);

    /**
     *  A field that stores the yearly evapotranspiration value (two bytes) in a ByteBuffer.
     *  The storm is stored as 100th of an inch. 
     */
    private ByteBuffer yearlyEvapoTranspiration = ByteBuffer.allocate(2);

    /**
     *  A field that stores soil moisture values (four bytes) in a ByteBuffer.
     *  The unit is in centibar.  It supports four soil sensors. 
     */
    private ByteBuffer soilMoistures = ByteBuffer.allocate(4);

    /**
     *  A field that stores leaf wetness values (four bytes) in a ByteBuffer.
     *  This is a scale number from 0 to 15 with 0 meaning very dry and 
     *  15 meaning very wet.  It supports four leaf sensors. 
     */
    private ByteBuffer leafWetnesses = ByteBuffer.allocate(4);

    /**
     *  A field that stores the currently active inside alarm value (one byte) in a ByteBuffer.
     *  Field                         Bit #  
     *  Inside Alarms                 Currently active inside alarms.  
     *  Falling bar trend alarm       0  
     *  Rising bar trend alarm        1  
     *  Low inside temp alarm         2  
     *  High inside temp alarm        3  
     *  Low inside hum alarm          4  
     *  High inside hum alarm         5  
     *  Time alarm                    6  
     */
    private ByteBuffer insideAlarm = ByteBuffer.allocate(1);

    /**
     *  A field that stores the currently active rain alarm value (one byte) in a ByteBuffer.
     *  Field                       Bit #  
     *  Rain Alarms                 Currently active rain alarms.  
     *  High rain rate alarm        0  
     *  15 min rain alarm           1  Flash flood alarm
     *  24 hr rain alarm            2  
     *  storm total rain alarm      3  
     *  daily ET  alarm             4  
     */
    private ByteBuffer rainAlarm = ByteBuffer.allocate(1);

    /**
     *  A field that stores the currently outside alarm values (two bytes) in a ByteBuffer.
     *  Field                       Bit #  of Byte 1
     *  Low outside temp alarm      0  
     *  High outside temp alarm     1  
     *  Wind speed alarm            2  
     *  10 min avg speed alarm      3  
     *  Low dewpoint alarm          4  
     *  High dewpoint alarm         5  
     *  High heat alarm             6  
     *  Low wind chill alarm        7  
     *  Field                       Bit #  of Byte 2
     *  High THSW alarm             0  
     *  High solar rad alarm        1  
     *  High UV alarm               2  
     *  UV Dose alarm               3  
     */
    private ByteBuffer outsideAlarms = ByteBuffer.allocate(2);

    /**
     *  A field that stores the extra temperature and humidity alarm values 
     * (eight bytes) in a ByteBuffer.
     *  Field                       Bit #  of Byte 1
     *  Low Humidity alarm          1  Currently active outside humidity alarms.
     *  Low Humidity alarm          2  
     *  High Humidity alarm         3  
     *  Field                       Bit #  of Byte 2-8:
     *    Each byte contains four alarm bits (0  3) for a single extra 
     *    Temp/Hum station. Bits (4  7) are not used and reserved for 
     *    future use. 
     *    Use the temperature and humidity sensor numbers, as 
     *    described in Section XIII.4 to locate which byte contains the 
     *    appropriate alarm bits. In particular, the humidity and 
     *    temperature alarms for a single station will be found in 
     *    different bytes.
     */
    private ByteBuffer extraTemperatureHumidityAlarms = ByteBuffer.allocate(8);

    /**
     *  A field that stores the soil and leaf alarm values 
     * (four bytes) in a ByteBuffer.
     *  Field                       Bit #  of Byte 1
     *  Low leaf wetness X alarm    0  
     *  High leaf wetness X alarm   1  
     *  Low soil moisture X alarm   2  
     *  High soil moisture X alarm  3  
     *  Low leaf temp X alarm       4  
     *  High leaf temp X alarm      5  
     *  Low soil temp X alarm       6  
     *  High soil temp X alarm      7  
     */
    private ByteBuffer soilLeafAlarms = ByteBuffer.allocate(4);

    /**
     *  A field that stores the transmitter battery status value (one byte) in a ByteBuffer.
     */
    private ByteBuffer transmitterBatteryStatus = ByteBuffer.allocate(1);

    /**
     *  A field that stores the console battery voltage value (two bytes) in a ByteBuffer.
     *  Voltage = ((Data * 300)/512)/100.0
     */
    private ByteBuffer consoleBatteryVoltage = ByteBuffer.allocate(2);

    /**
     *  A field that stores the forecast icon values (one byte) in a ByteBuffer.
     *  Forecast Icons      Bit maps for forecast icons on the console screen. 
     *  Rain                0  
     *  Cloud               1  
     *  Partly Cloudy       2  
     *  Sun                 3  
     *  Snow                4 
     * 
     *  Forecast Icon Values 
     *  
     *  Value Decimal Value Hex  Segments Shown          Forecast 
     *  8             0x08       Sun Mostly              Clear 
     *  6             0x06       Partial Sun + Cloud     Partially Cloudy 
     *  2             0x02       Cloud Mostly            Cloudy 
     *  3             0x03       Cloud + Rain            Mostly Cloudy, Rain within 12 hours 
     *  18            0x12       Cloud + Snow            Mostly Cloudy, Snow within 12 hours 
     *  19            0x13       Cloud + Rain + Snow     Mostly Cloudy, Rain or Snow within 12 hours 
     *  7             0x07       Partial Sun + Cloud +   Partially Cloudy, Rain within 12 hours
     *                           Rain  
     *  22            0x16       Partial Sun + Cloud +   Partially Cloudy, Snow within 12 hours 
     *                           Snow 
     *  23            0x17       Partial Sun + Cloud +   Partially Cloudy, Rain or Snow within 12 hours
     *                           Rain + Snow 
     */
    private ByteBuffer forecastIconValues = ByteBuffer.allocate(1);

    /**
     *  A field that stores the forecast rule number value (one byte) in a ByteBuffer.
     */
    private ByteBuffer forecastRuleNumber = ByteBuffer.allocate(1);

    /**
     *  A field that stores the time of sunrise (two bytes) in a ByteBuffer.
     *  The time is stored as hour * 100 + min. 
     */
    private ByteBuffer timeOfSunrise = ByteBuffer.allocate(2);

    /**
     *  A field that stores the time of sunset (two bytes) in a ByteBuffer.
     *  The time is stored as hour * 100 + min. 
     */
    private ByteBuffer timeOfSunset = ByteBuffer.allocate(2);

    /**
     *  A field that stores record delimiter (two bytes) in a ByteBuffer.
     *  The values are "\n" <LF> = 0x0A and then "\r" <CR> = 0x0D 
     */
    private ByteBuffer recordDelimiter = ByteBuffer.allocate(2);

    /**
     *  A field that stores CRC checksum value (two bytes) in a ByteBuffer.
     *  The CRC checking used by the WeatherLink is based on the CRC-CCITT 
     *  standard. The heart of the method involves a CRC-accumulator that 
     *  uses the following formula on each successive data byte. After all 
     *  the data bytes have been "accumulated", there will be a two byte CRC 
     *  checksum that will get processed in the same manner as the data bytes. 
     *  If there has been no transmission error, then the final CRC-accumulator 
     *  value will be 0 (assuming it was set to zero before accumulating data). 
            
     *  In the following code, "crc" is the crc accumulator (16 bits or 2 bytes), 
     *  "data" is the data or CRC checksum byte to be accumulated, and "crc_table" 
     *  is the table of CRC values found in the CCITT.h header file. The operator 
     *  "^" is an exclusive-or (XOR), ">> 8" shifts the data right by one byte 
     *  (divides by 256), and "<< 8" shifts the data left by one byte 
     *  (multiplies by 256).
     *
     *  crc = crc_table [(crc >> 8) ^ data] ^ (crc << 8);
     * 
     */
    private ByteBuffer crcChecksum = ByteBuffer.allocate(2);

    /**
     *  Constructor:  Builds all of the components of the LOOP packet from
     *  the ByteBuffer data being passed in.
     *
     *  @param packetBuffer  the ByteBuffer that contains the binary LOOP packet data
     */
    public DavisWxParser(ByteBuffer packetBuffer) {

        this.packetBuffer = packetBuffer;
        // prepare the buffer for reading
        this.packetBuffer.flip();

        // Each of the LOOP packet fields get set upon instantiation of the parser:

        // set the loopID field from offset 0-2
        this.loopID.put(this.packetBuffer.get());
        this.loopID.put(this.packetBuffer.get());
        this.loopID.put(this.packetBuffer.get());

        // set the barTrend field from offset 3
        this.barTrend.put(this.packetBuffer.get());

        // set the packetType field from offset 4
        this.packetType.put(this.packetBuffer.get());

        // set the nextRecord field from offset 5-6
        this.nextRecord.put(this.packetBuffer.get());
        this.nextRecord.put(this.packetBuffer.get());

        // set the barometer field from offset 7-8
        this.barometer.put(this.packetBuffer.get());
        this.barometer.put(this.packetBuffer.get());

        // set the insideTemperature field from offset 9-10
        this.insideTemperature.put(this.packetBuffer.get());
        this.insideTemperature.put(this.packetBuffer.get());

        // set the insideHumidity field from offset 11
        this.insideHumidity.put(this.packetBuffer.get());

        // set the outsideTemperature field from offset 12-13
        this.outsideTemperature.put(this.packetBuffer.get());
        this.outsideTemperature.put(this.packetBuffer.get());

        // set the windSpeed field from offset 14
        this.windSpeed.put(this.packetBuffer.get());

        // set the tenMinuteAverageWindSpeed field from offset 15
        this.tenMinuteAverageWindSpeed.put(this.packetBuffer.get());

        // set the windDirection field from offset 16-17
        this.windDirection.put(this.packetBuffer.get());
        this.windDirection.put(this.packetBuffer.get());

        // set the extraTemperatures field from offset 18-24
        this.extraTemperatures.put(this.packetBuffer.get());
        this.extraTemperatures.put(this.packetBuffer.get());
        this.extraTemperatures.put(this.packetBuffer.get());
        this.extraTemperatures.put(this.packetBuffer.get());
        this.extraTemperatures.put(this.packetBuffer.get());
        this.extraTemperatures.put(this.packetBuffer.get());
        this.extraTemperatures.put(this.packetBuffer.get());

        // set the soilTemperatures field from offset 25-28
        this.soilTemperatures.put(this.packetBuffer.get());
        this.soilTemperatures.put(this.packetBuffer.get());
        this.soilTemperatures.put(this.packetBuffer.get());
        this.soilTemperatures.put(this.packetBuffer.get());

        // set the leafTemperatures field from offset 29-32
        this.leafTemperatures.put(this.packetBuffer.get());
        this.leafTemperatures.put(this.packetBuffer.get());
        this.leafTemperatures.put(this.packetBuffer.get());
        this.leafTemperatures.put(this.packetBuffer.get());

        // set the outsideHumidity field from offset 33
        this.outsideHumidity.put(this.packetBuffer.get());

        // set the extraHumidities field from offset 34-40
        this.extraHumidities.put(this.packetBuffer.get());
        this.extraHumidities.put(this.packetBuffer.get());
        this.extraHumidities.put(this.packetBuffer.get());
        this.extraHumidities.put(this.packetBuffer.get());
        this.extraHumidities.put(this.packetBuffer.get());
        this.extraHumidities.put(this.packetBuffer.get());
        this.extraHumidities.put(this.packetBuffer.get());

        // set the rainRate field from offset 41-42
        this.rainRate.put(this.packetBuffer.get());
        this.rainRate.put(this.packetBuffer.get());

        // set the uvRadiation field from offset 43
        this.uvRadiation.put(this.packetBuffer.get());

        // set the solarRadiation field from offset 44-45
        this.solarRadiation.put(this.packetBuffer.get());
        this.solarRadiation.put(this.packetBuffer.get());

        // set the stormRain field from offset 46-47
        this.stormRain.put(this.packetBuffer.get());
        this.stormRain.put(this.packetBuffer.get());

        // set the currentStormStartDate field from offset 48-49
        this.currentStormStartDate.put(this.packetBuffer.get());
        this.currentStormStartDate.put(this.packetBuffer.get());

        // set the dailyRain field from offset 50-51
        this.dailyRain.put(this.packetBuffer.get());
        this.dailyRain.put(this.packetBuffer.get());

        // set the monthlyRain field from offset 52-53
        this.monthlyRain.put(this.packetBuffer.get());
        this.monthlyRain.put(this.packetBuffer.get());

        // set the yearlyRain field from offset 54-55
        this.yearlyRain.put(this.packetBuffer.get());
        this.yearlyRain.put(this.packetBuffer.get());

        // set the dailyEvapoTranspiration field from offset 56-57
        this.dailyEvapoTranspiration.put(this.packetBuffer.get());
        this.dailyEvapoTranspiration.put(this.packetBuffer.get());

        // set the monthlyEvapoTranspiration field from offset 58-59
        this.monthlyEvapoTranspiration.put(this.packetBuffer.get());
        this.monthlyEvapoTranspiration.put(this.packetBuffer.get());

        // set the yearlyEvapoTranspiration field from offset 60-61
        this.yearlyEvapoTranspiration.put(this.packetBuffer.get());
        this.yearlyEvapoTranspiration.put(this.packetBuffer.get());

        // set the soilMoistures field from offset 62-65
        this.soilMoistures.put(this.packetBuffer.get());
        this.soilMoistures.put(this.packetBuffer.get());
        this.soilMoistures.put(this.packetBuffer.get());
        this.soilMoistures.put(this.packetBuffer.get());

        // set the leafWetnesses field from offset 66-69
        this.leafWetnesses.put(this.packetBuffer.get());
        this.leafWetnesses.put(this.packetBuffer.get());
        this.leafWetnesses.put(this.packetBuffer.get());
        this.leafWetnesses.put(this.packetBuffer.get());

        // set the insideAlarm field from offset 70
        this.insideAlarm.put(this.packetBuffer.get());

        // set the rainAlarm field from offset 71
        this.rainAlarm.put(this.packetBuffer.get());

        // set the outsideAlarms field from offset 72-73
        this.outsideAlarms.put(this.packetBuffer.get());
        this.outsideAlarms.put(this.packetBuffer.get());

        // set the extraTemperatureHumidityAlarms field from offset 74-81
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());
        this.extraTemperatureHumidityAlarms.put(this.packetBuffer.get());

        // set the soilLeafAlarms field from offset 82-85
        this.soilLeafAlarms.put(this.packetBuffer.get());
        this.soilLeafAlarms.put(this.packetBuffer.get());
        this.soilLeafAlarms.put(this.packetBuffer.get());
        this.soilLeafAlarms.put(this.packetBuffer.get());

        // set the transmitterBatteryStatus field from offset 86
        this.transmitterBatteryStatus.put(this.packetBuffer.get());

        // set the consoleBatteryVoltage field from offset 87-88
        this.consoleBatteryVoltage.put(this.packetBuffer.get());
        this.consoleBatteryVoltage.put(this.packetBuffer.get());

        // set the forecastIconValues field from offset 89
        this.forecastIconValues.put(this.packetBuffer.get());

        // set the forecastRuleNumber field from offset 90
        this.forecastRuleNumber.put(this.packetBuffer.get());

        // set the timeOfSunrise field from offset 91-92
        this.timeOfSunrise.put(this.packetBuffer.get());
        this.timeOfSunrise.put(this.packetBuffer.get());

        // set the timeOfSunset field from offset 93-94
        this.timeOfSunset.put(this.packetBuffer.get());
        this.timeOfSunset.put(this.packetBuffer.get());

        // set the recordDelimiter field from offset 95-96
        this.recordDelimiter.put(this.packetBuffer.get());
        this.recordDelimiter.put(this.packetBuffer.get());

        // set the crcChecksum field from offset 97-98
        this.crcChecksum.put(this.packetBuffer.get());
        this.crcChecksum.put(this.packetBuffer.get());

    }

    /*
     *  The main entrypoint method.  THis takes a single argument that is the 
     *  path to a file containing a single binary LOOP packet of data.
     */
    public static void main(String[] args) {

        // Ensure we have a path to the binary file
        if (args.length != 1) {
            logger.info("Please provide the path to a file containing a binary LOOP packet.");
            System.exit(1);
        } else {
            try {
                // open and read the file
                File packetFile = new File(args[0]);
                FileInputStream fis = new FileInputStream(packetFile);
                FileChannel fileChannel = fis.getChannel();
                ByteBuffer inBuffer = ByteBuffer.allocateDirect(8192);
                ByteBuffer packetBuffer = ByteBuffer.allocateDirect(8192);

                while (fileChannel.read(inBuffer) != -1 || inBuffer.position() > 0) {
                    inBuffer.flip();
                    packetBuffer.put(inBuffer.get());
                    inBuffer.compact();
                }
                fileChannel.close();
                fis.close();
                packetBuffer.put(inBuffer.get());

                // create an instance of the parser, and report the field contents after parsing
                DavisWxParser davisWxParser = new DavisWxParser(packetBuffer);

                // Set up a simple logger that logs to the console
                PropertyConfigurator.configure(davisWxParser.getLogConfigurationFile());

                logger.info("loopID:                         " + davisWxParser.getLoopID());
                logger.info("barTrend:                       " + davisWxParser.getBarTrend());
                logger.info("barTrendAsString:               " + davisWxParser.getBarTrendAsString());
                logger.info("packetType:                     " + davisWxParser.getPacketType());
                logger.info("nextRecord:                     " + davisWxParser.getNextRecord());
                logger.info("barometer:                      " + davisWxParser.getBarometer());
                logger.info("insideTemperature:              " + davisWxParser.getInsideTemperature());
                logger.info("insideHumidity:                 " + davisWxParser.getInsideHumidity());
                logger.info("outsideTemperature:             " + davisWxParser.getOutsideTemperature());
                logger.info("windSpeed:                      " + davisWxParser.getWindSpeed());
                logger.info("tenMinuteAverageWindSpeed:      " + davisWxParser.getTenMinuteAverageWindSpeed());
                logger.info("windDirection:                  " + davisWxParser.getWindDirection());
                logger.info(
                        "extraTemperatures:              " + Arrays.toString(davisWxParser.getExtraTemperatures()));
                logger.info(
                        "soilTemperatures:               " + Arrays.toString(davisWxParser.getSoilTemperatures()));
                logger.info(
                        "leafTemperatures:               " + Arrays.toString(davisWxParser.getLeafTemperatures()));
                logger.info("outsideHumidity:                " + davisWxParser.getOutsideHumidity());
                logger.info(
                        "extraHumidities:                " + Arrays.toString(davisWxParser.getExtraHumidities()));
                logger.info("rainRate:                       " + davisWxParser.getRainRate());
                logger.info("uvRadiation:                    " + davisWxParser.getUvRadiation());
                logger.info("solarRadiation:                 " + davisWxParser.getSolarRadiation());
                logger.info("stormRain:                      " + davisWxParser.getStormRain());
                logger.info("currentStormStartDate:          " + davisWxParser.getCurrentStormStartDate());
                logger.info("dailyRain:                      " + davisWxParser.getDailyRain());
                logger.info("monthlyRain:                    " + davisWxParser.getMonthlyRain());
                logger.info("yearlyRain:                     " + davisWxParser.getYearlyRain());
                logger.info("dailyEvapoTranspiration:        " + davisWxParser.getDailyEvapoTranspiration());
                logger.info("monthlyEvapoTranspiration:      " + davisWxParser.getMonthlyEvapoTranspiration());
                logger.info("yearlyEvapoTranspiration:       " + davisWxParser.getYearlyEvapoTranspiration());
                logger.info("soilMoistures:                  " + Arrays.toString(davisWxParser.getSoilMoistures()));
                logger.info("leafWetnesses:                  " + Arrays.toString(davisWxParser.getLeafWetnesses()));
                logger.info("insideAlarm:                    " + davisWxParser.getInsideAlarm());
                logger.info("rainAlarm:                      " + davisWxParser.getRainAlarm());
                logger.info("outsideAlarms:                  " + davisWxParser.getOutsideAlarms());
                logger.info("extraTemperatureHumidityAlarms: " + davisWxParser.getExtraTemperatureHumidityAlarms());
                logger.info("soilLeafAlarms:                 " + davisWxParser.getSoilLeafAlarms());
                logger.info("transmitterBatteryStatus:       " + davisWxParser.getTransmitterBatteryStatus());
                logger.info("consoleBatteryVoltage:          " + davisWxParser.getConsoleBatteryVoltage());
                logger.info("forecastIconValues:             " + davisWxParser.getForecastAsString());
                logger.info("forecastRuleNumber:             " + davisWxParser.getForecastRuleNumberAsString());
                logger.info("timeOfSunrise:                  " + davisWxParser.getTimeOfSunrise());
                logger.info("timeOfSunset:                   " + davisWxParser.getTimeOfSunset());
                logger.info("recordDelimiter:                " + davisWxParser.getRecordDelimiterAsHexString());
                logger.info("crcChecksum:                    " + davisWxParser.getCrcChecksum());

            } catch (java.io.FileNotFoundException fnfe) {
                fnfe.printStackTrace();

            } catch (java.io.IOException ioe) {
                ioe.printStackTrace();

            }

        }
    }

    /**
     * A method that gets the log configuration file location
     *
     * @return logConfigurationFile  the log configuration file location
     */
    public String getLogConfigurationFile() {
        return this.logConfigurationFile;
    }

    /**
     * A method that sets the log configuration file name
     *
     * @param logConfigurationFile  the log configuration file name
     */
    public void setLogConfigurationFile(String logConfigurationFile) {
        this.logConfigurationFile = logConfigurationFile;
    }

    /**
     * get the value from the loopID field
     *
     * @return loopID - the loopID as a String
     */
    public String getLoopID() {
        this.loopID.flip();

        String loopString;
        try {
            loopString = new String(new byte[] { this.loopID.get() });
            loopString = loopString + new String(new byte[] { this.loopID.get() });
            loopString = loopString + new String(new byte[] { this.loopID.get() });

            //loopString = new String(this.loopID.order(ByteOrder.LITTLE_ENDIAN).array(), "US-ASCII");
        } catch (Exception e) {
            e.printStackTrace();
            loopString = "BAD";
        }
        return loopString;
    }

    /**
     * get the value from the barTrend field
     *
     * @return barTrend - the barTrend as an integer
     */
    public int getBarTrend() {
        this.barTrend.flip();
        return (this.barTrend.get() & 0xFF);
    }

    /**
     * get the interpreted value from the barTrend field as a String
     *
     * @return barTrend - the barTrend as a String
     */
    public String getBarTrendAsString() {
        this.barTrend.flip();
        String barTrendString = "Trend not available";
        int barTrend = this.barTrend.get();

        if (barTrend == -60) {
            barTrendString = "Falling Rapidly";
        } else if (barTrend == -20) {
            barTrendString = "Falling Slowly";
        } else if (barTrend == 0) {
            barTrendString = "Steady";
        } else if (barTrend == 20) {
            barTrendString = "Rising Slowly";
        } else if (barTrend == 60) {
            barTrendString = "Rising Rapidly";
        } else if (barTrend == 80) {
            barTrendString = "P";
        }
        return barTrendString;
    }

    /**
     * get the value from the packetType field
     *
     * @return packetType - the packetType as an integer
     */
    public int getPacketType() {
        this.packetType.flip();
        return (this.packetType.get() & 0xFF);
    }

    /**
     * get the value from the nextRecord field
     *
     * @return nextRecord - the nextRecord as an integer
     */
    public int getNextRecord() {
        this.nextRecord.flip();
        return (int) this.nextRecord.order(ByteOrder.LITTLE_ENDIAN).getShort();
    }

    /**
     * get the value from the barometer field
     *
     * @return barometer - the barometer as a float
     */
    public float getBarometer() {
        this.barometer.flip();
        return (float) (this.barometer.order(ByteOrder.LITTLE_ENDIAN).getShort()) / 1000;
    }

    /**
     * get the value from the insideTemperature field
     *
     * @return insideTemperature - the insideTemperature as a float
     */
    public float getInsideTemperature() {
        this.insideTemperature.flip();
        return (float) (this.insideTemperature.order(ByteOrder.LITTLE_ENDIAN).getShort()) / 10;
    }

    /**
     * get the value from the insideHumidity field
     *
     * @return insideHumidity - the insideHumidity as an integer
     */
    public int getInsideHumidity() {
        this.insideHumidity.flip();
        return (this.insideHumidity.get() & 0xFF);
    }

    /**
     * get the value from the outsideTemperature field
     *
     * @return outsideTemperature - the outsideTemperature as a float
     */
    public float getOutsideTemperature() {
        this.outsideTemperature.flip();
        return (float) (this.outsideTemperature.order(ByteOrder.LITTLE_ENDIAN).getShort()) / 10;
    }

    /**
     * get the value from the windSpeed field
     *
     * @return windSpeed - the windSpeed as an integer
     */
    public int getWindSpeed() {
        this.windSpeed.flip();
        return (this.windSpeed.get() & 0xFF);
    }

    /**
     * get the value from the tenMinuteAverageWindSpeed field
     *
     * @return tenMinuteAverageWindSpeed - the tenMinuteAverageWindSpeed as an integer
     */
    public int getTenMinuteAverageWindSpeed() {
        this.tenMinuteAverageWindSpeed.flip();
        return (this.tenMinuteAverageWindSpeed.get() & 0xFF);
    }

    /**
     * get the value from the windDirection field
     *
     * @return windDirection - the windDirection as an integer
     */
    public int getWindDirection() {
        this.windDirection.flip();
        return (this.windDirection.get() & 0xFF);
    }

    /**
     * get the value from the extraTemperatures field
     *
     * @return extraTemperatures - the extraTemperatures as an float array
     */
    public float[] getExtraTemperatures() {
        this.extraTemperatures.flip();

        // add each of the temperature values to a float array
        float[] extraTemperatures = new float[7];
        for (int i = 0; i < extraTemperatures.length; i++) {
            extraTemperatures[i] = (float) ((this.extraTemperatures.get() & 0xFF) - 90.0) / 10;
        }
        return extraTemperatures;
    }

    /**
     * get the value from the soilTemperatures field
     *
     * @return soilTemperatures - the soilTemperatures as an float array
     */
    public float[] getSoilTemperatures() {
        this.soilTemperatures.flip();

        // add each of the temperature values to a float array
        float[] soilTemperatures = new float[4];
        for (int i = 0; i < soilTemperatures.length; i++) {
            soilTemperatures[i] = (float) ((this.soilTemperatures.get() & 0xFF) - 90.0) / 10;
        }
        return soilTemperatures;
    }

    /**
     * get the value from the leafTemperatures field
     *
     * @return leafTemperatures - the leafTemperatures as an float array
     */
    public float[] getLeafTemperatures() {
        this.leafTemperatures.flip();

        // add each of the temperature values to a float array
        float[] leafTemperatures = new float[4];
        for (int i = 0; i < leafTemperatures.length; i++) {
            leafTemperatures[i] = (float) ((this.leafTemperatures.get() & 0xFF) - 90.0) / 10;
        }
        return leafTemperatures;
    }

    /**
     * get the value from the outsideHumidity field
     *
     * @return outsideHumidity - the outsideHumidity as an integer
     */
    public int getOutsideHumidity() {
        this.outsideHumidity.flip();
        return (this.outsideHumidity.get() & 0xFF);
    }

    /**
     * get the value from the extraHumidities field
     *
     * @return extraHumidities - the extraHumidities as an int array
     */
    public int[] getExtraHumidities() {
        this.extraHumidities.flip();

        // add each of the humidity values to a float array
        int[] extraHumidities = new int[7];
        for (int i = 0; i < extraHumidities.length; i++) {
            extraHumidities[i] = (int) this.extraHumidities.get();
        }
        return extraHumidities;
    }

    /**
     * get the value from the rainRate field
     *
     * @return rainRate - the rainRate as a float
     */
    public float getRainRate() {
        this.rainRate.flip();
        return (float) (this.rainRate.order(ByteOrder.LITTLE_ENDIAN).getShort()) / 100;
    }

    /**
     * get the value from the uvRadiation field
     *
     * @return uvRadiation - the uvRadiation as an integer
     */
    public int getUvRadiation() {
        this.uvRadiation.flip();
        return (this.uvRadiation.get() & 0xFF);
    }

    /**
     * get the value from the solarRadiation field
     *
     * @return solarRadiation - the solarRadiation as a float
     */
    public float getSolarRadiation() {
        this.solarRadiation.flip();
        return (float) (this.solarRadiation.order(ByteOrder.LITTLE_ENDIAN).getShort());
    }

    /**
     * get the value from the stormRain field
     *
     * @return stormRain - the stormRain as a float
     */
    public float getStormRain() {
        this.stormRain.flip();
        return (float) (this.stormRain.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the currentStormStartDate field
     *
     * @return currentStormStartDate - the currentStormStartDate as a String
     */
    public String getCurrentStormStartDate() {
        this.currentStormStartDate.flip();
        int currentStormStartDate = (int) this.currentStormStartDate.order(ByteOrder.LITTLE_ENDIAN).getShort();

        int month = currentStormStartDate;
        month = (month >> 11); // clear bits 0-11
        String monthString = (new Integer(month)).toString();

        int day = currentStormStartDate;
        day = (day << 21) >> 27; // clear bits 16-32, then 0-11
        String dayString = (new Integer(day)).toString();

        int year = currentStormStartDate;
        year = (year << 25) >> 25; // clear bits 7-32
        int centuryYear = 2000 + year;
        String centuryYearString = (new Integer(centuryYear)).toString();
        if (monthString.equals("-1") || dayString.equals("-1")) {
            return "01-01-1999";
        } else {
            return (monthString + "-" + dayString + "-" + centuryYearString);
        }
    }

    /**
     * get the value from the dailyRain field
     *
     * @return dailyRain - the dailyRain as a float
     */
    public float getDailyRain() {
        this.dailyRain.flip();
        return (float) (this.dailyRain.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the monthlyRain field
     *
     * @return monthlyRain - the monthlyRain as a float
     */
    public float getMonthlyRain() {
        this.monthlyRain.flip();
        return (float) (this.monthlyRain.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the yearlyRain field
     *
     * @return yearlyRain - the yearlyRain as a float
     */
    public float getYearlyRain() {
        this.yearlyRain.flip();
        return (float) (this.yearlyRain.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the dailyEvapoTranspiration field
     *
     * @return dailyEvapoTranspiration - the dailyEvapoTranspiration as a float
     */
    public float getDailyEvapoTranspiration() {
        this.dailyEvapoTranspiration.flip();
        return (float) (this.dailyEvapoTranspiration.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the monthlyEvapoTranspiration field
     *
     * @return monthlyEvapoTranspiration - the monthlyEvapoTranspiration as a float
     */
    public float getMonthlyEvapoTranspiration() {
        this.monthlyEvapoTranspiration.flip();
        return (float) (this.monthlyEvapoTranspiration.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the yearlyEvapoTranspiration field
     *
     * @return yearlyEvapoTranspiration - the yearlyEvapoTranspiration as a float
     */
    public float getYearlyEvapoTranspiration() {
        this.yearlyEvapoTranspiration.flip();
        return (float) (this.yearlyEvapoTranspiration.order(ByteOrder.LITTLE_ENDIAN).getShort() / 100);
    }

    /**
     * get the value from the soilMoistures field
     *
     * @return soilTemperatures - the soilMoistures values as a float array
     */
    public float[] getSoilMoistures() {
        this.soilMoistures.flip();

        // add each of the soilMoistures values to a float array
        float[] soilMoistures = new float[4];
        for (int i = 0; i < soilMoistures.length; i++) {
            soilMoistures[i] = (float) this.soilMoistures.get();
        }
        return soilMoistures;
    }

    /**
     * get the value from the leafWetnesses field
     *
     * @return leafWetnesses - the leafWetnesses values as a float array
     */
    public float[] getLeafWetnesses() {
        this.leafWetnesses.flip();

        // add each of the leafWetnesses values to a float array
        float[] leafWetnesses = new float[4];
        for (int i = 0; i < leafWetnesses.length; i++) {
            leafWetnesses[i] = (float) this.leafWetnesses.get();
        }
        return leafWetnesses;
    }

    /**
     * get the value from the insideAlarm field
     */
    public ByteBuffer getInsideAlarm() {
        return this.insideAlarm;
    }

    /**
     * get the value from the rainAlarm field
     */
    public ByteBuffer getRainAlarm() {
        return this.rainAlarm;
    }

    /**
     * get the value from the outsideAlarm field
     */
    public ByteBuffer getOutsideAlarms() {
        return this.outsideAlarms;
    }

    /**
     * get the value from the extraTemperatureHumidityAlarms field
     */
    public ByteBuffer getExtraTemperatureHumidityAlarms() {
        return this.extraTemperatureHumidityAlarms;
    }

    /**
     * get the value from the soilLeafAlarms field
     */
    public ByteBuffer getSoilLeafAlarms() {
        return this.soilLeafAlarms;
    }

    /**
     * get the value from the transmitterBatteryStatus field
     *
     * @return transmitterBatteryStatus - the transmitterBatteryStatus as an integer
     */
    public byte[] getTransmitterBatteryStatus() {
        this.transmitterBatteryStatus.flip();
        return new byte[] { this.transmitterBatteryStatus.get() };
    }

    /**
     * get the value from the consoleBatteryVoltage field
     *
     * @return consoleBatteryVoltage - the consoleBatteryVoltage as a float
     */
    public float getConsoleBatteryVoltage() {
        this.consoleBatteryVoltage.flip();
        float consoleBatteryVoltage = (float) this.consoleBatteryVoltage.order(ByteOrder.LITTLE_ENDIAN).getShort();

        consoleBatteryVoltage = ((consoleBatteryVoltage * 300.0f) / 512.0f) / 100.0f; // from the instrument guide
        return consoleBatteryVoltage;
    }

    /**
     * get the value from the forecastIconValues field
     *
     * @return forecastIconValueString - the forecastIconValue as a String
     */
    public String getForecastAsString() {
        this.forecastIconValues.flip();
        byte forecastIconValue = this.forecastIconValues.get();
        String forecastIconValueString = "Forecast not available";

        // from the instrument guide:
        if (forecastIconValue == 0x08) {
            forecastIconValueString = "Mostly Clear";

        } else if (forecastIconValue == 0x06) {
            forecastIconValueString = "Partially Cloudy";

        } else if (forecastIconValue == 0x02) {
            forecastIconValueString = "Mostly Cloudy";

        } else if (forecastIconValue == 0x03) {
            forecastIconValueString = "Mostly Cloudy, Rain within 12 hours";

        } else if (forecastIconValue == 0x12) {
            forecastIconValueString = "Mostly Cloudy, Snow within 12 hours";

        } else if (forecastIconValue == 0x13) {
            forecastIconValueString = "Mostly Cloudy, Rain or Snow within 12 hours";

        } else if (forecastIconValue == 0x07) {
            forecastIconValueString = "Partially Cloudy, Rain within 12 hours";

        } else if (forecastIconValue == 0x16) {
            forecastIconValueString = "Partially Cloudy, Snow within 12 hours";

        } else if (forecastIconValue == 0x17) {
            forecastIconValueString = "Partially Cloudy, Rain or Snow within 12 hours";

        }
        return forecastIconValueString;

    }

    /**
     * get the value from the forecastRuleNumber field. The forecast rule
     * numbers are mapped to specific forecast strings set by Davis Instruments.
     * However, these aren't documented in the Serial Protocol Guide.  The strings
     * were taken from the PERL module documentation at 
     * http://www.cpan.org/authors/id/S/ST/STSANDER/vanprod-doc.html
     *
     * @return forecastRuleNumberAsString - the forecastRuleNumberAsString as a string
     */
    public String getForecastRuleNumberAsString() {
        this.forecastRuleNumber.flip();
        int forecastRuleNumber = (this.forecastRuleNumber.get() & 0xFF);

        String forecastRuleNumberAsString = "No forecast rule string available";
        Map<Integer, String> forecastRuleMap = new HashMap<Integer, String>();

        forecastRuleMap.put(0, "Mostly clear and cooler.");
        forecastRuleMap.put(1, "Mostly clear with little temperature change.");
        forecastRuleMap.put(2, "Mostly clear for 12 hours with little temperature change.");
        forecastRuleMap.put(3, "Mostly clear for 12 to 24 hours and cooler.");
        forecastRuleMap.put(4, "Mostly clear with little temperature change.");
        forecastRuleMap.put(5, "Partly cloudy and cooler.");
        forecastRuleMap.put(6, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(7, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(8, "Mostly clear and warmer.");
        forecastRuleMap.put(9, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(10, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(11, "Mostly clear with little temperature change.");
        forecastRuleMap.put(12, "Increasing clouds and warmer. Precipitation possible within 24 to 48 hours.");
        forecastRuleMap.put(13, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(14, "Mostly clear with little temperature change.");
        forecastRuleMap.put(15,
                "Increasing clouds with little temperature change. Precipitation possible within 24 hours.");
        forecastRuleMap.put(16, "Mostly clear with little temperature change.");
        forecastRuleMap.put(17, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(18, "Mostly clear with little temperature change.");
        forecastRuleMap.put(19,
                "Increasing clouds with little temperature change. Precipitation possible within 12 hours.");
        forecastRuleMap.put(20, "Mostly clear with little temperature change.");
        forecastRuleMap.put(21, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(22, "Mostly clear with little temperature change.");
        forecastRuleMap.put(23, "Increasing clouds and warmer. Precipitation possible within 24 hours.");
        forecastRuleMap.put(24, "Mostly clear and warmer. Increasing winds.");
        forecastRuleMap.put(25, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(26, "Mostly clear with little temperature change.");
        forecastRuleMap.put(27,
                "Increasing clouds and warmer. Precipitation possible within 12 hours. Increasing winds.");
        forecastRuleMap.put(28, "Mostly clear and warmer. Increasing winds.");
        forecastRuleMap.put(29, "Increasing clouds and warmer.");
        forecastRuleMap.put(30, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(31, "Mostly clear with little temperature change.");
        forecastRuleMap.put(32,
                "Increasing clouds and warmer. Precipitation possible within 12 hours. Increasing winds.");
        forecastRuleMap.put(33, "Mostly clear and warmer. Increasing winds.");
        forecastRuleMap.put(34, "Increasing clouds and warmer.");
        forecastRuleMap.put(35, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(36, "Mostly clear with little temperature change.");
        forecastRuleMap.put(37,
                "Increasing clouds and warmer. Precipitation possible within 12 hours. Increasing winds.");
        forecastRuleMap.put(38, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(39, "Mostly clear with little temperature change.");
        forecastRuleMap.put(40, "Mostly clear and warmer. Precipitation possible within 48 hours.");
        forecastRuleMap.put(41, "Mostly clear and warmer.");
        forecastRuleMap.put(42, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(43, "Mostly clear with little temperature change.");
        forecastRuleMap.put(44,
                "Increasing clouds with little temperature change. Precipitation possible within 24 to 48 hours.");
        forecastRuleMap.put(45, "Increasing clouds with little temperature change.");
        forecastRuleMap.put(46, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(47, "Mostly clear with little temperature change.");
        forecastRuleMap.put(48, "Increasing clouds and warmer. Precipitation possible within 12 to 24 hours.");
        forecastRuleMap.put(49, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(50, "Mostly clear with little temperature change.");
        forecastRuleMap.put(51,
                "Increasing clouds and warmer. Precipitation possible within 12 to 24 hours. Windy.");
        forecastRuleMap.put(52, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(53, "Mostly clear with little temperature change.");
        forecastRuleMap.put(54,
                "Increasing clouds and warmer. Precipitation possible within 12 to 24 hours. Windy.");
        forecastRuleMap.put(55, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(56, "Mostly clear with little temperature change.");
        forecastRuleMap.put(57, "Increasing clouds and warmer. Precipitation possible within 6 to 12 hours.");
        forecastRuleMap.put(58, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(59, "Mostly clear with little temperature change.");
        forecastRuleMap.put(60,
                "Increasing clouds and warmer. Precipitation possible within 6 to 12 hours. Windy.");
        forecastRuleMap.put(61, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(62, "Mostly clear with little temperature change.");
        forecastRuleMap.put(63,
                "Increasing clouds and warmer. Precipitation possible within 12 to 24 hours. Windy.");
        forecastRuleMap.put(64, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(65, "Mostly clear with little temperature change.");
        forecastRuleMap.put(66, "Increasing clouds and warmer. Precipitation possible within 12 hours.");
        forecastRuleMap.put(67, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(68, "Mostly clear with little temperature change.");
        forecastRuleMap.put(69, "Increasing clouds and warmer. Precipitation likley.");
        forecastRuleMap.put(70, "Clearing and cooler. Precipitation ending within 6 hours.");
        forecastRuleMap.put(71, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(72, "Clearing and cooler. Precipitation ending within 6 hours.");
        forecastRuleMap.put(73, "Mostly clear with little temperature change.");
        forecastRuleMap.put(74, "Clearing and cooler. Precipitation ending within 6 hours.");
        forecastRuleMap.put(75, "Partly cloudy and cooler.");
        forecastRuleMap.put(76, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(77, "Mostly clear and cooler.");
        forecastRuleMap.put(78, "Clearing and cooler. Precipitation ending within 6 hours.");
        forecastRuleMap.put(79, "Mostly clear with little temperature change.");
        forecastRuleMap.put(80, "Clearing and cooler. Precipitation ending within 6 hours.");
        forecastRuleMap.put(81, "Mostly clear and cooler.");
        forecastRuleMap.put(82, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(83, "Mostly clear with little temperature change.");
        forecastRuleMap.put(84,
                "Increasing clouds with little temperature change. Precipitation possible within 24 hours.");
        forecastRuleMap.put(85, "Mostly cloudy and cooler. Precipitation continuing.");
        forecastRuleMap.put(86, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(87, "Mostly clear with little temperature change.");
        forecastRuleMap.put(88, "Mostly cloudy and cooler. Precipitation likely.");
        forecastRuleMap.put(89, "Mostly cloudy with little temperature change. Precipitation continuing.");
        forecastRuleMap.put(90, "Mostly cloudy with little temperature change. Precipitation likely.");
        forecastRuleMap.put(91, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(92, "Mostly clear with little temperature change.");
        forecastRuleMap.put(93, "Increasing clouds and cooler. Precipitation possible and windy within 6 hours.");
        forecastRuleMap.put(94,
                "Increasing clouds with little temperature change. Precipitation possible and windy within 6 hours.");
        forecastRuleMap.put(95, "Mostly cloudy and cooler. Precipitation continuing. Increasing winds.");
        forecastRuleMap.put(96, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(97, "Mostly clear with little temperature change.");
        forecastRuleMap.put(98, "Mostly cloudy and cooler. Precipitation likely. Increasing winds.");
        forecastRuleMap.put(99,
                "Mostly cloudy with little temperature change. Precipitation continuing. Increasing winds.");
        forecastRuleMap.put(100,
                "Mostly cloudy with little temperature change. Precipitation likely. Increasing winds.");
        forecastRuleMap.put(101, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(102, "Mostly clear with little temperature change.");
        forecastRuleMap.put(103,
                "Increasing clouds and cooler. Precipitation possible within 12 to 24 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(104,
                "Increasing clouds with little temperature change. Precipitation possible within 12 to 24 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(105, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(106, "Mostly clear with little temperature change.");
        forecastRuleMap.put(107,
                "Increasing clouds and cooler. Precipitation possible within 6 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(108,
                "Increasing clouds with little temperature change. Precipitation possible within 6 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(109,
                "Mostly cloudy and cooler. Precipitation ending within 12 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(110, "Mostly cloudy and cooler. Possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(111,
                "Mostly cloudy with little temperature change. Precipitation ending within 12 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(112,
                "Mostly cloudy with little temperature change. Possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(113,
                "Mostly cloudy and cooler. Precipitation ending within 12 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(114, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(115, "Mostly clear with little temperature change.");
        forecastRuleMap.put(116,
                "Mostly cloudy and cooler. Precipitation possible within 24 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(117,
                "Mostly cloudy with little temperature change. Precipitation ending within 12 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(118,
                "Mostly cloudy with little temperature change. Precipitation possible within 24 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(119, "Clearing, cooler and windy. Precipitation ending within 6 hours.");
        forecastRuleMap.put(120, "Clearing, cooler and windy.");
        forecastRuleMap.put(121,
                "Mostly cloudy and cooler. Precipitation ending within 6 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(122, "Mostly cloudy and cooler. Windy with possible wind shift o the W, NW, or N.");
        forecastRuleMap.put(123, "Clearing, cooler and windy.");
        forecastRuleMap.put(124, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(125, "Mostly clear with little temperature change.");
        forecastRuleMap.put(126,
                "Mostly cloudy with little temperature change. Precipitation possible within 12 hours. Windy.");
        forecastRuleMap.put(127, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(128, "Mostly clear with little temperature change.");
        forecastRuleMap.put(129,
                "Increasing clouds and cooler. Precipitation possible within 12 hours, possibly heavy at times. Windy.");
        forecastRuleMap.put(130, "Mostly cloudy and cooler. Precipitation ending within 6 hours. Windy.");
        forecastRuleMap.put(131, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(132, "Mostly clear with little temperature change.");
        forecastRuleMap.put(133, "Mostly cloudy and cooler. Precipitation possible within 12 hours. Windy.");
        forecastRuleMap.put(134, "Mostly cloudy and cooler. Precipitation ending in 12 to 24 hours.");
        forecastRuleMap.put(135, "Mostly cloudy and cooler.");
        forecastRuleMap.put(136,
                "Mostly cloudy and cooler. Precipitation continuing, possible heavy at times. Windy.");
        forecastRuleMap.put(137, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(138, "Mostly clear with little temperature change.");
        forecastRuleMap.put(139, "Mostly cloudy and cooler. Precipitation possible within 6 to 12 hours. Windy.");
        forecastRuleMap.put(140,
                "Mostly cloudy with little temperature change. Precipitation continuing, possibly heavy at times. Windy.");
        forecastRuleMap.put(141, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(142, "Mostly clear with little temperature change.");
        forecastRuleMap.put(143,
                "Mostly cloudy with little temperature change. Precipitation possible within 6 to 12 hours. Windy.");
        forecastRuleMap.put(144, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(145, "Mostly clear with little temperature change.");
        forecastRuleMap.put(146,
                "Increasing clouds with little temperature change. Precipitation possible within 12 hours, possibly heavy at times. Windy.");
        forecastRuleMap.put(147, "Mostly cloudy and cooler. Windy.");
        forecastRuleMap.put(148,
                "Mostly cloudy and cooler. Precipitation continuing, possibly heavy at times. Windy.");
        forecastRuleMap.put(149, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(150, "Mostly clear with little temperature change.");
        forecastRuleMap.put(151, "Mostly cloudy and cooler. Precipitation likely, possibly heavy at times. Windy.");
        forecastRuleMap.put(152,
                "Mostly cloudy with little temperature change. Precipitation continuing, possibly heavy at times. Windy.");
        forecastRuleMap.put(153,
                "Mostly cloudy with little temperature change. Precipitation likely, possibly heavy at times. Windy.");
        forecastRuleMap.put(154, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(155, "Mostly clear with little temperature change.");
        forecastRuleMap.put(156, "Increasing clouds and cooler. Precipitation possible within 6 hours. Windy.");
        forecastRuleMap.put(157,
                "Increasing clouds with little temperature change. Precipitation possible within 6 hours. Windy");
        forecastRuleMap.put(158,
                "Increasing clouds and cooler. Precipitation continuing. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(159, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(160, "Mostly clear with little temperature change.");
        forecastRuleMap.put(161,
                "Mostly cloudy and cooler. Precipitation likely. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(162,
                "Mostly cloudy with little temperature change. Precipitation continuing. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(163,
                "Mostly cloudy with little temperature change. Precipitation likely. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(164,
                "Increasing clouds and cooler. Precipitation possible within 6 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(165, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(166, "Mostly clear with little temperature change.");
        forecastRuleMap.put(167,
                "Increasing clouds and cooler. Precipitation possible within 6 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(168,
                "Increasing clouds with little temperature change. Precipitation possible within 6 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(169,
                "Increasing clouds with little temperature change. Precipitation possible within 6 hours possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(170, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(171, "Mostly clear with little temperature change.");
        forecastRuleMap.put(172,
                "Increasing clouds and cooler. Precipitation possible within 6 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(173,
                "Increasing clouds with little temperature change. Precipitation possible within 6 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(174, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(175, "Mostly clear with little temperature change.");
        forecastRuleMap.put(176,
                "Increasing clouds and cooler. Precipitation possible within 12 to 24 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(177,
                "Increasing clouds with little temperature change. Precipitation possible within 12 to 24 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(178,
                "Mostly cloudy and cooler. Precipitation possibly heavy at times and ending within 12 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(179, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(180, "Mostly clear with little temperature change.");
        forecastRuleMap.put(181,
                "Mostly cloudy and cooler. Precipitation possible within 6 to 12 hours, possibly heavy at times. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(182,
                "Mostly cloudy with little temperature change. Precipitation ending within 12 hours. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(183,
                "Mostly cloudy with little temperature change. Precipitation possible within 6 to 12 hours, possibly heavy at times. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(184, "Mostly cloudy and cooler. Precipitation continuing.");
        forecastRuleMap.put(185, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(186, "Mostly clear with little temperature change.");
        forecastRuleMap.put(187,
                "Mostly cloudy and cooler. Precipitation likely. Windy with possible wind shift to the W, NW, or N.");
        forecastRuleMap.put(188, "Mostly cloudy with little temperature change. Precipitation continuing.");
        forecastRuleMap.put(189, "Mostly cloudy with little temperature change. Precipitation likely.");
        forecastRuleMap.put(190, "Partly cloudy with little temperature change.");
        forecastRuleMap.put(191, "Mostly clear with little temperature change.");
        forecastRuleMap.put(192,
                "Mostly cloudy and cooler. Precipitation possible within 12 hours, possibly heavy at times. Windy.");
        forecastRuleMap.put(193, "FORECAST REQUIRES 3 HOURS OF RECENT DATA");
        forecastRuleMap.put(194, "Mostly clear and cooler.");
        forecastRuleMap.put(195, "Mostly clear and cooler.");
        forecastRuleMap.put(196, "Mostly clear and cooler.");

        forecastRuleNumberAsString = forecastRuleMap.get(forecastRuleNumber);
        return forecastRuleNumberAsString;
    }

    /**
     * get the value from the timeOfSunrise field
     *
     * @return timeOfSunrise - the timeOfSunrise value as a String
     */
    public String getTimeOfSunrise() {
        this.timeOfSunrise.flip();
        short timeOfSunrise = this.timeOfSunrise.order(ByteOrder.LITTLE_ENDIAN).getShort();
        double timeOfSunriseAsDouble = (double) timeOfSunrise;
        timeOfSunriseAsDouble = timeOfSunriseAsDouble / 100;

        String hour = "";
        String minute = "";
        String timeOfSunriseString = "";

        // use the modulo to get the fraction and integer of the time
        double fraction = timeOfSunriseAsDouble % 1;
        double integral = timeOfSunriseAsDouble - fraction;
        fraction = fraction * 100;
        int integralInt = (new Double(integral)).intValue();

        //convert the exponent to a minute string
        hour = String.format("%02d", (Object) integralInt);

        //convert the exponent to a minute string
        minute = String.format("%02d", (Object) Math.round(fraction));

        timeOfSunriseString = hour + ":" + minute;
        return timeOfSunriseString;
    }

    /**
     * get the value from the timeOfSunset field
     *
     * @return timeOfSunset - the timeOfSunset value as a String
     */
    public String getTimeOfSunset() {
        this.timeOfSunset.flip();
        short timeOfSunset = this.timeOfSunset.order(ByteOrder.LITTLE_ENDIAN).getShort();
        double timeOfSunsetAsDouble = (double) timeOfSunset;
        timeOfSunsetAsDouble = timeOfSunsetAsDouble / 100;

        String hour = "";
        String minute = "";
        String timeOfSunsetString = "";

        // use the modulo to get the fraction and integer of the time
        double fraction = timeOfSunsetAsDouble % 1;
        double integral = timeOfSunsetAsDouble - fraction;
        fraction = fraction * 100;
        int integralInt = (new Double(integral)).intValue();

        //convert the exponent to a minute string
        hour = String.format("%02d", (Object) integralInt);

        //convert the exponent to a minute string
        minute = String.format("%02d", (Object) Math.round(fraction));

        timeOfSunsetString = hour + ":" + minute;
        return timeOfSunsetString;
    }

    /**
     * get the value from the recordDelimiter field
     *
     * @return recordDelimiter - the recordDelimiter as a Hex encoded String
     */
    public String getRecordDelimiterAsHexString() {
        this.recordDelimiter.flip();
        String delim1 = new String(Hex.encodeHex(new byte[] { this.recordDelimiter.get() }));
        String delim2 = new String(Hex.encodeHex(new byte[] { this.recordDelimiter.get() }));

        String recordDelimiter = delim1 + delim2;
        return recordDelimiter;
    }

    /**
     * get the value from the crcChecksum field
     */
    public ByteBuffer getCrcChecksum() {
        return this.crcChecksum;
    }

}