- Transmitter use C++ NCO and upsampler git-svn-id: http://moon:8086/svn/software/trunk/projects/mpsk_rx_gui@111 b431acfa-c32f-4a4a-93f1-934dc6c82436
261 lines
5.1 KiB
C++
261 lines
5.1 KiB
C++
#ifndef _TRANSMITTER_H_
|
|
#define _TRANSMITTER_H_
|
|
|
|
#include <cstdint>
|
|
#include <radio/symbol.h>
|
|
#include <radio/interpolation.h>
|
|
#include <radio/Frame.hpp>
|
|
|
|
#include "JuceHeader.h"
|
|
#include "LogComponent.h"
|
|
|
|
#include <radio/Vector.hpp>
|
|
#include <radio/Nco.hpp>
|
|
#include <radio/Interpolation.hpp>
|
|
|
|
using namespace Radio;
|
|
|
|
#define TRANSMITTER_RCF_OVERSAMPLING ((uint32_t)32)
|
|
#define TRANSMITTER_RCF_ROLLOFF ((radio_float_t)0.35)
|
|
|
|
class Transmitter;
|
|
// ---------------------------------------------------
|
|
class TransmitterInterface
|
|
{
|
|
public:
|
|
TransmitterInterface() {}
|
|
virtual ~TransmitterInterface() {}
|
|
|
|
virtual void reset() = 0;
|
|
virtual void setBuffersize(uint32_t size) = 0;
|
|
virtual void setCarrierFrequency(float frequency_hz) = 0;
|
|
virtual void setSamplerate(float samplerate_hz) = 0;
|
|
virtual void setSymbolRate(float symbolrate_bit_per_sec) = 0;
|
|
virtual void setNumBitPerSymbol(uint32_t numBitPerSymbol) = 0;
|
|
virtual void getData(float *pRF, uint32_t len) = 0;
|
|
|
|
};
|
|
|
|
class TxBuffer
|
|
{
|
|
public:
|
|
TxBuffer(uint32_t size, uint32_t numBuffers)
|
|
: m_ppBuffer(0)
|
|
, m_capacityPerBuffer(0)
|
|
, m_numBuffers(0)
|
|
, m_pT(0)
|
|
{
|
|
resize(size, numBuffers);
|
|
}
|
|
|
|
~TxBuffer()
|
|
{
|
|
resize(0, 0);
|
|
}
|
|
|
|
void write(RVec const &data, uint32_t len)
|
|
{
|
|
uint32_t i;
|
|
|
|
m_pT = Thread::getCurrentThread();
|
|
while (m_numfill.get() >= m_numBuffers)
|
|
{
|
|
m_pT->wait(100);
|
|
if (m_pT->threadShouldExit())
|
|
return;
|
|
}
|
|
for (i=0; i < len; i++)
|
|
{
|
|
m_ppBuffer[m_w][i] = data[i];
|
|
}
|
|
m_w++;
|
|
if (m_w >= m_numBuffers)
|
|
m_w = 0;
|
|
|
|
m_numfill += 1;
|
|
}
|
|
|
|
uint32_t read(float *pData)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (m_numfill.get() == 0)
|
|
return 0;
|
|
|
|
for (i=0; i < m_capacityPerBuffer; i++)
|
|
{
|
|
pData[i] = m_ppBuffer[m_r][i];
|
|
}
|
|
m_r++;
|
|
if (m_r >= m_numBuffers)
|
|
m_r = 0;
|
|
|
|
m_numfill -= 1;
|
|
|
|
if (m_pT)
|
|
m_pT->notify();
|
|
|
|
return i;
|
|
}
|
|
|
|
void resize(uint32_t size, uint32_t numBuffers)
|
|
{
|
|
|
|
uint32_t i;
|
|
if ((size != m_capacityPerBuffer) || (numBuffers != m_numBuffers))
|
|
{
|
|
if (m_ppBuffer)
|
|
{
|
|
for (i=0; i < m_numBuffers; i++)
|
|
{
|
|
delete m_ppBuffer[i];
|
|
}
|
|
delete m_ppBuffer;
|
|
m_ppBuffer = nullptr;
|
|
}
|
|
m_r = 0;
|
|
m_w = 0;
|
|
m_numfill = 0;
|
|
}
|
|
|
|
m_numBuffers = numBuffers;
|
|
m_capacityPerBuffer = size;
|
|
|
|
if (!size || !numBuffers)
|
|
return;
|
|
|
|
m_ppBuffer = new float*[m_numBuffers];
|
|
for (i=0; i < m_numBuffers; i++)
|
|
{
|
|
m_ppBuffer[i] = new float[m_capacityPerBuffer];
|
|
}
|
|
}
|
|
|
|
private:
|
|
Atomic<uint32_t> m_numfill;
|
|
float **m_ppBuffer;
|
|
uint32_t m_capacityPerBuffer;
|
|
uint32_t m_numBuffers;
|
|
uint32_t m_r;
|
|
uint32_t m_w;
|
|
Thread *m_pT;
|
|
};
|
|
|
|
class Transmitter : public TransmitterInterface, public ISymbolizer, public juce::Thread
|
|
{
|
|
public:
|
|
Transmitter(void);
|
|
~Transmitter(void);
|
|
|
|
void bufferResize(uint32_t size);
|
|
|
|
private:
|
|
CriticalSection m_lock;
|
|
Symbolizer m_symbolizer;
|
|
Formatter m_formatter;
|
|
sym_map_t m_symbolMapper;
|
|
Interpolation::UpSampler m_firUp;
|
|
uint32_t m_bufferSize;
|
|
RVec m_bufferRF;
|
|
CVec m_bufferSym;
|
|
CVec m_bufferSymRcf;
|
|
float m_samplerate;
|
|
float m_ddcFrequency;
|
|
float m_symbolrate;
|
|
uint32_t m_numBitPerSymbol;
|
|
radio_float_t *m_pCoeffRCF;
|
|
radio_float_t m_kRcf;
|
|
radio_float_t m_carrierFreq;
|
|
Nco m_duc;
|
|
|
|
TxBuffer m_txBuf;
|
|
uint32_t m_symCount;
|
|
uint32_t m_upsampleFactor;
|
|
|
|
// Thread
|
|
void run() override
|
|
{
|
|
uint32_t data;
|
|
|
|
data = 0;
|
|
while(!threadShouldExit())
|
|
{
|
|
m_formatter.process(&data, sizeof(data));
|
|
data++;
|
|
}
|
|
}
|
|
|
|
// ISymbolizer
|
|
void onSymbol(symbol_t symbol) override
|
|
{
|
|
uint32_t symCountRcf;
|
|
const ScopedLock sl (m_lock);
|
|
|
|
m_bufferSym[m_symCount++] = toComplexScalar(SymMapMap(&m_symbolMapper, symbol));
|
|
|
|
if (m_symCount >= m_bufferSize/m_upsampleFactor)
|
|
{
|
|
symCountRcf = m_firUp.process(m_bufferSymRcf, m_bufferSym, m_symCount);
|
|
m_duc.mixComplexRealV(m_bufferRF, m_bufferSymRcf, ComplexScalar(m_kRcf/4, m_kRcf/4), symCountRcf);
|
|
m_txBuf.write(m_bufferRF, symCountRcf);
|
|
m_symCount = 0;
|
|
}
|
|
}
|
|
|
|
void paramChanged();
|
|
|
|
// TransmitterInterface
|
|
void reset() override
|
|
{
|
|
}
|
|
void setBuffersize(uint32_t size) override
|
|
{
|
|
bufferResize(size);
|
|
paramChanged();
|
|
}
|
|
void setSamplerate(float samplerate_hz) override
|
|
{
|
|
m_samplerate = samplerate_hz;
|
|
m_carrierFreq = samplerate_hz/4;
|
|
m_symbolrate = samplerate_hz/4;
|
|
paramChanged();
|
|
}
|
|
void setCarrierFrequency(float frequency_hz) override
|
|
{
|
|
const ScopedLock sl (m_lock);
|
|
m_carrierFreq = frequency_hz;
|
|
m_duc.init(m_carrierFreq/m_samplerate, 0);
|
|
}
|
|
void setSymbolRate(float symbolrate_bit_per_sec) override
|
|
{
|
|
m_symbolrate = symbolrate_bit_per_sec;
|
|
paramChanged();
|
|
}
|
|
void setNumBitPerSymbol(uint32_t numBitPerSymbol) override
|
|
{
|
|
m_numBitPerSymbol = numBitPerSymbol;
|
|
if (m_numBitPerSymbol > 16)
|
|
m_numBitPerSymbol = 16;
|
|
|
|
paramChanged();
|
|
}
|
|
void getData(float *pRF, uint32_t len) override
|
|
{
|
|
uint32_t res;
|
|
|
|
if (len != m_bufferSize)
|
|
{
|
|
memset(pRF, 0, len*sizeof(float));
|
|
return;
|
|
}
|
|
|
|
res = m_txBuf.read(pRF);
|
|
|
|
if (!res)
|
|
memset(pRF, 0, len*sizeof(float));
|
|
|
|
}
|
|
};
|
|
|
|
#endif // _TRANSMITTER_H_
|