SX1278 RA-01, RA-02 LoRa library

  1. This code is deprecated. Use this: https://github.com/luk6xff/DevLibs/tree/master/LORA instead.

sx1278.c

Committer:
igbt6
Date:
2019-11-16
Revision:
0:4e8ef5758455

File content as of revision 0:4e8ef5758455:

/**
 *  @brief:  Implementation of a SX1278 radio functions
 *  @author: luk6xff based on SEMTCH code: https://github.com/Lora-net/LoRaMac-node
 *  @email:  luszko@op.pl
 *  @date:   2019-11-15
 */
#include "registers.h"
#include "sx1278.h"

#include <math.h>
#include <string.h>

/**
 * ============================================================================
 * @brief Private global constants
 * ============================================================================
 */
//-----------------------------------------------------------------------------
/**
 * Precomputed FSK bandwidth registers values
 */
static const BandwidthMap_t SX1278FskBandwidths[] =
{
    { 2600  , 0x17 },
    { 3100  , 0x0F },
    { 3900  , 0x07 },
    { 5200  , 0x16 },
    { 6300  , 0x0E },
    { 7800  , 0x06 },
    { 10400 , 0x15 },
    { 12500 , 0x0D },
    { 15600 , 0x05 },
    { 20800 , 0x14 },
    { 25000 , 0x0C },
    { 31300 , 0x04 },
    { 41700 , 0x13 },
    { 50000 , 0x0B },
    { 62500 , 0x03 },
    { 83333 , 0x12 },
    { 100000, 0x0A },
    { 125000, 0x02 },
    { 166700, 0x11 },
    { 200000, 0x09 },
    { 250000, 0x01 },
    { 300000, 0x00 }, // Invalid Bandwidth
};

//-----------------------------------------------------------------------------
/**
 * @brief Precomputed LORA bandwidth registers values
 */
static const BandwidthMap_t SX1278LoRaBandwidths[] =
{
    {   7800, 0 }, //  7.8 kHz requires TCXO
    {  10400, 1 }, // 10.4 kHz requires TCXO
    {  15600, 2 }, // 15.6 kHz requires TCXO
    {  20800, 3 }, // 20.8 kHz requires TCXO
    {  31250, 4 }, // 31.25 kHz requires TCXO
    {  41700, 5 }, // 41.7 kHz requires TCXO
    {  62500, 6 }, // 62.5 kHz requires TCXO
    { 125000, 7 }, // 125 kHz the LoRa protocol default
    { 250000, 8 }, // 250 kHz
    { 500000, 9 }, // 500 kHz
    { 600000, 10 },// Invalid Bandwidth, reserved
 };

//-----------------------------------------------------------------------------
/**
 * @brief Radio hardware registers initialization definition
 */
static const RadioRegisters_t SX1278RadioRegsInit[] =
{                                                 \
    { MODEM_FSK , REG_LNA                , 0x23 },\
    { MODEM_FSK , REG_RXCONFIG           , 0x1E },\
    { MODEM_FSK , REG_RSSICONFIG         , 0xD2 },\
    { MODEM_FSK , REG_AFCFEI             , 0x01 },\
    { MODEM_FSK , REG_PREAMBLEDETECT     , 0xAA },\
    { MODEM_FSK , REG_OSC                , 0x07 },\
    { MODEM_FSK , REG_SYNCCONFIG         , 0x12 },\
    { MODEM_FSK , REG_SYNCVALUE1         , 0xC1 },\
    { MODEM_FSK , REG_SYNCVALUE2         , 0x94 },\
    { MODEM_FSK , REG_SYNCVALUE3         , 0xC1 },\
    { MODEM_FSK , REG_PACKETCONFIG1      , 0xD8 },\
    { MODEM_FSK , REG_FIFOTHRESH         , 0x8F },\
    { MODEM_FSK , REG_IMAGECAL           , 0x02 },\
    { MODEM_FSK , REG_DIOMAPPING1        , 0x00 },\
    { MODEM_FSK , REG_DIOMAPPING2        , 0x30 },\
    { MODEM_LORA, REG_LR_PAYLOADMAXLENGTH, 0x40 },\
};


/**
 * ============================================================================
 * @brief Private functions prototypes
 * ============================================================================
 */

/**
 * @brief Performs the Rx chain calibration for LF and HF bands
 * @note Must be called just after the reset so all registers are at their
 *         default values
 */
static void RxChainCalibration(void);

/**
 * Returns the known FSK bandwidth registers value
 *
 * @param [IN] bandwidth Bandwidth value in Hz
 * @retval regValue Bandwidth register value.
 */
static uint8_t GetFskBandwidthRegValue(uint32_t bandwidth);

/**
 * Returns the known LORA bandwidth registers value
 *
 * @param [IN] bandwidth Bandwidth value in Hz
 * @retval regValue Bandwidth register value.
 */
static uint8_t GetLoRaBandwidthRegValue(uint32_t bandwidth);


/**
 * @brief DIO 0 IRQ callback
 */
static void OnDio0Irq();

/**
 * @brief DIO 1 IRQ callback
 */
static void OnDio1Irq();

/**
 * @brief DIO 2 IRQ callback
 */
static void OnDio2Irq();

/**
 * @brief DIO 3 IRQ callback
 */
static void OnDio3Irq();

/**
 * @brief DIO 4 IRQ callback
 */
static void OnDio4Irq();

/**
 * @brief DIO 5 IRQ callback
 */
static void OnDio5Irq();

/**
 * @brief Tx & Rx timeout timer callback
 */
static void OnTimeoutIrq();



/**
 * ============================================================================
 * @brief Private global variables
 * ============================================================================
 */

/**
 * Radio callbacks variable
 */
static RadioEvents_t* RadioEvents;

/**
 * Reception buffer
 */
static uint8_t RxTxBuffer[RX_BUFFER_SIZE];

/**
 * Hardware DIO IRQ functions
 */
DioIrqHandler dioIrq[] = { OnDio0Irq, OnDio1Irq,
                            OnDio2Irq, OnDio3Irq,
                            OnDio4Irq, NULL };

/**
 * Radio settings
 */
RadioSettings_t settings;



/**
 * ============================================================================
 * @brief Public functions definitions
 * ============================================================================
 */
//-----------------------------------------------------------------------------
bool SX1278Init(RadioEvents_t *events)
{
    RadioEvents = events;

    if (SX1278Read(REG_VERSION) == 0x00)
    {
        return false;
    }
    
    SX1278Reset();
    RxChainCalibration();
    SX1278SetOpMode(RF_OPMODE_SLEEP);

    SX1278IoIrqInit(dioIrq);
    SX1278RadioRegistersInit();
    SX1278SetModem(MODEM_FSK);
    settings.State = RF_IDLE;
    return true;
}

//-----------------------------------------------------------------------------
void SX1278RadioRegistersInit()
{
    uint8_t i = 0;
    for(i = 0; i < sizeof(SX1278RadioRegsInit) / sizeof(RadioRegisters_t); i++)
    {
        SX1278SetModem(SX1278RadioRegsInit[i].Modem);
        SX1278Write(SX1278RadioRegsInit[i].Addr, SX1278RadioRegsInit[i].Value);
    }    
}

//-----------------------------------------------------------------------------
RadioState_t SX1278GetStatus(void)
{
    return settings.State;
}

//-----------------------------------------------------------------------------
void SX1278SetChannel(uint32_t freq)
{
    settings.Channel = freq;
    freq = (uint32_t)((double)freq / (double)FREQ_STEP);
    SX1278Write(REG_FRFMSB, (uint8_t)((freq >> 16) & 0xFF));
    SX1278Write(REG_FRFMID, (uint8_t)((freq >> 8) & 0xFF));
    SX1278Write(REG_FRFLSB, (uint8_t)(freq & 0xFF));
}

//-----------------------------------------------------------------------------
bool SX1278IsChannelFree(RadioModems_t modem, uint32_t freq, int16_t rssiThresh, uint32_t maxCarrierSenseTime)
{
    // TODO handle carrierSenseTime
    int16_t rssi = 0;

    SX1278SetModem(modem);

    SX1278SetChannel(freq);

    SX1278SetOpMode(RF_OPMODE_RECEIVER);

    SX1278DelayMs(1);

    rssi = SX1278GetRssi(modem);

    SX1278SetSleep();
    if (rssi > rssiThresh)
    {
        return false;
    }
    return true;
}

//-----------------------------------------------------------------------------
uint32_t SX1278Random(void)
{
    uint8_t i;
    uint32_t rnd = 0;

    /*
     * Radio setup for random number generation
     */
    // Set LoRa modem ON
    SX1278SetModem(MODEM_LORA);

    // Disable LoRa modem interrupts
    SX1278Write(REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT |
                  RFLR_IRQFLAGS_RXDONE |
                  RFLR_IRQFLAGS_PAYLOADCRCERROR |
                  RFLR_IRQFLAGS_VALIDHEADER |
                  RFLR_IRQFLAGS_TXDONE |
                  RFLR_IRQFLAGS_CADDONE |
                  RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                  RFLR_IRQFLAGS_CADDETECTED);

    // Set radio in continuous reception
    SX1278SetOpMode(RF_OPMODE_RECEIVER);

    for(i = 0; i < 32; i++)
    {
        SX1278DelayMs(1);
        // Unfiltered RSSI value SX1278Reading. Only takes the LSB value
        rnd |= ((uint32_t)SX1278Read(REG_LR_RSSIWIDEBAND) & 0x01) << i;
    }

    SX1278SetSleep();

    return rnd;
}

//-----------------------------------------------------------------------------
void SX1278SetRxConfig(RadioModems_t modem, uint32_t bandwidth,
                         uint32_t datarate, uint8_t coderate,
                         uint32_t bandwidthAfc, uint16_t preambleLen,
                         uint16_t symbTimeout, bool fixLen,
                         uint8_t payloadLen,
                         bool crcOn, bool freqHopOn, uint8_t hopPeriod,
                         bool iqInverted, bool rxContinuous)
{
    SX1278SetModem(modem);

    switch (modem)
    {
        case MODEM_FSK:
        {
            settings.Fsk.Bandwidth = bandwidth;
            settings.Fsk.Datarate = datarate;
            settings.Fsk.BandwidthAfc = bandwidthAfc;
            settings.Fsk.FixLen = fixLen;
            settings.Fsk.PayloadLen = payloadLen;
            settings.Fsk.CrcOn = crcOn;
            settings.Fsk.IqInverted = iqInverted;
            settings.Fsk.RxContinuous = rxContinuous;
            settings.Fsk.PreambleLen = preambleLen;
            settings.Fsk.RxSingleTimeout = symbTimeout * ((1.0 / (double)datarate) * 8.0) * 1000;

            datarate = (uint16_t)((double)XTAL_FREQ / (double)datarate);
            SX1278Write(REG_BITRATEMSB, (uint8_t)(datarate >> 8));
            SX1278Write(REG_BITRATELSB, (uint8_t)(datarate & 0xFF));

            SX1278Write(REG_RXBW, GetFskBandwidthRegValue(bandwidth));
            SX1278Write(REG_AFCBW, GetFskBandwidthRegValue(bandwidthAfc));

            SX1278Write(REG_PREAMBLEMSB, (uint8_t)((preambleLen >> 8) & 0xFF));
            SX1278Write(REG_PREAMBLELSB, (uint8_t)(preambleLen & 0xFF));

            if (fixLen == 1)
            {
                SX1278Write(REG_PAYLOADLENGTH, payloadLen);
            }
            else
            {
                SX1278Write(REG_PAYLOADLENGTH, 0xFF); // Set payload length to the maximum
            }
            
            SX1278Write(REG_PACKETCONFIG1,
                         (SX1278Read(REG_PACKETCONFIG1) &
                           RF_PACKETCONFIG1_CRC_MASK &
                           RF_PACKETCONFIG1_PACKETFORMAT_MASK) |
                           ((fixLen == 1) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE) |
                           (crcOn << 4));
            SX1278Write(REG_PACKETCONFIG2, (SX1278Read(REG_PACKETCONFIG2) | RF_PACKETCONFIG2_DATAMODE_PACKET));
        }
        break;
        case MODEM_LORA:
        {
            if (bandwidth > 11) // specified in Hz, needs mapping
            {
            	bandwidth = GetLoRaBandwidthRegValue(bandwidth);
            }
            if (bandwidth > LORA_BANDWIDTH_500kHz)
            {
                // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported
                while(1);
            }
            settings.LoRa.Bandwidth = bandwidth;
            settings.LoRa.Datarate = datarate;
            settings.LoRa.Coderate = coderate;
            settings.LoRa.PreambleLen = preambleLen;
            settings.LoRa.FixLen = fixLen;
            settings.LoRa.PayloadLen = payloadLen;
            settings.LoRa.CrcOn = crcOn;
            settings.LoRa.FreqHopOn = freqHopOn;
            settings.LoRa.HopPeriod = hopPeriod;
            settings.LoRa.IqInverted = iqInverted;
            settings.LoRa.RxContinuous = rxContinuous;

            if (datarate > LORA_SF12)
            {
                datarate = LORA_SF12;
            }
            else if (datarate < LORA_SF6)
            {
                datarate = LORA_SF6;
            }

            if (((bandwidth == LORA_BANDWIDTH_125kHz) && ((datarate == LORA_SF11) || (datarate == LORA_SF12))) ||
               ((bandwidth == LORA_BANDWIDTH_250kHz) && (datarate == LORA_SF12)))
            {
                settings.LoRa.LowDatarateOptimize = 0x01;
            }
            else
            {
                settings.LoRa.LowDatarateOptimize = 0x00;
            }

            SX1278Write(REG_LR_MODEMCONFIG1,
                         (SX1278Read(REG_LR_MODEMCONFIG1) &
                           RFLR_MODEMCONFIG1_BW_MASK &
                           RFLR_MODEMCONFIG1_CODINGRATE_MASK &
                           RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK) |
                           (bandwidth << 4) | (coderate << 1) |
                           fixLen);

            SX1278Write(REG_LR_MODEMCONFIG2,
                         (SX1278Read(REG_LR_MODEMCONFIG2) &
                           RFLR_MODEMCONFIG2_SF_MASK &
                           RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK &
                           RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK) |
                           (datarate << 4) | (crcOn << 2) |
                           ((symbTimeout >> 8) & ~RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK));

            SX1278Write(REG_LR_MODEMCONFIG3,
                         (SX1278Read(REG_LR_MODEMCONFIG3) &
                           RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK) |
                           (settings.LoRa.LowDatarateOptimize << 3));

            SX1278Write(REG_LR_SYMBTIMEOUTLSB, (uint8_t)(symbTimeout & 0xFF));

            SX1278Write(REG_LR_PREAMBLEMSB, (uint8_t)((preambleLen >> 8) & 0xFF));
            SX1278Write(REG_LR_PREAMBLELSB, (uint8_t)(preambleLen & 0xFF));

            if (fixLen == 1)
            {
                SX1278Write(REG_LR_PAYLOADLENGTH, payloadLen);
            }

            if (settings.LoRa.FreqHopOn == true)
            {
                SX1278Write(REG_LR_PLLHOP, (SX1278Read(REG_LR_PLLHOP) & RFLR_PLLHOP_FASTHOP_MASK) | RFLR_PLLHOP_FASTHOP_ON);
                SX1278Write(REG_LR_HOPPERIOD, settings.LoRa.HopPeriod);
            }

            if ((bandwidth == LORA_BANDWIDTH_500kHz) && (settings.Channel > RF_MID_BAND_THRESH))
            {
                // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth
                SX1278Write(REG_LR_HIGHBWOPTIMIZE1, 0x02);
                SX1278Write(REG_LR_HIGHBWOPTIMIZE2, 0x64);
            }
            else if (bandwidth == LORA_BANDWIDTH_500kHz)
            {
                // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth
                SX1278Write(REG_LR_HIGHBWOPTIMIZE1, 0x02);
                SX1278Write(REG_LR_HIGHBWOPTIMIZE2, 0x7F);
            }
            else
            {
                // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth
                SX1278Write(REG_LR_HIGHBWOPTIMIZE1, 0x03);
            }

            if (datarate == LORA_SF6)
            {
                SX1278Write(REG_LR_DETECTOPTIMIZE,
                             (SX1278Read(REG_LR_DETECTOPTIMIZE) &
                               RFLR_DETECTIONOPTIMIZE_MASK) |
                               RFLR_DETECTIONOPTIMIZE_SF6);
                SX1278Write(REG_LR_DETECTIONTHRESHOLD,
                             RFLR_DETECTIONTHRESH_SF6);
            }
            else
            {
                SX1278Write(REG_LR_DETECTOPTIMIZE,
                            (SX1278Read(REG_LR_DETECTOPTIMIZE) &
                            RFLR_DETECTIONOPTIMIZE_MASK) |
                            RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12);
                SX1278Write(REG_LR_DETECTIONTHRESHOLD,
                            RFLR_DETECTIONTHRESH_SF7_TO_SF12);
            }
        }
        break;
    }
}

//-----------------------------------------------------------------------------
void SX1278SetTxConfig(RadioModems_t modem, int8_t power, uint32_t fdev,
                        uint32_t bandwidth, uint32_t datarate,
                        uint8_t coderate, uint16_t preambleLen,
                        bool fixLen, bool crcOn, bool freqHopOn,
                        uint8_t hopPeriod, bool iqInverted, uint32_t timeout)
{
    SX1278SetModem(modem);
    SX1278SetRfTxPower(power);

    switch (modem)
    {
        case MODEM_FSK:
        {
            settings.Fsk.Power = power;
            settings.Fsk.Fdev = fdev;
            settings.Fsk.Bandwidth = bandwidth;
            settings.Fsk.Datarate = datarate;
            settings.Fsk.PreambleLen = preambleLen;
            settings.Fsk.FixLen = fixLen;
            settings.Fsk.CrcOn = crcOn;
            settings.Fsk.IqInverted = iqInverted;
            settings.Fsk.TxTimeout = timeout;

            fdev = (uint16_t)((double)fdev / (double)FREQ_STEP);
            SX1278Write(REG_FDEVMSB, (uint8_t)(fdev >> 8));
            SX1278Write(REG_FDEVLSB, (uint8_t)(fdev & 0xFF));

            datarate = (uint16_t)((double)XTAL_FREQ / (double)datarate);
            SX1278Write(REG_BITRATEMSB, (uint8_t)(datarate >> 8));
            SX1278Write(REG_BITRATELSB, (uint8_t)(datarate & 0xFF));

            SX1278Write(REG_PREAMBLEMSB, (preambleLen >> 8) & 0x00FF);
            SX1278Write(REG_PREAMBLELSB, preambleLen & 0xFF);

            SX1278Write(REG_PACKETCONFIG1,
                        (SX1278Read(REG_PACKETCONFIG1) &
                        RF_PACKETCONFIG1_CRC_MASK &
                        RF_PACKETCONFIG1_PACKETFORMAT_MASK) |
                        ((fixLen == 1) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE) |
                        (crcOn << 4));
            SX1278Write(REG_PACKETCONFIG2, (SX1278Read(REG_PACKETCONFIG2) | RF_PACKETCONFIG2_DATAMODE_PACKET));
        }
        break;
        case MODEM_LORA:
        {
            settings.LoRa.Power = power;
            if (bandwidth > LORA_BANDWIDTH_500kHz)
            {
                // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported
                while(1);
            }

            settings.LoRa.Bandwidth = bandwidth;
            settings.LoRa.Datarate = datarate;
            settings.LoRa.Coderate = coderate;
            settings.LoRa.PreambleLen = preambleLen;
            settings.LoRa.FixLen = fixLen;
            settings.LoRa.FreqHopOn = freqHopOn;
            settings.LoRa.HopPeriod = hopPeriod;
            settings.LoRa.CrcOn = crcOn;
            settings.LoRa.IqInverted = iqInverted;
            settings.LoRa.TxTimeout = timeout;

            if (datarate > LORA_SF12)
            {
                datarate = LORA_SF12;
            }
            else if (datarate < LORA_SF6)
            {
                datarate = LORA_SF6;
            }
            if (((bandwidth == LORA_BANDWIDTH_125kHz) && ((datarate == LORA_SF11) || (datarate == LORA_SF12))) ||
                ((bandwidth == LORA_BANDWIDTH_250kHz) && (datarate == LORA_SF12)))
            {
                settings.LoRa.LowDatarateOptimize = 0x01;
            }
            else
            {
                settings.LoRa.LowDatarateOptimize = 0x00;
            }

            if (settings.LoRa.FreqHopOn == true)
            {
                SX1278Write(REG_LR_PLLHOP, (SX1278Read(REG_LR_PLLHOP) & RFLR_PLLHOP_FASTHOP_MASK) | RFLR_PLLHOP_FASTHOP_ON);
                SX1278Write(REG_LR_HOPPERIOD, settings.LoRa.HopPeriod);
            }

            SX1278Write(REG_LR_MODEMCONFIG1,
                         (SX1278Read(REG_LR_MODEMCONFIG1) &
                           RFLR_MODEMCONFIG1_BW_MASK &
                           RFLR_MODEMCONFIG1_CODINGRATE_MASK &
                           RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK) |
                           (bandwidth << 4) | (coderate << 1) |
                           fixLen);

            SX1278Write(REG_LR_MODEMCONFIG2,
                         (SX1278Read(REG_LR_MODEMCONFIG2) &
                           RFLR_MODEMCONFIG2_SF_MASK &
                           RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK) |
                           (datarate << 4) | (crcOn << 2));

            SX1278Write(REG_LR_MODEMCONFIG3,
                         (SX1278Read(REG_LR_MODEMCONFIG3) &
                           RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK) |
                           (settings.LoRa.LowDatarateOptimize << 3));

            SX1278Write(REG_LR_PREAMBLEMSB, (preambleLen >> 8) & 0x00FF);
            SX1278Write(REG_LR_PREAMBLELSB, preambleLen & 0xFF);

            if (datarate == LORA_SF6)
            {
                SX1278Write(REG_LR_DETECTOPTIMIZE,
                             (SX1278Read(REG_LR_DETECTOPTIMIZE) &
                               RFLR_DETECTIONOPTIMIZE_MASK) |
                               RFLR_DETECTIONOPTIMIZE_SF6);
                SX1278Write(REG_LR_DETECTIONTHRESHOLD,
                             RFLR_DETECTIONTHRESH_SF6);
            }
            else
            {
                SX1278Write(REG_LR_DETECTOPTIMIZE,
                             (SX1278Read(REG_LR_DETECTOPTIMIZE) &
                             RFLR_DETECTIONOPTIMIZE_MASK) |
                             RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12);
                SX1278Write(REG_LR_DETECTIONTHRESHOLD,
                             RFLR_DETECTIONTHRESH_SF7_TO_SF12);
            }
        }
        break;
    }
}

//-----------------------------------------------------------------------------
uint32_t SX1278TimeOnAir(RadioModems_t modem, uint8_t pktLen)
{
    uint32_t airTime = 0;

    switch (modem)
    {
    case MODEM_FSK:
        {
            airTime = rint((8 * (settings.Fsk.PreambleLen +
                                ((SX1278Read(REG_SYNCCONFIG) & ~RF_SYNCCONFIG_SYNCSIZE_MASK) + 1) +
                                ((settings.Fsk.FixLen == 0x01) ? 0.0 : 1.0) +
                                (((SX1278Read(REG_PACKETCONFIG1) & ~RF_PACKETCONFIG1_ADDRSFILTERING_MASK) != 0x00) ? 1.0 : 0) +
                                pktLen +
                                ((settings.Fsk.CrcOn == 0x01) ? 2.0 : 0)) /
                                settings.Fsk.Datarate) * 1000);
        }
        break;
    case MODEM_LORA:
        {
            double bw = 0.0;
            // NOTE: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported
            switch (settings.LoRa.Bandwidth)
            {
                case LORA_BANDWIDTH_7kHz: // 7.8 kHz
                    bw = 78e2;
                    break;
                case LORA_BANDWIDTH_10kHz: // 10.4 kHz
                    bw = 104e2;
                    break;
                case LORA_BANDWIDTH_15kHz: // 15.6 kHz
                    bw = 156e2;
                    break;
                case LORA_BANDWIDTH_20kHz: // 20.8 kHz
                    bw = 208e2;
                    break;
                case LORA_BANDWIDTH_31kHz: // 31.25 kHz
                    bw = 312e2;
                    break;
                case LORA_BANDWIDTH_41kHz: // 41.7 kHz
                    bw = 414e2;
                    break;
                case LORA_BANDWIDTH_62kHz: // 62.5 kHz
                    bw = 625e2;
                    break;
                case LORA_BANDWIDTH_125kHz: // 125 kHz
                    bw = 125e3;
                    break;
                case LORA_BANDWIDTH_250kHz: // 250 kHz
                    bw = 250e3;
                    break;
                case LORA_BANDWIDTH_500kHz: // 500 kHz
                    bw = 500e3;
                break;
            }

            // Symbol rate : time for one symbol (secs)
            double rs = bw / (1 << settings.LoRa.Datarate);
            double ts = 1 / rs;
            // time of preamble
            double tPreamble = (settings.LoRa.PreambleLen + 4.25) * ts;
            // Symbol length of payload and time
            double tmp = ceil((8 * pktLen - 4 * settings.LoRa.Datarate +
                                 28 + 16 * settings.LoRa.CrcOn -
                                 (settings.LoRa.FixLen ? 20 : 0)) /
                                 (double)(4 * (settings.LoRa.Datarate -
                                 ((settings.LoRa.LowDatarateOptimize > 0) ? 2 : 0)))) *
                                 (settings.LoRa.Coderate + 4);
            double nPayload = 8 + ((tmp > 0) ? tmp : 0);
            double tPayload = nPayload * ts;
            // Time on air
            double tOnAir = tPreamble + tPayload;
            // return ms secs
            airTime = floor(tOnAir * 1000 + 0.999);
        }
        break;
    }
    return airTime;
}

//-----------------------------------------------------------------------------
void SX1278Send(uint8_t *buffer, uint8_t size)
{
    uint32_t txTimeout = 0;

    switch (settings.Modem)
    {
        case MODEM_FSK:
        {
            settings.FskPacketHandler.NbBytes = 0;
            settings.FskPacketHandler.Size = size;

            if (settings.Fsk.FixLen == false)
            {
                SX1278WriteFifo((uint8_t*)&size, 1);
            }
            else
            {
                SX1278Write(REG_PAYLOADLENGTH, size);
            }

            if ((size > 0) && (size <= 64))
            {
                settings.FskPacketHandler.ChunkSize = size;
            }
            else
            {
                memcpy(RxTxBuffer, buffer, size);
                settings.FskPacketHandler.ChunkSize = 32;
            }

            // Write payload buffer
            SX1278WriteFifo(buffer, settings.FskPacketHandler.ChunkSize);
            settings.FskPacketHandler.NbBytes += settings.FskPacketHandler.ChunkSize;
            txTimeout = settings.Fsk.TxTimeout;
        }
        break;
        case MODEM_LORA:
        {
            if (settings.LoRa.IqInverted == true)
            {
                SX1278Write(REG_LR_INVERTIQ, ((SX1278Read(REG_LR_INVERTIQ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_ON));
                SX1278Write(REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON);
            }
            else
            {
                SX1278Write(REG_LR_INVERTIQ, ((SX1278Read(REG_LR_INVERTIQ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF));
                SX1278Write(REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF);
            }

            settings.LoRaPacketHandler.Size = size;

            // Initializes the payload size
            SX1278Write(REG_LR_PAYLOADLENGTH, size);

            // Full buffer used for Tx
            SX1278Write(REG_LR_FIFOTXBASEADDR, 0);
            SX1278Write(REG_LR_FIFOADDRPTR, 0);

            // FIFO operations can not take place in SX1278SetSleep mode
            if ((SX1278Read(REG_OPMODE) & ~RF_OPMODE_MASK) == RF_OPMODE_SLEEP)
            {
                SX1278SetStandby();
                SX1278DelayMs(1);
            }
            // SX1278Write payload buffer
            SX1278WriteFifo(buffer, size);
            txTimeout = settings.LoRa.TxTimeout;
        }
        break;
    }

    SX1278SetTx(txTimeout);
}

//-----------------------------------------------------------------------------
void SX1278SetSleep(void)
{
    SX1278SetTimeout(TXTimeoutTimer, NULL, 0);
    SX1278SetTimeout(RXTimeoutTimer, NULL, 0);

    SX1278SetOpMode(RF_OPMODE_SLEEP);
    //TODO Disable TCXO radio is in SLEEP mode if available
    settings.State = RF_IDLE;
}

//-----------------------------------------------------------------------------
void SX1278SetStandby(void)
{
    SX1278SetTimeout(TXTimeoutTimer, NULL, 0);
    SX1278SetTimeout(RXTimeoutTimer, NULL, 0);
    SX1278SetTimeout(RXTimeoutSyncWordTimer, NULL, 0);

    SX1278SetOpMode(RF_OPMODE_STANDBY);
    settings.State = RF_IDLE;
}

//-----------------------------------------------------------------------------
void SX1278SetRx(uint32_t timeout)
{
    bool rxContinuous = false;

    switch (settings.Modem)
    {
    case MODEM_FSK:
        {
            rxContinuous = settings.Fsk.RxContinuous;

            // DIO0=PayloadSX1278Ready
            // DIO1=FifoLevel
            // DIO2=SyncAddr
            // DIO3=FifoEmpty
            // DIO4=Preamble
            // DIO5=ModeSX1278Ready
            SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1) & RF_DIOMAPPING1_DIO0_MASK &
                                                                        RF_DIOMAPPING1_DIO1_MASK &
                                                                        RF_DIOMAPPING1_DIO2_MASK) |
                                                                        RF_DIOMAPPING1_DIO0_00 |
                                                                        RF_DIOMAPPING1_DIO1_00 |
                                                                        RF_DIOMAPPING1_DIO2_11);

            SX1278Write(REG_DIOMAPPING2, (SX1278Read(REG_DIOMAPPING2) & RF_DIOMAPPING2_DIO4_MASK &
                                                                        RF_DIOMAPPING2_MAP_MASK) |
                                                                        RF_DIOMAPPING2_DIO4_11 |
                                                                        RF_DIOMAPPING2_MAP_PREAMBLEDETECT);

            settings.FskPacketHandler.FifoThresh = SX1278Read(REG_FIFOTHRESH) & 0x3F;

            SX1278Write(REG_RXCONFIG, RF_RXCONFIG_AFCAUTO_ON | RF_RXCONFIG_AGCAUTO_ON | RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT);

            settings.FskPacketHandler.PreambleDetected = false;
            settings.FskPacketHandler.SyncWordDetected = false;
            settings.FskPacketHandler.NbBytes = 0;
            settings.FskPacketHandler.Size = 0;
        }
        break;
    case MODEM_LORA:
        {
            if (settings.LoRa.IqInverted == true)
            {
                SX1278Write(REG_LR_INVERTIQ, ((SX1278Read(REG_LR_INVERTIQ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK) | RFLR_INVERTIQ_RX_ON | RFLR_INVERTIQ_TX_OFF));
                SX1278Write(REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON);
            }
            else
            {
                SX1278Write(REG_LR_INVERTIQ, ((SX1278Read(REG_LR_INVERTIQ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF));
                SX1278Write(REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF);
            }

            // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal
            if (settings.LoRa.Bandwidth < LORA_BANDWIDTH_500kHz)
            {
                SX1278Write(REG_LR_DETECTOPTIMIZE, SX1278Read(REG_LR_DETECTOPTIMIZE) & 0x7F);
                SX1278Write(REG_LR_IFFREQ2, 0x00);
                switch (settings.LoRa.Bandwidth)
                {
                    case LORA_BANDWIDTH_7kHz: // 7.8 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x48 );
                        SX1278SetChannel(settings.Channel + 7810);
                        break;
                    case LORA_BANDWIDTH_10kHz: // 10.4 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x44 );
                        SX1278SetChannel(settings.Channel + 10420);
                        break;
                    case LORA_BANDWIDTH_15kHz: // 15.6 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x44 );
                        SX1278SetChannel(settings.Channel + 15620);
                        break;
                    case LORA_BANDWIDTH_20kHz: // 20.8 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x44 );
                        SX1278SetChannel(settings.Channel + 20830);
                        break;
                    case LORA_BANDWIDTH_31kHz: // 31.25 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x44 );
                        SX1278SetChannel(settings.Channel + 31250);
                        break;
                    case LORA_BANDWIDTH_41kHz: // 41.4 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x44 );
                        SX1278SetChannel(settings.Channel + 41670);
                        break;
                    case LORA_BANDWIDTH_62kHz: // 62.5 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x40 );
                        break;
                    case LORA_BANDWIDTH_125kHz: // 125 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x40 );
                        break;
                    case LORA_BANDWIDTH_250kHz: // 250 kHz
                        SX1278Write(REG_LR_IFFREQ1, 0x40 );
                        break;
                }
            }
            else
            {
                SX1278Write(REG_LR_DETECTOPTIMIZE, SX1278Read(REG_LR_DETECTOPTIMIZE) | 0x80);
            }

            rxContinuous = settings.LoRa.RxContinuous;

            if (settings.LoRa.FreqHopOn == true)
            {
                SX1278Write(REG_LR_IRQFLAGSMASK,//RFLR_IRQFLAGS_RXTIMEOUT |
                                                //RFLR_IRQFLAGS_RXDONE |
                                                //RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                                RFLR_IRQFLAGS_VALIDHEADER |
                                                RFLR_IRQFLAGS_TXDONE |
                                                RFLR_IRQFLAGS_CADDONE |
                                                //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                                RFLR_IRQFLAGS_CADDETECTED);

                // DIO0=RxDone, DIO2=FhssChangeChannel
                SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO2_00);
            }
            else
            {
                SX1278Write(REG_LR_IRQFLAGSMASK,//RFLR_IRQFLAGS_RXTIMEOUT |
                                                //RFLR_IRQFLAGS_RXDONE |
                                                //RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                                RFLR_IRQFLAGS_VALIDHEADER |
                                                RFLR_IRQFLAGS_TXDONE |
                                                RFLR_IRQFLAGS_CADDONE |
                                                RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                                RFLR_IRQFLAGS_CADDETECTED);

                // DIO0=RxDone
                SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1) & RFLR_DIOMAPPING1_DIO0_MASK) | RFLR_DIOMAPPING1_DIO0_00);
            }
            SX1278Write(REG_LR_FIFORXBASEADDR, 0);
            SX1278Write(REG_LR_FIFOADDRPTR, 0);
        }
        break;
    }

    memset(RxTxBuffer, 0, (size_t)RX_BUFFER_SIZE);

    settings.State = RF_RX_RUNNING;
    if (timeout != 0)
    {
        SX1278SetTimeout(RXTimeoutTimer, &OnTimeoutIrq, timeout);
    }

    if (settings.Modem == MODEM_FSK)
    {
        SX1278SetOpMode(RF_OPMODE_RECEIVER);

        if (rxContinuous == false)
        {
            SX1278SetTimeout(RXTimeoutSyncWordTimer, &OnTimeoutIrq, settings.Fsk.RxSingleTimeout);
        }
    }
    else // MODEM_LORA
    {
        if (rxContinuous == true)
        {
            SX1278SetOpMode(RFLR_OPMODE_RECEIVER);
        }
        else
        {
            SX1278SetOpMode(RFLR_OPMODE_RECEIVER_SINGLE);
        }
    }
}

//-----------------------------------------------------------------------------
void SX1278SetTx(uint32_t timeout)
{

    switch (settings.Modem)
    {
    case MODEM_FSK:
        {
            // DIO0=PacketSent
            // DIO1=FifoEmpty
            // DIO2=FifoFull
            // DIO3=FifoEmpty
            // DIO4=LowBat
            // DIO5=ModeSX1278Ready
            SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1) & RF_DIOMAPPING1_DIO0_MASK &
                                                                        RF_DIOMAPPING1_DIO1_MASK &
                                                                        RF_DIOMAPPING1_DIO2_MASK) |
                                                                        RF_DIOMAPPING1_DIO1_01);

            SX1278Write(REG_DIOMAPPING2, (SX1278Read(REG_DIOMAPPING2) & RF_DIOMAPPING2_DIO4_MASK &
                                                                            RF_DIOMAPPING2_MAP_MASK));
            settings.FskPacketHandler.FifoThresh = SX1278Read(REG_FIFOTHRESH) & 0x3F;
        }
        break;
    case MODEM_LORA:
        {
            if (settings.LoRa.FreqHopOn == true)
            {
                SX1278Write(REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT |
                                                  RFLR_IRQFLAGS_RXDONE |
                                                  RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                                  RFLR_IRQFLAGS_VALIDHEADER |
                                                  //RFLR_IRQFLAGS_TXDONE |
                                                  RFLR_IRQFLAGS_CADDONE |
                                                  //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                                  RFLR_IRQFLAGS_CADDETECTED);

                // DIO0=TxDone, DIO2=FhssChangeChannel
                SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK) | RFLR_DIOMAPPING1_DIO0_01 | RFLR_DIOMAPPING1_DIO2_00);
            }
            else
            {
                SX1278Write(REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT |
                                                  RFLR_IRQFLAGS_RXDONE |
                                                  RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                                  RFLR_IRQFLAGS_VALIDHEADER |
                                                  //RFLR_IRQFLAGS_TXDONE |
                                                  RFLR_IRQFLAGS_CADDONE |
                                                  RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                                  RFLR_IRQFLAGS_CADDETECTED);

                // DIO0=TxDone
                SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1) & RFLR_DIOMAPPING1_DIO0_MASK) | RFLR_DIOMAPPING1_DIO0_01);
            }
        }
        break;
    }

    settings.State = RF_TX_RUNNING;
    SX1278SetTimeout(TXTimeoutTimer, &OnTimeoutIrq, timeout);
    SX1278SetOpMode(RF_OPMODE_TRANSMITTER);
}

//-----------------------------------------------------------------------------
void SX1278StartCad(void)
{
    switch (settings.Modem)
    {
    case MODEM_FSK:
        {

        }
        break;
    case MODEM_LORA:
        {
            SX1278Write(REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT |
                                        RFLR_IRQFLAGS_RXDONE |
                                        RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                        RFLR_IRQFLAGS_VALIDHEADER |
                                        RFLR_IRQFLAGS_TXDONE |
                                        //RFLR_IRQFLAGS_CADDONE |
                                        RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL // |
                                        //RFLR_IRQFLAGS_CADDETECTED
                                       );

            if (dioIrq[3]) 
            {
                // DIO3=CADDone
                SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO3_MASK ) | RFLR_DIOMAPPING1_DIO3_00 );
            } 
            else 
            {
                // DIO0=CADDone
                SX1278Write(REG_DIOMAPPING1, (SX1278Read(REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_10 );
            }

            settings.State = RF_CAD;
            SX1278SetOpMode(RFLR_OPMODE_CAD);
        }
        break;
    default:
        break;
    }
}

//-----------------------------------------------------------------------------
void SX1278SetTxContinuousWave(uint32_t freq, int8_t power, uint16_t time)
{
    uint32_t timeout = (uint32_t)(time);

    SX1278SetChannel(freq);

    SX1278SetTxConfig(MODEM_FSK, power, 0, 0, 4800, 0, 5, false, false, 0, 0, 0, timeout);

    SX1278Write(REG_PACKETCONFIG2, (SX1278Read(REG_PACKETCONFIG2) & RF_PACKETCONFIG2_DATAMODE_MASK));
    // Disable radio interrupts
    SX1278Write(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_11 | RF_DIOMAPPING1_DIO1_11);
    SX1278Write(REG_DIOMAPPING2, RF_DIOMAPPING2_DIO4_10 | RF_DIOMAPPING2_DIO5_10);

    settings.State = RF_TX_RUNNING;
    SX1278SetTimeout(TXTimeoutTimer, &OnTimeoutIrq, timeout);
    SX1278SetOpMode(RF_OPMODE_TRANSMITTER);
}

//-----------------------------------------------------------------------------
int16_t SX1278GetRssi(RadioModems_t modem)
{
    int16_t rssi = 0;

    switch (modem)
    {
        case MODEM_FSK:
            rssi = -(SX1278Read(REG_RSSIVALUE) >> 1);
            break;
        case MODEM_LORA:
            if (settings.Channel > RF_MID_BAND_THRESH)
            {
                rssi = RSSI_OFFSET_HF + SX1278Read(REG_LR_RSSIVALUE);
            }
            else
            {
                rssi = RSSI_OFFSET_LF + SX1278Read(REG_LR_RSSIVALUE);
            }
            break;
        default:
            rssi = -1;
            break;
    }
    return rssi;
}

//-----------------------------------------------------------------------------
int32_t SX1278GetFrequencyError(RadioModems_t modem )
{
    int32_t val = 0;
    
    if (modem != MODEM_LORA)
        return 0;
    
    val = (SX1278Read(REG_LR_FEIMSB) & 0b1111) << 16; // high word, 4 valid bits only
    val |= ((SX1278Read(REG_LR_FEIMID) << 8) | SX1278Read(REG_LR_FEILSB)); // high byte, low byte
    if (val & 0x80000) //convert sign bit
        val |= 0xfff00000;
    
    int32_t bandwidth = 0;
    for (int i = 0; i < (int)(sizeof(SX1278LoRaBandwidths) / sizeof(BandwidthMap_t)) -1; i++ ) {
        if (SX1278LoRaBandwidths[i].RegValue == settings.LoRa.Bandwidth) {
            bandwidth = SX1278LoRaBandwidths[i].bandwidth;
            break;
        }
    }
    if (!bandwidth)
    	return 0;
    
    float bandWidthkHz = (float)bandwidth/1000;
    
    int32_t hz = (((float)val * (float)(1<<24)) / ((float)XTAL_FREQ)) * (bandWidthkHz / 500.0);
        
    return hz;
}

//-----------------------------------------------------------------------------
void SX1278SetOpMode(uint8_t opMode)
{
    // if(opMode == RF_OPMODE_SLEEP ) // TODO NOT USED on RA-01
    // {
    //     SX1278SetAntSwLowPower( true );
    // }
    // else
    // {
    //     // Enable TCXO if operating mode different from SLEEP.
    //     SX1278SetBoardTcxo( true );
    //     SX1278SetAntSwLowPower( false );
    //     SX1278SetAntSw( opMode );
    // }
    SX1278Write(REG_OPMODE, (SX1278Read(REG_OPMODE) & RF_OPMODE_MASK) | opMode);
}

//-----------------------------------------------------------------------------
void SX1278SetModem(RadioModems_t modem)
{
    if ((SX1278Read(REG_OPMODE) & RFLR_OPMODE_LONGRANGEMODE_ON) != 0)
    {
        settings.Modem = MODEM_LORA;
    }
    else
    {
        settings.Modem = MODEM_FSK;
    }

    if (settings.Modem == modem)
    {
        return;
    }

    settings.Modem = modem;
    switch (settings.Modem)
    {
        default:
        case MODEM_FSK:
            SX1278SetSleep();
            SX1278Write(REG_OPMODE, (SX1278Read(REG_OPMODE) & RFLR_OPMODE_LONGRANGEMODE_MASK) | RFLR_OPMODE_LONGRANGEMODE_OFF);

            SX1278Write(REG_DIOMAPPING1, 0x00);
            SX1278Write(REG_DIOMAPPING2, 0x30); // DIO5=ModeSX1278Ready
            break;
        case MODEM_LORA:
            SX1278SetSleep();
            SX1278Write(REG_OPMODE, (SX1278Read(REG_OPMODE) & RFLR_OPMODE_LONGRANGEMODE_MASK) | RFLR_OPMODE_LONGRANGEMODE_ON);

            SX1278Write(REG_DIOMAPPING1, 0x00);
            SX1278Write(REG_DIOMAPPING2, 0x00);
            break;
    }
}

//-----------------------------------------------------------------------------
void SX1278SetMaxPayloadLength(RadioModems_t modem, uint8_t max)
{
    SX1278SetModem(modem);

    switch (modem)
    {
    case MODEM_FSK:
        if (settings.Fsk.FixLen == false)
        {
            SX1278Write(REG_PAYLOADLENGTH, max);
        }
        break;
    case MODEM_LORA:
        SX1278Write(REG_LR_PAYLOADMAXLENGTH, max);
        break;
    }
}

//-----------------------------------------------------------------------------
void SX1278SetPublicNetwork(bool enable)
{
    SX1278SetModem(MODEM_LORA);
    settings.LoRa.PublicNetwork = enable;
    if (enable == true)
    {
        // Change LoRa modem SyncWord
        SX1278Write(REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD);
    }
    else
    {
        // Change LoRa modem SyncWord
        SX1278Write(REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD);
    }
}

//-----------------------------------------------------------------------------
void OnTimeoutIrq()
{
    switch (settings.State)
    {
    case RF_RX_RUNNING:
        if (settings.Modem == MODEM_FSK)
        {
            settings.FskPacketHandler.PreambleDetected = false;
            settings.FskPacketHandler.SyncWordDetected = false;
            settings.FskPacketHandler.NbBytes = 0;
            settings.FskPacketHandler.Size = 0;

            // Clear Irqs
            SX1278Write(REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI |
                                        RF_IRQFLAGS1_PREAMBLEDETECT |
                                        RF_IRQFLAGS1_SYNCADDRESSMATCH);
            SX1278Write(REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN);

            if (settings.Fsk.RxContinuous == true)
            {
                // Continuous mode restart Rx chain
                SX1278Write(REG_RXCONFIG, SX1278Read(REG_RXCONFIG) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK);
                SX1278SetTimeout(RXTimeoutSyncWordTimer, &OnTimeoutIrq, settings.Fsk.RxSingleTimeout);
            }
            else
            {
                settings.State = RF_IDLE;
                SX1278SetTimeout(RXTimeoutSyncWordTimer, NULL, 0);
            }
        }
        if ((RadioEvents != NULL) && (RadioEvents->RxTimeout != NULL))
        {
            RadioEvents->RxTimeout();
        }
        break;
    case RF_TX_RUNNING:
        // Tx timeout shouldn't happen.
        // But it has been observed that when it happens it is a result of a corrupted SPI transfer
        // it depends on the platform design.
        // 
        // The workaround is to put the radio in a known state. Thus, we re-initialize it.

        // BEGIN WORKAROUND

        // Reset the radio
        SX1278Reset();

        // Calibrate Rx chain
        RxChainCalibration();

        // Initialize radio default values
        SX1278SetOpMode(RF_OPMODE_SLEEP);
        SX1278RadioRegistersInit();

        SX1278SetModem(MODEM_FSK);

        // Restore previous network type setting.
        SX1278SetPublicNetwork(settings.LoRa.PublicNetwork);
        // END WORKAROUND

        settings.State = RF_IDLE;
        if ((RadioEvents != NULL) && (RadioEvents->TxTimeout != NULL))
        {
            RadioEvents->TxTimeout();
        }
        break;
    default:
        break;
    }
}

//-----------------------------------------------------------------------------
void SX1278SetRfTxPower(int8_t power)
{
    uint8_t paConfig = 0;
    uint8_t paDac = 0;

    paConfig = SX1278Read(REG_PACONFIG);
    paDac = SX1278Read(REG_PADAC);

    paConfig = (paConfig & RF_PACONFIG_PASELECT_MASK) | SX1278GetPaSelect(settings.Channel);
    paConfig = (paConfig & RF_PACONFIG_MAX_POWER_MASK) | 0x70;

    if((paConfig & RF_PACONFIG_PASELECT_PABOOST) == RF_PACONFIG_PASELECT_PABOOST)
    {
        if(power > 17)
        {
            paDac = (paDac & RF_PADAC_20DBM_MASK) | RF_PADAC_20DBM_ON;
        }
        else
        {
            paDac = (paDac & RF_PADAC_20DBM_MASK) | RF_PADAC_20DBM_OFF;
        }
        if((paDac & RF_PADAC_20DBM_ON) == RF_PADAC_20DBM_ON)
        {
            if(power < 5)
            {
                power = 5;
            }
            if(power > 20)
            {
                power = 20;
            }
            paConfig = (paConfig & RF_PACONFIG_OUTPUTPOWER_MASK) | (uint8_t)((uint16_t)(power - 5) & 0x0F);
        }
        else
        {
            if(power < 2)
            {
                power = 2;
            }
            if(power > 17)
            {
                power = 17;
            }
            paConfig = (paConfig & RF_PACONFIG_OUTPUTPOWER_MASK) | (uint8_t)((uint16_t)(power - 2) & 0x0F);
        }
    }
    else
    {
        if(power < -1)
        {
            power = -1;
        }
        if(power > 14)
        {
            power = 14;
        }
        paConfig = (paConfig & RF_PACONFIG_OUTPUTPOWER_MASK) | (uint8_t)((uint16_t)(power + 1) & 0x0F);
    }
    SX1278Write(REG_PACONFIG, paConfig);
    SX1278Write(REG_PADAC, paDac);
}

//-----------------------------------------------------------------------------
uint8_t SX1278GetPaSelect(uint32_t channel)
{
    if(channel > RF_MID_BAND_THRESH)
    {
        return RF_PACONFIG_PASELECT_PABOOST;
    }
    else
    {
        return RF_PACONFIG_PASELECT_RFO;
    }
}

//-----------------------------------------------------------------------------
bool SX1278CheckRfFrequency(uint32_t frequency)
{
    // Implement check. Currently all frequencies are supported
    return true;
}

//-----------------------------------------------------------------------------
void SX1278Write(uint8_t addr, uint8_t data)
{
    SX1278WriteBuffer(addr, &data, 1);
}

//-----------------------------------------------------------------------------
uint8_t SX1278Read(uint8_t addr)
{
    uint8_t data;
    SX1278ReadBuffer(addr, &data, 1);
    return data;
}

//-----------------------------------------------------------------------------
void SX1278WriteFifo(uint8_t *buffer, uint8_t size)
{
    SX1278WriteBuffer(0, buffer, size);
}

//-----------------------------------------------------------------------------
void SX1278ReadFifo(uint8_t *buffer, uint8_t size)
{
    SX1278ReadBuffer(0, buffer, size);
}


//-----------------------------------------------------------------------------



/**
 * ============================================================================
 * @brief Private functions definitions
 * ============================================================================
 */
//-----------------------------------------------------------------------------
void RxChainCalibration(void)
{
    uint8_t regPaConfigInitVal;
    uint32_t initialFreq;

    // Save context
    regPaConfigInitVal = SX1278Read(REG_PACONFIG);
    initialFreq = (double)(((uint32_t)SX1278Read(REG_FRFMSB) << 16) |
                              ((uint32_t)SX1278Read(REG_FRFMID) << 8) |
                              ((uint32_t)SX1278Read(REG_FRFLSB))) * (double)FREQ_STEP;

    // Cut the PA just in case, RFO output, power = -1 dBm
    SX1278Write(REG_PACONFIG, 0x00);

    // Launch Rx chain calibration for LF band
    SX1278Write (REG_IMAGECAL, (SX1278Read(REG_IMAGECAL) & RF_IMAGECAL_IMAGECAL_MASK) | RF_IMAGECAL_IMAGECAL_START);
    while((SX1278Read(REG_IMAGECAL) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING)
    {
    }

    // Sets a Frequency in HF band
    SX1278SetChannel(868000000);

    // Launch Rx chain calibration for HF band
    SX1278Write(REG_IMAGECAL, (SX1278Read(REG_IMAGECAL) & RF_IMAGECAL_IMAGECAL_MASK) | RF_IMAGECAL_IMAGECAL_START);
    while((SX1278Read(REG_IMAGECAL) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING)
    {
    }

    // Restore context
    SX1278Write(REG_PACONFIG, regPaConfigInitVal);
    SX1278SetChannel(initialFreq);
}

//-----------------------------------------------------------------------------
uint8_t GetFskBandwidthRegValue(uint32_t bandwidth)
{
    uint8_t i;

    for (i = 0; i < (sizeof(SX1278FskBandwidths) / sizeof(BandwidthMap_t)) - 1; i++)
    {
        if ((bandwidth >= SX1278FskBandwidths[i].bandwidth) && (bandwidth < SX1278FskBandwidths[i + 1].bandwidth))
        {
            return SX1278FskBandwidths[i].RegValue;
        }
    }
    // ERROR: Value not found
    while(1);
}

//-----------------------------------------------------------------------------
uint8_t GetLoRaBandwidthRegValue(uint32_t bandwidth)
{
    uint8_t i;
    
    for (i = 0; i < (sizeof(SX1278LoRaBandwidths) / sizeof(BandwidthMap_t)) - 1; i++)
    {
        if ((bandwidth >= SX1278LoRaBandwidths[i].bandwidth) && (bandwidth < SX1278LoRaBandwidths[i + 1].bandwidth))
        {
            return SX1278LoRaBandwidths[i].RegValue;
        }
    }
    // ERROR: Value not found
    while(1);
}

//-----------------------------------------------------------------------------
void OnDio0Irq()
{
    volatile uint8_t irqFlags = 0;

    switch (settings.State)
    {
        case RF_RX_RUNNING:
            //TimerStop(&RxTimeoutTimer);
            // RxDone interrupt
            switch (settings.Modem)
            {
            case MODEM_FSK:
                if (settings.Fsk.CrcOn == true)
                {
                    irqFlags = SX1278Read(REG_IRQFLAGS2);
                    if ((irqFlags & RF_IRQFLAGS2_CRCOK) != RF_IRQFLAGS2_CRCOK)
                    {
                        // Clear Irqs
                        SX1278Write(REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI |
                                                   RF_IRQFLAGS1_PREAMBLEDETECT |
                                                   RF_IRQFLAGS1_SYNCADDRESSMATCH);
                        SX1278Write(REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN);

                        SX1278SetTimeout(RXTimeoutTimer, NULL, 0);

                        if (settings.Fsk.RxContinuous == false)
                        {
                            SX1278SetTimeout(RXTimeoutSyncWordTimer, NULL, 0);
                            settings.State = RF_IDLE;
                        }
                        else
                        {
                            // Continuous mode restart Rx chain
                            SX1278Write(REG_RXCONFIG, SX1278Read(REG_RXCONFIG) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK);
                            SX1278SetTimeout(RXTimeoutSyncWordTimer, &OnTimeoutIrq, settings.Fsk.RxSingleTimeout);
                        }

                        if ((RadioEvents != NULL) && (RadioEvents->RxError != NULL))
                        {
                            RadioEvents->RxError();
                        }
                        settings.FskPacketHandler.PreambleDetected = false;
                        settings.FskPacketHandler.SyncWordDetected = false;
                        settings.FskPacketHandler.NbBytes = 0;
                        settings.FskPacketHandler.Size = 0;
                        break;
                    }
                }

                // SX1278Read received packet size
                if ((settings.FskPacketHandler.Size == 0) && (settings.FskPacketHandler.NbBytes == 0))
                {
                    if (settings.Fsk.FixLen == false)
                    {
                        SX1278ReadFifo((uint8_t*)&settings.FskPacketHandler.Size, 1);
                    }
                    else
                    {
                        settings.FskPacketHandler.Size = SX1278Read(REG_PAYLOADLENGTH);
                    }
                    SX1278ReadFifo(RxTxBuffer + settings.FskPacketHandler.NbBytes, settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                    settings.FskPacketHandler.NbBytes += (settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                }
                else
                {
                    SX1278ReadFifo(RxTxBuffer + settings.FskPacketHandler.NbBytes, settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                    settings.FskPacketHandler.NbBytes += (settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                }

                SX1278SetTimeout(RXTimeoutTimer, NULL, 0);

                if (settings.Fsk.RxContinuous == false)
                {
                    settings.State = RF_IDLE;
                    SX1278SetTimeout(RXTimeoutSyncWordTimer, NULL, 0);
                }
                else
                {
                    // Continuous mode restart Rx chain
                    SX1278Write(REG_RXCONFIG, SX1278Read(REG_RXCONFIG) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK);
                    SX1278SetTimeout(RXTimeoutSyncWordTimer, &OnTimeoutIrq, settings.Fsk.RxSingleTimeout);
                }

                if ((RadioEvents != NULL) && (RadioEvents->RxDone != NULL))
                {
                    RadioEvents->RxDone(RxTxBuffer, settings.FskPacketHandler.Size, settings.FskPacketHandler.RssiValue, 0);
                }
                settings.FskPacketHandler.PreambleDetected = false;
                settings.FskPacketHandler.SyncWordDetected = false;
                settings.FskPacketHandler.NbBytes = 0;
                settings.FskPacketHandler.Size = 0;
                break;
            case MODEM_LORA:
                {
                    // Clear Irq
                    SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE);

                    irqFlags = SX1278Read(REG_LR_IRQFLAGS);
                    if ((irqFlags & RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK) == RFLR_IRQFLAGS_PAYLOADCRCERROR)
                    {
                        // Clear Irq
                        SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR);

                        if (settings.LoRa.RxContinuous == false)
                        {
                            settings.State = RF_IDLE;
                        }
                        SX1278SetTimeout(RXTimeoutTimer, NULL, 0);

                        if ((RadioEvents != NULL) && (RadioEvents->RxError != NULL))
                        {
                            RadioEvents->RxError();
                        }
                        break;
                    }
                    // Returns SNR value [dB] rounded to the nearest integer value
                    settings.LoRaPacketHandler.SnrValue = (((int8_t)SX1278Read(REG_LR_PKTSNRVALUE)) + 2) >> 2;
                    int16_t rssi = SX1278Read(REG_LR_PKTRSSIVALUE);
                    if (settings.LoRaPacketHandler.SnrValue < 0)
                    {
                        if (settings.Channel > RF_MID_BAND_THRESH)
                        {
                            settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) +
                                                                   settings.LoRaPacketHandler.SnrValue;
                        }
                        else
                        {
                            settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) +
                                                                   settings.LoRaPacketHandler.SnrValue;
                        }
                    }
                    else
                    {
                        if(settings.Channel > RF_MID_BAND_THRESH)
                        {
                            settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 );
                        }
                        else
                        {
                            settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 );
                        }
                    }

                    settings.LoRaPacketHandler.Size = SX1278Read( REG_LR_RXNBBYTES);
                    SX1278Write( REG_LR_FIFOADDRPTR, SX1278Read( REG_LR_FIFORXCURRENTADDR));
                    SX1278ReadFifo( RxTxBuffer, settings.LoRaPacketHandler.Size);

                    if(settings.LoRa.RxContinuous == false)
                    {
                        settings.State = RF_IDLE;
                    }

                    SX1278SetTimeout(RXTimeoutTimer, NULL, 0);

                    if ((RadioEvents != NULL) && (RadioEvents->RxDone != NULL))
                    {
                        RadioEvents->RxDone(RxTxBuffer, settings.LoRaPacketHandler.Size, settings.LoRaPacketHandler.RssiValue, settings.LoRaPacketHandler.SnrValue);
                    }
                }
                break;
            default:
                break;
            }
            break;
        case RF_TX_RUNNING:
            SX1278SetTimeout(TXTimeoutTimer, NULL, 0);
            // TxDone interrupt
            switch (settings.Modem)
            {
            case MODEM_LORA:
                // Clear Irq
                SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE);
                // Intentional fall through
            case MODEM_FSK:
            default:
                settings.State = RF_IDLE;
                if ((RadioEvents != NULL) && (RadioEvents->TxDone != NULL))
                {
                    RadioEvents->TxDone();
                }
                break;
            }
            break;
        default:
            break;
    }
}

//-----------------------------------------------------------------------------
void OnDio1Irq()
{
    switch (settings.State)
    {
        case RF_RX_RUNNING:
            switch (settings.Modem)
            {
                case MODEM_FSK:
                    // Stop Timer
                    SX1278SetTimeout(RXTimeoutSyncWordTimer, NULL, 0);
                    
                    // FifoLevel interrupt
                    // SX1278Read received packet size
                    if ((settings.FskPacketHandler.Size == 0) && (settings.FskPacketHandler.NbBytes == 0))
                    {
                        if (settings.Fsk.FixLen == false)
                        {
                            SX1278ReadFifo((uint8_t*)&settings.FskPacketHandler.Size, 1);
                        }
                        else
                        {
                            settings.FskPacketHandler.Size = SX1278Read(REG_PAYLOADLENGTH);
                        }
                    }
                    // ERRATA 3.1 - PayloadReady Set for 31.25ns if FIFO is Empty
                    //
                    //              When FifoLevel interrupt is used to offload the
                    //              FIFO, the microcontroller should  monitor  both
                    //              PayloadReady  and FifoLevel interrupts, and
                    //              read only (FifoThreshold-1) bytes off the FIFO
                    //              when FifoLevel fires
                    if ((settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes) > settings.FskPacketHandler.FifoThresh)
                    {
                        SX1278ReadFifo((RxTxBuffer + settings.FskPacketHandler.NbBytes), settings.FskPacketHandler.FifoThresh);
                        settings.FskPacketHandler.NbBytes += settings.FskPacketHandler.FifoThresh;
                    }
                    else
                    {
                        SX1278ReadFifo((RxTxBuffer + settings.FskPacketHandler.NbBytes), settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                        settings.FskPacketHandler.NbBytes += (settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                    }
                    break;
                case MODEM_LORA:
                    // Sync time out
                    SX1278SetTimeout(RXTimeoutTimer, NULL, 0);
                    // Clear Irq
                    SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXTIMEOUT);

                    settings.State = RF_IDLE;
                    if ((RadioEvents != NULL) && (RadioEvents->RxTimeout != NULL))
                    {
                        RadioEvents->RxTimeout();
                    }
                    break;
                default:
                    break;
                }
                break;
            case RF_TX_RUNNING:
                switch (settings.Modem)
                {
                case MODEM_FSK:
                    // FifoEmpty interrupt
                    if ((settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes) > settings.FskPacketHandler.ChunkSize)
                    {
                        SX1278WriteFifo((RxTxBuffer + settings.FskPacketHandler.NbBytes), settings.FskPacketHandler.ChunkSize);
                        settings.FskPacketHandler.NbBytes += settings.FskPacketHandler.ChunkSize;
                    }
                    else
                    {
                        // SX1278Write the last chunk of data
                        SX1278WriteFifo(RxTxBuffer + settings.FskPacketHandler.NbBytes, settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes);
                        settings.FskPacketHandler.NbBytes += settings.FskPacketHandler.Size - settings.FskPacketHandler.NbBytes;
                    }
                    break;
                case MODEM_LORA:
                    break;
                default:
                    break;
                }
            break;
        default:
            break;
    }
}

//-----------------------------------------------------------------------------
void OnDio2Irq()
{
    switch (settings.State)
    {
        case RF_RX_RUNNING:
            switch (settings.Modem)
            {
                case MODEM_FSK:
                    // Checks if DIO4 is connected. If it is not PreambleDtected is set to true.
                    if (dioIrq[4] == NULL)
                    {
                        settings.FskPacketHandler.PreambleDetected = true;
                    }

                    if ((settings.FskPacketHandler.PreambleDetected == true) && (settings.FskPacketHandler.SyncWordDetected == false))
                    {
                        SX1278SetTimeout(RXTimeoutSyncWordTimer, NULL, 0);

                        settings.FskPacketHandler.SyncWordDetected = true;

                        settings.FskPacketHandler.RssiValue = -(SX1278Read(REG_RSSIVALUE) >> 1);

                        settings.FskPacketHandler.AfcValue = (int32_t)(double)(((uint16_t)SX1278Read(REG_AFCMSB) << 8) |
                                                                            (uint16_t)SX1278Read(REG_AFCLSB)) *
                                                                            (double)FREQ_STEP;
                        settings.FskPacketHandler.RxGain = (SX1278Read(REG_LNA) >> 5) & 0x07;
                    }
                    break;
                case MODEM_LORA:
                    if (settings.LoRa.FreqHopOn == true)
                    {
                        // Clear Irq
                        SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL);

                        if ((RadioEvents != NULL) && (RadioEvents->FhssChangeChannel != NULL))
                        {
                            RadioEvents->FhssChangeChannel((SX1278Read(REG_LR_HOPCHANNEL) & RFLR_HOPCHANNEL_CHANNEL_MASK));
                        }
                    }
                    break;
                default:
                    break;
            }
            break;
        case RF_TX_RUNNING:
            switch (settings.Modem)
            {
                case MODEM_FSK:
                    break;
                case MODEM_LORA:
                    if (settings.LoRa.FreqHopOn == true)
                    {
                        // Clear Irq
                        SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL);

                        if ((RadioEvents != NULL) && (RadioEvents->FhssChangeChannel != NULL))
                        {
                            RadioEvents->FhssChangeChannel((SX1278Read(REG_LR_HOPCHANNEL) & RFLR_HOPCHANNEL_CHANNEL_MASK));
                        }
                    }
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
}

//-----------------------------------------------------------------------------
void OnDio3Irq()
{
    switch (settings.Modem)
    {
        case MODEM_FSK:
            break;
        case MODEM_LORA:
            if ((SX1278Read(REG_LR_IRQFLAGS) & RFLR_IRQFLAGS_CADDETECTED) == RFLR_IRQFLAGS_CADDETECTED)
            {
                // Clear Irq
                SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDETECTED | RFLR_IRQFLAGS_CADDONE);
                if ((RadioEvents != NULL) && (RadioEvents->CadDone != NULL))
                {
                    RadioEvents->CadDone(true);
                }
            }
            else
            {
                // Clear Irq
                SX1278Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDONE);
                if ((RadioEvents != NULL) && (RadioEvents->CadDone != NULL))
                {
                    RadioEvents->CadDone(false);
                }
            }
            break;
        default:
            break;
    }
}

//-----------------------------------------------------------------------------
void OnDio4Irq()
{
    switch (settings.Modem)
    {
    case MODEM_FSK:
        {
            if (settings.FskPacketHandler.PreambleDetected == false)
            {
                settings.FskPacketHandler.PreambleDetected = true;
            }
        }
        break;
    case MODEM_LORA:
        break;
    default:
        break;
    }
}

//-----------------------------------------------------------------------------
void OnDio5Irq()
{
    switch (settings.Modem)
    {
    case MODEM_FSK:
        break;
    case MODEM_LORA:
        break;
    default:
        break;
    }
}

//-----------------------------------------------------------------------------