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
Revision 0:82ff15078322, committed 2012-01-25
- Comitter:
- claytong
- Date:
- Wed Jan 25 20:32:53 2012 +0000
- Commit message:
- 1.0 (initial public release)
Changed in this revision
diff -r 000000000000 -r 82ff15078322 BufferSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BufferSys.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Buffer System + +---------------------------------------------------------------------------*/ +// include files + +#include "BufferSys.h" + +// Definitions + +// Macros + +// Local Data + +// Global Data +const TDataSample NullSample = {0,0}; + +// Function Prototypes + +//--------------------------------------------------------------------------- +// BUFFER SYSTEM METHODS +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Buffer Release Method +// +void TBufferHandle::Release( void ) +{ + if ( !HasBuffer() ) + return; + // If the handler was the only reference to the buffer + // then the buffer should be released to the pool + if ( pMyBuffer->iRefCount<=0 ) + error( "TBufferHandle::CheckForBuffer Invalid TBufferData iRefCount in Release - %d\r\n", pMyBuffer->iRefCount ); + pMyBuffer->iRefCount--; + if ( pMyBuffer->iRefCount==0 ) + { // Release message to the pool it came from + pMyBuffer->pPool->ReturnToPool( pMyBuffer ); + } + // Detach the message from the handler + pMyBuffer = NULL; +} + +//--------------------------------------------------------------------------- +// END +//---------------------------------------------------------------------------
diff -r 000000000000 -r 82ff15078322 BufferSys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BufferSys.h Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,612 @@ +/*--------------------------------------------------------------------------- + + 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 +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 DNSResolver.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DNSResolver.lib Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/hlipka/code/DNSResolver/#83c60912e03f
diff -r 000000000000 -r 82ff15078322 DSP.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DSP.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,451 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + DSP Processing class + +---------------------------------------------------------------------------*/ +// include files + +#include "DSP.h" + +// Definitions + +// Macros + +// Local Data + +// Global Data + +// Function Prototypes + + +/* + + LP FIR Filter calculated from Web calculator + http://www-users.cs.york.ac.uk/~fisher/cgi-bin/mkfscript + + Sample Rate: 35156 + Corner Freq: 200 + Taps: 511 + Beta: 0.5 + +// Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher +// Command line: /www/usr/fisher/helpers/mkshape -c 5.6889293435e-03 5.0000000000e-01 511 -b 16 -l + +#define NZEROS 510 +#define GAIN 8.763528442e+01 + +static float xv[NZEROS+1]; + +static float xcoeffs[] = + { +0.0006713867, +0.0008544922, +0.0010375977, +0.0012817383, + +0.0015258789, +0.0017700195, +0.0020446777, +0.0023498535, + +0.0026855469, +0.0030212402, +0.0033874512, +0.0037841797, + +0.0041809082, +0.0046081543, +0.0050659180, +0.0055236816, + +0.0060119629, +0.0065002441, +0.0070190430, +0.0075378418, + +0.0080871582, +0.0086364746, +0.0092163086, +0.0097961426, + +0.0103759766, +0.0109558105, +0.0115661621, +0.0121765137, + +0.0127868652, +0.0133972168, +0.0140075684, +0.0146179199, + +0.0151977539, +0.0158081055, +0.0163879395, +0.0169677734, + +0.0175476074, +0.0180969238, +0.0186462402, +0.0191650391, + +0.0196533203, +0.0201416016, +0.0205688477, +0.0209960938, + +0.0213928223, +0.0217590332, +0.0220947266, +0.0223693848, + +0.0226440430, +0.0228576660, +0.0230102539, +0.0231323242, + +0.0232238770, +0.0232238770, +0.0232238770, +0.0231323242, + +0.0229797363, +0.0227966309, +0.0225219727, +0.0222167969, + +0.0218200684, +0.0213623047, +0.0208435059, +0.0202636719, + +0.0195922852, +0.0188598633, +0.0180664062, +0.0171813965, + +0.0162048340, +0.0151672363, +0.0140380859, +0.0128479004, + +0.0115661621, +0.0102233887, +0.0087890625, +0.0072631836, + +0.0056762695, +0.0039978027, +0.0022277832, +0.0003967285, + -0.0015258789, -0.0035095215, -0.0055847168, -0.0077514648, + -0.0099792480, -0.0122680664, -0.0146484375, -0.0170898438, + -0.0195922852, -0.0221557617, -0.0248107910, -0.0274963379, + -0.0302429199, -0.0330505371, -0.0359191895, -0.0388183594, + -0.0417785645, -0.0447692871, -0.0477905273, -0.0508422852, + -0.0539245605, -0.0570373535, -0.0601501465, -0.0632934570, + -0.0664367676, -0.0695800781, -0.0727233887, -0.0758666992, + -0.0789794922, -0.0820922852, -0.0851745605, -0.0881958008, + -0.0912170410, -0.0941467285, -0.0970764160, -0.0999145508, + -0.1026916504, -0.1054077148, -0.1080322266, -0.1105651856, + -0.1130065918, -0.1153564453, -0.1175842285, -0.1197204590, + -0.1217346191, -0.1235961914, -0.1253356934, -0.1269226074, + -0.1283569336, -0.1296386719, -0.1307373047, -0.1316833496, + -0.1324462891, -0.1329956055, -0.1333923340, -0.1335754394, + -0.1335144043, -0.1332702637, -0.1328125000, -0.1321105957, + -0.1311950684, -0.1300048828, -0.1286010742, -0.1269226074, + -0.1250000000, -0.1228332519, -0.1203613281, -0.1176452637, + -0.1146545410, -0.1113586426, -0.1077880859, -0.1039428711, + -0.0997924805, -0.0953369141, -0.0906066895, -0.0855407715, + -0.0802001953, -0.0745544434, -0.0686035156, -0.0623168945, + -0.0557250977, -0.0488586426, -0.0416564941, -0.0341186523, + -0.0263061523, -0.0181579590, -0.0097351074, -0.0009765625, + +0.0080871582, +0.0174255371, +0.0270996094, +0.0370483398, + +0.0473022461, +0.0578308105, +0.0686645508, +0.0797729492, + +0.0911560059, +0.1028137207, +0.1147155762, +0.1268920898, + +0.1393432617, +0.1520080566, +0.1649169922, +0.1780700684, + +0.1914367676, +0.2050170898, +0.2188110352, +0.2327880859, + +0.2469482422, +0.2613220215, +0.2758178711, +0.2904968262, + +0.3053283691, +0.3203125000, +0.3353881836, +0.3505859375, + +0.3659057617, +0.3812866211, +0.3967590332, +0.4122924805, + +0.4279174805, +0.4435424805, +0.4591979981, +0.4748840332, + +0.4905700684, +0.5062561035, +0.5219116211, +0.5375366211, + +0.5531005859, +0.5686035156, +0.5840148926, +0.5993652344, + +0.6145935059, +0.6296997070, +0.6446838379, +0.6595153809, + +0.6741943359, +0.6886901855, +0.7030334473, +0.7171325684, + +0.7310485840, +0.7447204590, +0.7581787109, +0.7713623047, + +0.7843017578, +0.7969665527, +0.8093261719, +0.8214111328, + +0.8331604004, +0.8446044922, +0.8557128906, +0.8664855957, + +0.8768920898, +0.8869323730, +0.8966064453, +0.9058837891, + +0.9147949219, +0.9232788086, +0.9313659668, +0.9390258789, + +0.9462585449, +0.9530944824, +0.9594421387, +0.9653625488, + +0.9708557129, +0.9758605957, +0.9804077148, +0.9844970703, + +0.9880981445, +0.9912414551, +0.9938964844, +0.9960937500, + +0.9977722168, +0.9989929199, +0.9997253418, +0.9999694824, + +0.9997253418, +0.9989929199, +0.9977722168, +0.9960937500, + +0.9938964844, +0.9912414551, +0.9880981445, +0.9844970703, + +0.9804077148, +0.9758605957, +0.9708557129, +0.9653625488, + +0.9594421387, +0.9530944824, +0.9462585449, +0.9390258789, + +0.9313659668, +0.9232788086, +0.9147949219, +0.9058837891, + +0.8966064453, +0.8869323730, +0.8768920898, +0.8664855957, + +0.8557128906, +0.8446044922, +0.8331604004, +0.8214111328, + +0.8093261719, +0.7969665527, +0.7843017578, +0.7713623047, + +0.7581787109, +0.7447204590, +0.7310485840, +0.7171325684, + +0.7030334473, +0.6886901855, +0.6741943359, +0.6595153809, + +0.6446838379, +0.6296997070, +0.6145935059, +0.5993652344, + +0.5840148926, +0.5686035156, +0.5531005859, +0.5375366211, + +0.5219116211, +0.5062561035, +0.4905700684, +0.4748840332, + +0.4591979981, +0.4435424805, +0.4279174805, +0.4122924805, + +0.3967590332, +0.3812866211, +0.3659057617, +0.3505859375, + +0.3353881836, +0.3203125000, +0.3053283691, +0.2904968262, + +0.2758178711, +0.2613220215, +0.2469482422, +0.2327880859, + +0.2188110352, +0.2050170898, +0.1914367676, +0.1780700684, + +0.1649169922, +0.1520080566, +0.1393432617, +0.1268920898, + +0.1147155762, +0.1028137207, +0.0911560059, +0.0797729492, + +0.0686645508, +0.0578308105, +0.0473022461, +0.0370483398, + +0.0270996094, +0.0174255371, +0.0080871582, -0.0009765625, + -0.0097351074, -0.0181579590, -0.0263061523, -0.0341186523, + -0.0416564941, -0.0488586426, -0.0557250977, -0.0623168945, + -0.0686035156, -0.0745544434, -0.0802001953, -0.0855407715, + -0.0906066895, -0.0953369141, -0.0997924805, -0.1039428711, + -0.1077880859, -0.1113586426, -0.1146545410, -0.1176452637, + -0.1203613281, -0.1228332519, -0.1250000000, -0.1269226074, + -0.1286010742, -0.1300048828, -0.1311950684, -0.1321105957, + -0.1328125000, -0.1332702637, -0.1335144043, -0.1335754394, + -0.1333923340, -0.1329956055, -0.1324462891, -0.1316833496, + -0.1307373047, -0.1296386719, -0.1283569336, -0.1269226074, + -0.1253356934, -0.1235961914, -0.1217346191, -0.1197204590, + -0.1175842285, -0.1153564453, -0.1130065918, -0.1105651856, + -0.1080322266, -0.1054077148, -0.1026916504, -0.0999145508, + -0.0970764160, -0.0941467285, -0.0912170410, -0.0881958008, + -0.0851745605, -0.0820922852, -0.0789794922, -0.0758666992, + -0.0727233887, -0.0695800781, -0.0664367676, -0.0632934570, + -0.0601501465, -0.0570373535, -0.0539245605, -0.0508422852, + -0.0477905273, -0.0447692871, -0.0417785645, -0.0388183594, + -0.0359191895, -0.0330505371, -0.0302429199, -0.0274963379, + -0.0248107910, -0.0221557617, -0.0195922852, -0.0170898438, + -0.0146484375, -0.0122680664, -0.0099792480, -0.0077514648, + -0.0055847168, -0.0035095215, -0.0015258789, +0.0003967285, + +0.0022277832, +0.0039978027, +0.0056762695, +0.0072631836, + +0.0087890625, +0.0102233887, +0.0115661621, +0.0128479004, + +0.0140380859, +0.0151672363, +0.0162048340, +0.0171813965, + +0.0180664062, +0.0188598633, +0.0195922852, +0.0202636719, + +0.0208435059, +0.0213623047, +0.0218200684, +0.0222167969, + +0.0225219727, +0.0227966309, +0.0229797363, +0.0231323242, + +0.0232238770, +0.0232238770, +0.0232238770, +0.0231323242, + +0.0230102539, +0.0228576660, +0.0226440430, +0.0223693848, + +0.0220947266, +0.0217590332, +0.0213928223, +0.0209960938, + +0.0205688477, +0.0201416016, +0.0196533203, +0.0191650391, + +0.0186462402, +0.0180969238, +0.0175476074, +0.0169677734, + +0.0163879395, +0.0158081055, +0.0151977539, +0.0146179199, + +0.0140075684, +0.0133972168, +0.0127868652, +0.0121765137, + +0.0115661621, +0.0109558105, +0.0103759766, +0.0097961426, + +0.0092163086, +0.0086364746, +0.0080871582, +0.0075378418, + +0.0070190430, +0.0065002441, +0.0060119629, +0.0055236816, + +0.0050659180, +0.0046081543, +0.0041809082, +0.0037841797, + +0.0033874512, +0.0030212402, +0.0026855469, +0.0023498535, + +0.0020446777, +0.0017700195, +0.0015258789, +0.0012817383, + +0.0010375977, +0.0008544922, +0.0006713867, + }; + +static void filterloop() + { for (;;) + { float sum; int i; + for (i = 0; i < NZEROS; i++) xv[i] = xv[i+1]; + xv[NZEROS] = next input value / GAIN; + sum = 0.0; + for (i = 0; i <= NZEROS; i++) sum += (xcoeffs[i] * xv[i]); + next output value = sum; + } + } + +*/ + +const int16_t aiFIRCoefficients[DSP_FIR_COEFFICIENTS] = { + 22,28,34,42,50,58,67,77, + 88,99,111,124,137,151,166,181, + 197,213,230,247,265,283,302,321, + 340,359,379,399,419,439,459,479, + 498,518,537,556,575,593,611,628, + 644,660,674,688,701,713,724,733, + 742,749,754,758,761,761,761,758, + 753,747,738,728,715,700,683,664, + 642,618,592,563,531,497,460,421, + 379,335,288,238,186,131,73,13, + -50,-115,-183,-254,-327,-402,-480,-560, + -642,-726,-813,-901,-991,-1083,-1177,-1272, + -1369,-1467,-1566,-1666,-1767,-1869,-1971,-2074, + -2177,-2280,-2383,-2486,-2588,-2690,-2791,-2890, + -2989,-3085,-3181,-3274,-3365,-3454,-3540,-3623, + -3703,-3780,-3853,-3923,-3989,-4050,-4107,-4159, + -4206,-4248,-4284,-4315,-4340,-4358,-4371,-4377, + -4375,-4367,-4352,-4329,-4299,-4260,-4214,-4159, + -4096,-4025,-3944,-3855,-3757,-3649,-3532,-3406, + -3270,-3124,-2969,-2803,-2628,-2443,-2248,-2042, + -1826,-1601,-1365,-1118,-862,-595,-319,-32, + 265,571,888,1214,1550,1895,2250,2614, + 2987,3369,3759,4158,4566,4981,5404,5835, + 6273,6718,7170,7628,8092,8563,9038,9519, + 10005,10496,10990,11488,11990,12494,13001,13510, + 14022,14534,15047,15561,16075,16589,17102,17614, + 18124,18632,19137,19640,20139,20634,21125,21611, + 22092,22567,23037,23499,23955,24403,24844,25276, + 25700,26115,26520,26916,27301,27676,28040,28393, + 28734,29063,29380,29684,29976,30254,30519,30770, + 31007,31231,31439,31633,31813,31977,32126,32260, + 32378,32481,32568,32640,32695,32735,32759,32767, + 32759,32735,32695,32640,32568,32481,32378,32260, + 32126,31977,31813,31633,31439,31231,31007,30770, + 30519,30254,29976,29684,29380,29063,28734,28393, + 28040,27676,27301,26916,26520,26115,25700,25276, + 24844,24403,23955,23499,23037,22567,22092,21611, + 21125,20634,20139,19640,19137,18632,18124,17614, + 17102,16589,16075,15561,15047,14534,14022,13510, + 13001,12494,11990,11488,10990,10496,10005,9519, + 9038,8563,8092,7628,7170,6718,6273,5835, + 5404,4981,4566,4158,3759,3369,2987,2614, + 2250,1895,1550,1214,888,571,265,-32, + -319,-595,-862,-1118,-1365,-1601,-1826,-2042, + -2248,-2443,-2628,-2803,-2969,-3124,-3270,-3406, + -3532,-3649,-3757,-3855,-3944,-4025,-4096,-4159, + -4214,-4260,-4299,-4329,-4352,-4367,-4375,-4377, + -4371,-4358,-4340,-4315,-4284,-4248,-4206,-4159, + -4107,-4050,-3989,-3923,-3853,-3780,-3703,-3623, + -3540,-3454,-3365,-3274,-3181,-3085,-2989,-2890, + -2791,-2690,-2588,-2486,-2383,-2280,-2177,-2074, + -1971,-1869,-1767,-1666,-1566,-1467,-1369,-1272, + -1177,-1083,-991,-901,-813,-726,-642,-560, + -480,-402,-327,-254,-183,-115,-50,13, + 73,131,186,238,288,335,379,421, + 460,497,531,563,592,618,642,664, + 683,700,715,728,738,747,753,758, + 761,761,761,758,754,749,742,733, + 724,713,701,688,674,660,644,628, + 611,593,575,556,537,518,498,479, + 459,439,419,399,379,359,340,321, + 302,283,265,247,230,213,197,181, + 166,151,137,124,111,99,88,77, + 67,58,50,42,34,28,22 + }; + +/* + Sin table to convert NCO phase to Oscillator outputs +*/ +const int16_t aiSinTable[256] = { + 0,6,13,19,25,31,37,44, + 50,56,62,68,74,80,86,92, + 98,103,109,115,120,126,131,136, + 142,147,152,157,162,167,171,176, + 180,185,189,193,197,201,205,208, + 212,215,219,222,225,228,231,233, + 236,238,240,242,244,246,247,249, + 250,251,252,253,254,254,255,255, + 255,255,255,254,254,253,252,251, + 250,249,247,246,244,242,240,238, + 236,233,231,228,225,222,219,215, + 212,208,205,201,197,193,189,185, + 180,176,171,167,162,157,152,147, + 142,136,131,126,120,115,109,103, + 98,92,86,80,74,68,62,56, + 50,44,37,31,25,19,13,6, + 0,-6,-13,-19,-25,-31,-37,-44, + -50,-56,-62,-68,-74,-80,-86,-92, + -98,-103,-109,-115,-120,-126,-131,-136, + -142,-147,-152,-157,-162,-167,-171,-176, + -180,-185,-189,-193,-197,-201,-205,-208, + -212,-215,-219,-222,-225,-228,-231,-233, + -236,-238,-240,-242,-244,-246,-247,-249, + -250,-251,-252,-253,-254,-254,-255,-255, + -255,-255,-255,-254,-254,-253,-252,-251, + -250,-249,-247,-246,-244,-242,-240,-238, + -236,-233,-231,-228,-225,-222,-219,-215, + -212,-208,-205,-201,-197,-193,-189,-185, + -180,-176,-171,-167,-162,-157,-152,-147, + -142,-136,-131,-126,-120,-115,-109,-103, + -98,-92,-86,-80,-74,-68,-62,-56, + -50,-44,-37,-31,-25,-19,-13,-6 + }; + +//--------------------------------------------------------------------------- +// LOCAL FUNCTIONS +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Perform a MAC Operation (multiply and Accumulate) +// Assumes data is from samples structure, so skips every 2nd sample +// Produces a 64 bit result +// +int64_t MAC_Samples( int64_t llSum, const int32_t *piSamples, const int16_t *piCoefficients, int iCnt ) +{ + int64_t llMult; + + while ( iCnt>0 ) + { + llMult = *piSamples; + llMult *= *piCoefficients; + llSum += llMult; + piSamples++; + piSamples++; // skip other sample pair + piCoefficients++; + iCnt--; + } + return llSum; +} + +//--------------------------------------------------------------------------- +// +// Convert a 64 bit sum to a 32 bit output +// +int32_t ConvertToOutput( int64_t llSum ) +{ + return (int32_t)(llSum>>FIR_SHIFT_FACTOR); +} + +//--------------------------------------------------------------------------- +// DSP Methods +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Set up NCO Increment from a frequency value +// +void TDSPProcessor::NCOFrequency( int32_t iFreq ) +{ + int64_t llFreq; + int64_t llInc; + + // Inc (32bit) = Freq * 2^32 / SampleRate + // Use long long (64 bit int) for calculations to prevent overflow and get best resolution + llFreq = iFreq; + llInc = llFreq * 0x10000 * 0x10000 / SAMPLE_RATE; + // Convert back to 32 bit unsigned via integer type + uiMixerPhaseIncrement = (uint32_t)((int32_t)llInc); + printf( "LO Freq set to %d - NCO Inc set to %u\r\n", iFreq, uiMixerPhaseIncrement ); +} + + +//--------------------------------------------------------------------------- +// +// Reset processing +// +void TDSPProcessor::Reset() +{ + uiMixerPhaseAccumulator = 0; + bLPFPartailsValid = false; + Release(); +} + +//--------------------------------------------------------------------------- +// +// Mix samples with LO (local oscillator) +// +bool TDSPProcessor::MixLO() +{ + int ii; + int iLen = Length(); + TDataSample * pSample; + int16_t iIlo; + int16_t iQlo; + int32_t iIin; + int32_t iQin; + + for ( ii=0,pSample=SamplePtr(); ii<iLen; ii++,pSample++ ) + { + // generate quadrature oscillators. LO I=cos Q=sin + iIlo = aiSinTable[ (64+(uiMixerPhaseAccumulator>>24))&0xFF ]; // COS + iQlo = aiSinTable[ (uiMixerPhaseAccumulator>>24) ]; // SIN + // inc NCO + uiMixerPhaseAccumulator += uiMixerPhaseIncrement; + // scale samples (they are only 24 bits) + iIin = pSample->iIData / 256; + iQin = pSample->iQData / 256; + // complex multiply sample and LO + // (A + Bi) * (C + Di) = (AC - BD) + (BC + AD)i + pSample->iIData = (iIin * iIlo) - (iQin * iQlo); + pSample->iQData = (iQin * iIlo) + (iIin * iQlo); + } + + return true; +} + +//--------------------------------------------------------------------------- +// +// LPF processing +// +bool TDSPProcessor::LPF() +{ +/* + We just code this up for the parameters defined. + It could be dynamically coded, but for simplicity hard-coding will be used +*/ +#if (BUFFERSYS_SIZE!=512) + #error BUFFERSYS_SIZE has changed from 512 +#endif +#if (DSP_FIR_COEFFICIENTS!=511) + #error DSP_FIR_COEFFICIENTS has changed from 511 +#endif +#if (DECIMATION_RATIO!=64) + #error DECIMATION_RATIO has changed from 64 +#endif +#if (LPF_OUTPUTS_SIZE!=8) + #error LPF_OUTPUTS_SIZE has changed from 8 +#endif + + int64_t iSum; + int ii; + bool bRet = false; + + // Outputs + if ( bLPFPartailsValid ) + { + for ( ii=0; ii<8; ii++ ) + { + iSum = MAC_Samples( asLPFPartials[ii].iIData, &(SamplePtr(0)->iIData), &(aiFIRCoefficients[448-(ii*64)]), (ii*64)+62 ); + asLPFOutputs[ii].iIData = ConvertToOutput( iSum ); + iSum = MAC_Samples( asLPFPartials[ii].iQData, &(SamplePtr(0)->iQData), &(aiFIRCoefficients[448-(ii*64)]), (ii*64)+62 ); + asLPFOutputs[ii].iQData = ConvertToOutput( iSum ); + } + bRet = true; + } + // Partials + for ( ii=0; ii<7; ii++ ) + { + asLPFPartials[ii].iIData = MAC_Samples( 0, &(SamplePtr((ii*64)+64)->iIData), &(aiFIRCoefficients[0]), (7-ii)*64 ); + asLPFPartials[ii].iQData = MAC_Samples( 0, &(SamplePtr((ii*64)+64)->iQData), &(aiFIRCoefficients[0]), (7-ii)*64 ); + } + // Partials[7] = 0 + asLPFPartials[7].iIData = 0; + asLPFPartials[7].iQData = 0; + bLPFPartailsValid = true; + + return bRet; +} + +//--------------------------------------------------------------------------- +// END +//---------------------------------------------------------------------------
diff -r 000000000000 -r 82ff15078322 DSP.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DSP.h Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Header File for DSP Processing class + +---------------------------------------------------------------------------*/ +#ifndef _DSP_H +#define _DSP_H + +#include "mbed.h" +#include "BufferSys.h" + +// Definitions + +// Macros + +// +// Classes +// + +//--------------------------------------------------------------------------- +// +// DSP Processor Class - based on a buffer handle. Processes the attached samples buffer +// +class TDSPProcessor : public TBufferHandle +{ + + // parameters + public: + // Set NCO phase inc + // Set inc directly + void NCOPhaseInc( uint32_t uiPhaseInc ) + { uiMixerPhaseIncrement = uiPhaseInc; } + // Set inc from a frequency + void NCOFrequency( int32_t iFreq ); + + // results + public: + // get filtered output data + const TDataSample FilteredOutput( int iIndex ) const + { + if ( !HasBuffer() || (iIndex<0) || (iIndex>=LPF_OUTPUTS_SIZE) ) + return NullSample; // return empty sample + return asLPFOutputs[iIndex]; + } + + // processing operations + public: + // Reset processing + void Reset(); + + // Mix samples with LO + bool MixLO(); + + // LPF processing + bool LPF(); + + // data + protected: + // NCO mixer + uint32_t uiMixerPhaseAccumulator; + uint32_t uiMixerPhaseIncrement; + + // LPF outputs + TDataSample asLPFOutputs[LPF_OUTPUTS_SIZE]; + + // LPF partial results (ready to complete with the next buffer) + TLongDataSample asLPFPartials[LPF_OUTPUTS_SIZE]; + + // LPF Partials valid flag + bool bLPFPartailsValid; + +}; + +#endif + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 I2S_Rx.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2S_Rx.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,285 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + I2S Hardware Driver Routines + +---------------------------------------------------------------------------*/ +// include files + +#include "I2S_Rx.h" + +// Definitions + +// (bit 12) Source burst size = 1 +// (bit 15) Destination burst size = 1 +// (bit 18) Source Width = 32 bits +// (bit 21) Destination Width = 32 bits +// (bit 26) Source Increment +// (bit 27) Destination Increment +// (bit 31) enable INT +#define DMA_CONTROL ( (0x00 << 12) | (0x00 << 15) | (0x02 << 18) | (0x02 << 21) | (0 << 26) | (1 << 27) | (1U << 31) ) +#define DMA_TC_MASK 0xFFF + +// Macros + +// Local Data + +// pointer to receiver - for use by IRQ routine +static TI2SReceiver * pI2SReceiver; + +// Global Data + +// Function Prototypes + +//--------------------------------------------------------------------------- +// IRQ Routines +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// DMA Int routine - test and call the object handler if appropriate +// +void DMA_IRQHandler(void) +{ + uint32_t regVal; + + // read Int TC (terminal count) Status register + regVal = LPC_GPDMA->DMACIntTCStat; + // clear interrupts + LPC_GPDMA->DMACIntTCClear |= regVal; + // test for receiver DMA channel int + if ( regVal&1 ) + pI2SReceiver->DMA_Interrupt(); +} + +//--------------------------------------------------------------------------- +// I2S RECEIVER FUNCTIONS +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// TI2SReceiver constructor +// +TI2SReceiver::TI2SReceiver( TBufferPool &BuffPol ) : + EmptyBuffersPool( BuffPol ), + bRunning( false ), + ulBufferCount( 0 ), + ulBufferAllocations( 0 ), + ulBufferFailures( 0 ) +{ + // Set up I2S pointer for the IRQ routine + pI2SReceiver = this; + + // Enable I2S in the PCONP register (I2S is disabled on reset) + LPC_SC->PCONP |= (1 << 27); + + // Connect the I2S sigals to port pins (P0.23 - P0.25) + LPC_PINCON->PINSEL1 &= ~( (0x3<<14) | (0x3<<16) | (0x3<<18) ); + LPC_PINCON->PINSEL1 |= ( (0x2<<14) | (0x2<<16) | (0x2<<18) ); +} + +//--------------------------------------------------------------------------- +// +// TI2SReceiver Initialise operation +// +void TI2SReceiver::Init() +{ + // Enable I2S in the PCONP register (I2S is disabled on reset) + LPC_SC->PCONP |= (1 << 27); + + // Connect the I2S sigals to port pins (P0.23 - P0.25) + LPC_PINCON->PINSEL1 &= ~( (0x3<<14) | (0x3<<16) | (0x3<<18) ); + LPC_PINCON->PINSEL1 |= ( (0x2<<14) | (0x2<<16) | (0x2<<18) ); + + // Stop reception + LPC_I2S->I2SDAI = 0x03 | (0x1<<3) | (0x1<<4) | (0x1<<5) | (0x1F<<6); + + // Disable DMA + LPC_GPDMACH0->DMACCControl = 0; + + // Initialise DMA + LPC_SC->PCONP |= (1 << 29); // Enable GPDMA clock + LPC_GPDMA->DMACConfig = 0x01; // Enable DMA channels, little endian + while ( !(LPC_GPDMA->DMACConfig & 0x01) ) ; + + // Set up DMA Int routine + NVIC_SetVector( DMA_IRQn, (uint32_t)DMA_IRQHandler ); + NVIC_EnableIRQ( DMA_IRQn ); +} + +//--------------------------------------------------------------------------- +// +// TI2SReceiver Start operation +// +void TI2SReceiver::Start() +{ + // Stop reception + LPC_I2S->I2SDAI = 0x03 | (0x1<<3) | (0x1<<4) | (0x1<<5) | (0x1F<<6); + LPC_GPDMACH0->DMACCConfig = 0; + LPC_GPDMACH0->DMACCControl = 0; + bRunning = false; + LPC_GPDMA->DMACIntTCClear = 0x01; // clear DMA0 flags + LPC_GPDMA->DMACIntErrClr = 0x01; + + // Enable DMA + // Get new buffer and set up the DMA + StartDMATransfer(); + + // Slave receive mode. 32 bit data, stereo + LPC_I2S->I2SDMA1 = (0x1<<0) | (0x02<<8); // channel 1 = Rx DMA + LPC_I2S->I2SRXMODE = 0; + LPC_I2S->I2SDAI = 0x03 | (0x1<<5) | (0x1F<<6); + bRunning = true; +} + +//--------------------------------------------------------------------------- +// +// TI2SReceiver Stop operation +// +void TI2SReceiver::Stop() +{ + bRunning = false; + + // Stop reception + LPC_I2S->I2SDMA1 = 0; + LPC_I2S->I2SDAI = 0x03 | (0x1<<3) | (0x1<<4) | (0x1<<5) | (0x1F<<6); + LPC_GPDMACH0->DMACCConfig = 0; + LPC_GPDMACH0->DMACCControl = 0; + + // clear out buffers + ReceivedBuffers.Flush(); + CurrentRxBuffer.Release(); +} + +//--------------------------------------------------------------------------- +// +// Debug routine - +// Read samples from the I2S by polling it +// Returns number of samples read +// +int TI2SReceiver::PolledRead( int32_t *piBuffer, int iLen ) +{ + int iCnt = 0; + while ( iLen>0 ) + { + // wait for something in the FIFO + if ( ((LPC_I2S->I2SSTATE>>8)&0xF)==0 ) + continue; + // Read it out to the buffer + *piBuffer = LPC_I2S->I2SRXFIFO; + piBuffer++; + iLen--; + iCnt++; + } + return iCnt; +} + +//--------------------------------------------------------------------------- +// +// Debug routine - +// Read status register +// +uint32_t TI2SReceiver::Status() +{ + return LPC_I2S->I2SSTATE; +} +//--------------------------------------------------------------------------- +// +// Debug routine - +// Report Status +// +void TI2SReceiver::Report() +{ + printf( "C-%d\r\n", ulBufferCount ); + printf( "F-%d\r\n", ulBufferFailures ); +/* + printf( "I2S Buffers Counter - %d\r\n", ulBufferCount ); + printf( "I2S Buffer Failures - %d\r\n", ulBufferFailures ); + printf( "I2S Buffer Allocations - %d\r\n", ulBufferAllocations ); + printf( "I2S Status Register: 0x%08X\r\n", LPC_I2S->I2SSTATE ); + printf( "I2S DMA1 Register: 0x%08X\r\n", LPC_I2S->I2SDMA1 ); + printf( "I2S DMA2 Register: 0x%08X\r\n", LPC_I2S->I2SDMA2 ); + printf( "DMA Enabled Register: 0x%08X\r\n", LPC_GPDMA->DMACEnbldChns ); + printf( "DMA TC Status Register: 0x%08X\r\n", LPC_GPDMA->DMACIntTCStat ); + printf( "DMA Err Status Register: 0x%08X\r\n", LPC_GPDMA->DMACIntErrStat ); + printf( "DMA Config Register: 0x%08X\r\n", LPC_GPDMA->DMACConfig ); + printf( "DMA0 Control Register: 0x%08X\r\n", LPC_GPDMACH0->DMACCControl ); + printf( "DMA0 Config Register: 0x%08X\r\n", LPC_GPDMACH0->DMACCConfig ); +*/ +} + +//--------------------------------------------------------------------------- +// +// DMA Interrupt Routine +// +void TI2SReceiver::DMA_Interrupt( ) +{ + // exit if receiver is not running + if ( !bRunning ) + return; + + // Queue current buffer + CurrentRxBuffer.SetLength( 99999 ); // set length to full buffer size + ReceivedBuffers.Write( CurrentRxBuffer ); + ulBufferCount++; + + // Get new buffer and set up the DMA + StartDMATransfer(); +} + +//--------------------------------------------------------------------------- +// +// Start a new DMA transfer +// +void TI2SReceiver::StartDMATransfer() +{ + int32_t *pDataPtr; + int iBufSize; + + // uses DMA0 and sets it up with new buffer (or a dummy buffer if none available) + // DMA channel 0 Source is I2S DMA1 = RX FIFO, Destination is memory + + // count DMA allocations + ulBufferAllocations++; + + // get a buffer + pDataPtr = NULL; + if ( EmptyBuffersPool.Create(CurrentRxBuffer) ) + { // got a buffer + pDataPtr = (int32_t *)(CurrentRxBuffer.SamplePtr()); + iBufSize = CurrentRxBuffer.Size() * 2; // size = samples*2 (samples are I/Q) + CurrentRxBuffer.Timestamp( LPC_TIM2->TC ); // Set timestamp + } + // check for buffer + if ( pDataPtr==NULL ) + { // failed to get a buffer - use the dumy buffer + ulBufferFailures++; + pDataPtr = aulDummyBuffer; + iBufSize = I2S_DUMMY_BUFFER_SIZE; + } + + // clear any ints + LPC_GPDMA->DMACIntTCClear = 0x01; // clear DMA0 flags + LPC_GPDMA->DMACIntErrClr = 0x01; + + // write source addr + LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)(&(LPC_I2S->I2SRXFIFO)); + + // write destination + LPC_GPDMACH0->DMACCDestAddr = (uint32_t)pDataPtr; + + // write LL address - use 0 for single transfer + LPC_GPDMACH0->DMACCLLI = 0; + + // write control & config + LPC_GPDMACH0->DMACCControl = (iBufSize & DMA_TC_MASK) | DMA_CONTROL; + LPC_GPDMACH0->DMACCConfig = 0x08001 | (0x05 << 1) | (0x02 << 11); +} + +//--------------------------------------------------------------------------- +// END +//---------------------------------------------------------------------------
diff -r 000000000000 -r 82ff15078322 I2S_Rx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2S_Rx.h Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Header File for I2S Receiver + + I2S Receiver operates in slave mode + +---------------------------------------------------------------------------*/ +#ifndef _I2S_RX_H +#define _I2S_RX_H + +#include "mbed.h" +#include "BufferSys.h" + +// Definitions + +#define I2S_DUMMY_BUFFER_SIZE 16 + +// Macros + +// +// Classes +// + +//--------------------------------------------------------------------------- +// +// I2S Receiver Class +// +class TI2SReceiver +{ + // create/destroy + public: + TI2SReceiver( TBufferPool &BuffPol ); + ~TI2SReceiver() + { + Stop(); + } + + // API + public: + // Initialise the hardware + void Init(); + + // Start receiving + void Start(); + + // Stop receiving + void Stop(); + + // Receiver status + bool Running() + { return bRunning; } + + // Test for a buffer available + // Return number of queued buffers + int Count() const + { return ReceivedBuffers.Count(); } + // Test if empty + bool Empty() const + { return ReceivedBuffers.Empty(); } + + // Read out a buffer + bool Read( TBufferHandle &Handle ) + { + // queue is filled from interrupts, so we need to ensure we always access it with interrupts off + __disable_irq(); + bool bRet = ReceivedBuffers.Read(Handle); + __enable_irq(); + return bRet; + } + + // report the number of buffer failures + uint32_t BufferFailures() const + { return ulBufferFailures; } + + // DMA IRQ Routine + void DMA_Interrupt(void); + + // Debug routine - + // Read samples from the I2S by polling it + // Returns number of samples read + int PolledRead( int32_t *piBuffer, int iLen ); + + // Debug routine - + // Read status register + uint32_t Status(); + + // Debug routine - + // Report Status + void Report(); + + // Private methods + private: + + // Start a new DMA transfer + void StartDMATransfer(); + + // data + private: + // pool to get buffers from + TBufferPool &EmptyBuffersPool; + + // queue of incoming sample buffers + TBufferQueue ReceivedBuffers; + + // the current buffer being used + TBufferHandle CurrentRxBuffer; + + // flags indicating run status + bool bRunning; + + // a buffer to use when there are no empty buffers available + int32_t aulDummyBuffer[I2S_DUMMY_BUFFER_SIZE]; + + // frame counters etc + uint32_t ulBufferCount; + uint32_t ulBufferAllocations; + uint32_t ulBufferFailures; + +}; + +#endif + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 NetServices.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetServices.lib Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/simon/code/NetServices/#350011bf8be7
diff -r 000000000000 -r 82ff15078322 comms.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/comms.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,328 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Communications Module + + +---------------------------------------------------------------------------*/ +// include files + +#include "comms.h" + +// Definitions + +#define COMMS_DEBUG 0 + +// Macros + +// Local Data + +// Global Data + + +// Function Prototypes + +//--------------------------------------------------------------------------- +// API METHODS +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Initialisation Method +// +void TCommunications::Init() +{ + eState = ST_START_ETH; + iBufferCount = 0; + PollTimer.start(); + CommandTimer.start(); + uiTestModeInc = 0; +} + +//--------------------------------------------------------------------------- +// +// Processing Method +// Main state machine +// +void TCommunications::Poll() +{ + UDPSocketErr udpErr; + EthernetErr ethErr; + + switch ( eState ) + { + case ST_START_ETH: // Initialise the Ethernet interface + EthernetUpLED = 0; + RunningLED = 0; + ethErr = eth.setup( 25000 ); + if ( ethErr == ETH_OK ) + { + const char * pcHWAddr = eth.getHwAddr(); + IpAddr ip = eth.getIp(); + printf("mbed MAC Address is %02X-%02X-%02X-%02X-%02X-%02X\r\n", pcHWAddr[0], pcHWAddr[1], pcHWAddr[2], pcHWAddr[3], pcHWAddr[4], pcHWAddr[5] ); + printf("mbed IP Address is %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3] ); + } + // Set up UDP + ServerSocket.setOnEvent( this, &TCommunications::onUDPSocketEvent ); + MyPort.setIp( IpAddr() ); + MyPort.setPort( QRSS_UDP_PORT ); + ServerSocket.bind( MyPort ); + udpErr = ServerSocket.bind( MyPort ); + if ( udpErr != UDPSOCKET_OK ) + { + printf("UDP Bind error %d\r\n", udpErr); + break; + } + // to poll state, and force sending of poll message + printf("*POLL_SERVER State*\r\n" ); + eState = ST_POLL_SERVER; + iPPSCount = POLL_MSG_PERIOD; + bGotCmnd = false; + break; + + case ST_POLL_SERVER: // Poll the server and wait for it to start the system + EthernetUpLED = 1; + RunningLED = 0; + if ( (iPPSCount>=POLL_MSG_PERIOD) || (PollTimer.read_ms()>POLL_TIMEOUT) ) + { // Send a poll message + iPPSCount = 0; + ServerAddr = dnsService.resolveName( szServerName ); + if ( ServerAddr.isNull() ) + { + printf("DNS Failed - *START_ETH State*\r\n" ); + eState = ST_START_ETH; + break; + } + ServerPort.setIp( ServerAddr ); + ServerPort.setPort( QRSS_UDP_PORT ); + if ( !SendPollMessage() ) + { + printf("*START_ETH State*\r\n" ); + eState = ST_START_ETH; + break; + } + } + if ( bGotCmnd ) + { + bGotCmnd = false; + if ( uiCommand!=0 ) + { // start + uiNCOPhaseInc = uiNCO; + uiTestModeInc = uiTestOsc; + printf("*RUNNING State*\r\n" ); + eState = ST_RUNNING; + iBufferCount = 0; + CommandTimer.reset(); + } + } + break; + + case ST_RUNNING: // Running - transfer data to the server + EthernetUpLED = 1; + RunningLED = 1; + if ( (iPPSCount>=POLL_MSG_PERIOD) || (PollTimer.read_ms()>POLL_TIMEOUT) ) + { // Send a poll message + iPPSCount = 0; + if ( !SendPollMessage() ) + { + printf("*START_ETH State*\r\n" ); + eState = ST_START_ETH; + break; + } + } + if ( bGotCmnd ) + { + bGotCmnd = false; + CommandTimer.reset(); + if ( uiCommand==0 ) + { // stop + printf("*POLL_SERVER State*\r\n" ); + eState = ST_POLL_SERVER; + iPPSCount = POLL_MSG_PERIOD; + iBufferCount = 0; + } + } + if ( CommandTimer.read_ms()>RUNNING_TIMEOUT ) + { + printf("Timeout - *POLL_SERVER State*\r\n" ); + eState = ST_POLL_SERVER; + iPPSCount = POLL_MSG_PERIOD; + iBufferCount = 0; + } + break; + } + +} + +//--------------------------------------------------------------------------- +// +// RecordPPSTimestamps Method +// Records timestamps of events (1PPS or capture of LO divider) +// +void TCommunications::RecordPPSTimestamps( uint32_t uiPPSCapture, uint32_t uiLOscCapture, + TGPSController::TGPSData &GPSInfo ) +{ + int ii; + + #if COMMS_DEBUG>0 + printf( "PPS-Timestamp:Time %d Count %d PPSCap %u LOCap %u PPS %u LO %u\r\n", + LastGPSInfo.iGPSTimeSeconds, iPPSCount, uiPPSCapture, uiLOscCapture, + uiPPSCapture-auiPPSCaptures[0], uiLOscCapture-auiLOscCaptures[0] ); + #endif + + // clear the captures if GPS is non-operation + if ( GPSInfo.iGPSQuality<=0 ) + { // GPS is offline + uiPPSCapture = 0; + uiLOscCapture = 0; + } + + // shuffle the queue along + for ( ii=7; ii>0; ii-- ) + { + auiPPSCaptures[ii] = auiPPSCaptures[ii-1]; + auiLOscCaptures[ii] = auiLOscCaptures[ii-1]; + } + // record newest data + auiPPSCaptures[0] = uiPPSCapture; + auiLOscCaptures[0] = uiLOscCapture; + LastGPSInfo = GPSInfo; + + // count event + iPPSCount++; + +} + +//--------------------------------------------------------------------------- +// +// Pass sample data for sending. Send when buffer is full +// +void TCommunications::SendSamples( uint32_t uiTimestamp, TDataSample sSample ) +{ + if ( eState!=ST_RUNNING ) + return; + + if ( iBufferCount==0 ) + { // Build buffer header + pucBuffDataPtr = (uint8_t *)auiDataBuffer; + WriteWord( pucBuffDataPtr, COMM_DATA_MSG, iBufferCount ); + WriteWord( pucBuffDataPtr, uiTimestamp, iBufferCount ); + } + // append data + *((TDataSample*)pucBuffDataPtr) = sSample; + pucBuffDataPtr += SAMPLE_SET_SIZE; + iBufferCount += SAMPLE_SET_SIZE; + // send buffer when full + if ( iBufferCount>=COMMS_DATABUFF_SIZE ) + { // send message + int iRet = ServerSocket.sendto( (char *)auiDataBuffer ,iBufferCount, &ServerPort ); + if ( iRet!=iBufferCount ) + printf("Sent Error - %d\r\n", iRet ); + iBufferCount = 0; + } +} + +//--------------------------------------------------------------------------- +// PRIVATE METHODS +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// UDP Callback Method +// +void TCommunications::onUDPSocketEvent( UDPSocketEvent evnt ) +{ + if ( evnt==UDPSOCKET_READABLE ) + { + uint8_t aucInBf[COMMS_CNTRLBUFF_SIZE]; + Host FromHost; + int iLen; + while ( 1 ) + { + iLen = ServerSocket.recvfrom( (char *)aucInBf, COMMS_CNTRLBUFF_SIZE, &FromHost ); + if ( iLen<=0 ) + break; + if ( iLen!=COMM_CMND_MSG_LENGTH ) + continue; + if ( ReadWord(&(aucInBf[0]))!=COMM_CMND_MSG ) + continue; + if ( ReadWord(&(aucInBf[4]))!=COMM_MSG_VERSION ) + continue; + // have a real message! + uiCommand = ReadWord( &(aucInBf[8]) ); + uiNCO = ReadWord( &(aucInBf[12]) ); + uiTestOsc = ReadWord( &(aucInBf[16]) ); + bGotCmnd = true; + #if COMMS_DEBUG>0 + printf("Command %d\r\n", uiCommand ); + printf("NCO %d\r\n", uiNCO ); + printf("Test %d\r\n", uiTestOsc ); + #endif + } + } +} + +//--------------------------------------------------------------------------- +// +// Send a UDP Poll Message +// +bool TCommunications::SendPollMessage() +{ + // Build poll message + uint8_t aucPollBuffer[150]; + uint8_t *pucPtr = aucPollBuffer; + int iLen = 0; + int iRet; + + // reset the backup timer + PollTimer.reset(); + + // Build Message + WriteWord( pucPtr, COMM_POLL_MSG, iLen ); + WriteWord( pucPtr, COMM_MSG_VERSION, iLen ); + WriteWord( pucPtr, COMM_MSG_HW_ID, iLen ); + WriteWord( pucPtr, COMM_MSG_SW_VERSION, iLen ); + WriteWord( pucPtr, LastGPSInfo.iGPSQuality, iLen ); + WriteWord( pucPtr, LastGPSInfo.iGPSSatellites, iLen ); + WriteWord( pucPtr, LastGPSInfo.iGPSTimeSeconds, iLen ); + WriteWord( pucPtr, LastGPSInfo.iGPSLatMicroDegrees, iLen ); + WriteWord( pucPtr, LastGPSInfo.iGPSLongMicroDegrees, iLen ); + WriteWord( pucPtr, LastGPSInfo.iGPSAltitudeMeters, iLen ); + WriteWord( pucPtr, COMM_MSG_REF_CLOCK, iLen ); + WriteWord( pucPtr, COMM_MSG_RF_MIXER, iLen ); + WriteWord( pucPtr, COMM_MSG_SAMPLE_CLOCK, iLen ); + WriteWord( pucPtr, COMM_MSG_SAMPLE_DIVIDER, iLen ); + WriteWord( pucPtr, auiPPSCaptures[0], iLen ); + WriteWord( pucPtr, auiPPSCaptures[1], iLen ); + WriteWord( pucPtr, auiPPSCaptures[2], iLen ); + WriteWord( pucPtr, auiPPSCaptures[3], iLen ); + WriteWord( pucPtr, auiPPSCaptures[4], iLen ); + WriteWord( pucPtr, auiPPSCaptures[5], iLen ); + WriteWord( pucPtr, auiPPSCaptures[6], iLen ); + WriteWord( pucPtr, auiPPSCaptures[7], iLen ); + WriteWord( pucPtr, auiLOscCaptures[0], iLen ); + WriteWord( pucPtr, auiLOscCaptures[1], iLen ); + WriteWord( pucPtr, auiLOscCaptures[2], iLen ); + WriteWord( pucPtr, auiLOscCaptures[3], iLen ); + WriteWord( pucPtr, auiLOscCaptures[4], iLen ); + WriteWord( pucPtr, auiLOscCaptures[5], iLen ); + WriteWord( pucPtr, auiLOscCaptures[6], iLen ); + WriteWord( pucPtr, auiLOscCaptures[7], iLen ); + memcpy( pucPtr, COMM_MSG_NAME, COMM_MSGNAME_LEN ); + pucPtr += COMM_MSGNAME_LEN; + iLen += COMM_MSGNAME_LEN; + + // send message + iRet = ServerSocket.sendto( (char *)aucPollBuffer ,iLen, &ServerPort ); + if ( iRet < 0 ) + return false; + return true; +} + +//--------------------------------------------------------------------------- +// END +//---------------------------------------------------------------------------
diff -r 000000000000 -r 82ff15078322 comms.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/comms.h Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,211 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Header File for Communications Module + +---------------------------------------------------------------------------*/ +#ifndef _COMMS_H +#define _COMMS_H + +#include "mbed.h" +#include "global.h" +#include "EthernetNetIf.h" +#include "UDPSocket.h" +#include "dnsresolve.h" +#include "BufferSys.h" +#include "gps.h" + +// Definitions + +// UDP output data size - includes header +#define COMMS_DATABUFF_HEADER (4+4) +#define COMMS_DATABUFF_SIZE ((LPF_OUTPUTS_SIZE*SAMPLE_SET_SIZE*8)+COMMS_DATABUFF_HEADER) + +// UDP Control Buffer Size +#define COMMS_CNTRLBUFF_SIZE (60) + +// Number of seconds between each poll message +#define POLL_MSG_PERIOD (6) + +// default poll period in absence of 1PPS (mSec) +#define POLL_TIMEOUT (10000) + +// time to stop sending data if no commands are received +#define RUNNING_TIMEOUT (30000) + +// +// Message Encoding +// +#define COMM_POLL_MSG 10 +#define COMM_CMND_MSG 11 +#define COMM_DATA_MSG 12 +#define COMM_MSG_VERSION 1 +#define COMM_MSG_SW_VERSION 1 +#define COMM_MSG_HW_ID 1 +#define COMM_MSG_REF_CLOCK 96000000 +#define COMM_MSG_RF_MIXER 10125000 +#define COMM_MSG_SAMPLE_CLOCK 13500000 +#define COMM_MSG_SAMPLE_DIVIDER (384*64) +#define COMM_MSG_NAME "QRSS_proto" +#define COMM_MSGNAME_LEN 10 + +#define COMM_CMND_MSG_LENGTH (5*4) + +// Macros + +// +// Classes +// + +//--------------------------------------------------------------------------- +// +// Communications Class +// +class TCommunications +{ + // create/destroy + public: + TCommunications() : + EthernetUpLED( LED2 ), + RunningLED( LED4 ) + {} + ~TCommunications() + {} + + // Local Types - state machine state + typedef enum { + ST_START_ETH, + ST_POLL_SERVER, + ST_RUNNING + } TCommsStates; + + // API + public: + // Initialisation + void Init(); + + // Set up Server DNS name + void SetServer( char *szNm ) + { + strncpy( szServerName, szNm, sizeof(szServerName) ); + szServerName[sizeof(szServerName)-1] = 0; // place terminator at end incase name was too long + } + + // Processing routine + void Poll(); + + // RecordPPSTimestamps Method + // Records timestamps of events (1PPS or capture of LO divider) + void RecordPPSTimestamps( uint32_t uiPPSCapture, uint32_t uiLOscCapture, + TGPSController::TGPSData &GPSInfo ); + + // Test if comms is ready for sample data (ie in RUNNING state) + bool Running() + { return (eState==ST_RUNNING); } + + // Return NCO Inc setting received + uint32_t NCOPhaseInc() + { return uiNCOPhaseInc; } + + // Return Test Mode setting received + uint32_t TestMode() + { return uiTestModeInc; } + + // Pass sample data for sending. Send when buffer is full + void SendSamples( uint32_t uiTimestamp, TDataSample sSample ); + + + // Private methods + private: + // Callback for incoming UDP data + void onUDPSocketEvent( UDPSocketEvent evnt ); + + // Send a UDP Poll Message + bool SendPollMessage(); + + // Place a word into a buffer - network byte order + void WriteWord( uint8_t * &pucBuff, int32_t iWord, int &iLn ) + { + *pucBuff = (uint8_t)(iWord>>24); + pucBuff++; + *pucBuff = (uint8_t)(iWord>>16); + pucBuff++; + *pucBuff = (uint8_t)(iWord>>8); + pucBuff++; + *pucBuff = (uint8_t)(iWord>>0); + pucBuff++; + iLn += 4; + } + + // Read a word from a buffer - network byte order + uint32_t ReadWord( uint8_t * pucBuff ) + { return (0x1000000*pucBuff[0]) | (0x10000*pucBuff[1]) | (0x100*pucBuff[2]) | (pucBuff[3]); } + + // data + private: + // LEDs + DigitalOut EthernetUpLED; + DigitalOut RunningLED; + + // State machine state + TCommsStates eState; + + // Ethernet Interface + EthernetNetIf eth; + + // UDP Socket for comms + char szServerName[50]; + UDPSocket ServerSocket; + Host MyPort; + Host ServerPort; + IpAddr ServerAddr; + + // DNS lookup + DNSResolver dnsService; + + // Host Details + + // Output buffers - word align this buffer + uint32_t auiDataBuffer[(COMMS_DATABUFF_SIZE/4)+8]; // the buffer - defined as words to ensure alignment + uint8_t *pucBuffDataPtr; // buffer pointer - as a byte pointer + int iBufferCount; // byte count + + // Incoming UDP Data + uint32_t uiCommand; + uint32_t uiNCO; + uint32_t uiTestOsc; + bool bGotCmnd; + + // PPS Timer Event Counter + int iPPSCount; + + // Send Poll Message timer + Timer PollTimer; + Timer CommandTimer; + + // Timer Capture Data - queue of 8 captures. [0] is the most recent + uint32_t auiPPSCaptures[8]; + uint32_t auiLOscCaptures[8]; + + // Last GPS info + TGPSController::TGPSData LastGPSInfo; + + // data from the command packet + uint32_t uiNCOPhaseInc; + uint32_t uiTestModeInc; + +}; + +// declare the Communications module +extern TCommunications Comms; + +#endif + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 global.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/global.h Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Header File for global definitions + +---------------------------------------------------------------------------*/ +#ifndef _GLOBAL_H +#define _GLOBAL_H + +// Definitions + +// Size of data buffers (in samples) +#define BUFFERSYS_SIZE 512 + +// Number of buffers +#define NUM_OF_BUFFERS 4 + +// Number of co-efficients in the FIR filter +#define DSP_FIR_COEFFICIENTS 511 + +// ADC Clock (13.5MHz) +#define ADC_CLOCK 13500000 + +// Sample Rate (35156 SPS) +#define SAMPLE_RATE (ADC_CLOCK/384) + +// Decimation amount - ratio +#define DECIMATION_RATIO 64 + +// Size of LPF output per Buffer +// (BUFFERSYS_SIZE/DECIMATION_RATIO) +#define LPF_OUTPUTS_SIZE 8 + +// Decimation Rate (35156/64 = 549 SPS) +#define DECIMATED_RATE (SAMPLE_RATE/DECIMATION_RATIO) + +// Scale Factor rate of FIR filter result to output sample +// 32 bit sample * 16 bit coefficient * 511 scaled down to 32 bits +// 32+16+9 bits = 57. Reduce by 25 +#define FIR_SHIFT_FACTOR 25 + +// First LO in the Softrock (10.125MHz) +#define FIRST_LO (ADC_CLOCK*3/4) + +// Test NCO freq - approx 15kHz +#define TEST_NCO_FREQ (15600) + +// Size of each sample set in bytes - size of TDataSample +#define SAMPLE_SET_SIZE (8) + +// QRSS System UDP Port +#define QRSS_UDP_PORT 6595 + +// comments + +// Macros + +#endif + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 gps.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gps.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,430 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + GPS Module + +---------------------------------------------------------------------------*/ +// include files + +#include "gps.h" +#include "comms.h" + +// Definitions + +#define GPS_DEBUG 0 + +// comments + +// Macros + +// Local Data + +// Global Data +TGPSController GPSModule; + + +// Function Prototypes + +//--------------------------------------------------------------------------- +// TGPSController Class Methods +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Constructor +// +TGPSController::TGPSController() : + PPSInput( p30 ), + GPSPort( p28, p27 ), + GPSUpLED( LED3 ), + PPSEvent( p30 ), + bPulsed( false ), + uiPPSCapture( 0 ), + uiLOscCapture( 0 ) +{ + // clear current and last line + szCurrentLine[0] = 0; + szLastLine[0] = 0; + bNewLine = false; +} + +//--------------------------------------------------------------------------- +// +// Initialise routine +// +void TGPSController::Init() +{ + // Set up GPS port + GPSPort.baud( 4800 ); + + // capture 1PPS event + PPSEvent.rise( this, &TGPSController::PPSPulse ); + + // Start timeout timer + GPSMsgTimeout.start(); + GPSPulseTimeout.start(); + + // Set up timer hardware + LPC_PINCON->PINSEL0 |= ( (0x3<<8) | (0x3<<10) ); // enable CAP2.0, CAP2.1 + LPC_SC->PCONP |= 1 << 22; // power up TIMER2 (PCONP[22]) + LPC_SC->PCLKSEL1 &= ~(0x3<<12); + LPC_SC->PCLKSEL1 |= (0x1<<12); // Timer2 PCLK = CCLK + LPC_TIM2->TCR = 0x2; // reset timer + LPC_TIM2->CTCR = 0x0; // timer mode + LPC_TIM2->PR = 0; // no prescale + LPC_TIM2->MCR = 0x0; // no match + LPC_TIM2->CCR = (1<<0) | (1<<3); // Capture rising edges on both CAP2.0 & CAP2.1 + LPC_TIM2->TCR = 1; // start the timer +} + +//--------------------------------------------------------------------------- +// +// Poll routine +// +void TGPSController::Poll() +{ + // Test for a 1PPS pulse + if ( bPulsed ) + { // 1PPS from the GPS unit + bPulsed = false; + GPSPulseTimeout.reset(); + + // flush out any old serial data + while ( GPSPort.readable() ) + GPSPort.getc(); + #if GPS_DEBUG>2 + printf( "***1PPS***\r\n" ); + #endif + // Record capture data to comms module + Comms.RecordPPSTimestamps( uiPPSCapture, uiLOscCapture, GPSRecord ); + } + + // Test for GPS serial port data + while ( GPSPort.readable() ) + { + char cNextChar = GPSPort.getc(); + #if GPS_DEBUG>3 + printf( "%c", cNextChar ); + #endif + if ( ParseData(cNextChar) ) + { // have a completed GPS sentence + GPSMsgTimeout.reset(); + // analysis the sentence data + int iStatus = ProcessGPSData(); + if ( iStatus==0 ) + { // good sentence + #if GPS_DEBUG>0 + printf( "Time: %d\r\n", GPSRecord.iGPSTimeSeconds ); + printf( "Lat: %d\r\n", GPSRecord.iGPSLatMicroDegrees ); + printf( "Long: %d\r\n", GPSRecord.iGPSLongMicroDegrees ); + printf( "Alt: %d\r\n", GPSRecord.iGPSAltitudeMeters ); + printf( "Sats: %d\r\n", GPSRecord.iGPSSatellites ); + printf( "Qual: %d\r\n", GPSRecord.iGPSQuality ); + #endif + } + } + } + + // Test for GPS timeout (no data) + if ( GPSMsgTimeout.read_ms()>GPS_MSGTIMEOUT ) + { // No GPS messages for a period + #if GPS_DEBUG>1 + printf( "GPS Timeout - setting to offline\r\n" ); + #endif + GPSRecord.iGPSQuality = 0; // set quality to 0 - invalid data + GPSMsgTimeout.reset(); + // Record invalid GPS status to comms module + Comms.RecordPPSTimestamps( 0, 0, GPSRecord ); + } + + // Test for GPS timeout (no 1PPS) + if ( GPSPulseTimeout.read_ms()>GPS_PULSETIMEOUT ) + { // No GPS pulse for a period + // we just forward data to Comms module with invalid capture data + Comms.RecordPPSTimestamps( 0, 0, GPSRecord ); + GPSPulseTimeout.reset(); + } + +} + +//--------------------------------------------------------------------------- +// +// Parse data from the GPS +// Return true if a completed valid sentence is received +// +#define EXPECT_CHAR(CC) if (cChr==CC)iParseState++; else iParseState=0;ResetNumber() +#define PARSE_NUMBER(NN) if(cChr==','){NN=ParseNum;ResetNumber();iParseState++;break;}if (!ParseNumber(cChr))iParseState=0 +#define PARSE_CHARACTER(CH) if(cChr==','){CH=cCharacter;ResetNumber();iParseState++;break;}cCharacter=cChr +// +bool TGPSController::ParseData( char cChr ) +{ + ucChecksum ^= cChr; + switch ( iParseState ) + { + case 0: + if ( cChr=='$' ) + iParseState++; + ucChecksum = 0; + break; + case 1: EXPECT_CHAR('G'); break; + case 2: EXPECT_CHAR('P'); break; + case 3: EXPECT_CHAR('G'); break; + case 4: EXPECT_CHAR('G'); break; + case 5: EXPECT_CHAR('A'); break; + case 6: EXPECT_CHAR(','); break; + case 7: PARSE_NUMBER( GPSTime ); break; + case 8: PARSE_NUMBER( GPSLatitude ); break; + case 9: PARSE_CHARACTER( GPSLatNS ); break; + case 10: PARSE_NUMBER( GPSLongitude ); break; + case 11: PARSE_CHARACTER( GPSLongEW ); break; + case 12: PARSE_NUMBER( GPSQuality ); break; + case 13: PARSE_NUMBER( GPSSatellites ); break; + case 14: PARSE_NUMBER( GPSDOP ); break; + case 15: PARSE_NUMBER( GPSAltitude ); break; + case 16: PARSE_CHARACTER( GPSAltType ); break; + case 17: PARSE_NUMBER( GPSHeight ); break; + case 18: PARSE_CHARACTER( GPSHeightType ); break; + case 19: EXPECT_CHAR(','); + ucFinalChecksum = ucChecksum; + break; + case 20: EXPECT_CHAR('*'); break; + case 21: iParseState++; + if ( (cChr>='0') && (cChr<='9') ) + ucMsgChecksum = (cChr-'0') << 4; + else if ( (cChr>='A') && (cChr<='F') ) + ucMsgChecksum = (cChr-'A'+10) << 4; + else + iParseState = 0; + break; + case 22: iParseState++; + if ( (cChr>='0') && (cChr<='9') ) + ucMsgChecksum |= (cChr-'0'); + else if ( (cChr>='A') && (cChr<='F') ) + ucMsgChecksum |= (cChr-'A'+10); + else + iParseState = 0; + break; + case 23: // don't care about char (should be a CR) + // just check the results + if ( ucMsgChecksum==ucFinalChecksum ) + { // Checksum okay + // reset + iParseState = 0; + // return valid message + return true; + } + #if GPS_DEBUG>0 + else + printf( "!GPS Check failed - got %02X wanted %02X\r\n", (int)ucMsgChecksum, (int)ucFinalChecksum ); + #endif + // reset + iParseState = 0; + break; + } + + #if GPS_DEBUG>2 + // report parser state + printf( ":%d:", iParseState ); + #endif + + // GPS sentence note yet completed + return false; +} + +//--------------------------------------------------------------------------- +// +// Parse a number character by character +// If character is invalid, returns false +// +bool TGPSController::ParseNumber( char cChar ) +{ + // process digits + if ( (cChar>='0') && (cChar<='9') ) + { + ParseNum.uiNumber = (ParseNum.uiNumber*10) + (cChar-'0'); + ParseNum.iNumberLen++; + if ( ParseNum.iNumberDecimals>=0 ) + ParseNum.iNumberDecimals++; + return true; + } + if ( cChar=='.' ) + { + if ( ParseNum.iNumberDecimals>=0 ) + return false; // a second decimal point! + ParseNum.iNumberDecimals = 0; + return true; + } + if ( cChar=='-' ) + { + if ( ParseNum.iNumberLen>0 ) + return false; // '-' must be the first character + ParseNum.iNumberSign = -1; + return true; + } + // otherwise an invalid character + return false; +} + +//--------------------------------------------------------------------------- +// +// Reset the number parser +// +void TGPSController::ResetNumber( ) +{ + ParseNum.uiNumber = 0; + ParseNum.iNumberLen = 0; + ParseNum.iNumberSign = 1; + ParseNum.iNumberDecimals = -1; + cCharacter = 0; +} + + +//--------------------------------------------------------------------------- +// +// Process the data from the GPS message +// Returns 0 if all data is good, or an error code +// +int TGPSController::ProcessGPSData( ) +{ + // check Quality + if ( GPSQuality.iNumberLen<1 ) return 10; + if ( GPSQuality.iNumberDecimals!=-1 ) return 11; + if ( GPSQuality.iNumberSign!=1 ) return 12; + // check Satellites + if ( GPSSatellites.iNumberLen<1 ) return 20; + if ( GPSSatellites.iNumberDecimals!=-1 ) return 21; + if ( GPSSatellites.iNumberSign!=1 ) return 22; + // Store sats and quality parameters + GPSRecord.iGPSSatellites = GPSSatellites.uiNumber; + GPSRecord.iGPSQuality = GPSQuality.uiNumber; + // Check quality level + if ( GPSQuality.uiNumber<1 ) return 100; // no fix + // check Time + if ( GPSTime.iNumberLen<6 ) return 30; + if ( GPSTime.iNumberSign!=1 ) return 32; + // check Latitude + if ( GPSLatitude.iNumberLen!=7 ) return 40; + if ( GPSLatitude.iNumberDecimals!=3 ) return 41; + if ( GPSLatitude.iNumberSign!=1 ) return 42; + if ( (GPSLatNS!='N') && (GPSLatNS!='S') ) return 43; + // check Longitude + if ( GPSLongitude.iNumberLen!=8 ) return 50; + if ( GPSLongitude.iNumberDecimals!=3 ) return 51; + if ( GPSLongitude.iNumberSign!=1 ) return 52; + if ( (GPSLongEW!='E') && (GPSLongEW!='W') ) return 53; + // check Altitude + if ( GPSAltitude.iNumberLen<1 ) return 60; + // Don't care about DOPs and Height and Types + // Translate & Store parameters + + // discard fractions of seconds + while ( (GPSTime.iNumberDecimals--)>0 ) + GPSTime.uiNumber /= 10; + + int32_t iHours = GPSTime.uiNumber/10000; + int32_t iMins = (GPSTime.uiNumber/100)%100; + int32_t iSecs = GPSTime.uiNumber%100; + GPSRecord.iGPSTimeSeconds = iSecs + (60*iMins) + (3600*iHours); + + int32_t iDegrees = GPSLatitude.uiNumber / 100000; + int32_t iMilliMinutes = GPSLatitude.uiNumber % 100000; + GPSRecord.iGPSLatMicroDegrees = (iMilliMinutes * 100 / 6) + (iDegrees*1000000); + if ( GPSLatNS=='S' ) + GPSRecord.iGPSLatMicroDegrees = -GPSRecord.iGPSLatMicroDegrees; + + iDegrees = GPSLongitude.uiNumber / 100000; + iMilliMinutes = GPSLongitude.uiNumber % 100000; + GPSRecord.iGPSLongMicroDegrees = (iMilliMinutes * 100 / 6) + (iDegrees*1000000); + if ( GPSLongEW=='W' ) + GPSRecord.iGPSLatMicroDegrees = -GPSRecord.iGPSLatMicroDegrees; + + GPSRecord.iGPSAltitudeMeters = GPSAltitude.uiNumber * GPSAltitude.iNumberSign; + while ( (GPSAltitude.iNumberDecimals--)>0 ) + GPSRecord.iGPSAltitudeMeters /= 10; + + return 0; +} + +//--------------------------------------------------------------------------- +// +// PPS Event routine +// +void TGPSController::PPSPulse() +{ + // indicate we have pulsed + bPulsed = true; + + // capture timer capture registers + // 1PPP = CAP2.0 + // LOsc (/4096) = CAP2.1 + uiPPSCapture = LPC_TIM2->CR0; + uiLOscCapture = LPC_TIM2->CR1; + +} + +/* +GPS Messages: + +GGA - essential fix data which provide 3D location and accuracy data. + + $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + +Where: + GGA Global Positioning System Fix Data + 123519 Fix taken at 12:35:19 UTC + 4807.038,N Latitude 48 deg 07.038' N + 01131.000,E Longitude 11 deg 31.000' E + 1 Fix quality: 0 = invalid + 1 = GPS fix (SPS) + 2 = DGPS fix + 3 = PPS fix + 4 = Real Time Kinematic + 5 = Float RTK + 6 = estimated (dead reckoning) (2.3 feature) + 7 = Manual input mode + 8 = Simulation mode + 08 Number of satellites being tracked + 0.9 Horizontal dilution of position + 545.4,M Altitude, Meters, above mean sea level + 46.9,M Height of geoid (mean sea level) above WGS84 + ellipsoid + (empty field) time in seconds since last DGPS update + (empty field) DGPS station ID number + *47 the checksum data, always begins with * + + +VTG - Velocity made good. The gps receiver may use the LC prefix instead of GP if it is emulating Loran output. + + $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48 + +where: + VTG Track made good and ground speed + 054.7,T True track made good (degrees) + 034.4,M Magnetic track made good + 005.5,N Ground speed, knots + 010.2,K Ground speed, Kilometers per hour + *48 Checksum + + + +$GPGGA,,,,,,0,05,,,,,,,*63 +$GPGGA,,,,,,0,02,,,,,,,*64 +$GPGGA,,,,,,0,03,,,,,,,*65 +$GPGGA,120609.46,3515.405,S,14904.873,E,0,04,2.24,718,M,14,M,,*42 + $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48 + $GPGGA,120610.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*4B + $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48 + $GPGGA,120611.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*4A + $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48 + $GPGGA,120612.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*49 + +*/ + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 gps.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gps.h Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + Header File for GPS Module + +---------------------------------------------------------------------------*/ +#ifndef _GPS_H +#define _GPS_H + +#include "mbed.h" + +// Definitions + +#define GPS_FIELDLEN 20 +#define GPS_LINELENGTH 200 +#define GPS_MSGTIMEOUT 3000 // GPS message timeout (mSecs) +#define GPS_PULSETIMEOUT 1500 // GPS pulse timeout (mSecs) + +// comments + +// Macros + +// +// Classes +// + +//--------------------------------------------------------------------------- +// +// GPS Module Class +// +class TGPSController +{ + // create/destroy + public: + TGPSController(); + ~TGPSController() {} + + // public type - GPS data + public: + typedef struct _gpsdata + { + int32_t iGPSQuality; + int32_t iGPSSatellites; + int32_t iGPSTimeSeconds; + int32_t iGPSLatMicroDegrees; + int32_t iGPSLongMicroDegrees; + int32_t iGPSAltitudeMeters; + } TGPSData; + + // local type - for parsing message numbers + private: + typedef struct _parsenumber + { + uint32_t uiNumber; + int iNumberLen; + int iNumberSign; + int iNumberDecimals; + } TParsedNum; + + // API + public: + // Initi8alise routine + void Init(); + + // Processing routine + void Poll(); + + // Return the last line from the GPS + const char * LastLine() + { return szLastLine; } + + // indicate a new last line has been received from the GPS unit + // Self resetting + bool NewLine() + { + bool bRet = bNewLine; + bNewLine = false; + return bRet; + } + + // Get GPS Data + // Returns status value (0=offline) + int GPSData( TGPSData &GPSDat ) + { GPSDat = GPSRecord; + return GPSDat.iGPSQuality; } + + // Private methods + private: + // callback on 1PPS rising edge + void PPSPulse(); + + // Parse a number + bool ParseNumber( char cChr ); + void ResetNumber(); + + // Parse data from the GPS + // Return true if a completed valid sentence is received + bool ParseData( char cChr ); + + // Process the data from the GPS message + // Returns 0 if all data is good, or an error code + // Call after a valid ParseData has occurred + int ProcessGPSData(); + + // data + private: + // 1PPS pin + DigitalIn PPSInput; + + // GPS Serial Port + Serial GPSPort; + + // GPS Status LED + DigitalOut GPSUpLED; + + // Interrupt on 1PPS pulse + InterruptIn PPSEvent; + + // 1PPS flag and captured timer registers + bool bPulsed; + uint32_t uiPPSCapture; + uint32_t uiLOscCapture; + + // GPS data timeout timer + Timer GPSMsgTimeout; + Timer GPSPulseTimeout; + + // Parsing data and resulting values + int iParseState; + TParsedNum ParseNum; + char cCharacter; + + // last line variables + char szCurrentLine[GPS_LINELENGTH+1]; + char szLastLine[GPS_LINELENGTH+1]; + bool bNewLine; + + // GPS message data - raw data from message + TParsedNum GPSTime; + TParsedNum GPSLatitude; + char GPSLatNS; + TParsedNum GPSLongitude; + char GPSLongEW; + TParsedNum GPSQuality; + TParsedNum GPSSatellites; + TParsedNum GPSDOP; + TParsedNum GPSAltitude; + char GPSAltType; + TParsedNum GPSHeight; + char GPSHeightType; + uint8_t ucMsgChecksum; + uint8_t ucFinalChecksum; + uint8_t ucChecksum; + + // Final GPS info (validated) + TGPSData GPSRecord; +}; + +// declare the GPS module +extern TGPSController GPSModule; + +#endif + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- +
diff -r 000000000000 -r 82ff15078322 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------- + + QRSS Receiver Application + + by Clayton ZL3TKA/VK1TKA + clayton@isnotcrazy.com + + main function + +---------------------------------------------------------------------------*/ +// include files + +#include "mbed.h" +#include "global.h" +#include "EthernetNetIf.h" +#include "UDPSocket.h" +#include "dnsresolve.h" +#include "gps.h" +#include "BufferSys.h" +#include "I2S_Rx.h" +#include "DSP.h" +#include "comms.h" + +// Definitions + +// Macros + +// Local Data + +// Global Data + +// Serial port interface to the PC +Serial pc( USBTX, USBRX ); + +// LEDs +DigitalOut OnLED( LED1 ); + +// IO +DigitalOut TestOscillator( p21 ); +#define TESTOSC_ON 0 +#define TESTOSC_OFF 1 + +// The Buffer pool for audio data +TBufferPool BufferPool; + +// The array of buffers for the pool +TBufferData BufferDataArray[NUM_OF_BUFFERS]; + +// I2S Object +TI2SReceiver AudioInput( BufferPool ); + +// Communications Object +TCommunications Comms; + + +// Function Prototypes + +//--------------------------------------------------------------------------- +// MAIN +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Main start routine +// Just starts everything running +// +int main() +{ + TDSPProcessor ADCBuffer; + uint32_t uiCurrentTestmode = 0x1234; + + // Set up IOs + OnLED = 1; + TestOscillator = TESTOSC_OFF; + + // Set up PC port + pc.baud( 38400 ); + + // Report + printf( "\r\n\r\nQRSS_Rx Application running\r\n" ); + printf( "Built " __DATE__ " " __TIME__ "\r\n" ); + printf( "System Core Clock = %ld\r\n", SystemCoreClock ); + + // Add array of buffers to the pool + pc.printf ("Setting up buffer pool with %d buffer of %d samples ...", NUM_OF_BUFFERS, BUFFERSYS_SIZE ); + BufferPool.AddNewMsgsToPool( BufferDataArray, NUM_OF_BUFFERS ); + printf (" OK\r\n"); + + // Set up audio receiver + pc.printf ("Initialise the I2S Receiver ..."); + AudioInput.Init(); + printf (" OK\r\n"); + + // Set up GPS + pc.printf ("Initialise GPS ..."); + GPSModule.Init(); + printf (" OK\r\n"); + + // Set up communications + pc.printf ("Initialise Communications ..."); + Comms.Init(); + Comms.SetServer( "192.168.0.15" ); // default QRSS Server Address (could be a DNS name) + printf (" OK\r\n"); + + ADCBuffer.NCOFrequency( TEST_NCO_FREQ ); + + // echo GPS data out the PC port + while(1) + { + // Poll the sub-systems + Net::poll(); + GPSModule.Poll(); + Comms.Poll(); + + // Handle Comms Mode + if ( Comms.Running() ) + { // online comms + if ( !AudioInput.Running() ) + { + printf( "Starting Processing\r\n" ); + ADCBuffer.NCOPhaseInc( Comms.NCOPhaseInc() ); + AudioInput.Start(); + } + // process and output any received samples + if ( !AudioInput.Empty() ) + { + // ADC buffer will be automatically released + AudioInput.Read( ADCBuffer ); + // Mix the LO + ADCBuffer.MixLO(); + // Filter the result + ADCBuffer.LPF(); + // Output the data to the comms module + for ( int ii=0; ii<8; ii++ ) + Comms.SendSamples( ADCBuffer.Timestamp(), ADCBuffer.FilteredOutput(ii) ); + } + } + else + { // offline comms + if ( AudioInput.Running() ) + { + AudioInput.Stop(); + printf( "Stopping Processing\r\n" ); + } + if ( !AudioInput.Empty() ) + { // discard messages + AudioInput.Read( ADCBuffer ); + ADCBuffer.Release(); + } + } + + // Handle Test Modes + if ( Comms.TestMode()!=uiCurrentTestmode ) + { + uiCurrentTestmode = Comms.TestMode(); + if ( uiCurrentTestmode!=0 ) + { // enable test Osc + printf( "Turn ON Test Osc\r\n" ); + TestOscillator = TESTOSC_ON; + } + else + { // disable test Osc + printf( "Turn OFF Test Osc\r\n" ); + TestOscillator = TESTOSC_OFF; + } + } + + // Process Keystrokes - debug only + if( pc.readable() ) + { + int iCharIn = pc.getc(); + switch ( iCharIn ) + { + case'1': + printf( "Turn on Test Osc\r\n" ); + TestOscillator = TESTOSC_ON; + break; + case '2': + printf( "Turn off Test Osc\r\n" ); + TestOscillator = TESTOSC_OFF; + break; + case '7': + printf( "AudioInput Start\r\n" ); + AudioInput.Start(); + break; + case '8': + printf( "AudioInput Stop\r\n" ); + AudioInput.Stop(); + break; + case '9': + AudioInput.Report(); + break; + case 'x': + ADCBuffer.NCOFrequency( 15000 ); + break; + case 'y': + ADCBuffer.NCOFrequency( -15000 ); + break; + case 'z': + ADCBuffer.NCOFrequency( 15600 ); + break; + } + } + } + +} + +//--------------------------------------------------------------------------- +// END +//--------------------------------------------------------------------------- + +
diff -r 000000000000 -r 82ff15078322 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/63bcd7ba4912