Basic MAC data interface for LoRa transceiver

Dependencies:   L2Frame crc

Dependents:   LoRaBaseStation LoRaTerminal

AlohaTransceiver.cpp

Committer:
rba90
Date:
2016-09-02
Revision:
22:2e39f382f782
Parent:
21:3b86a44b98c8
Child:
23:4b51a8e27f6a

File content as of revision 22:2e39f382f782:

#include "AlohaTransceiver.h"
#include "mbed.h"
#include "radio.h"
#include "debug.h"
#include "AlohaFrame.h"
#include "RingBuffer.h"


// declear the type of radio state
typedef enum
{
    LOWPOWER = 0,
    IDLE,

    RX,
    RX_TIMEOUT,
    RX_ERROR,

    TX,
    TX_TIMEOUT,

    CAD,
    CAD_DONE
}AppStates_t;

// radio driver related variables
static uint16_t BufferSize;
static uint8_t Buffer[BUFFER_SIZE];

static int16_t RssiValue;
static int8_t SnrValue;

static volatile AppStates_t State;
static RadioEvents_t RadioEvents;



// callback functions for radio driver
void OnTxDone();
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
void OnTxTimeout();
void OnRxTimeout();
void OnRxError();
void OnFhssChangeChannel( uint8_t channelIndex );
void OnCadDone();

// radio driver
#ifdef DRIVER_SX1276
SX1276MB1xAS Radio( NULL );
#endif

#ifdef DRIVER_INAIR
SX1276inAir Radio( NULL );
#endif


/*
 * Abstract interface for accessing radio driver
 */

AlohaTransceiver::AlohaTransceiver(uint8_t id)
{
    // store unique device id
    deviceId = id;
    
    // initialize sequenceid
    memset(seqid, 0x0, sizeof(seqid));
    
    // configure properties
#if USE_MODEM_LORA == 1
    Settings.Power = TX_OUTPUT_POWER;
    Settings.Bandwidth = LORA_BANDWIDTH;
    Settings.Datarate = LORA_SPREADING_FACTOR;
    Settings.Coderate = LORA_CODINGRATE;
    Settings.PreambleLen = LORA_PREAMBLE_LENGTH;
    Settings.SymbolTimeout = LORA_SYMBOL_TIMEOUT;
    Settings.FixLen = LORA_FIX_LENGTH_PAYLOAD_ON;
    Settings.PayloadLen = 0;
    Settings.CrcOn = LORA_CRC_ENABLED;
    Settings.FreqHopOn = LORA_FHSS_ENABLED;
    Settings.HopPeriod = LORA_NB_SYMB_HOP;
    Settings.IqInverted = LORA_IQ_INVERSION_ON;
    Settings.RxContinuous = true;
    Settings.TxTimeout = TX_TIMEOUT_VALUE;

#elif USE_MODEM_FSK == 1
    // TODO: Complete settings for FSK mode
    #error "FSK not implemented"
#else
    #error "Please define a modem in the compiler options."
#endif
}

AlohaTransceiver::~AlohaTransceiver()
{

}

void AlohaTransceiver::boardInit()
{
    // configure callback functions
    RadioEvents.TxDone = OnTxDone;
    RadioEvents.RxDone = OnRxDone;
    RadioEvents.RxError = OnRxError;
    RadioEvents.TxTimeout = OnTxTimeout;
    RadioEvents.RxTimeout = OnRxTimeout;
    Radio.Init( &RadioEvents );

    // verify the connection with the board
    while( Radio.Read( REG_VERSION ) == 0x00  )
    {
#ifdef DEBUG_ALOHA
        debug( "Radio could not be detected!\n\r" );
#endif
        wait( 1 );
    }
#ifdef DEBUG_ALOHA
    printf("RadioRegVersion: %d\r\n", Radio.Read( REG_VERSION ));
#endif
}

void AlohaTransceiver::updateSettings()
{
    Radio.SetChannel( RF_FREQUENCY );
#if USE_MODEM_LORA == 1

    Radio.SetTxConfig( MODEM_LORA, Settings.Power, 0, Settings.Bandwidth,
                         Settings.Datarate, Settings.Coderate,
                         Settings.PreambleLen, Settings.FixLen,
                         Settings.CrcOn, Settings.FreqHopOn, Settings.HopPeriod,
                         Settings.IqInverted, Settings.TxTimeout );

    Radio.SetRxConfig( MODEM_LORA, Settings.Bandwidth, Settings.Datarate,
                         Settings.Coderate, 0, Settings.PreambleLen,
                         Settings.SymbolTimeout, Settings.FixLen, Settings.PayloadLen,
                         Settings.CrcOn, Settings.FreqHopOn, Settings.HopPeriod,
                         Settings.IqInverted, Settings.RxContinuous );

#elif USE_MODEM_FSK == 1
    #error "FSK not implemented"
#else
    #error "Please define a modem in the compiler options."
#endif
}

void AlohaTransceiver::enable()
{
    // entering passive receiver mode
    Radio.Rx( 0 );
}

void AlohaTransceiver::poll()
{
    switch( State )
    {
        case RX:
        {   
            // create new frame instance
            AlohaFrame frame(Buffer, BufferSize);
            
            // check destination
            // if the destination is the device id, then processing, otherwise drop the packet and continue
            // listening
            if (frame.getDestinationAddress() == (deviceId & 0x0f))
            {               
                // currently we only have 1 payload per frame
                uint8_t payload_length = frame.getPayloadLength();
                uint8_t payload[payload_length];
                uint8_t src_addr = frame.getSourceAddress();
                uint8_t type = frame.getType();
                
                // extract payload
                for (uint8_t i = 0; i < payload_length; i++)
                {
                    payload[i] = frame.getPayload(i);
                }
                
                // check registered callback function
                // execute callback functions if registered
                
                if (AlohaTypeCallbackTable[type] != NULL)
                {
                    AlohaTypeCallbackTable[type](payload, payload_length, src_addr);
                }
            }

            Radio.Rx( 0 );
            State = LOWPOWER;
            break;
        }
        case TX:
        {
            Radio.Rx( 0 );
            State = LOWPOWER;
            break;
        }
        case RX_TIMEOUT:
        {
            Radio.Rx( 0 );
            State = LOWPOWER;
            break;
        }
        case RX_ERROR:
        {
            Radio.Rx( 0 );
            State = LOWPOWER;
            break;
        }
        case TX_TIMEOUT:
        {
            Radio.Rx( 0 );
            State = LOWPOWER;
            break;
        }
        case LOWPOWER:
        {
            // transmit packet when the radio is free
            while (AlohaTxQueue.getCounter() > 0)
            {
                AlohaFrame *frame = AlohaTxQueue.dequeue();
                
                // create a buffer for transmit
                uint8_t frame_length = frame->getPayloadLength() + FIXED_BYTE;
                uint8_t buffer[frame_length];         // 4 fix fields
                memset(buffer, 0x0, sizeof(buffer));
                
                // copy content to buffer
                frame->serialize(buffer);
                
                // send to radio 
                Radio.Send(buffer, frame_length);
                
                // free memory
                delete frame;
            }
            
            break;
        }
        default:
        {
            State = LOWPOWER;
            break;
        }
    }
}

bool AlohaTransceiver::send(uint8_t *payload, uint8_t payload_length, uint8_t dest_addr)
{
    // assume the user will not transmit payload longer than 16 bytes
    if (payload_length > 16)
    {
        return false;
    }
    
    // create a new frame
    AlohaFrame *frame = new AlohaFrame();
    
    // set properfies
    frame->setType(AlohaFrame::Aloha_Data);
    frame->setPayloadLength(payload_length);
    frame->setSourceAddress(deviceId);
    frame->setDestinationAddress(dest_addr & 0x0f);
    frame->setFullMessageFlag(0x1);
    frame->setSequenceID(seqid[dest_addr]++);    // use dest_addr as key for accessing sequence id
                                                // the seqid should increase as it successfully transmit the packet
    
    // set payload
    for (uint8_t i = 0; i < payload_length; i++)
    {
        frame->setPayload(i, payload[i]);
    }
    
    // calculate crc
    frame->generateCrc();
    
    // push frame to the back of queue
    AlohaTxQueue.enqueue(frame);

    return true;
}

void AlohaTransceiver::registerType(AlohaFrame::AlohaType_t type, aloha_callback_func f)
{
    AlohaTypeCallbackTable[type] = f;
}

void AlohaTransceiver::deRegisterType(AlohaFrame::AlohaType_t type, aloha_callback_func f)
{
    AlohaTypeCallbackTable[type] = NULL;
}

int16_t AlohaTransceiver::getRssi()
{
    return RssiValue;
}

int8_t AlohaTransceiver::getSnr()
{
    return SnrValue;
}

uint8_t AlohaTransceiver::getDeviceID()
{
    return deviceId;
}

void AlohaTransceiver::setDeviceID(uint8_t id)
{
    deviceId = id;
}

#if USE_MODEM_LORA == 1
AlohaTransceiver::LoRaSettings_t *AlohaTransceiver::getSettings()
{
    return &Settings;
}
        
#elif USE_MODEM_FSK == 1
AlohaTransceiver::FskSettings_t *AlohaTransceiver::getSettings()
{
    return &Settings;
}
#else
    #error "Please define a modem in the compiler options."
#endif

void OnTxDone( void )
{
    Radio.Sleep( );
    State = TX;
    
#ifdef DEBUG_ALOHA
    debug("> OnTxDone\n\r" );
#endif
}

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
    Radio.Sleep( );

    // safeguard: if size exceeded maximum buffer size, it will cause memory overflow
    BufferSize = size ? BUFFER_SIZE : size <= BUFFER_SIZE;

    memcpy( Buffer, payload, BufferSize );
    RssiValue = rssi;
    SnrValue = snr;
    State = RX;
    
#ifdef DEBUG_ALOHA
    debug("> OnRxDone\n\r" );
#endif
}

void OnTxTimeout( void )
{
    Radio.Sleep( );
    State = TX_TIMEOUT;
    
#ifdef DEBUG_ALOHA
    debug("> OnTxTimeout\n\r" );
#endif
}

void OnRxTimeout( void )
{
    Radio.Sleep( );
    Buffer[ BufferSize ] = 0;
    State = RX_TIMEOUT;
    
#ifdef DEBUG_ALOHA
    debug("> OnRxTimeout\n\r" );
#endif
}

void OnRxError( void )
{
    Radio.Sleep( );
    State = RX_ERROR;
    
#ifdef DEBUG_ALOHA
    debug( "> OnRxError\n\r" );
#endif
}