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

297
AFSK.cpp
View File

@ -22,57 +22,69 @@ volatile unsigned long lastTx = 0;
volatile unsigned long lastTxEnd = 0;
volatile unsigned long lastRx = 0;
#define T_BIT ((unsigned int)(9600/1200))
#define T_BIT ((unsigned int)(SAMPLERATE/BITRATE))
#ifdef PACKET_PREALLOCATE
SimpleFIFO<AFSK::Packet *,PPOOL_SIZE> preallocPool;
AFSK::Packet preallocPackets[PPOOL_SIZE];
#endif
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
if(bitPosition == 0) {
if(preamble) { // Still in preamble
currentByte = HDLC_PREAMBLE;
--preamble; // Decrement by one
} else {
if(!packet) { // We aren't on a packet, grab one
// Unless we already sent enough
if(maxTx-- == 0) {
stop();
lastTxEnd = millis();
return;
}
packet = pBuf.getPacket();
if(!packet) { // There actually weren't any
stop(); // Stop transmitting and return
lastTxEnd = millis();
return;
}
lastTx = millis();
currentBytePos = 0;
// We're on the start of a byte position, so fetch one
if(bitPosition == 0) {
if(preamble) { // Still in preamble
currentByte = HDLC_PREAMBLE;
--preamble; // Decrement by one
} else {
if(!packet) { // We aren't on a packet, grab one
// Unless we already sent enough
if(maxTx-- == 0) {
stop();
lastTxEnd = millis();
return;
}
// We ran out of actual data, provide an HDLC frame (idle)
if(currentBytePos++ == packet->len) {
pBuf.freePacket(packet);
packet = pBuf.getPacket(); // Get the next, if any
currentBytePos = 0;
currentByte = HDLC_FRAME;
hdlc = true;
packet = pBuf.getPacket();
if(!packet) { // There actually weren't any
stop(); // Stop transmitting and return
lastTxEnd = millis();
return;
}
lastTx = millis();
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)
if(currentBytePos == packet->len && nextByte == 0) {
// We also get here if nextByte isn't set, to handle empty frames
pBuf.freePacket(packet);
packet = pBuf.getPacket(); // Get the next, if any
//packet = NULL;
currentBytePos = 0;
nextByte = 0;
currentByte = HDLC_FRAME;
hdlc = true;
} else {
if(nextByte) {
// We queued up something other than the actual stream to be sent next
currentByte = nextByte;
nextByte = 0;
} else {
// Grab the next byte
currentByte = packet->getByte(); //[currentBytePos++];
if(currentByte == HDLC_ESCAPE) {
currentByte = packet->getByte(); //[currentBytePos++];
hdlc = true;
// 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 {
hdlc = false;
currentBytePos++;
}
hdlc = false; // If we get here, it will be NRZI bit stuffed
}
}
}
}
// Pickup the last bit
currentBit = currentByte & 0x1;
@ -94,15 +106,12 @@ void AFSK::Encoder::process() {
// So, if not a 1, toggle to the opposite tone
if(!currentBit)
currentTone = !currentTone;
}
// Advance the bitclock here, to let first bit be sent early
if(++bitClock == T_BIT)
bitClock = 0;
if(currentTone == 0) {
PORTD |= _BV(7);
dds->setFrequency(AFSK_SPACE);
} else {
PORTD &= ~_BV(7);
dds->setFrequency(AFSK_MARK);
}
}
@ -120,7 +129,7 @@ bool AFSK::Encoder::start() {
currentBit = 0;
lastZero = 0;
bitPosition = 0;
bitClock = 0;
//bitClock = 0;
preamble = 0b110000; // 6.7ms each, 23 = 153ms
done = false;
hdlc = true;
@ -128,6 +137,7 @@ bool AFSK::Encoder::start() {
currentBytePos = 0;
maxTx = 3;
sending = true;
nextByte = 0;
dds->setFrequency(0);
dds->on();
return true;
@ -137,13 +147,14 @@ void AFSK::Encoder::stop() {
randomWait = 0;
sending = false;
done = true;
dds->setFrequency(0);
dds->off();
}
AFSK::Decoder::Decoder() {
// Initialize the sampler delay line (phase shift)
for(unsigned char i = 0; i < SAMPLEPERBIT/2; i++)
delay_fifo.enqueue(0);
//for(unsigned char i = 0; i < SAMPLEPERBIT/2; i++)
// delay_fifo.enqueue(0);
}
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;
if(c == HDLC_ESCAPE) { // We received an escaped byte, mark it
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
}
// Append all the bytes
// This will include unescaped HDLC_FRAME bytes
//if(c == HDLC_FRAME && !escaped)
//currentPacket->append(c); // Framing bytes don't get FCS updates
//else
if(c != HDLC_FRAME)
if(c != HDLC_FRAME || escaped) // Append frame if it is escaped
currentPacket->appendFCS(c); // Escaped characters and all else go into FCS
if(currentPacket->len > PACKET_MAX_LEN) {
@ -376,6 +385,12 @@ AFSK::PacketBuffer::PacketBuffer() {
for(unsigned char i = 0; i < PACKET_BUFFER_SIZE; ++i) {
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 {
@ -421,7 +436,7 @@ void AFSK::Packet::init(unsigned short dlen) {
ready = 0;
#ifdef PACKET_PREALLOCATE
freeData = 0;
maxLen = 128; // Put it here instead
maxLen = PACKET_MAX_LEN; // Put it here instead
#else
freeData = 1;
dataPtr = (uint8_t *)malloc(dlen+16);
@ -440,7 +455,9 @@ AFSK::Packet *AFSK::PacketBuffer::makePacket(unsigned short dlen) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
//Packet *p = findPooledPacket();
#ifdef PACKET_PREALLOCATE
p = preallocPool.dequeue();
if(preallocPool.count())
p = preallocPool.dequeue();
else p = NULL;
#else
p = new Packet(); //(Packet *)malloc(sizeof(Packet));
#endif
@ -518,8 +535,163 @@ size_t AFSK::Packet::appendCallsign(const char *callsign, uint8_t ssid, bool fin
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;
#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
for(i=7; i<13; i++) {
s->write(*(dataPtr+i)>>1);
@ -527,7 +699,7 @@ void AFSK::Packet::print(Stream *s) {
// SSID
s->write('-');
s->print((*(dataPtr+13) >> 1) & 0xF);
s->print(" -> ");
s->print(F(" -> "));
// First 6 bytes are destination callsign
for(i=0; i<6; i++) {
s->write(*(dataPtr+i)>>1);
@ -540,14 +712,23 @@ void AFSK::Packet::print(Stream *s) {
for(i = 15; i<len; i++) {
s->write(*(dataPtr+i));
}
#endif
}
// Determine what we want to do on this ADC tick.
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();
else
tcnt = 0;
PORTD &= ~_BV(6);
} else {
decoder.process(((int8_t)(ADCH - 128)));
}
}
void AFSK::start(DDS *dds) {

59
AFSK.h
View File

@ -15,15 +15,19 @@
#define PACKET_BUFFER_SIZE 2
#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
// This will use more RAM, but can make it easier to do memory planning.
// 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
// Two more bytes are added for HDLC_ESCAPEs
#define PACKET_MAX_LEN 512
#define AX25_PACKET_HEADER_MINLEN 22
// This is with all the digis, two addresses, and full payload
// Dst(7) + Src(7) + Digis(56) + Ctl(1) + PID(1) + Data(0-256) + FCS(2)
#define PACKET_MAX_LEN 330
// Minimum is Dst + Src + Ctl + FCS
#define AX25_PACKET_HEADER_MINLEN 17
// HDLC framing bits
#define HDLC_FRAME 0x7E
@ -65,9 +69,11 @@ public:
}
inline void start() {
fcs = 0xffff;
*dataPos++ = HDLC_ESCAPE;
*dataPos++ = HDLC_FRAME;
len = 2;
// No longer put an explicit frame start here
//*dataPos++ = HDLC_ESCAPE;
//*dataPos++ = HDLC_FRAME;
//len = 2;
len = 0;
}
inline bool append(char c) {
@ -96,8 +102,9 @@ public:
inline void finish() {
append(~(fcs & 0xff));
append(~((fcs>>8) & 0xff));
append(HDLC_ESCAPE);
append(HDLC_FRAME);
// No longer append the frame boundaries themselves
//append(HDLC_ESCAPE);
//append(HDLC_FRAME);
ready = 1;
}
@ -111,13 +118,27 @@ public:
inline bool crcOK() {
return (fcs == 0xF0B8);
}
void print(Stream *s);
#ifdef PACKET_PARSER
bool parsePacket();
#endif
void printPacket(Stream *s);
private:
#ifdef PACKET_PREALLOCATE
uint8_t dataPtr[128];
uint8_t dataPtr[PACKET_MAX_LEN]; // 256 byte I frame + headers max of 78
#else
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
uint8_t *dataPos, *readPos;
unsigned short fcs;
@ -156,6 +177,7 @@ public:
done = true;
packet = 0x0;
currentBytePos = 0;
nextByte = 0;
}
void setDDS(DDS *d) { dds = d; }
volatile inline bool isSending() volatile {
@ -184,12 +206,13 @@ public:
byte lastZero : 3;
byte bitPosition : 3;
byte preamble : 6;
byte bitClock;
//byte bitClock;
bool hdlc;
byte nextByte;
byte maxTx;
Packet *packet;
PacketBuffer pBuf;
unsigned char currentBytePos;
unsigned int currentBytePos;
volatile unsigned long randomWait;
volatile bool done;
DDS *dds;
@ -228,7 +251,7 @@ public:
}
private:
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
int16_t iir_x[2];
int16_t iir_y[2];
@ -243,12 +266,12 @@ public:
inline bool read() {
return decoder.read();
}
inline bool txReady() volatile {
volatile inline bool txReady() volatile {
if(encoder.isDone() && encoder.hasPackets())
return true;
return false;
}
inline bool isDone() volatile { return encoder.isDone(); }
volatile inline bool isDone() volatile { return encoder.isDone(); }
inline bool txStart() {
if(decoder.isReceiving()) {
encoder.setRandomWait();