From 121e909367554f1b894d290522cfb70230c4063e Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Sun, 17 Jun 2018 20:10:00 -0400 Subject: [PATCH 01/14] 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 @@ private: 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 @@ private: 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 @@ public: 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 @@ public: #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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; - uint32_t m_count; bool m_delay; void writeByte(uint8_t c); From 113b6444d844c664696d914b9de7c7bb892e88d3 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 18 Jun 2018 00:07:32 -0400 Subject: [PATCH 02/14] Fix bit order for POCSAG --- POCSAGTX.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/POCSAGTX.cpp b/POCSAGTX.cpp index 8717026..e969b9c 100644 --- a/POCSAGTX.cpp +++ b/POCSAGTX.cpp @@ -1,4 +1,5 @@ /* + * 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 @@ -90,18 +91,16 @@ uint8_t CPOCSAGTX::writeData(const uint8_t* data, uint8_t length) void CPOCSAGTX::writeByte(uint8_t c) { uint8_t bit; - uint8_t mask = 0x01U; + uint8_t mask = 0x80U; - for (uint8_t i = 0U; i < 8U; i++) { + for (uint8_t i = 0U; i < 8U; i++, c <<= 1) { if ((c & mask) == mask) bit = 1U; else bit = 0U; io.write(&bit, 1); - mask <<= 1; } - } void CPOCSAGTX::setTXDelay(uint8_t delay) From a2d079548276206f3800be565c5dae255b295d94 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 18 Jun 2018 13:51:00 -0400 Subject: [PATCH 03/14] Invert POCSAG TX data --- ADF7021.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADF7021.cpp b/ADF7021.cpp index 2fa56fe..5466da1 100644 --- a/ADF7021.cpp +++ b/ADF7021.cpp @@ -333,7 +333,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) // 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) 0b10 << 28; // inverted data, clock normal ADF7021_REG2 |= (uint32_t) (ADF7021_DEV_POCSAG / div2)<< 19; // deviation ADF7021_REG2 |= (uint32_t) 0b000 << 4; // modulation (2FSK) break; From d4dec3d76d868ed4e4c4fc302493ca345084fc35 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 18 Jun 2018 14:47:13 -0400 Subject: [PATCH 04/14] Fix getStatus() reply length --- SerialPort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPort.cpp b/SerialPort.cpp index 2fcef12..d6d2d37 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -116,7 +116,7 @@ void CSerialPort::getStatus() // Send all sorts of interesting internal values reply[0U] = MMDVM_FRAME_START; - reply[1U] = 11U; + reply[1U] = 13U; reply[2U] = MMDVM_GET_STATUS; reply[3U] = 0x00U; From 707157a777aa07bbbf831d7bd3ec690670635215 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Tue, 19 Jun 2018 14:56:37 -0400 Subject: [PATCH 05/14] Add support for separated POCSAG frequency --- ADF7021.cpp | 20 +++++++++++++++++-- IO.cpp | 10 +++++++++- IO.h | 3 ++- SerialPort.cpp | 54 ++++++++++++++++++++++++++++++-------------------- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/ADF7021.cpp b/ADF7021.cpp index 5466da1..e09114b 100644 --- a/ADF7021.cpp +++ b/ADF7021.cpp @@ -187,9 +187,19 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) uint32_t ADF7021_REG13 = 0U; int32_t AFC_OFFSET = 0; - if(modemState != STATE_CWID && modemState != STATE_POCSAG) + uint32_t frequency_tx_tmp, frequency_rx_tmp; + + if (modemState != STATE_CWID && modemState != STATE_POCSAG) m_modemState_prev = modemState; + // Change frequency for POCSAG mode, store a backup of DV frequencies + if (modemState == STATE_POCSAG) { + frequency_tx_tmp = m_frequency_tx; + frequency_rx_tmp = m_frequency_rx; + m_frequency_tx = m_pocsag_freq_tx; + m_frequency_rx = m_pocsag_freq_tx; + } + // Toggle CE pin for ADF7021 reset if(reset) { CE_pin(LOW); @@ -545,7 +555,13 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) AD7021_control_word = 0x000E000F; #endif Send_AD7021_control(); - + + // Restore normal DV frequencies + if (modemState == STATE_POCSAG) { + m_frequency_tx = frequency_tx_tmp; + m_frequency_rx = frequency_rx_tmp; + } + #if defined(DUPLEX) if(m_duplex && (modemState != STATE_CWID && modemState != STATE_POCSAG)) ifConf2(modemState); diff --git a/IO.cpp b/IO.cpp index 8c33119..d2d041f 100644 --- a/IO.cpp +++ b/IO.cpp @@ -24,6 +24,7 @@ uint32_t m_frequency_rx; uint32_t m_frequency_tx; +uint32_t m_pocsag_freq_tx; uint8_t m_power; CIO::CIO(): @@ -288,7 +289,7 @@ bool CIO::hasRXOverflow() return m_rxBuffer.hasOverflowed(); } -uint8_t CIO::setFreq(uint32_t frequency_rx, uint32_t frequency_tx, uint8_t rf_power) +uint8_t CIO::setFreq(uint32_t frequency_rx, uint32_t frequency_tx, uint8_t rf_power, uint32_t pocsag_freq_tx) { // Configure power level setPower(rf_power); @@ -300,9 +301,16 @@ uint8_t CIO::setFreq(uint32_t frequency_rx, uint32_t frequency_tx, uint8_t rf_po ((frequency_rx >= UHF2_MIN)&&(frequency_rx < UHF2_MAX)) || ((frequency_tx >= UHF2_MIN)&&(frequency_tx < UHF2_MAX)) ) ) return 4U; + if( !( ((pocsag_freq_tx >= VHF1_MIN)&&(pocsag_freq_tx < VHF1_MAX)) || \ + ((pocsag_freq_tx >= UHF1_MIN)&&(pocsag_freq_tx < UHF1_MAX)) || \ + ((pocsag_freq_tx >= VHF2_MIN)&&(pocsag_freq_tx < VHF2_MAX)) || \ + ((pocsag_freq_tx >= UHF2_MIN)&&(pocsag_freq_tx < UHF2_MAX)) ) ) + return 4U; + // Configure frequency m_frequency_rx = frequency_rx; m_frequency_tx = frequency_tx; + m_pocsag_freq_tx = pocsag_freq_tx; return 0U; } diff --git a/IO.h b/IO.h index 8e2c705..5fe4081 100644 --- a/IO.h +++ b/IO.h @@ -42,6 +42,7 @@ extern uint32_t m_frequency_rx; extern uint32_t m_frequency_tx; +extern uint32_t m_pocsag_freq_tx; extern uint8_t m_power; class CIO { @@ -93,7 +94,7 @@ public: void process(void); bool hasTXOverflow(void); bool hasRXOverflow(void); - uint8_t setFreq(uint32_t frequency_rx, uint32_t frequency_tx, uint8_t rf_power); + uint8_t setFreq(uint32_t frequency_rx, uint32_t frequency_tx, uint8_t rf_power, uint32_t pocsag_freq_tx); void setPower(uint8_t power); void setMode(MMDVM_STATE modemState); void setDecode(bool dcd); diff --git a/SerialPort.cpp b/SerialPort.cpp index d6d2d37..8ee44b5 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -388,31 +388,41 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFreq(const uint8_t* data, uint8_t length) { - uint32_t freq_rx, freq_tx; - uint8_t rf_power; + uint32_t freq_rx, freq_tx, pocsag_freq_tx; + uint8_t rf_power; - if (length < 9U) - return 4U; - - // Old MMDVMHost, set full power - if (length == 9U) - rf_power = 255U; + if (length < 9U) + return 4U; - // New MMDVMHost, set power from MMDVM.ini - if (length == 10U) - rf_power = data[9U]; + // Very old MMDVMHost, set full power + if (length == 9U) + rf_power = 255U; + + // Current MMDVMHost, set power from MMDVM.ini + if (length >= 10U) + rf_power = data[9U]; + + freq_rx = data[1U] << 0; + freq_rx |= data[2U] << 8; + freq_rx |= data[3U] << 16; + freq_rx |= data[4U] << 24; + + freq_tx = data[5U] << 0; + freq_tx |= data[6U] << 8; + freq_tx |= data[7U] << 16; + freq_tx |= data[8U] << 24; + + // New MMDVMHost, set POCSAG TX frequency + if (length >= 14U) { + pocsag_freq_tx = data[10U] << 0; + pocsag_freq_tx |= data[11U] << 8; + pocsag_freq_tx |= data[12U] << 16; + pocsag_freq_tx |= data[13U] << 24; + } + else + pocsag_freq_tx = freq_tx; - freq_rx = data[1U] * 1U; - freq_rx += data[2U] * 256U; - freq_rx += data[3U] * 65536U; - freq_rx += data[4U] * 16777216U; - - freq_tx = data[5U] * 1U; - freq_tx += data[6U] * 256U; - freq_tx += data[7U] * 65536U; - freq_tx += data[8U] * 16777216U; - - return io.setFreq(freq_rx, freq_tx, rf_power); + return io.setFreq(freq_rx, freq_tx, rf_power, pocsag_freq_tx); } void CSerialPort::setMode(MMDVM_STATE modemState) From c35aa8d874467846f698df739a345012cf8dbcd9 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Wed, 20 Jun 2018 20:14:40 -0400 Subject: [PATCH 06/14] Enable external POCSAG deviation adjustment (50 % = +-4.5 kHz for symbol deviation) --- ADF7021.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADF7021.cpp b/ADF7021.cpp index e09114b..f5f71dc 100644 --- a/ADF7021.cpp +++ b/ADF7021.cpp @@ -344,7 +344,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) ADF7021_REG13 = (uint32_t) 0b1101 << 0; // register 13 ADF7021_REG2 = (uint32_t) 0b10 << 28; // inverted data, clock normal - ADF7021_REG2 |= (uint32_t) (ADF7021_DEV_POCSAG / div2)<< 19; // deviation + ADF7021_REG2 |= (uint32_t) (m_pocsagDev / div2) << 19; // deviation ADF7021_REG2 |= (uint32_t) 0b000 << 4; // modulation (2FSK) break; From 72e7a4362f95e07fda93c1629e3e9edd26a9fe29 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 21 Jun 2018 15:11:05 -0400 Subject: [PATCH 07/14] Update README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d08f99b..bba2621 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # Introduction -This is the source code of ZUMspot/MMDVM_HS, personal hotspot (ADF7021 version of the MMDVM firmware), based on Jonathan G4KLX's [MMDVM](https://github.com/g4klx/MMDVM) software. This firmware supports D-Star, DMR, System Fusion, P25 and NXDN digital modes. +This is the source code of ZUMspot/MMDVM_HS firmware for personal hotspots (ADF7021 version of the MMDVM firmware), based on Jonathan G4KLX's [MMDVM](https://github.com/g4klx/MMDVM) software. This firmware supports D-Star, DMR, System Fusion, P25 and NXDN digital modes. This software is intended to be run on STM32F103 microcontroller. Also, Arduino with 3.3 V I/O (Arduino Due and Zero) and Teensy (3.1, 3.2, 3.5 or 3.6) are supported. You can build this code using Arduino IDE with Roger Clark's [STM32duino](https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/ZUMspot) package, or using command line tools with ARM GCC tools. The preferred method under Windows is using STM32duino, and under Linux or macOS (command line) is using [STM32F10X_Lib](https://github.com/juribeparada/STM32F10X_Lib). +This software comes with ABSOLUTELY NO WARRANTY, it is provided "AS IS" with the hope to be useful, use at your own risk. This firmware software is intended to be use into personal hotspots hardware, with a few mili-watts of RF power and short ranges (indoor). Because this is an amateur project doing by developers under their free time, and due to ADF7021 imitations, please DO NOT EXPECT high performance or full compliant to any digital voice standard of this firmware or any board based on this firmware. + This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden. # Features From f04cd58e1975dea53e28be83ba5cc26e43c74aa8 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 21 Jun 2018 19:44:57 -0400 Subject: [PATCH 08/14] Update README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bba2621..f656f24 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,20 @@ # Introduction -This is the source code of ZUMspot/MMDVM_HS firmware for personal hotspots (ADF7021 version of the MMDVM firmware), based on Jonathan G4KLX's [MMDVM](https://github.com/g4klx/MMDVM) software. This firmware supports D-Star, DMR, System Fusion, P25 and NXDN digital modes. +This is the source code of ZUMspot/MMDVM_HS firmware for personal hotspots (ADF7021 version of the MMDVM firmware), based on Jonathan G4KLX's [MMDVM](https://github.com/g4klx/MMDVM) software. This firmware supports D-Star, DMR, System Fusion, P25 and NXDN digital voice modes and POCSAG 1200 pager protocol. This software is intended to be run on STM32F103 microcontroller. Also, Arduino with 3.3 V I/O (Arduino Due and Zero) and Teensy (3.1, 3.2, 3.5 or 3.6) are supported. You can build this code using Arduino IDE with Roger Clark's [STM32duino](https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/ZUMspot) package, or using command line tools with ARM GCC tools. The preferred method under Windows is using STM32duino, and under Linux or macOS (command line) is using [STM32F10X_Lib](https://github.com/juribeparada/STM32F10X_Lib). -This software comes with ABSOLUTELY NO WARRANTY, it is provided "AS IS" with the hope to be useful, use at your own risk. This firmware software is intended to be use into personal hotspots hardware, with a few mili-watts of RF power and short ranges (indoor). Because this is an amateur project doing by developers under their free time, and due to ADF7021 imitations, please DO NOT EXPECT high performance or full compliant to any digital voice standard of this firmware or any board based on this firmware. +This software comes with ABSOLUTELY NO WARRANTY, it is provided "AS IS" with the hope to be useful, use at your own risk. This firmware software is intended to be use into personal hotspots hardware, with a few mili-watts of RF power and short ranges (indoor). Because this is an amateur project doing by developers under their free time, and due to ADF7021 limitations, please DO NOT EXPECT high performance or full compliant to any digital voice standard of this firmware or any board based on this firmware. This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden. # Features - Supported modes: D-Star, DMR, Yaesu Fusion, P25 Phase 1 and NXDN +- Other modes: POCSAG 1200 - Automatic mode detection (scanning) -- G4KLX software suite: [MMDVMHost](https://github.com/g4klx/MMDVMHost), [ircDDBGateway](https://github.com/dl5di/OpenDV), [YSFGateway](https://github.com/g4klx/YSFClients), [P25Gateway](https://github.com/g4klx/P25Clients), [DMRGateway](https://github.com/g4klx/DMRGateway), [NXDNGateway](https://github.com/g4klx/NXDNClients) and [MMDVMCal](https://github.com/g4klx/MMDVMCal) +- G4KLX software suite: [MMDVMHost](https://github.com/g4klx/MMDVMHost), [ircDDBGateway](https://github.com/g4klx/ircDDBGateway), [YSFGateway](https://github.com/g4klx/YSFClients), [P25Gateway](https://github.com/g4klx/P25Clients), [DMRGateway](https://github.com/g4klx/DMRGateway), [NXDNGateway](https://github.com/g4klx/NXDNClients), +[DAPNETGateway](https://github.com/g4klx/DAPNETGateway) and [MMDVMCal](https://github.com/g4klx/MMDVMCal) - Bands: 144, 220, 430 and 900 MHz (VHF requires external inductor) - Status LEDs (PTT, COR and digital modes) - Serial repeater port for Nextion displays From 81d61b3fbf7e654bcda96b5a006daf24d9b7fb5d Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 21 Jun 2018 19:46:08 -0400 Subject: [PATCH 09/14] Update version --- version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index d852d55..897da54 100644 --- a/version.h +++ b/version.h @@ -24,8 +24,8 @@ #define VER_MAJOR "1" #define VER_MINOR "3" -#define VER_REV "7" -#define VERSION_DATE "20180522" +#define VER_REV "8" +#define VERSION_DATE "20180621" #if defined(ZUMSPOT_ADF7021) #define BOARD_INFO "ZUMspot" From 654403afc004bf4619b1e86919a2178900154f2b Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 21 Jun 2018 20:48:19 -0400 Subject: [PATCH 10/14] Change to linear demodulator for POCSAG (not used for now...) --- ADF7021.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADF7021.cpp b/ADF7021.cpp index f5f71dc..8be1f8d 100644 --- a/ADF7021.cpp +++ b/ADF7021.cpp @@ -333,7 +333,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) 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) 0b000 << 4; // 2FSK linear demodulator ADF7021_REG4 |= (uint32_t) 0b1 << 7; ADF7021_REG4 |= (uint32_t) 0b10 << 8; ADF7021_REG4 |= (uint32_t) ADF7021_DISC_BW_POCSAG << 10; // Disc BW From 773c69afb01df188a3d940034c10d53bdb460e43 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 21 Jun 2018 20:48:42 -0400 Subject: [PATCH 11/14] Additional checks for POCSAG mode --- IO.cpp | 6 ++++-- SerialPort.cpp | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/IO.cpp b/IO.cpp index d2d041f..d5e38b1 100644 --- a/IO.cpp +++ b/IO.cpp @@ -140,12 +140,14 @@ void CIO::process() if(m_cwid_state) { // check for CW ID end of transmission m_cwid_state = false; // Restoring previous mode - io.ifConf(m_modemState_prev, true); + if (m_TotalModes) + 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); + if (m_TotalModes) + io.ifConf(m_modemState_prev, true); } setRX(false); } diff --git a/SerialPort.cpp b/SerialPort.cpp index 8ee44b5..5ee99e7 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -330,6 +330,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) io.ifConf(STATE_P25, true); else if(m_nxdnEnable) io.ifConf(STATE_NXDN, true); + else if(m_pocsagEnable) + io.ifConf(STATE_POCSAG, true); } io.start(); From dc9b8c28f648eeb362b917e02605ae4ff0bbec38 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Tue, 3 Jul 2018 14:17:42 -0400 Subject: [PATCH 12/14] Update TX delay according to G4KLX MMDVM firmware --- POCSAGDefines.h | 2 +- POCSAGTX.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/POCSAGDefines.h b/POCSAGDefines.h index 3b28fa3..513ffbc 100644 --- a/POCSAGDefines.h +++ b/POCSAGDefines.h @@ -20,7 +20,7 @@ #if !defined(POCSAGDEFINES_H) #define POCSAGDEFINES_H -const uint16_t POCSAG_PREAMBLE_LENGTH_BYTES = 576U / 8U; +const uint16_t POCSAG_PREAMBLE_LENGTH_BYTES = 18U * sizeof(uint32_t); const uint16_t POCSAG_FRAME_LENGTH_BYTES = 17U * sizeof(uint32_t); const uint8_t POCSAG_SYNC = 0xAAU; diff --git a/POCSAGTX.cpp b/POCSAGTX.cpp index e969b9c..9d8f389 100644 --- a/POCSAGTX.cpp +++ b/POCSAGTX.cpp @@ -105,10 +105,10 @@ void CPOCSAGTX::writeByte(uint8_t c) void CPOCSAGTX::setTXDelay(uint8_t delay) { - m_txDelay = POCSAG_PREAMBLE_LENGTH_BYTES + uint16_t(delay); + m_txDelay = POCSAG_PREAMBLE_LENGTH_BYTES + (delay * 3U) / 2U; - if (m_txDelay > 1200U) - m_txDelay = 1200U; + if (m_txDelay > 150U) + m_txDelay = 150U; } uint8_t CPOCSAGTX::getSpace() const From 602d773377094648805e48835b71ebe126afd86a Mon Sep 17 00:00:00 2001 From: phl0 Date: Wed, 4 Jul 2018 10:12:21 +0200 Subject: [PATCH 13/14] Use DStar and DMR LEDs for POCSAG mode until a separate POCSAG LED is available on newer boards --- IOArduino.cpp | 5 ++++- IOSTM.cpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/IOArduino.cpp b/IOArduino.cpp index e88aa37..1887951 100644 --- a/IOArduino.cpp +++ b/IOArduino.cpp @@ -318,7 +318,10 @@ void CIO::NXDN_pin(bool on) void CIO::POCSAG_pin(bool on) { - // TODO: add a LED pin for POCSAG mode + // Use D-Star and DMR LED to indicate POCSAG mode + // TODO: add a separate LED pin for POCSAG mode + digitalWrite(PIN_DSTAR_LED, on ? HIGH : LOW); + digitalWrite(PIN_DMR_LED, on ? HIGH : LOW); } void CIO::PTT_pin(bool on) diff --git a/IOSTM.cpp b/IOSTM.cpp index 7d894d6..962fe29 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -649,7 +649,10 @@ void CIO::NXDN_pin(bool on) void CIO::POCSAG_pin(bool on) { - // TODO: add a LED pin for POCSAG mode + // Use D-Star and DMR LED to indicate POCSAG mode + // TODO: add a separate LED pin for POCSAG mode + GPIO_WriteBit(PORT_DSTAR_LED, PIN_DSTAR_LED, on ? Bit_SET : Bit_RESET); + GPIO_WriteBit(PORT_DMR_LED, PIN_DMR_LED, on ? Bit_SET : Bit_RESET); } void CIO::PTT_pin(bool on) From 57878e1cea9cf03de8c9e4670113eeccb1e11dee Mon Sep 17 00:00:00 2001 From: phl0 Date: Wed, 4 Jul 2018 22:19:25 +0200 Subject: [PATCH 14/14] Adapt LED handling to MMDVM options --- Config.h | 6 ++++++ IOArduino.cpp | 10 ++++++++-- IOSTM.cpp | 10 ++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Config.h b/Config.h index 13ace8b..df112ef 100644 --- a/Config.h +++ b/Config.h @@ -70,6 +70,12 @@ // Constant Service LED once repeater is running // #define CONSTANT_SRV_LED +// Use the YSF and P25 LEDs for NXDN +// #define USE_ALTERNATE_NXDN_LEDS + +// Use the D-Star and DMR LEDs for POCSAG +// #define USE_ALTERNATE_POCSAG_LEDS + // Enable modem debug messages // #define ENABLE_DEBUG diff --git a/IOArduino.cpp b/IOArduino.cpp index 1887951..41042f1 100644 --- a/IOArduino.cpp +++ b/IOArduino.cpp @@ -313,15 +313,21 @@ void CIO::P25_pin(bool on) void CIO::NXDN_pin(bool on) { +#if defined(USE_ALTERNATE_NXDN_LEDS) + digitalWrite(PIN_YSF_LED, on ? HIGH : LOW); + digitalWrite(PIN_P25_LED, on ? HIGH : LOW); +#else digitalWrite(PIN_NXDN_LED, on ? HIGH : LOW); +#endif } void CIO::POCSAG_pin(bool on) { - // Use D-Star and DMR LED to indicate POCSAG mode - // TODO: add a separate LED pin for POCSAG mode +#if defined(USE_ALTERNATE_POCSAG_LEDS) digitalWrite(PIN_DSTAR_LED, on ? HIGH : LOW); digitalWrite(PIN_DMR_LED, on ? HIGH : LOW); +#endif + // TODO: add a separate LED pin for POCSAG mode } void CIO::PTT_pin(bool on) diff --git a/IOSTM.cpp b/IOSTM.cpp index 962fe29..ac2e26b 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -644,15 +644,21 @@ void CIO::P25_pin(bool on) void CIO::NXDN_pin(bool on) { +#if defined(USE_ALTERNATE_NXDN_LEDS) + GPIO_WriteBit(PORT_YSF_LED, PIN_YSF_LED, on ? Bit_SET : Bit_RESET); + GPIO_WriteBit(PORT_P25_LED, PIN_P25_LED, on ? Bit_SET : Bit_RESET); +#else GPIO_WriteBit(PORT_NXDN_LED, PIN_NXDN_LED, on ? Bit_SET : Bit_RESET); +#endif } void CIO::POCSAG_pin(bool on) { - // Use D-Star and DMR LED to indicate POCSAG mode - // TODO: add a separate LED pin for POCSAG mode +#if defined(USE_ALTERNATE_POCSAG_LEDS) GPIO_WriteBit(PORT_DSTAR_LED, PIN_DSTAR_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_DMR_LED, PIN_DMR_LED, on ? Bit_SET : Bit_RESET); +#endif + // TODO: add a separate LED pin for POCSAG mode } void CIO::PTT_pin(bool on)