From 20a0a875f3bbd59643f534ee0e790dec35d0ccee Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 6 Jun 2018 20:27:46 +0100 Subject: [PATCH] Add the POCSAG transmitter. --- Globals.h | 5 ++ IO.cpp | 64 ++++++++++++++++++------ IO.h | 4 +- IODue.cpp | 4 ++ IOSTM.cpp | 4 ++ IOSTM_CMSIS.cpp | 4 ++ IOTeensy.cpp | 4 ++ MMDVM.cpp | 16 ++++-- MMDVM.ino | 16 ++++-- POCSAGTX.cpp | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ POCSAGTX.h | 49 ++++++++++++++++++ SerialPort.cpp | 81 +++++++++++++++++++++++------- 12 files changed, 335 insertions(+), 44 deletions(-) create mode 100644 POCSAGTX.cpp create mode 100644 POCSAGTX.h diff --git a/Globals.h b/Globals.h index 9ab7d31..d68e2c5 100644 --- a/Globals.h +++ b/Globals.h @@ -49,6 +49,7 @@ enum MMDVM_STATE { STATE_YSF = 3, STATE_P25 = 4, STATE_NXDN = 5, + STATE_POCSAG = 6, // Dummy states start at 90 STATE_NXDNCAL1K = 91, @@ -76,6 +77,7 @@ enum MMDVM_STATE { #include "P25TX.h" #include "NXDNRX.h" #include "NXDNTX.h" +#include "POCSAGTX.h" #include "CalDStarRX.h" #include "CalDStarTX.h" #include "CalDMR.h" @@ -102,6 +104,7 @@ extern bool m_dmrEnable; extern bool m_ysfEnable; extern bool m_p25Enable; extern bool m_nxdnEnable; +extern bool m_pocsagEnable; extern bool m_duplex; @@ -130,6 +133,8 @@ extern CP25TX p25TX; extern CNXDNRX nxdnRX; extern CNXDNTX nxdnTX; +extern CPOCSAGTX pocsagTX; + extern CCalDStarRX calDStarRX; extern CCalDStarTX calDStarTX; extern CCalDMR calDMR; diff --git a/IO.cpp b/IO.cpp index 1260817..96b0128 100644 --- a/IO.cpp +++ b/IO.cpp @@ -79,6 +79,7 @@ m_dmrTXLevel(128 * 128), m_ysfTXLevel(128 * 128), m_p25TXLevel(128 * 128), m_nxdnTXLevel(128 * 128), +m_pocsagTXLevel(128 * 128), m_rxDCOffset(DC_OFFSET), m_txDCOffset(DC_OFFSET), m_ledCount(0U), @@ -142,6 +143,7 @@ void CIO::selfTest() setYSFInt(ledValue); setP25Int(ledValue); setNXDNInt(ledValue); + setPOCSAGInt(ledValue); #endif delayInt(250); } @@ -152,6 +154,7 @@ void CIO::selfTest() setYSFInt(false); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -160,6 +163,7 @@ void CIO::selfTest() setYSFInt(false); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -168,6 +172,7 @@ void CIO::selfTest() setYSFInt(true); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -176,6 +181,7 @@ void CIO::selfTest() setYSFInt(true); setP25Int(true); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -184,6 +190,25 @@ void CIO::selfTest() setYSFInt(true); setP25Int(true); setNXDNInt(true); + setPOCSAGInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(true); + setPOCSAGInt(true); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(true); + setPOCSAGInt(false); delayInt(250); @@ -192,6 +217,7 @@ void CIO::selfTest() setYSFInt(true); setP25Int(true); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -200,6 +226,7 @@ void CIO::selfTest() setYSFInt(true); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -208,6 +235,7 @@ void CIO::selfTest() setYSFInt(false); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -216,6 +244,7 @@ void CIO::selfTest() setYSFInt(false); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); delayInt(250); @@ -224,6 +253,7 @@ void CIO::selfTest() setYSFInt(false); setP25Int(false); setNXDNInt(false); + setPOCSAGInt(false); #endif } @@ -245,7 +275,7 @@ void CIO::process() if (m_started) { // Two seconds timeout if (m_watchdog >= 48000U) { - if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN) { + if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN || m_modemState == STATE_POCSAG) { if (m_modemState == STATE_DMR && m_tx) dmrTX.setStart(false); m_modemState = STATE_IDLE; @@ -472,6 +502,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t case STATE_NXDN: txLevel = m_nxdnTXLevel; break; + case STATE_POCSAG: + txLevel = m_pocsagTXLevel; + break; default: txLevel = m_cwIdTXLevel; break; @@ -519,20 +552,22 @@ void CIO::setMode() setYSFInt(m_modemState == STATE_YSF); setP25Int(m_modemState == STATE_P25); setNXDNInt(m_modemState == STATE_NXDN); + setPOCSAGInt(m_modemState == STATE_POCSAG); #endif } -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, uint8_t nxdnTXLevel, int16_t txDCOffset, int16_t rxDCOffset) +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, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, int16_t txDCOffset, int16_t rxDCOffset) { m_pttInvert = pttInvert; - m_rxLevel = q15_t(rxLevel * 128); - m_cwIdTXLevel = q15_t(cwIdTXLevel * 128); - m_dstarTXLevel = q15_t(dstarTXLevel * 128); - m_dmrTXLevel = q15_t(dmrTXLevel * 128); - m_ysfTXLevel = q15_t(ysfTXLevel * 128); - m_p25TXLevel = q15_t(p25TXLevel * 128); - m_nxdnTXLevel = q15_t(nxdnTXLevel * 128); + m_rxLevel = q15_t(rxLevel * 128); + m_cwIdTXLevel = q15_t(cwIdTXLevel * 128); + m_dstarTXLevel = q15_t(dstarTXLevel * 128); + m_dmrTXLevel = q15_t(dmrTXLevel * 128); + m_ysfTXLevel = q15_t(ysfTXLevel * 128); + m_p25TXLevel = q15_t(p25TXLevel * 128); + m_nxdnTXLevel = q15_t(nxdnTXLevel * 128); + m_pocsagTXLevel = q15_t(pocsagTXLevel * 128); m_rxDCOffset = DC_OFFSET + rxDCOffset; m_txDCOffset = DC_OFFSET + txDCOffset; @@ -541,11 +576,12 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_rxLevel = -m_rxLevel; if (txInvert) { - m_dstarTXLevel = -m_dstarTXLevel; - m_dmrTXLevel = -m_dmrTXLevel; - m_ysfTXLevel = -m_ysfTXLevel; - m_p25TXLevel = -m_p25TXLevel; - m_nxdnTXLevel = -m_nxdnTXLevel; + m_dstarTXLevel = -m_dstarTXLevel; + m_dmrTXLevel = -m_dmrTXLevel; + m_ysfTXLevel = -m_ysfTXLevel; + m_p25TXLevel = -m_p25TXLevel; + m_nxdnTXLevel = -m_nxdnTXLevel; + m_pocsagTXLevel = -m_pocsagTXLevel; } } diff --git a/IO.h b/IO.h index 79a2b69..4a7df28 100644 --- a/IO.h +++ b/IO.h @@ -42,7 +42,7 @@ public: void interrupt(); - void 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, uint8_t nxdnLevel, int16_t txDCOffset, int16_t rxDCOffset); + void 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, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, int16_t txDCOffset, int16_t rxDCOffset); void getOverflow(bool& adcOverflow, bool& dacOverflow); @@ -85,6 +85,7 @@ private: q15_t m_ysfTXLevel; q15_t m_p25TXLevel; q15_t m_nxdnTXLevel; + q15_t m_pocsagTXLevel; uint16_t m_rxDCOffset; uint16_t m_txDCOffset; @@ -116,6 +117,7 @@ private: void setYSFInt(bool on); void setP25Int(bool on); void setNXDNInt(bool on); + void setPOCSAGInt(bool on); void delayInt(unsigned int dly); }; diff --git a/IODue.cpp b/IODue.cpp index 7e86ae5..e5fa88b 100644 --- a/IODue.cpp +++ b/IODue.cpp @@ -235,6 +235,10 @@ void CIO::setNXDNInt(bool on) digitalWrite(PIN_NXDN, on ? HIGH : LOW); } +void CIO::setPOCSAGInt(bool on) +{ +} + void CIO::delayInt(unsigned int dly) { delay(dly); diff --git a/IOSTM.cpp b/IOSTM.cpp index 6647ab0..d8d84cc 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -1121,6 +1121,10 @@ void CIO::setNXDNInt(bool on) #endif } +void CIO::setPOCSAGInt(bool on) +{ +} + // Simple delay function for STM32 // Example from: http://thehackerworkshop.com/?p=1209 void CIO::delayInt(unsigned int dly) diff --git a/IOSTM_CMSIS.cpp b/IOSTM_CMSIS.cpp index 997c92b..7bded9c 100644 --- a/IOSTM_CMSIS.cpp +++ b/IOSTM_CMSIS.cpp @@ -434,6 +434,10 @@ void CIO::setNXDNInt(bool on) BB_NXDN = !!on; } +void CIO::setPOCSAGInt(bool on) +{ +} + void CIO::delayInt(unsigned int dly) { delay(dly); diff --git a/IOTeensy.cpp b/IOTeensy.cpp index 2440230..b1091c0 100644 --- a/IOTeensy.cpp +++ b/IOTeensy.cpp @@ -221,6 +221,10 @@ void CIO::setNXDNInt(bool on) digitalWrite(PIN_NXDN, on ? HIGH : LOW); } +void CIO::setPOCSAGInt(bool on) +{ +} + void CIO::delayInt(unsigned int dly) { delay(dly); diff --git a/MMDVM.cpp b/MMDVM.cpp index d4a12a9..639a068 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -26,11 +26,12 @@ // Global variables MMDVM_STATE m_modemState = STATE_IDLE; -bool m_dstarEnable = true; -bool m_dmrEnable = true; -bool m_ysfEnable = true; -bool m_p25Enable = true; -bool m_nxdnEnable = true; +bool m_dstarEnable = true; +bool m_dmrEnable = true; +bool m_ysfEnable = true; +bool m_p25Enable = true; +bool m_nxdnEnable = true; +bool m_pocsagEnable = true; bool m_duplex = true; @@ -56,6 +57,8 @@ CP25TX p25TX; CNXDNRX nxdnRX; CNXDNTX nxdnTX; +CPOCSAGTX pocsagTX; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; @@ -99,6 +102,9 @@ void loop() if (m_nxdnEnable && m_modemState == STATE_NXDN) nxdnTX.process(); + if (m_pocsagEnable && m_modemState == STATE_POCSAG) + pocsagTX.process(); + if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); diff --git a/MMDVM.ino b/MMDVM.ino index 0b0f852..ff317eb 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -23,11 +23,12 @@ // Global variables MMDVM_STATE m_modemState = STATE_IDLE; -bool m_dstarEnable = true; -bool m_dmrEnable = true; -bool m_ysfEnable = true; -bool m_p25Enable = true; -bool m_nxdnEnable = true; +bool m_dstarEnable = true; +bool m_dmrEnable = true; +bool m_ysfEnable = true; +bool m_p25Enable = true; +bool m_nxdnEnable = true; +bool m_pocsagEnable = true; bool m_duplex = true; @@ -53,6 +54,8 @@ CP25TX p25TX; CNXDNRX nxdnRX; CNXDNTX nxdnTX; +CPOCSAGTX pocsagTX; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; @@ -96,6 +99,9 @@ void loop() if (m_nxdnEnable && m_modemState == STATE_NXDN) nxdnTX.process(); + if (m_pocsagEnable && m_modemState == STATE_POCSAG) + pocsagTX.process(); + if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); diff --git a/POCSAGTX.cpp b/POCSAGTX.cpp new file mode 100644 index 0000000..0f243d1 --- /dev/null +++ b/POCSAGTX.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009-2018 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 + * 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. + */ + +#include "Config.h" +#include "Globals.h" +#include "POCSAGTX.h" + +const uint16_t POCSAG_PREAMBLE_LENGTH_BYTES = 576U / 8U; + +const uint16_t POCSAG_FRAME_LENGTH_BYTES = 17U * sizeof(uint32_t); + +const uint16_t POCSAG_RADIO_SYMBOL_LENGTH = 20U; + +const q15_t POCSAG_LEVEL0[] = { 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155, 3155}; +const q15_t POCSAG_LEVEL1[] = {-3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155, -3155}; + +const uint8_t POCSAG_SYNC = 0xAAU; + +CPOCSAGTX::CPOCSAGTX() : +m_buffer(4000U), +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_txDelay(POCSAG_PREAMBLE_LENGTH_BYTES) +{ +} + +void CPOCSAGTX::process() +{ + if (m_buffer.getData() == 0U && m_poLen == 0U) + return; + + if (m_poLen == 0U) { + if (!m_tx) { + for (uint16_t i = 0U; i < m_txDelay; i++) + m_poBuffer[m_poLen++] = POCSAG_SYNC; + } else { + for (uint8_t i = 0U; i < POCSAG_FRAME_LENGTH_BYTES; i++) { + uint8_t c = m_buffer.get(); + m_poBuffer[m_poLen++] = c; + } + } + + m_poPtr = 0U; + } + + if (m_poLen > 0U) { + uint16_t space = io.getSpace(); + + while (space > (8U * POCSAG_RADIO_SYMBOL_LENGTH)) { + uint8_t c = m_poBuffer[m_poPtr++]; + writeByte(c); + + space -= 8U * POCSAG_RADIO_SYMBOL_LENGTH; + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + return; + } + } + } +} + +uint8_t CPOCSAGTX::writeData(const uint8_t* data, uint8_t length) +{ + if (length != POCSAG_FRAME_LENGTH_BYTES) + return 4U; + + uint16_t space = m_buffer.getSpace(); + if (space < POCSAG_FRAME_LENGTH_BYTES) + return 5U; + + for (uint8_t i = 0U; i < POCSAG_FRAME_LENGTH_BYTES; i++) + m_buffer.put(data[i]); + + return 0U; +} + +void CPOCSAGTX::writeByte(uint8_t c) +{ + q15_t buffer[POCSAG_RADIO_SYMBOL_LENGTH * 8U]; + + const uint8_t MASK = 0x80U; + + uint8_t n = 0U; + for (uint8_t i = 0U; i < 8U; i++, c <<= 1, n += POCSAG_RADIO_SYMBOL_LENGTH) { + switch (c & MASK) { + case 0x80U: + ::memcpy(buffer + n, POCSAG_LEVEL1, POCSAG_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); + break; + default: + ::memcpy(buffer + n, POCSAG_LEVEL0, POCSAG_RADIO_SYMBOL_LENGTH * sizeof(q15_t)); + break; + } + } + + io.write(STATE_POCSAG, buffer, POCSAG_RADIO_SYMBOL_LENGTH * 8U); +} + +void CPOCSAGTX::setTXDelay(uint8_t delay) +{ + m_txDelay = POCSAG_PREAMBLE_LENGTH_BYTES + uint16_t(delay); + + if (m_txDelay > 1200U) + m_txDelay = 1200U; +} + +uint8_t CPOCSAGTX::getSpace() const +{ + return m_buffer.getSpace() / POCSAG_FRAME_LENGTH_BYTES; +} + diff --git a/POCSAGTX.h b/POCSAGTX.h new file mode 100644 index 0000000..a2f487b --- /dev/null +++ b/POCSAGTX.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015,2016,2017,2018 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 + * 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. + */ + +#if !defined(POCSAGTX_H) +#define POCSAGTX_H + +#include "Config.h" + +#include "SerialRB.h" + +class CPOCSAGTX { +public: + CPOCSAGTX(); + + uint8_t writeData(const uint8_t* data, uint8_t length); + + void process(); + + void setTXDelay(uint8_t delay); + + uint8_t getSpace() const; + +private: + CSerialRB m_buffer; + uint8_t m_poBuffer[1200U]; + uint16_t m_poLen; + uint16_t m_poPtr; + uint16_t m_txDelay; + + void writeByte(uint8_t c); +}; + +#endif + diff --git a/SerialPort.cpp b/SerialPort.cpp index 564df2d..b879cca 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -63,6 +63,8 @@ const uint8_t MMDVM_P25_LOST = 0x32U; const uint8_t MMDVM_NXDN_DATA = 0x40U; const uint8_t MMDVM_NXDN_LOST = 0x41U; +const uint8_t MMDVM_POCSAG_DATA = 0x50U; + const uint8_t MMDVM_ACK = 0x70U; const uint8_t MMDVM_NAK = 0x7FU; @@ -86,7 +88,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define TCXO "19.2000" #endif -#define DESCRIPTION "MMDVM 20180327 (D-Star/DMR/System Fusion/P25/NXDN)" +#define DESCRIPTION "MMDVM 20180327 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG)" #if defined(GITVERSION) #define concat(a, b, c) a " " b "MHz GitID #" c "" @@ -155,6 +157,8 @@ void CSerialPort::getStatus() reply[3U] |= 0x08U; if (m_nxdnEnable) reply[3U] |= 0x10U; + if (m_pocsagEnable) + reply[3U] |= 0x20U; reply[4U] = uint8_t(m_modemState); @@ -214,7 +218,12 @@ void CSerialPort::getStatus() else reply[11U] = 0U; - writeInt(1U, reply, 12); + if (m_pocsagEnable) + reply[12U] = pocsagTX.getSpace(); + else + reply[12U] = 0U; + + writeInt(1U, reply, 13); } void CSerialPort::getVersion() @@ -238,7 +247,7 @@ void CSerialPort::getVersion() uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) { - if (length < 17U) + if (length < 18U) return 4U; bool rxInvert = (data[0U] & 0x01U) == 0x01U; @@ -249,11 +258,12 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) m_debug = (data[0U] & 0x10U) == 0x10U; - bool dstarEnable = (data[1U] & 0x01U) == 0x01U; - bool dmrEnable = (data[1U] & 0x02U) == 0x02U; - bool ysfEnable = (data[1U] & 0x04U) == 0x04U; - bool p25Enable = (data[1U] & 0x08U) == 0x08U; - bool nxdnEnable = (data[1U] & 0x10U) == 0x10U; + bool dstarEnable = (data[1U] & 0x01U) == 0x01U; + bool dmrEnable = (data[1U] & 0x02U) == 0x02U; + bool ysfEnable = (data[1U] & 0x04U) == 0x04U; + bool p25Enable = (data[1U] & 0x08U) == 0x08U; + bool nxdnEnable = (data[1U] & 0x10U) == 0x10U; + bool pocsagEnable = (data[1U] & 0x20U) == 0x20U; uint8_t txDelay = data[2U]; if (txDelay > 50U) @@ -261,7 +271,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) MMDVM_STATE modemState = MMDVM_STATE(data[3U]); - if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -273,6 +283,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) return 4U; if (modemState == STATE_NXDN && !nxdnEnable) return 4U; + if (modemState == STATE_POCSAG && !pocsagEnable) + return 4U; uint8_t rxLevel = data[4U]; @@ -291,24 +303,28 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) int16_t txDCOffset = int16_t(data[13U]) - 128; int16_t rxDCOffset = int16_t(data[14U]) - 128; - uint8_t nxdnTXLevel = data[15U]; + uint8_t nxdnTXLevel = data[15U]; - uint8_t ysfTXHang = data[16U]; + uint8_t ysfTXHang = data[16U]; + + uint8_t pocsagTXLevel = data[17U]; m_modemState = modemState; - m_dstarEnable = dstarEnable; - m_dmrEnable = dmrEnable; - m_ysfEnable = ysfEnable; - m_p25Enable = p25Enable; - m_nxdnEnable = nxdnEnable; - m_duplex = !simplex; + m_dstarEnable = dstarEnable; + m_dmrEnable = dmrEnable; + m_ysfEnable = ysfEnable; + m_p25Enable = p25Enable; + m_nxdnEnable = nxdnEnable; + m_pocsagEnable = pocsagEnable; + m_duplex = !simplex; dstarTX.setTXDelay(txDelay); ysfTX.setTXDelay(txDelay); p25TX.setTXDelay(txDelay); dmrDMOTX.setTXDelay(txDelay); nxdnTX.setTXDelay(txDelay); + pocsagTX.setTXDelay(txDelay); dmrTX.setColorCode(colorCode); dmrRX.setColorCode(colorCode); @@ -318,7 +334,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) ysfTX.setParams(ysfLoDev, ysfTXHang); - io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, txDCOffset, rxDCOffset); + io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, txDCOffset, rxDCOffset); io.start(); @@ -335,7 +351,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) if (modemState == m_modemState) return 0U; - if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -347,6 +363,8 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) return 4U; if (modemState == STATE_NXDN && !m_nxdnEnable) return 4U; + if (modemState == STATE_POCSAG && !m_pocsagEnable) + return 4U; setMode(modemState); @@ -404,6 +422,17 @@ void CSerialPort::setMode(MMDVM_STATE modemState) p25RX.reset(); cwIdTX.reset(); break; + case STATE_POCSAG: + DEBUG1("Mode set to POCSAG"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; case STATE_DSTARCAL: DEBUG1("Mode set to D-Star Calibrate"); dmrIdleRX.reset(); @@ -768,6 +797,20 @@ void CSerialPort::process() } break; + case MMDVM_POCSAG_DATA: + if (m_pocsagEnable) { + if (m_modemState == STATE_IDLE || m_modemState == STATE_POCSAG) + err = pocsagTX.writeData(m_buffer + 3U, m_len - 3U); + } + if (err == 0U) { + if (m_modemState == STATE_IDLE) + setMode(STATE_POCSAG); + } else { + DEBUG2("Received invalid POCSAG data", err); + sendNAK(err); + } + break; + case MMDVM_TRANSPARENT: // Do nothing on the MMDVM. break;