diff --git a/AX25Defines.h b/AX25Defines.h new file mode 100644 index 0000000..03424b2 --- /dev/null +++ b/AX25Defines.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(AX25DEFINES_H) +#define AX25DEFINES_H + +const uint8_t AX25_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample rate + +const uint8_t AX25_FRAME_START = 0x7EU; +const uint8_t AX25_FRAME_END = 0x7EU; +const uint8_t AX25_FRAME_ABORT = 0xFEU; + +const uint8_t AX25_MAX_ONES = 5U; + +const uint16_t AX25_MIN_FRAME_LENGTH = 17U; // Callsign (7) + Callsign (7) + Control (1) + Checksum (2) + +const uint16_t AX25_MAX_FRAME_LENGTH = 294U; // Callsign (7) + Callsign (7) + 3 Digipeaters (21) + + // Control (1) + Data (256) + Checksum (2) + +#endif + diff --git a/AX25Demodulator.cpp b/AX25Demodulator.cpp index 7ffb99c..b59dc6b 100644 --- a/AX25Demodulator.cpp +++ b/AX25Demodulator.cpp @@ -20,6 +20,7 @@ #include "Config.h" #include "Globals.h" #include "AX25Demodulator.h" +#include "AX25Defines.h" const float32_t SAMPLE_RATE = 24000.0F; const float32_t SYMBOL_RATE = 1200.0F; @@ -178,7 +179,7 @@ bool CAX25Demodulator::PLL(bool input) bool CAX25Demodulator::HDLC(bool b) { - if (m_hdlcOnes == 5U) { + if (m_hdlcOnes == AX25_MAX_ONES) { if (b) { // flag byte m_hdlcFlag = true; @@ -203,8 +204,8 @@ bool CAX25Demodulator::HDLC(bool b) bool result = false; switch (m_hdlcBuffer) { - case 0x7E: - if (m_frame.m_length >= 17U) { + case AX25_FRAME_END: + if (m_frame.m_length >= AX25_MIN_FRAME_LENGTH) { result = m_frame.checkCRC(); if (!result) m_frame.m_length = 0U; @@ -216,7 +217,7 @@ bool CAX25Demodulator::HDLC(bool b) m_hdlcBits = 0U; break; - case 0xFE: + case AX25_FRAME_ABORT: // Frame aborted m_frame.m_length = 0U; m_hdlcState = AX25_IDLE; diff --git a/AX25Frame.cpp b/AX25Frame.cpp index 905682e..d666f33 100644 --- a/AX25Frame.cpp +++ b/AX25Frame.cpp @@ -54,6 +54,15 @@ const uint16_t CCITT_TABLE[] = { 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330, 0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78 }; +CAX25Frame::CAX25Frame(const uint8_t* data, uint16_t length) : +m_data(), +m_length(0U), +m_fcs(0U) +{ + for (uint16_t i = 0U; i < length && i < (AX25_MAX_PACKET_LEN - 2U); i++) + m_data[m_length++] = data[i]; +} + CAX25Frame::CAX25Frame() : m_data(), m_length(0U), @@ -92,3 +101,22 @@ bool CAX25Frame::checkCRC() } } +void CAX25Frame::addCRC() +{ + union { + uint16_t crc16; + uint8_t crc8[2U]; + }; + + crc16 = 0xFFFFU; + for (uint16_t i = 0U; i < m_length; i++) + crc16 = uint16_t(crc8[1U]) ^ CCITT_TABLE[crc8[0U] ^ m_data[i]]; + + crc16 = ~crc16; + + m_fcs = crc16; + + m_data[m_length++] = crc8[0U]; + m_data[m_length++] = crc8[1U]; +} + diff --git a/AX25Frame.h b/AX25Frame.h index eaf770c..7f79c28 100644 --- a/AX25Frame.h +++ b/AX25Frame.h @@ -25,12 +25,15 @@ const uint16_t AX25_MAX_PACKET_LEN = 300U; class CAX25Frame { public: + CAX25Frame(const uint8_t* data, uint16_t length); CAX25Frame(); bool append(uint16_t c); bool checkCRC(); + void addCRC(); + uint8_t m_data[AX25_MAX_PACKET_LEN]; uint16_t m_length; uint16_t m_fcs; diff --git a/AX25TX.cpp b/AX25TX.cpp index 6f9ed5e..8658fb4 100644 --- a/AX25TX.cpp +++ b/AX25TX.cpp @@ -20,30 +20,160 @@ #include "Globals.h" #include "AX25TX.h" +#include "AX25Defines.h" +#include "AX25Frame.h" -CAX25TX::CAX25TX() + +const uint8_t START_FLAG[] = { AX25_FRAME_START }; +const uint8_t END_FLAG[] = { AX25_FRAME_END }; + +const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7]) + +const uint8_t BIT_MASK_TABLE2[] = { 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U }; + +#define WRITE_BIT2(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE2[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE2[(i)&7]) +#define READ_BIT2(p,i) (p[(i)>>3] & BIT_MASK_TABLE2[(i)&7]) + +const uint16_t AUDIO_TABLE_LEN = 120U; + +const q15_t AUDIO_TABLE_DATA[] = { + 0, 214, 428, 641, 851, 1060, 1265, 1468, 1666, 1859, 2048, 2230, 2407, 2577, 2740, 2896, 3043, 3182, 3313, 3434, 3546, 3649, + 3741, 3823, 3895, 3955, 4006, 4045, 4073, 4089, 4095, 4089, 4073, 4045, 4006, 3955, 3895, 3823, 3741, 3649, 3546, 3434, 3313, + 3182, 3043, 2896, 2740, 2577, 2407, 2230, 2048, 1859, 1666, 1468, 1265, 1060, 851, 641, 428, 214, 0, -214, -428, -641, -851, + -1060, -1265, -1468, -1666, -1859, -2047, -2230, -2407, -2577, -2740, -2896, -3043, -3182, -3313, -3434, -3546, -3649, -3741, + -3823, -3895, -3955, -4006, -4045, -4073, -4089, -4095, -4089, -4073, -4045, -4006, -3955, -3895, -3823, -3741, -3649, -3546, + -3434, -3313, -3182, -3043, -2896, -2740, -2577, -2407, -2230, -2047, -1859, -1666, -1468, -1265, -1060, -851, -641, -428, -214 +}; + +CAX25TX::CAX25TX() : +m_twist(-6), +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_txDelay(120U), +m_tablePtr(0U), +m_nrzi(false) { } void CAX25TX::process() { + if (m_poLen == 0U) + return; + + uint16_t space = io.getSpace(); + + while (space > AX25_RADIO_SYMBOL_LENGTH) { + bool b = READ_BIT1(m_poBuffer, m_poPtr) != 0U; + m_poPtr++; + + writeBit(b); + + space -= AX25_RADIO_SYMBOL_LENGTH; + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + return; + } + } } uint8_t CAX25TX::writeData(const uint8_t* data, uint16_t length) { + CAX25Frame frame(data, length); + frame.addCRC(); + + m_poLen = 0U; + m_poPtr = 0U; + m_nrzi = false; + m_tablePtr = 0U; + + // Add TX delay (already NRZI) + for (uint16_t i = 0U; i < m_txDelay; i++, m_poLen++) + WRITE_BIT1(m_poBuffer, m_poLen, false); + + // Add the Start Flag + for (uint16_t i = 0U; i < 8U; i++, m_poLen++) { + bool b1 = READ_BIT1(START_FLAG, i) != 0U; + bool b2 = NRZI(b1); + WRITE_BIT1(m_poBuffer, m_poLen, b2); + } + + uint8_t ones = 0U; + for (uint16_t i = 0U; i < (frame.m_length * 8U); i++) { + bool b1 = READ_BIT2(START_FLAG, i) != 0U; + bool b2 = NRZI(b1); + WRITE_BIT1(m_poBuffer, m_poLen, b2); + m_poLen++; + + if (b1) { + ones++; + if (ones == AX25_MAX_ONES) { + bool b = NRZI(false); + WRITE_BIT1(m_poBuffer, m_poLen, b); + m_poLen++; + ones = 0U; + } + } else { + ones = 0U; + } + } + + // Add the End Flag + for (uint16_t i = 0U; i < 8U; i++, m_poLen++) { + bool b1 = READ_BIT1(END_FLAG, i) != 0U; + bool b2 = NRZI(b1); + WRITE_BIT1(m_poBuffer, m_poLen, b2); + } + return 0U; } +void CAX25TX::writeBit(bool b) +{ + q15_t in[AX25_RADIO_SYMBOL_LENGTH]; + for (uint8_t i = 0U; i < AX25_RADIO_SYMBOL_LENGTH; i++) { + in[i] = AUDIO_TABLE_DATA[m_tablePtr]; + if (b) + m_tablePtr += 6U; + else + m_tablePtr += 11U; + + if (m_tablePtr >= AUDIO_TABLE_LEN) + m_tablePtr -= AUDIO_TABLE_LEN; + } + + q15_t out[AX25_RADIO_SYMBOL_LENGTH]; + m_twist.process(in, out, AX25_RADIO_SYMBOL_LENGTH); + + io.write(STATE_AX25, out, AX25_RADIO_SYMBOL_LENGTH); +} + void CAX25TX::setParams(int8_t twist) { + m_twist.setTwist(twist); } void CAX25TX::setTXDelay(uint8_t delay) { + m_txDelay = delay * 12U; } uint8_t CAX25TX::getSpace() const { - return 0U; + return m_poLen == 0U ? 255U : 0U; +} + +bool CAX25TX::NRZI(bool b) +{ + bool result = (b == m_nrzi); + + m_nrzi = b; + + return result; } diff --git a/AX25TX.h b/AX25TX.h index d89f77c..e77c500 100644 --- a/AX25TX.h +++ b/AX25TX.h @@ -21,7 +21,7 @@ #include "Config.h" -#include "SerialRB.h" +#include "AX25Twist.h" class CAX25TX { public: @@ -38,6 +38,16 @@ public: uint8_t getSpace() const; private: + CAX25Twist m_twist; + uint8_t m_poBuffer[560U]; + uint16_t m_poLen; + uint16_t m_poPtr; + uint16_t m_txDelay; + uint16_t m_tablePtr; + bool m_nrzi; + + void writeBit(bool b); + bool NRZI(bool b); }; #endif