Rewrite of framing to always be pure frames, no escapes.

Added parser for packets to pretty print them to serial.
Added parser for easier packet handling on receipt.
Moved bitclock timer to outside ISR loop.
This commit is contained in:
Stephen Olesen 2015-07-13 18:53:18 -06:00
parent 4f1c863487
commit 45ec01bd31
2 changed files with 280 additions and 76 deletions

237
AFSK.cpp
View File

@ -22,15 +22,14 @@ volatile unsigned long lastTx = 0;
volatile unsigned long lastTxEnd = 0; volatile unsigned long lastTxEnd = 0;
volatile unsigned long lastRx = 0; volatile unsigned long lastRx = 0;
#define T_BIT ((unsigned int)(9600/1200)) #define T_BIT ((unsigned int)(SAMPLERATE/BITRATE))
#ifdef PACKET_PREALLOCATE #ifdef PACKET_PREALLOCATE
SimpleFIFO<AFSK::Packet *,PPOOL_SIZE> preallocPool; SimpleFIFO<AFSK::Packet *,PPOOL_SIZE> preallocPool;
AFSK::Packet preallocPackets[PPOOL_SIZE];
#endif #endif
void AFSK::Encoder::process() { void AFSK::Encoder::process() {
// Check what clock pulse we're on
if(bitClock == 0) { // We are onto our next bit timing
// We're on the start of a byte position, so fetch one // We're on the start of a byte position, so fetch one
if(bitPosition == 0) { if(bitPosition == 0) {
if(preamble) { // Still in preamble if(preamble) { // Still in preamble
@ -52,23 +51,36 @@ void AFSK::Encoder::process() {
} }
lastTx = millis(); lastTx = millis();
currentBytePos = 0; currentBytePos = 0;
nextByte = HDLC_FRAME; // Our next output should be a frame boundary
hdlc = true;
} }
// We ran out of actual data, provide an HDLC frame (idle) // We ran out of actual data, provide an HDLC frame (idle)
if(currentBytePos++ == packet->len) { if(currentBytePos == packet->len && nextByte == 0) {
// We also get here if nextByte isn't set, to handle empty frames
pBuf.freePacket(packet); pBuf.freePacket(packet);
packet = pBuf.getPacket(); // Get the next, if any packet = pBuf.getPacket(); // Get the next, if any
//packet = NULL;
currentBytePos = 0; currentBytePos = 0;
nextByte = 0;
currentByte = HDLC_FRAME; currentByte = HDLC_FRAME;
hdlc = true; hdlc = true;
} else { } else {
// Grab the next byte if(nextByte) {
currentByte = packet->getByte(); //[currentBytePos++]; // We queued up something other than the actual stream to be sent next
if(currentByte == HDLC_ESCAPE) { currentByte = nextByte;
currentByte = packet->getByte(); //[currentBytePos++]; nextByte = 0;
hdlc = true;
} else { } else {
hdlc = false; // Get the next byte to send, but if it's an HDLC frame, escape it
// and queue the real byte for the next cycle.
currentByte = packet->getByte();
if(currentByte == HDLC_FRAME) {
nextByte = currentByte;
currentByte = HDLC_ESCAPE;
} else {
currentBytePos++;
}
hdlc = false; // If we get here, it will be NRZI bit stuffed
} }
} }
} }
@ -94,15 +106,12 @@ void AFSK::Encoder::process() {
// So, if not a 1, toggle to the opposite tone // So, if not a 1, toggle to the opposite tone
if(!currentBit) if(!currentBit)
currentTone = !currentTone; currentTone = !currentTone;
}
// Advance the bitclock here, to let first bit be sent early
if(++bitClock == T_BIT)
bitClock = 0;
if(currentTone == 0) { if(currentTone == 0) {
PORTD |= _BV(7);
dds->setFrequency(AFSK_SPACE); dds->setFrequency(AFSK_SPACE);
} else { } else {
PORTD &= ~_BV(7);
dds->setFrequency(AFSK_MARK); dds->setFrequency(AFSK_MARK);
} }
} }
@ -120,7 +129,7 @@ bool AFSK::Encoder::start() {
currentBit = 0; currentBit = 0;
lastZero = 0; lastZero = 0;
bitPosition = 0; bitPosition = 0;
bitClock = 0; //bitClock = 0;
preamble = 0b110000; // 6.7ms each, 23 = 153ms preamble = 0b110000; // 6.7ms each, 23 = 153ms
done = false; done = false;
hdlc = true; hdlc = true;
@ -128,6 +137,7 @@ bool AFSK::Encoder::start() {
currentBytePos = 0; currentBytePos = 0;
maxTx = 3; maxTx = 3;
sending = true; sending = true;
nextByte = 0;
dds->setFrequency(0); dds->setFrequency(0);
dds->on(); dds->on();
return true; return true;
@ -137,13 +147,14 @@ void AFSK::Encoder::stop() {
randomWait = 0; randomWait = 0;
sending = false; sending = false;
done = true; done = true;
dds->setFrequency(0);
dds->off(); dds->off();
} }
AFSK::Decoder::Decoder() { AFSK::Decoder::Decoder() {
// Initialize the sampler delay line (phase shift) // Initialize the sampler delay line (phase shift)
for(unsigned char i = 0; i < SAMPLEPERBIT/2; i++) //for(unsigned char i = 0; i < SAMPLEPERBIT/2; i++)
delay_fifo.enqueue(0); // delay_fifo.enqueue(0);
} }
bool AFSK::HDLCDecode::hdlcParse(bool bit, SimpleFIFO<uint8_t,HAMSHIELD_AFSK_RX_FIFO_LEN> *fifo) { bool AFSK::HDLCDecode::hdlcParse(bool bit, SimpleFIFO<uint8_t,HAMSHIELD_AFSK_RX_FIFO_LEN> *fifo) {
@ -271,16 +282,14 @@ bool AFSK::Decoder::read() {
bool escaped = false; bool escaped = false;
if(c == HDLC_ESCAPE) { // We received an escaped byte, mark it if(c == HDLC_ESCAPE) { // We received an escaped byte, mark it
escaped = true; escaped = true;
currentPacket->append(HDLC_ESCAPE); // Append without FCS // Do we want to keep HDLC_ESCAPEs in the packet?
//currentPacket->append(HDLC_ESCAPE); // Append without FCS
c = rx_fifo.dequeue(); // Reset to the next character c = rx_fifo.dequeue(); // Reset to the next character
} }
// Append all the bytes // Append all the bytes
// This will include unescaped HDLC_FRAME bytes // This will include unescaped HDLC_FRAME bytes
//if(c == HDLC_FRAME && !escaped) if(c != HDLC_FRAME || escaped) // Append frame if it is escaped
//currentPacket->append(c); // Framing bytes don't get FCS updates
//else
if(c != HDLC_FRAME)
currentPacket->appendFCS(c); // Escaped characters and all else go into FCS currentPacket->appendFCS(c); // Escaped characters and all else go into FCS
if(currentPacket->len > PACKET_MAX_LEN) { if(currentPacket->len > PACKET_MAX_LEN) {
@ -376,6 +385,12 @@ AFSK::PacketBuffer::PacketBuffer() {
for(unsigned char i = 0; i < PACKET_BUFFER_SIZE; ++i) { for(unsigned char i = 0; i < PACKET_BUFFER_SIZE; ++i) {
packets[i] = 0x0; packets[i] = 0x0;
} }
#ifdef PACKET_PREALLOCATE
for(unsigned char i = 0; i < PPOOL_SIZE; ++i) {
// Put some empty packets in the FIFO
preallocPool.enqueue(&preallocPackets[i]);
}
#endif
} }
unsigned char AFSK::PacketBuffer::readyCount() volatile { unsigned char AFSK::PacketBuffer::readyCount() volatile {
@ -421,7 +436,7 @@ void AFSK::Packet::init(unsigned short dlen) {
ready = 0; ready = 0;
#ifdef PACKET_PREALLOCATE #ifdef PACKET_PREALLOCATE
freeData = 0; freeData = 0;
maxLen = 128; // Put it here instead maxLen = PACKET_MAX_LEN; // Put it here instead
#else #else
freeData = 1; freeData = 1;
dataPtr = (uint8_t *)malloc(dlen+16); dataPtr = (uint8_t *)malloc(dlen+16);
@ -440,7 +455,9 @@ AFSK::Packet *AFSK::PacketBuffer::makePacket(unsigned short dlen) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
//Packet *p = findPooledPacket(); //Packet *p = findPooledPacket();
#ifdef PACKET_PREALLOCATE #ifdef PACKET_PREALLOCATE
if(preallocPool.count())
p = preallocPool.dequeue(); p = preallocPool.dequeue();
else p = NULL;
#else #else
p = new Packet(); //(Packet *)malloc(sizeof(Packet)); p = new Packet(); //(Packet *)malloc(sizeof(Packet));
#endif #endif
@ -518,8 +535,163 @@ size_t AFSK::Packet::appendCallsign(const char *callsign, uint8_t ssid, bool fin
appendFCS(ssidField); appendFCS(ssidField);
} }
void AFSK::Packet::print(Stream *s) { #ifdef PACKET_PARSER
// Process the AX25 frame and turn it into a bunch of useful strings
bool AFSK::Packet::parsePacket() {
uint8_t *d = dataPtr;
int i;
// First 7 bytes are destination-ssid
for(i = 0; i < 6; i++) {
dstCallsign[i] = (*d++)>>1;
if(dstCallsign[i] == ' ') {
dstCallsign[i] = '\0';
}
}
dstCallsign[6] = '\0';
dstSSID = ((*d++)>>1) & 0xF;
// Next 7 bytes are source-ssid
for(i = 0; i < 6; i++) {
srcCallsign[i] = (*d++)>>1;
if(srcCallsign[i] == ' ') {
srcCallsign[i] = '\0';
}
}
srcCallsign[6] = '\0';
srcSSID = *d++; // Don't shift yet, we need the LSB
digipeater[0][0] = '\0'; // Set null in case we have none anyway
if((srcSSID & 1) == 0) { // Not the last address field
int digi; // Which digi we're on
for(digi = 0; digi < 8; digi++) {
for(i = 0; i < 6; i++) {
digipeater[digi][i] = (*d++)>>1;
if(digipeater[digi][i] == ' ') {
digipeater[digi][i] = '\0';
}
}
uint8_t last = (*d) & 1;
digipeaterSSID[digi] = ((*d++)>>1) & 0xF;
if(last == 1)
break;
}
digipeater[digi][6] = '\0';
for(digi += 1; digi<8; digi++) { // Empty out the rest of them
digipeater[digi][0] = '\0';
}
}
// Now handle the SSID itself
srcSSID >>= 1;
srcSSID &= 0xF;
// After the address parsing, we end up on the control field
control = *d++;
// We have a PID if control type is U or I
// Control & 1 == 0 == I frame
// Control & 3 == 3 == U frame
if((control & 1) == 0 || (control & 3) == 3)
pid = *d++;
else pid = 0;
// If there is no PID, we have no data
if(!pid) {
iFrameData = NULL;
return true;
}
// At this point, we've walked far enough along that data is just at d
iFrameData = d;
// Cheat a little by setting the first byte of the FCS to 0, making it a string
// First FCS byte is found at -2, HDLC flags aren't in this buffer
dataPtr[len-2] = '\0';
return true;
}
#endif
void AFSK::Packet::printPacket(Stream *s) {
uint8_t i; uint8_t i;
#ifdef PACKET_PARSER
if(!parsePacket()) {
s->print(F("Packet not valid"));
return;
}
s->print(srcCallsign);
if(srcSSID > 0) {
s->write('-');
s->print(srcSSID);
}
s->print(F(" > "));
s->print(dstCallsign);
if(dstSSID > 0) {
s->write('-');
s->print(dstSSID);
}
s->write(' ');
if(digipeater[0][0] != '\0') {
s->print(F("via "));
for(i = 0; i < 8; i++) {
if(digipeater[i][0] == '\0')
break;
s->print(digipeater[i]);
if(digipeaterSSID[i] != 0) {
s->write('-');
s->print(digipeaterSSID[i]);
}
if((digipeaterSSID[i] & _BV(7)) == _BV(7)) {
s->write('*'); // Digipeated already
}
// If we might have more, check to add a comma
if(i < 7 && digipeater[i+1][0] != '\0') {
s->write(',');
}
s->write(' ');
}
}
// This is an S frame, we can only print control info
if(control & 3 == 1) {
switch((control>>2)&3) {
case 0:
s->print(F("RR"));
break;
case 1:
s->print(F("RNR"));
break;
case 2:
s->print(F("REJ"));
break;
case 3: // Undefined
s->print(F("unk"));
break;
}
// Use a + to indicate poll bit
if(control & _BV(4) == _BV(4)) {
s->write('+');
}
} else if((control & 3) == 3) { // U Frame
s->print(F("U("));
s->print(control, HEX);
s->write(',');
s->print(pid, HEX);
s->print(F(") "));
} else if((control & 1) == 0) { // I Frame
s->print(F("I("));
s->print(control, HEX);
s->write(',');
s->print(pid, HEX);
s->print(F(") "));
}
s->print(F("len "));
s->print(len);
s->print(F(": "));
s->print((char *)iFrameData);
s->println();
#else // no packet parser, do a rudimentary print
// Second 6 bytes are source callsign // Second 6 bytes are source callsign
for(i=7; i<13; i++) { for(i=7; i<13; i++) {
s->write(*(dataPtr+i)>>1); s->write(*(dataPtr+i)>>1);
@ -527,7 +699,7 @@ void AFSK::Packet::print(Stream *s) {
// SSID // SSID
s->write('-'); s->write('-');
s->print((*(dataPtr+13) >> 1) & 0xF); s->print((*(dataPtr+13) >> 1) & 0xF);
s->print(" -> "); s->print(F(" -> "));
// First 6 bytes are destination callsign // First 6 bytes are destination callsign
for(i=0; i<6; i++) { for(i=0; i<6; i++) {
s->write(*(dataPtr+i)>>1); s->write(*(dataPtr+i)>>1);
@ -540,14 +712,23 @@ void AFSK::Packet::print(Stream *s) {
for(i = 15; i<len; i++) { for(i = 15; i<len; i++) {
s->write(*(dataPtr+i)); s->write(*(dataPtr+i));
} }
#endif
} }
// Determine what we want to do on this ADC tick. // Determine what we want to do on this ADC tick.
void AFSK::timer() { void AFSK::timer() {
if(encoder.isSending()) static uint8_t tcnt = 0;
if(++tcnt == T_BIT && encoder.isSending()) {
PORTD |= _BV(6);
// Only run the encoder every 8th tick
// This is actually DDS RefClk / 1200 = 8, set as T_BIT
// A different refclk needs a different value
encoder.process(); encoder.process();
else tcnt = 0;
PORTD &= ~_BV(6);
} else {
decoder.process(((int8_t)(ADCH - 128))); decoder.process(((int8_t)(ADCH - 128)));
}
} }
void AFSK::start(DDS *dds) { void AFSK::start(DDS *dds) {

59
AFSK.h
View File

@ -15,15 +15,19 @@
#define PACKET_BUFFER_SIZE 2 #define PACKET_BUFFER_SIZE 2
#define PACKET_STATIC 0 #define PACKET_STATIC 0
// Enable the packet parser which will tokenize the AX25 frame into easy strings
#define PACKET_PARSER
// If this is set, all the packet buffers will be pre-allocated at compile time // If this is set, all the packet buffers will be pre-allocated at compile time
// This will use more RAM, but can make it easier to do memory planning. // This will use more RAM, but can make it easier to do memory planning.
// TODO: Make this actually work right and not crash. // TODO: Make this actually work right and not crash.
//#define PACKET_PREALLOCATE #define PACKET_PREALLOCATE
// This is with all the digis, two addresses, framing and full payload // This is with all the digis, two addresses, and full payload
// Two more bytes are added for HDLC_ESCAPEs // Dst(7) + Src(7) + Digis(56) + Ctl(1) + PID(1) + Data(0-256) + FCS(2)
#define PACKET_MAX_LEN 512 #define PACKET_MAX_LEN 330
#define AX25_PACKET_HEADER_MINLEN 22 // Minimum is Dst + Src + Ctl + FCS
#define AX25_PACKET_HEADER_MINLEN 17
// HDLC framing bits // HDLC framing bits
#define HDLC_FRAME 0x7E #define HDLC_FRAME 0x7E
@ -65,9 +69,11 @@ public:
} }
inline void start() { inline void start() {
fcs = 0xffff; fcs = 0xffff;
*dataPos++ = HDLC_ESCAPE; // No longer put an explicit frame start here
*dataPos++ = HDLC_FRAME; //*dataPos++ = HDLC_ESCAPE;
len = 2; //*dataPos++ = HDLC_FRAME;
//len = 2;
len = 0;
} }
inline bool append(char c) { inline bool append(char c) {
@ -96,8 +102,9 @@ public:
inline void finish() { inline void finish() {
append(~(fcs & 0xff)); append(~(fcs & 0xff));
append(~((fcs>>8) & 0xff)); append(~((fcs>>8) & 0xff));
append(HDLC_ESCAPE); // No longer append the frame boundaries themselves
append(HDLC_FRAME); //append(HDLC_ESCAPE);
//append(HDLC_FRAME);
ready = 1; ready = 1;
} }
@ -111,13 +118,27 @@ public:
inline bool crcOK() { inline bool crcOK() {
return (fcs == 0xF0B8); return (fcs == 0xF0B8);
} }
#ifdef PACKET_PARSER
void print(Stream *s); bool parsePacket();
#endif
void printPacket(Stream *s);
private: private:
#ifdef PACKET_PREALLOCATE #ifdef PACKET_PREALLOCATE
uint8_t dataPtr[128]; uint8_t dataPtr[PACKET_MAX_LEN]; // 256 byte I frame + headers max of 78
#else #else
uint8_t *dataPtr; uint8_t *dataPtr;
#endif
#ifdef PACKET_PARSER
char srcCallsign[7];
uint8_t srcSSID;
char dstCallsign[7];
uint8_t dstSSID;
char digipeater[8][7];
uint8_t digipeaterSSID[8];
uint8_t *iFrameData;
uint8_t length;
uint8_t control;
uint8_t pid;
#endif #endif
uint8_t *dataPos, *readPos; uint8_t *dataPos, *readPos;
unsigned short fcs; unsigned short fcs;
@ -156,6 +177,7 @@ public:
done = true; done = true;
packet = 0x0; packet = 0x0;
currentBytePos = 0; currentBytePos = 0;
nextByte = 0;
} }
void setDDS(DDS *d) { dds = d; } void setDDS(DDS *d) { dds = d; }
volatile inline bool isSending() volatile { volatile inline bool isSending() volatile {
@ -184,12 +206,13 @@ public:
byte lastZero : 3; byte lastZero : 3;
byte bitPosition : 3; byte bitPosition : 3;
byte preamble : 6; byte preamble : 6;
byte bitClock; //byte bitClock;
bool hdlc; bool hdlc;
byte nextByte;
byte maxTx; byte maxTx;
Packet *packet; Packet *packet;
PacketBuffer pBuf; PacketBuffer pBuf;
unsigned char currentBytePos; unsigned int currentBytePos;
volatile unsigned long randomWait; volatile unsigned long randomWait;
volatile bool done; volatile bool done;
DDS *dds; DDS *dds;
@ -228,7 +251,7 @@ public:
} }
private: private:
Packet *currentPacket; Packet *currentPacket;
SimpleFIFO<int8_t,SAMPLEPERBIT/2+1> delay_fifo; //SimpleFIFO<int8_t,SAMPLEPERBIT/2+1> delay_fifo;
SimpleFIFO<uint8_t,RX_FIFO_LEN> rx_fifo; // This should be drained fairly often SimpleFIFO<uint8_t,RX_FIFO_LEN> rx_fifo; // This should be drained fairly often
int16_t iir_x[2]; int16_t iir_x[2];
int16_t iir_y[2]; int16_t iir_y[2];
@ -243,12 +266,12 @@ public:
inline bool read() { inline bool read() {
return decoder.read(); return decoder.read();
} }
inline bool txReady() volatile { volatile inline bool txReady() volatile {
if(encoder.isDone() && encoder.hasPackets()) if(encoder.isDone() && encoder.hasPackets())
return true; return true;
return false; return false;
} }
inline bool isDone() volatile { return encoder.isDone(); } volatile inline bool isDone() volatile { return encoder.isDone(); }
inline bool txStart() { inline bool txStart() {
if(decoder.isReceiving()) { if(decoder.isReceiving()) {
encoder.setRandomWait(); encoder.setRandomWait();