diff --git a/ADF7021.cpp b/ADF7021.cpp index db856f6..b906f4a 100644 --- a/ADF7021.cpp +++ b/ADF7021.cpp @@ -180,7 +180,8 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) uint32_t ADF7021_REG13 = 0; uint32_t AFC_OFFSET = 0; - m_modemState_prev = modemState; + if(modemState != STATE_CWID) + m_modemState_prev = modemState; // Toggle CE pin for ADF7021 reset if(reset) { @@ -222,6 +223,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) AFC_OFFSET = 0; break; case STATE_DMR: + case STATE_CWID: AFC_OFFSET = AFC_OFFSET_DMR; break; case STATE_YSF: @@ -279,6 +281,30 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) #endif switch (modemState) { + case STATE_CWID: + // CW ID base configuration: DMR + // Dev: +1 symb (variable), symb rate = 4800 + + ADF7021_REG3 = ADF7021_REG3_DMR; + ADF7021_REG10 = ADF7021_REG10_DMR; + + // K=32 + ADF7021_REG4 = (uint32_t) 0b0100 << 0; // register 4 + ADF7021_REG4 |= (uint32_t) 0b011 << 4; // mode, 4FSK + ADF7021_REG4 |= (uint32_t) 0b0 << 7; + ADF7021_REG4 |= (uint32_t) 0b11 << 8; + ADF7021_REG4 |= (uint32_t) ADF7021_DISC_BW_DMR << 10; // Disc BW + ADF7021_REG4 |= (uint32_t) ADF7021_POST_BW_DMR << 20; // Post dem BW + ADF7021_REG4 |= (uint32_t) 0b10 << 30; // IF filter (25 kHz) + + ADF7021_REG13 = (uint32_t) 0b1101 << 0; // register 13 + ADF7021_REG13 |= (uint32_t) ADF7021_SLICER_TH_DMR << 4; // slicer threshold + + ADF7021_REG2 = (uint32_t) 0b10 << 28; // invert data (and RC alpha = 0.5) + ADF7021_REG2 |= (uint32_t) (m_cwIdTXLevel / div2) << 19; // deviation + ADF7021_REG2 |= (uint32_t) 0b111 << 4; // modulation (RC 4FSK) + break; + case STATE_DSTAR: // Dev: 1200 Hz, symb rate = 4800 @@ -461,7 +487,7 @@ void CIO::ifConf(MMDVM_STATE modemState, bool reset) Send_AD7021_control(); #if defined(DUPLEX) -if(m_duplex) +if(m_duplex && (modemState != STATE_CWID)) ifConf2(modemState); #endif } diff --git a/CWIdTX.cpp b/CWIdTX.cpp new file mode 100644 index 0000000..8fb35b3 --- /dev/null +++ b/CWIdTX.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2009-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2016 by Colin Durbridge G4EML + * Copyright (C) 2017 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 "CWIdTX.h" + +// 4FSK symbol sequence (800 Hz "tone" at 4800 baud): +1 +3 +1 -1 -3 -1 +// Bit sequence: 00 01 00 10 11 10 +uint8_t TONE[] = {0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0}; + +uint8_t SILENCE[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +const uint8_t CYCLE_LENGTH = 12U; + +const uint8_t DOT_LENGTH = 40U; + +const struct { + uint8_t c; + uint32_t pattern; + uint8_t length; +} SYMBOL_LIST[] = { + {'A', 0xB8000000U, 8U}, + {'B', 0xEA800000U, 12U}, + {'C', 0xEBA00000U, 14U}, + {'D', 0xEA000000U, 10U}, + {'E', 0x80000000U, 4U}, + {'F', 0xAE800000U, 12U}, + {'G', 0xEE800000U, 12U}, + {'H', 0xAA000000U, 10U}, + {'I', 0xA0000000U, 6U}, + {'J', 0xBBB80000U, 16U}, + {'K', 0xEB800000U, 12U}, + {'L', 0xBA800000U, 12U}, + {'M', 0xEE000000U, 10U}, + {'N', 0xE8000000U, 8U}, + {'O', 0xEEE00000U, 14U}, + {'P', 0xBBA00000U, 14U}, + {'Q', 0xEEB80000U, 16U}, + {'R', 0xBA000000U, 10U}, + {'S', 0xA8000000U, 8U}, + {'T', 0xE0000000U, 6U}, + {'U', 0xAE000000U, 10U}, + {'V', 0xAB800000U, 12U}, + {'W', 0xBB800000U, 12U}, + {'X', 0xEAE00000U, 14U}, + {'Y', 0xEBB80000U, 16U}, + {'Z', 0xEEA00000U, 14U}, + {'1', 0xBBBB8000U, 20U}, + {'2', 0xAEEE0000U, 18U}, + {'3', 0xABB80000U, 16U}, + {'4', 0xAAE00000U, 14U}, + {'5', 0xAA800000U, 12U}, + {'6', 0xEAA00000U, 14U}, + {'7', 0xEEA80000U, 16U}, + {'8', 0xEEEA0000U, 18U}, + {'9', 0xEEEE8000U, 20U}, + {'0', 0xEEEEE000U, 22U}, + {'/', 0xEAE80000U, 16U}, + {'?', 0xAEEA0000U, 18U}, + {',', 0xEEAEE000U, 22U}, + {'-', 0xEAAE0000U, 18U}, + {'=', 0xEAB80000U, 16U}, + {' ', 0x00000000U, 4U}, + {0U, 0x00000000U, 0U} +}; + +const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +CCWIdTX::CCWIdTX() : +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_n(0U) +{ +} + +void CCWIdTX::process() +{ + if (m_poLen == 0U) + return; + + uint16_t space = io.getSpace(); + + while (space > CYCLE_LENGTH) { + bool b = READ_BIT1(m_poBuffer, m_poPtr); + if (b) + io.write(TONE, CYCLE_LENGTH); + else + io.write(SILENCE, CYCLE_LENGTH); + + space -= CYCLE_LENGTH; + + m_n++; + if (m_n >= DOT_LENGTH) { + m_poPtr++; + m_n = 0U; + } + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + return; + } + } +} + +uint8_t CCWIdTX::write(const uint8_t* data, uint8_t length) +{ + ::memset(m_poBuffer, 0x00U, 300U * sizeof(uint8_t)); + + m_poLen = 8U; + m_poPtr = 0U; + m_n = 0U; + + for (uint8_t i = 0U; i < length; i++) { + for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) { + if (SYMBOL_LIST[j].c == data[i]) { + uint32_t MASK = 0x80000000U; + for (uint8_t k = 0U; k < SYMBOL_LIST[j].length; k++, m_poLen++, MASK >>= 1) { + bool b = (SYMBOL_LIST[j].pattern & MASK) == MASK; + WRITE_BIT1(m_poBuffer, m_poLen, b); + + if (m_poLen >= 295U) { + m_poLen = 0U; + return 4U; + } + } + + break; + } + } + } + + // An empty message + if (m_poLen == 8U) { + m_poLen = 0U; + return 4U; + } + + m_poLen += 5U; + + DEBUG2("Message created with length", m_poLen); + + return 0U; +} + +void CCWIdTX::reset() +{ + m_poLen = 0U; + m_poPtr = 0U; + m_n = 0U; +} + diff --git a/CWIdTX.h b/CWIdTX.h new file mode 100644 index 0000000..4cda1e8 --- /dev/null +++ b/CWIdTX.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX + * Copyright (C) 2016 by Colin Durbridge G4EML + * Copyright (C) 2017 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(CWIDTX_H) +#define CWIDTX_H + +#include "Config.h" + +class CCWIdTX { +public: + CCWIdTX(); + + void process(); + + uint8_t write(const uint8_t* data, uint8_t length); + + void reset(); + +private: + uint8_t m_poBuffer[300U]; + uint16_t m_poLen; + uint16_t m_poPtr; + uint8_t m_n; +}; + +#endif + diff --git a/Globals.h b/Globals.h index aa7c9c8..0f7659d 100644 --- a/Globals.h +++ b/Globals.h @@ -32,7 +32,10 @@ enum MMDVM_STATE { STATE_DSTAR = 1, STATE_DMR = 2, STATE_YSF = 3, - STATE_P25 = 4 + STATE_P25 = 4, + + // Dummy states start at 90 + STATE_CWID = 97 }; const uint8_t MARK_SLOT1 = 0x08U; @@ -59,6 +62,7 @@ const uint8_t MARK_NONE = 0x00U; #include "YSFTX.h" #include "P25RX.h" #include "P25TX.h" +#include "CWIdTX.h" #include "Debug.h" #include "Utils.h" @@ -68,6 +72,9 @@ const uint16_t RX_RINGBUFFER_SIZE = 1024U; extern MMDVM_STATE m_modemState; extern MMDVM_STATE m_modemState_prev; +extern bool m_cwid_state; +extern uint8_t m_cwIdTXLevel; + extern uint32_t m_modeTimerCnt; extern bool m_dstarEnable; @@ -103,5 +110,7 @@ extern CYSFTX ysfTX; extern CP25RX p25RX; extern CP25TX p25TX; +extern CCWIdTX cwIdTX; + #endif diff --git a/IO.cpp b/IO.cpp index 15f32af..c908316 100644 --- a/IO.cpp +++ b/IO.cpp @@ -127,8 +127,14 @@ void CIO::process() } // Switch off the transmitter if needed - if (m_txBuffer.getData() == 0U && m_tx) + if (m_txBuffer.getData() == 0U && m_tx) { + if(m_cwid_state) { // check for CW ID end of transmission + m_cwid_state = false; + // Restoring previous mode + io.ifConf(m_modemState_prev, true); + } setRX(false); + } if(m_modemState_prev == STATE_DSTAR) scantime = SCAN_TIME; @@ -143,7 +149,7 @@ void CIO::process() if(m_modeTimerCnt >= scantime) { m_modeTimerCnt = 0; - if( (m_modemState == STATE_IDLE) && (m_scanPauseCnt == 0) && m_scanEnable) { + if( (m_modemState == STATE_IDLE) && (m_scanPauseCnt == 0) && m_scanEnable && !m_cwid_state) { m_scanPos = (m_scanPos + 1) % m_TotalModes; #if !defined(QUIET_MODE_LEDS) setMode(m_Modes[m_scanPos]); diff --git a/MMDVM_HS.cpp b/MMDVM_HS.cpp index a8d243e..5aa9086 100644 --- a/MMDVM_HS.cpp +++ b/MMDVM_HS.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Mathis Schmieder DB9MAT * Copyright (C) 2016 by Colin Durbridge G4EML - * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU + * Copyright (C) 2016,2017 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 @@ -29,6 +29,9 @@ MMDVM_STATE m_modemState = STATE_IDLE; MMDVM_STATE m_modemState_prev = STATE_IDLE; +bool m_cwid_state = false; +uint8_t m_cwIdTXLevel = 30; + uint32_t m_modeTimerCnt; bool m_dstarEnable = true; @@ -61,6 +64,8 @@ CYSFTX ysfTX; CP25RX p25RX; CP25TX p25TX; +CCWIdTX cwIdTX; + CSerialPort serial; CIO io; @@ -95,7 +100,9 @@ void loop() if (m_p25Enable && m_modemState == STATE_P25) p25TX.process(); - + + if (m_modemState == STATE_IDLE) + cwIdTX.process(); } int main() diff --git a/MMDVM_HS.ino b/MMDVM_HS.ino index c88c1ec..7e3269c 100644 --- a/MMDVM_HS.ino +++ b/MMDVM_HS.ino @@ -1,7 +1,7 @@ /* * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML - * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU + * Copyright (C) 2016,2017 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 @@ -25,6 +25,9 @@ MMDVM_STATE m_modemState = STATE_IDLE; MMDVM_STATE m_modemState_prev = STATE_IDLE; +bool m_cwid_state = false; +uint8_t m_cwIdTXLevel = 30; + uint32_t m_modeTimerCnt; bool m_dstarEnable = true; @@ -57,6 +60,8 @@ CYSFTX ysfTX; CP25RX p25RX; CP25TX p25TX; +CCWIdTX cwIdTX; + CSerialPort serial; CIO io; @@ -91,4 +96,6 @@ void loop() if (m_p25Enable && m_modemState == STATE_P25) p25TX.process(); + if (m_modemState == STATE_IDLE) + cwIdTX.process(); } diff --git a/SerialPort.cpp b/SerialPort.cpp index d41c67e..e6edfcf 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2013,2015,2016 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML - * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU + * Copyright (C) 2016,2017 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 @@ -227,6 +227,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t dmrDelay = data[7U]; #endif + m_cwIdTXLevel = data[5U]>>2; + m_modemState = modemState; m_dstarEnable = dstarEnable; @@ -321,6 +323,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + cwIdTX.reset(); break; case STATE_DSTAR: DEBUG1("Mode set to D-Star"); @@ -331,6 +334,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrDMORX.reset(); ysfRX.reset(); p25RX.reset(); + cwIdTX.reset(); break; case STATE_YSF: DEBUG1("Mode set to System Fusion"); @@ -341,6 +345,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrDMORX.reset(); dstarRX.reset(); p25RX.reset(); + cwIdTX.reset(); break; case STATE_P25: DEBUG1("Mode set to P25"); @@ -351,6 +356,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrDMORX.reset(); dstarRX.reset(); ysfRX.reset(); + cwIdTX.reset(); break; default: DEBUG1("Mode set to Idle"); @@ -443,6 +449,16 @@ void CSerialPort::process() break; case MMDVM_SEND_CWID: + err = 5U; + if (m_modemState == STATE_IDLE) { + m_cwid_state = true; + io.ifConf(STATE_CWID, true); + err = cwIdTX.write(m_buffer + 3U, m_len - 3U); + } + if (err != 0U) { + DEBUG2("Invalid CW Id data", err); + sendNAK(err); + } break; case MMDVM_DSTAR_HEADER: