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