Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

SnConfigFrame.h

Committer:
uci1
Date:
2015-10-31
Revision:
85:3ced48ef94c5
Parent:
84:80b15993944e
Child:
114:554fa3a956b4

File content as of revision 85:3ced48ef94c5:

#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)
    
#ifdef USE_INTERFACE_CHIP
    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   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
   };
    
    // 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==SST4CH
    // 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 {
            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;
        }
        uint32_t sz = maxsize;
        if (rv<11) {
            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     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==SST4CH
    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==SST4CH
            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==SST4CH
        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;
    
    void Reset() {
        memset(fLabel, 0, sizeof(char)*kConfLblLen);
        SetHardDefaults();
#ifdef USE_INTERFACE_CHIP
        if (ReadFromFile(kDefConfFile)==false) {
            // couldn't get default. use hardcoded version.
            // (reset in case a few parameters from the file
            // got assigned)
            SetHardDefaults();
        }
#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() {
        if (fgMacAdr==0) {
            SetMacAddress();
        }
        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