/* --COPYRIGHT--,BSD_EX
 * Copyright (c) 2014, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *******************************************************************************
 *
 *                       MSP430 CODE EXAMPLE DISCLAIMER
 *
 * MSP430 code examples are self-contained low-level programs that typically
 * demonstrate a single peripheral function or device feature in a highly
 * concise manner. For this the code may rely on the device's power-on default
 * register values and settings such as the clock configuration and care must
 * be taken when combining code from several examples to avoid potential side
 * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware
 * for an API functional library-approach to peripheral configuration.
 *
 * --/COPYRIGHT--*/
/*
 * Thermopile.c
 *
 *  Created on: May 25, 2020
 *      Author: JD Crutchfield
 */

#include <msp430.h>
#include "Thermopile.h"
#include "mcu.h"
#include "SD24.h"

long   g_lSD24ThermopileBuffer[SD24_NUM_AVERAGE_SAMPLES] = {0};
long   g_lSD24ThermistorBuffer[SD24_NUM_AVERAGE_SAMPLES] = {0};
long   g_lThermopileResult = 0;
long   g_lThermistorResult = 0;


/********************************************
 * Loading calibration data and Lookup tables
 ********************************************/
#pragma location =  0x1000
const long CalibrationThermopileOffset;

#pragma location =  0x1004
const long CalibrationThermopileScalingFactor;

#pragma location = 0x1008
const long CalibrationThermistorOffset;

#pragma location = 0x100C
const long S_CONV_x100;


//This calibration date must be first loaded into the MCU using the IT Thermometer Calibration Data loader project
// This is a list of ADC values calculated to correlate to Thermistor Voltage.  This is using ADC Ref=1.158V and based off APAT APP80A3 Sensor LUT table
#pragma location = 0x1010
const long ThermopileLUT[LUT_LENGTH];

// This is a list of ADC values calculated to correlate to ambient temperature.  This is using ADC Ref=1.158V and 154k R1 resistor and based off APAT APP80A3 Sensor LUT table
#pragma location = 0x10DC
const long ThermistorLUT[LUT_LENGTH];
//*****************************************



long LUTthermistor(long input, char direction){
    int lowerIndex, upperIndex, currentIndex;   //Index for LUT is in degrees Celsius
    long output, delta;
    long mDCelsius = 0;  // milli degrees

    if(direction == REVERSE_LOOKUP){    // input is Celcius and can be used for index
        lowerIndex = 0;
        upperIndex = LUT_LENGTH - 1;
        currentIndex = upperIndex / 2;

        //Binary Search in reverse sorted list - looking for 2 points closest to the input.
        while((upperIndex - 1) > lowerIndex){

            if(input > ThermistorLUT[currentIndex]){
                //Move search lower
                upperIndex = currentIndex;
                currentIndex = (upperIndex + lowerIndex) / 2;
            }
            else{
                //Move search higher
                lowerIndex = currentIndex;
                currentIndex = (upperIndex + lowerIndex) / 2;

            }
        }

        /*2 closes points in LUT found, going to do a weighted average between the two. */
        //delta between the input and lower calibration point
        // upper index is the smaller value because the list is reverse corrilated
        delta = ThermistorLUT[lowerIndex] - input;

        //Calculate the partial degree that this delta represents.  IE: 60% = 600 mC
        mDCelsius = (delta * 1000)/(ThermistorLUT[lowerIndex] - ThermistorLUT[upperIndex]);

        //Add total temperature together.  IE:   (30C * 1000) + 600 = 30600 mC = 30.6C
        mDCelsius += (long)lowerIndex * 1000;

        output = mDCelsius;
    }
    else{

    }

    return output;
}


long LUTthermopile(long input, char direction){ // using milliCelcius to all temp
    int lowerIndex, upperIndex, currentIndex;
    long output, delta = 0;
    long mDCelsius = 0;  // milli degrees
    long sd24Counts = 0;  // milli degrees

    if(direction == REVERSE_LOOKUP){    // input is Celcius and can be used for index
        lowerIndex = 0;
        upperIndex = LUT_LENGTH - 1;
        currentIndex = upperIndex / 2;

        //Binary Search - looking for 2 points closest to the input.  Loop until proper bucket is found
        while((upperIndex - 1) > lowerIndex){
            if(input > ThermopileLUT[currentIndex]){
                    //Move search higher
                lowerIndex = currentIndex;
                currentIndex = (upperIndex + lowerIndex) / 2;
            }
            else{   //Move search lower
                upperIndex = currentIndex;
                currentIndex = (upperIndex + lowerIndex) / 2;
            }
        }

        //Weighted average
        delta = input - ThermopileLUT[lowerIndex];
        mDCelsius = (delta * 1000)/(ThermopileLUT[upperIndex] - ThermopileLUT[lowerIndex]);
        mDCelsius += (long)lowerIndex * 1000;

        output = mDCelsius;
    }
    else{
        // Forward Look-up.  Used to find V_offset.
        // Use input voltage to set index bucket  Ex. at 31.6C:  input = 31600. Lowerindex = 31
        lowerIndex = input / 1000;
        upperIndex = lowerIndex + 1;

        //Check bounds of LUT
        if((lowerIndex >= 0) && (lowerIndex < LUT_LENGTH)){   //inbouds
            //Find delta between input voltage and calibration point  Ex cont:  Delta = 31600 - (31 * 1000) = 600
            delta = input - (lowerIndex * 1000);

            //Delta is used as the % to weight the average.  Ex cont: delta = 600/1000 mC = 60%.   we need the 60% point of Upper-lower
            //output from LUt is temperature in SD24 count value
            sd24Counts = (ThermopileLUT[upperIndex] - ThermopileLUT[lowerIndex])* delta/1000;

            //Add calulated delta above to Lower LUT value
            sd24Counts += ThermopileLUT[lowerIndex];

            output = sd24Counts;
        }
        else{}  //Out of bounds.

    }
    return output;
}




void InitTemperatureMeasurement(void){
    //Start measuring temp sensors and fill sample buffer for averaging.

    // Point to end to be sure to fully fill buffer
    g_uiSD24BufferPointer = 0;

    // Wait for buffer to fill.
    // Time taken= g_uiSD24BufferPointer / SD24_SAMPLES_PER_SECOND.
    while(g_uiSD24BufferPointer < (SD24_NUM_AVERAGE_SAMPLES-1)){
        LPM3;            // LPM0 for lower power waiting
        __no_operation();
    }

    //TODO Workaround because I'm pre-incrementing in the ISR and missing [0]
    while(g_uiSD24BufferPointer > 0){
        LPM3;            // LPM0 for lower power waiting
        __no_operation();
    }
}

long V_tp, V_offs;
long T_sensor, object_temperature;      // Temperature stored as milliCelcius (1C = 1000)

unsigned long ThermoCompensation(void){
    /*
     * This compensation routine is provided in TE Connectivities App note "Thermopile Sensor for Contactless Temperature"
     * It is described in detail in the Algorithem section, page 10.  It is implented step by step as outlined there
     * URL:  https://www.te.com/content/dam/te-com/documents/sensors/global/analog-digital-thermopile-application-note.pdf
     *
     * This routine is written using integar math to avoid the overhard of floating point math
     *
     * NOTE: All temperatueres are stored as millicelsius so they can be handled as integars
     */

    long V_tp, V_offs;
    long TempCompensationFactorx1000;     //Removed TCF for now as APAT does not provide one
    long T_sensor, object_temperature;      // Temperature stored as milliCelcius (1C = 1000)


    //Step 1. Measure V_tp and V_rtd voltages.
    //This code is constantly take period measurements, so this functino averages all the samples already buffered
    SD24averaging();

    //Step 2. Thermopile votage (Vtp) Sensitivity Correction - requires individual unit calibration
    V_tp = (g_lThermopileResult*100) / S_CONV_x100;


    //Step 3. Using NTC voltage (V_rtd), find the ambient temperature from lookup table (LUT)
    T_sensor = LUTthermistor(g_lThermistorResult, REVERSE_LOOKUP);

    //Step 4.  Calculate temperature compensation factor (TCF)  TCF = 1 + (Temperature Delta * TCsens)
    TempCompensationFactorx1000 = 1000 + ((T_sensor - AMBIENT_TEMP_CAL_mC) * TC_SENS_x10000)/10000;

    //Step 5.  Find Offset voltage (Voffs) in LUT.
    V_offs = LUTthermopile(T_sensor, FORWARD_LOOKUP);

    //Step 6. compenstate offset voltage for ambient temp.  Voffs = Voffs * TCF
    V_offs = (V_offs/8 * TempCompensationFactorx1000)/125;  //Same above equation but with overflow protection with minimal tradeoff in performance.  TCFx1000 should never exceed 1500 with this sensor.

    //Step 7. Add offset to thermopile voltage
    V_tp = V_tp + V_offs;

    //Step 8. Temperature compensation of Thermopile voltage: Vtp = Vtp / TCF
    V_tp = ((V_tp * 125) / TempCompensationFactorx1000) * 8;  // Same equation but avoiding overflows at worst case

    //Step 9. Look up object temperature
    object_temperature = LUTthermopile(V_tp, REVERSE_LOOKUP);

    return object_temperature;
}








