335 lines
9.1 KiB
C++
335 lines
9.1 KiB
C++
/**
|
|
@author Lime Microsystems
|
|
@brief Stream board communications for Matlab
|
|
*/
|
|
|
|
#include "LMS_SDR.h"
|
|
|
|
#include "lmsComms.h"
|
|
#include "LMS_StreamBoard.h"
|
|
#include "ringBuffer.h"
|
|
#include "IConnection.h"
|
|
#include "fifo.h"
|
|
#include "dataTypes.h"
|
|
#include <unistd.h>
|
|
#include <thread>
|
|
|
|
#include <vector>
|
|
#include <atomic>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <chrono>
|
|
|
|
#define _USE_MATH_DEFINES
|
|
#include <math.h>
|
|
|
|
using namespace std;
|
|
|
|
typedef enum
|
|
{
|
|
LMS_SUCCESS = 0,
|
|
LMS_ERROR
|
|
} LMS_STATUS;
|
|
|
|
LMS_SamplesFIFO rxBuffer(1);
|
|
static thread rxThread;
|
|
atomic<bool> rxStop(1);
|
|
atomic<bool> rxRunning(0);
|
|
atomic<unsigned long> rxDroppedSamples(0);
|
|
atomic<long> RxDataRate(0);
|
|
atomic<uint32_t> rxSamplingRate(0);
|
|
|
|
LMS_SamplesFIFO txBuffer(1);
|
|
static thread txThread;
|
|
atomic<bool> txStop(1);
|
|
atomic<bool> txRunning(0);
|
|
atomic<long> txDroppedSamples(0);
|
|
atomic<long> TxDataRate(0);
|
|
atomic<uint32_t> txSamplingRate(0);
|
|
|
|
LMScomms comPort(IConnection::COM_PORT);
|
|
LMScomms usbPort(IConnection::USB_PORT);
|
|
LMS_StreamBoard streamer(&usbPort);
|
|
|
|
const int samplesInPacket = 1024;
|
|
unsigned int opMode = 0;
|
|
|
|
DLL_EXPORT void LMS_Stats(uint32_t *RxBufSize, uint32_t *RxBufFilled, uint32_t *RxSamplingRate, uint32_t *TxBufSize, uint32_t *TxBufFilled, uint32_t *TxSamplingRate)
|
|
{
|
|
LMS_SamplesFIFO::BufferInfo rxStats = rxBuffer.GetInfo();
|
|
LMS_SamplesFIFO::BufferInfo txStats = txBuffer.GetInfo();
|
|
if (RxBufSize)
|
|
*RxBufSize = rxStats.size*samplesInPacket;
|
|
if (RxBufFilled)
|
|
*RxBufFilled = rxStats.itemsFilled*samplesInPacket;
|
|
if (TxBufSize)
|
|
*TxBufSize = txStats.size*samplesInPacket;
|
|
if (TxBufFilled)
|
|
*TxBufFilled = txStats.itemsFilled*samplesInPacket;
|
|
if (TxSamplingRate)
|
|
*TxSamplingRate = txSamplingRate.load();
|
|
if (RxSamplingRate)
|
|
*RxSamplingRate = rxSamplingRate.load();
|
|
}
|
|
|
|
DLL_EXPORT int LMS_Init(const int OperationMode, uint32_t trxBuffersLength)
|
|
{
|
|
opMode = OperationMode;
|
|
unsigned int packetsNeeded = trxBuffersLength / samplesInPacket;
|
|
if (trxBuffersLength % samplesInPacket != 0)
|
|
++packetsNeeded;
|
|
if (packetsNeeded >= (uint32_t)(1 << 31))
|
|
packetsNeeded = (uint32_t)(1 << 31);
|
|
for (int i = 0; i < 32; ++i)
|
|
if ((1 << i) >= packetsNeeded)
|
|
{
|
|
packetsNeeded = (1 << i);
|
|
break;
|
|
}
|
|
rxBuffer.Reset(packetsNeeded);
|
|
txBuffer.Reset(packetsNeeded);
|
|
return LMS_SUCCESS;
|
|
}
|
|
|
|
DLL_EXPORT int LMS_Destroy()
|
|
{
|
|
LMS_RxStop();
|
|
//buffers size is reduced to lower memory consumption when not used
|
|
rxBuffer.Reset(1);
|
|
txBuffer.Reset(1);
|
|
return LMS_SUCCESS;
|
|
}
|
|
|
|
DLL_EXPORT LMScomms* LMS_GetUSBPort()
|
|
{
|
|
return &usbPort;
|
|
}
|
|
|
|
DLL_EXPORT LMScomms* LMS_GetCOMPort()
|
|
{
|
|
return &comPort;
|
|
}
|
|
|
|
const int maxDevListLen = 32;
|
|
const int maxDevNameLen = 256; //each device name not longer than 256
|
|
static char DeviceNames[maxDevListLen][maxDevNameLen];
|
|
|
|
DLL_EXPORT int LMS_UpdateDeviceList(LMScomms* port)
|
|
{
|
|
memset(DeviceNames, 0, maxDevListLen*maxDevNameLen);
|
|
port->Close();
|
|
int devCount = port->RefreshDeviceList();
|
|
vector<string> names = port->GetDeviceList();
|
|
int charsWritten = 0;
|
|
for (unsigned int i = 0; i < names.size() && i < maxDevListLen; ++i)
|
|
{
|
|
charsWritten += sprintf(DeviceNames[i], "[%i] %.*s", i, maxDevNameLen, names[i].c_str());
|
|
}
|
|
return devCount;
|
|
}
|
|
|
|
DLL_EXPORT const char* LMS_GetDeviceName(LMScomms* port, unsigned int deviceIndex)
|
|
{
|
|
if (deviceIndex < maxDevListLen)
|
|
return DeviceNames[deviceIndex];
|
|
else
|
|
return "";
|
|
}
|
|
|
|
DLL_EXPORT int LMS_DeviceOpen(LMScomms* port, const uint32_t deviceIndex)
|
|
{
|
|
port->Close();
|
|
int status = port->Open(deviceIndex);
|
|
if (status == 1)
|
|
{
|
|
return LMS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return LMS_ERROR;
|
|
}
|
|
}
|
|
|
|
DLL_EXPORT void LMS_DeviceClose(LMScomms* port)
|
|
{
|
|
port->Close();
|
|
}
|
|
|
|
DLL_EXPORT uint32_t LMS_ControlWrite(LMScomms* port, const uint8_t *buffer, const uint16_t bufLen)
|
|
{
|
|
return port->Write(buffer, bufLen, 0);
|
|
}
|
|
|
|
DLL_EXPORT uint32_t LMS_ControlRead(LMScomms* port, uint8_t* buffer, const uint16_t bufLen)
|
|
{
|
|
return port->Read(buffer, bufLen, 0);
|
|
}
|
|
|
|
|
|
|
|
const int RX_BUFF_SZ (1020*1024/4);
|
|
uint32_t rx_buffer[RX_BUFF_SZ];
|
|
uint64_t start_timestamp = 0;;
|
|
uint32_t wr_pos = 0;
|
|
uint32_t rd_pos = 0;
|
|
|
|
void ReceivePackets()
|
|
{
|
|
rxRunning.store(true);
|
|
uint32_t samplesCollected = 0;
|
|
|
|
const int bufferSize = 4096;// 4096;
|
|
const int buffersCount = 32; // must be power of 2
|
|
const int buffersCountMask = buffersCount - 1;
|
|
int handles[buffersCount]= {0};
|
|
char buffers[buffersCount*bufferSize]={0};
|
|
|
|
struct sched_param sp;
|
|
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
|
if (pthread_setschedparam(pthread_self(),SCHED_FIFO,&sp)!=0)
|
|
{
|
|
printf("Shed prams failed\n");
|
|
}
|
|
|
|
//switch off Rx
|
|
uint16_t regVal = streamer.SPI_read(0x0005);
|
|
streamer.SPI_write(0x0005, regVal & ~0x6);
|
|
//USB FIFO reset
|
|
LMScomms::GenericPacket ctrPkt;
|
|
ctrPkt.cmd = CMD_USB_FIFO_RST;
|
|
ctrPkt.outBuffer.push_back(0x01);
|
|
usbPort.TransferPacket(ctrPkt);
|
|
ctrPkt.outBuffer[0] = 0x00;
|
|
usbPort.TransferPacket(ctrPkt);
|
|
|
|
streamer.SPI_write(0x0005, regVal | 0x6);
|
|
|
|
streamer.SPI_write(0x0001, 0x0001);
|
|
streamer.SPI_write(0x0007, 0x0000);
|
|
|
|
for (int i = 0; i<buffersCount; ++i)
|
|
handles[i] = usbPort.BeginDataReading(&buffers[i*bufferSize], bufferSize);
|
|
|
|
int bi = 0;
|
|
|
|
while (rxStop.load() != true)
|
|
{
|
|
if (usbPort.WaitForReading(handles[bi], 1000) == false)
|
|
break;
|
|
|
|
long bytesToRead = bufferSize;
|
|
long bytesReceived = usbPort.FinishDataReading(&buffers[bi*bufferSize], bytesToRead, handles[bi]);
|
|
if (bytesReceived > 0)
|
|
{
|
|
PacketLTE* pkt = (PacketLTE*)&buffers[bi*bufferSize];
|
|
for(uint16_t sampleIndex = 0; sampleIndex < sizeof(pkt->samples)/sizeof(int16_t); sampleIndex++)
|
|
{
|
|
pkt->samples[sampleIndex] <<= 4;
|
|
pkt->samples[sampleIndex] >>= 4;
|
|
}
|
|
|
|
rxBuffer.push_samples((complex16_t*)pkt->samples, 1020, pkt->counter, 100);
|
|
}
|
|
// Re-submit this request to keep the queue full
|
|
memset(&buffers[bi*bufferSize], 0, bufferSize);
|
|
handles[bi] = usbPort.BeginDataReading(&buffers[bi*bufferSize], bufferSize);
|
|
bi = (bi + 1) & buffersCountMask;
|
|
pthread_yield();
|
|
}
|
|
|
|
usbPort.AbortReading();
|
|
for (int j = 0; j<buffersCount; j++)
|
|
{
|
|
long bytesToRead = bufferSize;
|
|
usbPort.WaitForReading(handles[j], 1000);
|
|
usbPort.FinishDataReading(&buffers[j*bufferSize], bytesToRead, handles[j]);
|
|
}
|
|
rxRunning.store(false);
|
|
}
|
|
|
|
|
|
DLL_EXPORT uint32_t LMS_TRxWrite(const int16_t *data, const uint32_t samplesCount, const uint32_t antenna_id, uint64_t timestamp)
|
|
{
|
|
static uint32_t tx_buffer[1020];
|
|
static int index = 0;
|
|
|
|
const int bufferSize = 1024 * 4;
|
|
const int buffersCount = 32; // must be power of 2
|
|
const int buffersCountMask = buffersCount - 1;
|
|
|
|
static int handles[buffersCount] = {0};
|
|
static char buffers[buffersCount*bufferSize]={0};
|
|
static bool bufferUsed[buffersCount] = {0};
|
|
static int bi = 0; //buffer index
|
|
PacketLTE* pkt;
|
|
|
|
uint64_t ts = timestamp - index;
|
|
|
|
pkt = (PacketLTE*)&buffers[bi*bufferSize];
|
|
|
|
for (int i=0;i<samplesCount;i++)
|
|
{
|
|
((uint32_t*)pkt->samples)[index++]=(((uint32_t*)data)[i]& 0xFFF0FFF) | 0x1000;
|
|
|
|
if (index == 1020)
|
|
{
|
|
pkt->counter = ts;
|
|
|
|
if (bufferUsed[bi])
|
|
{
|
|
if (usbPort.WaitForSending(handles[bi], 1000) == false)
|
|
return -1;
|
|
// Must always call FinishDataXfer to release memory of contexts[i]
|
|
long tempToSend = sizeof(PacketLTE);
|
|
usbPort.FinishDataSending(&buffers[bi*bufferSize], tempToSend, handles[bi]);
|
|
|
|
bufferUsed[bi] = false;
|
|
}
|
|
handles[bi] = usbPort.BeginDataSending(&buffers[bi*bufferSize], sizeof(PacketLTE));
|
|
bufferUsed[bi] = true;
|
|
bi = (bi + 1) & buffersCountMask;
|
|
pkt = (PacketLTE*)&buffers[bi*bufferSize];
|
|
ts += 1020;
|
|
index = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<PacketLTE> PacketsBuffer(2048, PacketLTE());
|
|
DLL_EXPORT uint32_t LMS_TRxRead(int16_t *buffer, const uint32_t samplesCount, const uint32_t antenna_id, uint64_t *timestamp, const uint32_t timeout_ms)
|
|
{
|
|
if (usbPort.IsOpen() == false && opMode != -1)
|
|
return 0;
|
|
pthread_yield();
|
|
uint32_t samplesPopped = rxBuffer.pop_samples((complex16_t*)buffer, samplesCount, timestamp, timeout_ms);
|
|
return samplesPopped;
|
|
}
|
|
|
|
|
|
DLL_EXPORT int LMS_RxStart()
|
|
{
|
|
if (rxRunning.load())
|
|
return 1;
|
|
if (usbPort.IsOpen() == false && opMode != -1)
|
|
return 1;
|
|
rxStop.store(false);
|
|
unsigned int bufSz = rxBuffer.GetInfo().size;
|
|
rxBuffer.Reset(bufSz);
|
|
rxThread = thread(ReceivePackets);
|
|
return 0;
|
|
}
|
|
|
|
DLL_EXPORT int LMS_RxStop()
|
|
{
|
|
if (rxRunning.load())
|
|
{
|
|
if (rxThread.joinable())
|
|
{
|
|
rxStop.store(true);
|
|
rxThread.join();
|
|
}
|
|
rxStop.store(true);
|
|
}
|
|
return 0;
|
|
}
|