QRSS Rx Network receiver. A receiver to sample a segment of RF spectrum and send this data to a server for further processing and display. NXP mbed Design Challenge entry (Honorable Mention). Published in Circuit Cellar, Feb 2012

Dependencies:   NetServices mbed DNSResolver

BufferSys.h

Committer:
claytong
Date:
2012-01-25
Revision:
0:82ff15078322

File content as of revision 0:82ff15078322:

/*---------------------------------------------------------------------------

    QRSS Receiver Application
        
    by Clayton ZL3TKA/VK1TKA
    clayton@isnotcrazy.com

    Header File for Buffer System
    Designed to buffer 32 bit I/Q samples

---------------------------------------------------------------------------*/
#ifndef _BUFFERSYS_H
#define _BUFFERSYS_H

#include "mbed.h"
#include "global.h"

// Definitions

#ifndef BUFFERSYS_SIZE
  #define BUFFERSYS_SIZE    512
#endif

//
// Structures
//

//---------------------------------------------------------------------------
//
//  A Sample structure
//      The single data sample point
//
typedef struct _data_sample
{
    int32_t     iQData;
    int32_t     iIData;
} TDataSample;

// Long samples - more bit sizes
// Not used by the buffers, but available for other parts in the system
typedef struct _long_data_sample
{
    int64_t     iQData;
    int64_t     iIData;
} TLongDataSample;

extern const TDataSample NullSample;
 
//
// Classes
//

// Forward references
class TBufferPool;

//---------------------------------------------------------------------------
//
//  A Buffer Data Class
//      The actual thing that holds the sample data
//
class TBufferData
{
    // create/destroy
    public:
        // creates the buffer
        TBufferData() :
            iLength(0),
            pNext(NULL),
            iRefCount(0),
            pPool(NULL)
            {}
        // delete the buffer
        ~TBufferData()
            {}

    // API
    public:

        // Check the reference count
        //  Any operation that changes a buffer's data (or provides a write pointer) MUST check 
        //  iRefCount and throw exception if not = 1
        inline void CheckRefCount( void ) const
        {
            if ( iRefCount != 1 ) 
                error( "TBufferData::CheckRefCount RefCount not equal to 1\r\n" );
        }

        // Clear the buffer
        void Clear( void )
        {
            iLength = 0;
        }

        // Append a sample
        bool Append( const TDataSample *pSample )
        {
            CheckRefCount();
            // Check for overflows
            if ( iLength>=BUFFERSYS_SIZE )
                return false;   // no room
            // Append the data
            asSamples[iLength] = *pSample;
            iLength ++;
            return true;
        }

        // Remove samples form the end of the buffer
        //  Fails if there are not enough samples to remove
        bool Remove( int iSamples )
        {
            CheckRefCount();
            if ( iSamples<0 )
                error( "TBufferData::Remove Invalid remove number\r\n" );
            // Check for underflow
            if ( iLength<iSamples )
            {   // underflow - remove all anyway
                iLength = 0;
                return false;
            }
            // adjust the length
            iLength -= iSamples;
            return true;
        }

    // data
    private:
    
        // the buffer
        TDataSample     asSamples[BUFFERSYS_SIZE];
    
        // data size count
        int             iLength;    // current samples count
        
        // a pointer for queuing
        TBufferData     *pNext;
        
        // a reference counter for counting holders of this buffer
        int             iRefCount;

        // a timestamp of the sample capture
        uint32_t        uiTimestamp;
        
        // a pointer to this buffers pool (to return it too)
        TBufferPool     *pPool;         // the pool that the buffer came from

#ifdef BUFFER_DEBUG        
        // details of buffer ownership (for debugging only)
        bool    bQueued;                // the buffer is in a queue
        bool    bPooled;                // the buffer is in a pool (unused buffer)
#endif

        //Declare the buffer classes as friendly
        //so it can access the protected members of buffers.
        friend class TBufferHandle;
        friend class TBufferQueue;
        friend class TBufferPool;
        
        
};

//---------------------------------------------------------------------------
//
//  A Buffer Handle Class
//      Something that holds a buffer
//      Can also be the base class for something that processes a buffer
//
class TBufferHandle
{
    // create/destroy
    public:
        TBufferHandle() :
            pMyBuffer( NULL )
            {}

        virtual ~TBufferHandle()
            {   // release any attached buffer
                Release();
            }

    // buffer attached/release  
    public:
        // test if the handle has a buffer attached
        inline bool HasBuffer( void ) const
        {
            if ( pMyBuffer != NULL ) 
                return true;
            return false;
        }

    protected:
        // attached a buffer to the handle
        bool Attach( TBufferData * pBuffer )
        {
            // release any old buffer
            Release();

            // Attach the buffer to the handler
            if ( pBuffer != NULL )
            {
                pMyBuffer = pBuffer;
                // Increment the reference count of the buffer
                pMyBuffer->iRefCount++;
                return true;
            }
            return false;
        }
        // debug - check that a buffer is attached
        inline void CheckForBuffer( void ) const
        {
            if ( pMyBuffer == NULL ) 
                error( "TBufferHandle::CheckForBuffer Handle does not have a buffer attached\r\n" );
        }

    public:
        // transfer a buffer from another handle
        bool TransferBuffer( TBufferHandle & Handle )
        {
            // attach buffer from other handle
            if ( !Attach(Handle.pMyBuffer) )
                return false;
            // release buffer from the other handle                
            Handle.Release();
            return true;
        }

        // release the buffer from the handle
        void Release( void );

        // Set the timestamp
        void Timestamp( uint32_t uiTime )
        {
            if ( HasBuffer() )
                pMyBuffer->uiTimestamp = uiTime;
        }
            
        // Read the timestamp
        uint32_t Timestamp()
        {
            if ( HasBuffer() )
                return pMyBuffer->uiTimestamp;
            return 0;
        }

    // buffer manipulation
    public:
        // append a sample
        bool Append( const TDataSample &Sample )
        {
            CheckForBuffer();
            return pMyBuffer->Append( &Sample );
        }

        // Remove characters form the end of the buffer
        //  Fails if there are not enough bytes to remove
        bool Remove( int iBytes )
        {
            CheckForBuffer();
            return pMyBuffer->Remove( iBytes );
        }

        // Clear the buffer
        void Clear( void )
        {
            CheckForBuffer();
            pMyBuffer->Clear();
        }
    
        // set buffer length (after data has been manually added)
        void SetLength( int iLen )
        {
            if ( HasBuffer() )
            {
                if ( iLen<0 )
                    iLen = 0;
                if ( iLen>BUFFERSYS_SIZE )
                    iLen = BUFFERSYS_SIZE;
                pMyBuffer->iLength = iLen;
            }
        }

    // buffer examination
    public:
        // get buffer length
        int Length( void ) const
        {
            if ( !HasBuffer() )
                return 0;
            return pMyBuffer->iLength;
        }
        
        // get buffer size
        int Size( void ) const
        {
            if ( !HasBuffer() )
                return 0;
            return BUFFERSYS_SIZE;
        }

        int Room( void ) const
        {
            if ( !HasBuffer() )
                return 0;
            return ( BUFFERSYS_SIZE - pMyBuffer->iLength );
        }

        // get a pointer to the buffer data    
        TDataSample * SamplePtr( int iIndex=0 )
        {
            if ( !HasBuffer() )
                return NULL;        // no buffer
            if ( iIndex>pMyBuffer->iLength )
                return NULL;        // index past end of buffer
            return &(pMyBuffer->asSamples[iIndex]);
        }
        // get buffer data (note that there is no way to detect failure)
        const TDataSample operator []( int iIndex ) const
        {
            if ( HasBuffer() && (iIndex<pMyBuffer->iLength) && (iIndex>=0) )
                return pMyBuffer->asSamples[iIndex];
            return NullSample;  // otherwise return empty sample
        }

    // data
    protected:
        // pointer to the buffer being references to
        TBufferData    *pMyBuffer;
        
    friend class TBufferQueue;
    friend class TBufferPool;
};

//---------------------------------------------------------------------------
//
//  A Buffer Queue
//      A queue of buffers
//
class TBufferQueue
{
    // create/destroy
    public:
        TBufferQueue() :
            pFront(NULL),
            pBack(NULL),
            iCount(0)
            {}

        virtual ~TBufferQueue()
        {
            // release all buffer
            Flush();
        }

    // queue manipulation
    public:
        // peek at the front buffer on the queue, but still leave the buffer there
        // (buffer must not be editted)    
        bool Peek( TBufferHandle &Handle ) const
        {   // attached the buffer to the handle
            return Handle.Attach( pFront );     // Attach also inc ref count
        }

        // remove the first buffer from the queue and return in the handle
        bool Read( TBufferHandle &Handle )
        {
            // release any old buffers
            Handle.Release();
            // check for any buffers
            if ( iCount<=0 )
                return false;   //Nothing in queue
            // get the front buffer
            TBufferData * pRemovedBuffer = pFront;
            if ( pRemovedBuffer==NULL )
                error( "TBufferQueue::Read queue pFront pointer!\r\n" );
            // adjust the queue ptrs
            RemovePtrFromQueue();
#ifdef BUFFER_DEBUG        
            // reset queued flag
            pRemovedBuffer->bQueued = false;
#endif
            // no RefCount adjustment needed - buffer is added to handle but release from the queue
            // Manually put the buffer into the (empty) handle
            Handle.pMyBuffer = pRemovedBuffer;
            return true;
        }
        
        // Write a buffer into the queue, removing it from the handle
        bool Write( TBufferHandle &Handle )
        {
            // check there is a buffer
            if ( !Handle.HasBuffer() ) 
                return false;
            // Buffer is manually moved (Reference counter is untouched)
            // Add msg ptr to the queue (does not adjust the reference counter)
            AddPtrToQueue( Handle.pMyBuffer ); 
            // Flag the buffer as queued
#ifdef BUFFER_DEBUG        
            Handle.pMyBuffer->bQueued = true;       
#endif
            // no RefCount adjustment needed - buffer is added to queue but release from the handle
            // Manually remove the buffer from the handle
            Handle.pMyBuffer = NULL;
            return true;
        }
    
        // Return number of queued buffers
        int Count() const
            { return iCount; }

        // Test if empty
        bool Empty() const
            { return (iCount==0); }

        // strip all buffers from the queue
        bool Flush( void )
        {
            // check for buffers to flush
            if ( iCount==0 )
                return false;
            // read and release buffers until no more to read
            TBufferHandle MsgHandle;
            while ( Read(MsgHandle) )
            {   // release the buffers
                MsgHandle.Release();
            }
            return true;
        }

    // internal methods
    protected:
        // Add the buffer to the end of the queue 
        //  Does not do any checks etc or adjust buffer reference count
        void AddPtrToQueue( TBufferData *pMsg )
        {
            pMsg->pNext = NULL;    
            if ( iCount == 0 ) 
            {   // Queue is currently empty
                // Front = back = buffer
                pFront = pMsg;
                pBack = pMsg;
                iCount = 1;
            }   
            else 
            {   // Add the buffer to the back of the queue
                pBack->pNext = pMsg;
                pBack = pMsg;
                iCount++;
            }
        }
      
        // Remove a pointer from the front of the queue
        //  Does not do any checks etc or adjust buffer reference count - just removes pointer
        void RemovePtrFromQueue( void )
        {
            // adjust the queue
            if ( iCount<=1 )
            {   // Empty queue
                pFront = NULL;
                pBack = NULL;
                iCount=0;
            } 
            else 
            {
                // Remove first item
                pFront = pFront->pNext;
                // Decrement the number of buffers in the queue
                iCount--;
            }
        }
        
    // data
    protected:
        TBufferData    *pFront;        // front of the queue (out point)
        TBufferData    *pBack;         // back of the queue (in point)
        int         iCount;         // number of items in the queue

    friend class TBufferHandle;




};

//---------------------------------------------------------------------------
//
//  A Buffer Pool
//      A pool of free buffers
//
class TBufferPool : protected TBufferQueue
{
    // create/destroy
    public:
        // create an pool and optionally add buffers to it off the stack
        TBufferPool( int iNumMsgs=0 ) :
            iBuffCount(0)
            {
                if (iNumMsgs>0)
                    CreateBuffers( iNumMsgs );
            }
        // destroy a pool and all of its internal methods
        virtual ~TBufferPool()
        {
            // delete all buffers
            TBufferHandle MsgHandle;
            while ( Read(MsgHandle) )
            {
                // delete the TBufferData
                delete MsgHandle.pMyBuffer;
                // clear the buffer handle
                MsgHandle.pMyBuffer = NULL;
            }
        }

    // pool setup
    public:
        // Add buffers to the pool
        // Buffers are created off the heap
        bool CreateBuffers( int iNumMsgs )
        {
            TBufferData * pBuff;
            // Loop and create msgs
            while ( iNumMsgs>0 )
            {   
                //printf( "Creating buffer %d\r\n", iBuffCount );
                // Create the new buffer
                pBuff = new TBufferData;
                if ( pBuff==NULL )
                    return false;
                // add the buffer to the pool
                pBuff->pPool = this;
                ReturnToPool( pBuff );
                iBuffCount++;
                iNumMsgs--;
            }
            return true;           
        }
    
        // add a new buffer to the pool - 
        //      either as a buffer (with or without a data structure)
        //      or as a data structure with a buffer
        void AddNewMsgToPool( TBufferData & Buffer )
        {
            Buffer.pPool = this;
            ReturnToPool( &Buffer );
            iBuffCount++;
        }

        // add an array of buffers to the pool
        void AddNewMsgsToPool( TBufferData *pBuffer, int iCount )
        {
            while ( iCount>0 )
            {
                AddNewMsgToPool( *pBuffer );
                pBuffer++;
                iCount--;
            }
        }

        // place or return a buffer in the pool
        void ReturnToPool( TBufferData * pBuffer )
        {
            // Add to the queue (does not adjust the reference counter)
            AddPtrToQueue( pBuffer );
            // zero reference (should already be the case)
            pBuffer->iRefCount = 0;
#ifdef BUFFER_DEBUG
            // Flag the buffer as pooled
            pBuffer->bPooled = true;       
#endif
        }

    // Message Handling
    public:
        // buffer creation - takes a msg from the pool
        bool Create( TBufferHandle &Handle )
        {
            // read the buffer from the queue
            if ( !Read(Handle) )
                return false;
            // inc ref count (RefCount is 0 in pool, and Read does not adjust it)
            Handle.pMyBuffer->iRefCount++;
#ifdef BUFFER_DEBUG
            // reset pool flag
            Handle.pMyBuffer->bPooled = false;
#endif
            // Initialise the buffer
            Handle.Clear();
            Handle.pMyBuffer->uiTimestamp = 0;
            return true;
        }

    // Pool Status
    public:
        // return number of buffers available
        int Available() const
            { return iCount; }

    // data
    protected:
        int     iBuffCount;      // number of buffers installed in the pool
    
    friend class TBufferData;
    friend class TBufferHandle;
    
};


#endif

//---------------------------------------------------------------------------
//  END
//---------------------------------------------------------------------------