//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//--------------------------------------------------------------------------
//
//  humutil.c - functions to set mission and mission states for DS1922.
//  Version 2.00
//

#include <time.h>
#include "math.h"
#include "humutil.h"
#include "mbee77.h"
#include "pw77.h"

// Temperature resolution in degrees Celsius
double temperatureResolution = 0.5;

// max and min temperature
double maxTemperature = 85, minTemperature = -40;

// should we update the Real time clock?
SMALLINT updatertc = FALSE;

#define MAX_READ_RETRY_CNT 15

// *****************************************************************************
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register addresses and control bits
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// *****************************************************************************

/** Address of the Real-time Clock Time value*/
#define RTC_TIME 0x200
/** Address of the Real-time Clock Date value*/
#define RTC_DATE 0x203

/** Address of the Sample Rate Register */
#define SAMPLE_RATE 0x206 // 2 bytes, LSB first, MSB no greater than 0x3F

/** Address of the Temperature Low Alarm Register */
#define TEMPERATURE_LOW_ALARM_THRESHOLD 0x208
/** Address of the Temperature High Alarm Register */
#define TEMPERATURE_HIGH_ALARM_THRESHOLD 0x209

/** Address of the Data Low Alarm Register */
#define DATA_LOW_ALARM_THRESHOLD 0x20A
/** Address of the Data High Alarm Register */
#define DATA_HIGH_ALARM_THRESHOLD 0x20B

/** Address of the last temperature conversion's LSB */
#define LAST_TEMPERATURE_CONVERSION_LSB 0x20C
/** Address of the last temperature conversion's MSB */
#define LAST_TEMPERATURE_CONVERSION_MSB 0x20D

/** Address of the last data conversion's LSB */
#define LAST_DATA_CONVERSION_LSB 0x20E
/** Address of the last data conversion's MSB */
#define LAST_DATA_CONVERSION_MSB 0x20F

/** Address of Temperature Control Register */
#define TEMPERATURE_CONTROL_REGISTER 0x210
/** Temperature Control Register Bit: Enable Data Low Alarm */
#define TCR_BIT_ENABLE_TEMPERATURE_LOW_ALARM 0x01
/** Temperature Control Register Bit: Enable Data Low Alarm */
#define TCR_BIT_ENABLE_TEMPERATURE_HIGH_ALARM 0x02

/** Address of Data Control Register */
#define DATA_CONTROL_REGISTER 0x211
/** Data Control Register Bit: Enable Data Low Alarm */
#define DCR_BIT_ENABLE_DATA_LOW_ALARM 0x01
/** Data Control Register Bit: Enable Data High Alarm */
#define DCR_BIT_ENABLE_DATA_HIGH_ALARM 0x02

/** Address of Real-Time Clock Control Register */
#define RTC_CONTROL_REGISTER 0x212
/** Real-Time Clock Control Register Bit: Enable Oscillator */
#define RCR_BIT_ENABLE_OSCILLATOR 0x01
/** Real-Time Clock Control Register Bit: Enable High Speed Sample */
#define RCR_BIT_ENABLE_HIGH_SPEED_SAMPLE 0x02

/** Address of Mission Control Register */
#define MISSION_CONTROL_REGISTER 0x213
/** Mission Control Register Bit: Enable Temperature Logging */
#define MCR_BIT_ENABLE_TEMPERATURE_LOGGING 0x01
/** Mission Control Register Bit: Enable Data Logging */
#define MCR_BIT_ENABLE_DATA_LOGGING 0x02
/** Mission Control Register Bit: Set Temperature Resolution */
#define MCR_BIT_TEMPERATURE_RESOLUTION 0x04
/** Mission Control Register Bit: Set Data Resolution */
#define MCR_BIT_DATA_RESOLUTION 0x08
/** Mission Control Register Bit: Enable Rollover */
#define MCR_BIT_ENABLE_ROLLOVER 0x10
/** Mission Control Register Bit: Start Mission on Temperature Alarm */
#define MCR_BIT_START_MISSION_ON_TEMPERATURE_ALARM 0x20

/** Address of Alarm Status Register */
#define ALARM_STATUS_REGISTER 0x214
/** Alarm Status Register Bit: Temperature Low Alarm */
#define ASR_BIT_TEMPERATURE_LOW_ALARM 0x01
/** Alarm Status Register Bit: Temperature High Alarm */
#define ASR_BIT_TEMPERATURE_HIGH_ALARM 0x02
/** Alarm Status Register Bit: Data Low Alarm */
#define ASR_BIT_DATA_LOW_ALARM 0x04
/** Alarm Status Register Bit: Data High Alarm */
#define ASR_BIT_DATA_HIGH_ALARM 0x08
/** Alarm Status Register Bit: Battery On Reset */
#define ASR_BIT_BATTERY_ON_RESET 0x80

/** Address of General Status Register */
#define GENERAL_STATUS_REGISTER 0x215
/** General Status Register Bit: Sample In Progress */
#define GSR_BIT_SAMPLE_IN_PROGRESS 0x01
/** General Status Register Bit: Mission In Progress */
#define GSR_BIT_MISSION_IN_PROGRESS 0x02
/** General Status Register Bit: Conversion In Progress */
#define GSR_BIT_CONVERSION_IN_PROGRESS 0x04
/** General Status Register Bit: Memory Cleared */
#define GSR_BIT_MEMORY_CLEARED 0x08
/** General Status Register Bit: Waiting for Temperature Alarm */
#define GSR_BIT_WAITING_FOR_TEMPERATURE_ALARM 0x10
/** General Status Register Bit: Forced Conversion In Progress */
#define GSR_BIT_FORCED_CONVERSION_IN_PROGRESS 0x20

/** Address of the Mission Start Delay */
#define MISSION_START_DELAY 0x216 // 3 bytes, LSB first

/** Address of the Mission Timestamp Time value*/
#define MISSION_TIMESTAMP_TIME 0x219
/** Address of the Mission Timestamp Date value*/
#define MISSION_TIMESTAMP_DATE 0x21C

/** Address of Device Configuration Register */
#define DEVICE_CONFIGURATION_BYTE 0x226

// 1 byte, alternating ones and zeroes indicates passwords are enabled
/** Address of the Password Control Register. */
#define PASSWORD_CONTROL_REGISTER 0x227

// 8 bytes, write only, for setting the Read Access Password
/** Address of Read Access Password. */
#define READ_ACCESS_PASSWORD 0x228

// 8 bytes, write only, for setting the Read Access Password
/** Address of the Read Write Access Password. */
#define READ_WRITE_ACCESS_PASSWORD 0x230

// 3 bytes, LSB first
/** Address of the Mission Sample Count */
#define MISSION_SAMPLE_COUNT 0x220

// 3 bytes, LSB first
/** Address of the Device Sample Count */
#define DEVICE_SAMPLE_COUNT 0x223

// first year that calendar starts counting years from
#define FIRST_YEAR_EVER 2000

// maximum size of the mission log
#define MISSION_LOG_SIZE 8192

// mission log size for odd combination of resolutions (i.e. 8-bit temperature
// & 16-bit data or 16-bit temperature & 8-bit data
#define ODD_MISSION_LOG_SIZE 7680

#define TEMPERATURE_CHANNEL 0
#define DATA_CHANNEL 1

/** 1-Wire command for Clear Memory With Password */
#define CLEAR_MEMORY_PW_COMMAND 0x96
/** 1-Wire command for Start Mission With Password */
#define START_MISSION_PW_COMMAND 0xCC
/** 1-Wire command for Stop Mission With Password */
#define STOP_MISSION_PW_COMMAND 0x33
/** 1-Wire command for Forced Conversion */
#define FORCED_CONVERSION 0x55

// *****************************************************************************
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Sensor read/write
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// *****************************************************************************

/**
 * Retrieves the 1-Wire device sensor state.  This state is
 * returned as a byte array.  Pass this byte array to the 'get'
 * and 'set' methods.  If the device state needs to be changed then call
 * the 'writeDevice' to finalize the changes.
 *
 * @return 1-Wire device sensor state
 */
SMALLINT readDevice(int portnum, uchar *SNum, uchar *buffer, configLog *config) {
  int i;
  int retryCnt = MAX_READ_RETRY_CNT;
  uchar temp_buff[96];
  // reference humidities that the calibration was calculated over
  double ref1 = 20.0, ref2 = 60.0, ref3 = 90.0;
  // the average value for each reference point
  double read1 = 0.0, read2 = 0.0, read3 = 0.0;
  // the average error for each reference point
  double error1 = 0.0, error2 = 0.0, error3 = 0.0;
  double ref1sq, ref2sq, ref3sq;

  config->adDeviceBits = 10;
  config->adReferenceVoltage = 5.02;

  do {
    if (readPageCRCEE77(0, portnum, SNum, 16, &temp_buff[0]) && readPageCRCEE77(0, portnum, SNum, 17, &temp_buff[32]) &&
        readPageCRCEE77(0, portnum, SNum, 18, &temp_buff[64])) {
      for (i = 0; i < 96; i++)
        buffer[i] = temp_buff[i];
    } else {
      retryCnt++;
    }
  } while (retryCnt < MAX_READ_RETRY_CNT);

  switch (config->configByte) {
  case 0x00:
    config->lowTemp = -40;
    config->highTemp = 125;
    break;

  case 0x20:
    config->lowTemp = -40;
    config->highTemp = 125;
    config->hasHumidity = TRUE;
    break;

  case 0x40:
    config->lowTemp = -40;
    config->highTemp = 125;
    break;

  case 0x60:
    config->lowTemp = 0;
    config->highTemp = 125;
    break;

  default:
    config->lowTemp = -40;
    config->highTemp = 125;
    break;
  }

  if (config->hasHumidity) {
    config->useHumidityCalibration = TRUE;

    ref1 = decodeHumidity(&buffer[72], 2, TRUE, *config);
    read1 = decodeHumidity(&buffer[74], 2, TRUE, *config);
    error1 = read1 - ref1;
    ref2 = decodeHumidity(&buffer[76], 2, TRUE, *config);
    read2 = decodeHumidity(&buffer[78], 2, TRUE, *config);
    error2 = read2 - ref2;
    ref3 = decodeHumidity(&buffer[80], 2, TRUE, *config);
    read3 = decodeHumidity(&buffer[82], 2, TRUE, *config);
    error3 = read3 - ref3;

    ref1sq = ref1 * ref1;
    ref2sq = ref2 * ref2;
    ref3sq = ref3 * ref3;

    config->humCoeffB = ((ref2sq - ref1sq) * (error3 - error1) + ref3sq * (error1 - error2) + ref1sq * (error2 - error1)) /
                        ((ref2sq - ref1sq) * (ref3 - ref1) + (ref3sq - ref1sq) * (ref1 - ref2));
    config->humCoeffA = (error2 - error1 + config->humCoeffB * (ref1 - ref2)) / (ref2sq - ref1sq);
    config->humCoeffC = error1 - config->humCoeffA * ref1sq - config->humCoeffB * ref1;
  }

  config->useTemperatureCalibration = TRUE;

  ref2 = decodeHumidity(&buffer[64], 2, TRUE, *config);
  read2 = decodeHumidity(&buffer[66], 2, TRUE, *config);
  error2 = read2 - ref2;
  ref3 = decodeHumidity(&buffer[68], 2, TRUE, *config);
  read3 = decodeHumidity(&buffer[70], 2, TRUE, *config);
  error3 = read3 - ref3;
  ref1 = 60.0;
  error1 = error2;
  read1 = ref1 + error1;

  ref1sq = ref1 * ref1;
  ref2sq = ref2 * ref2;
  ref3sq = ref3 * ref3;

  config->tempCoeffB = ((ref2sq - ref1sq) * (error3 - error1) + ref3sq * (error1 - error2) + ref1sq * (error2 - error1)) /
                       ((ref2sq - ref1sq) * (ref3 - ref1) + (ref3sq - ref1sq) * (ref1 - ref2));
  config->tempCoeffA = (error2 - error1 + config->tempCoeffB * (ref1 - ref2)) / (ref2sq - ref1sq);
  config->tempCoeffC = error1 - config->tempCoeffA * ref1sq - config->tempCoeffB * ref1;

  config->useTempCalforHumidity = FALSE;

  return TRUE;
}

/**
 * Writes the 1-Wire device sensor state that
 * have been changed by 'set' methods.  Only the state registers that
 * changed are updated.  This is done by referencing a field information
 * appended to the state data.
 *
 * @param  state 1-Wire device sensor state
 *
 */
SMALLINT writeDevice(int portnum, uchar *SNum, uchar *state, SMALLINT updatertc) {
  int start = 0;
  int str_add = 0;

  if (getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MISSION_IN_PROGRESS)) {
    printf("get flag error.\n");
    return FALSE;
  }

  start = updatertc ? 0 : 6;
  str_add = start + 512;

  if (!writeEE77(0, portnum, SNum, str_add, &state[start], (64 - start))) {
    printf("writing error.\n");
    return FALSE;
  }

  return TRUE;
}

/**
 * Reads a single byte from the DS1922.  Note that the preferred manner
 * of reading from the DS1922 Thermocron is through the <code>readDevice()</code>
 * method or through the <code>MemoryBank</code> objects returned in the
 * <code>getMemoryBanks()</code> method.
 *
 * memAddr the address to read from  (in the range of 0x200-0x21F)
 */
uchar readByte(int portnum, uchar *SNum, int memAddr) {
  uchar buffer[32];
  int page;

  page = memAddr / 32;

  readPageCRCEE77(0, portnum, SNum, page, &buffer[0]);

  return buffer[memAddr % 32];
}

/**
 * Gets the status of the specified flag from the specified register.
 * This method actually communicates with the Thermocron.  To improve
 * performance if you intend to make multiple calls to this method,
 * first call readDevice() and use the
 * getFlag(int, byte, byte[]) method instead.</p>
 *
 * The DS1922 Thermocron has two sets of flags.  One set belongs
 * to the control register.  When reading from the control register,
 * valid values for bitMask.
 *
 * @param register address of register containing the flag (valid values
 * are CONTROL_REGISTER and STATUS_REGISTER)
 * @param bitMask the flag to read (see above for available options)
 *
 * @return the status of the flag, where true
 * signifies a "1" and false signifies a "0"
 */
SMALLINT getFlag(int portnum, uchar *SNum, int reg, uchar bitMask) { return ((readByte(portnum, SNum, reg) & bitMask) != 0); }

/**
 * <p>Sets the status of the specified flag in the specified register.
 * If a mission is in progress a <code>OneWireIOException</code> will be thrown
 * (one cannot write to the registers while a mission is commencing).  This method
 * is the preferred manner of setting the DS1922 status and control flags.
 * The method <code>writeDevice()</code> must be called to finalize
 * changes to the device.  Note that multiple 'set' methods can
 * be called before one call to <code>writeDevice()</code>.</p>
 *
 * <p>For more information on valid values for the <code>bitMask</code>
 * parameter, see the {@link #getFlag(int,byte) getFlag(int,byte)} method.</p>
 *
 * @param register address of register containing the flag (valid values
 * are <code>CONTROL_REGISTER</code> and <code>STATUS_REGISTER</code>)
 * @param bitMask the flag to read (see {@link #getFlag(int,byte) getFlag(int,byte)}
 * for available options)
 * @param flagValue new value for the flag (<code>true</code> is logic "1")
 * @param state current state of the device returned from <code>readDevice()</code>
 */
void setFlag(int reg, uchar bitMask, SMALLINT flagValue, uchar *state) {
  uchar flags;

  reg = reg & 0x3F;

  flags = state[reg];

  if (flagValue)
    flags = (uchar)(flags | bitMask);
  else
    flags = (uchar)(flags & ~(bitMask));

  // write the regs back
  state[reg] = flags;
}

// *****************************************************************************
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// DS1922 Device Specific Functions
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// *****************************************************************************

/**
 * Stops the currently running mission.
 *
 */
SMALLINT stopMission(int portnum, uchar *SNum) {
  int i;
  uchar buffer[10];

  if (!getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MISSION_IN_PROGRESS)) {
    OWERROR(OWERROR_HYGRO_STOP_MISSION_UNNECESSARY);
    return FALSE;
  }

  if (owAccess(portnum)) {
    buffer[0] = STOP_MISSION_PW_COMMAND;
    for (i = 0; i < 8; i++)
      buffer[i + 1] = psw[i];
    buffer[9] = 0xFF;

    if (!owBlock(portnum, FALSE, &buffer[0], 10)) {
      OWERROR(OWERROR_BLOCK_FAILED);
      return FALSE;
    }
  }

  if (getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MISSION_IN_PROGRESS)) {
    OWERROR(OWERROR_HYGRO_STOP_MISSION_ERROR);
    return FALSE;
  }

  return TRUE;
}

/**
 * Erases the log memory from this missioning device.
 */
SMALLINT clearMemory(int portnum, uchar *SNum) {
  int i;
  uchar buffer[10];

  if (owAccess(portnum)) {
    buffer[0] = CLEAR_MEMORY_PW_COMMAND;
    for (i = 0; i < 8; i++)
      buffer[i + 1] = psw[i];
    buffer[9] = 0xFF;

    if (!owBlock(portnum, FALSE, &buffer[0], 10)) {
      OWERROR(OWERROR_BLOCK_FAILED);
      return FALSE;
    }

    // wait 2 ms for Clear Memory to comlete
    msDelay(2);

    if (!getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MEMORY_CLEARED)) {
      printf("error.\n"); //       "OneWireContainer41-Clear Memory failed.  Check read/write password.");
      return FALSE;
    }
  }

  return TRUE;
}

// *****************************************************************************
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Mission Interface Functions
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// *****************************************************************************

/**
 * Begins a new mission on this missioning device.
 *
 * @param sampleRate indicates the sampling rate, in seconds, that
 *        this missioning device should log samples.
 * @param missionStartDelay indicates the amount of time, in seconds,
 *        that should pass before the mission begins.
 * @param rolloverEnabled if <code>false</code>, this device will stop
 *        recording new samples after the data log is full.  Otherwise,
 *        it will replace samples starting at the beginning.
 * @param syncClock if <code>true</code>, the real-time clock of this
 *        missioning device will be synchronized with the current time
 *        according to this <code>java.util.Date</code>.
 * @param startUponTempAlarm if <code>true</code>, the mission will begin
 *        after a temperature alarm has occurred.
 */
SMALLINT startMission(int portnum, uchar *SNum, startMissionData set, configLog *config) {
  int i;
  time_t tint;
  struct tm *tstruct;
  uchar buffer[10];
  uchar state[96];
  int sampleRate;

  sampleRate = set.sampleRate;

  if (getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MISSION_IN_PROGRESS)) {
    printf("get flag error.\n");
    return FALSE;
  } else {
    if (!readDevice(portnum, SNum, &state[0], config)) {
      printf("read device error.\n");
      return FALSE;
    }
  }

  setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_TEMPERATURE_LOGGING, set.tempEnabled, &state[0]);
  setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_DATA_LOGGING, set.dataEnabled, &state[0]);
  setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_TEMPERATURE_RESOLUTION, set.highTempResolution, &state[0]);
  setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_DATA_RESOLUTION, set.highDataResolution, &state[0]);

  if (set.tempAlarm) {
    setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_START_MISSION_ON_TEMPERATURE_ALARM, set.tempAlarm, &state[0]);
  } else {
    setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_START_MISSION_ON_TEMPERATURE_ALARM, set.tempAlarm, &state[0]);
  }

  if (set.highTempAlarm) {
    setFlag(TEMPERATURE_CONTROL_REGISTER, TCR_BIT_ENABLE_TEMPERATURE_HIGH_ALARM, set.highTempAlarm, &state[0]);
    state[TEMPERATURE_HIGH_ALARM_THRESHOLD & 0x3F] = (uchar)((set.highTemp * 2) + 82);
  } else {
    setFlag(TEMPERATURE_CONTROL_REGISTER, TCR_BIT_ENABLE_TEMPERATURE_HIGH_ALARM, set.highTempAlarm, &state[0]);
  }

  if (set.lowTempAlarm) {
    setFlag(TEMPERATURE_CONTROL_REGISTER, TCR_BIT_ENABLE_TEMPERATURE_LOW_ALARM, set.lowTempAlarm, &state[0]);
    state[TEMPERATURE_LOW_ALARM_THRESHOLD & 0x3F] = (uchar)((set.lowTemp * 2) + 82);
  } else {
    setFlag(TEMPERATURE_CONTROL_REGISTER, TCR_BIT_ENABLE_TEMPERATURE_LOW_ALARM, set.lowTempAlarm, &state[0]);
  }

  if (set.highDataAlarm) {
    setFlag(DATA_CONTROL_REGISTER, DCR_BIT_ENABLE_DATA_HIGH_ALARM, set.highDataAlarm, &state[0]);

    state[DATA_HIGH_ALARM_THRESHOLD & 0x3F] =
        (uchar)(((int)(((((((1 - config->humCoeffB) - sqrt(((config->humCoeffB - 1) * (config->humCoeffB - 1)) -
                                                           4 * config->humCoeffA * (config->humCoeffC + set.highData))) /
                           (2 * config->humCoeffA)) *
                          .0307) +
                         .958) *
                        ((1 << config->adDeviceBits) - 1)) /
                       config->adReferenceVoltage) &
                 0x3FC) >>
                (config->adDeviceBits - 8));
  } else {
    setFlag(DATA_CONTROL_REGISTER, DCR_BIT_ENABLE_DATA_HIGH_ALARM, set.highDataAlarm, &state[0]);
  }

  if (set.lowDataAlarm) {
    setFlag(DATA_CONTROL_REGISTER, DCR_BIT_ENABLE_DATA_LOW_ALARM, set.lowDataAlarm, &state[0]);

    state[DATA_LOW_ALARM_THRESHOLD & 0x3F] =
        (uchar)(((int)(((((((1 - config->humCoeffB) - sqrt(((config->humCoeffB - 1) * (config->humCoeffB - 1)) -
                                                           4 * config->humCoeffA * (config->humCoeffC + set.lowData))) /
                           (2 * config->humCoeffA)) *
                          .0307) +
                         .958) *
                        ((1 << config->adDeviceBits) - 1)) /
                       config->adReferenceVoltage) &
                 0x3FC) >>
                (config->adDeviceBits - 8));
  } else {
    setFlag(DATA_CONTROL_REGISTER, DCR_BIT_ENABLE_DATA_LOW_ALARM, set.lowDataAlarm, &state[0]);
  }

  if (sampleRate % 60 == 0 || sampleRate > 0x03FFF) {
    // convert to minutes
    sampleRate = (sampleRate / 60) & 0x03FFF;
    setFlag(RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_HIGH_SPEED_SAMPLE, FALSE, &state[0]);
  } else {
    setFlag(RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_HIGH_SPEED_SAMPLE, TRUE, &state[0]);
  }

  state[6] = (uchar)(sampleRate & 0x00FF);
  state[7] = (uchar)((sampleRate & 0x3F00) >> 8);

  state[22] = (uchar)(set.startDelay & 0x00FF);
  state[23] = (uchar)((set.startDelay & 0x3F00) >> 8);

  setFlag(MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_ROLLOVER, set.enableRollover, &state[0]);

  if (set.timeSync) {
    time(&tint);
    tint++;

    tstruct = localtime(&tint);

    setTime(RTC_TIME & 0x3F, tstruct->tm_hour, tstruct->tm_min, tstruct->tm_sec, FALSE, &state[0]);

    setDate(RTC_DATE & 0x3F, tstruct->tm_year, (tstruct->tm_mon + 1), tstruct->tm_mday, &state[0]);

    if (!getFlag(portnum, SNum, RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_OSCILLATOR)) {
      setFlag(RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_OSCILLATOR, TRUE, &state[0]);
    }
  } else if (!getFlag(portnum, SNum, RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_OSCILLATOR)) {
    // System.out.println("Enabling Oscillator");
    setFlag(RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_OSCILLATOR, TRUE, &state[0]);
  }

  // System.out.println("Clearing memory");
  clearMemory(portnum, SNum);

  // System.out.println("Updating Device state");
  if (!writeDevice(portnum, SNum, state, TRUE)) {
    return FALSE;
  }

  // System.out.println("Starting mission");
  if (owAccess(portnum)) {
    buffer[0] = START_MISSION_PW_COMMAND;
    for (i = 0; i < 8; i++)
      buffer[i + 1] = psw[i];
    buffer[9] = 0xFF;

    if (!owBlock(portnum, FALSE, &buffer[0], 10)) {
      OWERROR(OWERROR_BLOCK_FAILED);
      return FALSE;
    }
  }

  return TRUE;
}

/**
 * Loads the results of the currently running mission.  Must be called
 * before all mission result/status methods.
 */
SMALLINT loadMissionResults(int portnum, uchar *SNum, configLog config) {
  uchar state[96];
  uchar page[8192];
  configLog tempConfig;
  uchar upper, lower;
  double val, valsq, error, humCal, tempVal;
  int timeOffset;
  int month, day, year = 0;
  int hour, min, sec;
  int tempone, temptwo, tempthr;
  int pm = 0, i;
  int sampleCnt = 0;
  int sampleRate = 0;
  int tempBytes = 0;
  int dataBytes = 0;
  int maxSamples = 0;
  int wrapCount = 0;
  int offsetDepth = 0;
  int temperatureLogSize = 0;
  // default size of the log, could be different if using an odd
  // sample size combination.
  // check for rollover

  FILE *fidTemp;
  FILE *fidData;

  // read the register contents
  if (!readDevice(portnum, SNum, &state[0], &tempConfig)) {
    printf("read device error.\n");
    return FALSE;
  }

  // get the number of samples
  tempone = (int)(state[32] & 0x0000FF);
  temptwo = (int)(state[33] << 8) & 0x00FF00;
  tempthr = (int)(state[34] << 16) & 0xFF0000;
  sampleCnt = tempone + temptwo + tempthr;

  if (sampleCnt == 0) {
    printf("No, samples yet for this mission.\n");
    return FALSE;
  }

  // sample rate, in seconds
  tempone = (int)(state[SAMPLE_RATE & 0x3F] & 0x00FF);
  temptwo = (int)((state[(SAMPLE_RATE & 0x3F) + 1] << 8) & 0xFF00);
  sampleRate = tempone + temptwo;
  if (!getFlag(portnum, SNum, RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_HIGH_SPEED_SAMPLE))
    // if sample rate is in minutes, convert to seconds
    sampleRate = sampleRate * 60;

  // get day
  lower = state[MISSION_TIMESTAMP_DATE & 0x3F];
  upper = ((lower >> 4) & 0x0f);
  lower = (lower & 0x0f);
  day = upper * 10 + lower;

  // get month
  lower = state[(MISSION_TIMESTAMP_DATE & 0x3F) + 1];
  upper = ((lower >> 4) & 0x01);
  lower = (lower & 0x0f);
  month = upper * 10 + lower;

  // get year
  if ((state[(MISSION_TIMESTAMP_DATE & 0x3F) + 1] & 0x80) == 0x80)
    year = 100;
  lower = state[(MISSION_TIMESTAMP_DATE & 0x3F) + 2];
  upper = ((lower >> 4) & 0x0f);
  lower = (lower & 0x0f);
  year = upper * 10 + lower + FIRST_YEAR_EVER + year;

  // get seconds
  lower = state[MISSION_TIMESTAMP_TIME & 0x3F];
  upper = ((lower >> 4) & 0x07);
  lower = (lower & 0x0f);
  sec = (int)lower + (int)upper * 10;

  // get minutes
  lower = state[(MISSION_TIMESTAMP_TIME & 0x3F) + 1];
  upper = ((lower >> 4) & 0x07);
  lower = (lower & 0x0f);
  min = (int)lower + (int)upper * 10;

  // get hours
  lower = state[(MISSION_TIMESTAMP_TIME & 0x3F) + 2];
  upper = ((lower >> 4) & 0x07);
  lower = (lower & 0x0f);
  if ((upper & 0x04) != 0) {
    // extract the AM/PM byte (PM is indicated by a 1)
    if ((upper & 0x02) > 0)
      pm = 12;

    // isolate the 10s place
    upper &= 0x01;
  }
  hour = (int)(upper * 10) + (int)lower + (int)pm;

  // figure out how many bytes for each temperature sample
  // if it's being logged, add 1 to the size
  if (getFlag(portnum, SNum, MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_TEMPERATURE_LOGGING)) {
    tempBytes += 1;
    // if it's 16-bit resolution, add another 1 to the size
    if (getFlag(portnum, SNum, MISSION_CONTROL_REGISTER, MCR_BIT_TEMPERATURE_RESOLUTION))
      tempBytes += 1;
  }

  // figure out how many bytes for each data sample
  // if it's being logged, add 1 to the size
  if (getFlag(portnum, SNum, MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_DATA_LOGGING)) {
    dataBytes += 1;
    // if it's 16-bit resolution, add another 1 to the size
    if (getFlag(portnum, SNum, MISSION_CONTROL_REGISTER, MCR_BIT_DATA_RESOLUTION))
      dataBytes += 1;
  }

  // figure max number of samples
  switch (tempBytes + dataBytes) {
  case 1:
    maxSamples = 8192;
    break;
  case 2:
    maxSamples = 4096;
    break;
  case 3:
    maxSamples = 2560;
    break;
  case 4:
    maxSamples = 2048;
    break;
  default:
  case 0:
    // assert! should never, ever get here
    break;
  }

  if (getFlag(portnum, SNum, MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_ROLLOVER) &&
      (sampleCnt > maxSamples)) { // intentional assignment
    wrapCount = sampleCnt / maxSamples;
    offsetDepth = sampleCnt % maxSamples;
    sampleCnt = maxSamples;
  }

  // DEBUG: For bad SOICS
  if (!getFlag(portnum, SNum, MISSION_CONTROL_REGISTER, MCR_BIT_ENABLE_ROLLOVER) && (sampleCnt > maxSamples)) {
    printf("Device Error: rollover was not enabled, but it did occur.\n");
    return FALSE;
  }

  // figure out where the temperature bytes end, that's where
  // the data bytes begin
  temperatureLogSize = tempBytes * maxSamples;

  // calculate first log entry time offset, in seconds
  timeOffset = ((wrapCount * maxSamples) + offsetDepth) * sampleRate;

  if ((tempBytes * sampleCnt) > 0) {
    for (i = 0; i < ((tempBytes * sampleCnt / 32) + ((tempBytes * sampleCnt % 32) > 0 ? 1 : 0)); i++) {
      if (!readPageCRCEE77(2, portnum, SNum, i + 128, &page[i * 32]))
        return FALSE;
    }

    fidTemp = fopen("temp.log", "a+");

    fprintf(fidTemp, "mission start date and time: %d/%d/%d %d:%d:%d \n", month, day, year, hour, min, sec);
    fprintf(fidTemp, "time offset(s), temperature reading(C)\n");

    for (i = 0; i < sampleCnt; i++) {
      fprintf(fidTemp, "%d, ", timeOffset + i * sampleRate);
      if (config.useTemperatureCalibration) {
        val = decodeTemperature(&page[(offsetDepth + i * tempBytes) % maxSamples], tempBytes, TRUE, config);
        valsq = val * val;
        error = config.tempCoeffA * valsq + config.tempCoeffB * val + config.tempCoeffC;
        val = val - error;
        fprintf(fidTemp, "%f \n", val);
      } else {
        fprintf(fidTemp, "%f \n",
                decodeTemperature(&page[(offsetDepth + i * tempBytes) % maxSamples], tempBytes, TRUE, config));
      }
    }
    fflush(fidTemp);
    fclose(fidTemp);
    printf("The temperature data is in temp.log file.\n");
  }

  if ((sampleCnt * dataBytes) > 0) {
    for (i = 0; i < ((dataBytes * sampleCnt / 32) + ((dataBytes * sampleCnt % 32) > 0 ? 1 : 0)); i++) {
      if (!readPageCRCEE77(2, portnum, SNum, ((temperatureLogSize / 32) + i + 128), &page[temperatureLogSize + i * 32]))
        return FALSE;
    }

    fidData = fopen("data.log", "a+");

    fprintf(fidData, "mission start date and time: %d/%d/%d %d:%d:%d \n", month, day, year, hour, min, sec);
    fprintf(fidData, "time offset(s), data reading(percentage humidity or Volts)\n");

    for (i = 0; i < sampleCnt; i++) {
      fprintf(fidData, "%d, ", timeOffset + i * sampleRate);
      if (config.hasHumidity) {
        if (config.useHumidityCalibration) {
          val = decodeHumidity(&page[temperatureLogSize + ((offsetDepth + i * dataBytes) % maxSamples)], dataBytes, FALSE,
                               config);
          valsq = val * val;
          error = config.humCoeffA * valsq + config.humCoeffB * val + config.humCoeffC;
          val = val - error;

          if (config.useTempCalforHumidity) {
            tempVal = val;

            if ((temperatureLogSize > 0) && config.useTemperatureCalibration) {
              val = decodeTemperature(&page[temperatureLogSize + ((offsetDepth + i * tempBytes) % maxSamples)], tempBytes, TRUE,
                                      config);
              valsq = val * val;
              error = config.tempCoeffA * valsq + config.tempCoeffB * val + config.tempCoeffC;
              humCal = val - error;
            } else if (temperatureLogSize > 0) {
              humCal = decodeTemperature(&page[temperatureLogSize + ((offsetDepth + i * tempBytes) % maxSamples)], tempBytes,
                                         TRUE, config);
            } else
              humCal = 25.0;

            if (humCal <= 15)
              val = ((tempVal * 0.0307 + 0.958) - (0.8767 - 0.0035 * humCal + 0.000043 * humCal * humCal)) /
                    (0.035 + 0.000067 * humCal - 0.000002 * humCal * humCal);
            else
              val = ((tempVal * 0.0307 + 0.958) - (0.8767 - 0.0035 * humCal + 0.000043 * humCal * humCal)) /
                    (0.0317 + 0.000067 * humCal - 0.000002 * humCal * humCal);
          }

          if (val < 0.0)
            val = 0.0;

          fprintf(fidData, "%f \n", val);
        } else {
          val = decodeHumidity(&page[temperatureLogSize + ((offsetDepth + i * dataBytes) % maxSamples)], dataBytes, FALSE,
                               config);

          if (val < 0.0)
            val = 0.0;

          fprintf(fidData, "%f \n", val);
        }
      } else {
        fprintf(fidData, "%f \n",
                getADVoltage(&page[temperatureLogSize + (offsetDepth + i * dataBytes) % maxSamples], dataBytes, FALSE, config));
      }
    }
    fflush(fidData);
    fclose(fidData);
    printf("The humidity percentage or voltage is in data.log file.\n");
  }

  return TRUE;
}

// *****************************************************************************
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Temperature Interface Functions
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// *****************************************************************************

/**
 * Performs a temperature conversion.  Use the <code>state</code>
 * information to calculate the conversion time.
 *
 * @param  state byte array with device state information
 *
 */
SMALLINT doTemperatureConvert(int portnum, uchar *SNum, uchar *state) {
  uchar buffer[2];

  /* check for mission in progress */
  if (getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MISSION_IN_PROGRESS)) {
    printf("Can't force temperature read during a mission.\n");
    return FALSE;
  }

  owSerialNum(portnum, SNum, FALSE);

  if (!owAccess(portnum)) {
    OWERROR(OWERROR_DEVICE_SELECT_FAIL);
    return FALSE;
  }

  // perform the conversion
  buffer[0] = (uchar)FORCED_CONVERSION;
  buffer[1] = 0xFF;

  // send block (check copy indication complete)
  if (!owBlock(portnum, FALSE, &buffer[0], 2)) {
    OWERROR(OWERROR_BLOCK_FAILED);
    return FALSE;
  }

  msDelay(750);

  // grab the data
  state[LAST_TEMPERATURE_CONVERSION_LSB & 0x3F] = readByte(portnum, SNum, LAST_TEMPERATURE_CONVERSION_LSB);
  state[LAST_TEMPERATURE_CONVERSION_MSB & 0x3F] = readByte(portnum, SNum, LAST_TEMPERATURE_CONVERSION_MSB);

  return TRUE;
}

// *****************************************************************************
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A-to-D Interface Functions
//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// *****************************************************************************

/**
 * Performs a voltage conversion on one specified channel.
 * Use the method <code>getADVoltage(int,byte[])</code> to read
 * the result of this conversion, using the same <code>channel</code>
 * argument as this method uses.
 *
 * @param channel channel number in the range [0 to (<code>getNumberADChannels()</code> - 1)]
 * @param state current state of the device returned from <code>readDevice()</code>
 */
SMALLINT doADConvert(int portnum, uchar *SNum, uchar *state) {
  uchar buffer[2];

  /* check for mission in progress */
  if (getFlag(portnum, SNum, GENERAL_STATUS_REGISTER, GSR_BIT_MISSION_IN_PROGRESS)) {
    printf("Can't force temperature read during a mission.\n");
    return FALSE;
  }

  /* check that the RTC is running */
  if (!getFlag(portnum, SNum, RTC_CONTROL_REGISTER, RCR_BIT_ENABLE_OSCILLATOR)) {
    printf("Can't force A/D conversion if the oscillator is not enabled.\n");
    return FALSE;
  }

  owSerialNum(portnum, SNum, FALSE);

  if (!owAccess(portnum)) {
    OWERROR(OWERROR_DEVICE_SELECT_FAIL);
    return FALSE;
  }

  // perform the conversion
  buffer[0] = (uchar)FORCED_CONVERSION;
  buffer[1] = 0xFF;

  // send block (check copy indication complete)
  if (!owBlock(portnum, FALSE, &buffer[0], 2)) {
    OWERROR(OWERROR_BLOCK_FAILED);
    return FALSE;
  }

  msDelay(750);

  // grab the data
  state[LAST_DATA_CONVERSION_LSB & 0x3F] = readByte(portnum, SNum, LAST_DATA_CONVERSION_LSB);
  state[LAST_DATA_CONVERSION_MSB & 0x3F] = readByte(portnum, SNum, LAST_DATA_CONVERSION_MSB);

  return TRUE;
}

double getADVoltage(uchar *data, int length, SMALLINT reverse, configLog config) {
  double dval;
  // get the 10-bit value of vout
  int ival = 0;

  if (reverse && length == 2) {
    ival = ((data[0] & 0x0FF) << (config.adDeviceBits - 8));
    ival |= ((data[1] & 0x0FF) >> (16 - config.adDeviceBits));
  } else if (length == 2) {
    ival = ((data[1] & 0x0FF) << (config.adDeviceBits - 8));
    ival |= ((data[0] & 0x0FF) >> (16 - config.adDeviceBits));
  } else {
    ival = ((data[0] & 0x0FF) << (config.adDeviceBits - 8));
  }

  dval = (ival * config.adReferenceVoltage) / (1 << config.adDeviceBits);

  return dval;
}

//--------
//-------- Clock 'set' Methods
//--------

/**
 * Set the time in the DS1922 time register format.
 */
void setTime(int timeReg, int hours, int minutes, int seconds, SMALLINT AMPM, uchar *state) {
  uchar upper, lower;

  /* format in bytes and write seconds */
  upper = (uchar)(((seconds / 10) << 4) & 0xf0);
  lower = (uchar)((seconds % 10) & 0x0f);
  state[timeReg++] = (uchar)(upper | lower);

  /* format in bytes and write minutes */
  upper = (uchar)(((minutes / 10) << 4) & 0xf0);
  lower = (uchar)((minutes % 10) & 0x0f);
  state[timeReg++] = (uchar)(upper | lower);

  /* format in bytes and write hours/(12/24) bit */
  if (AMPM) {
    upper = 0x04;

    if (hours > 11)
      upper = (uchar)(upper | 0x02);

    // this next logic simply checks for a decade hour
    if (((hours % 12) == 0) || ((hours % 12) > 9))
      upper = (uchar)(upper | 0x01);

    if (hours > 12)
      hours = hours - 12;

    if (hours == 0)
      lower = 0x02;
    else
      lower = (uchar)((hours % 10) & 0x0f);
  } else {
    upper = (uchar)(hours / 10);
    lower = (uchar)(hours % 10);
  }

  upper = (uchar)((upper << 4) & 0xf0);
  lower = (uchar)(lower & 0x0f);
  state[timeReg] = (uchar)(upper | lower);
}

/**
 * Set the current date in the DS1922's real time clock.
 *
 * year - The year to set to, i.e. 2001.
 * month - The month to set to, i.e. 1 for January, 12 for December.
 * day - The day of month to set to, i.e. 1 to 31 in January, 1 to 30 in April.
 */
void setDate(int timeReg, int year, int month, int day, uchar *state) {
  uchar upper, lower;

  /* write the day byte (the upper holds 10s of days, lower holds single days) */
  upper = (uchar)(((day / 10) << 4) & 0xf0);
  lower = (uchar)((day % 10) & 0x0f);
  state[timeReg++] = (uchar)(upper | lower);

  /* write the month bit in the same manner, with the MSBit indicating
     the century (1 for 2000, 0 for 1900) */
  upper = (uchar)(((month / 10) << 4) & 0xf0);
  lower = (uchar)((month % 10) & 0x0f);
  state[timeReg++] = (uchar)(upper | lower);

  // now write the year
  year = year + 1900;
  year = year - FIRST_YEAR_EVER;

  if (year > 100) {
    state[timeReg - 1] |= 0x80;
    year -= 100;
  }
  upper = (uchar)(((year / 10) << 4) & 0xf0);
  lower = (uchar)((year % 10) & 0x0f);
  state[timeReg] = (uchar)(upper | lower);
}

/**
 * helper method for decoding temperature values
 */
double decodeTemperature(uchar *data, int length, SMALLINT reverse, configLog config) {
  double whole, fraction = 0;

  if (reverse && length == 2) {
    fraction = ((data[1] & 0x0FF) / 512.0);
    whole = (data[0] & 0x0FF) / 2.0 + (config.lowTemp - 1);
  } else if (length == 2) {
    fraction = ((data[0] & 0x0FF) / 512.0);
    whole = (data[1] & 0x0FF) / 2.0 + (config.lowTemp - 1);
  } else {
    whole = (data[0] & 0x0FF) / 2.0 + (config.lowTemp - 1);
  }

  return whole + fraction;
}

/**
 * helper method for decoding humidity values
 */
double decodeHumidity(uchar *data, int length, SMALLINT reverse, configLog config) {
  double val;

  // get the 10-bit value of Vout
  val = getADVoltage(data, length, reverse, config);

  // convert Vout to a humidity reading
  // this formula is from HIH-3610 sensor datasheet
  val = (val - .958) / .0307;

  return val;
}
