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:
parent
4f1c863487
commit
45ec01bd31
237
AFSK.cpp
237
AFSK.cpp
|
@ -22,15 +22,14 @@ 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
|
||||
|
@ -52,23 +51,36 @@ void AFSK::Encoder::process() {
|
|||
}
|
||||
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) {
|
||||
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 {
|
||||
// Grab the next byte
|
||||
currentByte = packet->getByte(); //[currentBytePos++];
|
||||
if(currentByte == HDLC_ESCAPE) {
|
||||
currentByte = packet->getByte(); //[currentBytePos++];
|
||||
hdlc = true;
|
||||
if(nextByte) {
|
||||
// We queued up something other than the actual stream to be sent next
|
||||
currentByte = nextByte;
|
||||
nextByte = 0;
|
||||
} 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
|
||||
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
|
||||
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
59
AFSK.h
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue