MMDVM/IO.cpp

387 lines
9.9 KiB
C++
Raw Normal View History

2016-01-14 19:57:21 +01:00
/*
2017-01-05 19:59:15 +01:00
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
* Copyright (C) 2016 by Colin Durbridge G4EML
2016-01-14 19:57:21 +01:00
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
2016-03-14 21:24:51 +01:00
// #define WANT_DEBUG
2016-01-14 19:57:21 +01:00
#include "Config.h"
#include "Globals.h"
#include "IO.h"
#if defined(WIDE_C4FSK_FILTERS_RX)
2016-07-11 19:02:53 +02:00
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
static q15_t 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 C4FSK_FILTER_LEN = 22U;
#else
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
static q15_t 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 C4FSK_FILTER_LEN = 42U;
#endif
2016-01-14 19:57:21 +01:00
// Generated using gaussfir(0.5, 4, 5) in MATLAB
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 DC_OFFSET = 2048U;
CIO::CIO() :
m_started(false),
2016-05-10 19:11:31 +02:00
m_rxBuffer(RX_RINGBUFFER_SIZE),
m_txBuffer(TX_RINGBUFFER_SIZE),
2016-10-31 20:39:32 +01:00
m_rssiBuffer(RX_RINGBUFFER_SIZE),
2016-01-14 19:57:21 +01:00
m_C4FSKFilter(),
m_GMSKFilter(),
m_C4FSKState(),
m_GMSKState(),
m_pttInvert(false),
m_rxLevel(128 * 128),
2016-10-10 18:35:33 +02:00
m_cwIdTXLevel(128 * 128),
2016-08-09 08:11:10 +02:00
m_dstarTXLevel(128 * 128),
m_dmrTXLevel(128 * 128),
m_ysfTXLevel(128 * 128),
2016-09-08 18:53:59 +02:00
m_p25TXLevel(128 * 128),
2016-01-14 19:57:21 +01:00
m_ledCount(0U),
m_ledValue(true),
m_detect(false),
2016-06-09 20:50:34 +02:00
m_adcOverflow(0U),
m_dacOverflow(0U),
m_count(0U),
m_watchdog(0U),
m_lockout(false)
2016-01-14 19:57:21 +01:00
{
::memset(m_C4FSKState, 0x00U, 70U * sizeof(q15_t));
::memset(m_GMSKState, 0x00U, 40U * sizeof(q15_t));
m_C4FSKFilter.numTaps = C4FSK_FILTER_LEN;
m_C4FSKFilter.pState = m_C4FSKState;
m_C4FSKFilter.pCoeffs = C4FSK_FILTER;
m_GMSKFilter.numTaps = GMSK_FILTER_LEN;
m_GMSKFilter.pState = m_GMSKState;
m_GMSKFilter.pCoeffs = GMSK_FILTER;
initInt();
2016-01-14 19:57:21 +01:00
}
void CIO::start()
{
if (m_started)
return;
startInt();
2016-01-14 19:57:21 +01:00
m_count = 0U;
2016-01-14 19:57:21 +01:00
m_started = true;
setMode();
2016-01-14 19:57:21 +01:00
}
void CIO::process()
{
m_ledCount++;
if (m_started) {
2016-02-02 21:23:37 +01:00
// Two seconds timeout
if (m_watchdog >= 48000U) {
if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF) {
if (m_modemState == STATE_DMR && m_tx)
2016-02-02 21:23:37 +01:00
dmrTX.setStart(false);
m_modemState = STATE_IDLE;
setMode();
2016-02-02 21:23:37 +01:00
}
m_watchdog = 0U;
}
2016-01-14 19:57:21 +01:00
if (m_ledCount >= 24000U) {
m_ledCount = 0U;
m_ledValue = !m_ledValue;
2016-11-01 21:30:18 +01:00
setLEDInt(m_ledValue);
2016-01-14 19:57:21 +01:00
}
} else {
if (m_ledCount >= 240000U) {
m_ledCount = 0U;
m_ledValue = !m_ledValue;
2016-11-01 21:30:18 +01:00
setLEDInt(m_ledValue);
2016-01-14 19:57:21 +01:00
}
return;
}
#if defined(USE_COS_AS_LOCKOUT)
m_lockout = getCOSInt();
#endif
2016-01-14 19:57:21 +01:00
// Switch off the transmitter if needed
if (m_txBuffer.getData() == 0U && m_tx) {
m_tx = false;
setPTTInt(m_pttInvert ? true : false);
2016-01-14 19:57:21 +01:00
}
if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) {
2016-10-31 20:39:32 +01:00
q15_t samples[RX_BLOCK_SIZE + 1U];
uint8_t control[RX_BLOCK_SIZE + 1U];
uint16_t rssi[RX_BLOCK_SIZE + 1U];
2016-01-14 19:57:21 +01:00
uint8_t blockSize = RX_BLOCK_SIZE;
for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) {
2016-01-14 19:57:21 +01:00
uint16_t sample;
m_rxBuffer.get(sample, control[i]);
2016-10-31 20:39:32 +01:00
m_rssiBuffer.get(rssi[i]);
2016-01-14 19:57:21 +01:00
// Detect ADC overflow
if (m_detect && (sample == 0U || sample == 4095U))
2016-06-09 20:50:34 +02:00
m_adcOverflow++;
2016-01-14 19:57:21 +01:00
q15_t res1 = q15_t(sample) - DC_OFFSET;
q31_t res2 = res1 * m_rxLevel;
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) {
2016-03-23 18:46:21 +01:00
blockSize++;
samples[RX_BLOCK_SIZE] = 0;
for (int8_t i = RX_BLOCK_SIZE - 1; i >= 0; i--)
control[i + 1] = control[i];
} else {
2016-03-23 18:46:21 +01:00
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;
2016-01-14 19:57:21 +01:00
if (m_modemState == STATE_IDLE) {
if (m_dstarEnable) {
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
2016-01-14 19:57:21 +01:00
2017-01-05 19:59:15 +01:00
dstarRX.samples(GMSKVals, rssi, blockSize);
2016-01-14 19:57:21 +01:00
}
2016-10-03 12:20:35 +02:00
if (m_dmrEnable || m_ysfEnable || m_p25Enable) {
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
2016-01-14 19:57:21 +01:00
if (m_dmrEnable) {
if (m_duplex)
dmrIdleRX.samples(C4FSKVals, blockSize);
else
2016-10-31 20:39:32 +01:00
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
}
2016-01-14 19:57:21 +01:00
if (m_ysfEnable)
2017-01-05 19:59:15 +01:00
ysfRX.samples(C4FSKVals, rssi, blockSize);
2016-09-06 20:11:16 +02:00
if (m_p25Enable)
2017-01-05 19:59:15 +01:00
p25RX.samples(C4FSKVals, rssi, blockSize);
2016-01-14 19:57:21 +01:00
}
} 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);
2016-01-14 19:57:21 +01:00
2017-01-05 19:59:15 +01:00
dstarRX.samples(GMSKVals, rssi, blockSize);
2016-01-14 19:57:21 +01:00
}
} 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);
2016-01-14 19:57:21 +01:00
if (m_duplex) {
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
if (m_tx)
2016-10-31 20:39:32 +01:00
dmrRX.samples(C4FSKVals, rssi, control, blockSize);
else
dmrIdleRX.samples(C4FSKVals, blockSize);
} else {
2016-10-31 20:39:32 +01:00
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
}
2016-01-14 19:57:21 +01:00
}
} 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);
2016-01-14 19:57:21 +01:00
2017-01-05 19:59:15 +01:00
ysfRX.samples(C4FSKVals, rssi, blockSize);
2016-01-14 19:57:21 +01:00
}
2016-09-06 20:11:16 +02:00
} 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);
2017-01-05 19:59:15 +01:00
p25RX.samples(C4FSKVals, rssi, blockSize);
2016-09-06 20:11:16 +02:00
}
2016-04-14 18:42:38 +02:00
} else if (m_modemState == STATE_DSTARCAL) {
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
2016-01-14 19:57:21 +01:00
2016-04-14 18:42:38 +02:00
calDStarRX.samples(GMSKVals, blockSize);
2016-12-28 19:07:31 +01:00
} else if (m_modemState == STATE_RSSICAL) {
calRSSI.samples(rssi, blockSize);
2016-01-14 19:57:21 +01:00
}
}
}
2016-08-09 08:11:10 +02:00
void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t* control)
2016-01-14 19:57:21 +01:00
{
if (!m_started)
return;
if (m_lockout)
return;
2016-01-14 19:57:21 +01:00
// Switch the transmitter on if needed
if (!m_tx) {
m_tx = true;
setPTTInt(m_pttInvert ? false : true);
2016-01-14 19:57:21 +01:00
}
2016-08-09 08:11:10 +02:00
q15_t txLevel = 0;
switch (mode) {
2016-10-10 18:35:33 +02:00
case STATE_DSTAR:
txLevel = m_dstarTXLevel;
break;
2016-08-09 08:11:10 +02:00
case STATE_DMR:
txLevel = m_dmrTXLevel;
break;
case STATE_YSF:
txLevel = m_ysfTXLevel;
break;
2016-09-08 18:53:59 +02:00
case STATE_P25:
txLevel = m_p25TXLevel;
break;
2016-08-09 08:11:10 +02:00
default:
2016-10-10 18:35:33 +02:00
txLevel = m_cwIdTXLevel;
2016-08-09 08:11:10 +02:00
break;
}
2016-01-14 19:57:21 +01:00
for (uint16_t i = 0U; i < length; i++) {
2016-08-09 08:11:10 +02:00
q31_t res1 = samples[i] * txLevel;
2016-01-14 19:57:21 +01:00
q15_t res2 = q15_t(__SSAT((res1 >> 15), 16));
2016-06-09 20:50:34 +02:00
uint16_t res3 = uint16_t(res2 + DC_OFFSET);
// Detect DAC overflow
2016-06-13 20:31:16 +02:00
if (res3 > 4095U)
2016-06-09 20:50:34 +02:00
m_dacOverflow++;
2016-01-14 19:57:21 +01:00
if (control == NULL)
2016-06-09 20:50:34 +02:00
m_txBuffer.put(res3, MARK_NONE);
2016-01-14 19:57:21 +01:00
else
2016-06-09 20:50:34 +02:00
m_txBuffer.put(res3, control[i]);
2016-01-14 19:57:21 +01:00
}
}
uint16_t CIO::getSpace() const
{
return m_txBuffer.getSpace();
}
void CIO::setDecode(bool dcd)
{
if (dcd != m_dcd)
setCOSInt(dcd ? true : false);
2016-01-14 19:57:21 +01:00
m_dcd = dcd;
}
void CIO::setADCDetection(bool detect)
{
m_detect = detect;
}
void CIO::setMode()
{
#if defined(ARDUINO_MODE_PINS)
2016-11-03 21:33:22 +01:00
setDStarInt(m_modemState == STATE_DSTAR);
setDMRInt(m_modemState == STATE_DMR);
setYSFInt(m_modemState == STATE_YSF);
setP25Int(m_modemState == STATE_P25);
#endif
}
2016-10-10 18:35:33 +02:00
void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel)
2016-01-14 19:57:21 +01:00
{
m_pttInvert = pttInvert;
2016-08-09 08:11:10 +02:00
m_rxLevel = q15_t(rxLevel * 128);
2016-10-10 18:35:33 +02:00
m_cwIdTXLevel = q15_t(cwIdTXLevel * 128);
2016-08-09 08:11:10 +02:00
m_dstarTXLevel = q15_t(dstarTXLevel * 128);
m_dmrTXLevel = q15_t(dmrTXLevel * 128);
m_ysfTXLevel = q15_t(ysfTXLevel * 128);
2016-09-08 18:53:59 +02:00
m_p25TXLevel = q15_t(p25TXLevel * 128);
2016-01-14 19:57:21 +01:00
if (rxInvert)
m_rxLevel = -m_rxLevel;
2016-08-09 08:11:10 +02:00
if (txInvert) {
m_dstarTXLevel = -m_dstarTXLevel;
m_dmrTXLevel = -m_dmrTXLevel;
m_ysfTXLevel = -m_ysfTXLevel;
2016-09-08 18:53:59 +02:00
m_p25TXLevel = -m_p25TXLevel;
2016-08-09 08:11:10 +02:00
}
2016-01-14 19:57:21 +01:00
}
2016-06-09 20:50:34 +02:00
void CIO::getOverflow(bool& adcOverflow, bool& dacOverflow)
2016-01-14 19:57:21 +01:00
{
2016-06-09 20:50:34 +02:00
adcOverflow = m_adcOverflow > 0U;
dacOverflow = m_dacOverflow > 0U;
2016-01-14 19:57:21 +01:00
#if defined(WANT_DEBUG)
2016-06-09 20:50:34 +02:00
if (m_adcOverflow > 0U || m_dacOverflow > 0U)
DEBUG3("IO: adc/dac", m_adcOverflow, m_dacOverflow);
2016-01-14 19:57:21 +01:00
#endif
2016-06-09 20:50:34 +02:00
m_adcOverflow = 0U;
m_dacOverflow = 0U;
2016-01-14 19:57:21 +01:00
}
bool CIO::hasTXOverflow()
{
return m_txBuffer.hasOverflowed();
}
bool CIO::hasRXOverflow()
{
return m_rxBuffer.hasOverflowed();
}
2016-02-02 21:23:37 +01:00
void CIO::resetWatchdog()
{
m_watchdog = 0U;
}
bool CIO::hasLockout() const
{
return m_lockout;
}