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 * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -25,22 +25,43 @@
#if defined(WIDE_C4FSK_FILTERS_TX) #if defined(WIDE_C4FSK_FILTERS_TX)
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB // 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, 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, 0}; 11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688}; // numTaps = 25, L = 5
const uint16_t DMR_C4FSK_FILTER_LEN = 22U; const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 5U; // phaseLength = numTaps/L
#else #else
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB // 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, 11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
-553, -847, -731, -340, 104, 401, 0}; -553, -847, -731, -340, 104, 401}; // numTaps = 45, L = 5
const uint16_t DMR_C4FSK_FILTER_LEN = 42U; const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
#endif #endif
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640}; const q15_t DMR_LEVELA = 2889;
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213}; const q15_t DMR_LEVELB = 963;
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213}; const q15_t DMR_LEVELC = -963;
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640}; 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() : CDMRDMOTX::CDMRDMOTX() :
m_fifo(), m_fifo(),
@ -50,13 +71,15 @@ m_poBuffer(),
m_poLen(0U), m_poLen(0U),
m_poPtr(0U), m_poPtr(0U),
m_txDelay(240U), // 200ms 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.L = DMR_RADIO_SYMBOL_LENGTH;
m_modFilter.pState = m_modState; m_modFilter.phaseLength = DMR_C4FSK_FILTER_PHASE_LEN;
m_modFilter.pCoeffs = DMR_C4FSK_FILTER; m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
m_modFilter.pState = m_modState;
} }
void CDMRDMOTX::process() void CDMRDMOTX::process()
@ -64,13 +87,21 @@ void CDMRDMOTX::process()
if (m_poLen == 0U && m_fifo.getData() > 0U) { if (m_poLen == 0U && m_fifo.getData() > 0U) {
if (!m_tx) { if (!m_tx) {
for (uint16_t i = 0U; i < m_txDelay; i++) for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = 0x00U; m_poBuffer[i] = DMR_SYNC;
m_poLen = m_txDelay;
} else { } else {
for (unsigned int i = 0U; i < 72U; i++) createCACH(m_poBuffer + 0U, 0U);
m_poBuffer[m_poLen++] = 0x00U;
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) 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; m_poPtr = 0U;
@ -112,53 +143,34 @@ uint8_t CDMRDMOTX::writeData(const uint8_t* data, uint8_t length)
void CDMRDMOTX::writeByte(uint8_t c) void CDMRDMOTX::writeByte(uint8_t c)
{ {
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t inBuffer[4U];
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
const uint8_t MASK = 0xC0U; const uint8_t MASK = 0xC0U;
q15_t* p = inBuffer; for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
switch (c & MASK) { switch (c & MASK) {
case 0xC0U: case 0xC0U:
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELA;
break; break;
case 0x80U: case 0x80U:
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELB;
break; break;
case 0x00U: case 0x00U:
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELC;
break; break;
default: default:
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELD;
break; 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 io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U);
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; uint8_t CDMRDMOTX::getSpace() const
}
}
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
io.write(STATE_DMR, outBuffer, blockSize);
}
uint16_t CDMRDMOTX::getSpace() const
{ {
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U); return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
} }
@ -166,5 +178,48 @@ uint16_t CDMRDMOTX::getSpace() const
void CDMRDMOTX::setTXDelay(uint8_t delay) void CDMRDMOTX::setTXDelay(uint8_t delay)
{ {
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx 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 * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -35,18 +35,22 @@ public:
void setTXDelay(uint8_t delay); void setTXDelay(uint8_t delay);
uint16_t getSpace() const; uint8_t getSpace() const;
void setColorCode(uint8_t colorCode);
private: private:
CSerialRB m_fifo; CSerialRB m_fifo;
arm_fir_instance_q15 m_modFilter; arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
uint8_t m_poBuffer[1200U]; uint8_t m_poBuffer[1200U];
uint16_t m_poLen; uint16_t m_poLen;
uint16_t m_poPtr; uint16_t m_poPtr;
uint16_t m_txDelay; uint32_t m_txDelay;
uint32_t m_count; 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); 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 * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -25,21 +25,21 @@
#if defined(WIDE_C4FSK_FILTERS_TX) #if defined(WIDE_C4FSK_FILTERS_TX)
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB // 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, 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, 0}; 11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688}; // numTaps = 25, L = 5
const uint16_t DMR_C4FSK_FILTER_LEN = 22U; const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 5U; // phaseLength = numTaps/L
#else #else
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB // 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, 11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
-553, -847, -731, -340, 104, 401, 0}; -553, -847, -731, -340, 104, 401}; // numTaps = 45, L = 5
const uint16_t DMR_C4FSK_FILTER_LEN = 42U; const uint16_t DMR_C4FSK_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
#endif #endif
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640}; const q15_t DMR_LEVELA = 2889;
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213}; const q15_t DMR_LEVELB = 963;
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213}; const q15_t DMR_LEVELC = -963;
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640}; const q15_t DMR_LEVELD = -2889;
// The PR FILL and Data Sync pattern. // The PR FILL and Data Sync pattern.
const uint8_t IDLE_DATA[] = 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 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]) #define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
const uint32_t STARTUP_COUNT = 20U;
CDMRTX::CDMRTX() : CDMRTX::CDMRTX() :
m_fifo(), m_fifo(),
m_modFilter(), m_modFilter(),
@ -74,14 +76,15 @@ m_markBuffer(),
m_poBuffer(), m_poBuffer(),
m_poLen(0U), m_poLen(0U),
m_poPtr(0U), m_poPtr(0U),
m_count(0U), m_frameCount(0U),
m_abort() 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.L = DMR_RADIO_SYMBOL_LENGTH;
m_modFilter.pState = m_modState; m_modFilter.phaseLength = DMR_C4FSK_FILTER_PHASE_LEN;
m_modFilter.pCoeffs = DMR_C4FSK_FILTER; m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
m_modFilter.pState = m_modState;
::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U); ::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U);
::memcpy(m_shortLC, 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_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
m_count = 0U; m_frameCount = 0U;
m_abort[0U] = false; m_abort[0U] = false;
m_abort[1U] = false; m_abort[1U] = false;
@ -240,80 +243,54 @@ void CDMRTX::setStart(bool start)
void CDMRTX::setCal(bool start) void CDMRTX::setCal(bool start)
{ {
m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE; m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE;
m_count = 0U;
} }
void CDMRTX::writeByte(uint8_t c, uint8_t control) void CDMRTX::writeByte(uint8_t c, uint8_t control)
{ {
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t inBuffer[4U];
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
const uint8_t MASK = 0xC0U; const uint8_t MASK = 0xC0U;
q15_t* p = inBuffer; for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
switch (c & MASK) { switch (c & MASK) {
case 0xC0U: case 0xC0U:
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELA;
break; break;
case 0x80U: case 0x80U:
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELB;
break; break;
case 0x00U: case 0x00U:
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELC;
break; break;
default: default:
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = DMR_LEVELD;
break; break;
} }
} }
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U; uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
::memset(controlBuffer, MARK_NONE, DMR_RADIO_SYMBOL_LENGTH * 4U * sizeof(uint8_t)); ::memset(controlBuffer, MARK_NONE, DMR_RADIO_SYMBOL_LENGTH * 4U * sizeof(uint8_t));
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U] = control; controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U] = control;
// Handle the case of the oscillator not being accurate enough ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
if (m_sampleCount > 0U) {
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
if (m_count >= m_sampleCount) { io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U, controlBuffer);
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; uint8_t CDMRTX::getSpace1() const
}
}
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
io.write(STATE_DMR, outBuffer, blockSize, controlBuffer);
}
uint16_t CDMRTX::getSpace1() const
{ {
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U); 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); return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
} }
void CDMRTX::createData(uint8_t slotIndex) 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++) { for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = m_fifo[slotIndex].get(); m_poBuffer[i] = m_fifo[slotIndex].get();
m_markBuffer[i] = MARK_NONE; m_markBuffer[i] = MARK_NONE;
@ -344,6 +321,8 @@ void CDMRTX::createCal()
void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex) void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
{ {
m_frameCount++;
if (m_cachPtr >= 12U) if (m_cachPtr >= 12U)
m_cachPtr = 0U; m_cachPtr = 0U;
@ -359,7 +338,9 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
m_markBuffer[1U] = MARK_NONE; m_markBuffer[1U] = MARK_NONE;
m_markBuffer[2U] = rxSlotIndex == 1U ? MARK_SLOT1 : MARK_SLOT2; 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 tc = txSlotIndex == 1U;
bool ls0 = true; // For 1 and 2 bool ls0 = true; // For 1 and 2
bool ls1 = true; bool ls1 = true;

12
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 * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -49,15 +49,15 @@ public:
void process(); void process();
uint16_t getSpace1() const; uint8_t getSpace1() const;
uint16_t getSpace2() const; uint8_t getSpace2() const;
void setColorCode(uint8_t colorCode); void setColorCode(uint8_t colorCode);
private: private:
CSerialRB m_fifo[2U]; CSerialRB m_fifo[2U];
arm_fir_instance_q15 m_modFilter; arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
DMRTXSTATE m_state; DMRTXSTATE m_state;
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES]; uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
uint8_t m_cachPtr; uint8_t m_cachPtr;
@ -67,7 +67,7 @@ private:
uint8_t m_poBuffer[40U]; uint8_t m_poBuffer[40U];
uint16_t m_poLen; uint16_t m_poLen;
uint16_t m_poPtr; uint16_t m_poPtr;
uint32_t m_count; uint32_t m_frameCount;
bool m_abort[2U]; bool m_abort[2U];
void createData(uint8_t slotIndex); void createData(uint8_t slotIndex);

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_START = SYNC_POS - 3U;
const unsigned int SYNC_SCAN_END = 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 // D-Star bit order version of 0x55 0x55 0x6E 0x0A
const uint32_t FRAME_SYNC_DATA = 0x00557650U; const uint32_t FRAME_SYNC_DATA = 0x00557650U;
@ -262,6 +264,12 @@ m_fecOutput(),
m_rssiAccum(0U), m_rssiAccum(0U),
m_rssiCount(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() void CDStarRX::reset()
@ -278,11 +286,21 @@ void CDStarRX::reset()
void CDStarRX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length) 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++) { for (uint16_t i = 0U; i < length; i++) {
m_rssiAccum += rssi[i]; m_rssiAccum += rssi[i];
m_rssiCount++; m_rssiCount++;
bool bit = samples[i] < THRESHOLD; bool bit = samples[i] < (q15_t)dc_level;
if (bit != m_prev) { if (bit != m_prev) {
if (m_pll < (PLLMAX / 2U)) if (m_pll < (PLLMAX / 2U))

View File

@ -54,6 +54,9 @@ private:
uint32_t m_rssiAccum; uint32_t m_rssiAccum;
uint16_t m_rssiCount; uint16_t m_rssiCount;
arm_biquad_casd_df1_inst_q15 m_DCFilter;
q15_t m_DCState[4];
void processNone(bool bit); void processNone(bool bit);
void processHeader(bool bit); void processHeader(bool bit);
void processData(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}; const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U};
// Generated using gaussfir(0.5, 4, 5) in MATLAB // Generated using gaussfir(0.35, 1, 5) in MATLAB
static q15_t DSTAR_GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0}; 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_LEN = 12U; 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_LEVEL0 = -4000;
const q15_t DSTAR_LEVEL1[] = { 800, 800, 800, 800, 800}; const q15_t DSTAR_LEVEL1 = 4000;
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
@ -195,14 +195,14 @@ m_modState(),
m_poBuffer(), m_poBuffer(),
m_poLen(0U), m_poLen(0U),
m_poPtr(0U), m_poPtr(0U),
m_txDelay(60U), // 100ms m_txDelay(60U) // 100ms
m_count(0U)
{ {
::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.L = DSTAR_RADIO_BIT_LENGTH;
m_modFilter.pState = m_modState; m_modFilter.phaseLength = DSTAR_GMSK_FILTER_PHASE_LEN;
m_modFilter.pCoeffs = DSTAR_GMSK_FILTER; m_modFilter.pCoeffs = DSTAR_GMSK_FILTER;
m_modFilter.pState = m_modState;
} }
void CDStarTX::process() void CDStarTX::process()
@ -214,8 +214,6 @@ void CDStarTX::process()
if (type == DSTAR_HEADER && m_poLen == 0U) { if (type == DSTAR_HEADER && m_poLen == 0U) {
if (!m_tx) { if (!m_tx) {
m_count = 0U;
for (uint16_t i = 0U; i < m_txDelay; i++) for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = BIT_SYNC; m_poBuffer[m_poLen++] = BIT_SYNC;
} else { } else {
@ -241,9 +239,6 @@ void CDStarTX::process()
} }
if (type == DSTAR_DATA && m_poLen == 0U) { if (type == DSTAR_DATA && m_poLen == 0U) {
if (!m_tx)
m_count = 0U;
// Pop the type byte off // Pop the type byte off
m_buffer.get(); m_buffer.get();
@ -417,50 +412,34 @@ void CDStarTX::txHeader(const uint8_t* in, uint8_t* out) const
void CDStarTX::writeByte(uint8_t c) void CDStarTX::writeByte(uint8_t c)
{ {
q15_t inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U]; q15_t inBuffer[8U];
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U]; q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U];
uint8_t mask = 0x01U; uint8_t mask = 0x01U;
q15_t* p = inBuffer; for (uint8_t i = 0U; i < 8U; i++) {
for (uint8_t i = 0U; i < 8U; i++, p += DSTAR_RADIO_BIT_LENGTH) {
if ((c & mask) == mask) if ((c & mask) == mask)
::memcpy(p, DSTAR_LEVEL0, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t)); inBuffer[i] = DSTAR_LEVEL0;
else else
::memcpy(p, DSTAR_LEVEL1, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t)); inBuffer[i] = DSTAR_LEVEL1;
mask <<= 1; 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 io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_BIT_LENGTH * 8U);
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);
} }
void CDStarTX::setTXDelay(uint8_t delay) void CDStarTX::setTXDelay(uint8_t delay)
{ {
m_txDelay = 300U + uint16_t(delay) * 6U; // 250ms + tx 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); return m_buffer.getSpace() / (DSTAR_DATA_LENGTH_BYTES + 1U);
} }

View File

@ -35,17 +35,16 @@ public:
void setTXDelay(uint8_t delay); void setTXDelay(uint8_t delay);
uint16_t getSpace() const; uint8_t getSpace() const;
private: private:
CSerialRB m_buffer; CSerialRB m_buffer;
arm_fir_instance_q15 m_modFilter; arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[60U]; // NoTaps + BlockSize - 1, 12 + 40 - 1 plus some spare q15_t m_modState[20U]; // blockSize + phaseLength - 1, 8 + 9 - 1 plus some spare
uint8_t m_poBuffer[600U]; uint8_t m_poBuffer[600U];
uint16_t m_poLen; uint16_t m_poLen;
uint16_t m_poPtr; uint16_t m_poPtr;
uint16_t m_txDelay; // In bytes uint16_t m_txDelay; // In bytes
uint32_t m_count;
void txHeader(const uint8_t* in, uint8_t* out) const; void txHeader(const uint8_t* in, uint8_t* out) const;
void writeByte(uint8_t c); void writeByte(uint8_t c);

View File

@ -93,9 +93,6 @@ extern bool m_duplex;
extern bool m_tx; extern bool m_tx;
extern bool m_dcd; extern bool m_dcd;
extern uint32_t m_sampleCount;
extern bool m_sampleInsert;
extern CSerialPort serial; extern CSerialPort serial;
extern CIO io; 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}; static q15_t GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
const uint16_t GMSK_FILTER_LEN = 12U; 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; const uint16_t DC_OFFSET = 2048U;
CIO::CIO() : CIO::CIO() :
@ -50,8 +54,10 @@ m_txBuffer(TX_RINGBUFFER_SIZE),
m_rssiBuffer(RX_RINGBUFFER_SIZE), m_rssiBuffer(RX_RINGBUFFER_SIZE),
m_C4FSKFilter(), m_C4FSKFilter(),
m_GMSKFilter(), m_GMSKFilter(),
m_P25Filter(),
m_C4FSKState(), m_C4FSKState(),
m_GMSKState(), m_GMSKState(),
m_P25State(),
m_pttInvert(false), m_pttInvert(false),
m_rxLevel(128 * 128), m_rxLevel(128 * 128),
m_cwIdTXLevel(128 * 128), m_cwIdTXLevel(128 * 128),
@ -64,12 +70,12 @@ m_ledValue(true),
m_detect(false), m_detect(false),
m_adcOverflow(0U), m_adcOverflow(0U),
m_dacOverflow(0U), m_dacOverflow(0U),
m_count(0U),
m_watchdog(0U), m_watchdog(0U),
m_lockout(false) m_lockout(false)
{ {
::memset(m_C4FSKState, 0x00U, 70U * sizeof(q15_t)); ::memset(m_C4FSKState, 0x00U, 70U * sizeof(q15_t));
::memset(m_GMSKState, 0x00U, 40U * 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.numTaps = C4FSK_FILTER_LEN;
m_C4FSKFilter.pState = m_C4FSKState; m_C4FSKFilter.pState = m_C4FSKState;
@ -79,6 +85,10 @@ m_lockout(false)
m_GMSKFilter.pState = m_GMSKState; m_GMSKFilter.pState = m_GMSKState;
m_GMSKFilter.pCoeffs = GMSK_FILTER; m_GMSKFilter.pCoeffs = GMSK_FILTER;
m_P25Filter.numTaps = P25_FILTER_LEN;
m_P25Filter.pState = m_P25State;
m_P25Filter.pCoeffs = P25_FILTER;
initInt(); initInt();
} }
@ -89,7 +99,6 @@ void CIO::start()
startInt(); startInt();
m_count = 0U;
m_started = true; m_started = true;
setMode(); setMode();
@ -136,11 +145,9 @@ void CIO::process()
} }
if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) { if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) {
q15_t samples[RX_BLOCK_SIZE + 1U]; q15_t samples[RX_BLOCK_SIZE];
uint8_t control[RX_BLOCK_SIZE + 1U]; uint8_t control[RX_BLOCK_SIZE];
uint16_t rssi[RX_BLOCK_SIZE + 1U]; uint16_t rssi[RX_BLOCK_SIZE];
uint8_t blockSize = RX_BLOCK_SIZE;
for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) {
uint16_t sample; uint16_t sample;
@ -156,97 +163,81 @@ void CIO::process()
samples[i] = q15_t(__SSAT((res2 >> 15), 16)); 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) if (m_lockout)
return; return;
if (m_modemState == STATE_IDLE) { if (m_modemState == STATE_IDLE) {
if (m_dstarEnable) { if (m_dstarEnable) {
q15_t GMSKVals[RX_BLOCK_SIZE + 1U]; q15_t GMSKVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize); ::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) { if (m_p25Enable) {
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U]; q15_t P25Vals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize); ::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_dmrEnable) {
if (m_duplex) if (m_duplex)
dmrIdleRX.samples(C4FSKVals, blockSize); dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE);
else else
dmrDMORX.samples(C4FSKVals, rssi, blockSize); dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
} }
if (m_ysfEnable) if (m_ysfEnable)
ysfRX.samples(C4FSKVals, rssi, blockSize); ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
if (m_p25Enable)
p25RX.samples(C4FSKVals, rssi, blockSize);
} }
} else if (m_modemState == STATE_DSTAR) { } else if (m_modemState == STATE_DSTAR) {
if (m_dstarEnable) { if (m_dstarEnable) {
q15_t GMSKVals[RX_BLOCK_SIZE + 1U]; q15_t GMSKVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize); ::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) { } else if (m_modemState == STATE_DMR) {
if (m_dmrEnable) { if (m_dmrEnable) {
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U]; q15_t C4FSKVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize); ::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
if (m_duplex) { if (m_duplex) {
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs // If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
if (m_tx) if (m_tx)
dmrRX.samples(C4FSKVals, rssi, control, blockSize); dmrRX.samples(C4FSKVals, rssi, control, RX_BLOCK_SIZE);
else else
dmrIdleRX.samples(C4FSKVals, blockSize); dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE);
} else { } else {
dmrDMORX.samples(C4FSKVals, rssi, blockSize); dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
} }
} }
} else if (m_modemState == STATE_YSF) { } else if (m_modemState == STATE_YSF) {
if (m_ysfEnable) { if (m_ysfEnable) {
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U]; q15_t C4FSKVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize); ::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) { } else if (m_modemState == STATE_P25) {
if (m_p25Enable) { if (m_p25Enable) {
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U]; q15_t P25Vals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize); ::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) { } else if (m_modemState == STATE_DSTARCAL) {
q15_t GMSKVals[RX_BLOCK_SIZE + 1U]; q15_t GMSKVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize); ::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) { } 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_C4FSKFilter;
arm_fir_instance_q15 m_GMSKFilter; 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_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_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; bool m_pttInvert;
q15_t m_rxLevel; q15_t m_rxLevel;
@ -81,8 +83,6 @@ private:
uint16_t m_adcOverflow; uint16_t m_adcOverflow;
uint16_t m_dacOverflow; uint16_t m_dacOverflow;
uint32_t m_count;
volatile uint32_t m_watchdog; volatile uint32_t m_watchdog;
bool m_lockout; 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 Mathis Schmieder DB9MAT
* Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016 by Colin Durbridge G4EML
* *
@ -36,9 +36,6 @@ bool m_duplex = true;
bool m_tx = false; bool m_tx = false;
bool m_dcd = false; bool m_dcd = false;
uint32_t m_sampleCount = 0U;
bool m_sampleInsert = false;
CDStarRX dstarRX; CDStarRX dstarRX;
CDStarTX dstarTX; 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 * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * 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_tx = false;
bool m_dcd = false; bool m_dcd = false;
uint32_t m_sampleCount = 0U;
bool m_sampleInsert = false;
CDStarRX dstarRX; CDStarRX dstarRX;
CDStarTX dstarTX; 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 * 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 * it under the terms of the GNU General Public License as published by
@ -25,16 +25,16 @@
#include "P25Defines.h" #include "P25Defines.h"
#if defined(WIDE_C4FSK_FILTERS_TX) #if defined(WIDE_C4FSK_FILTERS_TX)
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB // Generated using rcosdesign(0.2, 4, 5, 'normal') in MATLAB
static q15_t P25_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425, // numTaps = 20, L = 5
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0}; 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_LEN = 22U; const uint16_t P25_C4FSK_FILTER_PHASE_LEN = 4U; // phaseLength = numTaps/L
#else #else
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB // Generated using rcosdesign(0.2, 8, 5, 'normal') 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, // numTaps = 40, L = 5
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112, 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,
-553, -847, -731, -340, 104, 401, 0}; 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_LEN = 42U; const uint16_t P25_C4FSK_FILTER_PHASE_LEN = 8U; // phaseLength = numTaps/L
#endif #endif
// Generated in MATLAB using the following commands, and then normalised for unity gain // 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}; 281, -478, -715, -203, 340, 401, 170};
const uint16_t P25_LP_FILTER_LEN = 44U; const uint16_t P25_LP_FILTER_LEN = 44U;
const q15_t P25_LEVELA[] = { 495, 495, 495, 495, 495}; const q15_t P25_LEVELA = 1698;
const q15_t P25_LEVELB[] = { 165, 165, 165, 165, 165}; const q15_t P25_LEVELB = 566;
const q15_t P25_LEVELC[] = {-165, -165, -165, -165, -165}; const q15_t P25_LEVELC = -566;
const q15_t P25_LEVELD[] = {-495, -495, -495, -495, -495}; const q15_t P25_LEVELD = -1698;
const uint8_t P25_START_SYNC = 0x77U; const uint8_t P25_START_SYNC = 0x77U;
@ -62,15 +62,15 @@ m_lpState(),
m_poBuffer(), m_poBuffer(),
m_poLen(0U), m_poLen(0U),
m_poPtr(0U), m_poPtr(0U),
m_txDelay(240U), // 200ms m_txDelay(240U) // 200ms
m_count(0U)
{ {
::memset(m_modState, 0x00U, 70U * sizeof(q15_t)); ::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
::memset(m_lpState, 0x00U, 70U * sizeof(q15_t)); ::memset(m_lpState, 0x00U, 70U * sizeof(q15_t));
m_modFilter.numTaps = P25_C4FSK_FILTER_LEN; m_modFilter.L = P25_RADIO_SYMBOL_LENGTH;
m_modFilter.pState = m_modState; m_modFilter.phaseLength = P25_C4FSK_FILTER_PHASE_LEN;
m_modFilter.pCoeffs = P25_C4FSK_FILTER; m_modFilter.pCoeffs = P25_C4FSK_FILTER;
m_modFilter.pState = m_modState;
m_lpFilter.numTaps = P25_LP_FILTER_LEN; m_lpFilter.numTaps = P25_LP_FILTER_LEN;
m_lpFilter.pState = m_lpState; m_lpFilter.pState = m_lpState;
@ -84,8 +84,6 @@ void CP25TX::process()
if (m_poLen == 0U) { if (m_poLen == 0U) {
if (!m_tx) { if (!m_tx) {
m_count = 0U;
for (uint16_t i = 0U; i < m_txDelay; i++) for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = P25_START_SYNC; m_poBuffer[m_poLen++] = P25_START_SYNC;
} else { } else {
@ -135,61 +133,45 @@ uint8_t CP25TX::writeData(const uint8_t* data, uint8_t length)
void CP25TX::writeByte(uint8_t c) void CP25TX::writeByte(uint8_t c)
{ {
q15_t inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t inBuffer[4U];
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
const uint8_t MASK = 0xC0U; const uint8_t MASK = 0xC0U;
q15_t* p = inBuffer; for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += P25_RADIO_SYMBOL_LENGTH) {
switch (c & MASK) { switch (c & MASK) {
case 0xC0U: case 0xC0U:
::memcpy(p, P25_LEVELA, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = P25_LEVELA;
break; break;
case 0x80U: case 0x80U:
::memcpy(p, P25_LEVELB, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = P25_LEVELB;
break; break;
case 0x00U: case 0x00U:
::memcpy(p, P25_LEVELC, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = P25_LEVELC;
break; break;
default: default:
::memcpy(p, P25_LEVELD, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = P25_LEVELD;
break; 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 ::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U);
if (m_sampleCount > 0U) {
m_count += P25_RADIO_SYMBOL_LENGTH * 4U;
if (m_count >= m_sampleCount) { io.write(STATE_P25, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U);
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);
} }
void CP25TX::setTXDelay(uint8_t delay) void CP25TX::setTXDelay(uint8_t delay)
{ {
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx 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; return m_buffer.getSpace() / P25_LDU_FRAME_LENGTH_BYTES;
} }

View File

@ -33,19 +33,18 @@ public:
void setTXDelay(uint8_t delay); void setTXDelay(uint8_t delay);
uint16_t getSpace() const; uint8_t getSpace() const;
private: private:
CSerialRB m_buffer; CSerialRB m_buffer;
arm_fir_instance_q15 m_modFilter; arm_fir_interpolate_instance_q15 m_modFilter;
arm_fir_instance_q15 m_lpFilter; arm_fir_instance_q15 m_lpFilter;
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare 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 q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare
uint8_t m_poBuffer[1200U]; uint8_t m_poBuffer[1200U];
uint16_t m_poLen; uint16_t m_poLen;
uint16_t m_poPtr; uint16_t m_poPtr;
uint16_t m_txDelay; uint16_t m_txDelay;
uint32_t m_count;
void writeByte(uint8_t c); 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 rxInvert = (data[0U] & 0x01U) == 0x01U;
bool txInvert = (data[0U] & 0x02U) == 0x02U; bool txInvert = (data[0U] & 0x02U) == 0x02U;
bool pttInvert = (data[0U] & 0x04U) == 0x04U; bool pttInvert = (data[0U] & 0x04U) == 0x04U;
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
bool simplex = (data[0U] & 0x80U) == 0x80U; bool simplex = (data[0U] & 0x80U) == 0x80U;
bool dstarEnable = (data[1U] & 0x01U) == 0x01U; 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]; 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 cwIdTXLevel = data[5U];
uint8_t dstarTXLevel = data[9U]; uint8_t dstarTXLevel = data[9U];
uint8_t dmrTXLevel = data[10U]; uint8_t dmrTXLevel = data[10U];
@ -284,9 +273,12 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
dmrTX.setColorCode(colorCode); dmrTX.setColorCode(colorCode);
dmrRX.setColorCode(colorCode); dmrRX.setColorCode(colorCode);
dmrRX.setDelay(dmrDelay); dmrRX.setDelay(dmrDelay);
dmrDMOTX.setColorCode(colorCode);
dmrDMORX.setColorCode(colorCode); dmrDMORX.setColorCode(colorCode);
dmrIdleRX.setColorCode(colorCode); dmrIdleRX.setColorCode(colorCode);
ysfTX.setLoDev(ysfLoDev);
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel); io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel);
io.start(); 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) #if defined(STM32F4XX) || defined(STM32F4)
#define TX_SERIAL_FIFO_SIZE 256U #define TX_SERIAL_FIFO_SIZE 512U
#define RX_SERIAL_FIFO_SIZE 256U #define RX_SERIAL_FIFO_SIZE 512U
extern "C" { extern "C" {
void USART1_IRQHandler(); 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 * 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 * it under the terms of the GNU General Public License as published by
@ -26,21 +26,26 @@
#if defined(WIDE_C4FSK_FILTERS_TX) #if defined(WIDE_C4FSK_FILTERS_TX)
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB // 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, 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, 0}; 11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688}; // numTaps = 25, L = 5
const uint16_t YSF_C4FSK_FILTER_LEN = 22U; const uint16_t YSF_C4FSK_FILTER_PHASE_LEN = 5U; // phaseLength = numTaps/L
#else #else
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB // 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, 11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
-553, -847, -731, -340, 104, 401, 0}; -553, -847, -731, -340, 104, 401}; // numTaps = 45, L = 5
const uint16_t YSF_C4FSK_FILTER_LEN = 42U; const uint16_t YSF_C4FSK_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
#endif #endif
const q15_t YSF_LEVELA[] = { 809, 809, 809, 809, 809}; const q15_t YSF_LEVELA_HI = 3510;
const q15_t YSF_LEVELB[] = { 269, 269, 269, 269, 269}; const q15_t YSF_LEVELB_HI = 1170;
const q15_t YSF_LEVELC[] = {-269, -269, -269, -269, -269}; const q15_t YSF_LEVELC_HI = -1170;
const q15_t YSF_LEVELD[] = {-809, -809, -809, -809, -809}; 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_START_SYNC = 0x77U;
const uint8_t YSF_END_SYNC = 0xFFU; const uint8_t YSF_END_SYNC = 0xFFU;
@ -53,13 +58,14 @@ m_poBuffer(),
m_poLen(0U), m_poLen(0U),
m_poPtr(0U), m_poPtr(0U),
m_txDelay(240U), // 200ms 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.L = YSF_RADIO_SYMBOL_LENGTH;
m_modFilter.pState = m_modState; m_modFilter.phaseLength = YSF_C4FSK_FILTER_PHASE_LEN;
m_modFilter.pCoeffs = YSF_C4FSK_FILTER; m_modFilter.pCoeffs = YSF_C4FSK_FILTER;
m_modFilter.pState = m_modState;
} }
void CYSFTX::process() void CYSFTX::process()
@ -69,8 +75,6 @@ void CYSFTX::process()
if (m_poLen == 0U) { if (m_poLen == 0U) {
if (!m_tx) { if (!m_tx) {
m_count = 0U;
for (uint16_t i = 0U; i < m_txDelay; i++) for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = YSF_START_SYNC; m_poBuffer[m_poLen++] = YSF_START_SYNC;
} else { } else {
@ -118,59 +122,48 @@ uint8_t CYSFTX::writeData(const uint8_t* data, uint8_t length)
void CYSFTX::writeByte(uint8_t c) void CYSFTX::writeByte(uint8_t c)
{ {
q15_t inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t inBuffer[4U];
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U]; q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U];
const uint8_t MASK = 0xC0U; const uint8_t MASK = 0xC0U;
q15_t* p = inBuffer; for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += YSF_RADIO_SYMBOL_LENGTH) {
switch (c & MASK) { switch (c & MASK) {
case 0xC0U: case 0xC0U:
::memcpy(p, YSF_LEVELA, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = m_loDev ? YSF_LEVELA_LO : YSF_LEVELA_HI;
break; break;
case 0x80U: case 0x80U:
::memcpy(p, YSF_LEVELB, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = m_loDev ? YSF_LEVELB_LO : YSF_LEVELB_HI;
break; break;
case 0x00U: case 0x00U:
::memcpy(p, YSF_LEVELC, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = m_loDev ? YSF_LEVELC_LO : YSF_LEVELC_HI;
break; break;
default: default:
::memcpy(p, YSF_LEVELD, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); inBuffer[i] = m_loDev ? YSF_LEVELD_LO : YSF_LEVELD_HI;
break; 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 io.write(STATE_YSF, outBuffer, YSF_RADIO_SYMBOL_LENGTH * 4U);
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);
} }
void CYSFTX::setTXDelay(uint8_t delay) void CYSFTX::setTXDelay(uint8_t delay)
{ {
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx 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; return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES;
} }
void CYSFTX::setLoDev(bool on)
{
m_loDev = on;
}

10
YSFTX.h
View File

@ -33,17 +33,19 @@ public:
void setTXDelay(uint8_t delay); void setTXDelay(uint8_t delay);
uint16_t getSpace() const; uint8_t getSpace() const;
void setLoDev(bool on);
private: private:
CSerialRB m_buffer; CSerialRB m_buffer;
arm_fir_instance_q15 m_modFilter; arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
uint8_t m_poBuffer[1200U]; uint8_t m_poBuffer[1200U];
uint16_t m_poLen; uint16_t m_poLen;
uint16_t m_poPtr; uint16_t m_poPtr;
uint16_t m_txDelay; uint16_t m_txDelay;
uint32_t m_count; bool m_loDev;
void writeByte(uint8_t c); 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 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/ ---------------------------------------------------------------------------*/
#if defined(STM32F105xC)
.syntax unified .syntax unified
.arch armv7-m .arch armv7-m
@ -421,3 +423,5 @@ Default_Handler:
def_irq_handler OTG_FS_IRQHandler def_irq_handler OTG_FS_IRQHandler
.end .end
#endif

View File

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