From 121e909367554f1b894d290522cfb70230c4063e Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Sun, 17 Jun 2018 20:10:00 -0400 Subject: [PATCH] Add POCSAG support (TX) --- ADF7021.cpp | 37 +++++++++++++-- ADF7021.h | 12 +++++ DMRDMOTX.cpp | 1 - DMRDMOTX.h | 1 - DStarTX.cpp | 6 +-- DStarTX.h | 1 - Globals.h | 7 +++ IO.cpp | 14 ++++-- IO.h | 4 +- IOArduino.cpp | 5 ++ IOSTM.cpp | 5 ++ MMDVM_HS.cpp | 18 ++++++-- MMDVM_HS.ino | 18 ++++++-- NXDNTX.cpp | 2 - NXDNTX.h | 1 - P25TX.cpp | 3 +- P25TX.h | 1 - POCSAGDefines.h | 27 +++++++++++ POCSAGTX.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ POCSAGTX.h | 46 +++++++++++++++++++ SerialPort.cpp | 92 ++++++++++++++++++++++++++++--------- YSFTX.cpp | 3 +- YSFTX.h | 1 - 23 files changed, 367 insertions(+), 56 deletions(-) create mode 100644 POCSAGDefines.h create mode 100644 POCSAGTX.cpp create mode 100644 POCSAGTX.h diff --git a/ADF7021.cpp b/ADF7021.cpp index d100da0..2fa56fe 100644 --- a/ADF7021.cpp +++ b/ADF7021.cpp @@ -48,6 +48,7 @@ uint16_t m_dmrDev; uint16_t m_ysfDev; uint16_t m_p25Dev; uint16_t m_nxdnDev; +uint16_t m_pocsagDev; static void Send_AD7021_control_shift() { @@ -186,7 +187,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) uint32_t ADF7021_REG13 = 0U; int32_t AFC_OFFSET = 0; - if(modemState != STATE_CWID) + if(modemState != STATE_CWID && modemState != STATE_POCSAG) m_modemState_prev = modemState; // Toggle CE pin for ADF7021 reset @@ -226,6 +227,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) switch (modemState) { case STATE_DSTAR: + case STATE_POCSAG: AFC_OFFSET = 0; break; case STATE_DMR: @@ -314,6 +316,28 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) ADF7021_REG2 |= (uint32_t) 0b111 << 4; // modulation (RC 4FSK) break; + case STATE_POCSAG: + // Dev: 4500 Hz, symb rate = 1200 + + ADF7021_REG3 = ADF7021_REG3_POCSAG; + ADF7021_REG10 = ADF7021_REG10_POCSAG; + + ADF7021_REG4 = (uint32_t) 0b0100 << 0; // register 4 + ADF7021_REG4 |= (uint32_t) 0b001 << 4; // demod mode, 2FSK + ADF7021_REG4 |= (uint32_t) 0b1 << 7; + ADF7021_REG4 |= (uint32_t) 0b10 << 8; + ADF7021_REG4 |= (uint32_t) ADF7021_DISC_BW_POCSAG << 10; // Disc BW + ADF7021_REG4 |= (uint32_t) ADF7021_POST_BW_POCSAG << 20; // Post dem BW + ADF7021_REG4 |= (uint32_t) 0b10 << 30; // IF filter (25 kHz) + + // Register 13 not used with 2FSK + ADF7021_REG13 = (uint32_t) 0b1101 << 0; // register 13 + + ADF7021_REG2 = (uint32_t) 0b00 << 28; // clock normal + ADF7021_REG2 |= (uint32_t) (ADF7021_DEV_POCSAG / div2)<< 19; // deviation + ADF7021_REG2 |= (uint32_t) 0b000 << 4; // modulation (2FSK) + break; + case STATE_DSTAR: // Dev: 1200 Hz, symb rate = 4800 @@ -523,7 +547,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) Send_AD7021_control(); #if defined(DUPLEX) -if(m_duplex && (modemState != STATE_CWID)) +if(m_duplex && (modemState != STATE_CWID && modemState != STATE_POCSAG)) ifConf2(modemState); #endif } @@ -888,7 +912,7 @@ void CIO::setPower(uint8_t power) m_power = power >> 2; } -void CIO::setDeviations(uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, bool ysfLoDev) +void CIO::setDeviations(uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, bool ysfLoDev) { m_dstarDev = uint16_t((ADF7021_DEV_DSTAR * uint16_t(dstarTXLevel)) / 128U); m_dmrDev = uint16_t((ADF7021_DEV_DMR * uint16_t(dmrTXLevel)) / 128U); @@ -900,6 +924,7 @@ void CIO::setDeviations(uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXL m_p25Dev = uint16_t((ADF7021_DEV_P25 * uint16_t(p25TXLevel)) / 128U); m_nxdnDev = uint16_t((ADF7021_DEV_NXDN * uint16_t(nxdnTXLevel)) / 128U); + m_pocsagDev = uint16_t((ADF7021_DEV_POCSAG * uint16_t(pocsagTXLevel)) / 128U); } void CIO::updateCal() @@ -1011,6 +1036,11 @@ uint16_t CIO::devNXDN() return (uint16_t)((ADF7021_PFD * m_nxdnDev) / (f_div * 65536)); } +uint16_t CIO::devPOCSAG() +{ + return (uint16_t)((ADF7021_PFD * m_pocsagDev) / (f_div * 65536)); +} + void CIO::printConf() { DEBUG1("MMDVM_HS FW configuration:"); @@ -1022,6 +1052,7 @@ void CIO::printConf() DEBUG2("YSF +1 sym dev (Hz):", devYSF()); DEBUG2("P25 +1 sym dev (Hz):", devP25()); DEBUG2("NXDN +1 sym dev (Hz):", devNXDN()); + DEBUG2("POCSAG dev (Hz):", devPOCSAG()); } #endif diff --git a/ADF7021.h b/ADF7021.h index 3fcecb9..e56bf6f 100644 --- a/ADF7021.h +++ b/ADF7021.h @@ -67,6 +67,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf // DEMOD_CLK = 4.9152 MHz (DMR, YSF_L, P25) // DEMOD_CLK = 7.3728 MHz (YSF_H) // DEMOD CLK = 3.6864 MHz (NXDN) +// DEMOD_CLK = 7.3728 MHz (POCSAG) #define ADF7021_PFD 3686400.0 // PLL (REG 01) @@ -86,6 +87,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_DEV_P25 22U #endif #define ADF7021_DEV_NXDN 13U +#define ADF7021_DEV_POCSAG 160U // TX/RX CLOCK register (REG 03) #define ADF7021_REG3_DSTAR 0x2A4C4193 @@ -102,6 +104,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_REG3_P25 0x2A4C80D3 #define ADF7021_REG3_NXDN 0x2A4CC113 #endif +#define ADF7021_REG3_POCSAG 0x2A4F0093 // Discriminator bandwith, demodulator (REG 04) // Bug in ADI evaluation software, use datasheet formula (4FSK) @@ -111,6 +114,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_DISC_BW_YSF_H 516U // K=28 #define ADF7021_DISC_BW_P25 394U // K=32 #define ADF7021_DISC_BW_NXDN 295U // K=32 +#define ADF7021_DISC_BW_POCSAG 406U // K=22 // Post demodulator bandwith (REG 04) #define ADF7021_POST_BW_DSTAR 10U @@ -118,6 +122,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_POST_BW_YSF 20U #define ADF7021_POST_BW_P25 6U #define ADF7021_POST_BW_NXDN 7U +#define ADF7021_POST_BW_POCSAG 1U // IF filter (REG 05) #define ADF7021_REG5 0x000024F5 @@ -127,6 +132,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf // AFC configuration (REG 10) #define ADF7021_REG10_DSTAR 0x0C96473A +#define ADF7021_REG10_POCSAG 0x1496473A #if defined(ADF7021_ENABLE_4FSK_AFC) #define ADF7021_REG10_DMR 0x01FE473A @@ -162,6 +168,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf // DEMOD_CLK = 2.4576 MHz (DSTAR) // DEMOD_CLK = 6.1440 MHz (DMR, YSF_H, YSF_L, P25) // DEMOD_CLK = 3.0720 MHz (NXDN) +// DEMOD_CLK = 6.1440 MHz (POCSAG) #define ADF7021_PFD 6144000.0 // PLL (REG 01) @@ -181,6 +188,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_DEV_P25 13U #endif #define ADF7021_DEV_NXDN 8U +#define ADF7021_DEV_POCSAG 96U // TX/RX CLOCK register (REG 03) #define ADF7021_REG3_DSTAR 0x29EC4153 @@ -197,6 +205,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_REG3_P25 0x29ECA093 #define ADF7021_REG3_NXDN 0x29ECA113 #endif +#define ADF7021_REG3_POCSAG 0x29EE8093 // Discriminator bandwith, demodulator (REG 04) // Bug in ADI evaluation software, use datasheet formula (4FSK) @@ -206,6 +215,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_DISC_BW_YSF_H 430U // K=28 #define ADF7021_DISC_BW_P25 493U // K=32 #define ADF7021_DISC_BW_NXDN 246U // K=32 +#define ADF7021_DISC_BW_POCSAG 338U // K=22 // Post demodulator bandwith (REG 04) #define ADF7021_POST_BW_DSTAR 10U @@ -213,6 +223,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf #define ADF7021_POST_BW_YSF 20U #define ADF7021_POST_BW_P25 6U #define ADF7021_POST_BW_NXDN 8U +#define ADF7021_POST_BW_POCSAG 1U // IF filter (REG 05) #define ADF7021_REG5 0x00001ED5 @@ -222,6 +233,7 @@ www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf // AFC (REG 10) #define ADF7021_REG10_DSTAR 0x0C96557A +#define ADF7021_REG10_POCSAG 0x1496557A #if defined(ADF7021_ENABLE_4FSK_AFC) #define ADF7021_REG10_DMR 0x01FE557A diff --git a/DMRDMOTX.cpp b/DMRDMOTX.cpp index 75bda46..bfc02c0 100644 --- a/DMRDMOTX.cpp +++ b/DMRDMOTX.cpp @@ -36,7 +36,6 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(240U), // 200ms -m_count(0U), m_delay(false), m_cal(false) { diff --git a/DMRDMOTX.h b/DMRDMOTX.h index 010ba99..1e3e4f3 100644 --- a/DMRDMOTX.h +++ b/DMRDMOTX.h @@ -45,7 +45,6 @@ class CDMRDMOTX { uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; - uint32_t m_count; bool m_delay; bool m_cal; diff --git a/DStarTX.cpp b/DStarTX.cpp index 25e0416..ba873c3 100644 --- a/DStarTX.cpp +++ b/DStarTX.cpp @@ -186,9 +186,8 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(60U), // 100ms -m_count(0U) +m_delay(false) { - } void CDStarTX::process() @@ -201,7 +200,6 @@ void CDStarTX::process() if (type == DSTAR_HEADER && m_poLen == 0U) { if (!m_tx) { m_delay = true; - m_count = 0U; m_poLen = m_txDelay; } else { m_delay = false; @@ -228,8 +226,6 @@ void CDStarTX::process() if (type == DSTAR_DATA && m_poLen == 0U) { m_delay = false; - if (!m_tx) - m_count = 0U; // Pop the type byte off m_buffer.get(); diff --git a/DStarTX.h b/DStarTX.h index c3f0308..5b3fdae 100644 --- a/DStarTX.h +++ b/DStarTX.h @@ -42,7 +42,6 @@ class CDStarTX { uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; // In bytes - uint32_t m_count; bool m_delay; void txHeader(const uint8_t* in, uint8_t* out) const; diff --git a/Globals.h b/Globals.h index 76569a4..a6bcd37 100644 --- a/Globals.h +++ b/Globals.h @@ -40,6 +40,7 @@ enum MMDVM_STATE { STATE_YSF = 3, STATE_P25 = 4, STATE_NXDN = 5, + STATE_POCSAG = 6, // Dummy states start at 90 STATE_DMRDMO1K = 92, @@ -75,6 +76,7 @@ const uint8_t MARK_NONE = 0x00U; #include "P25TX.h" #include "NXDNRX.h" #include "NXDNTX.h" +#include "POCSAGTX.h" #include "CWIdTX.h" #include "CalRSSI.h" #include "CalDMR.h" @@ -89,6 +91,8 @@ extern MMDVM_STATE m_calState; extern MMDVM_STATE m_modemState_prev; extern bool m_cwid_state; +extern bool m_pocsag_state; + extern uint8_t m_cwIdTXLevel; extern uint32_t m_modeTimerCnt; @@ -98,6 +102,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 +135,8 @@ extern CP25TX p25TX; extern CNXDNRX nxdnRX; extern CNXDNTX nxdnTX; +extern CPOCSAGTX pocsagTX; + extern CCalDMR calDMR; #if defined(SEND_RSSI_DATA) diff --git a/IO.cpp b/IO.cpp index b0e5369..8c33119 100644 --- a/IO.cpp +++ b/IO.cpp @@ -48,6 +48,7 @@ m_watchdog(0U) YSF_pin(LOW); P25_pin(LOW); NXDN_pin(LOW); + POCSAG_pin(LOW); COS_pin(LOW); DEB_pin(LOW); @@ -85,6 +86,7 @@ void CIO::selfTest() YSF_pin(ledValue); P25_pin(ledValue); NXDN_pin(ledValue); + POCSAG_pin(ledValue); COS_pin(ledValue); blinks++; @@ -106,7 +108,7 @@ void CIO::process() if (m_started) { // Two seconds timeout if (m_watchdog >= 19200U) { - 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_IDLE; setMode(m_modemState); } @@ -139,6 +141,11 @@ void CIO::process() // Restoring previous mode io.ifConf(m_modemState_prev, true); } + if(m_pocsag_state) { // check for POCSAG end of transmission + m_pocsag_state = false; + // Restoring previous mode + io.ifConf(m_modemState_prev, true); + } setRX(false); } @@ -157,7 +164,7 @@ void CIO::process() if(m_modeTimerCnt >= scantime) { m_modeTimerCnt = 0U; - if( (m_modemState == STATE_IDLE) && (m_scanPauseCnt == 0U) && m_scanEnable && !m_cwid_state) { + if( (m_modemState == STATE_IDLE) && (m_scanPauseCnt == 0U) && m_scanEnable && !m_cwid_state && !m_pocsag_state) { m_scanPos = (m_scanPos + 1U) % m_TotalModes; #if !defined(QUIET_MODE_LEDS) setMode(m_Modes[m_scanPos]); @@ -306,7 +313,8 @@ void CIO::setMode(MMDVM_STATE modemState) DMR_pin(modemState == STATE_DMR); YSF_pin(modemState == STATE_YSF); P25_pin(modemState == STATE_P25); - NXDN_pin(modemState == STATE_NXDN); + NXDN_pin(modemState == STATE_NXDN); + POCSAG_pin(modemState == STATE_POCSAG); } void CIO::setDecode(bool dcd) diff --git a/IO.h b/IO.h index b6c13e0..8e2c705 100644 --- a/IO.h +++ b/IO.h @@ -76,6 +76,7 @@ class CIO { void YSF_pin(bool on); void P25_pin(bool on); void NXDN_pin(bool on); + void POCSAG_pin(bool on); void COS_pin(bool on); void interrupt(void); #if defined(DUPLEX) @@ -110,7 +111,7 @@ class CIO { #endif void start(void); void startInt(void); - void setDeviations(uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, bool ysfLoDev); + void setDeviations(uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, bool ysfLoDev); void updateCal(void); #if defined(SEND_RSSI_DATA) @@ -131,6 +132,7 @@ class CIO { uint16_t devYSF(void); uint16_t devP25(void); uint16_t devNXDN(void); + uint16_t devPOCSAG(void); void printConf(); #endif diff --git a/IOArduino.cpp b/IOArduino.cpp index 0d0d80a..e88aa37 100644 --- a/IOArduino.cpp +++ b/IOArduino.cpp @@ -316,6 +316,11 @@ void CIO::NXDN_pin(bool on) digitalWrite(PIN_NXDN_LED, on ? HIGH : LOW); } +void CIO::POCSAG_pin(bool on) +{ + // TODO: add a LED pin for POCSAG mode +} + void CIO::PTT_pin(bool on) { digitalWrite(PIN_PTT_LED, on ? HIGH : LOW); diff --git a/IOSTM.cpp b/IOSTM.cpp index 79821a6..7d894d6 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -647,6 +647,11 @@ void CIO::NXDN_pin(bool on) GPIO_WriteBit(PORT_NXDN_LED, PIN_NXDN_LED, on ? Bit_SET : Bit_RESET); } +void CIO::POCSAG_pin(bool on) +{ + // TODO: add a LED pin for POCSAG mode +} + void CIO::PTT_pin(bool on) { GPIO_WriteBit(PORT_PTT_LED, PIN_PTT_LED, on ? Bit_SET : Bit_RESET); diff --git a/MMDVM_HS.cpp b/MMDVM_HS.cpp index ea32e2e..2b556ec 100644 --- a/MMDVM_HS.cpp +++ b/MMDVM_HS.cpp @@ -31,15 +31,18 @@ MMDVM_STATE m_calState = STATE_IDLE; MMDVM_STATE m_modemState_prev = STATE_IDLE; bool m_cwid_state = false; +bool m_pocsag_state = false; + uint8_t m_cwIdTXLevel = 30; uint32_t m_modeTimerCnt; -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 = false; @@ -69,6 +72,8 @@ CP25TX p25TX; CNXDNRX nxdnRX; CNXDNTX nxdnTX; +CPOCSAGTX pocsagTX; + CCalDMR calDMR; #if defined(SEND_RSSI_DATA) @@ -115,6 +120,9 @@ void loop() if (m_nxdnEnable && m_modemState == STATE_NXDN) nxdnTX.process(); + if (m_pocsagEnable && m_modemState == STATE_POCSAG) + pocsagTX.process(); + if (m_calState == STATE_DMRCAL || m_calState == STATE_DMRDMO1K) calDMR.process(); diff --git a/MMDVM_HS.ino b/MMDVM_HS.ino index 1cacba2..7b1cd39 100644 --- a/MMDVM_HS.ino +++ b/MMDVM_HS.ino @@ -27,15 +27,18 @@ MMDVM_STATE m_calState = STATE_IDLE; MMDVM_STATE m_modemState_prev = STATE_IDLE; bool m_cwid_state = false; +bool m_pocsag_state = false; + uint8_t m_cwIdTXLevel = 30; uint32_t m_modeTimerCnt; -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 = false; @@ -65,6 +68,8 @@ CP25TX p25TX; CNXDNRX nxdnRX; CNXDNTX nxdnTX; +CPOCSAGTX pocsagTX; + CCalDMR calDMR; #if defined(SEND_RSSI_DATA) @@ -110,6 +115,9 @@ void loop() if (m_nxdnEnable && m_modemState == STATE_NXDN) nxdnTX.process(); + if (m_pocsagEnable && m_modemState == STATE_POCSAG) + pocsagTX.process(); + if (m_calState == STATE_DMRCAL || m_calState == STATE_DMRDMO1K) calDMR.process(); diff --git a/NXDNTX.cpp b/NXDNTX.cpp index 0f3938c..1e84cfc 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -32,7 +32,6 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(240U), // 200ms -m_count(0U), m_delay(false), m_preamble(false) { @@ -47,7 +46,6 @@ void CNXDNTX::process() if (!m_tx) { m_delay = true; m_preamble = false; - m_count = 0U; m_poLen = m_txDelay; } else { m_delay = false; diff --git a/NXDNTX.h b/NXDNTX.h index c17ab37..e12467e 100644 --- a/NXDNTX.h +++ b/NXDNTX.h @@ -40,7 +40,6 @@ class CNXDNTX { uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; - uint32_t m_count; bool m_delay; bool m_preamble; diff --git a/P25TX.cpp b/P25TX.cpp index 48f688e..6831527 100644 --- a/P25TX.cpp +++ b/P25TX.cpp @@ -31,7 +31,7 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(240U), // 200ms -m_count(0U) +m_delay(false) { } @@ -43,7 +43,6 @@ void CP25TX::process() if (m_poLen == 0U) { if (!m_tx) { m_delay = true; - m_count = 0U; m_poLen = m_txDelay; } else { uint8_t length = m_buffer.get(); diff --git a/P25TX.h b/P25TX.h index 93b3290..beb9da5 100644 --- a/P25TX.h +++ b/P25TX.h @@ -40,7 +40,6 @@ class CP25TX { uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; - uint32_t m_count; bool m_delay; void writeByte(uint8_t c); diff --git a/POCSAGDefines.h b/POCSAGDefines.h new file mode 100644 index 0000000..3b28fa3 --- /dev/null +++ b/POCSAGDefines.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * 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(POCSAGDEFINES_H) +#define POCSAGDEFINES_H + +const uint16_t POCSAG_PREAMBLE_LENGTH_BYTES = 576U / 8U; +const uint16_t POCSAG_FRAME_LENGTH_BYTES = 17U * sizeof(uint32_t); +const uint8_t POCSAG_SYNC = 0xAAU; + +#endif diff --git a/POCSAGTX.cpp b/POCSAGTX.cpp new file mode 100644 index 0000000..8717026 --- /dev/null +++ b/POCSAGTX.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * 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" +#include "POCSAGDefines.h" + +CPOCSAGTX::CPOCSAGTX() : +m_buffer(1000U), +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_txDelay(POCSAG_PREAMBLE_LENGTH_BYTES), +m_delay(false) +{ +} + +void CPOCSAGTX::process() +{ + if (m_buffer.getData() == 0U && m_poLen == 0U) + return; + + if (m_poLen == 0U) { + if (!m_tx) { + m_delay = true; + m_poLen = m_txDelay; + } else { + m_delay = false; + for (uint8_t i = 0U; i < POCSAG_FRAME_LENGTH_BYTES; i++) + m_poBuffer[m_poLen++] = m_buffer.get(); + } + + m_poPtr = 0U; + } + + if (m_poLen > 0U) { + uint16_t space = io.getSpace(); + + while (space > 8U) { + if (m_delay) { + m_poPtr++; + writeByte(POCSAG_SYNC); + } + else + writeByte(m_poBuffer[m_poPtr++]); + + space -= 8U; + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + m_delay = false; + 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) +{ + uint8_t bit; + uint8_t mask = 0x01U; + + for (uint8_t i = 0U; i < 8U; i++) { + if ((c & mask) == mask) + bit = 1U; + else + bit = 0U; + + io.write(&bit, 1); + mask <<= 1; + } + +} + +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..a102ce9 --- /dev/null +++ b/POCSAGTX.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * 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 + +class CPOCSAGTX { +public: + CPOCSAGTX(); + + uint8_t writeData(const uint8_t* data, uint8_t length); + + void setTXDelay(uint8_t delay); + + uint8_t getSpace() const; + + void process(); + +private: + CSerialRB m_buffer; + uint8_t m_poBuffer[200U]; + uint16_t m_poLen; + uint16_t m_poPtr; + uint16_t m_txDelay; + bool m_delay; + + void writeByte(uint8_t c); +}; + +#endif diff --git a/SerialPort.cpp b/SerialPort.cpp index b2042aa..2fcef12 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -59,6 +59,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; @@ -128,7 +130,9 @@ void CSerialPort::getStatus() reply[3U] |= 0x08U; if (m_nxdnEnable) reply[3U] |= 0x10U; - + if (m_pocsagEnable) + reply[3U] |= 0x20U; + reply[4U] = uint8_t(m_modemState); reply[5U] = m_tx ? 0x01U : 0x00U; @@ -177,7 +181,12 @@ void CSerialPort::getStatus() else reply[11U] = 0U; - writeInt(1U, reply, 11); + if (m_pocsagEnable) + reply[12U] = pocsagTX.getSpace(); + else + reply[12U] = 0U; + + writeInt(1U, reply, 13); } void CSerialPort::getVersion() @@ -209,11 +218,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) @@ -221,7 +231,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_DMRDMO1K && modemState != STATE_RSSICAL) + 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_DMRDMO1K && modemState != STATE_RSSICAL) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -233,6 +243,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 colorCode = data[6U]; if (colorCode > 15U) @@ -244,22 +256,27 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) m_cwIdTXLevel = data[5U]>>2; - uint8_t dstarTXLevel = data[9U]; - uint8_t dmrTXLevel = data[10U]; - uint8_t ysfTXLevel = data[11U]; - uint8_t p25TXLevel = data[12U]; - uint8_t nxdnTXLevel = 128U; + uint8_t dstarTXLevel = data[9U]; + uint8_t dmrTXLevel = data[10U]; + uint8_t ysfTXLevel = data[11U]; + uint8_t p25TXLevel = data[12U]; + uint8_t nxdnTXLevel = 128U; + uint8_t pocsagTXLevel = 128U; if (length >= 16U) nxdnTXLevel = data[15U]; - io.setDeviations(dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, ysfLoDev); + if (length >= 18U) + pocsagTXLevel = data[17U]; + + io.setDeviations(dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, ysfLoDev); - m_dstarEnable = dstarEnable; - m_dmrEnable = dmrEnable; - m_ysfEnable = ysfEnable; - m_p25Enable = p25Enable; - m_nxdnEnable = nxdnEnable; + m_dstarEnable = dstarEnable; + m_dmrEnable = dmrEnable; + m_ysfEnable = ysfEnable; + m_p25Enable = p25Enable; + m_nxdnEnable = nxdnEnable; + m_pocsagEnable = pocsagEnable; if (modemState == STATE_DMRCAL || modemState == STATE_DMRDMO1K || modemState == STATE_RSSICAL) { m_dmrEnable = true; @@ -288,6 +305,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) ysfTX.setTXDelay(txDelay); p25TX.setTXDelay(txDelay); nxdnTX.setTXDelay(txDelay); + pocsagTX.setTXDelay(txDelay); dmrDMOTX.setTXDelay(txDelay); #if defined(DUPLEX) @@ -336,7 +354,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_DMRDMO1K && modemState != STATE_RSSICAL) + 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_DMRDMO1K && modemState != STATE_RSSICAL) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -348,6 +366,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; if (modemState == STATE_DMRCAL || modemState == STATE_DMRDMO1K || modemState == STATE_RSSICAL) { m_dmrEnable = true; @@ -454,6 +474,19 @@ void CSerialPort::setMode(MMDVM_STATE modemState) p25RX.reset(); cwIdTX.reset(); break; + case STATE_POCSAG: + DEBUG1("Mode set to POCSAG"); +#if defined(DUPLEX) + dmrIdleRX.reset(); + dmrRX.reset(); +#endif + dmrDMORX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; default: DEBUG1("Mode set to Idle"); // STATE_IDLE @@ -461,12 +494,12 @@ void CSerialPort::setMode(MMDVM_STATE modemState) } m_modemState = modemState; - + if ((modemState != STATE_IDLE) && (m_modemState_prev != modemState)) { DEBUG1("setMode: configuring Hardware"); io.ifConf(modemState, true); } - + io.setMode(m_modemState); } @@ -751,6 +784,21 @@ void CSerialPort::process() } break; + case MMDVM_POCSAG_DATA: + if (m_pocsagEnable) { + if (m_modemState == STATE_IDLE || m_modemState == STATE_POCSAG) + m_pocsag_state = true; + 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; + #if defined(SERIAL_REPEATER) || defined(SERIAL_REPEATER_USART1) case MMDVM_SERIAL: writeInt(3U, m_buffer + 3U, m_len - 3U); diff --git a/YSFTX.cpp b/YSFTX.cpp index 9e01f55..fa5e402 100644 --- a/YSFTX.cpp +++ b/YSFTX.cpp @@ -32,7 +32,7 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(240U), // 200ms -m_count(0U) +m_delay(false) { } @@ -44,7 +44,6 @@ void CYSFTX::process() if (m_poLen == 0U) { if (!m_tx) { m_delay = true; - m_count = 0U; m_poLen = m_txDelay; } else { m_delay = false; diff --git a/YSFTX.h b/YSFTX.h index fc80f63..21dcb1d 100644 --- a/YSFTX.h +++ b/YSFTX.h @@ -40,7 +40,6 @@ class CYSFTX { uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; - uint32_t m_count; bool m_delay; void writeByte(uint8_t c);