mirror of https://github.com/g4klx/MMDVM.git
Merge branch 'c4fmdemod'
This commit is contained in:
commit
bc969ff5e5
151
DMRDMOTX.cpp
151
DMRDMOTX.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -25,22 +25,43 @@
|
|||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 22U;
|
||||
static q15_t DMR_C4FSK_FILTER[] = {0, 0, 0, 0, 688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688}; // numTaps = 25, L = 5
|
||||
const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 5U; // phaseLength = numTaps/L
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
static q15_t DMR_C4FSK_FILTER[] = {0, 0, 0, 0, 401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 42U;
|
||||
-553, -847, -731, -340, 104, 401}; // numTaps = 45, L = 5
|
||||
const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
#endif
|
||||
|
||||
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640};
|
||||
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213};
|
||||
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213};
|
||||
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640};
|
||||
const q15_t DMR_LEVELA = 2889;
|
||||
const q15_t DMR_LEVELB = 963;
|
||||
const q15_t DMR_LEVELC = -963;
|
||||
const q15_t DMR_LEVELD = -2889;
|
||||
|
||||
// The PR FILL and Data Sync pattern.
|
||||
const uint8_t IDLE_DATA[] =
|
||||
{0x53U, 0xC2U, 0x5EU, 0xABU, 0xA8U, 0x67U, 0x1DU, 0xC7U, 0x38U, 0x3BU, 0xD9U,
|
||||
0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U,
|
||||
0xE4U, 0x65U, 0x17U, 0x1BU, 0x48U, 0xCAU, 0x6DU, 0x4FU, 0xC6U, 0x10U, 0xB4U};
|
||||
|
||||
const uint8_t CACH_INTERLEAVE[] =
|
||||
{1U, 2U, 3U, 5U, 6U, 7U, 9U, 10U, 11U, 13U, 15U, 16U, 17U, 19U, 20U, 21U, 23U,
|
||||
25U, 26U, 27U, 29U, 30U, 31U, 33U, 34U, 35U, 37U, 39U, 40U, 41U, 43U, 44U, 45U, 47U,
|
||||
49U, 50U, 51U, 53U, 54U, 55U, 57U, 58U, 59U, 61U, 63U, 64U, 65U, 67U, 68U, 69U, 71U,
|
||||
73U, 74U, 75U, 77U, 78U, 79U, 81U, 82U, 83U, 85U, 87U, 88U, 89U, 91U, 92U, 93U, 95U};
|
||||
|
||||
const uint8_t EMPTY_SHORT_LC[] =
|
||||
{0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
|
||||
|
||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint8_t DMR_SYNC = 0x5FU;
|
||||
|
||||
CDMRDMOTX::CDMRDMOTX() :
|
||||
m_fifo(),
|
||||
|
@ -50,13 +71,15 @@ m_poBuffer(),
|
|||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_count(0U)
|
||||
m_idle(),
|
||||
m_cachPtr(0U)
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = DMR_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.L = DMR_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = DMR_C4FSK_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
void CDMRDMOTX::process()
|
||||
|
@ -64,13 +87,21 @@ void CDMRDMOTX::process()
|
|||
if (m_poLen == 0U && m_fifo.getData() > 0U) {
|
||||
if (!m_tx) {
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = 0x00U;
|
||||
m_poBuffer[i] = DMR_SYNC;
|
||||
|
||||
m_poLen = m_txDelay;
|
||||
} else {
|
||||
for (unsigned int i = 0U; i < 72U; i++)
|
||||
m_poBuffer[m_poLen++] = 0x00U;
|
||||
createCACH(m_poBuffer + 0U, 0U);
|
||||
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
||||
m_poBuffer[i] = m_fifo.get();
|
||||
m_poBuffer[i + 3U] = m_fifo.get();
|
||||
|
||||
createCACH(m_poBuffer + 36U, 1U);
|
||||
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
||||
m_poBuffer[i + 39U] = m_idle[i];
|
||||
|
||||
m_poLen = 72U;
|
||||
}
|
||||
|
||||
m_poPtr = 0U;
|
||||
|
@ -112,53 +143,34 @@ uint8_t CDMRDMOTX::writeData(const uint8_t* data, uint8_t length)
|
|||
|
||||
void CDMRDMOTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELC;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_DMR, outBuffer, blockSize);
|
||||
io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
uint16_t CDMRDMOTX::getSpace() const
|
||||
uint8_t CDMRDMOTX::getSpace() const
|
||||
{
|
||||
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
|
@ -166,5 +178,48 @@ uint16_t CDMRDMOTX::getSpace() const
|
|||
void CDMRDMOTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
void CDMRDMOTX::createCACH(uint8_t* buffer, uint8_t slotIndex)
|
||||
{
|
||||
if (m_cachPtr >= 12U)
|
||||
m_cachPtr = 0U;
|
||||
|
||||
::memcpy(buffer, EMPTY_SHORT_LC + m_cachPtr, 3U);
|
||||
|
||||
bool at = true;
|
||||
bool tc = slotIndex == 1U;
|
||||
bool ls0 = true; // For 1 and 2
|
||||
bool ls1 = true;
|
||||
|
||||
if (m_cachPtr == 0U) // For 0
|
||||
ls1 = false;
|
||||
else if (m_cachPtr == 9U) // For 3
|
||||
ls0 = false;
|
||||
|
||||
bool h0 = at ^ tc ^ ls1;
|
||||
bool h1 = tc ^ ls1 ^ ls0;
|
||||
bool h2 = at ^ tc ^ ls0;
|
||||
|
||||
buffer[0U] |= at ? 0x80U : 0x00U;
|
||||
buffer[0U] |= tc ? 0x08U : 0x00U;
|
||||
buffer[1U] |= ls1 ? 0x80U : 0x00U;
|
||||
buffer[1U] |= ls0 ? 0x08U : 0x00U;
|
||||
buffer[1U] |= h0 ? 0x02U : 0x00U;
|
||||
buffer[2U] |= h1 ? 0x20U : 0x00U;
|
||||
buffer[2U] |= h2 ? 0x02U : 0x00U;
|
||||
|
||||
m_cachPtr += 3U;
|
||||
}
|
||||
|
||||
void CDMRDMOTX::setColorCode(uint8_t colorCode)
|
||||
{
|
||||
::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES);
|
||||
|
||||
CDMRSlotType slotType;
|
||||
slotType.encode(colorCode, DT_IDLE, m_idle);
|
||||
}
|
||||
|
||||
|
|
24
DMRDMOTX.h
24
DMRDMOTX.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -35,18 +35,22 @@ public:
|
|||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
void setColorCode(uint8_t colorCode);
|
||||
|
||||
private:
|
||||
CSerialRB m_fifo;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
uint32_t m_count;
|
||||
CSerialRB m_fifo;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_txDelay;
|
||||
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
||||
uint8_t m_cachPtr;
|
||||
|
||||
void createCACH(uint8_t* buffer, uint8_t slotIndex);
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
||||
|
|
93
DMRTX.cpp
93
DMRTX.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -25,21 +25,21 @@
|
|||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 22U;
|
||||
static q15_t DMR_C4FSK_FILTER[] = {0, 0, 0, 0, 688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688}; // numTaps = 25, L = 5
|
||||
const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 5U; // phaseLength = numTaps/L
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
static q15_t DMR_C4FSK_FILTER[] = {0, 0, 0, 0, 401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 42U;
|
||||
-553, -847, -731, -340, 104, 401}; // numTaps = 45, L = 5
|
||||
const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
#endif
|
||||
|
||||
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640};
|
||||
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213};
|
||||
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213};
|
||||
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640};
|
||||
const q15_t DMR_LEVELA = 2889;
|
||||
const q15_t DMR_LEVELB = 963;
|
||||
const q15_t DMR_LEVELC = -963;
|
||||
const q15_t DMR_LEVELD = -2889;
|
||||
|
||||
// The PR FILL and Data Sync pattern.
|
||||
const uint8_t IDLE_DATA[] =
|
||||
|
@ -61,6 +61,8 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02
|
|||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint32_t STARTUP_COUNT = 20U;
|
||||
|
||||
CDMRTX::CDMRTX() :
|
||||
m_fifo(),
|
||||
m_modFilter(),
|
||||
|
@ -74,14 +76,15 @@ m_markBuffer(),
|
|||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_count(0U),
|
||||
m_frameCount(0U),
|
||||
m_abort()
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = DMR_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.L = DMR_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = DMR_C4FSK_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
|
||||
::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U);
|
||||
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
|
||||
|
@ -231,7 +234,7 @@ void CDMRTX::setStart(bool start)
|
|||
{
|
||||
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
|
||||
|
||||
m_count = 0U;
|
||||
m_frameCount = 0U;
|
||||
|
||||
m_abort[0U] = false;
|
||||
m_abort[1U] = false;
|
||||
|
@ -240,80 +243,54 @@ void CDMRTX::setStart(bool start)
|
|||
void CDMRTX::setCal(bool start)
|
||||
{
|
||||
m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE;
|
||||
|
||||
m_count = 0U;
|
||||
}
|
||||
|
||||
void CDMRTX::writeByte(uint8_t c, uint8_t control)
|
||||
{
|
||||
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELC;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
|
||||
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||
::memset(controlBuffer, MARK_NONE, DMR_RADIO_SYMBOL_LENGTH * 4U * sizeof(uint8_t));
|
||||
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U] = control;
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
for (int8_t i = DMR_RADIO_SYMBOL_LENGTH * 4U - 1; i >= 0; i--)
|
||||
controlBuffer[i + 1] = controlBuffer[i];
|
||||
blockSize++;
|
||||
} else {
|
||||
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U - 1U] = control;
|
||||
for (uint8_t i = 0U; i < (DMR_RADIO_SYMBOL_LENGTH * 4U - 1U); i++)
|
||||
controlBuffer[i] = controlBuffer[i + 1U];
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_DMR, outBuffer, blockSize, controlBuffer);
|
||||
io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U, controlBuffer);
|
||||
}
|
||||
|
||||
uint16_t CDMRTX::getSpace1() const
|
||||
uint8_t CDMRTX::getSpace1() const
|
||||
{
|
||||
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
|
||||
uint16_t CDMRTX::getSpace2() const
|
||||
uint8_t CDMRTX::getSpace2() const
|
||||
{
|
||||
return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
|
||||
void CDMRTX::createData(uint8_t slotIndex)
|
||||
{
|
||||
if (m_fifo[slotIndex].getData()> 0U) {
|
||||
if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) {
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
||||
m_poBuffer[i] = m_fifo[slotIndex].get();
|
||||
m_markBuffer[i] = MARK_NONE;
|
||||
|
@ -344,6 +321,8 @@ void CDMRTX::createCal()
|
|||
|
||||
void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
||||
{
|
||||
m_frameCount++;
|
||||
|
||||
if (m_cachPtr >= 12U)
|
||||
m_cachPtr = 0U;
|
||||
|
||||
|
@ -359,7 +338,9 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
|||
m_markBuffer[1U] = MARK_NONE;
|
||||
m_markBuffer[2U] = rxSlotIndex == 1U ? MARK_SLOT1 : MARK_SLOT2;
|
||||
|
||||
bool at = m_fifo[rxSlotIndex].getData() > 0U;
|
||||
bool at = false;
|
||||
if (m_frameCount >= STARTUP_COUNT)
|
||||
at = m_fifo[rxSlotIndex].getData() > 0U;
|
||||
bool tc = txSlotIndex == 1U;
|
||||
bool ls0 = true; // For 1 and 2
|
||||
bool ls1 = true;
|
||||
|
|
34
DMRTX.h
34
DMRTX.h
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -49,26 +49,26 @@ public:
|
|||
|
||||
void process();
|
||||
|
||||
uint16_t getSpace1() const;
|
||||
uint16_t getSpace2() const;
|
||||
uint8_t getSpace1() const;
|
||||
uint8_t getSpace2() const;
|
||||
|
||||
void setColorCode(uint8_t colorCode);
|
||||
|
||||
private:
|
||||
CSerialRB m_fifo[2U];
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
DMRTXSTATE m_state;
|
||||
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
||||
uint8_t m_cachPtr;
|
||||
uint8_t m_shortLC[12U];
|
||||
uint8_t m_newShortLC[12U];
|
||||
uint8_t m_markBuffer[40U];
|
||||
uint8_t m_poBuffer[40U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_count;
|
||||
bool m_abort[2U];
|
||||
CSerialRB m_fifo[2U];
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
DMRTXSTATE m_state;
|
||||
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
||||
uint8_t m_cachPtr;
|
||||
uint8_t m_shortLC[12U];
|
||||
uint8_t m_newShortLC[12U];
|
||||
uint8_t m_markBuffer[40U];
|
||||
uint8_t m_poBuffer[40U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_frameCount;
|
||||
bool m_abort[2U];
|
||||
|
||||
void createData(uint8_t slotIndex);
|
||||
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
|
||||
|
|
22
DStarRX.cpp
22
DStarRX.cpp
|
@ -35,7 +35,9 @@ const unsigned int SYNC_POS = 21U * DSTAR_DATA_LENGTH_BITS;
|
|||
const unsigned int SYNC_SCAN_START = SYNC_POS - 3U;
|
||||
const unsigned int SYNC_SCAN_END = SYNC_POS + 3U;
|
||||
|
||||
const q15_t THRESHOLD = 0;
|
||||
// Generated using [b, a] = butter(1, 0.002) in MATLAB
|
||||
static q15_t DC_FILTER[] = {103, 0, 103, 0, 32563, 0}; // {b0, 0, b1, b2, -a1, -a2}
|
||||
const uint16_t DC_FILTER_STAGES = 1U; // One Biquad stage
|
||||
|
||||
// D-Star bit order version of 0x55 0x55 0x6E 0x0A
|
||||
const uint32_t FRAME_SYNC_DATA = 0x00557650U;
|
||||
|
@ -262,6 +264,12 @@ m_fecOutput(),
|
|||
m_rssiAccum(0U),
|
||||
m_rssiCount(0U)
|
||||
{
|
||||
::memset(m_DCState, 0x00U, 4U * sizeof(q15_t));
|
||||
|
||||
m_DCFilter.numStages = DC_FILTER_STAGES;
|
||||
m_DCFilter.pState = m_DCState;
|
||||
m_DCFilter.pCoeffs = DC_FILTER;
|
||||
m_DCFilter.postShift = 0;
|
||||
}
|
||||
|
||||
void CDStarRX::reset()
|
||||
|
@ -278,11 +286,21 @@ void CDStarRX::reset()
|
|||
|
||||
void CDStarRX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
||||
{
|
||||
q31_t dc_level = 0;
|
||||
q15_t DCVals[20];
|
||||
|
||||
::arm_biquad_cascade_df1_q15(&m_DCFilter, (q15_t*)samples, DCVals, length);
|
||||
|
||||
for (uint8_t i = 0U; i < length; i++)
|
||||
dc_level += (q31_t)DCVals[i];
|
||||
|
||||
dc_level /= length;
|
||||
|
||||
for (uint16_t i = 0U; i < length; i++) {
|
||||
m_rssiAccum += rssi[i];
|
||||
m_rssiCount++;
|
||||
|
||||
bool bit = samples[i] < THRESHOLD;
|
||||
bool bit = samples[i] < (q15_t)dc_level;
|
||||
|
||||
if (bit != m_prev) {
|
||||
if (m_pll < (PLLMAX / 2U))
|
||||
|
|
|
@ -53,7 +53,10 @@ private:
|
|||
uint8_t m_fecOutput[42U];
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
|
||||
|
||||
arm_biquad_casd_df1_inst_q15 m_DCFilter;
|
||||
q15_t m_DCState[4];
|
||||
|
||||
void processNone(bool bit);
|
||||
void processHeader(bool bit);
|
||||
void processData(bool bit);
|
||||
|
|
65
DStarTX.cpp
65
DStarTX.cpp
|
@ -28,12 +28,12 @@ const uint8_t BIT_SYNC = 0xAAU;
|
|||
|
||||
const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U};
|
||||
|
||||
// Generated using gaussfir(0.5, 4, 5) in MATLAB
|
||||
static q15_t DSTAR_GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
||||
const uint16_t DSTAR_GMSK_FILTER_LEN = 12U;
|
||||
// Generated using gaussfir(0.35, 1, 5) in MATLAB
|
||||
static q15_t DSTAR_GMSK_FILTER[] = {0, 0, 0, 0, 212, 743, 1974, 3965, 6026, 6929, 6026, 3965, 1974, 743, 212}; // numTaps = 15, L = 5
|
||||
const uint16_t DSTAR_GMSK_FILTER_PHASE_LEN = 3U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DSTAR_LEVEL0[] = {-800, -800, -800, -800, -800};
|
||||
const q15_t DSTAR_LEVEL1[] = { 800, 800, 800, 800, 800};
|
||||
const q15_t DSTAR_LEVEL0 = -4000;
|
||||
const q15_t DSTAR_LEVEL1 = 4000;
|
||||
|
||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
|
@ -195,14 +195,14 @@ m_modState(),
|
|||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(60U), // 100ms
|
||||
m_count(0U)
|
||||
m_txDelay(60U) // 100ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 60U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 20U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = DSTAR_GMSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.L = DSTAR_RADIO_BIT_LENGTH;
|
||||
m_modFilter.phaseLength = DSTAR_GMSK_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = DSTAR_GMSK_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
void CDStarTX::process()
|
||||
|
@ -214,8 +214,6 @@ void CDStarTX::process()
|
|||
|
||||
if (type == DSTAR_HEADER && m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
m_count = 0U;
|
||||
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = BIT_SYNC;
|
||||
} else {
|
||||
|
@ -241,9 +239,6 @@ void CDStarTX::process()
|
|||
}
|
||||
|
||||
if (type == DSTAR_DATA && m_poLen == 0U) {
|
||||
if (!m_tx)
|
||||
m_count = 0U;
|
||||
|
||||
// Pop the type byte off
|
||||
m_buffer.get();
|
||||
|
||||
|
@ -417,50 +412,34 @@ void CDStarTX::txHeader(const uint8_t* in, uint8_t* out) const
|
|||
|
||||
void CDStarTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U];
|
||||
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U];
|
||||
q15_t inBuffer[8U];
|
||||
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U];
|
||||
|
||||
uint8_t mask = 0x01U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 8U; i++, p += DSTAR_RADIO_BIT_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 8U; i++) {
|
||||
if ((c & mask) == mask)
|
||||
::memcpy(p, DSTAR_LEVEL0, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DSTAR_LEVEL0;
|
||||
else
|
||||
::memcpy(p, DSTAR_LEVEL1, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DSTAR_LEVEL1;
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
uint16_t blockSize = DSTAR_RADIO_BIT_LENGTH * 8U;
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += DSTAR_RADIO_BIT_LENGTH * 8U;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U] = inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_DSTAR, outBuffer, blockSize);
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 8U);
|
||||
|
||||
io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_BIT_LENGTH * 8U);
|
||||
}
|
||||
|
||||
void CDStarTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 300U + uint16_t(delay) * 6U; // 250ms + tx delay
|
||||
|
||||
if (m_txDelay > 600U)
|
||||
m_txDelay = 600U;
|
||||
}
|
||||
|
||||
uint16_t CDStarTX::getSpace() const
|
||||
uint8_t CDStarTX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / (DSTAR_DATA_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
|
17
DStarTX.h
17
DStarTX.h
|
@ -35,17 +35,16 @@ public:
|
|||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[60U]; // NoTaps + BlockSize - 1, 12 + 40 - 1 plus some spare
|
||||
uint8_t m_poBuffer[600U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay; // In bytes
|
||||
uint32_t m_count;
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[20U]; // blockSize + phaseLength - 1, 8 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[600U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay; // In bytes
|
||||
|
||||
void txHeader(const uint8_t* in, uint8_t* out) const;
|
||||
void writeByte(uint8_t c);
|
||||
|
|
|
@ -93,9 +93,6 @@ extern bool m_duplex;
|
|||
extern bool m_tx;
|
||||
extern bool m_dcd;
|
||||
|
||||
extern uint32_t m_sampleCount;
|
||||
extern bool m_sampleInsert;
|
||||
|
||||
extern CSerialPort serial;
|
||||
extern CIO io;
|
||||
|
||||
|
|
105
IO.cpp
105
IO.cpp
|
@ -41,6 +41,10 @@ const uint16_t C4FSK_FILTER_LEN = 42U;
|
|||
static q15_t GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
||||
const uint16_t GMSK_FILTER_LEN = 12U;
|
||||
|
||||
// One symbol boxcar filter
|
||||
static q15_t P25_FILTER[] = {3000, 3000, 3000, 3000, 3000, 0};
|
||||
const uint16_t P25_FILTER_LEN = 6U;
|
||||
|
||||
const uint16_t DC_OFFSET = 2048U;
|
||||
|
||||
CIO::CIO() :
|
||||
|
@ -50,8 +54,10 @@ m_txBuffer(TX_RINGBUFFER_SIZE),
|
|||
m_rssiBuffer(RX_RINGBUFFER_SIZE),
|
||||
m_C4FSKFilter(),
|
||||
m_GMSKFilter(),
|
||||
m_P25Filter(),
|
||||
m_C4FSKState(),
|
||||
m_GMSKState(),
|
||||
m_P25State(),
|
||||
m_pttInvert(false),
|
||||
m_rxLevel(128 * 128),
|
||||
m_cwIdTXLevel(128 * 128),
|
||||
|
@ -64,12 +70,12 @@ m_ledValue(true),
|
|||
m_detect(false),
|
||||
m_adcOverflow(0U),
|
||||
m_dacOverflow(0U),
|
||||
m_count(0U),
|
||||
m_watchdog(0U),
|
||||
m_lockout(false)
|
||||
{
|
||||
::memset(m_C4FSKState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_GMSKState, 0x00U, 40U * sizeof(q15_t));
|
||||
::memset(m_P25State, 0x00U, 30U * sizeof(q15_t));
|
||||
|
||||
m_C4FSKFilter.numTaps = C4FSK_FILTER_LEN;
|
||||
m_C4FSKFilter.pState = m_C4FSKState;
|
||||
|
@ -79,6 +85,10 @@ m_lockout(false)
|
|||
m_GMSKFilter.pState = m_GMSKState;
|
||||
m_GMSKFilter.pCoeffs = GMSK_FILTER;
|
||||
|
||||
m_P25Filter.numTaps = P25_FILTER_LEN;
|
||||
m_P25Filter.pState = m_P25State;
|
||||
m_P25Filter.pCoeffs = P25_FILTER;
|
||||
|
||||
initInt();
|
||||
}
|
||||
|
||||
|
@ -89,7 +99,6 @@ void CIO::start()
|
|||
|
||||
startInt();
|
||||
|
||||
m_count = 0U;
|
||||
m_started = true;
|
||||
|
||||
setMode();
|
||||
|
@ -136,11 +145,9 @@ void CIO::process()
|
|||
}
|
||||
|
||||
if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) {
|
||||
q15_t samples[RX_BLOCK_SIZE + 1U];
|
||||
uint8_t control[RX_BLOCK_SIZE + 1U];
|
||||
uint16_t rssi[RX_BLOCK_SIZE + 1U];
|
||||
|
||||
uint8_t blockSize = RX_BLOCK_SIZE;
|
||||
q15_t samples[RX_BLOCK_SIZE];
|
||||
uint8_t control[RX_BLOCK_SIZE];
|
||||
uint16_t rssi[RX_BLOCK_SIZE];
|
||||
|
||||
for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) {
|
||||
uint16_t sample;
|
||||
|
@ -156,97 +163,81 @@ void CIO::process()
|
|||
samples[i] = q15_t(__SSAT((res2 >> 15), 16));
|
||||
}
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += RX_BLOCK_SIZE;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
blockSize++;
|
||||
samples[RX_BLOCK_SIZE] = 0;
|
||||
for (int8_t i = RX_BLOCK_SIZE - 1; i >= 0; i--)
|
||||
control[i + 1] = control[i];
|
||||
} else {
|
||||
blockSize--;
|
||||
for (uint8_t i = 0U; i < (RX_BLOCK_SIZE - 1U); i++)
|
||||
control[i] = control[i + 1U];
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_lockout)
|
||||
return;
|
||||
|
||||
if (m_modemState == STATE_IDLE) {
|
||||
if (m_dstarEnable) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
dstarRX.samples(GMSKVals, rssi, blockSize);
|
||||
dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_dmrEnable || m_ysfEnable || m_p25Enable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
if (m_p25Enable) {
|
||||
q15_t P25Vals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_P25Filter, samples, P25Vals, RX_BLOCK_SIZE);
|
||||
|
||||
p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_dmrEnable || m_ysfEnable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_dmrEnable) {
|
||||
if (m_duplex)
|
||||
dmrIdleRX.samples(C4FSKVals, blockSize);
|
||||
dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
|
||||
dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_ysfEnable)
|
||||
ysfRX.samples(C4FSKVals, rssi, blockSize);
|
||||
|
||||
if (m_p25Enable)
|
||||
p25RX.samples(C4FSKVals, rssi, blockSize);
|
||||
ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DSTAR) {
|
||||
if (m_dstarEnable) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
dstarRX.samples(GMSKVals, rssi, blockSize);
|
||||
dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DMR) {
|
||||
if (m_dmrEnable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_duplex) {
|
||||
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
|
||||
if (m_tx)
|
||||
dmrRX.samples(C4FSKVals, rssi, control, blockSize);
|
||||
dmrRX.samples(C4FSKVals, rssi, control, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrIdleRX.samples(C4FSKVals, blockSize);
|
||||
dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE);
|
||||
} else {
|
||||
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
|
||||
dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
} else if (m_modemState == STATE_YSF) {
|
||||
if (m_ysfEnable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
ysfRX.samples(C4FSKVals, rssi, blockSize);
|
||||
ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_P25) {
|
||||
if (m_p25Enable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
q15_t P25Vals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_P25Filter, samples, P25Vals, RX_BLOCK_SIZE);
|
||||
|
||||
p25RX.samples(C4FSKVals, rssi, blockSize);
|
||||
p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DSTARCAL) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
calDStarRX.samples(GMSKVals, blockSize);
|
||||
calDStarRX.samples(GMSKVals, RX_BLOCK_SIZE);
|
||||
} else if (m_modemState == STATE_RSSICAL) {
|
||||
calRSSI.samples(rssi, blockSize);
|
||||
calRSSI.samples(rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
4
IO.h
4
IO.h
|
@ -62,8 +62,10 @@ private:
|
|||
|
||||
arm_fir_instance_q15 m_C4FSKFilter;
|
||||
arm_fir_instance_q15 m_GMSKFilter;
|
||||
arm_fir_instance_q15 m_P25Filter;
|
||||
q15_t m_C4FSKState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
q15_t m_GMSKState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
|
||||
q15_t m_P25State[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare
|
||||
|
||||
bool m_pttInvert;
|
||||
q15_t m_rxLevel;
|
||||
|
@ -81,8 +83,6 @@ private:
|
|||
uint16_t m_adcOverflow;
|
||||
uint16_t m_dacOverflow;
|
||||
|
||||
uint32_t m_count;
|
||||
|
||||
volatile uint32_t m_watchdog;
|
||||
|
||||
bool m_lockout;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Mathis Schmieder DB9MAT
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
|
@ -36,9 +36,6 @@ bool m_duplex = true;
|
|||
bool m_tx = false;
|
||||
bool m_dcd = false;
|
||||
|
||||
uint32_t m_sampleCount = 0U;
|
||||
bool m_sampleInsert = false;
|
||||
|
||||
CDStarRX dstarRX;
|
||||
CDStarTX dstarTX;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -33,9 +33,6 @@ bool m_duplex = true;
|
|||
bool m_tx = false;
|
||||
bool m_dcd = false;
|
||||
|
||||
uint32_t m_sampleCount = 0U;
|
||||
bool m_sampleInsert = false;
|
||||
|
||||
CDStarRX dstarRX;
|
||||
CDStarTX dstarTX;
|
||||
|
||||
|
|
86
P25TX.cpp
86
P25TX.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -25,16 +25,16 @@
|
|||
#include "P25Defines.h"
|
||||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t P25_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t P25_C4FSK_FILTER_LEN = 22U;
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'normal') in MATLAB
|
||||
// numTaps = 20, L = 5
|
||||
static q15_t P25_C4FSK_FILTER[] = {-1392, -2602, -3043, -2238, 0, 3460, 7543, 11400, 14153, 15152, 14153, 11400, 7543, 3460, 0, -2238, -3043, -2602, -1392, 0};
|
||||
const uint16_t P25_C4FSK_FILTER_PHASE_LEN = 4U; // phaseLength = numTaps/L
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t P25_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t P25_C4FSK_FILTER_LEN = 42U;
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'normal') in MATLAB
|
||||
// numTaps = 40, L = 5
|
||||
static q15_t P25_C4FSK_FILTER[] = {-413, -751, -845, -587, 0, 740, 1348, 1520, 1063, 0, -1383, -2583, -3021, -2222, 0, 3435, 7488, 11318, 14053, 15044, 14053,
|
||||
11318, 7488, 3435, 0, -2222, -3021, -2583, -1383, 0, 1063, 1520, 1348, 740, 0, -587, -845, -751, -413, 0};
|
||||
const uint16_t P25_C4FSK_FILTER_PHASE_LEN = 8U; // phaseLength = numTaps/L
|
||||
#endif
|
||||
|
||||
// Generated in MATLAB using the following commands, and then normalised for unity gain
|
||||
|
@ -46,10 +46,10 @@ static q15_t P25_LP_FILTER[] = {170, 401, 340, -203, -715, -478, 281, 419, -440,
|
|||
281, -478, -715, -203, 340, 401, 170};
|
||||
const uint16_t P25_LP_FILTER_LEN = 44U;
|
||||
|
||||
const q15_t P25_LEVELA[] = { 495, 495, 495, 495, 495};
|
||||
const q15_t P25_LEVELB[] = { 165, 165, 165, 165, 165};
|
||||
const q15_t P25_LEVELC[] = {-165, -165, -165, -165, -165};
|
||||
const q15_t P25_LEVELD[] = {-495, -495, -495, -495, -495};
|
||||
const q15_t P25_LEVELA = 1698;
|
||||
const q15_t P25_LEVELB = 566;
|
||||
const q15_t P25_LEVELC = -566;
|
||||
const q15_t P25_LEVELD = -1698;
|
||||
|
||||
const uint8_t P25_START_SYNC = 0x77U;
|
||||
|
||||
|
@ -62,15 +62,15 @@ m_lpState(),
|
|||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_count(0U)
|
||||
m_txDelay(240U) // 200ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
::memset(m_lpState, 0x00U, 70U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = P25_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.L = P25_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = P25_C4FSK_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = P25_C4FSK_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
|
||||
m_lpFilter.numTaps = P25_LP_FILTER_LEN;
|
||||
m_lpFilter.pState = m_lpState;
|
||||
|
@ -84,8 +84,6 @@ void CP25TX::process()
|
|||
|
||||
if (m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
m_count = 0U;
|
||||
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = P25_START_SYNC;
|
||||
} else {
|
||||
|
@ -135,61 +133,45 @@ uint8_t CP25TX::writeData(const uint8_t* data, uint8_t length)
|
|||
|
||||
void CP25TX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
|
||||
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += P25_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, P25_LEVELA, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, P25_LEVELB, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, P25_LEVELC, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELC;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, P25_LEVELD, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = P25_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += P25_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U);
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, intBuffer, blockSize);
|
||||
|
||||
::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_P25, outBuffer, blockSize);
|
||||
io.write(STATE_P25, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CP25TX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
uint16_t CP25TX::getSpace() const
|
||||
uint8_t CP25TX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / P25_LDU_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
|
21
P25TX.h
21
P25TX.h
|
@ -33,19 +33,18 @@ public:
|
|||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
arm_fir_instance_q15 m_lpFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
uint32_t m_count;
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
arm_fir_instance_q15 m_lpFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
|
|
@ -218,6 +218,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
|||
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
|
||||
bool txInvert = (data[0U] & 0x02U) == 0x02U;
|
||||
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
|
||||
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
|
||||
bool simplex = (data[0U] & 0x80U) == 0x80U;
|
||||
|
||||
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
|
||||
|
@ -250,18 +251,6 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
|||
|
||||
uint8_t dmrDelay = data[7U];
|
||||
|
||||
int8_t oscOffset = int8_t(data[8U]) - 128;
|
||||
if (oscOffset < 0) {
|
||||
m_sampleCount = 1000000U / uint32_t(-oscOffset);
|
||||
m_sampleInsert = false;
|
||||
} else if (oscOffset > 0) {
|
||||
m_sampleCount = 1000000U / uint32_t(oscOffset);
|
||||
m_sampleInsert = true;
|
||||
} else {
|
||||
m_sampleCount = 0U;
|
||||
m_sampleInsert = false;
|
||||
}
|
||||
|
||||
uint8_t cwIdTXLevel = data[5U];
|
||||
uint8_t dstarTXLevel = data[9U];
|
||||
uint8_t dmrTXLevel = data[10U];
|
||||
|
@ -284,9 +273,12 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
|||
dmrTX.setColorCode(colorCode);
|
||||
dmrRX.setColorCode(colorCode);
|
||||
dmrRX.setDelay(dmrDelay);
|
||||
dmrDMOTX.setColorCode(colorCode);
|
||||
dmrDMORX.setColorCode(colorCode);
|
||||
dmrIdleRX.setColorCode(colorCode);
|
||||
|
||||
ysfTX.setLoDev(ysfLoDev);
|
||||
|
||||
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel);
|
||||
|
||||
io.start();
|
||||
|
|
|
@ -38,8 +38,8 @@ UART5 - TXD PC12 - RXD PD2 (Discovery, Pi and Nucleo with Morpho header)
|
|||
|
||||
#if defined(STM32F4XX) || defined(STM32F4)
|
||||
|
||||
#define TX_SERIAL_FIFO_SIZE 256U
|
||||
#define RX_SERIAL_FIFO_SIZE 256U
|
||||
#define TX_SERIAL_FIFO_SIZE 512U
|
||||
#define RX_SERIAL_FIFO_SIZE 512U
|
||||
|
||||
extern "C" {
|
||||
void USART1_IRQHandler();
|
||||
|
|
85
YSFTX.cpp
85
YSFTX.cpp
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -26,21 +26,26 @@
|
|||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t YSF_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t YSF_C4FSK_FILTER_LEN = 22U;
|
||||
static q15_t YSF_C4FSK_FILTER[] = {0, 0, 0, 0, 688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688}; // numTaps = 25, L = 5
|
||||
const uint16_t YSF_C4FSK_FILTER_PHASE_LEN = 5U; // phaseLength = numTaps/L
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t YSF_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
static q15_t YSF_C4FSK_FILTER[] = {0, 0, 0, 0, 401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t YSF_C4FSK_FILTER_LEN = 42U;
|
||||
-553, -847, -731, -340, 104, 401}; // numTaps = 45, L = 5
|
||||
const uint16_t YSF_C4FSK_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
#endif
|
||||
|
||||
const q15_t YSF_LEVELA[] = { 809, 809, 809, 809, 809};
|
||||
const q15_t YSF_LEVELB[] = { 269, 269, 269, 269, 269};
|
||||
const q15_t YSF_LEVELC[] = {-269, -269, -269, -269, -269};
|
||||
const q15_t YSF_LEVELD[] = {-809, -809, -809, -809, -809};
|
||||
const q15_t YSF_LEVELA_HI = 3510;
|
||||
const q15_t YSF_LEVELB_HI = 1170;
|
||||
const q15_t YSF_LEVELC_HI = -1170;
|
||||
const q15_t YSF_LEVELD_HI = -3510;
|
||||
|
||||
const q15_t YSF_LEVELA_LO = 1755;
|
||||
const q15_t YSF_LEVELB_LO = 585;
|
||||
const q15_t YSF_LEVELC_LO = -585;
|
||||
const q15_t YSF_LEVELD_LO = -1755;
|
||||
|
||||
const uint8_t YSF_START_SYNC = 0x77U;
|
||||
const uint8_t YSF_END_SYNC = 0xFFU;
|
||||
|
@ -53,13 +58,14 @@ m_poBuffer(),
|
|||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_count(0U)
|
||||
m_loDev(false)
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = YSF_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.L = YSF_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = YSF_C4FSK_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = YSF_C4FSK_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
void CYSFTX::process()
|
||||
|
@ -69,8 +75,6 @@ void CYSFTX::process()
|
|||
|
||||
if (m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
m_count = 0U;
|
||||
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = YSF_START_SYNC;
|
||||
} else {
|
||||
|
@ -118,59 +122,48 @@ uint8_t CYSFTX::writeData(const uint8_t* data, uint8_t length)
|
|||
|
||||
void CYSFTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += YSF_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, YSF_LEVELA, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELA_LO : YSF_LEVELA_HI;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, YSF_LEVELB, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELB_LO : YSF_LEVELB_HI;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, YSF_LEVELC, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELC_LO : YSF_LEVELC_HI;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, YSF_LEVELD, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELD_LO : YSF_LEVELD_HI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = YSF_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += YSF_RADIO_SYMBOL_LENGTH * 4U;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_YSF, outBuffer, blockSize);
|
||||
io.write(STATE_YSF, outBuffer, YSF_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CYSFTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
uint16_t CYSFTX::getSpace() const
|
||||
uint8_t CYSFTX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
void CYSFTX::setLoDev(bool on)
|
||||
{
|
||||
m_loDev = on;
|
||||
}
|
||||
|
||||
|
|
20
YSFTX.h
20
YSFTX.h
|
@ -33,17 +33,19 @@ public:
|
|||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
void setLoDev(bool on);
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
uint32_t m_count;
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
bool m_loDev;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
---------------------------------------------------------------------------*/
|
||||
#if defined(STM32F105xC)
|
||||
|
||||
.syntax unified
|
||||
.arch armv7-m
|
||||
|
||||
|
@ -421,3 +423,5 @@ Default_Handler:
|
|||
def_irq_handler OTG_FS_IRQHandler
|
||||
|
||||
.end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#if defined(STM32F105xC)
|
||||
|
||||
/** @addtogroup CMSIS
|
||||
* @{
|
||||
|
@ -220,6 +221,7 @@ static void SetSysClock(void)
|
|||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
|
||||
|
|
Loading…
Reference in New Issue