Merge branch 'c4fmdemod'

This commit is contained in:
Andy CA6JAU 2017-04-09 14:55:03 -03:00
commit bc969ff5e5
21 changed files with 384 additions and 389 deletions

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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
View File

@ -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);

View File

@ -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))

View File

@ -54,6 +54,9 @@ private:
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);

View File

@ -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;
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 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);
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);
}

View File

@ -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);

View File

@ -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
View File

@ -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
View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
View File

@ -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);
};

View File

@ -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();

View File

@ -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();

View File

@ -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
View File

@ -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);
};

View File

@ -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

View File

@ -84,6 +84,7 @@
*
******************************************************************************
*/
#if defined(STM32F105xC)
/** @addtogroup CMSIS
* @{
@ -221,5 +222,6 @@ static void SetSysClock(void)
* @}
*/
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/