#ifdef MERGELATER
/*
 * ph_comb.c
 * some combined processing functions for loopback tests
 *
 *  Created on: Jan 25, 2022
 *      Author: Irene Hu
 */

#include "ph_comb.h"


//******************************************************************************
//* Public Methods - User Menu
//******************************************************************************

void phc_comm_test(void)
// a test menu for mFET-mpHOX ph comms test
// adapted from adv test menu
// this one is for loopback, and calls loopback functions
{
    uprintf("\nWelcome to the loopback ph comms test menu!  woof!\n");
    uprintf("\nCTD and optode ports are configured for pH and will not be compatible w/ CTD or optode again until restart.\n");
    // init is called in test.c, which calls this menu

    int retVal;
    time_t timer;
    time_t currentTime;
    struct tm *time_struct;
    char input;
    char buff[256];

    // for case 2 and case 6 (measure)
    double totCtTime_min;
    char fn_append[MAX_FILENAME];
    int charCount;

    // for ADV
    int mode;

    adv_open();

    // Copied from test menu loop
    while(1)
    {
        uprintf("\n\n\nmFET-mpHOX Comms Menu:\n");
        uprintf("1 -- Measure once\n");
        uprintf("2 -- Measure continuously\n");
        uprintf("5 -- Wake up ADV\n");
        uprintf("6 -- EC measurements\n");
        uprintf("7 -- Exit ADV measurement mode - in case things went wrong somewhere\n");
        uprintf("9 -- Exit to Test Menu\n");
        uprintf("\nEnter Selection [9]: ");

        UARTFlushRx();
        timer = 30000 + ROM_HibernateRTCGet();  //30 second

        while(1)
        {
            if( UARTRxBytesAvail() )
            {
                input = get_key();
                break;
            }

            //if( timer <= ROM_HibernateRTCGet() ) sleep(IDLE, 0);
        }

        switch(input)
        {
            case '1':
            {   // measure once

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] Sending command to measure once\n", buff);
                // measure function will print the rest of the narration

                struct pH_ptdata pHDatStruct;

                retVal = phc_measOnce(&pHDatStruct);

                if (retVal == 0) {

                    retVal = ph_printpHDataHdr(NULL, 1);
                    uprintf("\n");

#ifdef PRINT_PH_TIMING
                    uprintf("%.4f\t", difftime(ROM_HibernateRTCGet(), currentTime));
                    // difftime for subtracting time_t
                    // https://stackoverflow.com/questions/45614595/how-to-subtract-two-time-t-values-difftime-returns-double-instead-time-t
#endif

                    retVal = ph_printpHData(&pHDatStruct, NULL, 1);
                } else {
                    currentTime = ROM_HibernateRTCGet();
                    time_struct = localtime(&currentTime);
                    strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);
                    uprintf("\n[%s] Error getting one measurement.\n", buff);
                }

                break;
            }

            case '2':
            {
               // copied from ADV menu, use same structure

               while(1) {
                   uprintf("\nEnter measurement duration (min): ");
                   if(getUserInput(buff, 20)) // assume this blocks until something is received?
                   {
                       if(sscanf(buff, "%lf", &totCtTime_min) == 1)
                       {
                           if(totCtTime_min <= 0)
                           {
                               uprintf("\nMeasurement duration must be greater than 0.\n");
                               continue;
                           }

                           break;
                       }
                       else // sscanf returned something besides 1
                       {
                           uprintf("\nPlease enter a single number.\n");
                           continue;
                       }
                   }
                   //else break; // no default value, so take this break out
               }

               while(1) { // no default values, so keep asking until get a value

                   uprintf("\nEnter data file name (5 chars, no extension): ");
                   if (charCount = getUserInput(buff, MAX_FILENAME)) // if >0 chars received
                   {
                       if (charCount > 5) buff[5] = 0; // force it to 5
                       strcpy(fn_append, buff);

                       if (strcmp(fn_append,"NULL") == 0) // secret code to test out not writing to file
                           uprintf("\nWill not write to file.\n");
                       else
                           uprintf("\nFile append sequence: %s\n", fn_append);

                       break;
                   }
               }

               currentTime = ROM_HibernateRTCGet();
               time_struct = localtime(&currentTime);
               strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

               uprintf("[%s] ", buff);
               // measure function will print the rest of the narration

               if (strcmp(fn_append,"NULL") == 0)
                   retVal = phc_measCont(16, totCtTime_min, NULL, 1);
               else
                   retVal = phc_measCont(16, totCtTime_min, fn_append, 1);

               currentTime = ROM_HibernateRTCGet();
               time_struct = localtime(&currentTime);
               strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

               if (retVal == 1)
                   uprintf("\n[%s] Exited with errors.\n", buff);
               else
                   uprintf("\n[%s] Finished measuring and recording data.\n", buff);


               break;
            }

            case '5':
            {
                // Wake up ADV

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                adv_sendBreak();
                uprintf("[%s] Attempting to wake up ADV:\n", buff);

                ROM_SysCtlDelay(MILLISECOND*1000); // copied from optode code for a simple delay (other option is while loop using timer)

                adv_flushPort();

                break;
            }

            case '6':
            {
                // take ADV + pH measurement

                while(1) {
                    uprintf("\nEnter measurement duration (min): ");
                    if(getUserInput(buff, 20)) // assume this blocks until something is received?
                    {
                        if(sscanf(buff, "%lf", &totCtTime_min) == 1)
                        {
                            if(totCtTime_min <= 0)
                            {
                                uprintf("\nMeasurement duration must be greater than 0.\n");
                                continue;
                            }

                            break;
                        }
                        else // sscanf returned something besides 1
                        {
                            uprintf("\nPlease enter a single number.\n");
                            continue;
                        }
                    }
                    //else break; // no default value, so take this break out
                }

                while(1) { // no default values, so keep asking until get a value

                    uprintf("\nEnter data file name (5 chars, no extension): ");
                    if( charCount = getUserInput(buff, MAX_FILENAME) )
                    {
                        if (charCount > 5) buff[5] = 0; // force it to 5
                        strcpy(fn_append, buff);
                        uprintf("\nFile append sequence: %s\n", fn_append);
                        break;
                    }
                }

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] ", buff);
                // measure function will print the rest of the narration

                retVal = phc_ec(totCtTime_min, fn_append);

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                if (retVal == 1)
                    uprintf("\n[%s] Exited with errors.\n", buff);
                else
                    uprintf("\n[%s] Finished measuring and recording data.\n", buff);


                break;
            }

            case '7':
            {
                // Exit measurement mode - shouldn't need to call this unless things go wrong
                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                // int mode; - declared it up above, for now

                uprintf("[%s] Trying to exit ADV's measurement mode...\n", buff);
                if(adv_exitMeasMode() != 0) {
                    // adv_getMode(buff, &mode);
                    // uprintf("Error trying to exit measurement mode. Current mode is %s.\n", buff);
                    uprintf("Error trying to exit measurement mode. ");
                } else {
                    uprintf("Exited measurement mode. ");
                }
                // want to test this get mode function
                adv_getMode(buff, &mode);
                uprintf("Current mode is %s.\n", buff);

                break;

            }


             default: // Exit
                uprintf("\nPress enter to bring up main menu\n");
                return;

        } // end switch
    } // end while for menu printing
}


//******************************************************************************
//* Public Methods - Measure
//******************************************************************************


int phc_measOnce(struct pH_ptdata *pHDatStruct)
// back end senesor tells front end pH sensor to measure once, and writes data into the passed-in structure
// similar to phb_measOnce but has phf responses built in, for testing as loopback
// returns 0 if success, 1 if not
{
    _phb_flushRx(); // reset
    char cmd[] = PH_MEASONCE;
    if (_phb_sendTwoCharCmd(cmd) == 1) return 1;

    char rcvd[3];
    rcvd[2] = '\0'; // make it a null terminated string, to compare to the string literal #defined in ph_base
    rcvd[0] = _phf_getc();
    rcvd[1] = _phf_getc();

    if (strcmp(rcvd, PH_MEASONCE) == 0) {
        _phf_sendAck();
    } else {
        uprintf("pHF: Did not receive MO command\n");
        if (phf_rxBytesAvail()) {
            uprintf("Received: ");
            while (phf_rxBytesAvail() > 0) {
                uprintf("%#x ", _phf_getc());
            }
            uprintf("\n");
        }
        return 1;
    }

    if (_phb_receiveAck() != 0) return 1;

    phf_measOnce();

    // wait a reasonable amount of time for a response
    // cmd timeout is at 5s right now, so that should be more than enough time

    // 2/15 had it send some gibberish to test noise while reading adc
    _phb_flushRx();

    pHDatStruct->id = _phb_sortIncomingDataStruct();
    if (pHDatStruct->id != PH_IDPTD) return 1;

    pHDatStruct->sync = PH_SYNCBYTE;

    // there is no timeout in the getdata fxn, may want to check for bytes available here
    phb_readpHThermData(pHDatStruct);

    return 0;
}


int phc_measCont(double measFreq_Hz, double measDuration_min, char* fn_append, int txFlg)
// measure at measFreq_Hz Hz for measDuration_min minutes
// pass in a character array containing a <=5-char append string sequence
// consistent w/ ADV code, not passing in FIL* to save stack space (even tho only one file here)
// pass NULL for fn_append to not write to file
// pass 1 for txFlg to print to screen
// combines phb_measCont and phf_meascont, for testing as loopback
// return 0 if success, 1 if not
{
    // from phb
    int writeToFile;
    if (fn_append == NULL) writeToFile = 0;
    else writeToFile = 1;

    if (!writeToFile & !txFlg) // well i don't know what you're doing in this case
        return 1;

    uint64_t measDuration_ms = (uint64_t)(measDuration_min * 60 * 1000);
    double measPer_ms = 1000/measFreq_Hz;
    uint64_t count = 0; //  timing is calculated off a total count; allows self-correction for small inaccuracies in timing
    // esp necessary bc eg 16 Hz is 62.5 ms, and we're stuck with whole ms
    int numerrs = 0;

    struct pH_ptdata pHDatStruct_b;
    pHDatStruct_b.sync = PH_SYNCBYTE; // set for checksum calc

    // polling for cancel
    int inChar;

    // think i'm not allowed to declare these under an if
    char filename[MAX_FILENAME];
    FIL ph_ofs; // file object
    FRESULT fresult;

    if (writeToFile) {

        if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] != 1)   // copied from Thom fix 6 Aug 2021
        {
            uprintf("Some lil flag is not set properly and the microsd is not enabled. Will not be written to file.\n");
            if (!txFlg) return 1;
        } // rather than only opening files etc if condition is met, just quit now if it isn't

        sprintf(filename, "ph_%s.txt", fn_append);
        fresult = f_open(&ph_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
        if(fresult != FR_OK)
        {
            // != FR_OK
            uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
            //error_store("f_open error");
            if (!txFlg) return 1;
            writeToFile = 0;
        }
        else  // FILE is open
        {
            uprintf("Data will be written to file named %s.\n", filename);
            // Seek to the end, to append our file
            fresult = f_lseek(&ph_ofs, ph_ofs.fsize);
            if(fresult != FR_OK)
            {
                uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
            }
        }
    }

    uprintf("Initiating pH measurements for %.2f minutes at %g Hz (%g ms between measurements).\n", measDuration_min, measFreq_Hz, measPer_ms);

    if (measFreq_Hz >= PH_ADCFREQ_VRS) { // for now, set ADC polling at 20 Hz.  it can't go faster than that
        uprintf("Warning: frequency %.2f Hz is faster than pH ADC frequency %d Hz; measurements will duplicate.\n", measFreq_Hz, PH_ADCFREQ_VRS);
    }

/********* here need to break up function *********/
    //if (phb_startMeas() == 1) return 1;

    char cmd[] = PH_MEASCONT;
    if (_phb_sendTwoCharCmd(cmd) == 1) return 1;

    // from phf
    char rcvd[3];
    rcvd[2] = '\0'; // make it a null terminated string, to compare to the string literal #defined in ph_base

    rcvd[0] = _phf_getc();
    rcvd[1] = _phf_getc();

    if (strcmp(rcvd, PH_MEASCONT) == 0) {
        _phf_sendAck();
    } else {
        uprintf("pHF: Did not receive MC command\n");
        if (phf_rxBytesAvail()) {
            uprintf("Received: ");
            while (phf_rxBytesAvail() > 0) {
                uprintf("%#x ", _phf_getc());
            }
            uprintf("\n");
        }
        return 1;
    }

    if (_phb_receiveAck() != 0) return 1;

/********* phf section *********/
    struct pH_ptdata pHDatStruct_f;
    pHDatStruct_f.sync = PH_SYNCBYTE;
    pHDatStruct_f.id = PH_IDPTD;
    pHDatStruct_f.count = -1; // so it gets incremented to 0 after first read
    // all data, incluidng this count, will have issues if phb asks for a measurement before first read is done

    int stopFlg = 0;

    // measure stuff copied from sample.c, also using modified ads1248_iso.c
    openADS1248_iso();              // turn on isolated power to 24bit A/D converter
    ROM_SysCtlDelay(100*MILLISECOND);         // new, 3 Aug 2021 - BUG fix (Crash)
    ADS1248_gpio_init();            // defd in ADS1248_iso.c  (CRASH HAPPENS HERE - first call to fast_sampling is good, next call crashes

    ADS1248_gpio_ADG1609_mux(3); // for Vrsi
    ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog

    startmeas_ADS1248_iso(1, 6, 1, PH_ADCFREQ_VRS); // Vrsi, 20 Hz

/********* back to phb *********/

    Timer_1ms(TRUE); // reset timer

    // sadly i can't set &ph_ofs NULL if not writing to file and just pass it through, since it's a pointer.
    // but anyways also need to add an extra field for this function's count

    if (writeToFile) {
        sd_fprintf(&ph_ofs, "phb count\t");
        if (txFlg) uprintf("phb count\t");
        ph_printpHDataHdr(&ph_ofs, txFlg);
        sd_fprintf(&ph_ofs, "\n");
        if (txFlg) uprintf("\n");
    } else {
        uprintf("phb count\t");
        ph_printpHDataHdr(NULL, txFlg);
        uprintf("\n");
    }


    while( Timer_1ms(FALSE) <= measDuration_ms) {
        if (Timer_1ms(FALSE) >= (count+1) * measPer_ms) { // use count+1 bc we ask for meas after it has happened
/********* here need to break up function *********/
            //if (phb_getMeas(&pHDatStruct_b) == 1) {
            _phb_flushRx(); // if errors in previous transmission, seems to leave some characters in the buffer
            char cmd2[] = PH_GETMEAS;
            if (_phb_sendTwoCharCmd(cmd2) == 1) return 1;

            unsigned long time1;
            time1 = Timer_1ms(FALSE); // allows timer to be in use

/********* phf *********/
            ROM_SysCtlDelay(MILLISECOND); // give some time for command to reach phf

            //while(iso_DRDY()) { // defined in ads1248_iso.c - means we're still waiting for conversion
            // remove the while loop here, since we can't have it hang in this while loop waiting... phb needs to do stuff
            // once in a while, command from phb will come when it's ready for a new conversion, so simulate that here
            while(!iso_DRDY()) { // use a while to check that it goes back up after this
                pHDatStruct_f.Vrs = readonce_ADS1248_iso();
                pHDatStruct_f.Vtherm = pollADS1248_iso_once(0, 6, 1, PH_ADCFREQ_VTHERM);
                //uprintf("pH front: read in %" PRId32 " for Vrsi and %" PRId32 " for VTherm\n", pHDatStruct_f.Vrs, pHDatStruct_f.Vtherm);
                startmeas_ADS1248_iso(1, 6, 1, PH_ADCFREQ_VRS); // set it going for Vrs again
                (pHDatStruct_f.count)++;
                pHDatStruct_f.checksum = ph_calcChecksum(&pHDatStruct_f);
            }
            // wait for poll and send info upon receipt
            if (phf_rxBytesAvail() == 2) { // \0 doesn't get transmitted, it's just 2 chars

                rcvd[0] = _phf_getc();
                rcvd[1] = _phf_getc();

               // if (strcmp(rcvd, PH_STOPMEAS) == 0) {
               //    stopFlg = 1;
               // }

                if (strcmp(rcvd, PH_GETMEAS) == 0) {
                    _phf_sendpHThermData(&pHDatStruct_f);
                } else {
                    uprintf("pHF: received 2 chars but not expected get-meas cmd: got %#x %#x\n", rcvd[0], rcvd[1]);
                }
            } else if (phf_rxBytesAvail()) {
                uprintf("pHF: not 2 chars in rx buffer, they are: ");
                while (phf_rxBytesAvail() > 0) {
                    uprintf("%#x ", _phf_getc());
                }
                uprintf("\n");
            } else
                uprintf("pHF: should have received get-meas but no characters in rx buffer\n");

/********* phb *********/
            while (phb_rxBytesAvail() < 2 && !stopFlg) {
                if ( Timer_1ms(FALSE) - time1 > PHGETMEAS_TIMEOUT_MS) {
                    uprintf("pHB get meas: Bytes not available on port.\n");
                    uprintf("Failed to get pH data %" PRIu64 "; waiting for next point\n", count);
                    numerrs++;
                    stopFlg = 1; // reusing this flag; do not want to end function, wnat to break out of a few conditions
                }
            }

            if (stopFlg == 0) {
                pHDatStruct_b.id = _phb_sortIncomingDataStruct();

                if (pHDatStruct_b.id != PH_IDPTD) {
                    uprintf("pHB: Did not receive correct ID byte. Got %#x\n", pHDatStruct_b.id);
                    uprintf("Failed to get pH data %" PRIu64 "; waiting for next point\n", count);
                    if(phb_rxBytesAvail()) {
                        uprintf("pHB: flush buffer: ");
                        while (phb_rxBytesAvail() > 0) {
                            uprintf("%#x ", _phb_getc());
                        }
                        uprintf("\n");
                    }
                    //_phb_flushRx();
                    numerrs++;
                    stopFlg = 1;
                }
            }

            if (stopFlg == 0) {

                // there is no timeout in the getdata fxn, may want to check for bytes available here
                phb_readpHThermData(&pHDatStruct_b);

                if (writeToFile) sd_fprintf(&ph_ofs, "%" PRIu64 "\t", count);
                if (txFlg) uprintf("%" PRIu64 "\t", count);

#ifdef PRINT_PH_TIMING
                if (writeToFile) sd_fprintf(&ph_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
                if (txFlg) uprintf("%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                if (writeToFile) {
                    ph_printpHData(&pHDatStruct_b, &ph_ofs, txFlg);
                    sd_fprintf(&ph_ofs, "\n");
                    if (txFlg) uprintf("\n");
                } else {
                    ph_printpHData(&pHDatStruct_b, NULL, txFlg);
                    uprintf("\n");
                }

                // troubleshooting code
                if((int)pHDatStruct_b.checksum == 1) {
                    uprintf("pHDatStruct_f: ");
                    ph_printHexData(&pHDatStruct_f);
                    uprintf("pHDatStruct_b: ");
                    ph_printHexData(&pHDatStruct_b);
                }
            }

            count++;
            stopFlg = 0;

            //uprintf("Ended one measurement, variable check... phf rx buffer: %d bytes.  numerrs: %d\n", phf_rxBytesAvail(),numerrs);
        } // end of if(time for next point)

        // poll continuously for these things while waiting for next meas time

        // poll for a ctrl-X to quit - copied partly from optode code
        if(UARTRxBytesAvail())
        {
            inChar = UARTgetc();
            if(inChar == CTRL_X)
            {
                uprintf("Ctrl-x received, aborting measurement.\n");
                break;
            }
        }

/********* phf *********/

        while(!iso_DRDY()) { // use a while to check that it goes back up after this
            pHDatStruct_f.Vrs = readonce_ADS1248_iso();
            pHDatStruct_f.Vtherm = pollADS1248_iso_once(0, 6, 1, PH_ADCFREQ_VTHERM);
            //uprintf("pH front: read in %" PRId32 " for Vrsi and %" PRId32 " for VTherm\n", pHDatStruct_f.Vrs, pHDatStruct_f.Vtherm);
            startmeas_ADS1248_iso(1, 6, 1, PH_ADCFREQ_VRS); // set it going for Vrs again
            (pHDatStruct_f.count)++;
            pHDatStruct_f.checksum = ph_calcChecksum(&pHDatStruct_f);
        }

    } // end of measurement duration

/********* here need to break up function *********/
    //phb_stopMeas();

    // there apears to be leftover chars somewhere, SM is not being received because GM is in front of it
    if (phf_rxBytesAvail()) {
        uprintf("End meas duration, phf rx buffer: ");
        while (phf_rxBytesAvail() > 0) {
            uprintf("%#x ", _phf_getc());
        }
        uprintf("\n");
    }

    char cmd3[] = PH_STOPMEAS;
    if (_phb_sendTwoCharCmd(cmd3) == 1) return 1;

    //ROM_SysCtlDelay(MILLISECOND);

    rcvd[0] = _phf_getc();
    rcvd[1] = _phf_getc();

    if (strcmp(rcvd, PH_STOPMEAS) == 0) {
        _phf_sendAck();
    } else {
        uprintf("pHF: Did not receive SM command.  Received %#x %#x\n",rcvd[0], rcvd[1]);

        if (phf_rxBytesAvail()) {
            uprintf("More in phf rx buffer: ");
            while (phf_rxBytesAvail() > 0) {
                uprintf("%#x ", _phf_getc());
            }
            uprintf("\n");
        }
        return 1;
    }

    _phf_sendAck();
    closeADS1248_iso();

    if (_phb_receiveAck() != 0) return 1;

    uint64_t elapsedTime = Timer_1ms(FALSE);

    //uprintf("Total %" PRIu64 " pH data received, plus %d readin errors, over a period of %.2f min\n", count-numerrs, numerrs, (double)(elapsedTime)/60000);
    uprintf("Total %" PRIu64 " pH data received, plus %d readin errors, over a period of %" PRIu64 " ms\n", count-numerrs, numerrs, elapsedTime);

    // close files
    if (writeToFile) {
        fresult = f_close( &ph_ofs );
        if(fresult != FR_OK)
        {
            uprintf("f_close error: %s\n", StringFromFresult(fresult));
            //error_store("f_close error");
        }
    }

    return 0;

}


int phc_ec(double measDuration_min, char* fn_append)
// measure ADV and pH for measDuration_min minutes at whatever frequency is set in ADV settings
// pass in a character array containing a <=5-char append string sequence
// combines adv_measure and phc_measCont for a loopback test
// no more option to output to screen; must write to file (bc so much ADV data)
// ph measurements are not timed; rather, ask for and receive one every time a velocity measurement is received
// ph data will show up in velocity data file
// for printing of timing data, works best if print_adv_timing is defined (adv.h) but not print_ph_timing (ph_base.h)
// return 0 if success, 1 if not
{
    uint64_t measDuration_ms = (uint64_t)(measDuration_min * 60 * 1000); // switch from us in MIT code to ms here, to use timers

    // not sure the below still matters
    /*
    // check that continuous measurement is enabled
    if (_adv_config.B1 != 0) {
        cerr << "Vector is in burst, not continuous mode. Download a new deployment file.\n";
        return 1;
    } */

    struct adv_dataHeader datHdrStruct;
    struct adv_probeChkData prbChkStruct;
    struct adv_sysData sysDatStruct;
    struct adv_velocityData velDatStruct;
    struct pH_ptdata pHDatStruct_b;
    pHDatStruct_b.sync = PH_SYNCBYTE; // set for checksum calc

    char filename[MAX_FILENAME];
    FIL reused_ofs, sysdat_ofs, veldat_ofs; // file objects
    FRESULT fresult;

    // these are for velocity and sys data
    int16_t checkSum;
    int ID;

    // keep track of how many - now that I'm not counting pulses
    int numsys = 0, numvel = 0, numvelerrs = 0, numph = 0, numpherrs = 0;

    // polling for cancel
    int inChar;

    if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] != 1)   // copied from Thom fix 6 Aug 2021
    {
        uprintf("Some lil flag is not set properly and the microsd is not enabled. This fxn relies on writing to files. Goodbye, better luck next time.\n");
        return 1;
    } // rather than only opening files etc if condition is met, just quit now if it isn't

    uprintf("Initiating ADV+pH measurements for %.2f minutes at %d Hz\n", measDuration_min, 512/_adv_config.AvgInterval);
    if (512/_adv_config.AvgInterval >= PH_ADCFREQ_VRS) { // for now, set ADC polling at 20 Hz.  it can't go faster than that
        uprintf("Warning: frequency %d Hz is faster than pH ADC frequency %d Hz; measurements will duplicate.\n", 512/_adv_config.AvgInterval, PH_ADCFREQ_VRS);
    }
    uprintf("Files will be named [uc / dh / pc / sd / vd]_%s.txt for user config, data header, probe check, system data, and velocity+conc data, respectively.\n", fn_append);

    // set pH going first, so it doesn't mess with ADV timing at the beginning
    // since pH is polled, but ADV just streams
/********* here need to break up function *********/
    //if (phb_startMeas() == 1) return 1;

    char cmd1[] = PH_MEASCONT;
    if (_phb_sendTwoCharCmd(cmd1) == 1) return 1;

    // from phf
    char rcvd[3];
    rcvd[2] = '\0'; // make it a null terminated string, to compare to the string literal #defined in ph_base

    rcvd[0] = _phf_getc();
    rcvd[1] = _phf_getc();

    if (strcmp(rcvd, PH_MEASCONT) == 0) {
        _phf_sendAck();
    } else {
        uprintf("pHF: Did not receive MC command\n");
        if (phf_rxBytesAvail()) {
            uprintf("Received: ");
            while (phf_rxBytesAvail() > 0) {
                uprintf("%#x ", _phf_getc());
            }
            uprintf("\n");
        }
        return 1;
    }

    if (_phb_receiveAck() != 0) return 1;

/********* phf section *********/
    struct pH_ptdata pHDatStruct_f;
    pHDatStruct_f.sync = PH_SYNCBYTE;
    pHDatStruct_f.id = PH_IDPTD;
    pHDatStruct_f.count = -1; // so it gets incremented to 0 after first read
    // all data, incluidng this count, will have issues if phb asks for a measurement before first read is done

    int stopFlg = 0;

    // measure stuff copied from sample.c, also using modified ads1248_iso.c
    openADS1248_iso();              // turn on isolated power to 24bit A/D converter
    ROM_SysCtlDelay(100*MILLISECOND);         // new, 3 Aug 2021 - BUG fix (Crash)
    ADS1248_gpio_init();            // defd in ADS1248_iso.c  (CRASH HAPPENS HERE - first call to fast_sampling is good, next call crashes

    ADS1248_gpio_ADG1609_mux(3); // for Vrsi
    ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog

    startmeas_ADS1248_iso(1, 6, 1, PH_ADCFREQ_VRS); // Vrsi, 20 Hz

    //uprintf("\npH measurements kicked off.\n");

    // back to ADV
    // start recording - then will need 1 second before header data is available
    char cmd[] = "ST";
    //char cmd[] = "SD"; // 5/15/18 test if it's still streaming to serial
    if (_adv_sendTwoCharCmd(cmd) == 1) return 1;

    if (_adv_receiveAck() != 0) return 1;

    // 11/16/21 no longer need this since we're not implementing pauses during startup
    // Timer_1ms(TRUE); // reset 1ms tick - assumes timer not being used for anything else, otherwise need to store value here and calc a difference later

    // need to wait 1 s; while we wait, output user config and file headers
    //FA_OPEN_APPEND is not supported in this fatfs version

    // user config - open, output, and close
    sprintf(filename, "uc_%s.txt", fn_append);
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printUserConfig(&reused_ofs,0);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    // system and velocity data - these need to stay open

    sprintf(filename, "sd_%s.txt", fn_append);
    fresult = f_open(&sysdat_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&sysdat_ofs, sysdat_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    sprintf(filename, "vd_%s.txt", fn_append);
    fresult = f_open(&veldat_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&veldat_ofs, veldat_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printSysDataHdr(&sysdat_ofs);
    adv_printVelDataHdr(&veldat_ofs);

    // add pH header to end of velocity header, in that file
    sd_fprintf(&veldat_ofs, "\t");
    ph_printpHDataHdr(&veldat_ofs, 0);

    sd_fprintf(&sysdat_ofs, "\n");
    sd_fprintf(&veldat_ofs, "\n");

    // 1 s after sent command, can get header and probe check data
    //while ( Timer_1ms(FALSE) < 1000) ; // waiting loop
    // 11/16/21 this is now removed as it seems to cause timing problems, and is not necessary since functions will wait for data until it appears

    if (adv_getDatHeader(&datHdrStruct) == 1) return 1;
    if (adv_getProbeChkDat(&prbChkStruct) == 1) return 1;

    // need to wait another s; while we wait, output
    // data header files and probe check can open, write, and close now

    sprintf(filename, "dh_%s.txt", fn_append);
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printDatHeader(&datHdrStruct, &reused_ofs);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    sprintf(filename, "pc_%s.txt", fn_append);
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printProbeChkDat(&prbChkStruct, &reused_ofs);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    // 2 s after command, can get system data
    //while ( Timer_1ms(FALSE) < 2000) ; // waiting loop
    // 11/16/21 this is now removed as it seems to cause timing problems, and is not necessary since functions will wait for data until it appears

    // system data requires a little more processing
    // check we're getting the right structure
    ID = _adv_sortIncomingDataStruct(&checkSum);
    if (ID == -1) return 1; // means transmission or sync byte failed- error msg already printed
    if (ID != ADV_IDSYS) {
        uprintf("Did not receive correct structure ID. Got %#x\n", ID);
        return 1;
    }
    if (adv_getSysData(&sysDatStruct, checkSum) == 1) return 1;
    numsys++;

    // without synch pulses, Vector will just start measuring
    // we'll estimate this as the starting time, so reset timer here
    Timer_1ms(TRUE);

    // output sys data
#ifdef PRINT_ADV_TIMING
    sd_fprintf(&sysdat_ofs, "0\t");
#endif
    adv_printSysData(&sysDatStruct, &sysdat_ofs);
    sd_fprintf(&sysdat_ofs, "\n");

    // sit and wait for data
    // error condition: do not abort if something goes wrong
    // TODO: rethink how to handle errors, when pH data stream is also coming in

    // for debugging
    uprintf("\nStarting to measure.\n");

    while( Timer_1ms(FALSE) < measDuration_ms)
    {
        // check for velocity and system data
        ID = _adv_sortIncomingDataStruct(&checkSum);

        switch (ID) {
            case -1: // transmission or synch byte failed
            {
                break;
            }

            case ADV_IDSYS: //system data
            {
                // other code has somethign to throw out incomplete data structures - ??

                if (adv_getSysData(&sysDatStruct, checkSum) == 1) {
                    uprintf("Failed to get system data %d; continuing to check for more data\n", numsys++);
                    break;
                }
#ifdef PRINT_ADV_TIMING
                sd_fprintf(&sysdat_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                adv_printSysData(&sysDatStruct, &sysdat_ofs);
                sd_fprintf(&sysdat_ofs, "\n");
                numsys++;

                // since we're not coordinating synch pulses, ok to go back to top of loop and check again for velocity
                // rather than having to read the velocity immediately

                break;

            }

            case ADV_IDVEL: //velocity data
            {
                // other code has somethign to throw out incomplete data structures - ??

                if (adv_getVelData(&velDatStruct, checkSum) == 1) {
                    uprintf("Failed to get velocity data %d; continuing to check for more data\n", numvel++);
                    break;
                }

                while (velDatStruct.Count != numvel % 256) {
                    uprintf("Velocity data point #%d not read in, read in point #%d.\n", numvel, (int)(velDatStruct.Count));
                    numvel++;
                    numvelerrs++;
                }

                // get ph data here, then print them both out (for better time synching, since printing takes time)
/********* here need to break up function *********/
                //if (phb_getMeas(&pHDatStruct_b) == 1) {
                _phb_flushRx(); // if errors in previous transmission, seems to leave some characters in the buffer
                char cmd2[] = PH_GETMEAS;
                if (_phb_sendTwoCharCmd(cmd2) == 1) return 1;

                unsigned long time1;
                time1 = Timer_1ms(FALSE); // allows timer to be in use

/********* phf *********/
                ROM_SysCtlDelay(MILLISECOND); // give some time for command to reach phf

                //while(iso_DRDY()) { // defined in ads1248_iso.c - means we're still waiting for conversion
                // remove the while loop here, since we can't have it hang in this while loop waiting... phb needs to do stuff
                // once in a while, command from phb will come when it's ready for a new conversion, so simulate that here
                while(!iso_DRDY()) { // use a while to check that it goes back up after this
                    pHDatStruct_f.Vrs = readonce_ADS1248_iso();
                    pHDatStruct_f.Vtherm = pollADS1248_iso_once(0, 6, 1, PH_ADCFREQ_VTHERM);
                    //uprintf("pH front: read in %" PRId32 " for Vrsi and %" PRId32 " for VTherm\n", pHDatStruct_f.Vrs, pHDatStruct_f.Vtherm);
                    startmeas_ADS1248_iso(1, 6, 1, PH_ADCFREQ_VRS); // set it going for Vrs again
                    (pHDatStruct_f.count)++;
                    pHDatStruct_f.checksum = ph_calcChecksum(&pHDatStruct_f);
                }
                // wait for poll and send info upon receipt
                if (phf_rxBytesAvail() >= 2) { // \0 doesn't get transmitted, it's just 2 chars

                    rcvd[0] = _phf_getc();
                    rcvd[1] = _phf_getc();

                   // if (strcmp(rcvd, PH_STOPMEAS) == 0) {
                   //    stopFlg = 1;
                   // }

                    if (strcmp(rcvd, PH_GETMEAS) == 0) {
                        _phf_sendpHThermData(&pHDatStruct_f);
                    } else {
                        uprintf("pHF: unknown cmd: %#x %#x\n", rcvd[0], rcvd[1]);
                        while (phf_rxBytesAvail() > 0) { // in case there are more
                            uprintf("%#x ", _phf_getc()); // this flushes buffer
                        }
                        uprintf("\n");
                    }

                } else
                    uprintf("pHF: should have received get-meas but no characters in rx buffer\n");

/********* phb *********/
                while (phb_rxBytesAvail() < 2 && !stopFlg) {
                    if ( Timer_1ms(FALSE) - time1 > PHGETMEAS_TIMEOUT_MS) {
                        uprintf("pHB get meas: Bytes not available on port.\n");
                        uprintf("Failed to get pH data %d; waiting for next point\n", numvel);
                        numpherrs++;
                        stopFlg = 1; // reusing this flag; do not want to end function, wnat to break out of a few conditions
                    }
                }

                if (stopFlg == 0) {
                    pHDatStruct_b.id = _phb_sortIncomingDataStruct();

                    if (pHDatStruct_b.id != PH_IDPTD) {
                        uprintf("pHB: Did not receive correct ID byte. Got %#x\n", pHDatStruct_b.id);
                        uprintf("Failed to get pH data %d; waiting for next point\n", numvel);
                        if(phb_rxBytesAvail()) {
                            uprintf("pHB: flush buffer: ");
                            while (phb_rxBytesAvail() > 0) {
                                uprintf("%#x ", _phb_getc());
                            }
                            uprintf("\n");
                        }
                        //_phb_flushRx();
                        numpherrs++;
                        stopFlg = 1;
                    }
                }

                if (stopFlg == 0) {

                    // there is no timeout in the getdata fxn, may want to check for bytes available here
                    phb_readpHThermData(&pHDatStruct_b);
                }

#ifdef PRINT_ADV_TIMING
                sd_fprintf(&veldat_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                adv_printVelData(&velDatStruct, &veldat_ofs, numvel); // it starts at 0

                // pH: do not need to print out phb count - same as numvel

                if (stopFlg == 0) { // have ph data

                    sd_fprintf(&veldat_ofs, "\t");

#ifdef PRINT_PH_TIMING
                    sd_fprintf(&veldat_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                    ph_printpHData(&pHDatStruct_b, &veldat_ofs, 0);

                }

                sd_fprintf(&veldat_ofs, "\n");

                stopFlg = 0; // reset

                numph++;
                numvel++;

                break;
            }

            default:
            {
                uprintf("Got synch byte, but ID byte received (%#x) does not correspond to system or velocity data. Ignoring these bytes, continuing to check for more data.\n", ID);
                break;
            }
        } // end switch

        // poll for a ctrl-X to quit - copied partly from optode code
        if(UARTRxBytesAvail())
        {
            inChar = UARTgetc();
            if(inChar == CTRL_X)
            {
                uprintf("Ctrl-x received, aborting measurement.\n");
                break;
            }
        }

/********* phf *********/

        while(!iso_DRDY()) { // use a while to check that it goes back up after this
            pHDatStruct_f.Vrs = readonce_ADS1248_iso();
            pHDatStruct_f.Vtherm = pollADS1248_iso_once(0, 6, 1, PH_ADCFREQ_VTHERM);
            //uprintf("pH front: read in %" PRId32 " for Vrsi and %" PRId32 " for VTherm\n", pHDatStruct_f.Vrs, pHDatStruct_f.Vtherm);
            startmeas_ADS1248_iso(1, 6, 1, PH_ADCFREQ_VRS); // set it going for Vrs again
            (pHDatStruct_f.count)++;
            pHDatStruct_f.checksum = ph_calcChecksum(&pHDatStruct_f);
        }

        // go back to top of loop, keep checking for data until timeout

    }

    // all done measuring!
    unsigned long elapsedTime = Timer_1ms(FALSE);

/********* here need to break up function *********/
    //phb_stopMeas();

    // there apears to be leftover chars somewhere, SM is not being received because GM is in front of it
    if (phf_rxBytesAvail()) {
        uprintf("End meas duration, phf rx buffer: ");
        while (phf_rxBytesAvail() > 0) {
            uprintf("%#x ", _phf_getc());
        }
        uprintf("\n");
    }

    char cmd3[] = PH_STOPMEAS;
    if (_phb_sendTwoCharCmd(cmd3) == 1){
        // return 1;
        uprintf("Failed sending cmd.\n");
    }

    //ROM_SysCtlDelay(MILLISECOND);

    rcvd[0] = _phf_getc();
    rcvd[1] = _phf_getc();

    if (strcmp(rcvd, PH_STOPMEAS) == 0) {
        _phf_sendAck();
    } else {
        uprintf("pHF: Did not receive SM command.  Received %#x %#x\n",rcvd[0], rcvd[1]);

        if (phf_rxBytesAvail()) {
            uprintf("More in phf rx buffer: ");
            while (phf_rxBytesAvail() > 0) {
                uprintf("%#x ", _phf_getc());
            }
            uprintf("\n");
        }
        //return 1;
    }

    _phf_sendAck();
    closeADS1248_iso();

    //if (_phb_receiveAck() != 0) return 1;
    _phb_receiveAck(); // this will print out an error if it failed; but we want to continue now

    uprintf("Total %d system data, %d velocity data, and %d pH data received, plus %d velocity and %d pH readin errors, over a period of %.2f min\n", numsys, numvel-numvelerrs, numph-numpherrs, numvelerrs, numpherrs, (double)(elapsedTime)/60000);

    // close files
    fresult = f_close( &sysdat_ofs );
    if(fresult != FR_OK)
    {
        uprintf("System data f_close error: %s\n", StringFromFresult(fresult));
        //error_store("f_close error");
    }

    fresult = f_close( &veldat_ofs );
    if(fresult != FR_OK)
    {
        uprintf("Velocity data f_close error: %s\n", StringFromFresult(fresult));
        //error_store("f_close error");
    }

    return adv_exitMeasModeNoNarration();
}

#endif
