Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

main.cpp

Committer:
uci1
Date:
2019-06-05
Revision:
125:ce4045184366
Parent:
123:6267de54c8ba

File content as of revision 125:ce4045184366:

#include "mbed.h"

const uint32_t gPowerOnTime( time(0) );

// start a watchdog as soon as possible
#include "Watchdog.h"
Watchdog::SnKickStarter gKickStarter(WDFAILSAFE);

// CHIPBOARD is defined in SnPreCompOptions.h

#include <stdint.h>
#include "SnConstants.h"

#ifndef USE_INTERFACE_CHIP
#include "SnUIDtoMac.h"
extern "C" void mbed_mac_address(char * mac) {
#ifdef DEBUG
    printf("calling MY mbed_mac_address\r\n");
#endif
    // to avoid calling the interface chip, the mac address may be hard coded (ugh!)
    
    // even though SnConfigFrame will cache the mac address,
    // it appears some low level code (in Ethernet?) calls this
    // function. altho searching the code for mbed_mac_address doesn't
    // find it, this function still gets called.
    // so -- cache the result and return it.
    static char cachedMac[sizeof(uint64_t)];
    static bool cached = false;
    if (cached) {
        memmove(mac, cachedMac, sizeof(uint64_t));
#ifdef DEBUG
        printf("using cached mac=%02X%02X%02X%02X%02X%02X\r\n",
            mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
#endif
    } else {
        SnUIDtoMac::GetMacAddress(mac);
        memmove(cachedMac, mac, sizeof(uint64_t));
        cached = true;
#ifdef DEBUG
        printf("got new mac mac=%02X%02X%02X%02X%02X%02X\r\n",
            mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
#endif
    }
};
#endif // USE_INTERFACE_CHIP

#ifdef DEBUG
#include "SnMemMonitor.h"
#endif

#include "SDFileSystem.h"
#ifdef USE_MODSERIAL
#define MODSERIAL_RX_BUF_SIZE 512//Geoffs512
#define MODSERIAL_TX_BUF_SIZE 512//Geoffs512 
#include "MODSERIAL.h"
#endif // USE_MODSERIAL
#include "FATDirHandle.h"
#include "EthernetPowerControl.h"
#include "SnBitUtils.h"
#include "SnSDUtils.h"
#include "SnConfigFrame.h"
#include "SnEventFrame.h"
#include "SnStatusFrame.h"
#include "SnHeaderFrame.h"
#include "SnHeartbeatFrame.h"
#include "SnClockSetFrame.h"
#include "SnSignalStrengthFrame.h"
#include "SnCommWin.h"
#ifdef ENABLE_AFAR_COMM
#ifdef USE_ETH_INTERFACE
#include "SnCommAfarTCP.h"
#else
#include "SnCommWinAfar.h"
#ifdef ENABLE_AFAR_TWITTER
#include "SnCommWinTwitter.h"
#endif // ENABLE_AFAR_TWITTER
#endif // USE_ETH_INTERFACE
#endif // ENABLE_AFAR_COMM
#ifdef ENABLE_USB_COMM
#include "SnCommWinUsb.h"
#endif // ENABLE_USB_COMM
#ifdef ENABLE_SBD_COMM
#include "SnCommWinSBD.h"
#endif // ENABLE_SBD_COMM
//#include "SnBase64.h"
#ifdef USE_RTOS_TIMER
#include "RtosTimer.h"
#endif // USE_RTOS_TIMER
#include "DS1820.h"
#include "SnL1SingleFreqSupp.h"

extern "C" void mbed_reset();

//
// MBED PINS (ordered by number)
//
// leds (for debugging)
DigitalOut led1(LED1,1);
DigitalOut led2(LED2,1);
DigitalOut led3(LED3,1);
DigitalOut led4(LED4,1);

#ifndef CHIPBOARD
#error CHIPBOARD is not defined! Define it in SnPreCompOptions.h
#endif

// Set up power pins - Note that it's Zero for "on" in ATWD2013, but high for "on" in SST2014
#if CHIPBOARD==ATWD4CH
DigitalOut PIN_turn_on_system(p17,1); // this turns off the system
DigitalOut PIN_turn_on_amps(p25,1);   // this turns off the amps
#else
DigitalOut PIN_turn_on_system(p17,0); // this turns off the system
DigitalOut PIN_turn_on_amps(p25,0);   // this turns off the amps
#endif // ATWD4CH
// SD card select
DigitalOut PIN_SD_CS(p8,0);
#if CHIPBOARD==ATWD4CH
// Activate/select chip by falling edge
DigitalOut PIN_ADC_CS(p9,0);
// clock signal to activate PLA setting
DigitalOut PIN_PLA_cs(p10,0);
#else
I2C PIN_i2c(p9, p10);
#endif // ATWD4CH
// To force a trigger
DigitalOut PIN_forceTrigger(p11,0);  //modification
// To suppress thermal triggers
DigitalOut PIN_enableThermTrig(p12,0);
#if CHIPBOARD==ATWD4CH
// Restart clock on all FPGAs.
DigitalOut PIN_DoNotRestartAllClocks(p13,0);
#else
DigitalOut PIN_ResetChips(p13,0);
#endif // ATWD4CH
#if CHIPBOARD==ATWD4CH
// This tells the DFPGAs to store the data on motherboard FPGA and
// read it out.
DigitalIn PIN_a_sf_clk(p14);
DigitalIn PIN_rst_a_sf(p15);
#else
DigitalIn PIN_dataReady(p14); // when triggered data is stored in the mb FPGA and ready for readout by mbed
#endif // ATWD4CH
// afar power
DigitalOut PIN_afar_power(p16,0);
// batter voltage/current measurement
AnalogIn PIN_vADC1(p19); 
AnalogIn PIN_vADC2(p18); 
#if CHIPBOARD==ATWD4CH
// Lock daughter card registeres (during data readout).
DigitalOut PIN_lockRegisters(p20,0);
#else
DigitalOut PIN_readingData(p20,0);
#endif // ATWD4CH
// iridium (SBD) power
DigitalOut PIN_iridSbd_power(p21,0);
// Majority logic pins
DigitalOut PIN_MajLogHiBit(p22,0);
DigitalOut PIN_MajLogLoBit(p23,0);
// To launch a heartbeat pulse
DigitalOut PIN_heartbeat(p24,0);
#if CHIPBOARD==ATWD4CH
// Tell FPGA to be ready to accept DAC values
DigitalOut PIN_start_fpga(p26,0); 
#else
DigitalIn PIN_unused26(p26);
#endif // ATWD4CH
#if CHIPBOARD==ATWD4CH
// Two bits to the select the daughter card for readout
DigitalOut PIN_selCardHiBit(p29,0);
DigitalOut PIN_selCardLoBit(p30,0);
#else
DigitalOut PIN_dualOrSingleThresholds(p29, 1); // 1 = dual (hi AND lo thresh crossings)
DigitalOut PIN_differentialTrigSignal(p30, 1); // 1 = chip sends one trigger signal per channel with reduced noise, 0 = chip sends each comparator signal separately
#endif // ATWD4CH
// Setup SPI pins
SPI PIN_spi( p5, p6, p7 );

#if CHIPBOARD!=ATWD4CH
PinName gThermPinName(p15);
DS1820 PIN_therm(gThermPinName, gThermPinName, false); // be default, on external power
#endif

// we have to do this shit because Serial and MODSERIAL don't have virtual functions
// so calling, e.g. readable from a Serial* will call Serial::readable instead of MODSERIAL::readable
// and the comms will wait forever
#ifdef USE_MODSERIAL
#define MAIN_SERIALTYPE AjK::MODSERIAL
#else
#define MAIN_SERIALTYPE Serial
#endif // USE_MODSERIAL

//#if defined(DEBUG) || defined(ENABLE_USB_COMM) || defined(EVT_TIME_PROFILE)
// this needs to be first in case some other global uses a print statement
static MAIN_SERIALTYPE  gCpu( USBTX, USBRX ); // defined here so it might be used for debugging output
//#endif // DEBUG or ENABLE_USB_COMM

static MAIN_SERIALTYPE  gSBDport(p28, p27, 
#ifdef USE_MODSERIAL
                                 MODSERIAL_TX_BUF_SIZE, MODSERIAL_RX_BUF_SIZE,
#endif // USE_MODSERIAL
                                 "sbd");

// The SD card
static SDFileSystem     sd(p5, p6, p7, p8, SnSDUtils::kSDdir+1); // no leading '/'

// local file system is still created even if USE_INTERFACE_CHIP is not defined
// this is done to allow the mbed to be reprogrammed remotely
static LocalFileSystem  local((SnCommWin::kLocalDir)+1); // no leading '/'

//
// fwd declare fcns
//
void                      ReadAllRegisters();
void                      ReadRegister(const uint8_t chan, int16_t* dev);
void                      SaveHeartbeat();
bool                      SaveEvent(const int32_t etms);
void                      WaitTrigAndSendClock();
void                      SetConfigAndMakeOutputFile();
SnCommWin::ECommWinResult OpenCommWin(const bool isStartupWin=false);
void                      MakeOutputFile(const bool stopRunning=false);
bool                      IsPowerAllowedFor(const bool isCommWin,
                                            const uint8_t devicesInUse,
                                            const SnConfigFrame::EDatPackBit b);
void                      SetPower(const bool isCommWin,
                                   const uint8_t devicesInUse);
void                      CheckPower(const bool isCommWin,
                                     const bool saveReading=true,
                                     const uint8_t devicesInUse=
                                       SnConfigFrame::kIrid |
                                       SnConfigFrame::kAfar);
void                      CheckTemp();
void                      UpdateTemperature();
bool                      AreCardsPowered(const bool checkPin);
void                      GetAvePowerReading();
#if CHIPBOARD!=ATWD4CH
void                      InitTempProbe();
#endif
void                      ResetCountersClearEvt();
/*
void                      CalcRate(const uint32_t numtrgs,
                                   const double   tottime_ms,
                                   float&         rate);
void                      GetRates(float& thmrate, float& evtrate);
void                      AddToRate(const double dt, const bool isThm,
                                    const uint32_t nev=1u);
*/
bool                      IsSeqComplete();
void                      PrepForSeqClose();
void                      PrepForNewSeq(double& etms);
float                     GetSeqLivetime();
void                      procForceTrigger();
void                      procHeartbeat();
void                      procPowerCheck();
void                      procTempCheck();
void                      procCommWin();
#ifdef USE_RTOS
void                      procForceTrigger(void const *) { return procForceTrigger(); }
void                      procHeartbeat(void const *) { return procHeartbeat(); }
void                      procPowerCheck(void const *) { return procPowerCheck(); }
void                      procCommWin(void const *) { return procCommWin(); }
void                      procTempCheck(void const *) { return procTempCheck(); }
#endif // USE_RTOS

#if CHIPBOARD==ATWD4CH
void                      SetAtwdPlas();
void                      SetAtwdDACs();
#else
void                      SetSstDACs();
#endif

//
// globals
//
// readout objs
// TODO: use RtosTimer instead of Ticker?
#ifdef USE_RTOS
static rtos::RtosTimer*     gForceTicker;
static rtos::RtosTimer*     gHeartbeatTicker;
static rtos::RtosTimer*     gCommWinTicker;
static rtos::RtosTimer*     gPowerCheckTicker;
static rtos::RtosTimer*     gTempCheckTicker;
#else
static Ticker         gForceTicker;
static Ticker         gHeartbeatTicker;
static Ticker         gCommWinTicker;
static Ticker         gPowerCheckTicker;
static Ticker         gTempCheckTicker;
#endif // USE_RTOS
static Timer          gAllTrgTimer;
static Timer          gTrgLiveTimer;       // in case the sequence is "short"
//static Timer          gThmTrgTimer;
static Timer          gAdcToMBtimer;

static Timer                 gSinceClkSet; // this timer can roll over; the offline software accounts for it
static SnClockSetFrame       gClkSet;
static SnSignalStrengthFrame gSigStr;
#ifdef DISABLE_CONFIG_SAFETYNETS
static SnConfigFrame  gConf(false);
static SnConfigFrame  gConfCopy(false);
#else
static SnConfigFrame  gConf;
static SnConfigFrame  gConfCopy;
#endif // DISABLE_CONFIG_SAFETYNETS
static SnEventFrame   gEvent;
//static SnEventFrame   gLastEvent;
static SnPowerFrame   gPower;
static SnTempFrame    gTemperature;
// parameters
static bool           gCardsPowered     = false;
static bool           gFirstEvt         = true;
static volatile bool  gReadingOut       = false; // if data is being read from the FPGA
static volatile bool  gCommWinOpen      = false; // if it's open
static volatile bool  gOpenCommWin      = false; // if it should be opened
static volatile bool  gCheckPower       = false; // if it should be checked
static volatile bool  gCheckTemp        = false; // if it should be checked
static uint32_t       gPowNum           = 0;
static uint32_t       gEvtNum           = 0;   // num of evt written
static volatile bool  gForcedTrig       = false; // forced trigger bit
static volatile bool  gAdcToMBflag      = false; // flag in case getting the ADC values took too long
//static uint32_t       gTrgNum[kNumTrgs] = {0}; // num of this type of trg received
static uint32_t       gNumThmTrigs      = 0; // number of thermal triggers counted
static uint32_t       gNumFrcTrigs      = 0; // number of forced triggers counted
static uint32_t       gNumSavedEvts     = 0; // number of events saved. differs from gEvtNum as this one always starts at 0, no matter the current sequence number
static uint8_t        gL1ScaledownCount = 0;   // write an event every X L1 failures
// i/o
static time_t         gLastCommWin      = 0; // time
static uint32_t       gCommWinChecks    = 0;
static uint32_t       gNcommWinChecks   = 0;
static uint16_t       gConsecCommFails  = 0;
// heartbeat
static SnHeartbeatFrame gHrtbt;
static time_t         gLastHrtbt        = 0;
static volatile bool  gHrtbtFired       = false;
static uint32_t       gHrtbtNum         = 0;
// rates
//static double         gThmDtSum         = 0; // sum of all time diffs between thermal trigs
//static double         gEvtDtSum         = 0; // sum of all time diffs between events
//static uint32_t       gThmNumDt         = 0; // number of thermal trig time diffs added up
//static uint32_t       gEvtNumDt         = 0; // number of event time diffs added up
// this should be bigger than anything that will actually be used
static const uint32_t gBufSize=SnStatusFrame::kMaxSizeOf + (2u*SnHeaderFrame::kMaxSizeOf) + SnPowerFrame::kMaxSizeOf
                                + SnEventFrame::kMaxSizeOf // this is too big, because max status frame already includes an ATWD 4*128samp event (as of status i/o v9)
                                //- SnEventFrame::kMaxSizeOfV1 // so we should be able to do this.. but requires long term testing as of 2016-04-26
                                + 256;//Geoffs+ 256; // some breathing room
static char           gGenBuf[gBufSize]; // must be big enough for event or status or config!
static SnCommWin*     gComms[kNcomms]   = { 0 }; // order => priority. afar uses RTOS, and must be made inside main
#if defined(ENABLE_AFAR_TWITTER) && defined(ENABLE_AFAR_COMM)
static SnCommAfarNetIfTwitter* gTwit     = 0;
#endif
static float                   gChanFFT[kNsamps] = { 0 }; // only one chan at a time to save RAM
// status update data cache and flags
static SnClockSetFrame         gStTrgStartClk;
static SnClockSetFrame         gStTrgStopClk;
static SnPowerFrame            gStPower;
static SnTempFrame             gStTemperature;
static volatile bool           gStNewPower(false);
static volatile bool           gStNewEvent(false);
static volatile bool           gStNewHeartbeat(false);
static volatile bool           gStNewTemperature(false);

#ifdef EVT_TIME_PROFILE
    Timer gProfiler;
#endif

void procForceTrigger() {
    if (gReadingOut==false && gCommWinOpen==false) {
        led3=!led3;
#ifdef DEBUG
        printf("proc force\r\n");
#if CHIPBOARD==ATWD4CH
        printf("PIN_forceTrigge=%d, PIN_turn_on_system=%d, "
            "PIN_a_sf_clk=%d\r\n",
            PIN_forceTrigger.read(), PIN_turn_on_system.read(),
            PIN_a_sf_clk.read());
#else
        printf("PIN_forceTrigge=%d, PIN_turn_on_system=%d, "
            "PIN_dataReady=%d\r\n",
            PIN_forceTrigger.read(), PIN_turn_on_system.read(),
            PIN_dataReady.read());
#endif // ATWD4CH
#endif
        gForcedTrig = true;
        ++gNumFrcTrigs;
//        ++(gTrgNum[kFrcTrg]);
//        gEvent.SetTrgBit(kFrcTrg);
//        gEvent.SetTrgNum(++(gTrgNum[kFrcTrg]));
        //PIN_forceTrigger = 0;
        PIN_forceTrigger = 1;     // force a trigger
        PIN_forceTrigger = 0;
    }
}

void procHeartbeat() {
    if (gReadingOut==false && gCommWinOpen==false) {
#ifdef DEBUG
        printf("proc heartbeat\r\n");
#endif
        led3=!led3;
        //PIN_heartbeat = 0;
        PIN_heartbeat = 1; // heartbeat pulse
        PIN_heartbeat = 0;
        gLastHrtbt    = time(0);
        gHrtbtFired   = true;
        ++gHrtbtNum;
    }
}

void procPowerCheck() {
#ifdef DEBUG
    printf("proc power\r\n");
#endif
    led3=!led3;
    gCheckPower=true;
}

void procTempCheck() {
#ifdef DEBUG
    printf("proc temp check\r\n");
#endif
    led3=!led3;
    gCheckTemp=true;
}

void procCommWin() {
    ++gCommWinChecks;
    //if ( (time(0) - gLastCommWin) > gConf.GetCommWinPeriod() ) {
#ifdef DEBUG
    printf("<><><><><><> gCommWinChecks=%u, gNcommWinChecks=%u\r\n",
        gCommWinChecks, gNcommWinChecks);
#endif
    if ( gCommWinChecks >= gNcommWinChecks ) {
#ifdef DEBUG
        printf("proc comm win.\r\n"); 
#endif
        led3=!led3;
        gOpenCommWin = true;
    }
}

bool AreCardsPowered(const bool checkPin) {
#ifdef DEBUG
    printf("acp call: PIN_turn_on_system=%d, gCardsPowered=%d\r\n",
        PIN_turn_on_system.read(), gCardsPowered);
#endif
    if (checkPin) {
#if CHIPBOARD==ATWD4CH
        gCardsPowered = (PIN_turn_on_system.read()==0);
#else
        gCardsPowered = (PIN_turn_on_system.read()==1);
#endif // ATWD4CH
    }
#ifdef DEBUG
    printf("acp return: PIN_turn_on_system=%d, gCardsPowered=%d\r\n",
        PIN_turn_on_system.read(), gCardsPowered);
#endif
    return gCardsPowered;
}

void GetAvePowerReading() {
    // use one measurement as the assumed average
    // in order to reduce computational errors
    int32_t v1, v2;
    const uint16_t aaveV1 = PIN_vADC1.read_u16();
    const uint16_t aaveV2 = PIN_vADC2.read_u16();
    float n=0, ave1=0, ave2=0, rms1=0, rms2=0;
    for (uint16_t i=0; i<kNvoltsAve; ++i) {
        v1    = PIN_vADC1.read_u16() - aaveV1;
        v2    = PIN_vADC2.read_u16() - aaveV2;
        n    += 1;
        ave1 += v1;
        rms1 += v1*v1;
        ave2 += v2;
        rms2 += v2*v2;
    }
    rms1 -= (ave1*ave1)/n;
    rms2 -= (ave2*ave2)/n;
    rms1 /= n-1;
    rms2 /= n-1;
    rms1  = sqrt(rms1);
    rms2  = sqrt(rms2);
    ave1 /= n;
    ave2 /= n;
    ave1 += aaveV1;
    ave2 += aaveV2;
    gPower.Set(ave1, ave2, rms1, rms2, time(0));
#ifdef DEBUG
    printf("ave power. v1=%g, v2=%g, r1=%g, r2=%g, t=%u\r\n",
        gPower.GetAveV1(), gPower.GetAveV2(), 
        gPower.GetRmsV1(), gPower.GetRmsV2(), gPower.GetTime());
#endif
}

#if CHIPBOARD!=ATWD4CH
void InitTempProbe() {
#ifdef DEBUG
    printf("setting temp probe power mode\r\n");
#endif
    // set power style for temperature probe
    PIN_therm.set_use_parasite_power( gConf.IsTempUsingParasitePower() );
    // setup the probe
#ifdef DEBUG
    printf("calling therm probe search_ROM_setup\r\n");
#endif
    PIN_therm.search_ROM_setup();
    const int tsr = PIN_therm.search_ROM(); // this is necessary for some reason
#ifdef DEBUG
    printf("search ROM = %d\r\n", tsr);
#endif
}
#endif

void UpdateTemperature() {
    // ask chip to convert temperature
#if CHIPBOARD!=ATWD4CH
    PIN_therm.convert_temperature(true, DS1820::all_devices);
    gTemperature.SetTempAndTime( PIN_therm.temperature('c'), time(0) );
#ifdef DEBUG
    printf("TTTTTTTT temp = %g at %u\r\n", gTemperature.GetTemperature(),
        gTemperature.GetTime());
#endif
#else
    return; // do nothing
#endif
}

void CheckTemp() {
#ifdef DEBUG
    printf("CheckTemp\r\n");
#endif
    UpdateTemperature();
    // save to disk
    FILE* cf = SnSDUtils::GetCurFile();
    if (cf!=0) {
#if CHIPBOARD==ATWD4CH
        PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
        PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif // ATWD4CH
#ifdef DEBUG
        printf("writing temp. temp = %g at %u\r\n",
            gTemperature.GetTemperature(),
            gTemperature.GetTime());
#endif
        SnSDUtils::WriteTempTo(cf, gTemperature);
    }
   gCheckTemp = false;
}

void CheckPower(const bool isCommWin,
                const bool saveReading,
                const uint8_t devicesInUse) {
#ifdef DEBUG
    printf("CheckPower\r\n");
#endif
    // read power
    GetAvePowerReading();
    if (saveReading) {
        // save to disk
        FILE* cf = SnSDUtils::GetCurFile();
        if (cf!=0) {
#if CHIPBOARD==ATWD4CH
            PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
            PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif // ATWD4CH
#ifdef DEBUG
            printf("writing power. v1=%g, v2=%g, r1=%g, r2=%g, t=%u, pownum=%u\r\n",
                gPower.GetAveV1(), gPower.GetAveV2(), 
                gPower.GetRmsV1(), gPower.GetRmsV2(), gPower.GetTime(),
                gPowNum);
#endif
            SnSDUtils::WritePowerTo(cf, gPower, gPowNum);
        }
    }
    // do we need to change modes?
    bool changed = false;
    if (gConf.IsLowPowerMode()) {
#ifdef DEBUG
        printf("in low pwr. v1=%g, fromLP=%hu\r\n",
            gPower.GetAveV1(), gConf.GetBatVoltFromLowPwr());
#endif
        if (gPower.GetAveV1() > gConf.GetBatVoltFromLowPwr()) {
#ifdef DEBUG
            printf("chaing to normal power!\r\n");
#endif
            gConf.ChangeToNormPower();
            changed = true;
        }
    } else {
#ifdef DEBUG
        printf("in normal pwr. v1=%g, toLP=%hu\r\n",
            gPower.GetAveV1(), gConf.GetBatVoltToLowPwr());
#endif
        if (gPower.GetAveV1() < gConf.GetBatVoltToLowPwr()) {
#ifdef DEBUG
            printf("chaing to low power!\r\n");
#endif
            gConf.ChangeToLowPower();
            changed = true;
        }
    }
    if (changed) {
        SetPower(isCommWin, devicesInUse); // TODO: This will fail if isCommWin==false? (currently not possible, but..)
#ifdef DEBUG
        printf("Using config %s\r\n",gConf.GetLabel());
#endif
        SetConfigAndMakeOutputFile(); // setup defaults in case no communication
    }
    // checking done
    gCheckPower = false;
}

void ResetCounters() {
    const uint32_t evtStartCurSeq = (SnSDUtils::GetCurSeqNum()) // no -1; start with seq=0
                                     * gConf.GetEvtsPerFile();
//    gEvent.ClearEvent();
    gEvtNum = evtStartCurSeq;
    gPowNum = evtStartCurSeq;
//    memset(gTrgNum, 0, sizeof(uint32_t)*kNumTrgs);
    gNumSavedEvts = 0;
    gNumThmTrigs = 0;
    gNumFrcTrigs = 0;
    gForcedTrig = false;
    // reset rate counters
//    gThmDtSum = 0;
//    gThmNumDt = 0;
//    gEvtDtSum = 0;
//    gEvtNumDt = 0;
    // reset heartbeat counters
    gLastHrtbt  = 0;
    gHrtbtFired = false;
    gHrtbtNum   = 0;
    // reset status data caches
    gStNewPower = false;
    gStNewEvent = false;
    gStNewHeartbeat = false;
    gStNewTemperature = false;
    
#ifdef DEBUG
    printf("Reset: gEvtNum=%u, gPowNum=%u, evtStartCS=%u\r\n",
        gEvtNum, gPowNum, evtStartCurSeq);
#endif
}

/*
void CalcRate(const uint32_t numtrgs,
              const double   tottime_ms,
              float&         rate) {
    rate = 0;
    if (numtrgs>1) {
        if (tottime_ms>0.0) {
            rate = static_cast<float>(numtrgs)
                / (tottime_ms/1e3);
        } else {
            // lots of triggers in 0 time
            rate = 1e6;
        }
    }
}

void GetRates(float& thmrate, float& evtrate) {
    thmrate = evtrate = 0;
#ifdef DEBUG
    printf("** Getting rates: gThmNumDt=%d, gThmDtSum=%g, "
           "gEvtNumDt=%d, gEvtDtSum=%g\r\n",
           gThmNumDt, gThmDtSum, gEvtNumDt, gEvtDtSum);
#endif
    
    CalcRate(gThmNumDt, gThmDtSum, thmrate);
    CalcRate(gEvtNumDt, gEvtDtSum, evtrate);
}


void AddToRate(const double dt, const bool isThm,
               const uint32_t nev) {
    // isThm==true => specifically a thermal trigger, not necessarily saved
    // isThm==false => ANY event (thermal or forced) that gets saved
    if (isThm) {
        gThmDtSum += dt;
        gThmNumDt += nev;
    } else {
        gEvtDtSum += dt;
        gEvtNumDt += nev;
    }
#ifdef DEBUG
    printf("** AddToRate: dt=%g, isThm=%d\r\n",dt,(int)isThm);
    printf("** AddToRate: gThmNumDt=%d, gThmDtSum=%g, "
           "gEvtNumDt=%d, gEvtDtSum=%g\r\n",
           gThmNumDt, gThmDtSum, gEvtNumDt, gEvtDtSum);
#endif
}
*/

bool IsSeqComplete() {
#ifdef DEBUG
    printf("IsSeqComplete: eps=%hu, cntpow=%d, pow=%u, evt=%u, seq=%hu\r\n",
        gConf.GetEvtsPerFile(), (int)gConf.IsCountingPowerReadings(),
        gPowNum, gEvtNum, SnSDUtils::GetCurSeqNum());
#endif
    if (gConf.GetEvtsPerFile()>0) {
        const uint32_t evtEndCurSeq = (SnSDUtils::GetCurSeqNum()+1) // account for seq=0
                                        * gConf.GetEvtsPerFile();
#ifdef DEBUG
        printf("evtEndCurSeq=%u\r\n",evtEndCurSeq);
#endif
        if (gConf.IsCountingPowerReadings()) {
            return (gPowNum>=evtEndCurSeq);
        } else {
            // first event num is a one-time per run offset, not one per sequence
            return (gEvtNum>=evtEndCurSeq);
        }
    } else {
        return false;
    }
}

#ifdef USE_RTOS
void stopTicker(rtos::RtosTimer* tik) {
    if (tik!=0) {
        tik->stop();
    }
}
#else
void stopTicker(Ticker& tik) {
    tik.detach();
}
#endif // USE_RTOS

#ifdef USE_RTOS
float resetTicker(rtos::RtosTimer* tik, const float timSec,
                  const float maxTimSec) {
    if (tik!=0) {
        tik->stop();
        if (timSec>0) {
            float tp = timSec > maxTimSec ? maxTimSec : timSec;
            tp *= 1000u; // ms
            tik->start(tp);
            return tp;
        }
     }
     return 0;
}
#else
float resetTicker(Ticker& tik, const float timSec,
                  const float maxTimSec, void (*fptr)(void)) {
    tik.detach();
    if (timSec>0) {
        const float tp = timSec > maxTimSec ? maxTimSec : timSec;
        tik.attach(fptr, tp);
        return tp;
    }
    return 0;
}
#endif // USE_RTOS

void StopAllTickers() {
    stopTicker(gForceTicker);
    stopTicker(gHeartbeatTicker);
    stopTicker(gCommWinTicker);
    stopTicker(gPowerCheckTicker);
    stopTicker(gTempCheckTicker);
}

void ResetAllTickers() {
#ifdef USE_RTOS
    const float ftp = resetTicker(gForceTicker, gConf.GetForceTrigPeriod(),
                                  kAbsMaxTimer);
    Thread::wait(131); // make it less likely for multiple triggers to fire too close together
    const float hbp = resetTicker(gHeartbeatTicker, gConf.GetHeartbeatPeriod(),
                                  kAbsMaxTimer);
    Thread::wait(173); // make it less likely for multiple triggers to fire too close together
    const float cwp = resetTicker(gCommWinTicker, gConf.GetCommWinPeriod(),
                                  kCommWinLongPrdTk);
    Thread::wait(169); // make it less likely for multiple triggers to fire too close together
    const float pcp = resetTicker(gPowerCheckTicker, gConf.GetVoltCheckPeriod(),
                                  kAbsMaxTimer);
    Thread::wait(143); // make it less likely for multiple triggers to fire too close together
    const float ctp = resetTicker(gTempCheckTicker, gConf.GetTempCheckPeriod(),
                                  kAbsMaxTimer);
#else
    const float ftp = resetTicker(gForceTicker, gConf.GetForceTrigPeriod(),
                                  kAbsMaxTimer, &procForceTrigger);
    wait_ms(131); // make it less likely for multiple triggers to fire too close together
    const float hbp = resetTicker(gHeartbeatTicker, gConf.GetHeartbeatPeriod(),
                                  kAbsMaxTimer, &procHeartbeat);
    wait_ms(173); // make it less likely for multiple triggers to fire too close together
    const float cwp = resetTicker(gCommWinTicker, gConf.GetCommWinPeriod(),
                                  kCommWinLongPrdTk, &procCommWin);
    wait_ms(169); // make it less likely for multiple triggers to fire too close together
    const float pcp = resetTicker(gPowerCheckTicker, gConf.GetVoltCheckPeriod(),
                                  kAbsMaxTimer, &procPowerCheck);
    wait_ms(143); // make it less likely for multiple triggers to fire too close together
    const float ctp = resetTicker(gTempCheckTicker, gConf.GetTempCheckPeriod(),
                                  kAbsMaxTimer, &procTempCheck);
#endif // USE_RTOS
#ifdef DEBUG
    printf("attach force trig %g\r\n",ftp);
    printf("attach heart beat %g\r\n",hbp);
    printf("attach comm win   %g\r\n",cwp);
    printf("attach power chk  %g\r\n",pcp);
    printf("attach temp chk   %g\r\n",ctp);
#endif
}
/*
void UponBrownout() {
    // signal brownout by all LEDs off
    led1 = led2 = led3 = led4 = 0;
    // note that debug printing here is pointless,
    // since power over USB will prevent brownout
    
    // close the current file
#if CHIPBOARD==ATWD4CH
    PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
    PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif
    SnSDUtils::WriteTrigWaitWinTime(SnSDUtils::GetCurFile(),
                                    gClkSet,
                                    false);
    SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());

    // change to low power settings
    gConf.ChangeToLowPower();
    // actually power stuff down
    SetPower(gCommWinOpen);
    
    // goodnight
    Sleep();
}
*/
void StopRunning() {
#if defined(DEBUG) || defined(SSNOTIFY)
    printf("stop running\r\n");
#endif
    StopAllTickers();
    OpenCommWin();
    while (true) {
        led3 = 1; led4=1;
#ifdef USE_RTOS
        Thread::wait(500);
#else
        wait(0.5);
#endif // USE_RTOS
        led3 = 0; led4=0;
#ifdef USE_RTOS
        Thread::wait(500);
#else
        wait(0.5);
#endif // USE_RTOS
        // don't kick the watchdog
        // if we do, the station is unrecoverable without physical access
    }
}


int InitSDCard() {
#ifdef DEBUG
    printf("initializing SD card..\r\n");
#endif
    int ret = 1; // always fail if ignoring the SD card (1==fail)
    if (gConf.IsIgnoringSDcard()==false) {
        // initialize the SD card. this should prevent the issue with
        // seq 0 being overwritten upon power up or the SD card first
        // being insterted
        ret = sd.disk_initialize();
        // may need to try a bunch of times to get it to init
        for (int i=0; i<25 && (ret!=0); ++i) {
            ret = sd.disk_initialize();
    #ifdef DEBUG
            printf("called disk_initialize (ret=%d)\r\n",ret);
    #endif
        }
    }
#ifdef DEBUG
    printf("init SD card %d..\r\n", ret);
#endif
    return ret;
}

int main() {
    // a failsafe
    //Watchdog::kick(WDFAILSAFE);
#ifdef DEBUG
    printf("Restart watchdog with time [%u] at [%u]\r\n",
        gConf.GetWatchdogPeriod(), time(0));
    printf("Free memory = %d\r\n", FreeMem());
    printf("CHIPBOARD=%d\r\n", int(CHIPBOARD));
#endif
    Watchdog::kick(gConf.GetWatchdogPeriod());
    
//#if defined(DEBUG) || defined(ENABLE_USB_COMM) || defined(EVT_TIME_PROFILE)
    gCpu.baud(CPUBAUD_SN);
//#endif // DEBUG or ENABLE_USB_COMM

    {
#if defined(SSNOTIFY) || defined(DEBUG)
        printf("\n\n\n\n\nmain: start\r\n");
#endif
        led1=led2=led3=led4=0;
#ifdef USE_RTOS
        led1=1; Thread::wait(200);
        led1=0; led2=1; Thread::wait(200);
        led2=0; led3=1; Thread::wait(200);
        led3=0; led4=1; Thread::wait(200);
#else
        led1=1; wait(0.2);
        led1=0; led2=1; wait(0.2);
        led2=0; led3=1; wait(0.2);
        led3=0; led4=1; wait(0.2);
#endif // USE_RTOS
        led4=0;
    }
    
    // signal startup before first comm win
    led2 = led1 = 1;

    // don't initialize yet, but give SnSDUtils a pointer to
    // the function that initializes it, so it can call the fcn later
    SnSDUtils::fgDoInit = &InitSDCard;
    
    // check in case we need to go to low power
    //wait(4); // TODO: the vADCs read high for first ~4-5sec
    CheckPower(false, false);
#ifdef DEBUG
    printf("startup power: cards %d, amps %d, irid %d, afar %d\r\n",
        PIN_turn_on_system.read(), PIN_turn_on_amps.read(),
        PIN_iridSbd_power.read(), PIN_afar_power.read());
#endif

    /*
    // Note: the brownout isn't useful since it sees either 5V or nothing on our board
    // set up the brownout interrupt
    NVIC_SetVector(BOD_IRQn, (uint32_t)&UponBrownout);
    // Enable Brown Out Detect Interrupt
    NVIC_EnableIRQ(BOD_IRQn);
    */
    
#ifdef DEBUG
    printf("making comm objects\r\n");
#endif

    uint8_t comi(0);
#ifdef ENABLE_USB_COMM
#ifdef DEBUG
    printf("makin SnCommWinUsb\r\n");
#endif
    gComms[comi++] = new SnCommWinUsb(&gCpu);
#endif // ENABLE_USB_COMM
#ifdef ENABLE_AFAR_COMM
    // RTOS stuff must be made inside main for some reason    
#ifdef USE_ETH_INTERFACE
#ifdef DEBUG
    printf("making SnCommAfarTCP\r\n");
#endif
    gComms[comi++] = new SnCommAfarTCP(gConf);
#else
#ifdef DEBUG
    printf("making SnCommWinAfar\r\n");
#endif
    //gComms[comi++] = new SnCommAfarNetIf(gConf);
    gComms[comi++] = new SnCommWinAfar(gConf);
#ifdef ENABLE_AFAR_TWITTER
#ifdef DEBUG
    printf("making SnCommAfarNetIfTwitter\r\n");
#endif
    gTwit = new SnCommAfarNetIfTwitter(gConf);
#endif // ENABLE_AFAR_TWITTER
#endif // USE_ETH_INTERFACE
#endif // ENABLE_AFAR_COMM
#ifdef ENABLE_SBD_COMM
#ifdef DEBUG
    printf("making SnCommWinSBD\r\n");
#endif
    gComms[comi++] = new SnCommWinSBD(&gSBDport);
#endif // ENABLE_SBD_COMM

#ifdef DEBUG
    printf("made comm objects\r\n");
#ifdef USE_MODSERIAL
    printf("using MODSERIAL\r\n");
#endif // USE_MODSERIAL
#endif
    
    if (comi!=kNcomms) {
        error("comi=[%hhu] != kNcomms=[%hhu]\r\n",
            comi, kNcomms);
        // will die here with blue lights of death
    }
    
#ifdef USE_RTOS
    gForceTicker        = new rtos::RtosTimer(&procForceTrigger);
    gHeartbeatTicker    = new rtos::RtosTimer(&procHeartbeat);
    gCommWinTicker      = new rtos::RtosTimer(&procCommWin);
    gPowerCheckTicker   = new rtos::RtosTimer(&procPowerCheck);
    gTempCheckTicker    = new rtos::RtosTimer(&procTempCheck);
#endif     // USE_RTOS
                
    // set the clock to the BS time, if it's not set
    if ( (static_cast<int32_t>(time(0)))<0 ) {
        set_time(kBStime);
    }
#ifdef DEBUG
    printf("time = %d\r\n",(int32_t)time(0));
#endif
    gLastCommWin = time(0); // prevent comm win proc
    
#ifdef USE_RTOS
    gForceTicker->stop();
#else
    gForceTicker.detach();
#endif // USE_RTOS
    gFirstEvt = true;


#ifdef DEBUG
    printf("MAC=%012llX\r\n", (gConf.GetMacAddress())>>16); // 64 -> 48 bits
    printf("my ip   = %s\r\n", gConf.GetMbedIP());
    printf("my mask = %s\r\n", gConf.GetMbedMask());
    printf("my gate = %s\r\n", gConf.GetMbedGate());
    printf("remote server = %s : %hu\r\n",
        gConf.GetRemoteServer(), gConf.GetRemotePort());
    for (uint8_t i=0; i<SnConfigFrame::kNumDatStreams; ++i) {
        printf("data stream %hhu (%s): connectTO=%hhu, listenTO=%hhu\r\n",
            i, SnConfigFrame::GetDataStreamNameOfIdx(i),
            gConf.GetCommWinConnectTOofIdx(i),
            gConf.GetCommWinListenTOofIdx(i));
    }
#endif

    // (probably) power down comms and power up cards,amps
    SetPower(false, SnConfigFrame::kIrid | SnConfigFrame::kAfar);
    // check power again to see if voltages drooped
    CheckPower(false, false);

    //
    // get config
    //
#ifdef DEBUG
    printf("run mode\r\n");
    printf("IsCountingPowerReadings=%d\r\n",(int)gConf.IsCountingPowerReadings());
    printf("IsSingleSeqRunMode=%d\r\n",(int)gConf.IsSingleSeqRunMode());
    printf("IsDualThresholdMode=%d\r\n",(int)gConf.IsDualThresholdMode());
    printf("IsDifferentialTrigMode=%d\r\n",(int)gConf.IsDifferentialTrigMode());
    printf("IsSBDonlyLowPwrMode=%d\r\n",(int)gConf.IsSBDonlyLowPwrMode());
    printf("IsRunSeqListOneCommWinOnly=%d\r\n",(int)gConf.IsRunSeqListOneCommWinOnly());
    printf("IsIgnoringSDcard=%d\r\n",(int)gConf.IsIgnoringSDcard());
    printf("IsCommPowerSimple=%d\r\n",(int)gConf.IsCommPowerSimple());
    printf("call OpenCommWin\r\n");
    printf("Free memory = %d\r\n", FreeMem());
#endif
    led2 = led1 = 0; // back to "normal" for comm win
    SnCommWin::ECommWinResult firstRes = OpenCommWin(true); // is the startup config

#if defined(USE_INTERFACE_CHIP) || defined(LOAD_DEFAULT_CONFIG_FROM_SD)
    if (firstRes<=SnCommWin::kAllFails) {
        // initial communication failed -- try to load default config from file
        gConf.SetFromDefaultConfFile();
    }
#endif 
   
    // get ready to trigger
    PIN_spi.format( 16, 1 ); // change to data readout format
    PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz
    
    // read and cache the dCard power pin setting,
    // so we can use the cached value later
    AreCardsPowered(true); // TODO: should this be an if?
    register double etms=0; // time between written events

    // the main event loop. wait for triggers in SendClock    
    while ( true ) {
        // in here, we wait for triggers from the MB-FPGA
        Watchdog::kick(); // don't reset!
        
        led1 = 1; // signal normal running (i.e. waiting)
        
#ifdef DEBUG
        printf("calling wait trig\r\n");
        printf("gFirstEvt=%s\r\n",gFirstEvt?"true":"false");
        printf("readingout=%d\r\n",(int)gReadingOut);
        printf("Free memory = %d\r\n", FreeMem());
#endif
        if (gFirstEvt) {
#ifdef DEBUG
            printf("WriteTrigWaitWinTime (start)\r\n");
#endif
            gClkSet.UpdateClock( gSinceClkSet );
            SnSDUtils::WriteTrigWaitWinTime(SnSDUtils::GetCurFile(),
                                            gClkSet,
                                            true);
//            gThmTrgTimer.reset(); gThmTrgTimer.start();
            gAllTrgTimer.reset(); gAllTrgTimer.start();
            gTrgLiveTimer.reset(); gTrgLiveTimer.start();
            gStTrgStartClk = gClkSet;
#if CHIPBOARD!=ATWD4CH
            // reset in case a trigger arrived before we were ready
            // this is mostly to ensure that the chip gets reset on soft
            // reboot, in case it got stopped prior to the reboot
            if (gConf.IsSkippingTrgStartReset()==false) {
#ifdef DEBUG
                printf("-----------------------------------------\r\n");
                printf("trigger start reset\r\n");
#endif
                PIN_ResetChips = 1;
                PIN_ResetChips = 0;
            }
#ifdef DEBUG
              else {
                printf("-----------------------------------------\r\n");
                printf("SKIP trig start reset\r\n");
            }
#endif // DEBUG
#endif // not atwd4ch
        }

#if CHIPBOARD==ATWD4CH
        PIN_lockRegisters = 0;   // allow data to come from DFPGA
        WaitTrigAndSendClock();  // wait for trigger and move data to MB. this returns immediately if cards are powered off
        PIN_lockRegisters = 1;   // block registers during readout
#else  // ATWD4CH
        PIN_readingData   = 0;  // not reading yet
        WaitTrigAndSendClock(); // wait for trigger. this returns immediately if cards are powered off
        // PIN_readingData will be set high by SnEventFrame::ReadWaveformsSST
#endif // not ATWD4CH
#ifdef EVT_TIME_PROFILE
        gProfiler.start();
        int befSaveEvt=0, aftSaveEvt=0,
            befChkPow=0, aftChkPow=0, befNewSeq=0, aftNewSeq=0, endOfLoop=0;
#endif // EVT_TIME_PROFILE

        if (gReadingOut) {

            led1 = 0; led4 = 1; // signal reading out

//            const double ttms = gThmTrgTimer.read_us() / 1e3; // for rate calculation
            const double atms = gAllTrgTimer.read_us() / 1e3; // for throttle
/*
            if (gForcedTrig==false) {
                // don't reset if not a thermal trigger
                gThmTrgTimer.reset(); gThmTrgTimer.start();
            }
*/
            gAllTrgTimer.reset(); gAllTrgTimer.start();     // restart trigger timer
            etms += atms; // time between events
            
            Watchdog::kick(); // don't reset!
                        
            //
            // got trigger. read registers to mbed and build the event
            //
            
            // TODO: no way to check for external trigger?
            if (gForcedTrig==false) {
//                ++(gTrgNum[kThmTrg]);
                ++gNumThmTrigs;
//                AddToRate(ttms, true);
            }
            
            if ( gForcedTrig || gFirstEvt ||
                (etms>=gConf.GetEvtThrtlPeriodMs()) ) {
                
#ifdef EVT_TIME_PROFILE
    gProfiler.stop(); befSaveEvt=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE
    
                // want to keep this event. save it (and check if it passes L1)
                const bool saved = SaveEvent(etms);
                if (saved) {
//                    AddToRate(etms, false);
                    ++gNumSavedEvts;
                    etms=0;
                }
 
#ifdef EVT_TIME_PROFILE
    gProfiler.stop(); aftSaveEvt=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE
                
            } else {
#if CHIPBOARD!=ATWD4CH
                // got a trigger, but don't want the event. reset the chip to continue triggering
#ifdef DEBUG
                printf(">>>>>>> THROW EVENT AWAY! forced=%s, first=%s, "
                    "etms=%g, throttle=%hu, etms>=thr=%s\r\n",
                    (gForcedTrig ? "true" : "false"),
                    (gFirstEvt ? "true" : "false"),
                    etms, gConf.GetEvtThrtlPeriodMs(),
                    (etms>=gConf.GetEvtThrtlPeriodMs() ? "true" : "false"));
#endif // DEBUG
                PIN_ResetChips = 1;
                PIN_ResetChips = 0;
#endif
            }

            // done with this event.
            // reset flags from the "old" event
            gForcedTrig = false;
            gAdcToMBflag = false;

            led1 = 1; led4 = 0; // end signal reading out
            
        }
#ifdef DEBUG
        printf("past reading out\r\n");
#endif
        
        if (gHrtbtFired) {
            led1 = 1; led4 = 1; // signal saving transient
            gHrtbt.SetTime( gLastHrtbt );
            // -1 so we start counting at 0
            // counting inside proc so we have a record of the number of proc's
            // even if we are saving/triggering slower than they're firing
            gHrtbt.SetNum( (gHrtbtNum>0) ? (gHrtbtNum - 1) : 0 ); 
            SaveHeartbeat();
            gHrtbtFired=false;
            gStNewHeartbeat = true;
            led1 = 1; led4 = 0; // end signal saving transient
        }
                
#ifdef EVT_TIME_PROFILE
        gProfiler.stop(); befChkPow=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE
        // check the power?
        if (gCheckPower) {
#ifdef DEBUG
            printf("call check power\r\n");
#endif
            led1 = 1; led4 = 1; // signal saving transient
            CheckPower(false);
            gStPower = gPower;
            gStNewPower = true;
            led1 = 1; led4 = 0; // end signal saving transient
        }
#ifdef EVT_TIME_PROFILE
        gProfiler.stop(); aftChkPow=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE
        
        if (gCheckTemp) {
#ifdef DEBUG
            printf("call check temp\r\n");
#endif
            led1 = 1; led4 = 1; // signal saving transient
            CheckTemp();
            gStTemperature = gTemperature;
            gStNewTemperature = true;
            led1 = 1; led4 = 0; // end signal saving transient
        }
        
        // open comm win?
        if (gOpenCommWin) {
#ifdef DEBUG
            printf("gOpenComWin=%s, opening\r\n",gOpenCommWin?"true":"false");
#endif
            PrepForSeqClose();
            led1 = 0; // signal not waiting
            OpenCommWin();
            led1 = 1; // end signal not waiting
            gOpenCommWin=false;
            PrepForNewSeq(etms);

        } else {
#ifdef DEBUG
            printf("gOpenCommWin=false, gCommWinChecks=%u, gNcommWinChecks=%u\r\n",
                gCommWinChecks, gNcommWinChecks);
#endif
        }
        
#ifdef EVT_TIME_PROFILE
        gProfiler.stop(); befNewSeq=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE
        // make new seq?
        if (IsSeqComplete()) {
#ifdef DEBUG
            printf("seq complete. sngseq=%d\r\n",gConf.IsSingleSeqRunMode());
#endif
            led1 = 1; led2 = 1; led4 = 1; // signal saving file
            PrepForSeqClose();
            MakeOutputFile(gConf.IsSingleSeqRunMode());
            PrepForNewSeq(etms);
            led1 = 1; led2 = 0; led4 = 0; // end signal saving file
        }
#ifdef EVT_TIME_PROFILE
        gProfiler.stop(); aftNewSeq=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE

#ifdef EVT_TIME_PROFILE
        gProfiler.stop(); endOfLoop=gProfiler.read_us(); gProfiler.start();
        printf("befSaveEvt=%d, aftSaveEvt=%d, "
            "befChkPow=%d, aftChkPow=%d, befNewSeq=%d, aftNewSeq=%d, endOfLoop=%d\r\n",
            befSaveEvt, aftSaveEvt,
            befChkPow, aftChkPow, befNewSeq, aftNewSeq, endOfLoop);
#endif // EVT_TIME_PROFILE
        
        /*
        // get ready to trigger
        PIN_spi.format( 16, 1 ); // change to data readout format
        PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz
        */
        
        // reset event
        // clear after comm win, so full event can be sent with status
        // but don't clear counters or trigger bits, as
//        gEvent.ClearEvent(true);
    
    } // end while (true)

}

float GetSeqLivetime() {
    const float dt = gStTrgStopClk.GetCurTime() - gStTrgStartClk.GetCurTime();
    if ( dt < kAbsMaxTimer ) {
        // the timer should be valid (not rolled over)
        return gTrgLiveTimer.read();
    } else {
        // lose the sub-second resolution, but who cares
        return dt;
    }
}

void PrepForSeqClose() {
#ifdef DEBUG
    printf("WriteTrigWaitWinTime (stop)\r\n");
#endif
    // add on time since last trigger/event so rates are better calculated
    // do not increment trigger/event counters, however
//    AddToRate( gThmTrgTimer.read_us() / 1e3,  true, 0 );
//    AddToRate( gAllTrgTimer.read_us() / 1e3, false, 0 );
    gTrgLiveTimer.stop();
    // write the trigger stop time
    gClkSet.UpdateClock( gSinceClkSet );
    SnSDUtils::WriteTrigWaitWinTime(SnSDUtils::GetCurFile(),
                                    gClkSet,
                                    false);
    // save copy of trig stop time so it can be sent in status data pack
    gStTrgStopClk = gClkSet;
}

void PrepForNewSeq(double& etms) {
    // reset timers, first event flag and the time since last saved event
    gFirstEvt = true;
    gAllTrgTimer.reset();
//    gThmTrgTimer.reset();
    gTrgLiveTimer.reset();
    etms=0;
}



//
// save a heartbeat tag
//
void SaveHeartbeat() {
#ifdef DEBUG
    printf("save heartbeat #%u, time %u\r\n",
        gHrtbt.GetNum(), gHrtbt.GetTime());
#endif
    // save to SD
#if CHIPBOARD==ATWD4CH
    PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
    PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif
    SnSDUtils::WriteHeartbeatTo(SnSDUtils::GetCurFile(), gHrtbt);
}

//
// save the event
//
bool SaveEvent(const int32_t etms) {
    // write the event
    bool didSave = false;
    
#ifdef DEBUG
    // check that the event is still the same as gLastEvent
    /*
    bool esame = gEvent.GetMbedTime() == gLastEvent.GetMbedTime();
    esame &= gEvent.GetEvtNum() == gLastEvent.GetEvtNum();
    esame &= gEvent.GetDTms() == gLastEvent.GetDTms();
    esame &= gEvent.GetTrgNum() == gLastEvent.GetTrgNum();
    esame &= gEvent.GetTrgBits() == gLastEvent.GetTrgBits();
    esame &= gEvent.GetCRC() == gLastEvent.GetCRC();
    for (uint16_t i=0; i<kTotSamps; ++i) {
        esame &= (gEvent.GetData())[i] == (gLastEvent.GetData())[i];
    }
#if CHIPBOARD!=ATWD4CH
    for (uint16_t i=0; i<kNstopBytes; ++i) {
        esame &= (gEvent.GetStop())[i] == (gLastEvent.GetStop())[i];
    }
    printf("EEEEEEEE event==lastEvt = %s\r\n", (esame?"true":"false"));
#endif
    */
#endif
    
    // ok, now we can overwrite the old gEvent data
    // reset event
    gEvent.ClearEvent(true, true);
    
    // read data & calc CRC
#ifdef EVT_TIME_PROFILE
    int befReadWv=0, aftReadWv=0, befWriteEvt=0, aftWriteEvt=0;
    int befL1=0, aftL1=0;
    gProfiler.stop(); befReadWv=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE

#if CHIPBOARD==ATWD4CH
    // get the data to the MBED
    gEvent.ReadWaveformsATWD(PIN_spi, PIN_selCardHiBit, PIN_selCardLoBit);
#else
    gEvent.ReadWaveformsSST(PIN_spi, PIN_readingData);
    // reset the digitizer so it will continue waiting for a trigger
    PIN_ResetChips = 1;
//  wait_us(1);
    PIN_ResetChips = 0;
#endif //ATWD4CH

#ifdef EVT_TIME_PROFILE
    gProfiler.stop(); aftReadWv=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE

    gEvent.SetCurMbedTime();
                
    Watchdog::kick(); // don't reset!
        
#ifdef DEBUG
    printf("gFirstEvt=%s\r\n",gFirstEvt?"true":"false");
#endif
                
#if CHIPBOARD==ATWD4CH
    PIN_lockRegisters = 0; // done reading, unlock so we can talk to the SD card
#else
    // (this is redundant; it's already set low by SnEventFrame)
    PIN_readingData   = 0; // done reading, unlock so we can talk to the SD card
#endif // ATWD4CH
                
#ifdef DEBUG
    printf("waveform read out. save event (%u)\r\n", gEvtNum);
#endif

    // set the event number & dt
    gEvent.SetEvtNum(gEvtNum);
    gEvent.SetDTms(etms);
    // set trigger bits
    if (gForcedTrig) {
        gEvent.SetTrgBit(kForced);
//        gEvent.SetTrgNum(gTrgNum[kFrcTrg]);
        gEvent.SetTrgNum(gNumFrcTrigs);
    } else {
        gEvent.SetTrgBit(kThermal);
//        gEvent.SetTrgNum(gTrgNum[kThmTrg]);
        gEvent.SetTrgNum(gNumThmTrigs);
    }
    if (gAdcToMBflag) {
        gEvent.SetTrgBit(kAdcToMBflag);
    }
    
    // this is in the config too, but doesn't hurt to flip the bit
    // in the event
    if (gConf.IsL1TrigApplied()) {
        gEvent.SetTrgBit(kL1TrgApplied);
    }

    // ----
    // L1 check(s) go here!
    bool L1okToSave = true;
    
#ifdef DEBUG
    Timer l1timer;
    l1timer.start();
    printf("L1 single freq supp ratio = %hhu (%g)\r\n",
        gConf.GetSingleFreqSuppRatioRaw(),
        gConf.GetSingleFreqSuppRatio());
#endif

#ifdef EVT_TIME_PROFILE
    gProfiler.stop(); befL1=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE

    // TODO: move the FFT calculation out here so that
    // other L1's could use it too. haven't done this already
    // in order to save RAM and not cache the whole thing when
    // we only have one L1 cut.

    // check L1 single frequency suppression cut
    if ( gConf.IsSingleFreqSuppEnabled() ) {
        const EL1TrigStatus passL1SingleFreqSupp = 
            SnL1SingleFreqSupp::ProcessEvent(gEvent, gChanFFT,
                                             gConf.GetSingleFreqSuppRatio());
#ifdef DEBUG
            l1timer.stop();
            printf("L1 single freq supp took [%d] us\r\n",
                l1timer.read_us());
            printf("passL1SingleFreqSupp=%s\r\n",
                passL1SingleFreqSupp==kL1Pass ? "pass"
                    : passL1SingleFreqSupp==kL1Fail ? "fail"
                        : "other");
#endif
        if (passL1SingleFreqSupp==kL1Pass) {
            gEvent.SetTrgBit(kSingleFreqSupp);
        } else if ( (passL1SingleFreqSupp==kL1Fail) &&
                     gConf.IsL1TrigApplied() ) {
            L1okToSave = false;
        }
        // else if kL1UnableToProcess, the bit won't get set
        // but the event can/will be saved
    }
    
    
    // NO MORE L1 checks below here! (or the scaledown won't work)
    // -----
    
#ifdef EVT_TIME_PROFILE
    gProfiler.stop(); aftL1=gProfiler.read_us(); gProfiler.start();
    gProfiler.stop(); befWriteEvt=gProfiler.read_us(); gProfiler.start();
#endif // EVT_TIME_PROFILE

    // save to SD
#if CHIPBOARD==ATWD4CH
    PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
    PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif
        
    // scale down to override L1 trigger failures
    if ( (L1okToSave==false) && (gConf.GetL1Scaledown()!=0) ) {
        ++gL1ScaledownCount;
        
        // check with >= in case the config changes
        if ( gL1ScaledownCount >= gConf.GetL1Scaledown() ) {
            gL1ScaledownCount = 0;
            L1okToSave = true;
            gEvent.SetTrgBit(kL1Scaledown);
#ifdef DEBUG
            printf("))) L1 scaledown!\r\n");
#endif
        }
    }

#ifdef DEBUG
    printf("L1 scaledown count = %hhu, scaledown=%hhu, L1okToSave=%s\r\n",
        gL1ScaledownCount,
        gConf.GetL1Scaledown(),
        L1okToSave ? "true" : "false");
    printf("event trigger bits = ");
    SnBitUtils::printBits(gEvent.GetTrgBits(), true);
#endif
    
    // always save forced triggers
    if (L1okToSave || gForcedTrig) {
        SnSDUtils::WriteEventTo(SnSDUtils::GetCurFile(), gGenBuf, gEvent, gConf);
        
        // a good event
        gStNewEvent = true;
        
        // mark that the event was actually accepted
        didSave = true;
        
        // send it?
        if ( gConf.IsCommWindowEachEvent() ) {
#ifdef DEBUG
            printf("COMM EACH EVENT => gOpenCommWin = true\r\n");
#endif
            gOpenCommWin = true;
        }
    }
    
#ifdef EVT_TIME_PROFILE
    gProfiler.stop(); aftWriteEvt=gProfiler.read_us(); gProfiler.start();
    printf("befReadWv=%d, aftReadWv=%d, befWriteEvt=%d, aftWriteEvt=%d\r\n",
        befReadWv, aftReadWv, befWriteEvt, aftWriteEvt);
    printf("befL1=%d, aftL1=%d\r\n", befL1, aftL1);
#endif // EVT_TIME_PROFILE

    // make a copy in case we need to send it with the status
    // (copy it because we are going to clear this event while
    // waiting for the next trigger)
//    gEvent.CopyTo(gLastEvent);
    
    // increment event number
    ++gEvtNum;
    
#ifdef DEBUG
    printf("next gEvtNum=%u\r\n",gEvtNum);
#endif
    
    return didSave;
}

void MakeOutputFile(const bool stopRunning) {
#if CHIPBOARD==ATWD4CH
    PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
    PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif
#ifdef DEBUG
    printf("closing output file. gEvtNum=%u, gPowNum=%u, stop=%d\r\n",
        gEvtNum,gPowNum,(int)stopRunning);
#endif
    
    SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());
    
#ifdef DEBUG
    printf("file closed\r\n");
#endif
    if (stopRunning) {
        StopRunning();
    }
    FILE* cf = SnSDUtils::OpenNewOutputFile(SnConfigFrame::GetMacAddress(),
                                            gConf.GetRun(),
                                            gConf.GetFirstSeq(),
                                            gConf.IsSendingFilesRunSeqList());
    // reset event, timers, trigger counters
    ResetCounters();
    if (cf!=0) {
#ifdef USE_RTOS
        Thread::wait(200);
#else
        wait_ms(200);
#endif
        GetAvePowerReading();
#ifdef DEBUG
        printf("writing power. v1=%g, v2=%g, r1=%g, r2=%g, t=%u, pownum=%u\r\n",
            gPower.GetAveV1(), gPower.GetAveV2(), 
            gPower.GetRmsV1(), gPower.GetRmsV2(), gPower.GetTime(),
            gPowNum);
#endif
        SnSDUtils::WritePowerTo(cf, gPower, gPowNum);
    }
#ifdef DEBUG
    printf("made output file with run %u\r\n",gConf.GetRun());
    printf("filename=%s\r\n",SnSDUtils::GetCurFileName());
#endif
    SnSDUtils::WriteConfig(SnSDUtils::GetCurFile(), gConf);
#ifdef DEBUG
    printf("write config to file\r\n");
#endif
}

bool PowerDownCommPeriph(const SnConfigFrame::EDatPackBit type) {
#ifdef DEBUG
    printf("PowerDownCommPeriph: type=%d\r\n",(int)type);
#endif    
    SnCommWin** cw = gComms;
    for (uint8_t i=0; i<kNcomms; ++i, ++cw) {
        if ((*cw)==0) {
            continue;
        } else if ((*cw)->GetCommType()==type) {
#ifdef DEBUG
            printf("calling PowerDown\r\n");
#endif
            return (*cw)->PowerDown(gConf.GetTimeoutTime(time(0),
                                    gConf.GetCommWinConnectTO(type)));
        }
    }
    return false;
}

//
// power stuff
//
bool IsCurrentlyPowered(const SnConfigFrame::EPowerModeBit p,
                        DigitalOut& pin) {
    // check if the current pin value is equal to
    // what it should be if the peripheral is on
    return gConf.GetPowPinSetting(p, true) == pin.read();
}

void ChangeCardPower(const bool isCommWin,
                     const bool isOn) {
    // change cards power
    const SnConfigFrame::EPowerModeBit pbit = (isCommWin)
        ? SnConfigFrame::kCardComWin
        : SnConfigFrame::kCardDatTak;
    const int value = gConf.GetPowPinSetting(pbit, isOn);
#ifdef DEBUG
    printf("setting cards pin power to %d (comm=%d)\r\n",
        value, static_cast<int>(isCommWin));
#endif
    PIN_turn_on_system = value;
#ifdef USE_RTOS
    Thread::wait(10);
#else
    wait_ms(10);
#endif
}

void ChangeAmpsPower(const bool isCommWin,
                     const bool isOn) {
    // change amps power
    const SnConfigFrame::EPowerModeBit pbit = (isCommWin)
        ? SnConfigFrame::kAmpsComWin
        : SnConfigFrame::kAmpsDatTak;
    const int value = gConf.GetPowPinSetting(pbit, isOn);
#ifdef DEBUG
    printf("setting amps pin power to %d (comm=%d)\r\n",
        value, static_cast<int>(isCommWin));
#endif
    PIN_turn_on_amps = value;
#ifdef USE_RTOS
    Thread::wait(10);
#else
    wait_ms(10);
#endif
}

void ChangeIridPower(const bool isCommWin,
                     const bool isOn) {
    // change iridium power setting
    // if going from ON to OFF, call the special
    // power down procedure, so as not to lose
    // the non-volatile memory settings

    const SnConfigFrame::EPowerModeBit ibit = (isCommWin)
        ? SnConfigFrame::kIridComWin
        : SnConfigFrame::kIridDatTak;
    const SnConfigFrame::EPowerModeBit abit = (isCommWin)
        ? SnConfigFrame::kAfarComWin
        : SnConfigFrame::kAfarDatTak;
    
    // these checks are complicated because the iridium might
    // be powered off the Afar (12V) relay, 
    // rather than the iridium (5V) relay
    const bool iridFromOn = (kIridPwrFromAfar) ?
        IsCurrentlyPowered(abit, PIN_afar_power) :
        IsCurrentlyPowered(ibit, PIN_iridSbd_power);
    if ( iridFromOn && (isOn==false) ) {
#ifdef DEBUG
        printf("calling PowerDown for Iridium\r\n");
#endif
        PowerDownCommPeriph(SnConfigFrame::kIrid);
    }
    const int value = (kIridPwrFromAfar)
        ? gConf.GetPowPinSetting(ibit, false) // leave the iridium relay off. use afar relay.
        : gConf.GetPowPinSetting(ibit, isOn);
#ifdef DEBUG
    printf("setting iridium pin power to %d (comm=%d)\r\n",
        value, static_cast<int>(isCommWin));
#endif
    PIN_iridSbd_power = value;
#ifdef USE_RTOS
    Thread::wait(10);
#else
    wait_ms(10);
#endif
    
}

void ChangeAfarPower(const bool isCommWin,
                     const bool isOn) {
    // change the AFAR relay power setting
    // also power up or down the ethernet PHY port
    // as appropriate
    // the procedure is complicated by the fact that
    // the iridium may be powered off the Afar (12V) relay,
    // in which case we need to turn this relay on if
    // *either* Afar or iridium want power
#ifdef DEBUG
    printf("bef: pconp=%u (%08x), pcenet=%u (%08x)\r\n",
        LPC_SC->PCONP, LPC_SC->PCONP, LPC1768_PCONP_PCENET, LPC1768_PCONP_PCENET);
    printf("pcenet bef power: status=%d\r\n",Peripheral_GetStatus(LPC1768_PCONP_PCENET));
#endif
    
    const SnConfigFrame::EPowerModeBit abit = (isCommWin)
        ? SnConfigFrame::kAfarComWin
        : SnConfigFrame::kAfarDatTak;

    const bool afarFromOn = IsCurrentlyPowered(abit, PIN_afar_power);

    // change ethernet PHY port power
#ifdef DEBUG
    printf("afar pin=%d, afarFromOn=%d, afarToOn=%d\r\n",
        PIN_afar_power.read(), static_cast<int>(afarFromOn),
        static_cast<int>(isOn));
#endif
    if (isOn) {
#ifdef DEBUG
        printf("PHY cowin powering up\r\n");
#endif
        PHY_PowerUp();
#ifdef USE_RTOS
        Thread::wait(1000);
#else
        wait(1);
#endif
#ifdef DEBUG
        printf("PHY cowin powered up\r\n");
#endif
    } else {
        // change afar power
        // power down periph if going from on to off
        // change afar power
        if (afarFromOn) {
            PowerDownCommPeriph(SnConfigFrame::kAfar);
        }
#ifdef DEBUG
        printf("PHY cowin powering down\r\n");
#endif
        PHY_PowerDown();
#ifdef USE_RTOS
        Thread::wait(1000);
#else
        wait(1);
#endif
#ifdef DEBUG
        printf("PHY cowin powered down\r\n");
#endif
    }
#ifdef DEBUG
    printf("PHY done\r\n");
#endif
#ifdef USE_RTOS
    Thread::wait(100);
#else
    wait_ms(100);
#endif

    // change afar power
    const int value = gConf.GetPowPinSetting(abit, isOn);
    PIN_afar_power = value;
#ifdef USE_RTOS
    Thread::wait(1500);
#else
    wait(1.5);
#endif
#ifdef DEBUG
    printf("aft: pconp=%u (%08x), pcenet=%u (%08x)\r\n",
        LPC_SC->PCONP, LPC_SC->PCONP, LPC1768_PCONP_PCENET, LPC1768_PCONP_PCENET);
    printf("pcenet aft power: status=%d\r\n",Peripheral_GetStatus(LPC1768_PCONP_PCENET));
#endif
    
}

bool IsPowerAllowedFor(const bool isCommWin,
                       const uint8_t devicesInUse,
                       const SnConfigFrame::EDatPackBit b) {
    if (isCommWin && (gConf.IsCommPowerSimple()==false)) {
        return ( (devicesInUse & b)!=0 );
    }
    return true;
}

void SetPower(const bool isCommWin,
              const uint8_t devicesInUse) {
    // devicesInUse could really just be a second set of "power mode" bits
    // where the bit being on means "I want to change power for this device"
    // but then.. how to handle the possibility that the bit word contains,
    // for example, both bits AfarDatTak and AfarComWin being on?
    // so instead, we keep the "isCommWin" boolean which forces the separation
    // and deviceInUse is only applied when isCommWin==true
    //
    // multiple devices can be specified by, e.g., kIrid | kAfar
#ifdef DEBUG
    printf("set power. isCommWin=%s\r\n",(isCommWin)?"true":"false");
    printf("WD reset = %d\r\n",(int)Watchdog::didWatchdogReset());
    printf("power word (%hhu): ",gConf.GetPowerMode()); SnBitUtils::printBits(gConf.GetPowerMode(),true);
    printf("IsPoweredFor CardDatTak = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kCardDatTak));
    printf("IsPoweredFor AmpsDatTak = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kAmpsDatTak));
    printf("IsPoweredFor IridDatTak = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kIridDatTak));
    printf("IsPoweredFor AfarDatTak = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kAfarDatTak));
    printf("IsPoweredFor CardComWin = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kCardComWin));
    printf("IsPoweredFor AmpsComWin = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kAmpsComWin));
    printf("IsPoweredFor IridComWin = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kIridComWin));
    printf("IsPoweredFor AfarComWin = %d\r\n",(int)gConf.IsPoweredFor(SnConfigFrame::kAfarComWin));
    printf("devicesInUse = %hhu\r\n", (uint8_t)devicesInUse);
    printf("IsCommPowerSimple = %s\r\n", (gConf.IsCommPowerSimple() ? "true" : "false"));
#endif
    
    const SnConfigFrame::EPowerModeBit cardpb = (isCommWin)
        ? SnConfigFrame::kCardComWin
        : SnConfigFrame::kCardDatTak;
    const SnConfigFrame::EPowerModeBit ampspb = (isCommWin)
        ? SnConfigFrame::kAmpsComWin
        : SnConfigFrame::kAmpsDatTak;
    const SnConfigFrame::EPowerModeBit iridpb = (isCommWin)
        ? SnConfigFrame::kIridComWin
        : SnConfigFrame::kIridDatTak;
    const SnConfigFrame::EPowerModeBit afarpb = (isCommWin)
        ? SnConfigFrame::kAfarComWin
        : SnConfigFrame::kAfarDatTak;
    
    //
    // FIRST turn the things off that should be off.
    // this prevents short periods of high power usage
    //

    const bool cardToOn = gConf.IsPoweredFor(cardpb);
    const bool ampsToOn = gConf.IsPoweredFor(ampspb);

    // these are complicated because iridium might
    // be powered off of the afar line
    // also ignore the devices in use if we're not in a comm window
    const bool iridToOn = (kIridPwrFromAfar) ?
        (gConf.IsPoweredFor(afarpb) && IsPowerAllowedFor(isCommWin, devicesInUse, SnConfigFrame::kIrid)) : 
        (gConf.IsPoweredFor(iridpb) && IsPowerAllowedFor(isCommWin, devicesInUse, SnConfigFrame::kIrid));  
    const bool afarToOn = (kIridPwrFromAfar) ?
        (  (gConf.IsPoweredFor(afarpb) && IsPowerAllowedFor(isCommWin, devicesInUse, SnConfigFrame::kAfar)) 
         ||(gConf.IsPoweredFor(iridpb) && IsPowerAllowedFor(isCommWin, devicesInUse, SnConfigFrame::kIrid)) ) :
        (gConf.IsPoweredFor(afarpb) && IsPowerAllowedFor(isCommWin, devicesInUse, SnConfigFrame::kAfar));

    if (cardToOn==false) {
#ifdef DEBUG
        printf("power down cards\r\n");
#endif
        ChangeCardPower(isCommWin, cardToOn);
    }
    if (ampsToOn==false) {
#ifdef DEBUG
        printf("power down amps\r\n");
        printf("ampspb=%d, is powered for =%d\r\n",
            (int)ampspb,
            (int)(gConf.IsPoweredFor(ampspb)));
        printf("is powered amps dat tak = %d, amps com win = %d\r\n",
            (int)(gConf.IsPoweredFor(SnConfigFrame::kAmpsDatTak)),
            (int)(gConf.IsPoweredFor(SnConfigFrame::kAmpsComWin)));
#endif
        ChangeAmpsPower(isCommWin, ampsToOn);
    }
    if (iridToOn==false) {
#ifdef DEBUG
        printf("power down irid\r\n");
#endif
        ChangeIridPower(isCommWin, iridToOn);
    }
    if (afarToOn==false) {
#ifdef DEBUG
        printf("power down afar\r\n");
#endif
        ChangeAfarPower(isCommWin, afarToOn);
    }
    
    //
    // THEN turn on things that want to go on
    //
    if (cardToOn) {
#ifdef DEBUG
        printf("power ON cards\r\n");
#endif
        ChangeCardPower(isCommWin, cardToOn);
    }
    if (ampsToOn) {
#ifdef DEBUG
        printf("power ON amps\r\n");
#endif
        ChangeAmpsPower(isCommWin, ampsToOn);
    }
    if (iridToOn) {
#ifdef DEBUG
        printf("power ON iridium\r\n");
#endif
        ChangeIridPower(isCommWin, iridToOn);
    }
    if (afarToOn) {
#ifdef DEBUG
        printf("power ON afar\r\n");
#endif
        ChangeAfarPower(isCommWin, afarToOn);
    }
    
#ifdef DEBUG
    printf("power word (%hhu): ",gConf.GetPowerMode()); SnBitUtils::printBits(gConf.GetPowerMode(),true);
    printf("set power (iscom %d, pw %hhu): cards %d, amps %d, irid %d, afar %d\r\n",
        isCommWin, gConf.GetPowerMode(), PIN_turn_on_system.read(), PIN_turn_on_amps.read(),
        PIN_iridSbd_power.read(), PIN_afar_power.read());
#endif
}

//
// trigger board setup
//
#if CHIPBOARD==ATWD4CH
void SetAtwdPlas() {
    uint16_t hi, lo;
    PIN_PLA_cs=1;
#ifdef USE_RTOS
    Thread::wait(4000);
#else
    wait(4);
#endif
    for (uint8_t pi=0; pi<kNplas; ++pi) {
        if (pi < gConf.GetNumPlas()) {
            SnConfigFrame::GetHiLoPlas(gConf.GetPla(pi), hi, lo);
            PIN_spi.write(hi);
            PIN_spi.write(lo);
#ifdef DEBUG
            printf("pla hi %hu, lo %hu\r\n",hi,lo);
#endif
        } else {
            PIN_spi.write(kNoTrigPla); // hi
            PIN_spi.write(kNoTrigPla); // lo
#ifdef DEBUG
            printf("pla hi %hu, lo %hu\r\n",kNoTrigPla,kNoTrigPla);
#endif
        }
        Watchdog::kick();
    }
#ifdef USE_RTOS
    Thread::wait(3000);
#else
    wait(3);
#endif
    PIN_PLA_cs=0;
#ifdef USE_RTOS
    Thread::wait(3000);
#else
    wait(3);
#endif
}

void SetAtwdDACs() {
    // DAC values
    //
    // first 12 bits = DAC value
    // next 2 bits = DAC ID
    // last 2 bits = dFPGA ID
    //
    // But FPGA uses "gray encoding" which means only 1 bit
    // can change at a time (of the last 4 bits). So even tho
    // the card/dac# is encoded, the order is also important
    // 0000 (dac0,card0), 0001 (dac0,card1), 0011 (dac0,card3), 0010 (dac0,card2),
    // 0110 (dac1,card2), 0111 (dac1,card3), 0101 (dac1,card1), etc.
#ifdef DEBUG
    printf("setting dacs\r\n");
#endif
    uint16_t dv=0;
    for (uint8_t i=0, gri=0; i<kTotDacs; ++i) {
        // get the gray-codes for this iteration
        gri = SnBitUtils::binToGray(i);
        
        // build bit word
        dv   = static_cast<int>(gConf.GetDac(gri & 0x0003u, gri >> 2u));
        dv <<= 4u;
        dv  |= gri;
        
#ifdef DEBUG
        printf("dac %04x\r\n",dv);
#endif
        
        // send to FPGA
        PIN_start_fpga=1;
        PIN_spi.write(dv);
        PIN_start_fpga=0;

        Watchdog::kick();
        
    }
}
#endif // ATWD4CH

#if CHIPBOARD!=ATWD4CH
void SetSstDACs() {
    // set and/or & differential pins
    // set DACs via I2C
    
    uint16_t dv=0;
    uint8_t dn=0;
    uint8_t cmdAndDac[3];
    uint8_t dadr=0;
    for (uint8_t ch=0; ch<kNchans; ++ch) {
        dadr = kLTC2657Adrs[ ch / kChansPerLTC2657 ];
        for (uint8_t dc=0; dc<kNchanDacs; ++dc) {
            bool dok = false;
            for (uint8_t tries = 0; (tries<kMaxDacSetTries) && (dok==false); ++tries) {
#ifdef DEBUG
                printf("start i2c for dc=%hhu, ch=%hhu, try=%hhu, dok=%s\r\n",
                    dc, ch, tries, (dok ? "true" : "false"));
                printf("address 0x%hhx (%hhd) ", dadr, dadr);
                SnBitUtils::printBits(dadr, true);
#endif
                // build data to send
                // blame the engineers for this bizzare mapping from
                // chan, threshold -> DAC number
#if (CHIPBOARD==SST8CH) || (CHIPBOARD==SST8CH_1GHz)
                dn = (kChansPerLTC2657*kNchanDacs)-(dc*kChansPerLTC2657)-kChansPerLTC2657+(ch % kChansPerLTC2657);
#else
                dn = ((kChansPerLTC2657*kNchanDacs)-1)-(dc*kChansPerLTC2657)-(ch % kChansPerLTC2657);
#endif
                if (dn>7) { // invalid code for LTC2657 dac chip
                   printf("kTotDacs=%hu, dc=%hhu, ch=%hhu, kChansPerLTC2657=%hhu, dn=%hhu\n",
                    kTotDacs, dc, ch, kChansPerLTC2657, dn);
                   error("chan/dac combination too big for 3 bits!");
                }
                dn |= (kUpdateDacCmd<<4); // prefix with update DAC value command
#ifdef DEBUG
                printf("dn=%hhu ", dn);
                SnBitUtils::printBits(dn, true);
#endif

#ifdef DEBUG
                printf("%d bit dacs\n", static_cast<int>(DAC_BITS));
#endif
                dv = (gConf.GetDac(ch, dc));
#if DAC_BITS==12
                dv = dv << 4; // put 0's at the end (12 bits of num then 4 zero bits)
#elif DAC_BITS==16
                // no shift required for 16 bit dacs
#else
#error DAC_BITS has invalid value! Must be either 12 or 16.
#endif // DAC_BITS

#ifdef DEBUG
                printf("dv=%hu\r\n",dv);
                printf("ch=%hhu, dc=%hhu, dac=%hu\r\n",ch,dc,gConf.GetDac(ch,dc));
#endif
               // mbed i2c.write seems to send it "backwards" from a (low endian) bit
                // point of view.. i guess it's forwards from an intuitive pov?
                cmdAndDac[0] = dn;
                cmdAndDac[1] = (dv & 0xFF00u) >> 8; // 8 MSBs of dac first
                cmdAndDac[2] = (dv & 0x00FFu);      // 8 LSBs of dac second
                
#ifdef DEBUG
                printf("cmdAndDac[0]="); SnBitUtils::printBits(cmdAndDac[2],true);
                printf("cmdAndDac[1]="); SnBitUtils::printBits(cmdAndDac[1],true);
                printf("cmdAndDac[2]="); SnBitUtils::printBits(cmdAndDac[0],true);
#endif
                // try to send it
                // TODO: if no ACK, is just re-trying the whole thing good enough?
                dok =  PIN_i2c.write(dadr,
                    reinterpret_cast<char*>(cmdAndDac),
                    3*sizeof(uint8_t))==0;
            } // end try loop
        }
    }
}
#endif

//
// set configuration
//
void SetConfigAndMakeOutputFile() {
#ifdef DEBUG
    printf("SetConfigAndMakeOutputFile\r\n");
#endif
    
    // restart watchdog
#ifdef DEBUG
    printf("Restart watchdog with time [%u]\r\n",
        gConf.GetWatchdogPeriod());
#endif
    Watchdog::kick(gConf.GetWatchdogPeriod());
    
    // block (thermal) triggers during configuration
    PIN_enableThermTrig       = 0;
#if CHIPBOARD==ATWD4CH
    PIN_ADC_CS                = 1;
    PIN_DoNotRestartAllClocks = 1;
#endif
    PIN_forceTrigger          = 0;
    PIN_heartbeat             = 0;
#ifdef USE_RTOS
    Thread::wait(20);
#else
    wait_ms(20);
#endif
    
    gCommWinChecks  = 0;
    gNcommWinChecks = gConf.GetCommWinPeriod() / kCommWinLongPrdTk;
    
    if (AreCardsPowered(true)) {
        // Set PLA value(s)
        PIN_spi.format( 16, 0 ); // change mode for DAC & PLA value setting (with ATWD4CH)
        PIN_spi.frequency(1000000);
        PIN_MajLogHiBit=1;
        PIN_MajLogLoBit=1;
        PIN_enableThermTrig=0;
        
        // set DACs (and PLAs if needed)
#if CHIPBOARD==ATWD4CH
        SetAtwdPlas();
        SetAtwdDACs();
#else // SST
        SetSstDACs();
#endif // CHIPBOARD

#ifdef DEBUG
        printf("dacs set\r\n");
#endif
#ifdef USE_RTOS
        Thread::wait(20);
#else
        wait_ms(20);
#endif
    } else {
#ifdef DEBUG
        printf("cards off. skipping PLA and DAC setting\r\n");
#endif
    }

#if CHIPBOARD!=ATWD4CH
    // set the SST triggering run mode
    PIN_dualOrSingleThresholds = gConf.IsDualThresholdMode();
    PIN_differentialTrigSignal = gConf.IsDifferentialTrigMode();
#endif
    
    // Majority Logic Trigger selection (# of cards)
    SnBitUtils::SetChanNumBits(gConf.GetNumCardsMajLog() - 1u,
                   PIN_MajLogHiBit, PIN_MajLogLoBit);
    
    // Enable thermal trigger?
    PIN_enableThermTrig = gConf.IsThermTrigEnabled();

#if CHIPBOARD!=ATWD4CH
    InitTempProbe();
#endif

    PIN_spi.format( 16, 1 ); // back to trigger mode
    PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz

    // make new output file
    // put after PLA/DAC, in case they affect the power readings
#ifdef USE_RTOS
    Thread::wait(200);
#else
    wait_ms(200);
#endif
    MakeOutputFile();
    
    // reset tickers
    ResetAllTickers();
    
    Watchdog::kick(); // don't reset!
    
#ifdef DEBUG
    printf("set config done\r\n");
#endif
}

//
// readout functions
//
void WaitTrigAndSendClock() {
    
#ifdef DEBUG
    printf("WaitTrigAndSendClock\r\n");
    printf("wait trig: (pw %hhu): cards %d, amps %d, irid %d, afar %d\r\n",
        gConf.GetPowerMode(), PIN_turn_on_system.read(), PIN_turn_on_amps.read(),
        PIN_iridSbd_power.read(), PIN_afar_power.read());
    printf("cards powered=%d\r\n",(int)AreCardsPowered(true));
#endif

#ifdef DEBUG
    printf("gFirstEvt=%s\r\n",gFirstEvt?"true":"false");
#endif
    if (AreCardsPowered(false)) {

        PIN_spi.format( 16, 1 ); // back to trigger mode
        PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz
        
        if (gFirstEvt==false) {
#if CHIPBOARD==ATWD4CH
            PIN_DoNotRestartAllClocks    = 0;
            wait_us(1);
            PIN_DoNotRestartAllClocks    = 1;
#endif
        } else {
            gFirstEvt = false;
        }
            
        //
        // wait for a trigger here.
        //
#ifdef DEBUG
        printf("starting wait for trig\r\n");
#endif

        gReadingOut = false;  // this will allow forced triggers (see procForceTrigger())
#if CHIPBOARD==ATWD4CH
        while ( PIN_a_sf_clk == 1 ) { // wait for trigger
#else
        while ( PIN_dataReady==0 ) {  // wait for data in mb fpga
#endif
            if (gOpenCommWin || gCheckPower || gCheckTemp) {
#ifdef DEBUG
                printf("break com=%d, pow=%d, tmp=%d\r\n",
                    gOpenCommWin,gCheckPower,gCheckTemp);
#endif
                return; // break out to open comms or check power
            }
        }
        gReadingOut = true;   // disallow new forced triggers

        // we can't be interrupted before data arrives at the MB FPGA
        //StopAllTickers();
        
        //wait_us(5);

#if CHIPBOARD==ATWD4CH        
        // ATWD
        //
        // collect data from daughter cards
        //
        // TODO: what if some card (set of channels) doesn't respond?
        // currently, will wait forever?
        // also, if ch1 is dead, will wait forever (due to FPGA code)
        gAdcToMBtimer.start();
        for( uint16_t i = 0; i < kNsamps; ++i ) {
            while (PIN_a_sf_clk==1) {}
            while (PIN_a_sf_clk==0) {}

            PIN_ADC_CS = 0;
            PIN_spi.write( 0x00 );
            PIN_ADC_CS = 1;
        }
        gAdcToMBtimer.stop();
#ifdef DEBUG
        printf("total time = %d us\r\n", gAdcToMBtimer.read_us());
#endif
        if ( kAdcToMBtimeCut < gAdcToMBtimer.read_us() ) {
//            gEvent.SetTrgBit(kAdcToMBflag);
            gAdcToMBflag = true;
        }
        gAdcToMBtimer.reset();
        
#else
        // For SST, data is already in the mb FPGA, so no need to do anything here
#endif

    } else {
        // cards have no power. don't try reading out
        gReadingOut=false;
        // set gFirstEvt to false even if cards are powered off.
        // otherwise, if cards ARE powered off, it will always be
        // true and the "start trigger" clock will be written continuously
        gFirstEvt = false;
    }

}

bool IsPinPowered(const SnCommWin* cw) {
    bool havePower = false;
    switch (cw->GetCommType()) {
        case SnConfigFrame::kIrid:
            havePower = gConf.IsPoweredFor(SnConfigFrame::kIridComWin)
                && ( (kIridPwrFromAfar)
                      ? PIN_afar_power.read()
                      : PIN_iridSbd_power.read() ==
                    gConf.GetPowPinSetting(SnConfigFrame::kIridComWin));
            break;
        case SnConfigFrame::kAfar:
            havePower = gConf.IsPoweredFor(SnConfigFrame::kAfarComWin)
                && (PIN_afar_power.read() ==
                    gConf.GetPowPinSetting(SnConfigFrame::kAfarComWin));
            break;
        case SnConfigFrame::kUSB:
            havePower = true; // USB always on (for now)
            break;
        case SnConfigFrame::kSDcard: // shouldn't happen. skip it
        default: // unknown.. skip it
            break;
    };
    return havePower;
}


SnCommWin::ECommWinResult OpenCommWin(const bool isStartupWin) {
    // loop through each comm mode:
    //  a) try to connect
    //  b) if connected, listen for config
    //  c) if config requests data, send it
    
    led2 = 1;
    
    gLastCommWin = time(0);

    SnCommWin::ECommWinResult res = SnCommWin::kUndefFail;
    
    // get the trigger rates
//    float thmrate=0, evtrate=0;
//    GetRates(thmrate, evtrate);
    const float seqlive = GetSeqLivetime();
#ifdef DEBUG
    printf("config=%s\r\n", gConf.GetLabel());
//    printf("thmrate=%g, evtrate=%g\r\n",thmrate,evtrate);
    printf("SEQ livetime = %g\r\n", seqlive);
    printf("Free memory = %d\r\n", FreeMem());
#endif

    StopAllTickers();
    
    if (gConf.GetCommWinDuration()==0) {
        // TODO: set min so this is not possible
        res = SnCommWin::kOkNoMsg;
    } else {
    
        gCommWinOpen = true;
        Watchdog::kick(); // don't reset!
    
#ifdef DEBUG
        printf("opening comm window at %d\r\n", (int32_t)gLastCommWin);
        printf("duration=%u\r\n",gConf.GetCommWinDuration());
#endif
    
        // close the file so that the data is all written out.
        // and open it back up at the beginning (for reading)
#ifdef DEBUG
        printf("close & open file. gEvtNum=%u, gPowNum=%u\r\n",gEvtNum,gPowNum);
        printf("curfile=%p, filename=%s\r\n",SnSDUtils::GetCurFile(),
            SnSDUtils::GetCurFileName());
#endif
        
        if (isStartupWin==false) {
#if CHIPBOARD==ATWD4CH
            PIN_lockRegisters = 0; // unlock so we can talk to the SD card
#else
            PIN_readingData   = 0; // unlock so we can talk to the SD card
#endif
#ifdef DEBUG
            printf("closing output file\r\n");
#endif
            SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());
#ifdef DEBUG
            printf("open existing file (%d)\r\n",strlen(SnSDUtils::GetCurFileName()));
#endif
            SnSDUtils::OpenExistingFile(SnSDUtils::GetCurFileName(), true, false);
        }

#ifdef DEBUG
        printf("setting power\r\n");
#endif
        // (probably) power down cards,amps
        // don't power up comms yet (only as needed for use)
        SetPower(true, 0);
        
        // time to recount files for the status update
        // for the startup win, don't access SD card in case we
        // rebooted due to a problem with the SD card
        SnStatusFrame::fgRecalcFiles = !isStartupWin;
        
#if defined(ENABLE_AFAR_TWITTER) && defined(ENABLE_AFAR_COMM)
        bool doTwitter = false;
#endif

#ifdef DEBUG
        printf("start loop over comms\r\n");
#endif
        bool needToConnect=true; // still need to talk to the outside
        bool sendStat[kNcomms]; // if need to send status over this peripheral
        bool needClos[kNcomms]; // if need to call CloseConn on this peripheral
        for (uint8_t i=0; i<kNcomms; ++i) {
            sendStat[i]=true;
            needClos[i]=true;
        }
        bool* ss = sendStat;
        bool* nc = needClos;
        SnCommWin** cw = gComms;
        for (uint8_t i=0; needToConnect && ((time(0)-gLastCommWin)<gConf.GetCommWinDuration());
                     ++i, ++cw, ++ss, ++nc) {
            Watchdog::kick(); // don't reset!
            if (i==kNcomms) {
                i=0;
                cw = gComms;
                ss = sendStat;
                nc = needClos;
            }
            // skip if no comm object
            if ((*cw)==0) {
                continue;
            }

            const SnConfigFrame::EDatPackBit ctype = (*cw)->GetCommType();
            
            // power up for this device
            if (gConf.IsCommPowerSimple()==false) {
                SetPower(true, ctype);
            }
            
            // skip if no power for this comm
            // THIS IS VITAL! For example, if the ethernet
            // port is powered down, making an Ethernet obejct
            // (done in netif) will stall forever waiting for the clock.
            // Do it here to keep all PIN usage in main.cpp
            const bool havePower=IsPinPowered(*cw);
            if (havePower==false) {
                continue;
            }
            
            // always apply safety nets to connection and listen so
            // that we don't accidently shut down comms (i.e. with
            // connectTO or listnTO being 0)
            gConf.ApplyConnectListenSafetyNets();
            
            const uint32_t conto = 
                (gConf.GetCommWinDuration() < gConf.GetCommWinConnectTO(ctype)) ?
                 gConf.GetCommWinDuration() : gConf.GetCommWinConnectTO(ctype);
            const uint32_t listo = 
                (gConf.GetCommWinDuration() < gConf.GetCommWinListenTO(ctype)) ?
                 gConf.GetCommWinDuration() : gConf.GetCommWinListenTO(ctype);
            
            //
            // try to connect to the outside if we haven't yet
            //
            // update power reading in case we want to send it in status
            GetAvePowerReading();
            
            // update temperature in case we want to send it in status
            if (isStartupWin) {
#if CHIPBOARD!=ATWD4CH
                InitTempProbe();
#endif
            }
            UpdateTemperature();

            // open window and (mabye) send status update
#ifdef DEBUG
            printf("Free memory = %d\r\n", FreeMem());
            printf("calling OpenWindow. ss=%d. ctype=%hhu (%s)\r\n",
                (int)(*ss),(uint8_t)ctype,SnConfigFrame::GetDataStreamName(ctype));
            printf("conto=%u, listo=%u, dur=%u, connTO=%u, lisTO=%u\r\n",
                conto,listo,gConf.GetCommWinDuration(),
                gConf.GetCommWinConnectTO(ctype), gConf.GetCommWinListenTO(ctype));
            printf("stopAt=%u, current=%d, lastComWin=%d, dt~%u\r\n",
                gConf.GetTimeoutTime(gLastCommWin,conto),
                time(0), gLastCommWin,
                gConf.GetTimeoutTime(gLastCommWin,conto)-time(0));
#endif
            
            //
            // open the connection and send the status update (if the
            // status has not yet been sent over this peripheral)
            //
            const SnCommWin::ECommWinResult conres = (*cw)->OpenWindow( *ss,
                gConf, gPower, gEvent, SnSDUtils::GetCurSeqNum(), 
//                thmrate, evtrate, 
                gNumThmTrigs, gNumSavedEvts, seqlive,
                gPowerOnTime, gTemperature,
                gGenBuf, gConf.GetTimeoutTime(gLastCommWin, conto));

#ifdef DEBUG
            printf("conres = %d\r\n",static_cast<int>(conres));
#endif
            if (conres>=SnCommWin::kConnected) {
                Watchdog::kick(); // don't reset!
                // connected. listen for config
                *ss = false; // don't send status next time
                *nc = true;  // will need to close this connection
                
                // clear watchdog reset bit now that we've told someone
                Watchdog::clearResetFlag();
                
#if defined(ENABLE_AFAR_TWITTER) && defined(ENABLE_AFAR_COMM)
                if (ctype==SnConfigFrame::kAfar) {
                    // if we connected by Afar
                    doTwitter = true;
                }
#endif

#ifdef DEBUG
                printf("get conf gtt=%u\r\n",gConf.GetTimeoutTime(gLastCommWin, listo));
#endif
                // copy old config
                gConfCopy = gConf;
                
                //
                // ask for a new config
                //
                const SnCommWin::ECommWinResult cfgres = (*cw)->GetConfig(
                    gConf, gConf.GetTimeoutTime(gLastCommWin, listo), gGenBuf, gBufSize);
#ifdef DEBUG
                printf("cfgres = %d\r\n",static_cast<int>(cfgres));
#endif
                if (cfgres>=SnCommWin::kOkWithMsg) {
                    Watchdog::kick(); // don't reset!
                    
#ifdef DEBUG
                    printf("received config!\r\n");
                    printf("send data = %d\r\n", gConf.GetCommSendData());
#endif

                    const uint32_t winto = gConf.GetTimeoutTime(gLastCommWin, 
                                                         gConf.GetCommWinDuration());
                    //const uint32_t gtt = gConf.IsObeyingTimeout() ? winto : 0;
                    
                    //
                    // send status data -- do this after getting the config!
                    // (avoid a situation where sending all this data causes a timeout,
                    // thereby preventing a new config from being accepted)
                    //
                    res = (*cw)->SendStatusData(gConf, gConfCopy,
                                                gStTrgStartClk, gStTrgStopClk,
                                                gStPower, gEvent, gStTemperature,
                                                gHrtbt, gStNewPower, gStNewEvent, 
                                                gStNewHeartbeat, gStNewTemperature,
                                                gGenBuf, winto);
#ifdef DEBUG
                    printf("SendStatusData res=%d\r\n",
                        static_cast<int32_t>(res));
#endif
                    
                    //
                    // check if there are any requests before sending data
                    //
                    if (gConf.IsWaitingHndShkBeforeSendData()) {
                        // send handshake request
                        (*cw)->SendHndshkReq(gGenBuf, winto);
                        // wait for response
                        uint8_t hndshk(0); uint32_t hndshkLen(0);
                        res = (*cw)->WaitHandshake(gConf, winto, gGenBuf, gBufSize, hndshk,
                                                   &hndshkLen);
#ifdef DEBUG
                        printf("WaitHandshake res = %d\r\n",static_cast<int>(res));
#endif
                        // handle response
                        if (SnCommWin::kOkWithMsg==res) {
                            res = (*cw)->HandleHandshake(SnSDUtils::GetCurFile(), 
                                      SnSDUtils::GetCurFileName(),
                                      gConf, gEvent, gPower, gGenBuf, gBufSize,
//                                      gConf, gLastEvent, gPower, gGenBuf, gBufSize,
                                      winto, hndshk, hndshkLen);
#ifdef DEBUG
                            printf("HandleHandshake res = %d\r\n",static_cast<int>(res));
#endif
                        }
                    } // if handshake before data
                    
                    //
                    // send data if need be (files, some events, etc)
                    //
                    if (gConf.GetCommSendData()!=0) {
#ifdef DEBUG
                        printf("sending data, winto=%u. lcw=%u, dur=%u, obey=%s\r\n",
                            winto,
                            gLastCommWin, gConf.GetCommWinDuration(),
                            gConf.IsObeyingTimeout() ? "true" : "false");
#endif

                        res = (*cw)->SendData(gConf, 
//                                              gLastEvent, gPower, gGenBuf, gBufSize,
                                              gEvent, gPower, gGenBuf, gBufSize,
                                              winto);
#ifdef DEBUG
                        printf("SendData res = %d\r\n",static_cast<int>(res));
#endif

                    } else {
                        // don't send anything
                        res = cfgres;
                    } // if send data
#ifdef DEBUG
                    printf("Got config!\r\n");
#endif
                    Watchdog::kick(); // don't reset!
                    
                    // need to close this connection
                    *nc = true;
                    // stop trying to connect to outside
                    // don't break immediately, so that we can close conn and
                    // maybe tweet (pff..)
                    needToConnect = false;

                } // if config recvd ok with message

                //
                // stupid legacy twitter crap.
                //
                // after normal Afar connection closed, try to tweet
#if defined(ENABLE_AFAR_TWITTER) && defined(ENABLE_AFAR_COMM)
                if (ctype==SnConfigFrame::kAfar) {
                    // tweet
#ifdef DEBUG
                    printf("for twitter: gTwit=%p, doTwitter=%d\r\n",gTwit,(int)doTwitter);
#endif
                    // send a twitter update
                    if ( (gTwit!=0) && doTwitter ) {
                        const uint32_t conto = 
                            (gConf.GetCommWinDuration() < gTwit->GetConnectTimeout()) ?
                             gConf.GetCommWinDuration() : gTwit->GetConnectTimeout();
                        const uint32_t listo = 
                            (gConf.GetCommWinDuration() < gTwit->GetListenTimeout()) ?
                             gConf.GetCommWinDuration() : gTwit->GetListenTimeout();
#ifdef DEBUG
                        printf("open twit window. conto=%u, listo=%u\r\n",
                            conto, listo);
#endif
                        const SnCommWin::ECommWinResult conres = gTwit->OpenWindow(
                            gConf.GetTimeoutTime(gLastCommWin, conto), false, gConf,
    //                        gLastEvent, gPower,
                            gEvent, gPower,
                            SnSDUtils::GetCurSeqNum(), thmrate, evtrate,
                            gGenBuf);
                        if (conres>=SnCommWin::kConnected) {
                            Watchdog::kick(); // don't reset!
                            gTwit->Tweet(gConf, thmrate, evtrate, gGenBuf,
                                         gConf.GetTimeoutTime(time(0), listo));
                        }
                    }
                } // end tweet block
#endif

            } else {
                // OpenWindow did not connect
                (*cw)->CloseConn(gConf.GetTimeoutTime(gLastCommWin, listo));
                *nc = false;
#ifdef DEBUG
                printf("no connect close conn. i=%d, nc=%s\r\n",
                    (int32_t)i, (*nc) ? "true" : "false");
#endif
            } // if connected
            
            Watchdog::kick(); // don't reset!

            if (*nc) {
                // need to close this connection
#ifdef DEBUG
                printf("close conn extra time. i=%d, nc=%s\r\n",
                    (int32_t)i, (*nc) ? "true" : "false");
#endif
                const uint32_t extraDiscTime = gLastCommWin 
                    + gConf.GetCommWinConnectTO((*cw)->GetCommType());
                (*cw)->CloseConn(
                    gConf.GetTimeoutTime(extraDiscTime, gConf.GetCommWinDuration()),
                    gGenBuf, true); // send the "closing window" signal
                *nc = false;
            }
            
            Watchdog::kick(); // don't reset!
        } // end loop over comms
        
        
        // check Iridium time, send Iridium signal strength, and close the connection(s)
        cw = gComms;
        nc = needClos;
        for (uint8_t i=0; i<kNcomms; ++i, ++cw, ++nc) {
            if ((*cw)==0) {
                continue;
            }
            
            const SnConfigFrame::EDatPackBit ctype = (*cw)->GetCommType();
                        
            // check Iridium time
            if (ctype==SnConfigFrame::kIrid) {

                // power up for this device
                if (gConf.IsCommPowerSimple()==false) {
                    SetPower(true, ctype);
                }
                const bool havePower=IsPinPowered(*cw);
                if (havePower==false) {
                    continue;
                }

#ifdef DEBUG
                printf("try to set iridium time\r\n");
#endif
                // set the clock before closing connection
                const uint32_t totime = 
                    gConf.GetTimeoutTime(gLastCommWin,
                                         gConf.GetCommWinDuration());
#ifdef DEBUG
                printf("totime=%u, ctime=%u\r\n",totime,time(0));
#endif
                const bool con = (*cw)->Connect(totime);
                *nc = true;
                
                if (con) {
                    uint32_t prvTime(0), setTime(0);
                    const bool gottime = (*cw)->TrySetSysTimeUnix(
                        totime, prvTime, setTime);
                    if (gottime) {
                        gClkSet.SetClocks( prvTime, setTime, time(0),
                                           gSinceClkSet.read_us() );
                        gSinceClkSet.reset();
                        gSinceClkSet.start();
#ifdef DEBUG
//                        printf("sig str: totime=%u, ctime=%u\r\n",totime,time(0));
                        printf("sig str: totime=%u, ctime=%u\r\n",totime,gClkSet.GetCurTime());
#endif
                        // got time; now send signal strength
                        (*cw)->SendSignalStrength( gGenBuf, gSigStr, totime );
                    } // if got the iridium system time
                } // if connected (again, possibly)
            } // if iridium

            if (*nc) {

                // power up for this device
                if (gConf.IsCommPowerSimple()==false) {
                    SetPower(true, ctype);
                }
                const bool havePower=IsPinPowered(*cw);
                if (havePower==false) {
                    continue;
                }

#ifdef DEBUG
                printf("last loop close conn. i=%d, nc=%s\r\n",
                    (int32_t)i, (*nc) ? "true" : "false");
#endif

                const uint32_t extraDiscTime = gLastCommWin 
                    + gConf.GetCommWinConnectTO((*cw)->GetCommType());
                (*cw)->CloseConn(gConf.GetTimeoutTime(extraDiscTime, gConf.GetCommWinDuration()));
                // no "close window" signal sent here...
                *nc = false;
            }
        } // end loop: check time, close conns
        
        /*
        // close connections
        cw = gComms;
        for (uint8_t i=0; i<kNcomms; ++i, ++cw) {
            if ((*cw)==0) {
                continue;
            } else {
            }
        }
        */
        
    } // if duration >0
    
    /* not working. must use DEFCONF.DAT to change IP's.
    // change comm parameters (IP addresses)
#ifdef DEBUG
    printf("set comm params\r\n");
#endif
    for (uint8_t cc=0; cc<kNcomms; cc++) {
        if (gComms[cc]!=0) {
            gComms[cc]->Set(gConf);
        }
    }
    */

    
    // check if we missed too many consecutive connections
    if (res<=SnCommWin::kAllFails) {
        ++gConsecCommFails;
#ifdef DEBUG
        printf("res=%d, gConsecCommFails=%hu, kMaxConsecCommFails=%hu\r\n",
            static_cast<int>(res), gConsecCommFails,kMaxConsecCommFails);
#endif                    
        if (gConsecCommFails>kMaxConsecCommFails) {
#ifdef DEBUG
            printf("rebooting\r\n");
#endif                    
            // goodbye cruel world, it's over. walk on by...
            mbed_reset();
        }
    } else {
        gConsecCommFails=0;
    }
    
    // (probably) power down comms and power up cards,amps
    SetPower(false, SnConfigFrame::kIrid | SnConfigFrame::kAfar);

    // reset config with system powered (for DAC/PLA setting)
#ifdef DEBUG
    printf("calling SetConfigAndMakeOutputFile\r\n");
#endif

    if (gConf.IsRunSeqListOneCommWinOnly()) {
        SnSDUtils::ClearRunSeqList(gConf.IsSendingFilesRunSeqList());
    }
    
    SetConfigAndMakeOutputFile();

    // check power in case we should be in low power mode
    // but don't save this reading to the file
    // (there's already one near the beginning)
    CheckPower(false, false);

#ifdef DEBUG
    printf("closing comm win at %d\r\n",(int32_t)time(0));
    printf("Free memory = %d\r\n", FreeMem());
#endif

    led2 = 0;
    
    gCommWinOpen = false;
    return res;
}