Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

SnConfigFrame.h

Committer:
uci1
Date:
2019-06-05
Revision:
125:ce4045184366
Parent:
122:c1b5023eac69

File content as of revision 125:ce4045184366:

#ifndef SN_SnConfigFrame
#define SN_SnConfigFrame

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

//#define DEBUG

class SnConfigFrame {
 public:
    static bool              fgApplySafetyNets;    // whether to apply safety bounds on certain parameters in ReadFrom (default: true)
 
    static const uint32_t    kMinCommWinPrdLowPwr; // exclusive min low power comm win period (s)
    static const uint32_t    kMaxCommWinPrdLowPwr; // exclusive max low power comm win period (s)
    static const uint32_t    kMinCommWinDurLowPwr; // exclusive min low power comm win duration (s)
    static const uint32_t    kMaxCommWinDurLowPwr; // exclusive max low power comm win duration (s)
    static const uint8_t     kConfLblLenNoStrTerm=63;            // length of configuration label char array without the terminating '\0' (must not change!! used in i/o sizes)
    static const uint8_t     kConfLblLen=kConfLblLenNoStrTerm+1; // length of configuration label char array (63+'\0') (must not change!! used in i/o sizes)
    static const uint8_t     kIPLen=16;            // length of IP string (includes \0). matches MBED's Socket class (so no ipv6) (must not change!! used in i/o sizes)
    
#if defined(USE_INTERFACE_CHIP) || defined(LOAD_DEFAULT_CONFIG_FROM_SD)
    static const char* const kDefConfFile;  // default configuration file
    static const char* const kDefRemIpFilen;
    static const char* const kDefRemPortFilen;
    static const char* const kDefMbedIPFilen;
    static const char* const kDefMbedMaskFilen;
    static const char* const kDefMbedGateFilen;
#endif

    static const char* const kDefIPflag;    // flag to use IP default
    static const uint32_t    kDefIPvalue;   // value to indicate use of default IP

    // ATWD 4channel configs
    static const uint32_t   kMaxSizeOfV1 = 
            + (9u*sizeof(uint32_t)) + (6u*sizeof(uint16_t))
            + (10u*sizeof(uint8_t)) + (3u*kNplasV1*sizeof(uint16_t)) // 3*Nplas because if StreamHiLo is set, will stream 3 values (composite, high side, low side)
            + (kTotDacsAtwd4ch*sizeof(uint16_t))
            + (kConfLblLen*sizeof(uint8_t));
    static const uint32_t   kMaxSizeOfV2 =
            kMaxSizeOfV1 + sizeof(uint32_t) + sizeof(uint8_t);
    static const uint32_t   kMaxSizeOfV3 =
            kMaxSizeOfV2 + (2u*sizeof(uint16_t)) + (4u*kIPLen*sizeof(char));
    static const uint32_t   kMaxSizeOfV4 = kMaxSizeOfV3 + (sizeof(float)-sizeof(uint16_t)); // forced trig per to float
    static const uint32_t   kMaxSizeOfV5 = kMaxSizeOfV4;
    static const uint32_t   kMaxSizeOfV6 = kMaxSizeOfV5 + sizeof(uint16_t); // To/From Low Power
    static const uint32_t   kMaxSizeOfV7 = kMaxSizeOfV6 + (2u*sizeof(uint8_t)); // add ConnTOmins, ListenTOmins
    static const uint32_t   kMaxSizeOfV8 = kMaxSizeOfV7 - sizeof(uint32_t) + sizeof(uint16_t); // FirstEvt -> FirstSeq
    
    // SST 4channel configs
    static const uint32_t   kMaxSizeOfV9 = kMaxSizeOfV8
            - (3u*kNplasV1*sizeof(uint16_t)) // no patterns in SST
            - (kTotDacsAtwd4ch*sizeof(uint16_t))
            + (kTotDacsSst4ch*sizeof(uint16_t)) // switch to number of SST dacs
            - sizeof(uint8_t)  // no stream hi/lo pla flag
            - sizeof(uint8_t); // no num plas variable
    static const uint32_t   kMaxSizeOfV10 = kMaxSizeOfV9 + sizeof(int8_t); // add fTempCheckPeriod
    
    // ATWD - expanding upon V8 (previous ATWD config)
    static const uint32_t   kMaxSizeOfV11 = kMaxSizeOfV8
            - sizeof(uint32_t) // label length (now always 64 bytes written)
            - (sizeof(uint32_t) - sizeof(uint16_t)) // fEvtsPerSeq from uint32_t to uint16_t
            - sizeof(uint8_t) // remove stream hi/lo pla's (always false now)
            - sizeof(uint8_t) // remove pack data (always done for all peripherals)
            - sizeof(uint8_t) // remove amps on completely
            - (4u*kIPLen) + (4u*sizeof(uint32_t)) // change IP from strings to numbers
            - (2u*kNplasV1*sizeof(uint16_t)) // no streaming of hi/lo patterns
            + sizeof(uint8_t) // add single freq ratio L1 trig parameter
            + sizeof(uint8_t) // L1 scaledown
            + sizeof(uint16_t) - sizeof(uint8_t) // change fEnableTherm from 8 bit to 16 bit to match trigger bit word in event
            + (2u*sizeof(uint8_t)) // connect and listen TOs for Irid & Afar
            + sizeof(uint16_t) - sizeof(uint8_t); // run mode from 8 to 16 bit integer

    // SST - expanding upon V10 (previous SST config)
    static const uint32_t   kMaxSizeOfV12 = kMaxSizeOfV10
            - sizeof(uint32_t) // label length (now always 64 bytes written)
            - (sizeof(uint32_t) - sizeof(uint16_t)) // fEvtsPerSeq from uint32_t to uint16_t
            - sizeof(uint8_t) // remove pack data (always done for all peripherals)
            - sizeof(uint8_t) // remove amps on completely
            - (4u*kIPLen) + (4u*sizeof(uint32_t)) // change IP from strings to numbers
            + sizeof(uint8_t) // add single freq ratio L1 trig parameter
            + sizeof(uint8_t) // L1 scaledown
            + sizeof(uint16_t) - sizeof(uint8_t) // change fEnableTherm from 8 bit to 16 bit to match trigger bit word in event
            + (2u*sizeof(uint8_t)) // connect and listen TOs for Irid & Afar
            + sizeof(uint16_t) - sizeof(uint8_t); // run mode from 8 to 16 bit integer
    static const uint32_t   kMaxSizeOfV13 = kMaxSizeOfV12; // same as V12 (just signifies SST4CH_1GHz)
    static const uint32_t   kMaxSizeOfV14 = kMaxSizeOfV12; // same as V12 (just signifies SST4CH512)
    static const uint32_t   kMaxSizeOfV15 = kMaxSizeOfV12; // same as V12 (just signifies SST4CH512_1GHz)
    static const uint32_t   kMaxSizeOfV16 = kMaxSizeOfV15
            - (kTotDacsSst4ch*sizeof(uint16_t))
            + (kTotDacsSst8ch*sizeof(uint16_t)); // more DACs for more channels
    static const uint32_t   kMaxSizeOfV17 = kMaxSizeOfV16; // same as V16 (just signifies SST8CH_1GHz)
    
    static const uint32_t   kMaxSizeOf = kMaxSizeOfV7; // should be the biggest one
    
    enum EDatPackBit {
        kSDcard = BIT(0),
        kIrid   = BIT(1),
        kAfar   = BIT(2),
        kUSB    = BIT(3)
    };
    static const uint8_t kNumDatStreams = 4; // ** note: need to change manually
    
    enum ESendDataBit {
        // can't use BIT(0)! (-0 = 0 => send nothing)
        kAllFiles       =  BIT(1),  // if bit=0 => send most recent file
        kTimeout        =  BIT(2),  // if bit=0 => ignore timeout
        kDelete         =  BIT(3),  // if bit=0 => do not delete sent files
        kForceSBDdata   =  BIT(4),  // if bit=0 => do not send data over SBD
        kHnShBefSendDat =  BIT(5),  // if bit=0 => do not wait for a handshake after GetConfig before calling SendData
        kSendRunSeqList =  BIT(6),  // if bit=1 and kAllFiles bit=0, send all files in the run/seq list (instead of most recent file)
        kStatSendConf   =  BIT(7),  // if bit=1, send a config with the status update data
        kStatSendTrgTim =  BIT(8),  // if bit=1, send trigger start/stop times with the status update data
        kStatSendPwrDat =  BIT(9),  // if bit=1, send the last power frame recorded in data taking mode with the status update
        kStatSendEvent  =  BIT(10), // if bit=1, send the last event frame recorded in data taking mode with the status update
        kStatSendHtbt   =  BIT(11), // if bit=1, send the last heartbeat frame recorded in data taking mode with the status update
        kStatSendTmpDat =  BIT(12), // if bit=1, send the last temperature frame recorded in data taking mode with the status update
        kUseBits        = static_cast<int16_t>(-BIT(14)) // useful to initialize fCommSendData as a bit word
    };
    
    enum EPowerModeBit {
        kAmpsDatTak = BIT(0),
        kCardDatTak = BIT(1),
        kIridDatTak = BIT(2),
        kAfarDatTak = BIT(3),
        kAmpsComWin = BIT(4),
        kCardComWin = BIT(5),
        kIridComWin = BIT(6),
        kAfarComWin = BIT(7)
    };
    
    enum ERunMode {
        kSingleSeqBit   = BIT(0), // if 0, infinite sequences
        kCountPowerBit  = BIT(1), // if 0, count events
        kDualThreshBit  = BIT(2), // if 0, single sided thresholds on SST
        kDiffTrigBit    = BIT(3), // if 0, send result of each comparator on SST
        kLowPwrSBDonly  = BIT(4), // if 0, low power afar/sbd power settings same as normal. if 1, afar off and sbd on during low power mode
        kRSListOneCW    = BIT(5), // if 0, only clear run/seq list after files sent from it
        kIgnoreSDcard   = BIT(6), // if 0, read/write data to SD card as normal. if 1, function as though no SD card is present
        kComPwrSimple   = BIT(7), // if 0, comm periphs powered as needed during comm win. if 1, power adjusted once at start/finish of comm win
        kCommEachEvent  = BIT(8), // if 0, comm windows only after comm period of seconds. if 1, comm winodows also after each event that qualifies for saving to SD card
        kSkipTrgStartReset = BIT(9) // if 0, the digitizer (SST) is reset when the mbed starts waiting for triggers. if 0, no reset occurs, which can cause an immediate readout if the chip had triggered before the mbed was ready
   };
    
    // i/o version
    static const uint8_t     kIOVers;   // MUST BE INCREASED if any member var changes (==> also if kNchans, etc. change!)
    
 private:
    // !!
    // !! If any member variables change, update: SizeOf function and kIOVers value! (also if kNchans, etc. change!)
    // !!
    
    // mbed mac address
    static uint64_t fgMacAdr;              // mbed mac address
    // conf header
    char        fLabel[kConfLblLen];       // configuration label
    uint32_t    fConfTime;                 // cpu config time
    uint32_t    fRun;                      // run number
    uint16_t    fFirstSeq;                 // starting sequence number
    uint16_t    fEvtsPerSeq;               // number of events per file
    uint16_t    fRunMode;                  // mode of running (see ERunMode) -- changed from uint8 to uint16 with V11+
#if CHIPBOARD==ATWD4CH
    // in newer versions (11+), this is always false
    uint8_t     fStreamHiLoPlas;           // (1byte bool) if true, add the separated hi/lo thresh PLA patterns to the i/o
#endif
    // data packing
    uint8_t     fWvLoseLSB;                // number of least significant bits to lose when packing waveform data
    uint8_t     fWvLoseMSB;                // number of  most significant bits to lose when packing waveform data
    uint16_t    fWvBaseline;               // global baseline to use when packing data (useful to reduce clipping on the high end)
    uint8_t     fDatPackType;              // type of data packing. OR'd bitword: if bit 1, will pack for writing. see EDatPackBit. default: always pack (all 1's). starting with IOVers 11+, this is always all 1's and is not settable
    // trigger setup
    uint16_t    fDAC[kNchans][kNchanDacs]; //[card id][dac id] values should be 0-4095 here (not checked tho)
#if CHIPBOARD==ATWD4CH
    uint8_t     fNumPlas;                  // number of patterns to use. must be <= kNplas.
    uint16_t    fPLA[kNplas];              //[pattern id] (same for each card)
#endif
    uint8_t     fNumCardsMajLog;           // number of cards participating in the MajLogic trigger (1 to 4)
    uint16_t    fEnableThermTrig;          // whether or not to allow thermal triggers. as of vers 11+, now a bit word that can enable L1 triggers in addition to in-chip triggers. that means it now must be the same size as the trigger bit word in the event!
    float       fForceTrigPeriod;          // number of seconds between force triggers (0=none)
    uint16_t    fHeartBeatPeriod;          // number of seconds between heartbeats (0=none)
    //uint8_t     fAmpsOn;                   // which amps are on (bit word. uint8_t => 8 amps max). removed vers 11+, as it cannot be implemented.
    uint16_t    fEvtThrtlPeriodMs;         // throttle period to write events (ms)
    // power
    uint8_t     fPowerMode;                // power mode bit word: see EPowerModeBit
    uint16_t    fBatVoltToLowPwr;          // battery level at which to switch to low power (not used?)
    uint16_t    fBatVoltFromLowPwr;        // battery level at which to switch back from low power (not used?)
    uint16_t    fVoltCheckPeriod;          // how often to check the voltages (s)
    // communication
    uint32_t    fCommWinPeriod;            // seconds between communication window startup (0=always on)
    uint32_t    fCommWinDuration;          // seconds that communication window stays open (0=always open)
    int16_t     fCommSendData;             // data to send during comm win (=0: none, >0=send up to x events from last file until comm win closes, <0=see ESendDataBit)
    uint32_t    fCommWinPrdLowPwr;         // low power communication window period (seconds) (range enforced)
    uint32_t    fCommWinDurLowPwr;         // low power communication window duration (seconds) (range enforced)
    uint8_t     fCommWinConnTOMins[kNumDatStreams]; // comm win connection timeout (minutes) for each data stream (range enforced)
    uint8_t     fCommWinListTOMins[kNumDatStreams]; // comm win listening timeout (minutes) for each data stream (range enforced)
    char        fRemoteServer[kIPLen];     // IP address of remote server (for afar)
    uint16_t    fRemotePort;               // port number of remote server (for afar)
    char        fMbedIP[kIPLen];           // IP address of this mbed
    char        fMbedMask[kIPLen];         // IP address of this mbed mask
    char        fMbedGate[kIPLen];         // IP address of this mbed gateway
    // watchdog
    uint32_t    fWatchDogPeriod;           // number of seconds of inactivity for watchdog to issue a reset
#if CHIPBOARD!=ATWD4CH
    // temp
    int8_t      fTempCheckPeriod;          // number of minutes between temperature checks. if negative, uses parasite power. if 0, never check.
#endif
    // vers 11+ below
    uint8_t     fSnglFreqRatio;            // single frequency L1 trigger parameter = max / (tot - max). [0,255] => [0.0, 1.0]
    uint8_t     fL1Scaledown;              // save an L1 fail event every fL1Scaledown
    
    // in case of low power, store regular settings
    // these are not sent over i/o or stored in the file
    // so they are not included in SizeOf
    bool        fIsLowPower;
    char        fNormLabel[kConfLblLen];
    uint8_t     fNormPowerMode;
    
    void SetHardDefaults();
    
    static
    void SetSDNeedToInitFlag();
    
    static
    bool        IsIOversForATWD(const uint8_t rv) {
        return ( (rv<9) || (rv==11) );
    }
    
    static
    uint16_t    GetTotDacsForIOVers(const uint8_t rv) {
        if ( IsIOversForATWD(rv) ) {
            return kTotDacsAtwd4ch;
        } else if ((rv==16)||(rv==17)) {
            return kTotDacsSst8ch;
        } else {
            return kTotDacsSst4ch;
        }
    }
    
    static
    uint16_t    GetMaxPlasForIOVers(const uint8_t rv) {
        if ( IsIOversForATWD(rv) ) {
            return kNplasV1;
        } else {
            return 0;
        }
    }


    static
    uint32_t    SizeOf(const uint8_t rv,
                       const bool streamHiLoPlas,
                       const uint8_t nplas,
                       const uint8_t lblLen) {
        // private because it cannot be used to read from a buffer
        // (the label length and fStreamHiLoPlas are not known a priori)
        // returns the num of bytes needed to stream this object
        // = size of member vars + 1 for i/o version + extra PLA strings (maybe)
        uint32_t maxsize = kMaxSizeOf;
        if (rv==1) {
            maxsize = kMaxSizeOfV1;
        } else if (rv==2) {
            maxsize = kMaxSizeOfV2;
        } else if (rv==3) {
            maxsize = kMaxSizeOfV3;   
        } else if (rv==4) {
            maxsize = kMaxSizeOfV4;   
        } else if (rv==5) {
            maxsize = kMaxSizeOfV5;   
        } else if (rv==6) {
            maxsize = kMaxSizeOfV6;
        } else if (rv==7) {
            maxsize = kMaxSizeOfV7;
        } else if (rv==8) {
            maxsize = kMaxSizeOfV8;
        } else if (rv==9) {
            maxsize = kMaxSizeOfV9;
        } else if (rv==10) {
            maxsize = kMaxSizeOfV10;
        } else if (rv==11) {
            maxsize = kMaxSizeOfV11;
        } else if (rv==12) {
            maxsize = kMaxSizeOfV12;
        } else if (rv==13) {
            maxsize = kMaxSizeOfV13;
        } else if (rv==14) {
            maxsize = kMaxSizeOfV14;
        } else if (rv==15) {
            maxsize = kMaxSizeOfV15;
        } else if (rv==16) {
            maxsize = kMaxSizeOfV16;
        } else if (rv==17) {
            maxsize = kMaxSizeOfV17;
        }
        uint32_t sz = maxsize;
        if (rv<11) {
            // less than V11? then we need to account for the dynamic
            // length of the label :( with V12+, the length is always the same
            const int32_t lbldiff = kConfLblLen - lblLen;
            sz = maxsize - lbldiff;
            if ((lbldiff!=0) && (rv>=4)) {
                sz += 1; // the \0 at the end of the string
            }
        }
#if CHIPBOARD==ATWD4CH
        if ( IsIOversForATWD(rv) ) {
            // streaming hi/lo plas separately?
            const uint8_t fac = (streamHiLoPlas && (rv<11)) ? 3u : 1u;
            const int32_t nplasNotSent = GetMaxPlasForIOVers(rv) - nplas;
            sz -= (fac*nplasNotSent);

            /*
            const uint32_t mhlp = 2u*GetMaxPlasForIOVers(rv)*sizeof(uint16_t);
            const int32_t dp = (nplas-GetMaxPlasForIOVers(rv))*sizeof(uint16_t);
            const uint8_t fac = (streamHiLoPlas && (rv<11)) ? 3u : 1u;
            sz += (fac*dp);
            if ((streamHiLoPlas==false) || (rv>=11)) {
                sz -= mhlp; // should be * (maxPlas - nplas), right?
            }
            */
        }
#endif
        return sz;
    }

#ifdef USE_INTERFACE_CHIP
    bool ReadOneIPFrom(const char* ipfname, char* ipstr);
#endif
    bool ReadDefaultRemoteServer();
    bool ReadDefaultRemotePort();
    bool ReadDefaultMbedIP();
    bool ReadDefaultMbedMask();
    bool ReadDefaultMbedGate();
    void SetDefaultIPs();
    void SetDefaultRemoteServ();
    void SetDefaultRemotePort();
    void SetDefaultMbedIP();
    void SetDefaultMaskIP();
    void SetDefaultGateIP();
    void ApplySafetyNets();
    void ApplyConnectListenSafetyNets(const uint8_t dataStreamIdx);

 public:
    SnConfigFrame(const bool applySafety=true) : fIsLowPower(false) {
        memset(fLabel, 0, kConfLblLen);
        fgApplySafetyNets = applySafety;
        Reset();
    }
    virtual ~SnConfigFrame() {}

    void ApplyConnectListenSafetyNets();
    
    bool     IsCountingPowerReadings() const { return ((fRunMode & kCountPowerBit)!=0); }
    bool     IsSingleSeqRunMode() const { return ((fRunMode & kSingleSeqBit)!=0); }
    bool     IsDualThresholdMode() const { return ((fRunMode & kDualThreshBit)!=0); }
    bool     IsDifferentialTrigMode() const { return ((fRunMode & kDiffTrigBit)!=0); }
    bool     IsSBDonlyLowPwrMode() const { return ((fRunMode & kLowPwrSBDonly)!=0); }
    bool     IsRunSeqListOneCommWinOnly() const { return ((fRunMode & kRSListOneCW)!=0); }
    bool     IsIgnoringSDcard() const { return ((fRunMode & kIgnoreSDcard)!=0); }
    bool     IsCommPowerSimple() const { return ((fRunMode & kComPwrSimple)!=0); }
    bool     IsCommWindowEachEvent() const { return ((fRunMode & kCommEachEvent)!=0); }
    bool     IsSkippingTrgStartReset() const { return ((fRunMode & kSkipTrgStartReset)!=0); }
    bool     IsLowPowerMode() const { return fIsLowPower; }
    const char* GetLabel() const { return fLabel; }
    uint32_t GetLabelStrLen() const { return strlen(fLabel); }
    uint32_t GetRun() const { return fRun; }
    uint16_t GetFirstSeq() const { return fFirstSeq; }
    uint16_t GetEvtsPerFile() const { return fEvtsPerSeq; }
    inline
    uint16_t GetEvtThrtlPeriodMs() const { return fEvtThrtlPeriodMs; }
    float    GetForceTrigPeriod() const { return fForceTrigPeriod; }
    uint16_t GetHeartbeatPeriod() const { return fHeartBeatPeriod; }
    uint16_t GetBatVoltToLowPwr() const { return fBatVoltToLowPwr; }
    uint16_t GetBatVoltFromLowPwr() const { return fBatVoltFromLowPwr; }
    uint16_t GetVoltCheckPeriod() const { return fVoltCheckPeriod; }
    uint32_t GetWatchdogPeriod() const { return fWatchDogPeriod; }
#if CHIPBOARD!=ATWD4CH
    uint16_t GetTempCheckPeriod() const {
        const uint16_t t = (fTempCheckPeriod<0) ? (-fTempCheckPeriod) : fTempCheckPeriod;
        return t*60;
    }
    bool     IsTempUsingParasitePower() const { return (fTempCheckPeriod<0); }
#else
    uint16_t GetTempCheckPeriod() const { return 0; }
#endif
    uint8_t  GetSingleFreqSuppRatioRaw() const { return fSnglFreqRatio; }
    inline
    float    GetSingleFreqSuppRatio() const { return static_cast<float>(fSnglFreqRatio) / 255.0f; }
    uint16_t GetDac(const uint8_t ch, const uint8_t dn) const { return fDAC[ch][dn]; }
#if CHIPBOARD==ATWD4CH
    uint8_t  GetNumPlas() const { return fNumPlas; }
    uint16_t GetPla(const uint8_t pn) const { return fPLA[pn]; }
#endif
    uint8_t  GetNumCardsMajLog() const { return fNumCardsMajLog; }
//    bool     IsThermTrigEnabled() const { return fEnableThermTrig!=0; }
    uint8_t  GetL1Scaledown() const { return fL1Scaledown; }
    bool     IsThermTrigEnabled() const 
       { return (fEnableThermTrig & kThermal)!=0; }
    bool     IsSingleFreqSuppEnabled() const
       { return (fEnableThermTrig & kSingleFreqSupp)!=0; }
    bool     IsL1TrigApplied() const
       { return (fEnableThermTrig & kL1TrgApplied)!=0; }
    /*
    bool     IsEachAmpOn() const {
        bool allon=true;
        for (uint8_t i=0; (i<kNchans) && allon; i++) {
            allon = (fAmpsOn & BIT(i))!=0;
        }
        return allon;
    }
    // TODO: allow check for individual amps, when they can be turned on individually
    */
    const char* GetRemoteServer() const { return fRemoteServer; }
    uint16_t    GetRemotePort() const { return fRemotePort; }
    const char* GetMbedIP() const { return fMbedIP; }
    const char* GetMbedMask() const { return fMbedMask; }
    const char* GetMbedGate() const { return fMbedGate; }
    uint32_t GetCommWinPeriod() const { return fIsLowPower ? fCommWinPrdLowPwr : fCommWinPeriod; }
    uint32_t GetCommWinDuration() const { return fIsLowPower ? fCommWinDurLowPwr : fCommWinDuration; }
    uint32_t GetCommWinConnectTOofIdx(const uint8_t idx) const {
        return (static_cast<uint32_t>( fCommWinConnTOMins[idx] ) * 60u);
    }
    uint32_t GetCommWinConnectTO(const SnConfigFrame::EDatPackBit b) const {
        return GetCommWinConnectTOofIdx(IndexOfDataStream(b));
    }
    uint32_t GetCommWinListenTOofIdx(const uint8_t idx) const {
        return (static_cast<uint32_t>( fCommWinListTOMins[idx] ) * 60u);
    }
    uint32_t GetCommWinListenTO(const SnConfigFrame::EDatPackBit b) const {
        return GetCommWinListenTOofIdx(IndexOfDataStream(b));
    }
    int16_t  GetCommSendData() const { return fCommSendData; }
    
    bool     IsSendingAllFiles() const
        { return (fCommSendData<0) && ((fCommSendData & kAllFiles)!=0); }
    bool     IsObeyingTimeout() const
        { return (fCommSendData<0) && ((fCommSendData & kTimeout)!=0); }
    bool     IsDeletingFiles() const
        { return (fCommSendData<0) && ((fCommSendData & kDelete)!=0); }
    bool     IsForcingSBDdata() const
        { return (fCommSendData<0) && ((fCommSendData & kForceSBDdata)!=0); }
    bool     IsWaitingHndShkBeforeSendData() const
        { return (fCommSendData<0) && ((fCommSendData & kHnShBefSendDat)!=0); }
    bool     IsSendingFilesRunSeqList() const
        { return (fCommSendData<0) && ((fCommSendData & kSendRunSeqList)!=0); }
    bool     IsStatusSendingConfig() const
        { return (fCommSendData<0) && ((fCommSendData & kStatSendConf)!=0); }
    bool     IsStatusSendingTrigTimes() const
        { return (fCommSendData<0) && ((fCommSendData & kStatSendTrgTim)!=0); }
    bool     IsStatusSendingPowerReading() const
        { return (fCommSendData<0) && ((fCommSendData & kStatSendPwrDat)!=0); }
    bool     IsStatusSendingEvent() const
        { return (fCommSendData<0) && ((fCommSendData & kStatSendEvent)!=0); }
    bool     IsStatusSendingHeartbeat() const
        { return (fCommSendData<0) && ((fCommSendData & kStatSendHtbt)!=0); }
    bool     IsStatusSendingTemperature() const
        { return (fCommSendData<0) && ((fCommSendData & kStatSendTmpDat)!=0); }
    
    uint8_t GetPowerMode() const { return fPowerMode; }
    int     GetPowPinSetting(const EPowerModeBit p, const bool isOn) const {
#if CHIPBOARD==ATWD4CH
        if (p==kCardDatTak || p==kCardComWin ||
            p==kAmpsDatTak || p==kAmpsComWin) {
            return isOn ? 0 : 1;
        } else {
            return isOn ? 1 : 0;
        }
#else
        return isOn ? 1 : 0;
#endif
    }
    int     GetPowPinSetting(const EPowerModeBit p) const {
        // return int to correspond to what DigitalOut::operator= expects
        return GetPowPinSetting(p, IsPoweredFor(p));
    }
    bool   IsPoweredFor(const EPowerModeBit p) const {
        return ((fPowerMode & p)!=0);
    }
    
    void EnablePowerFor(const EPowerModeBit p)  { fPowerMode |= p; }
    void DisablePowerFor(const EPowerModeBit p) { fPowerMode &= ~p; }
    
    void ChangeToLowPower();
    void ChangeToNormPower();
    
    const char* GetOutFileName(const char* dir) const;
    
    uint32_t GetTimeoutTime(const uint32_t startTime,
                            const uint32_t delta) const;

    
    // waveform packing info
    uint16_t GetWvBaseline() const { return fWvBaseline; }
    uint8_t  GetWvLoseLSB() const { return fWvLoseLSB; }
    uint8_t  GetWvLoseMSB() const { return fWvLoseMSB; }
    bool     IsDatPackedFor(const EDatPackBit d) const { return (fDatPackType & d)!=0; }
    void     GetPackParsFor(const EDatPackBit d,
                            uint8_t& loseLSB, uint8_t& loseMSB,
                            uint16_t& wvBase) const;
    
    // i/o
    static
    bool    IsIOversionOk(const uint8_t rv) {
        return (kIOVers == rv);
    }
    
    template<class T>
    static
    uint8_t PeekIOversion(T& b) {
        // the buffer/file/whatever 'b' must be at the start of the config frame
        uint8_t Rv=0;
        SnBitUtils::ReadFrom(b, Rv);
        return Rv;
    }
    
    template<class T>
    void ReadFrom(T& b) {
        // no check on the length of buf is done here
        // that should be been done already
        //
        // must match WriteTo
        
        uint8_t Rv=0;
        b               = SnBitUtils::ReadFrom(b, Rv); // i/o version
#ifdef DEBUG
        printf("Rv=%hhu\r\n",Rv);
#endif
        if ( IsIOversionOk(Rv) ) {
            
            if (IsLowPowerMode()) {
                // the low power bit is not streamed, so we need to
                // reset it explicitly
                ChangeToNormPower();
            }
            
            if (Rv<11) {
                uint32_t llen=kConfLblLen;
                b           = SnBitUtils::ReadFrom(b, llen);
#ifdef DEBUG
                printf("llen=%u\r\n",llen);
#endif
                b           = SnBitUtils::ReadFrom(b, fLabel, llen);
            } else {
                b           = SnBitUtils::ReadFrom(b, fLabel, kConfLblLen);
            }
#ifdef DEBUG
            printf("lbl=%s\r\n",fLabel);
#endif
            b           = SnBitUtils::ReadFrom(b, fConfTime);
#ifdef DEBUG
            printf("ct=%u\r\n",fConfTime);
#endif
            b           = SnBitUtils::ReadFrom(b, fRun);
#ifdef DEBUG
            printf("run=%u\r\n",fRun);
#endif
            if (Rv>7) {
                b           = SnBitUtils::ReadFrom(b, fFirstSeq);
            } else {
                uint32_t fe(0);
                fFirstSeq = 0;
                b           = SnBitUtils::ReadFrom(b, fe);
            }
#ifdef DEBUG
            printf("firstseq=%hu\r\n",fFirstSeq);
#endif
            if (Rv>1) {
                if (Rv<11) {
                    uint32_t eps(0);
                    b       = SnBitUtils::ReadFrom(b, eps);
                    fEvtsPerSeq = (1<<sizeof(fEvtsPerSeq))-1; // biggest value
                    if ( eps < fEvtsPerSeq ) {
                        fEvtsPerSeq = eps;
                    }
                } else {
                    b       = SnBitUtils::ReadFrom(b, fEvtsPerSeq);
                }
                if (Rv<11) {
                    uint8_t rm(0);
                    b       = SnBitUtils::ReadFrom(b, rm);
                    fRunMode = rm;
                } else {
                    b       = SnBitUtils::ReadFrom(b, fRunMode);
                }
            } else {
                fEvtsPerSeq = 1000;
                fRunMode    = 0;
            }
#ifdef DEBUG
                printf("eps=%hu\r\n",fEvtsPerSeq);
                printf("rm=%hu\r\n",fRunMode);
#endif
#if CHIPBOARD==ATWD4CH
            if ( IsIOversForATWD(Rv) && Rv<11 ) {
                b           = SnBitUtils::ReadFrom(b, fStreamHiLoPlas);
#ifdef DEBUG
                printf("shilo=%d\r\n",(int)fStreamHiLoPlas);
#endif
            }
#endif // ATWD4CH
            b           = SnBitUtils::ReadFrom(b, fWvLoseLSB);
#ifdef DEBUG
            printf("lsb=%hhu\r\n",fWvLoseLSB);
#endif
            b           = SnBitUtils::ReadFrom(b, fWvLoseMSB);
#ifdef DEBUG
            printf("msb=%hhu\r\n",fWvLoseMSB);
#endif
            b           = SnBitUtils::ReadFrom(b, fWvBaseline);
#ifdef DEBUG
            printf("bl=%hu\r\n",fWvBaseline);
#endif
            if (Rv<11) {
                b           = SnBitUtils::ReadFrom(b, fDatPackType);
#ifdef DEBUG
                printf("dp=%hhu\r\n",fDatPackType);
#endif
            }
            uint16_t* dc = &(fDAC[0][0]);
            const uint8_t ntotdacs = GetTotDacsForIOVers(Rv);
#ifdef DEBUG
            printf("ntotdacs=%hhu\r\n",ntotdacs);
#endif
            for (uint16_t i=0; i<ntotdacs; i++, dc++) {
                b       = SnBitUtils::ReadFrom(b, *dc);
#ifdef DEBUG
                printf("dac[%hu]=%hu\r\n",i,*dc);
#endif
            }
#if CHIPBOARD==ATWD4CH
            if ( IsIOversForATWD(Rv) ) {
                b           = SnBitUtils::ReadFrom(b, fNumPlas);
#ifdef DEBUG
                printf("npla=%hhu\r\n",fNumPlas);
#endif
                uint16_t* pl = &(fPLA[0]);
                for (uint8_t j=0; j<fNumPlas; j++, pl++) {
                    b       = SnBitUtils::ReadFrom(b, *pl);
#ifdef DEBUG
                    printf("pla[%hhu]=%hu\r\n",j,*pl);
#endif
                }
            }
#endif // ATWD4CH
            b           = SnBitUtils::ReadFrom(b, fNumCardsMajLog);
#ifdef DEBUG
            printf("mj=%hhu\r\n",fNumCardsMajLog);
#endif
            if (Rv<11) {
                uint8_t e(0);
                b       = SnBitUtils::ReadFrom(b, e);
                fEnableThermTrig = e;
            } else {
                b       = SnBitUtils::ReadFrom(b, fEnableThermTrig);
            }
#ifdef DEBUG
            printf("thm=%d\r\n",(int)fEnableThermTrig);
#endif
            if (Rv>3) {
                b       = SnBitUtils::ReadFrom(b, fForceTrigPeriod);
            } else {
                uint16_t ftrg(0);
                b       = SnBitUtils::ReadFrom(b, ftrg);
                fForceTrigPeriod = ftrg;
            }
#ifdef DEBUG
            printf("force=%g\r\n",fForceTrigPeriod);
#endif
            b           = SnBitUtils::ReadFrom(b, fHeartBeatPeriod);
#ifdef DEBUG
            printf("heart=%hu\r\n",fHeartBeatPeriod);
#endif
            if (Rv<11) {
                uint8_t ampson(0);
                b           = SnBitUtils::ReadFrom(b, ampson);
#ifdef DEBUG
                printf("amps=%hhu (IGNORED)\r\n",ampson);
#endif
            }
            b           = SnBitUtils::ReadFrom(b, fEvtThrtlPeriodMs);
#ifdef DEBUG
            printf("throt=%hu\r\n",fEvtThrtlPeriodMs);
#endif
            b           = SnBitUtils::ReadFrom(b, fPowerMode);
#ifdef DEBUG
            printf("pow=%hhu\r\n",fPowerMode);
#endif
            if (Rv<6) {
                b       = SnBitUtils::ReadFrom(b, fBatVoltToLowPwr);
                fBatVoltFromLowPwr = 1.1*fBatVoltToLowPwr;
            } else {
                b       = SnBitUtils::ReadFrom(b, fBatVoltToLowPwr);
                b       = SnBitUtils::ReadFrom(b, fBatVoltFromLowPwr);
            }
#ifdef DEBUG
                printf("batlow(to,from)=(%hu,%hu)\r\n",fBatVoltToLowPwr,fBatVoltFromLowPwr);
#endif
            if (Rv>2) {
                b       = SnBitUtils::ReadFrom(b, fVoltCheckPeriod);
            } else {
                fVoltCheckPeriod = 600u;
            }
#ifdef DEBUG
                printf("vltchk=%hu\r\n",fVoltCheckPeriod);
#endif
            b           = SnBitUtils::ReadFrom(b, fCommWinPeriod);
#ifdef DEBUG
            printf("cmper=%u\r\n",fCommWinPeriod);
#endif
            b           = SnBitUtils::ReadFrom(b, fCommWinDuration);
#ifdef DEBUG
            printf("cmdur=%u\r\n",fCommWinDuration);
#endif
            b           = SnBitUtils::ReadFrom(b, fCommSendData);
#ifdef DEBUG
            printf("send=%hd\r\n",fCommSendData);
#endif
            b           = SnBitUtils::ReadFrom(b, fCommWinPrdLowPwr);
#ifdef DEBUG
            printf("cmperlp=%u\r\n",fCommWinPrdLowPwr);
#endif
            b           = SnBitUtils::ReadFrom(b, fCommWinDurLowPwr);
#ifdef DEBUG
            printf("cmdurlp=%u\r\n",fCommWinDurLowPwr);
#endif
            if (Rv>6) {
                if (Rv>10) {
                    // Irid first
                    b    = SnBitUtils::ReadFrom(b, fCommWinConnTOMins[IndexOfDataStream(kIrid)]);
                    b    = SnBitUtils::ReadFrom(b, fCommWinListTOMins[IndexOfDataStream(kIrid)]);
                    // Afar next
                    b    = SnBitUtils::ReadFrom(b, fCommWinConnTOMins[IndexOfDataStream(kAfar)]);
                    b    = SnBitUtils::ReadFrom(b, fCommWinListTOMins[IndexOfDataStream(kAfar)]);
                } else {
                    // older versions: set them all the same
                    uint8_t cto, lto;
                    b    = SnBitUtils::ReadFrom(b, cto);
                    b    = SnBitUtils::ReadFrom(b, lto);
                    for (uint8_t i=0; i<kNumDatStreams; ++i) {
                        fCommWinConnTOMins[i] = cto;
                        fCommWinListTOMins[i] = lto;
                    }
                }
            } else {
                for (uint8_t i=0; i<kNumDatStreams; ++i) {
                    fCommWinConnTOMins[i] = GetDefaultConnTOMinOf(GetDataStreamForIndex(i));
                    fCommWinListTOMins[i] = GetDefaultListTOMinOf(GetDataStreamForIndex(i));
                }
            }
#ifdef DEBUG
            for (uint8_t i=0; i<kNumDatStreams; ++i) {
                printf("data stream %hhu (%s): connectTO=%hhu, listenTO=%hhu\r\n",
                    i, GetDataStreamNameOfIdx(i),
                    fCommWinConnTOMins[i], fCommWinListTOMins[i]);
            }
#endif
            if (Rv>2) {
                if (Rv<11) {
                    b       = SnBitUtils::ReadFrom(b, fRemoteServer, kIPLen);
                } else {
                    // read the numerical value and convert to string
                    uint32_t rip(0);
                    b       = SnBitUtils::ReadFrom(b, rip);
                    GetIpStrFromVal(rip, fRemoteServer);
                }
                if (strncmp(fRemoteServer, kDefIPflag,kIPLen)==0) {
                    SetDefaultRemoteServ();
                }
#ifdef DEBUG
                printf("rserv=%s\r\n",fRemoteServer);
#endif
                b       = SnBitUtils::ReadFrom(b, fRemotePort);
                if (fRemotePort==0) {
                    SetDefaultRemotePort();
                }
#ifdef DEBUG
                printf("rport=%hu\r\n",fRemotePort);
#endif
                if (Rv<11) {
                    b       = SnBitUtils::ReadFrom(b, fMbedIP, kIPLen);
                } else {
                    // read the numerical value and convert to string
                    uint32_t rip(0);
                    b       = SnBitUtils::ReadFrom(b, rip);
                    GetIpStrFromVal(rip, fMbedIP);
                }
                if (strncmp(fMbedIP, kDefIPflag,kIPLen)==0) {
                    SetDefaultMbedIP();
                }
#ifdef DEBUG
                printf("mbedip=%s\r\n",fMbedIP);
#endif
                if (Rv<11) {
                    b       = SnBitUtils::ReadFrom(b, fMbedMask, kIPLen);
                } else {
                    // read the numerical value and convert to string
                    uint32_t rip(0);
                    b       = SnBitUtils::ReadFrom(b, rip);
                    GetIpStrFromVal(rip, fMbedMask);
                }
                if (strncmp(fMbedMask, kDefIPflag,kIPLen)==0) {
                    SetDefaultMaskIP();
                }
#ifdef DEBUG
                printf("mbedmask=%s\r\n",fMbedMask);
#endif
                if (Rv<11) {
                    b       = SnBitUtils::ReadFrom(b, fMbedGate, kIPLen);
                } else {
                    // read the numerical value and convert to string
                    uint32_t rip(0);
                    b       = SnBitUtils::ReadFrom(b, rip);
                    GetIpStrFromVal(rip, fMbedGate);
                }
                if (strncmp(fMbedGate, kDefIPflag,kIPLen)==0) {
                    SetDefaultGateIP();
                }
#ifdef DEBUG
                printf("mbedgate=%s\r\n",fMbedGate);
#endif
            } else {
                SetDefaultIPs();
            }
            b           = SnBitUtils::ReadFrom(b, fWatchDogPeriod);
#ifdef DEBUG
            printf("watch=%u\r\n",fWatchDogPeriod);
#endif
#if CHIPBOARD!=ATWD4CH
            if ( (IsIOversForATWD(Rv)==false) && (Rv>9) ) {
                b       = SnBitUtils::ReadFrom(b, fTempCheckPeriod);
            }
#ifdef DEBUG
            printf("temp check period=%hhd\r\n", fTempCheckPeriod);
#endif
#endif
            if (Rv>10) {
                b       = SnBitUtils::ReadFrom(b, fSnglFreqRatio);
#ifdef DEBUG
                printf("single freq ratio=%hhd\r\n", fSnglFreqRatio);
#endif
                
                b       = SnBitUtils::ReadFrom(b, fL1Scaledown);
#ifdef DEBUG
                printf("L1 scaledown=%hhd\r\n", fL1Scaledown);
#endif
            }
#if CHIPBOARD==ATWD4CH
            if ( IsIOversForATWD(Rv) ) {
                if (fStreamHiLoPlas!=0) {
                    uint16_t hi, lo;
                    for (uint8_t j=0; j<fNumPlas; j++) {
                        b   = SnBitUtils::ReadFrom(b, hi);
#ifdef DEBUG
                        printf("hi=%hu\r\n",hi);
#endif
                        b   = SnBitUtils::ReadFrom(b, lo);
#ifdef DEBUG
                            printf("lo=%hu\r\n",lo);
#endif
                    // don't save these
                    }
                }
            }
#endif // ATWD4CH
        } else {
            // trying to read an invalid config version
#ifdef DEBUG
            printf("Cannot accept config version [%hhu]. "
                   "Expect [%hhu].",Rv,SnConfigFrame::kIOVers);
#endif
        }
        
        if (fgApplySafetyNets) {
            ApplySafetyNets();
        }
        
        // reset the SD card init cache, in case the SD ignore run mode changed
        SetSDNeedToInitFlag();
        
#ifdef DEBUG
        printf("read from done\r\n");
#endif
    }
    
    template <class T>
    void WriteTo(T& b) const {
        // no check on the length of the buf is done here
        // that should be done already
        //
        // must match ReadFromBuf
        //
        // intentionally not writing mac address here, so we don't have to read it in
        
        b           = SnBitUtils::WriteTo(b, kIOVers); // i/o version
        
        // first write the label, then explicitly write the trailing \0
        // so it's for sure always there
        /* legacy code when io vers < 11
        // account for the ending \0
        uint32_t llen = strlen(fLabel);
        static const uint32_t maxllen = kConfLblLen-1;
        if (llen > maxllen) {
            llen = maxllen;
        }        
        b           = SnBitUtils::WriteTo(b, llen+1); // strlen + \0
        b           = SnBitUtils::WriteTo(b, fLabel, llen);
        */
        b           = SnBitUtils::WriteTo(b, fLabel, kConfLblLenNoStrTerm);            
        b           = SnBitUtils::WriteTo(b, char('\0'));
        b           = SnBitUtils::WriteTo(b, fConfTime);
        b           = SnBitUtils::WriteTo(b, fRun);
        b           = SnBitUtils::WriteTo(b, fFirstSeq);
        b           = SnBitUtils::WriteTo(b, fEvtsPerSeq); // changed to uint16 with V11+
        b           = SnBitUtils::WriteTo(b, fRunMode);    // changed to uint16 with V11+
/* - the option is no longer written to the config
#if CHIPBOARD==ATWD4CH
        if ( IsIOversForATWD(kIOVers) ) {
            b           = SnBitUtils::WriteTo(b, fStreamHiLoPlas);
        }
#endif
*/
        b           = SnBitUtils::WriteTo(b, fWvLoseLSB);
        b           = SnBitUtils::WriteTo(b, fWvLoseMSB);
        b           = SnBitUtils::WriteTo(b, fWvBaseline);
        //b           = SnBitUtils::WriteTo(b, fDatPackType); // removed vers 11+
        const uint16_t* dc = &(fDAC[0][0]);
        const uint8_t ntotdacs = GetTotDacsForIOVers(kIOVers);
        for (uint16_t i=0; i<ntotdacs; i++, dc++) {
            b       = SnBitUtils::WriteTo(b, *dc);
        }
#if CHIPBOARD==ATWD4CH
        if ( IsIOversForATWD(kIOVers) ) {
            b           = SnBitUtils::WriteTo(b, fNumPlas);
            const uint16_t* pl = &(fPLA[0]);
            for (uint8_t j=0; j<fNumPlas; j++, pl++) {
                b       = SnBitUtils::WriteTo(b, *pl);
            }
        }
#endif
        b           = SnBitUtils::WriteTo(b, fNumCardsMajLog);
        b           = SnBitUtils::WriteTo(b, fEnableThermTrig); // 16 bits in vers 11+
        b           = SnBitUtils::WriteTo(b, fForceTrigPeriod);
        b           = SnBitUtils::WriteTo(b, fHeartBeatPeriod);
        //b           = SnBitUtils::WriteTo(b, fAmpsOn); removed vers 11+
        b           = SnBitUtils::WriteTo(b, fEvtThrtlPeriodMs);
        b           = SnBitUtils::WriteTo(b, fPowerMode);
        b           = SnBitUtils::WriteTo(b, fBatVoltToLowPwr);
        b           = SnBitUtils::WriteTo(b, fBatVoltFromLowPwr);
        b           = SnBitUtils::WriteTo(b, fVoltCheckPeriod);
        b           = SnBitUtils::WriteTo(b, fCommWinPeriod);
        b           = SnBitUtils::WriteTo(b, fCommWinDuration);
        b           = SnBitUtils::WriteTo(b, fCommSendData);
        b           = SnBitUtils::WriteTo(b, fCommWinPrdLowPwr);
        b           = SnBitUtils::WriteTo(b, fCommWinDurLowPwr);
        // vers 11+, separate conn/listen timeouts for Irid and Afar
        b           = SnBitUtils::WriteTo(b, fCommWinConnTOMins[IndexOfDataStream(kIrid)]);
        b           = SnBitUtils::WriteTo(b, fCommWinListTOMins[IndexOfDataStream(kIrid)]);
        b           = SnBitUtils::WriteTo(b, fCommWinConnTOMins[IndexOfDataStream(kAfar)]);
        b           = SnBitUtils::WriteTo(b, fCommWinListTOMins[IndexOfDataStream(kAfar)]);
        // with vers 11+, the numerical values are stored instead of strings (saves many bytes)
        b           = SnBitUtils::WriteTo(b, GetIpValFromStr(fRemoteServer) );
        b           = SnBitUtils::WriteTo(b, fRemotePort);
        b           = SnBitUtils::WriteTo(b, GetIpValFromStr(fMbedIP) );
        b           = SnBitUtils::WriteTo(b, GetIpValFromStr(fMbedMask) );
        b           = SnBitUtils::WriteTo(b, GetIpValFromStr(fMbedGate) );
        b           = SnBitUtils::WriteTo(b, fWatchDogPeriod);
#if CHIPBOARD!=ATWD4CH
        if ( (IsIOversForATWD(kIOVers)==false) && (kIOVers>9) ) {
            b       = SnBitUtils::WriteTo(b, fTempCheckPeriod);
        }
#endif
        b           = SnBitUtils::WriteTo(b, fSnglFreqRatio); // with vers 11+
        b           = SnBitUtils::WriteTo(b, fL1Scaledown);   // with vers 11+

#if CHIPBOARD==ATWD4CH
        if ( IsIOversForATWD(kIOVers) ) {
            if (fStreamHiLoPlas!=0) {
                const uint16_t* pl = &(fPLA[0]);
                uint16_t hi, lo;
                for (uint8_t j=0; j<fNumPlas; j++, pl++) {
                    GetHiLoPlas(*pl, hi, lo);
                    b   = SnBitUtils::WriteTo(b, hi);
                    b   = SnBitUtils::WriteTo(b, lo);
                }
            }
        }
#endif
    }
    
    bool ReadFromFile(const char* cfile);
    bool WriteToFile(const char* cfile) const;
    
#if defined(USE_INTERFACE_CHIP) || defined(LOAD_DEFAULT_CONFIG_FROM_SD)
    void SetFromDefaultConfFile() {
        if (ReadFromFile(kDefConfFile)==false) {
            // couldn't get setharddefaults. use hardcoded version.
            // (reset in case a few parameters from the file
            // got assigned)
            SetHardDefaults();
        }
    }
#endif
    
    void Reset() {
        memset(fLabel, 0, sizeof(char)*kConfLblLen);
        SetHardDefaults();
#ifdef LOAD_DEFAULT_CONFIG_IMMEDIATELY
        SetFromDefaultConfFile();
#endif        
#ifdef DEBUG
        printf("config reset to %s\r\n",fLabel);
#endif
    }
    
    uint32_t    SizeOf(const uint8_t rv=SnConfigFrame::kIOVers) const {
        // returns the num of bytes needed to stream this object
        // = size of member vars + 1 for i/o version + extra PLA strings (maybe)
        //   + length of label string if rv<11
        const uint8_t lbllen = (rv<11) ? strlen(fLabel) : kConfLblLen;
#if CHIPBOARD==ATWD4CH
        return SizeOf(rv, fStreamHiLoPlas!=0, fNumPlas, lbllen);
#else // SST
        return SizeOf(rv, false,              0,        lbllen);
#endif
    }
    
    static void     SetMacAddress();
    static uint64_t GetMacAddress() {
#ifdef DEBUG
        printf("GetMacAddress call: fgMacAdr=%012llX\r\n", fgMacAdr>>16); // 64 -> 48 bits
#endif
        if (fgMacAdr==0) {
#ifdef DEBUG
            printf("Calling SetMacAddress()\r\n");
#endif
            SetMacAddress();
        }
#ifdef DEBUG
        printf("GetMacAddress return: fgMacAdr=%012llX\r\n", fgMacAdr>>16); // 64 -> 48 bits
#endif
        return fgMacAdr;
    }
    
    static uint32_t GetLabelMaxLen() { return kConfLblLen; }

    static void GetHiLoPlas(const uint16_t pla,
                            uint16_t& hiPla,
                            uint16_t& loPla,
                            const bool r2l=false);

    static
    void GetIpStrFromVal(const uint32_t ip,
                         char(& str)[kIPLen]);

    static
    uint32_t GetIpValFromStr(const char(& str)[kIPLen]);

    static
    EDatPackBit GetDataStreamForIndex(const uint8_t idx) {
        switch (idx) {
            case 0: return kSDcard;
            case 1: return kIrid;
            case 2: return kAfar;
            case 3: return kUSB;
            default:
#ifdef DEBUG
                printf("**** unknown stream idx [%hhu]", idx);
#endif
                return kSDcard;
        };
    }

    static
    uint8_t IndexOfDataStream(const SnConfigFrame::EDatPackBit b) {
        switch (b) {
            case kSDcard: return 0u;
            case kIrid:   return 1u;
            case kAfar:   return 2u;
            case kUSB:    return 3u;
            default:
#ifdef DEBUG
                printf("**** unknown stream bit [%u]", static_cast<uint32_t>(b));
#endif
                return 0;
        };
    }

    static
    uint8_t GetDefaultConnTOMinOf(const SnConfigFrame::EDatPackBit b) {
        switch (b) {
            case kSDcard: return 1u;
            case kIrid:   return 3u;
            case kAfar:   return 1u;
            case kUSB:    return 1u;
            default:
#ifdef DEBUG
                printf("**** unknown stream big [%u]", 
                    static_cast<uint32_t>(b));
#endif
                return 3u;
        };
    }

    static
    uint8_t GetDefaultListTOMinOf(const SnConfigFrame::EDatPackBit b) {
        return GetDefaultConnTOMinOf(b);
    }

    static
    const char* GetDataStreamName(const SnConfigFrame::EDatPackBit b) {
        switch (b) {
            case kSDcard: return "SDcard";
            case kIrid:   return "Iridium";
            case kAfar:   return "Afar";
            case kUSB:    return "USB";
            default:
#ifdef DEBUG
                printf("**** unknown stream bit [%u]", static_cast<uint32_t>(b));
#endif
                return NULL;
        };
    }

    static
    const char* GetDataStreamNameOfIdx(const uint8_t idx) {
        switch (idx) {
            case 0u: return GetDataStreamName(kSDcard);
            case 1u: return GetDataStreamName(kIrid);
            case 2u: return GetDataStreamName(kAfar);
            case 3u: return GetDataStreamName(kUSB);
            default: 
#ifdef DEBUG
                printf("**** unknown stream index [%hhu]", idx);
#endif
                return NULL;
        };
    }

};

#endif // SN_SnConfigFrame