Add POCSAG support (TX)

This commit is contained in:
Andy CA6JAU 2018-06-17 20:10:00 -04:00
parent 82b62b9645
commit 121e909367
23 changed files with 367 additions and 56 deletions

View file

@ -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

View file

@ -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

View file

@ -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)
{

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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)

14
IO.cpp
View file

@ -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)

4
IO.h
View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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);

27
POCSAGDefines.h Normal file
View file

@ -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

118
POCSAGTX.cpp Normal file
View file

@ -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;
}

46
POCSAGTX.h Normal file
View file

@ -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

View file

@ -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];
m_dstarEnable = dstarEnable;
m_dmrEnable = dmrEnable;
m_ysfEnable = ysfEnable;
m_p25Enable = p25Enable;
m_nxdnEnable = nxdnEnable;
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_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);

View file

@ -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;

View file

@ -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);