/*SKETCH FOR THE Teensy 3.6 card used for thermodynamics and GPS sampling.
To be used at least in the 2019 Perlan flight season.
Modified 13 May 2019 to measure UVa and UVb from photodiodes using an additonal ADS1115 A/D convertor.
Modified 21 May 2019 after calibration of the UVa and UVb sensors using solar light UVa and UVb sensors.
Modified 21 May 2019 to set the voltage range of the ADS1115 for UVa UVb and thermal sensor to 4 volts.
Modified 21 May 2019 to correct the baud rate for the serial 1 output to 57,600.  The gps serial was set with this rate previously.
This version of the code is for Rev 11 of the board (includes APC220 radio and no LCD screen).
This mesurement device uses: Teensy 3.6; Analog to digital convertor ADS1115; microSD card built in to Teensy;
Real time clock aboard the Teensy; ANTEK DR-V0 GPS; GY-63;
SHT75 digital T and RH sensor for inside equipment bay; BR11KA152M micro miniature thermistor used to measure ambient air Temp.
Note:  The code for running the system with an external RTC is included for completeness.
Outputs: 
1. Temperature in NEMA format through the DB9 connector acting as a serial port.
2. UVa and UVb data in microwatts/cm^2.
2. All data saved on the microSD card, and written out the USB port of the microcontroller.
The sketch for setting the real time clock on the Teensy is included at the end, commented out.
 By Pat Arnott, April 2017. 
 Revised for the VEML UV sensor April 2018.  VEML removed in May 2019 because the UVb component does not work.
 Revised in May 2019 to measure UVa and UVb using photodiodes and the ADS1115 A/D convertor on the board. 
 The UVa and UVb board also has an analog TMP36 sensor on it for quantifying board temperature.  */
/*
***************THE HARDWARE CONNECTIONS:***********************************
***************SHT75 T and RH sensor******
Pin on chip              Pins on Teensy
1 SCK                    7
2 VDD                    3.3 volts
3 GND                    GND
4 DATA                   6  Pulled up with 10kOhm resistor
*****************SD **************************
SD :bob-00544
Library:SD.h
Built in to the Teensy 3.6.
*****************16 bit ADC 1115 ADAFRUIT breakout board **************************
Device Pin Names                          Pins on Teensy
1  VDD---------                           3.3V
2  GND--------                            GND
3  SCL-------                             pin 19 A5
4  SDA-------A4                           pin 18 A4
5  ADDR----(0x48 (1001000) ADR -> GND     Using this address for the board to photodiodes and TMP36 sensor.
6  0x49 (1001001) ADR -> VDD              Using this address for the board to measure temperature.
7  0x4A (1001010) ADR -> SDA
8  0x4B (1001011) ADR -> SCL)
9  ALRT
Pin assignments for 6  0x49 (1001001) ADR -> VDD.
10 A0                Thermistor voltage from voltage divider with 5 V supply voltage.                                   
11 A1                Thermistor voltage divider (tied to channel A0)
12 A2                Analog pressure sensor (not currently used on the board)
13 A3                5 V supply of the board for use in obtaining the thermistor resistance.
Pin assignments for 6  0x48 (1001000) ADR -> GND.
10 A0                GND.                                   
11 A1                TMP36 temperature sensor.UVa photodiode voltage from transimpedance amplifier.
12 A2                UVa photodiode voltage from transimpedance amplifier.
13 A3                UVb photodiode voltage from transimpedance amplifier.
************** Real Time Clock ****************************************************************
Built in to the Teensy 3.6
**************** ANTEK DR-V0 GPS ********************************************************
*** This device simply uses standard NMEA format data,
*** and serial port communication. Serial port 5 is used on the Teensy.
Device Pin Names    Pins on Teensy
1 RX                33 TX
2 TX                34 RX
3 GND               GND
4 VDD               3.3 V
**** I2C Interface Sensors ************
********************* ADAFRUIT ads1115 ***************                            
********************* ADS1115 for the TMP36, UVa, and UVb sensors *************                            
Device Pin Names                          Pins on Teensy
1  VDD---------                           3.3V
2  GND--------                            GND
3  SCL-------                             pin 19 A5
4  SDA-------A4                           pin 18 A4
*/
/************** LIBRARIES ****************/
#include <Adafruit_ADS1015.h>  /* ADC library                         */
#include <Wire.h>              /* For the RTC of the Chronodot type   */
#include <TimeLib.h>           /* For the RTC built in to the Teensy  */
#include <TinyGPS.h>           /* For the GPS                         */
#define HWSERIALgps Serial5    /* Serial port used for the NMEA GPS   */
#include <SPI.h>               /* For the SD card                     */
#include <SD.h>                /* SD library                          */
//#include </Applications/Arduino1_8_8.app/Contents/Java/hardware/teensy/avr/libraries/SD/SD.h>
#include <Sensirion.h>         /* For the SHT75 T and RH sensor       */

/******************** TEENSY PIN ASSIGNMENTS ********************/
/* Pins used for the Sensirion SHT75 sensor. */
const uint8_t dataPin  =  6; // These are the digital pins 6 and 7 on the Teensy.
const uint8_t clockPin =  7;

/******************  Set up the devices  ******************/
Adafruit_ADS1115 ads0(0x49);   /* For the 16 bit ADS1115 analog to digital convertor for the temperature sensor */ 
Adafruit_ADS1115 ads1(0x48);   /* For the 16 bit ADS1115 analog to digital convertor for the UVa UVb and TMP36 temperature sensors Default address. */

Sensirion tempSensor = Sensirion(dataPin, clockPin);

/**** This code uses a TinyGPS object, for the GPS. *****/
TinyGPS gps;
void gpsdump(TinyGPS &gps);
void printFloat(double f, int digits = 2); 

File mySensorData;
char fheader[]= "header.csv"; //write the header to SD card
char fsensor[11]="sensor.csv";  //write the sensor data  to SD card.  Updated to time and date in the setup loop. (9April2019).
String topOfFile;

/* Variables for the ads1 ADS1115 analog to digital convertor. */
// float UVAcalFac=9.00   ;     // microWatts per square cm per millivolt. // 10 Megohm feedback resistor, 13 May 2019 calibration.
 float UVAcalFac=9.745   ;     // microWatts per square cm per millivolt. // 10 Megohm feedback resistor, 21 May 2019 calibration.
 float UVBcalFac=0.0794    ;     // microWatts per square cm per millivolt. Cal 21May2019, with 100Mohm feedback resistor.
 const int TMP36pin = 1 ;     // TMP36 temperature sensor.
 const int UVApin   = 2 ;     // UVa voltage from the transimpedance amplifier.
 const int UVBpin   = 3 ;     // UVb voltage from the transimpedance amplifier.
 float UVA       = -1.0 ;
 float UVB       = -1.0 ;
 float TMP36     = -1.0 ;
 float RawUVA    = -1.0 ;
 float RawUVB    = -1.0 ;
 float RawTMP36  = -1.0 ;

/**************************** GLOBAL VARIABLES **************************/
/* Variable Name Protocol:  xxxYY where xxx is the sensor, and YY is the 1st two letters of variable */

/* Variable from the ADC1115 analog to digital convertor for the micro thermistor sensor */
int gainPass=0        ; /* Global variable used in the function getADS1115 to send gain back and forth to the main loop.*/
// int gainTh=0          ; /* Thermistor sensor initial gain value */
float gainAD623=3.6738 ; /* Fixed gain of the instrument amp on the sensor head. */
float Rfixed = 997600.0 ; /* Sum of resistance of two resistors used to get thermistor resistance.*/
int nMeas = 1 ;  /* Number of measurements done each time through the analog to digital conversion routine */

/* Variables from Sensirion sht75. */
float shtTE, shtHU, shtDE ;  /* temperature, RH, and dewpoint */

/* Variables from the ads1115 analog to digital converter for the thermistor sensor */
float adsTh, adsVO ; /* Thermistor, and Vcc=5 volts */
float premeas ; /* Temporary variable used for preliminary measurement with A/D to adapt to channel changes. */
int numAve    ; /* Number of averages made during each measurement. */

unsigned long int avetime = 850UL;         /* Averaging time in milliseconds                                           */
unsigned long int Recycle = 4294957295UL;  /* Check to see if averaging routine needs recycled                         */
unsigned long int endtime ;                /* Time in milliseconds for averaging interval                              */
unsigned long int elatime ;                /* Total elapsed time since program started in ms. resets periodically.     */

int ii=0;
int jj=0;
int iDis =1;
int i    ;
int itot ;

/**************************************** Real Time Clock VALUES  ****************************************/
// These are global variables, available to all functions and procedures.
int seconds;  //  00-59 
int minutes;  //  00-59 
int hours;    //  1-12 - 00-23 
int days;     //  1-7 assuming Sunday is day 1.
int date;     //  01-31
int months;   //  01-12
int years;    //  0-99 

/******************** GPS VALUES, start with defaults ***************************************/
float gpslat=-99.99            ; /* decimal degrees */
float gpslon=-99.99            ; /* decimal degrees */
float gpsalt=-99.99            ; /* meters */
float gpsspeed=-99.99          ; /* kilometers / hour */
float gpscourse=-99.99         ; /* degrees */
int   gpsyear=-99              ; /* e.g. 2016 */
int   gpsmon=99                ; /* 1 thru 12 UTC */
int   gpsday=99                ; /* 1 thru 31 UTC */
int   gpshour=99               ; /* 0 thru 59 UTC */
int   gpsmin=99                ; /* 0 thru 59 UTC */
int   gpssec=99                ; /* 0 thru 59 UTC */

/************************************** END GLOBAL VARIABLES ********************************/

void setup()
{
/***************** Write header for the top of the file and the serial port ********/                           ;
topOfFile="UTC_RTC_Date,UTC_RTC_Time,UTC_GPS_Date,UTC_GPS_Time,"                                                ;
topOfFile.concat("ThermistorTemp_C,ADSgain,")                                                                   ;
topOfFile.concat("ThermistorSens_mV,numberAverages,VO_mV,ThermistorResistance_Ohms,")                           ;       
topOfFile.concat("sht_Temperature_C,sht_RH_%,shtDewTemp_C,")                            ;         
topOfFile.concat("Latitude,Longitude,Altitude_m,Speed_kph,Course_degrees,")                                     ;
topOfFile.concat("RawTMP36_mvolts,RawUVa_mvolts,RawUVb_mvolts,UVboardTemp_C,UVa_uWpercm2,UVb_uWpercm2")         ;              
              
/*************** Start up the USB to serial output of the Teensy to monitor it if needed************/
Serial.begin(230400);     // Turn on the Teensy serial port output for USB output.
while (!Serial && millis() < 1000) {
    //wait up to 1 seconds for serial monitor to connect
  }
Serial.println(__FILE__); // Prints details of the sketch used for this program.
Serial.print("Compiled on ");
Serial.print(__DATE__);
Serial.print(' ');
Serial.println(__TIME__);
Serial.println(topOfFile);  // Prints out the data header.  

/*************Set up the Teensy analog to digital conversion*********************************/ 
/* Teensy analog to digital ports are currently not used */
 analogReadAveraging(1024);  // Do 1024 measurements and average them for every analog input measurement.
 analogReadResolution(13); // Do 13 bit analog read resolution on the Teensy for the AUX channels.
 
/*********************** SD **************/
SD.begin(BUILTIN_SDCARD);//initilize the sd card with CS
delay(1000) ; // Allow SD card to warm up. 
if (!SD.begin(BUILTIN_SDCARD))
  {
    Serial.println("MicroSD card may not be working") ;
  }

mySensorData = SD.open(fheader, FILE_WRITE);//Open SD File, Append if the file is already exist
mySensorData.println(__FILE__); // Prints details of the sketch used for this program.
mySensorData.print("Compiled on ");
mySensorData.print(__DATE__);
mySensorData.print(' ');
mySensorData.println(__TIME__);
mySensorData.println(topOfFile); //the file header
mySensorData.close(); 

get_date_time();  // Command for use with the built in RTC on the Teensy.
    String daySt   =   String(date)  ; 
    String monthSt = String(months)  ; 
    String yearSt  =  String(years-2000)  ; 
    String filename=""               ;
    if( date < 10 && months >= 10 ) filename =       String( monthSt + "0" + daySt + yearSt + ".csv")  ;
    if( date >= 10 && months < 10 ) filename =       String( "0" + monthSt + daySt + yearSt + ".csv")  ;
    if( date < 10 && months < 10  ) filename = String( "0" + monthSt + "0" + daySt + yearSt + ".csv")  ;
    if( date >= 10 && months >= 10) filename =            String( monthSt +  daySt + yearSt + ".csv")  ;
    filename.toCharArray(fsensor, 11);

/*********************** ADC Part for the Thermistor sensor **************/
ads0.setGain(GAIN_TWOTHIRDS);    // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
ads0.begin();                    // Initialize ads1115
gainPass=0 ; /* Set the gain variable to the starting value. This is for autogaining the thermistor measurement.*/

/*********************** ADC Part for the UVa UVb and TMP36 sensors **************/
ads1.setGain(GAIN_ONE);    // 1x gain   +/- 4.096V  1 bit = 0.125mV
ads1.begin();                    // Initialize ads1115

// Set up the I2C connection for the RTC clock.
Wire.begin();  // Not sure this is needed for the RTC on the Teensy 3.6.

/***************** GPS STARTUP *****************/
HWSERIALgps.begin(9600);

/**** NMEA Serial Port STARTUP for communicating to the aircraft data system ****/
Serial1.begin(57600);

} /* Finished setting up. */


/********************* LOOP *****************************/
/**************** Program repeats on this ***************/
void loop() 
{

/*******  Do the temperature, humidity, and dewpoint measurements using the SHT75 sensor. *********/
tempSensor.measure(&shtTE, &shtHU, &shtDE);

/* Do the time averaged measurements, being careful to restart the millis timer if it over runs */

unsigned long int elatime = millis();
if (elatime >= Recycle) /* check to see if the millisecond timer is about to recycle to 0, and if so, wait. */
{
  delay(15000);        /* Allow the millisecond timer to return to 0 and start over */
  elatime = millis() ; /* second call is needed in case Recycle has happened */
}

  endtime = elatime+avetime;

/*************** DO THE VOLTAGE SOURCE, AND PRESSURE MEASUREMENTS */
/* Initialize the variables for averaging */
adsTh=0.0; adsVO=0.0 ;  i=0; itot=0;
float eachADSth ;

 do
{  
  /* Get the thermistor sensor and power supply voltages, using fixed gain of zero */
  int gainTh=gainPass ;  /* write the gain to this variable since autogain was used */
    eachADSth=getADS1115(0,false,gainTh)   ;
  adsTh+=eachADSth                       ;
  itot += nMeas ;  // Total number of measurements since some averaging is done in the acquisition .
  i++ ;
  
  checkGPS() ; 
  
 } while (millis() <= endtime);

  adsVO=getADS1115(3,true,0) ; /* Measure the power supply voltage for reference */

  adsTh=adsTh/gainAD623 ; /* Divide by the gain on the instrument amplifier */
  numAve=itot ; 
  float den = (float)i ;
  adsTh=adsTh/den               ;  
  
  float ThermRes = Rfixed / ((adsVO/adsTh) - 1.00000000) ;
  float ThermTemp = Thermistor(ThermRes) ;  /* Obtained using the Stein Hart equation */

// Read UVa, UVb, and TMP36 temperature sensor measurements from the ADS1115 on the photodiode board.
// Using 0.1250 millivolts / count for the analog to digital conversion full scale.
  RawTMP36 = ads1.readADC_SingleEnded(TMP36pin) * 0.1250    ; // milliVolts units.
  RawTMP36 = ads1.readADC_SingleEnded(TMP36pin) * 0.1250    ; // Repeat the measurement for settling the ADS1115. Implement 7/10/2019.
// ads1.setGain(GAIN_ONE)                                    ;
  RawUVA   = ads1.readADC_SingleEnded(UVApin)   * 0.1250    ;
  RawUVA   = ads1.readADC_SingleEnded(UVApin)   * 0.1250    ; // Repeat measurement for settling the ADS1115.Implement 7/10/2019.
  RawUVB   = ads1.readADC_SingleEnded(UVBpin)   * 0.1250    ; 
  RawUVB   = ads1.readADC_SingleEnded(UVBpin)   * 0.1250    ; // Repeat measurement for settling the ADS1115.Implement 7/10/2019.
//  ads1.setGain(GAIN_TWOTHIRDS)                              ; 
  TMP36 = (RawTMP36 - 500.0) * 0.1   ;  // Convert from millivolts to Celcius.
  UVA   = RawUVA * UVAcalFac         ;
  UVB   = RawUVB * UVBcalFac         ;   
 

/***************** Get the actual time now, from the real time clock ******************/
/*  Variables are filled due to the global definitions of variable types. */
get_date_time();  // Command for use with the built in RTC on the Teensy.

/************ Write the temperature data out through the DB9 connector *********/
/************ Write it out in NMEA format **************************************/
  String text="therm,"+String(ThermTemp,2)+String(",")+String(UVA,2)+String(",")+String(UVB,4)+String(",")+String(TMP36,2);
  Serial1.print("$")               ;
  Serial1.print(text)                   ; 
  Serial1.print("*")                    ;
  int chk= getCheckSum(text)            ;
  if (chk < 0x10) 
  { 
    Serial1.print("0") ;
  }
    Serial1.println(chk,HEX)  ;
 
  
/************ Write the data to the serial port to look at it. *****************/
  digitalClockDisplay()     ; /* Writes the time and date from the RTC */
  digitalClockDisplayGPS()  ; /* Writes the time and date from the GPS */
  Serial.print(ThermTemp,2); Serial.print(",");
  Serial.print(gainPass);    Serial.print(",");
  Serial.print(adsTh,4);     Serial.print(",");
  Serial.print(numAve);      Serial.print(",");
  Serial.print(adsVO,4);     Serial.print(",");
  Serial.print(ThermRes,4);  Serial.print(",");
  Serial.print(shtTE,2);     Serial.print(",");
  Serial.print(shtHU);       Serial.print(",");
  Serial.print(shtDE);       Serial.print(",");
  Serial.print(gpslat,5);    Serial.print(",");
  Serial.print(gpslon,5);    Serial.print(",");
  Serial.print(gpsalt);      Serial.print(",");
  Serial.print(gpsspeed);    Serial.print(",");
  Serial.print(gpscourse);   Serial.print(",");
  Serial.print(RawTMP36);    Serial.print(",");
  Serial.print(RawUVA) ;     Serial.print(",");
  Serial.print(RawUVB)  ;    Serial.print(",");
  Serial.print(TMP36,2) ;    Serial.print(",");
  Serial.print(UVA,4)  ;     Serial.print(",");
  Serial.println(UVB,5) ;
            
                             
/************************* WRITE DATA TO THE SD CARD NOW **********************************/
  mySensorData = SD.open(fsensor, FILE_WRITE); /* Open SD File, Append if the file is already exist. */
  digitalClockDisplaySD()     ; /* Writes the time and date from the RTC */
  digitalClockDisplaySDGPS()  ; /* Writes the time and date from the GPS */
  mySensorData.print(ThermTemp,4); mySensorData.print(",");
  mySensorData.print(gainPass);      mySensorData.print(",");
  mySensorData.print(adsTh,4);     mySensorData.print(",");
  mySensorData.print(numAve);      mySensorData.print(",");
  mySensorData.print(adsVO,4);     mySensorData.print(",");
  mySensorData.print(ThermRes,4);  mySensorData.print(",");
  mySensorData.print(shtTE);       mySensorData.print(",");
  mySensorData.print(shtHU);       mySensorData.print(",");
  mySensorData.print(shtDE);       mySensorData.print(",");
  mySensorData.print(gpslat,5);    mySensorData.print(",");
  mySensorData.print(gpslon,5);    mySensorData.print(",");
  mySensorData.print(gpsalt);      mySensorData.print(",");
  mySensorData.print(gpsspeed);    mySensorData.print(",");
  mySensorData.print(gpscourse);   mySensorData.print(",");
  mySensorData.print(RawTMP36);    mySensorData.print(",");
  mySensorData.print(RawUVA) ;     mySensorData.print(",");
  mySensorData.print(RawUVB)  ;    mySensorData.print(",");
  mySensorData.print(TMP36,2) ;    mySensorData.print(",");
  mySensorData.print(UVA,4)  ;     mySensorData.print(",");
  mySensorData.println(UVB,5) ; 
  mySensorData.close(); 
 
   
}  /* void loop() */

/*********************** SUBROUTINES *****************************/

/*  SD card printing of actual time and date.
*   Write the digital clock output to the SD card writer. */
void digitalClockDisplaySD(){
  SDPrintDigits(months)      ;
  mySensorData.print("/")    ;
  SDPrintDigits(date)        ; 
  mySensorData.print("/")  ;
  SDPrintDigits(years)       ;
  mySensorData.print(",")    ;
  SDPrintDigits(hours)       ;
  mySensorData.print(":")    ;
  SDPrintDigits(minutes)     ;
  mySensorData.print(":")    ;
  SDPrintDigits(seconds)     ;
  mySensorData.print(",")    ;
}
void SDPrintDigits(int digits){
  if(digits < 10) mySensorData.print("0") ; 
  mySensorData.print(digits) ;
}
void digitalClockDisplay()
{
  SerialPrintDigits(months)  ;
  Serial.print("/")          ;
  SerialPrintDigits(date)    ; 
  Serial.print("/")          ;
  SerialPrintDigits(years)   ;
  Serial.print(",")          ;
  SerialPrintDigits(hours)   ;
  Serial.print(":")          ;
  SerialPrintDigits(minutes) ;
  Serial.print(":")          ;
  SerialPrintDigits(seconds) ;
  Serial.print(",")          ;
} 

void SerialPrintDigits(int digits)
{
  if(digits < 10) Serial.print("0") ; 
  Serial.print(digits) ;
}

/* Procedure to get date and time from the built in RTC on the Teensy */
void get_date_time()
{
  time_t t=Teensy3Clock.get();
  date    =  day(t);
  months  =  month(t);
  years   =  year(t);
  hours   =  hour(t);
  minutes =  minute(t);
  seconds =  second(t);
}

/* Procedure to get date from the Chronodot RTC */

void get_date()
{
  Wire.beginTransmission(104); 
  Wire.write(3);//set register to 3 (day)
  Wire.endTransmission();
  Wire.requestFrom(104, 4); //get 5 bytes(day,date,month,year,control);
  days   = bcdToDec(Wire.read()); 
  date  =  bcdToDec(Wire.read());   
  months = bcdToDec(Wire.read()); 
  years  = bcdToDec(Wire.read());  
}

void get_time()
{
  Wire.beginTransmission(104); 
  Wire.write(0);//set register to 0
  Wire.endTransmission();
  Wire.requestFrom(104, 3);//get 3 bytes (seconds,minutes,hours);
  seconds = bcdToDec(Wire.read() & 0x7f);
  minutes = bcdToDec(Wire.read());
  hours   = bcdToDec(Wire.read() & 0x3f);
}
   
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}


/*********************************************** GPS PROCEDURES ************************************/
/***************************************** GPS GlOBAL VALUES ***************************************/
/******************** GPS VALUES, start with defaults, from global declaration**********************/
// float gpslat=-99.99          ; /* decimal degrees */
// float gpslon=-99.99          ; /* decimal degrees */
// float gpsalt=-99.99          ; /* meters */
// float gpsspeed=-99.99        ; /* kilometers / hour */
// float gpscourse=-99.99       ; /* degrees */
// int gpsyear=-99              ; /* e.g. 2016 */
// int gpsmon=99                ; /* 1 thru 12 UTC */
// int gpsday=99                ; /* 1 thru 31 UTC */
// int gpshour=99               ; /* 0 thru 59 UTC */
// int gpsmin=99                ; /* 0 thru 59 UTC */
// int gpssec=99                ; /* 0 thru 59 UTC */

void gpsdump(TinyGPS &gps)
{
  long lat, lon;
  float flat, flon;
  unsigned long age, date, time, chars;
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned short sentences, failed;

    gps.get_position(&lat, &lon, &age);
//  Serial.print("Lat/Long(10^-5 deg): "); Serial.print(lat); Serial.print(", "); Serial.print(lon); 
//  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");

  gps.f_get_position(&flat, &flon, &age);
//  Serial.print("Lat/Long(float): "); printFloat(flat, 5); Serial.print(", "); printFloat(flon, 5);
//  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");

    gps.get_datetime(&date, &time, &age);
//  Serial.print("Date(ddmmyy): "); Serial.print(date); Serial.print(" Time(hhmmsscc): ");
//    Serial.print(time);
//  Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms.");

  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
//  Serial.print("Date: "); Serial.print(static_cast<int>(month)); Serial.print("/"); 
//    Serial.print(static_cast<int>(day)); Serial.print("/"); Serial.print(year);
//  Serial.print("  Time: "); Serial.print(static_cast<int>(hour)); Serial.print(":"); 
//    Serial.print(static_cast<int>(minute)); Serial.print(":"); Serial.print(static_cast<int>(second));
//    Serial.print("."); Serial.print(static_cast<int>(hundredths));
//  Serial.print("  Fix age: ");  Serial.print(age); Serial.println("ms.");

  gpslat=flat                                   ; 
  gpslon=flon                                   ;
  gpsalt   =gps.f_altitude()                    ;
  gpsspeed=gps.f_speed_kmph()                   ;
  gpscourse =gps.f_course()                     ;
  gpsyear   = year                              ;
  gpsmon    = static_cast<int>(month)           ;
  gpsday    = static_cast<int>(day)             ;
  gpshour   = static_cast<int>(hour)            ;
  gpsmin    = static_cast<int>(minute)          ;
  gpssec    = static_cast<int>(second)          ;

/*
    Serial.print("Alt(cm): "); Serial.print(gps.altitude()); Serial.print(" Course(10^-2 deg): ");
    Serial.print(gps.course()); Serial.print(" Speed(10^-2 knots): "); Serial.println(gps.speed());
    Serial.print("Alt(float): "); printFloat(gps.f_altitude()); Serial.print(" Course(float): ");
    printFloat(gps.f_course()); Serial.println();
    Serial.print("Speed(knots): "); printFloat(gps.f_speed_knots()); Serial.print(" (mph): ");
      printFloat(gps.f_speed_mph());
    Serial.print(" (mps): "); printFloat(gps.f_speed_mps()); Serial.print(" (kmph): ");
      printFloat(gps.f_speed_kmph()); Serial.println();
*/

  gps.stats(&chars, &sentences, &failed);
//  Serial.print("Stats: characters: "); Serial.print(chars); Serial.print(" sentences: ");
//  Serial.print(sentences); Serial.print(" failed checksum: "); Serial.println(failed);
}

void printFloat(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0) {
     Serial.print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
  
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    Serial.print("."); 

  // Extract digits from the remainder one at a time
  while (digits-- > 0) {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    remainder -= toPrint;
  }
}

/****************** Print a comma to the serial port ************************/
void c()  {
  Serial.print(",") ;
}

/*  SD card printing of actual time and date.
*   Write the digital clock output to the SD card writer. */
void digitalClockDisplaySDGPS(){
  SDPrintDigits(gpsmon) ;
  mySensorData.print("/");
  SDPrintDigits(gpsday) ; 
  mySensorData.print("/") ;
  SDPrintDigits(gpsyear) ;
  mySensorData.print(",") ;
  SDPrintDigits(gpshour) ;
  mySensorData.print(":") ;
  SDPrintDigits(gpsmin) ;
  mySensorData.print(":") ;
  SDPrintDigits(gpssec) ;
  mySensorData.print(",") ;
}

void digitalClockDisplayGPS()
{
  SerialPrintDigits(gpsmon) ;
  Serial.print("/");
  SerialPrintDigits(gpsday) ; 
  Serial.print("/") ;
  SerialPrintDigits(gpsyear) ;
  Serial.print(",") ;
  SerialPrintDigits(gpshour) ;
  Serial.print(":") ;
  SerialPrintDigits(gpsmin) ;
  Serial.print(":") ;
  SerialPrintDigits(gpssec) ;
  Serial.print(",") ;
} 

/**************** Function to obtain data from the ADS1115 sensor ****************/
/* Used to get data for the thermistor temperature sensor */
/**************** It makes use of the global variable integer named gain *********/
/***  Inputs:  */
/* int channel = channel 0 to 3 to acquire data for. */
/* bool setGain=true, just acquire data; else autogain to find best gain and then acquire data ******/
/* int startGain = value of gain to use for either acquiring data directly, or starting autogain. */
/* At the end of the routine, the global variable is filled with the startGain value. */
/***  Outputs: */
/* measuredMilliVolts */
/* GAIN DEFINITIONS: */
/* gain=0 ads.setGain(GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 0.1875mV (default) */
/* gain=1 ads.setGain(GAIN_ONE);        // 1x gain   +/- 4.096V  1 bit = 0.125mV */
/* gain=2 ads.setGain(GAIN_TWO);        // 2x gain   +/- 2.048V  1 bit = 0.0625mV */
/* gain=3 ads.setGain(GAIN_FOUR);       // 4x gain   +/- 1.024V  1 bit = 0.03125mV */
/* gain=4 ads.setGain(GAIN_EIGHT);      // 8x gain   +/- 0.512V  1 bit = 0.015625mV */
/* gain=5 ads.setGain(GAIN_SIXTEEN);    // 16x gain  +/- 0.256V  1 bit = 0.0078125mV */
/* nMeas = 1 if 1 average of the data is done; nMeas=2 if 2 averages of data are done.*/
float getADS1115(int channel, bool setGain, int startGain ) 
{
float conv[6]={0.1875,0.125,0.0625,0.03125,0.015625,0.0078125} ; /* mVolts / bit conversion factors */
uint16_t adc0   ;
uint16_t adc0_1 ;
uint16_t target=10000 ; /* Make a/d counts greater than this value if possible with autogain employed */ 
uint16_t over=32760   ; /* Peak allowed value of a/d counts during autozero.*/
nMeas = 1 ; // Initialize the measurements to at least one. This is a global variable that passes back to the main.

if (setGain) {
  ADSsetGain(startGain) ; /* pre read the channel, pointing the ADS to this channel with chosen gain */
  adc0=ads0.readADC_SingleEnded(channel) ;
  adc0=ads0.readADC_SingleEnded(channel) ;  // Repeat measurement to let it settle.
  adc0_1 = ads0.readADC_SingleEnded(channel) ; // Average the signal
  adc0 = adc0 + adc0_1          ;
  nMeas = 2 ;
  return conv[startGain]*(float)adc0 / 2.0000;
  }
else{  /* Must find the autogain that makes sense */
  ADSsetGain(gainPass) ; /* pre read the channel, pointing the ADS to this channel with previously found gain */
  adc0_1=ads0.readADC_SingleEnded(channel) ;
  adc0_1=ads0.readADC_SingleEnded(channel) ;
  int gainIn = gainPass ; // Initialize the initial gain for possibly averaging data.
  bool failed=true ;
  int tries=0 ;
  do
  {
  tries++;
  ADSsetGain(startGain) ;
  adc0=ads0.readADC_SingleEnded(channel) ;
  if (adc0 > target && adc0 < over ) { /* the current gain value is good if true */
    gainPass=startGain ; 
    failed=false ;
    if (startGain==gainIn) {
       adc0=adc0+adc0_1  ;     nMeas++;
       return conv[startGain]*(float)adc0 / (float)nMeas ;
    }
    else {
       return conv[startGain]*(float)adc0 ;
    }
  }
  else if (adc0 < target && startGain == 5) {
    gainPass=startGain ; 
    failed=false ;
    if (startGain==gainIn) {
       adc0=adc0+adc0_1  ;  nMeas++ ;
       return conv[startGain]*(float)adc0 / (float)nMeas ;
    }
    else {
       return conv[startGain]*(float)adc0 ;
    }
  }  
    else if (adc0 < target && startGain <5) {
    startGain++ ; /* Increase the gain */
    gainPass=startGain;
  }
  else if (adc0 > over && startGain > 0)   {
    startGain-- ; /* Decrease the gain */
    gainPass=startGain;
  }
  else if (tries > 5) {
    gainPass=startGain ; 
    failed=false ;
    if (startGain==gainIn) {
       adc0=adc0+adc0_1   ;  nMeas++ ;
       return conv[startGain]*(float)adc0 /(float)nMeas ;
    }
    else {
       return conv[startGain]*(float)adc0 ;
    }
  }
//  Serial.print("tries=") ; Serial.println(tries) ; 
  } while (failed) ;
}
return conv[startGain]*(float)adc0 ;
}

/* Subroutine to set gain on the preamp */
void ADSsetGain(int g) {
   if (g == 0) {
      ads0.setGain(GAIN_TWOTHIRDS);
   }
   else if (g==1) {
          ads0.setGain(GAIN_ONE);
   }
   else if (g==2) {
          ads0.setGain(GAIN_TWO);
   }
   else if (g==3) {
          ads0.setGain(GAIN_FOUR);
   }
   else if (g==4) {
          ads0.setGain(GAIN_EIGHT);
   }
   else if (g==5) {
          ads0.setGain(GAIN_SIXTEEN);
   }
   else { /* coerce the value to the safest value */
          ads0.setGain(GAIN_TWOTHIRDS);
   }
}

/*** Subroutine to calculate the thermistor temperature from its measured resistance. ***/
/* Uses the Stein Hart equation, with coefficients from the BR11KA152M thermistor. */
/* Resistance R is in Ohms */

  float Thermistor(float R) {
  float rat = log(R) ; 
//  float a=1.051740553E-03   ; 
//  float b=3.04251503400E-04 ;
//  float c=1.9741958170E-07  ;
  float a=1.13235023654109E-03   ; // New Coefficients Derived 9 April 2019.
  float b=2.97097811053206E-04   ; // New Coefficients Derived 9 April 2019.
  float c=1.9741958170E-07       ; // New Coefficients Derived 9 April 2019.
  float Ttherm = a + b*rat + c*rat*rat*rat  ;
  Ttherm = 1.0/Ttherm - 273.15  ; // Thermistor temperature in Celcius.
//  Ttherm = Ttherm - 1.63 ;  // Empirical Calibration based on comparison with SHT75.  Removed 9April2019
  return Ttherm ;
} 

/***********************************************/
/* Procedure to get a checksum */
/************/
  uint8_t getCheckSum(String string) 
{
  int len=string.length()+1 ;  
  char buf[len] ;
  string.toCharArray(buf,len) ;
  
  int XOR = 0;  
  for (int i = 0; i < len; i++) 
  {
    XOR = XOR ^ buf[i];
  }
  return XOR;
}

/****************************************************/
/* Procedure to get the GPS data if it's available. */
/****************************************************/
void checkGPS()
{
bool newdata = false             ;
unsigned long startup = millis() ;
while (millis() - startup < 10L) {
 if (HWSERIALgps.available()) {
    char c = HWSERIALgps.read();
    if (gps.encode(c)) { 
      newdata = true    ;
      break             ; // Obtained data, bail out of the while loop 
         }
       }
     } 
if (newdata) gpsdump(gps);
}


/*****************  CODE USED FOR SETTING THE TEENSY REAL TIME CLOCK **************/
/* Included here for completeness */

/*
 * Set the built in RTC on the Teensy 3.x using the time library. * 
 * Written by Pat Arnott, modified from TEENSY3time example.  .
 */

/*
#include <TimeLib.h>

void setup()  {
  time_t t=now()  ; // Create a time_t variable to use.
  TimeElements tm ; // Define a TimeElements variable.
  breakTime(t,tm) ; // Initialize the time variable.
  
      tm.Second = 0        ;  // Range from 0 to 59.
      tm.Minute = 45       ;  // Range from 0 to 59.
      tm.Hour   = 1        ;  // Range from 0 to 23.
      tm.Wday   = 0        ;  // Range from 0 to 6, 0 Sunday. 
      tm.Day    = 23       ;  // Range from 1 to 31.
      tm.Month  = 4        ;  // Range from 1 to 12.
      tm.Year   = 47       ;  // Range from 0 to 99, offset from 1970, e.g. 2017=47.
       
      t=makeTime(tm)       ;  // Convert 

      Teensy3Clock.set(t) ;
      
      Serial.begin(9600)  ; 
}

void loop() {
  time_t t=Teensy3Clock.get();

  // Note: restarting the serial port reruns this program, so sets the time wrong.
  // Recommend not looking at the serial port with this program in the Teensy.
  // Recommend using the READTeensyClock, etc program.
  
  Serial.print("t=");        Serial.println(t)         ;
  Serial.print("hour=");     Serial.println(hour(t))   ;
  Serial.print("minute=");   Serial.println(minute(t)) ; 
  Serial.print("second=");   Serial.println(second(t)) ; 
  Serial.print("month=");    Serial.println(month(t))  ; 
  Serial.print("day=");      Serial.println(day(t))    ; 
  Serial.print("year=");     Serial.println(year(t))   ; 

  delay(1000) ;
}

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}
*/
