diff --git a/.cproject b/.cproject
index b0d130b..32b6693 100644
--- a/.cproject
+++ b/.cproject
@@ -52,7 +52,7 @@
-
+
@@ -133,16 +133,16 @@
-
-
-
-
+
+
+
-
-
-
+
+
+
@@ -160,14 +160,15 @@
-
+
@@ -192,10 +193,10 @@
-
-
-
-
+
+
+
+
@@ -215,17 +216,18 @@
-
+
-
-
+
+
+
@@ -252,4 +254,5 @@
+
diff --git a/Inc/ax25.h b/Inc/ax25.h
index 45ea226..c5c24e2 100644
--- a/Inc/ax25.h
+++ b/Inc/ax25.h
@@ -1,4 +1,6 @@
/*
+Copyright 2020-2023 Piotr Wilkon
+
This file is part of VP-Digi.
VP-Digi is free software: you can redistribute it and/or modify
@@ -21,11 +23,17 @@ along with VP-Digi. If not, see .
#include
#include
+#define AX25_NOT_FX25 255
+
enum Ax25RxStage
{
RX_STAGE_IDLE = 0,
RX_STAGE_FLAG,
RX_STAGE_FRAME,
+#ifdef ENABLE_FX25
+ RX_STAGE_FX25_TAG,
+ RX_STAGE_FX25_FRAME,
+#endif
};
struct Ax25ProtoConfig
@@ -35,6 +43,7 @@ struct Ax25ProtoConfig
uint16_t quietTime; //Quiet time in ms
uint8_t allowNonAprs : 1; //allow non-APRS packets
uint8_t fx25 : 1; //enable FX.25 (AX.25 + FEC)
+ uint8_t fx25Tx : 1; //enable TX in FX.25
};
extern struct Ax25ProtoConfig Ax25Config;
@@ -70,10 +79,13 @@ void Ax25ClearReceivedFrameBitmap(void);
* @brief Get next received frame (if available)
* @param **dst Pointer to internal buffer
* @param *size Actual frame size
- * @param *signalLevel Frame signal level (RMS)
+ * @param *peak Signak positive peak value in %
+ * @param *valley Signal negative peak value in %
+ * @param *level Signal level in %
+ * @param *corrected Number of bytes corrected in FX.25 mode. 255 is returned if not a FX.25 packet.
* @return True if frame was read, false if no more frames to read
*/
-bool Ax25ReadNextRxFrame(uint8_t **dst, uint16_t *size, uint16_t *signalLevel);
+bool Ax25ReadNextRxFrame(uint8_t **dst, uint16_t *size, int8_t *peak, int8_t *valley, uint8_t *level, uint8_t *corrected);
/**
* @brief Get current RX stage
diff --git a/Inc/drivers/modem.h b/Inc/drivers/modem.h
index 2530f33..0933ab0 100644
--- a/Inc/drivers/modem.h
+++ b/Inc/drivers/modem.h
@@ -59,6 +59,15 @@ enum ModemPrefilter
PREFILTER_NONE,
};
+/**
+ * @brief Get measured signal level
+ * @param modem Modem number
+ * @param *peak Output signal positive peak in %
+ * @param *valley Output signal negative peak in %
+ * @param *level Output signal level in %
+ */
+void ModemGetSignalLevel(uint8_t modem, int8_t *peak, int8_t *valley, uint8_t *level);
+
/**
* @brief Get current modem baudrate
* @return Baudrate
diff --git a/Inc/fx25.h b/Inc/fx25.h
index 21497ea..3a528e4 100644
--- a/Inc/fx25.h
+++ b/Inc/fx25.h
@@ -4,6 +4,7 @@
#ifdef ENABLE_FX25
#include
+#include
#define FX25_MAX_BLOCK_SIZE 255
@@ -16,15 +17,39 @@ struct Fx25Mode
extern const struct Fx25Mode Fx25ModeList[11];
+/**
+ * @brief Get FX.25 mode for given correlation tag
+ * @param tag FX.25 correlation tag
+ * @return FX.25 mode structure pointer or NULL if not a FX.25 tag
+ */
+const struct Fx25Mode* Fx25GetModeForTag(uint64_t tag);
+
/**
* @brief Get FX.25 mode for given payload size
* @param size Payload size including flags and CRC
* @return FX.25 mode structure pointer or NULL if standard AX.25 must be used
*/
-const struct Fx25Mode* Fx25GetMode(uint16_t size);
+const struct Fx25Mode* Fx25GetModeForSize(uint16_t size);
+
+/**
+ * @brief Encode AX.25 packet using FX.25
+ * @param *buffer Input full AX.25 packets with flags and output FX.25 packet
+ * @param *mode FEC mode to use
+ */
+void Fx25Encode(uint8_t *buffer, const struct Fx25Mode *mode);
-void Fx25AddParity(uint8_t *buffer, const struct Fx25Mode *mode);
+/**
+ * @brief Decode FX.25 packet to AX.25
+ * @param *buffer Input FX.25 packet and output AX.25 packet
+ * @param *mode FEC mode to use
+ * @param *fixed Number of bytes corrected
+ * @return True on success, false on failure
+ */
+bool Fx25Decode(uint8_t *buffer, const struct Fx25Mode *mode, uint8_t *fixed);
+/**
+ * @brief Initialize FX.25 module
+ */
void Fx25Init(void);
#endif
diff --git a/Src/ax25.c b/Src/ax25.c
index 1d467bd..cf80cbb 100644
--- a/Src/ax25.c
+++ b/Src/ax25.c
@@ -1,4 +1,6 @@
/*
+Copyright 2020-2023 Piotr Wilkon
+
This file is part of VP-Digi.
VP-Digi is free software: you can redistribute it and/or modify
@@ -15,14 +17,14 @@ You should have received a copy of the GNU General Public License
along with VP-Digi. If not, see .
*/
+
#include "ax25.h"
#include
#include "drivers/modem.h"
#include "common.h"
-#include "drivers/systick.h"
#include
-#include "digipeater.h"
-#include "fx25.h"
+#include
+#include "drivers/systick.h"
struct Ax25ProtoConfig Ax25Config;
@@ -36,7 +38,9 @@ struct Ax25ProtoConfig Ax25Config;
//Reed-Solomon library needs a bit of memory and the frame buffer must be smaller
//otherwise we run out of RAM
#define FRAME_MAX_SIZE (280) //single frame max length
+#include "fx25.h"
#endif
+
#define FRAME_MAX_COUNT (10) //max count of frames in buffer
#define FRAME_BUFFER_SIZE (FRAME_MAX_COUNT * FRAME_MAX_SIZE) //circular frame buffer length
@@ -49,9 +53,12 @@ struct FrameHandle
{
uint16_t start;
uint16_t size;
- uint16_t signalLevel;
+ int8_t peak;
+ int8_t valley;
+ uint8_t level;
#ifdef ENABLE_FX25
struct Fx25Mode *fx25Mode;
+ uint8_t corrected;
#endif
};
@@ -125,6 +132,12 @@ struct RxState
uint8_t rawData; //raw data being currently received
enum Ax25RxStage rx; //current RX stage
uint8_t frameReceived; //frame received flag
+#ifdef ENABLE_FX25
+ struct Fx25Mode *fx25Mode;
+ uint64_t tag; //received correlation tag
+ uint8_t tagBit;
+ uint8_t corrected;
+#endif
};
static volatile struct RxState rxState[MODEM_MAX_DEMODULATOR_COUNT];
@@ -186,13 +199,24 @@ void Ax25TxKiss(uint8_t *buf, uint16_t len)
if(end == len) //no frame end marker found
return;
Ax25WriteTxFrame(&buf[i + 2], end - (i + 2)); //skip modem number and send frame
- DigiStoreDeDupe(&buf[i + 2], end - (i + 2));
+ //DigiStoreDeDupe(&buf[i + 2], end - (i + 2));
i = end; //move pointer to the next byte if there are more consecutive frames
}
}
}
+static void removeLastFrameFromRxBuffer(void)
+{
+ rxBufferHead = rxFrame[rxFrameHead].start;
+ if(rxFrameHead == 0)
+ rxFrameHead = FRAME_MAX_COUNT - 1;
+ else
+ rxFrameHead--;
+ rxFrameBufferFull = false;
+}
+
#ifdef ENABLE_FX25
+
static void *writeFx25Frame(uint8_t *data, uint16_t size)
{
//first calculate how big the frame can be
@@ -200,7 +224,7 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
//bitstuffing occurs after 5 consecutive ones, so in worst scenario
//bits inserted by bitstuffing can occupy up to frame size / 5 additional bytes
//also add 1 in case there is a remainder when dividing
- const struct Fx25Mode *fx25Mode = fx25Mode = Fx25GetMode(size + 4 + (size / 5) + 1);
+ const struct Fx25Mode *fx25Mode = fx25Mode = Fx25GetModeForSize(size + 4 + (size / 5) + 1);
uint16_t requiredSize = size;
if(NULL != fx25Mode)
requiredSize = fx25Mode->K + fx25Mode->T;
@@ -217,6 +241,8 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
txFrame[txFrameHead].start = txBufferHead;
txFrame[txFrameHead].fx25Mode = (struct Fx25Mode*)fx25Mode;
+ memset(txFx25Buffer, 0, sizeof(txFx25Buffer));
+
uint16_t index = 0;
//header flag
txFx25Buffer[index++] = 0x7E;
@@ -233,7 +259,7 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
bits++;
if(i < size) //frame data
{
- if(data[i] >> k)
+ if((data[i] >> k) & 1)
{
calculateCRC(1, &crc);
bitstuff++;
@@ -253,7 +279,7 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
else
c = (crc >> 8) ^ 0xFF;
- if(c >> k)
+ if((c >> k) & 1)
{
bitstuff++;
txFx25Buffer[index] |= 0x80;
@@ -291,7 +317,7 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
txFx25Buffer[index] >>= 1;
bits++;
- if(0x7E >> k)
+ if((0x7E >> k) & 1)
{
txFx25Buffer[index] |= 0x80;
}
@@ -304,7 +330,7 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
}
}
- Fx25AddParity(txFx25Buffer, fx25Mode);
+ Fx25Encode(txFx25Buffer, fx25Mode);
for(uint16_t i = 0; i < (fx25Mode->K + fx25Mode->T); i++)
{
@@ -319,6 +345,93 @@ static void *writeFx25Frame(uint8_t *data, uint16_t size)
txFrameBufferFull = true;
return ret;
}
+
+static struct FrameHandle* parseFx25Frame(uint8_t *frame, uint16_t size, uint16_t *crc)
+{
+ struct FrameHandle *h = &rxFrame[rxFrameHead];
+ uint16_t initialRxBufferHead = rxBufferHead;
+ if(!rxFrameBufferFull)
+ {
+ rxFrame[rxFrameHead++].start = rxBufferHead;
+ rxFrameHead %= FRAME_MAX_COUNT;
+ if(rxFrameHead == txFrameHead)
+ rxFrameBufferFull = true;
+ }
+ else
+ return NULL;
+
+ uint16_t i = 0; //input data index
+ uint16_t k = 0; //output data size
+ while(frame[i] == 0x7E)
+ i++;
+
+ uint8_t bitstuff = 0;
+ uint8_t outBit = 0;
+ for(; i < size; i++)
+ {
+ for(uint8_t b = 0; b < 8; b++)
+ {
+ if(frame[i] & (1 << b))
+ {
+ rxBuffer[rxBufferHead] >>= 1;
+ rxBuffer[rxBufferHead] |= 0x80;
+ bitstuff++;
+ }
+ else
+ {
+ if(bitstuff == 5) //zero after 5 ones, normal bitstuffing
+ {
+ bitstuff = 0;
+ continue;
+ }
+ else if(bitstuff == 6) //zero after 6 ones, this is a flag
+ {
+ goto endParseFx25Frame;
+ }
+ else if(bitstuff >= 7) //zero after 7 ones, illegal byte
+ {
+ removeLastFrameFromRxBuffer();
+ return NULL;
+ }
+ bitstuff = 0;
+ rxBuffer[rxBufferHead] >>= 1;
+ }
+ outBit++;
+ if(outBit == 8)
+ {
+ k++;
+ rxBufferHead++;
+ rxBufferHead %= FRAME_BUFFER_SIZE;
+ outBit = 0;
+ }
+ }
+ }
+
+endParseFx25Frame:
+ *crc = 0xFFFF;
+ i = initialRxBufferHead;
+ for(uint16_t j = 0; j < (k - 2); j++)
+ {
+ for(uint8_t b = 0; b < 8; b++)
+ calculateCRC((rxBuffer[i] >> b) & 1, crc);
+
+ i++;
+ i %= FRAME_BUFFER_SIZE;
+ }
+
+ *crc ^= 0xFFFF;
+ if((rxBuffer[i] == (*crc & 0xFF) )
+ && (rxBuffer[(i + 1) % FRAME_BUFFER_SIZE] == ((*crc >> 8) & 0xFF))) //check CRC
+ {
+ h->size = k - 2;
+ return h;
+ }
+ else
+ {
+ removeLastFrameFromRxBuffer();
+ return NULL;
+ }
+}
#endif
void *Ax25WriteTxFrame(uint8_t *data, uint16_t size)
@@ -330,7 +443,7 @@ void *Ax25WriteTxFrame(uint8_t *data, uint16_t size)
return NULL;
#ifdef ENABLE_FX25
- if(Ax25Config.fx25)
+ if(Ax25Config.fx25 && Ax25Config.fx25Tx)
{
void *ret = writeFx25Frame(data, size);
if(ret)
@@ -367,7 +480,7 @@ void *Ax25WriteTxFrame(uint8_t *data, uint16_t size)
}
-bool Ax25ReadNextRxFrame(uint8_t **dst, uint16_t *size, uint16_t *signalLevel)
+bool Ax25ReadNextRxFrame(uint8_t **dst, uint16_t *size, int8_t *peak, int8_t *valley, uint8_t *level, uint8_t *corrected)
{
if((rxFrameHead == rxFrameTail) && !rxFrameBufferFull)
return false;
@@ -379,8 +492,13 @@ bool Ax25ReadNextRxFrame(uint8_t **dst, uint16_t *size, uint16_t *signalLevel)
(*dst)[i] = rxBuffer[(rxFrame[rxFrameTail].start + i) % FRAME_BUFFER_SIZE];
}
- *signalLevel = rxFrame[rxFrameTail].signalLevel;
+ *peak = rxFrame[rxFrameTail].peak;
+ *valley = rxFrame[rxFrameTail].valley;
+ *level = rxFrame[rxFrameTail].level;
*size = rxFrame[rxFrameTail].size;
+#ifdef ENABLE_FX25
+ *corrected = rxFrame[rxFrameTail].corrected;
+#endif
rxFrameBufferFull = false;
rxFrameTail++;
@@ -393,6 +511,7 @@ enum Ax25RxStage Ax25GetRxStage(uint8_t modem)
return rxState[modem].rx;
}
+#include
void Ax25BitParse(uint8_t bit, uint8_t modem)
{
@@ -408,118 +527,200 @@ void Ax25BitParse(uint8_t bit, uint8_t modem)
frameReceived |= ((rxState[i].frameReceived > 0) << i);
rxState[i].frameReceived = 0;
}
+
+ rxFrameTail++;
+ rxFrameTail %= FRAME_MAX_COUNT;
+ rxFrameBufferFull = false;
}
}
-
struct RxState *rx = (struct RxState*)&(rxState[modem]);
rx->rawData <<= 1; //store incoming bit
rx->rawData |= (bit > 0);
+#ifdef ENABLE_FX25
+ rx->tag >>= 1;
+ if(bit)
+ rx->tag |= 0x8000000000000000;
+ rx->tagBit++;
+
- if(rx->rawData == 0x7E) //HDLC flag received
+ if((rx->rx == RX_STAGE_FX25_TAG) || (rx->rx == RX_STAGE_FX25_FRAME))
{
- if(rx->rx == RX_STAGE_FRAME) //if we are in frame, this is the end of the frame
+ if((rx->rx == RX_STAGE_FX25_TAG) && (rx->tagBit == 64))
{
- if((rx->frameIdx > 15)) //correct frame must be at least 16 bytes long
- {
- uint16_t i = 0;
- for(; i < rx->frameIdx - 2; i++) //look for path end bit
- {
- if(rx->frame[i] & 1)
- break;
- }
-
- //if non-APRS frames are not allowed, check if this frame has control=0x03 and PID=0xF0
-
- if(Ax25Config.allowNonAprs || (((rx->frame[i + 1] == 0x03) && (rx->frame[i + 2] == 0xf0))))
+ if(Ax25Config.fx25)
+ {
+ rx->fx25Mode = (struct Fx25Mode*)Fx25GetModeForTag(rx->tag);
+ if(Ax25Config.fx25 && (rx->fx25Mode != NULL))
{
- if((rx->frame[rx->frameIdx - 2] == ((rx->crc & 0xFF) ^ 0xFF)) && (rx->frame[rx->frameIdx - 1] == (((rx->crc >> 8) & 0xFF) ^ 0xFF))) //check CRC
+ rx->rx = RX_STAGE_FX25_FRAME;
+ rx->frameIdx = 0;
+ rx->receivedBitIdx = 0;
+ rx->receivedByte = 0;
+ return;
+ }
+ }
+ rx->rx = RX_STAGE_FRAME;
+ }
+ }
+ else
+ {
+ if(rx->rawData != 0x7E)
+ {
+ if(Ax25Config.fx25)
+ {
+ if((rx->rx == RX_STAGE_FLAG) && (rx->tagBit == 8))
+ rx->rx = RX_STAGE_FX25_TAG;
+ }
+ else
+ rx->rx = RX_STAGE_FRAME;
+ }
+ else
+ rx->tagBit = 0;
+#endif
+
+ if(rx->rawData == 0x7E) //HDLC flag received
+ {
+ if(rx->rx == RX_STAGE_FRAME) //if we are in frame, this is the end of the frame
+ {
+ if((rx->frameIdx > 15)) //correct frame must be at least 16 bytes long
+ {
+ uint16_t i = 0;
+ for(; i < rx->frameIdx - 2; i++) //look for path end bit
{
- rx->frameReceived = 1;
- rx->frameIdx -= 2; //remove CRC
- if(rx->crc != lastCrc) //the other decoder has not received this frame yet, so store it in main frame buffer
- {
- lastCrc = rx->crc; //store CRC of this frame
+ if(rx->frame[i] & 1)
+ break;
+ }
- if(!rxFrameBufferFull) //if enough space, store the frame
+ //if non-APRS frames are not allowed, check if this frame has control=0x03 and PID=0xF0
+ if(Ax25Config.allowNonAprs || (((rx->frame[i + 1] == 0x03) && (rx->frame[i + 2] == 0xF0))))
+ {
+ rx->crc = 0xFFFF;
+ for(i = 0; i < rx->frameIdx - 2; i++)
+ {
+ for(uint8_t k = 0; k < 8; k++)
+ {
+ calculateCRC((rx->frame[i] >> k) & 1, &(rx->crc));
+ }
+ }
+ rx->crc ^= 0xFFFF;
+ if((rx->frame[rx->frameIdx - 2] == (rx->crc & 0xFF)) && (rx->frame[rx->frameIdx - 1] == ((rx->crc >> 8) & 0xFF))) //check CRC
+ {
+ rx->frameReceived = 1;
+ rx->frameIdx -= 2; //remove CRC
+ if(rx->crc != lastCrc) //the other decoder has not received this frame yet, so store it in main frame buffer
{
- rxFrame[rxFrameHead].start = rxBufferHead;
- rxFrame[rxFrameHead].signalLevel = ModemGetRMS(modem);
- rxFrame[rxFrameHead++].size = rx->frameIdx;
- rxFrameHead %= FRAME_MAX_COUNT;
- if(rxFrameHead == txFrameHead)
- rxFrameBufferFull = true;
-
- for(uint16_t i = 0; i < rx->frameIdx; i++)
+ lastCrc = rx->crc; //store CRC of this frame
+
+ if(!rxFrameBufferFull) //if enough space, store the frame
{
- rxBuffer[rxBufferHead++] = rx->frame[i];
- rxBufferHead %= FRAME_BUFFER_SIZE;
+ rxFrame[rxFrameHead].start = rxBufferHead;
+ ModemGetSignalLevel(modem, &rxFrame[rxFrameHead].peak, &rxFrame[rxFrameHead].valley, &rxFrame[rxFrameHead].level);
+#ifdef ENABLE_FX25
+ rxFrame[rxFrameHead].fx25Mode = NULL;
+#endif
+ rxFrame[rxFrameHead].corrected = AX25_NOT_FX25;
+ rxFrame[rxFrameHead++].size = rx->frameIdx;
+ rxFrameHead %= FRAME_MAX_COUNT;
+ if(rxFrameHead == txFrameHead)
+ rxFrameBufferFull = true;
+
+ for(uint16_t i = 0; i < rx->frameIdx; i++)
+ {
+ rxBuffer[rxBufferHead++] = rx->frame[i];
+ rxBufferHead %= FRAME_BUFFER_SIZE;
+ }
}
-
}
}
}
}
-
- }
-
+ }
+ rx->rx = RX_STAGE_FLAG;
+ rx->receivedByte = 0;
+ rx->receivedBitIdx = 0;
+ rx->frameIdx = 0;
+ return;
}
- rx->rx = RX_STAGE_FLAG;
- ModemClearRMS(modem);
- rx->receivedByte = 0;
- rx->receivedBitIdx = 0;
- rx->frameIdx = 0;
- rx->crc = 0xFFFF;
- return;
+
+#ifdef ENABLE_FX25
}
- if((rx->rawData & 0x7F) == 0x7F) //received 7 consecutive ones, this is an error (sometimes called "escape byte")
+ if((rx->rx != RX_STAGE_FX25_FRAME) && (rx->rx != RX_STAGE_FX25_TAG))
{
- rx->rx = RX_STAGE_FLAG;
- ModemClearRMS(modem);
- rx->receivedByte = 0;
- rx->receivedBitIdx = 0;
- rx->frameIdx = 0;
- rx->crc = 0xFFFF;
- return;
+#else
+ {
+ //this condition must not be checked when FX.25 is enabled
+ //because FX.25 parity bytes and tags contain >= 7 consecutive ones
+ if((rx->rawData & 0x7F) == 0x7F) //received 7 consecutive ones, this is an error (sometimes called "escape byte")
+ {
+ rx->rx = RX_STAGE_FLAG;
+ ModemClearRMS(modem);
+ rx->receivedByte = 0;
+ rx->receivedBitIdx = 0;
+ rx->frameIdx = 0;
+ rx->crc = 0xFFFF;
+ return;
+ }
+#endif
+ if((rx->rawData & 0x3F) == 0x3E) //dismiss bit 0 added by bit stuffing
+ return;
}
-
-
- if(rx->rx == RX_STAGE_IDLE) //not in a frame, don't go further
- return;
-
-
- if((rx->rawData & 0x3F) == 0x3E) //dismiss bit 0 added by bit stuffing
- return;
-
if(rx->rawData & 0x01) //received bit 1
rx->receivedByte |= 0x80; //store it
if(++rx->receivedBitIdx >= 8) //received full byte
{
- if(rx->frameIdx > FRAME_MAX_SIZE) //frame is too long
+#ifdef ENABLE_FX25
+ //end of FX.25 reception, that is received full block
+ if((rx->fx25Mode != NULL) && (rx->frameIdx == (rx->fx25Mode->K + rx->fx25Mode->T)))
{
- rx->rx = RX_STAGE_IDLE;
- ModemClearRMS(modem);
+ uint8_t fixed = 0;
+ if(Fx25Decode(rx->frame, rx->fx25Mode, &fixed))
+ {
+ uint16_t crc;
+ struct FrameHandle *h = parseFx25Frame(rx->frame, rx->frameIdx, &crc);
+ if(h != NULL)
+ {
+ rx->frameReceived = 1;
+ ModemGetSignalLevel(modem, &h->peak, &h->valley, &h->level);
+ h->corrected = fixed;
+ //FX.25 (RS) decoding is not reentrant/interrupt safe
+ //use only one modem when FX.25 is enabled
+ // if(crc != lastCrc)
+ // {
+// h->signalLevel = ModemGetRMS(modem);
+ h->fx25Mode = rx->fx25Mode;
+ lastCrc = crc;
+ // }
+ // else
+ // removeLastFrameFromRxBuffer();
+ }
+ }
+ rx->rx = RX_STAGE_FX25_TAG;
+ rx->tagBit = 0;
rx->receivedByte = 0;
rx->receivedBitIdx = 0;
rx->frameIdx = 0;
- rx->crc = 0xFFFF;
return;
}
- if(rx->frameIdx >= 2) //more than 2 bytes received, calculate CRC
+#else
+ rx->rx = RX_STAGE_FRAME;
+#endif
+ if(rx->frameIdx >= FRAME_MAX_SIZE) //frame is too long
{
- for(uint8_t i = 0; i < 8; i++)
- {
- calculateCRC((rx->frame[rx->frameIdx - 2] >> i) & 1, &(rx->crc));
- }
+ rx->rx = RX_STAGE_IDLE;
+ rx->receivedByte = 0;
+ rx->receivedBitIdx = 0;
+ rx->frameIdx = 0;
+ rx->crc = 0xFFFF;
+ return;
}
- rx->rx = RX_STAGE_FRAME;
rx->frame[rx->frameIdx++] = rx->receivedByte; //store received byte
rx->receivedByte = 0;
rx->receivedBitIdx = 0;
@@ -544,12 +745,14 @@ uint8_t Ax25GetTxBit(void)
else
{
txDelayElapsed = 0;
+#ifdef ENABLE_FX25
if(NULL != txFrame[txFrameTail].fx25Mode)
{
txStage = TX_STAGE_CORRELATION_TAG;
txTagByteIdx = 0;
}
else
+#endif
txStage = TX_STAGE_HEADER_FLAGS;
}
}
@@ -561,6 +764,8 @@ transmitTag:
txByte = (txFrame[txFrameTail].fx25Mode->tag >> (8 * txTagByteIdx)) & 0xFF;
else
txStage = TX_STAGE_DATA;
+
+ txTagByteIdx++;
}
#endif
if(txStage == TX_STAGE_HEADER_FLAGS) //transmitting initial flags
@@ -689,7 +894,11 @@ transmitTail:
uint8_t txBit = 0;
//transmitting normal data or CRC in AX.25 mode
- if((NULL != txFrame[txFrameTail].fx25Mode) || (txStage == TX_STAGE_DATA) || (txStage == TX_STAGE_CRC))
+ if(
+#ifdef ENABLE_FX25
+ (NULL == txFrame[txFrameTail].fx25Mode) &&
+#endif
+ ((txStage == TX_STAGE_DATA) || (txStage == TX_STAGE_CRC)))
{
if(txBitstuff == 5) //5 consecutive ones transmitted
{
@@ -730,16 +939,16 @@ transmitTail:
*/
void Ax25TransmitBuffer(void)
{
- if(txInitStage == TX_INIT_WAITING)
- return;
- if(txInitStage == TX_INIT_TRANSMITTING)
- return;
-
- if((txFrameHead != txFrameTail) || txFrameBufferFull)
- {
- txQuiet = (SysTickGet() + (Ax25Config.quietTime / SYSTICK_INTERVAL) + Random(0, 200 / SYSTICK_INTERVAL)); //calculate required delay
- txInitStage = TX_INIT_WAITING;
- }
+ if(txInitStage == TX_INIT_WAITING)
+ return;
+ if(txInitStage == TX_INIT_TRANSMITTING)
+ return;
+
+ if((txFrameHead != txFrameTail) || txFrameBufferFull)
+ {
+ txQuiet = (SysTickGet() + (Ax25Config.quietTime / SYSTICK_INTERVAL) + Random(0, 200 / SYSTICK_INTERVAL)); //calculate required delay
+ txInitStage = TX_INIT_WAITING;
+ }
}
@@ -765,37 +974,37 @@ static void transmitStart(void)
*/
void Ax25TransmitCheck(void)
{
- if(txInitStage == TX_INIT_OFF) //TX not initialized at all, nothing to transmit
- return;
- if(txInitStage == TX_INIT_TRANSMITTING) //already transmitting
- return;
-
- if(ModemIsTxTestOngoing()) //TX test is enabled, wait for now
- return;
-
- if(txQuiet < SysTickGet()) //quit time has elapsed
- {
- if(!ModemDcdState()) //channel is free
- {
- txInitStage = TX_INIT_TRANSMITTING; //transmit right now
- txRetries = 0;
- transmitStart();
- }
- else //channel is busy
- {
- if(txRetries == MAX_TRANSMIT_RETRY_COUNT) //timeout
- {
- txInitStage = TX_INIT_TRANSMITTING; //transmit right now
- txRetries = 0;
- transmitStart();
- }
- else //still trying
- {
- txQuiet = SysTickGet() + Random(100 / SYSTICK_INTERVAL, 500 / SYSTICK_INTERVAL); //try again after some random time
- txRetries++;
- }
- }
- }
+ if(txInitStage == TX_INIT_OFF) //TX not initialized at all, nothing to transmit
+ return;
+ if(txInitStage == TX_INIT_TRANSMITTING) //already transmitting
+ return;
+
+ if(ModemIsTxTestOngoing()) //TX test is enabled, wait for now
+ return;
+
+ if(txQuiet < SysTickGet()) //quit time has elapsed
+ {
+ if(!ModemDcdState()) //channel is free
+ {
+ txInitStage = TX_INIT_TRANSMITTING; //transmit right now
+ txRetries = 0;
+ transmitStart();
+ }
+ else //channel is busy
+ {
+ if(txRetries == MAX_TRANSMIT_RETRY_COUNT) //timeout
+ {
+ txInitStage = TX_INIT_TRANSMITTING; //transmit right now
+ txRetries = 0;
+ transmitStart();
+ }
+ else //still trying
+ {
+ txQuiet = SysTickGet() + Random(100 / SYSTICK_INTERVAL, 500 / SYSTICK_INTERVAL); //try again after some random time
+ txRetries++;
+ }
+ }
+ }
}
void Ax25Init(void)
diff --git a/Src/beacon.c b/Src/beacon.c
index 9462b0c..1f3a39d 100644
--- a/Src/beacon.c
+++ b/Src/beacon.c
@@ -105,7 +105,7 @@ void BeaconCheck(void)
if((beacon[i].interval > 0) && ((SysTickGet() >= beacon[i].next) || (beacon[i].next == 0)))
{
if(beaconDelay[i] > SysTickGet()) //check for beacon delay (only for the very first transmission)
- return;
+ continue;
beacon[i].next = SysTickGet() + beacon[i].interval; //save next beacon timestamp
beaconDelay[i] = 0;
BeaconSend(i);
diff --git a/Src/drivers/modem.c b/Src/drivers/modem.c
index 006b7a3..c7e3c8c 100644
--- a/Src/drivers/modem.c
+++ b/Src/drivers/modem.c
@@ -16,12 +16,11 @@ along with VP-Digi. If not, see .
*/
#include "drivers/modem.h"
-#include "drivers/systick.h"
#include "ax25.h"
-#include "stm32f1xx.h"
#include
#include
#include "common.h"
+#include
/*
@@ -53,10 +52,10 @@ along with VP-Digi. If not, see .
#define DCD300_INC 5
#define DCD300_PLLTUNE 0
-#define N1200 8 //samples per symbol
-#define N9600 4
-#define N300 16
-#define NMAX 16 //keep this value equal to the biggest Nx
+#define N1200 8 //samples per symbol @ fs=9600, oversampling = 38400 Hz
+#define N9600 4 //fs=38400, oversampling = 153600 Hz
+#define N300 32 //fs=9600, oversampling = 38400 Hz
+#define NMAX 32 //keep this value equal to the biggest Nx
#define PLL1200_STEP (((uint64_t)1 << 32) / N1200) //PLL tick increment value
#define PLL9600_STEP (((uint64_t)1 << 32) / N9600)
@@ -69,12 +68,12 @@ along with VP-Digi. If not, see .
#define PLL300_LOCKED_TUNE 0.74f
#define PLL300_NOT_LOCKED_TUNE 0.50f
-#define AGC9600_ATTACK 0.08f
-#define AGC9600_DECAY 0.0008f
-#define DAC_SINE_SIZE 32 //DAC sine table size
+#define AMP_TRACKING_ATTACK 0.16f //0.16
+#define AMP_TRACKING_DECAY 0.00004f //0.00004
+#define DAC_SINE_SIZE 32 //DAC sine table size
struct ModemDemodConfig ModemConfig;
@@ -86,6 +85,7 @@ static uint16_t dacSine[DAC_SINE_SIZE]; //sine samples for DAC
static uint8_t dacSineIdx; //current sine sample index
static uint16_t samples[4]; //very raw received samples, filled directly by DMA
static uint8_t currentSymbol; //current symbol for NRZI encoding
+static uint8_t scrambledSymbol; //current symbol after scrambling
static float markFreq; //mark frequency
static float spaceFreq; //space frequency
static float baudRate; //baudrate
@@ -94,12 +94,12 @@ static uint8_t spaceStep; //space timer step
static uint16_t baudRateStep; //baudrate timer step
static int16_t coeffHiI[NMAX], coeffLoI[NMAX], coeffHiQ[NMAX], coeffLoQ[NMAX]; //correlator IQ coefficients
static uint8_t dcd = 0; //multiplexed DCD state from both demodulators
-static uint32_t lfsr = 0; //LFSR for 9600 Bd
+static uint32_t lfsr = 0xFFFFF; //LFSR for 9600 Bd
/**
* @brief BPF filter with 2200 Hz tone 6 dB preemphasis (it actually attenuates 1200 Hz tone by 6 dB)
*/
-static int16_t bpf1200[8] =
+static const int16_t bpf1200[8] =
{
728,
-13418,
@@ -114,67 +114,58 @@ static int16_t bpf1200[8] =
/**
* @brief BPF filter with 2200 Hz tone 6 dB deemphasis
*/
-static int16_t bpf1200Inv[8] =
+static const int16_t bpf1200Inv[8] =
{
- -10513,
- -10854,
- 9589,
- 23884,
- 9589,
- -10854,
- -10513,
- -879
+ -10513,
+ -10854,
+ 9589,
+ 23884,
+ 9589,
+ -10854,
+ -10513,
+ -879
};
-//fs=4800, rectangular, fc1=1400, fc2=2000, 0 dB @ 1600 Hz and 1800 Hz, N = 15, gain 65536
-static int16_t bpf300[15] =
+//fs=9600, rectangular, fc1=1500, fc2=1900, 0 dB @ 1600 Hz and 1800 Hz, N = 15, gain 65536
+static const int16_t bpf300[15] =
{
- -2327, 3557, 1048, -9284, 12210, -3989, -9849, 17268, -9849, -3989, 12210, -9284, 1048, 3557, -2327,
+ 186, 8887, 8184, -1662, -10171, -8509, 386, 5394, 386, -8509, -10171, -1662, 8184, 8887, 186,
};
#define BPF_MAX_TAPS 15
-//fs=4800 Hz, raised cosine, fc=300 Hz (BR=600 Bd), beta=0.8, N=14, gain=65536
-static int16_t lpf300[14] =
+//fs=9600 Hz, raised cosine, fc=300 Hz (BR=600 Bd), beta=0.8, N=14, gain=65536
+static const int16_t lpf300[14] =
{
- 741, 1834, 3216, 4756, 6268, 7547, 8402, 8402, 7547, 6268, 4756, 3216, 1834, 741,
+ 4385, 4515, 4627, 4720, 4793, 4846, 4878, 4878, 4846, 4793, 4720, 4627, 4515, 4385,
};
-#ifdef EXPERIMENTAL_LPF1200
-//fs=9600 Hz, raised cosine, fc=600Hz (BR=1200 Bd), beta=0.8, N=14, gain=65536
-static const int16_t lpf1200[14] =
-{
- 741, 1834, 3216, 4756, 6268, 7547, 8402, 8402, 7547, 6268, 4756, 3216, 1834, 741,
-};
-#define LPF_MAX_TAPS 14
-#else
-static int16_t lpf1200[15] =
+//I don't remember what are this filter parameters,
+//but it seems to be the best among all I have tested
+static const int16_t lpf1200[15] =
{
- -6128,
- -5974,
- -2503,
- 4125,
- 12679,
- 21152,
- 27364,
- 29643,
- 27364,
- 21152,
- 12679,
- 4125,
- -2503,
- -5974,
- -6128
+ -6128,
+ -5974,
+ -2503,
+ 4125,
+ 12679,
+ 21152,
+ 27364,
+ 29643,
+ 27364,
+ 21152,
+ 12679,
+ 4125,
+ -2503,
+ -5974,
+ -6128
};
-#define LPF_MAX_TAPS 15
-
-#endif
-
//fs=38400 Hz, Gaussian, fc=4800 Hz (9600 Bd), N=9, gain=65536
-//seems like there is no difference between N=9 and any higher order
+//seems like there is almost no difference between N=9 and any higher order
static int16_t lpf9600[9] = {497, 2360, 7178, 13992, 17478, 13992, 7178, 2360, 497};
+#define LPF_MAX_TAPS 15
#define FILTER_MAX_TAPS ((LPF_MAX_TAPS > BPF_MAX_TAPS) ? LPF_MAX_TAPS : BPF_MAX_TAPS)
@@ -198,8 +189,6 @@ struct DemodState
struct Filter lpf;
uint8_t dcd : 1; //DCD state
- uint64_t RMSenergy; //frame energy counter (sum of samples squared)
- uint32_t RMSsampleCount; //number of samples for RMS
int32_t pll; //bit recovery PLL counter
int32_t pllStep;
@@ -235,7 +224,7 @@ static int32_t filter(struct Filter *filter, int32_t input)
filter->samples[0] = input; //store new sample
for(uint8_t i = 0; i < filter->taps; i++)
{
- out += filter->coeffs[i] * filter->samples[i];
+ out += (int32_t)filter->coeffs[i] * filter->samples[i];
}
return out >> filter->gainShift;
}
@@ -263,17 +252,11 @@ uint8_t ModemIsTxTestOngoing(void)
return 0;
}
-void ModemClearRMS(uint8_t modem)
-{
-
- demodState[modem].RMSenergy = 0;
- demodState[modem].RMSsampleCount = 0;
-
-}
-
-uint16_t ModemGetRMS(uint8_t modem)
+void ModemGetSignalLevel(uint8_t modem, int8_t *peak, int8_t *valley, uint8_t *level)
{
- return sqrtf((float)demodState[modem].RMSenergy / (float)demodState[modem].RMSsampleCount);
+ *peak = (100 * (int32_t)demodState[modem].peak) >> 12;
+ *valley = (100 * (int32_t)demodState[modem].valley) >> 12;
+ *level = (100 * (int32_t)(demodState[modem].peak - demodState[modem].valley)) >> 13;
}
enum ModemPrefilter ModemGetFilterType(uint8_t modem)
@@ -287,16 +270,26 @@ enum ModemPrefilter ModemGetFilterType(uint8_t modem)
*/
static void setDcd(uint8_t state)
{
- if(state)
- {
- GPIOC->BSRR = GPIO_BSRR_BR13;
- GPIOB->BSRR = GPIO_BSRR_BS5;
- }
- else
- {
- GPIOC->BSRR = GPIO_BSRR_BS13;
- GPIOB->BSRR = GPIO_BSRR_BR5;
- }
+ if(state)
+ {
+ GPIOC->BSRR = GPIO_BSRR_BR13;
+ GPIOB->BSRR = GPIO_BSRR_BS5;
+ }
+ else
+ {
+ GPIOC->BSRR = GPIO_BSRR_BS13;
+ GPIOB->BSRR = GPIO_BSRR_BR5;
+ }
+}
+
+static inline uint8_t descramble(uint8_t in)
+{
+ //G3RUH descrambling (x^17+x^12+1)
+ uint8_t bit = ((lfsr & 0x10000) > 0) ^ ((lfsr & 0x800) > 0) ^ (in > 0);
+
+ lfsr <<= 1;
+ lfsr |= in;
+ return bit;
}
static inline uint8_t scramble(uint8_t in)
@@ -316,11 +309,12 @@ static inline uint8_t scramble(uint8_t in)
void DMA1_Channel2_IRQHandler(void) __attribute__ ((interrupt));
void DMA1_Channel2_IRQHandler(void)
{
- if(DMA1->ISR & DMA_ISR_TCIF2)
- {
- DMA1->IFCR |= DMA_IFCR_CTCIF2;
+ if(DMA1->ISR & DMA_ISR_TCIF2)
+ {
+ DMA1->IFCR |= DMA_IFCR_CTCIF2;
- int32_t sample = ((samples[0] + samples[1] + samples[2] + samples[3]) >> 1) - 4095; //calculate input sample (decimation)
+ //each sample is 12 bits, output sample is 13 bits
+ int16_t sample = ((samples[0] + samples[1] + samples[2] + samples[3]) >> 1) - 4095; //calculate input sample (decimation)
bool partialDcd = false;
@@ -345,95 +339,108 @@ void DMA1_Channel2_IRQHandler(void)
}
}
-
-
/**
* @brief ISR for pushing DAC samples
*/
-void TIM1_UP_IRQHandler(void) __attribute__ ((interrupt));
-void TIM1_UP_IRQHandler(void)
-{
- TIM1->SR &= ~TIM_SR_UIF;
-
- int32_t sample = 0;
-
- if(ModemConfig.modem == MODEM_9600)
- {
- if(ModemConfig.usePWM)
- sample = currentSymbol ? 90 : 0;
- else
- sample = currentSymbol ? 15 : 1;
-
- sample = filter(&demodState[0].lpf, sample);
- }
- else
- {
- sample = dacSine[dacSineIdx];
- dacSineIdx++;
- dacSineIdx &= (DAC_SINE_SIZE - 1);
- }
-
- if(ModemConfig.usePWM)
- {
- TIM4->CCR1 = sample;
- }
- else
- {
- GPIOB->ODR &= ~0xF000; //zero 4 oldest bits
- GPIOB->ODR |= (sample << 12); //write sample to 4 oldest bits
- }
-
-}
-
+ void TIM1_UP_IRQHandler(void) __attribute__ ((interrupt));
+ void TIM1_UP_IRQHandler(void)
+ {
+ TIM1->SR &= ~TIM_SR_UIF;
+
+ int32_t sample = 0;
+
+ if(ModemConfig.modem == MODEM_9600)
+ {
+ if(ModemConfig.usePWM)
+ sample = scrambledSymbol ? 90 : 0;
+ else
+ sample = scrambledSymbol ? 15 : 1;
+
+ sample = filter(&demodState[0].lpf, sample);
+ }
+ else
+ {
+ sample = dacSine[dacSineIdx];
+ dacSineIdx++;
+ dacSineIdx &= (DAC_SINE_SIZE - 1);
+ }
+
+ if(ModemConfig.usePWM)
+ {
+ TIM4->CCR1 = sample;
+ }
+ else
+ {
+ GPIOB->ODR &= ~0xF000; //zero 4 oldest bits
+ GPIOB->ODR |= (sample << 12); //write sample to 4 oldest bits
+ }
+
+ }
/**
* @brief ISR for baudrate generator timer. NRZI encoding is done here.
*/
-void TIM3_IRQHandler(void) __attribute__ ((interrupt));
-void TIM3_IRQHandler(void)
-{
- TIM3->SR &= ~TIM_SR_UIF;
+ void TIM3_IRQHandler(void) __attribute__ ((interrupt));
+ void TIM3_IRQHandler(void)
+ {
+ TIM3->SR &= ~TIM_SR_UIF;
+
+ if(txTestState == TEST_DISABLED) //transmitting normal data
+ {
+ if(Ax25GetTxBit() == 0) //get next bit and check if it's 0
+ {
+ currentSymbol ^= 1; //change symbol - NRZI encoding
+ }
+ //if 1, no symbol change
+ }
+ else //transmit test mode
+ {
+ currentSymbol ^= 1; //change symbol
+ }
+
+ TIM1->CNT = 0;
+
+ if(ModemConfig.modem == MODEM_9600)
+ {
+ scrambledSymbol = scramble(currentSymbol);
+ }
+ else
+ {
+ if(currentSymbol) //current symbol is space
+ TIM1->ARR = spaceStep;
+ else //mark
+ TIM1->ARR = markStep;
+ }
+
+ }
- if(txTestState == TEST_DISABLED) //transmitting normal data
+/**
+ * @brief Demodulate received sample (4x oversampling)
+ * @param[in] sample Received sample, no more than 13 bits
+ * @param[in] *dem Demodulator state
+ * @return Current tone (0 or 1)
+ */
+static int32_t demodulate(int16_t sample, struct DemodState *dem)
+{
+ //input signal amplitude tracking
+ if(sample >= dem->peak)
{
- if(Ax25GetTxBit() == 0) //get next bit and check if it's 0
- {
- currentSymbol ^= 1; //change symbol - NRZI encoding
- }
- //if 1, no symbol change
+ dem->peak += (((int32_t)(AMP_TRACKING_ATTACK * (float)32768) * (int32_t)(sample - dem->peak)) >> 15);
}
- else //transmit test mode
+ else
{
- currentSymbol ^= 1; //change symbol
+ dem->peak += (((int32_t)(AMP_TRACKING_DECAY * (float)32768) * (int32_t)(sample - dem->peak)) >> 15);
}
- TIM1->CNT = 0;
-
- if(ModemConfig.modem == MODEM_9600)
+ if(sample <= dem->valley)
{
- currentSymbol = scramble(currentSymbol);
+ dem->valley += (((int32_t)(AMP_TRACKING_ATTACK * (float)32768) * (int32_t)(sample - dem->valley)) >> 15);
}
else
{
- if(currentSymbol) //current symbol is space
- TIM1->ARR = spaceStep;
- else //mark
- TIM1->ARR = markStep;
+ dem->valley += (((int32_t)(AMP_TRACKING_DECAY * (float)32768) * (int32_t)(sample - dem->valley)) >> 15);
}
-}
-
-
-/**
- * @brief Demodulate received sample (4x oversampling)
- * @param[in] sample Received sample
- * @param[in] *dem Demodulator state
- * @return Current tone (0 or 1)
- */
-static int32_t demodulate(int16_t sample, struct DemodState *dem)
-{
- dem->RMSenergy += ((sample >> 1) * (sample >> 1)); //square the sample and add it to the sum
- dem->RMSsampleCount++; //increment number of samples
if(ModemConfig.modem != MODEM_9600)
{
@@ -459,17 +466,17 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem)
outHiQ += t * coeffHiQ[i];
}
- outHiI >>= 12;
- outHiQ >>= 12;
- outLoI >>= 12;
- outLoQ >>= 12;
+ outHiI >>= 14;
+ outHiQ >>= 14;
+ outLoI >>= 14;
+ outLoQ >>= 14;
- sample = ABS(outHiI) + ABS(outHiQ) - ABS(outLoI) - ABS(outLoQ);
+ sample = (abs(outLoI) + abs(outLoQ)) - (abs(outHiI) + abs(outHiQ));
}
- //DCD using PLL
- //PLL is running nominally at 1200 Hz (= baudrate)
+ //DCD using "PLL"
+ //PLL is running nominally at the frequency equal to the baudrate
//PLL timer is counting up and eventually overflows to a minimal negative value
//so it crosses zero in the middle
//tone change should happen somewhere near this zero-crossing (in ideal case of exactly same TX and RX baudrates)
@@ -480,11 +487,11 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem)
//when configured properly, it's generally immune to noise, as the detected tone changes much faster than 1200 baud
//it's also important to set some maximum value for DCD counter, otherwise the DCD is "sticky"
- dem->dcdPll = (signed)((unsigned)(dem->dcdPll) + ((unsigned)dem->pll)); //keep PLL ticking at the frequency equal to baudrate
+ dem->dcdPll = (signed)((unsigned)(dem->dcdPll) + (unsigned)(dem->pllStep)); //keep PLL ticking at the frequency equal to baudrate
if((sample > 0) != dem->dcdLastSymbol) //tone changed
{
- if(abs(dem->dcdPll) < dem->dcdInc) //tone change occurred near zero
+ if(abs(dem->dcdPll) < dem->pllStep) //tone change occurred near zero
dem->dcdCounter += dem->dcdInc; //increase DCD counter
else //tone change occurred far from zero
{
@@ -504,43 +511,9 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem)
else //below DCD threshold
dem->dcd = 0; //no DCD
- //TODO: check if demodulator works well after all changes
- sample = filter(&dem->lpf, sample);
-
- if(ModemConfig.modem == MODEM_9600)
- {
- //AGC
- if(sample >= dem->peak)
- {
- dem->peak += (((int32_t)(AGC9600_ATTACK * (float)32768) * (int32_t)(sample - dem->peak)) >> 15);
- }
- else
- {
- dem->peak += (((int32_t)(AGC9600_DECAY * (float)32768) * (int32_t)(sample - dem->peak)) >> 15);
- }
-
- if(sample <= dem->valley)
- {
- dem->valley += (((int32_t)(AGC9600_ATTACK * (float)32768) * (int32_t)(sample - dem->valley)) >> 15);
- }
- else
- {
- dem->valley += (((int32_t)(AGC9600_DECAY * (float)32768) * (int32_t)(sample - dem->valley)) >> 15);
- }
-
- //remove DC component (subtract average value of peaks)
- //and normalize to 32768 peak-to-peak (-16384:16384)
- //32768 is equal to 1 << 15
- if(dem->peak > dem->valley)
- {
- sample = ((((int32_t)(sample) - ((int32_t)(dem->peak + dem->valley) >> 1)) << 15) / (int32_t)(dem->peak - dem->valley));
- }
- }
-
- return sample > 0;
+ return filter(&dem->lpf, sample) > 0;
}
-
/**
* @brief Decode received symbol: bit recovery, NRZI decoding and pass the decoded bit to higher level protocol
* @param[in] symbol Received symbol
@@ -555,7 +528,7 @@ static void decode(uint8_t symbol, uint8_t demod)
//Current symbol is sampled at PLL counter overflow, so symbol transition should occur at PLL counter zero
int32_t previous = dem->pll; //store last clock state
- dem->pll = (signed)((unsigned)(dem->pll) + (unsigned)dem->pllStep); //keep PLL running
+ dem->pll = (signed)((unsigned)(dem->pll) + (unsigned)(dem->pllStep)); //keep PLL running
dem->rawSymbols <<= 1; //store received unsynchronized symbol
dem->rawSymbols |= (symbol & 1);
@@ -572,7 +545,7 @@ static void decode(uint8_t symbol, uint8_t demod)
sym = 0;
if(ModemConfig.modem == MODEM_9600)
- sym = scramble(sym); //descramble
+ sym = descramble(sym); //descramble
dem->syncSymbols |= sym;
@@ -589,12 +562,11 @@ static void decode(uint8_t symbol, uint8_t demod)
if(((dem->rawSymbols & 0x03) == 0b10) || ((dem->rawSymbols & 0x03) == 0b01)) //if there was a symbol transition, adjust PLL
{
-
- if(Ax25GetRxStage(demod) != RX_STAGE_FRAME) //not in a frame
+ if(!dem->dcd) //PLL not locked
{
dem->pll = (int)(dem->pll * dem->pllNotLockedAdjust); //adjust PLL faster
}
- else //in a frame
+ else //PLL locked
{
dem->pll = (int)(dem->pll * dem->pllLockedAdjust); //adjust PLL slower
}
@@ -606,62 +578,62 @@ static void decode(uint8_t symbol, uint8_t demod)
void ModemTxTestStart(enum ModemTxTestMode type)
{
- if(txTestState != TEST_DISABLED) //TX test is already running
- ModemTxTestStop(); //stop this test
-
- setPtt(1); //PTT on
- txTestState = type;
-
- TIM2->CR1 &= ~TIM_CR1_CEN; //disable RX timer
- TIM1->CR1 |= TIM_CR1_CEN; //enable DAC timer
-
- NVIC_DisableIRQ(DMA1_Channel2_IRQn); //disable RX DMA interrupt
- NVIC_EnableIRQ(TIM1_UP_IRQn); //enable DAC interrupt
-
- if(type == TEST_MARK)
- {
- TIM1->ARR = markStep;
- }
- else if(type == TEST_SPACE)
- {
- TIM1->ARR = spaceStep;
- }
- else //alternating tones
- {
- //enable baudrate generator
- TIM3->CR1 = TIM_CR1_CEN; //enable timer
- NVIC_EnableIRQ(TIM3_IRQn); //enable interrupt in NVIC
- }
+ if(txTestState != TEST_DISABLED) //TX test is already running
+ ModemTxTestStop(); //stop this test
+
+ setPtt(1); //PTT on
+ txTestState = type;
+
+ TIM2->CR1 &= ~TIM_CR1_CEN; //disable RX timer
+ TIM1->CR1 |= TIM_CR1_CEN; //enable DAC timer
+
+ NVIC_DisableIRQ(DMA1_Channel2_IRQn); //disable RX DMA interrupt
+ NVIC_EnableIRQ(TIM1_UP_IRQn); //enable DAC interrupt
+
+ if(type == TEST_MARK)
+ {
+ TIM1->ARR = markStep;
+ }
+ else if(type == TEST_SPACE)
+ {
+ TIM1->ARR = spaceStep;
+ }
+ else //alternating tones
+ {
+ //enable baudrate generator
+ TIM3->CR1 = TIM_CR1_CEN; //enable timer
+ NVIC_EnableIRQ(TIM3_IRQn); //enable interrupt in NVIC
+ }
}
void ModemTxTestStop(void)
{
- txTestState = TEST_DISABLED;
+ txTestState = TEST_DISABLED;
- TIM3->CR1 &= ~TIM_CR1_CEN; //disable baudrate timer
- TIM1->CR1 &= ~TIM_CR1_CEN; //disable DAC timer
- TIM2->CR1 |= TIM_CR1_CEN; //enable RX timer
+ TIM3->CR1 &= ~TIM_CR1_CEN; //disable baudrate timer
+ TIM1->CR1 &= ~TIM_CR1_CEN; //disable DAC timer
+ TIM2->CR1 |= TIM_CR1_CEN; //enable RX timer
- NVIC_DisableIRQ(TIM3_IRQn);
- NVIC_DisableIRQ(TIM1_UP_IRQn);
- NVIC_EnableIRQ(DMA1_Channel2_IRQn);
+ NVIC_DisableIRQ(TIM3_IRQn);
+ NVIC_DisableIRQ(TIM1_UP_IRQn);
+ NVIC_EnableIRQ(DMA1_Channel2_IRQn);
- setPtt(0); //PTT off
+ setPtt(0); //PTT off
}
void ModemTransmitStart(void)
{
- setPtt(1); //PTT on
+ setPtt(1); //PTT on
- TIM3->CR1 = TIM_CR1_CEN;
- TIM1->CR1 = TIM_CR1_CEN;
- TIM2->CR1 &= ~TIM_CR1_CEN;
+ TIM3->CR1 = TIM_CR1_CEN;
+ TIM1->CR1 = TIM_CR1_CEN;
+ TIM2->CR1 &= ~TIM_CR1_CEN;
- NVIC_DisableIRQ(DMA1_Channel2_IRQn);
- NVIC_EnableIRQ(TIM1_UP_IRQn);
- NVIC_EnableIRQ(TIM3_IRQn);
+ NVIC_DisableIRQ(DMA1_Channel2_IRQn);
+ NVIC_EnableIRQ(TIM1_UP_IRQn);
+ NVIC_EnableIRQ(TIM3_IRQn);
}
@@ -670,17 +642,17 @@ void ModemTransmitStart(void)
*/
void ModemTransmitStop(void)
{
- TIM2->CR1 |= TIM_CR1_CEN;
- TIM3->CR1 &= ~TIM_CR1_CEN;
- TIM1->CR1 &= ~TIM_CR1_CEN;
+ TIM2->CR1 |= TIM_CR1_CEN;
+ TIM3->CR1 &= ~TIM_CR1_CEN;
+ TIM1->CR1 &= ~TIM_CR1_CEN;
- NVIC_DisableIRQ(TIM1_UP_IRQn);
- NVIC_DisableIRQ(TIM3_IRQn);
- NVIC_EnableIRQ(DMA1_Channel2_IRQn);
+ NVIC_DisableIRQ(TIM1_UP_IRQn);
+ NVIC_DisableIRQ(TIM3_IRQn);
+ NVIC_EnableIRQ(DMA1_Channel2_IRQn);
- setPtt(0);
+ setPtt(0);
- TIM4->CCR1 = 44; //set around 50% duty cycle
+ TIM4->CCR1 = 44; //set around 50% duty cycle
}
/**
@@ -689,10 +661,10 @@ void ModemTransmitStop(void)
*/
static void setPtt(uint8_t state)
{
- if(state)
- GPIOB->BSRR = GPIO_BSRR_BS7;
- else
- GPIOB->BSRR = GPIO_BSRR_BR7;
+ if(state)
+ GPIOB->BSRR = GPIO_BSRR_BS7;
+ else
+ GPIOB->BSRR = GPIO_BSRR_BR7;
}
@@ -710,86 +682,108 @@ void ModemInit(void)
* TIM2 is the RX sampling timer with no software interrupt, but it directly calls DMA
*/
- RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
- RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
- RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
- RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
- RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
- RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
- RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
- RCC->AHBENR |= RCC_AHBENR_DMA1EN;
+ RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
+ RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
+ RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
+ RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
+ RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
+ RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
+ RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
+ RCC->AHBENR |= RCC_AHBENR_DMA1EN;
- GPIOC->CRH |= GPIO_CRH_MODE13_1; //DCD LED on PC13
- GPIOC->CRH &= ~GPIO_CRH_MODE13_0;
- GPIOC->CRH &= ~GPIO_CRH_CNF13;
+ GPIOC->CRH |= GPIO_CRH_MODE13_1; //DCD LED on PC13
+ GPIOC->CRH &= ~GPIO_CRH_MODE13_0;
+ GPIOC->CRH &= ~GPIO_CRH_CNF13;
- GPIOB->CRH &= ~0xFFFF0000; //R2R output on PB12-PB15
- GPIOB->CRH |= 0x22220000;
+ GPIOB->CRH &= ~0xFFFF0000; //R2R output on PB12-PB15
+ GPIOB->CRH |= 0x22220000;
- GPIOA->CRL &= ~GPIO_CRL_CNF0; //ADC input on PA0
- GPIOA->CRL &= ~GPIO_CRL_MODE0;
+ GPIOA->CRL &= ~GPIO_CRL_CNF0; //ADC input on PA0
+ GPIOA->CRL &= ~GPIO_CRL_MODE0;
- GPIOB->CRL |= GPIO_CRL_MODE7_1; //PTT output on PB7
- GPIOB->CRL &= ~GPIO_CRL_MODE7_0;
- GPIOB->CRL &= ~GPIO_CRL_CNF7;
+ GPIOB->CRL |= GPIO_CRL_MODE7_1; //PTT output on PB7
+ GPIOB->CRL &= ~GPIO_CRL_MODE7_0;
+ GPIOB->CRL &= ~GPIO_CRL_CNF7;
- GPIOB->CRL |= GPIO_CRL_MODE5_1; //2nd DCD LED on PB5
- GPIOB->CRL &= ~GPIO_CRL_MODE5_0;
- GPIOB->CRL &= ~GPIO_CRL_CNF5;
+ GPIOB->CRL |= GPIO_CRL_MODE5_1; //2nd DCD LED on PB5
+ GPIOB->CRL &= ~GPIO_CRL_MODE5_0;
+ GPIOB->CRL &= ~GPIO_CRL_CNF5;
- RCC->CFGR |= RCC_CFGR_ADCPRE_1; //ADC prescaler /6
- RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;
+ RCC->CFGR |= RCC_CFGR_ADCPRE_1; //ADC prescaler /6
+ RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;
- ADC1->CR2 |= ADC_CR2_CONT; //continuous conversion
- ADC1->CR2 |= ADC_CR2_EXTSEL;
- ADC1->SQR1 &= ~ADC_SQR1_L; //1 conversion
- ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; //41.5 cycle sampling
- ADC1->SQR3 &= ~ADC_SQR3_SQ1; //channel 0 is first in the sequence
- ADC1->CR2 |= ADC_CR2_ADON; //ADC on
+ ADC1->CR2 |= ADC_CR2_CONT; //continuous conversion
+ ADC1->CR2 |= ADC_CR2_EXTSEL;
+ ADC1->SQR1 &= ~ADC_SQR1_L; //1 conversion
+ ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; //41.5 cycle sampling
+ ADC1->SQR3 &= ~ADC_SQR3_SQ1; //channel 0 is first in the sequence
+ ADC1->CR2 |= ADC_CR2_ADON; //ADC on
- ADC1->CR2 |= ADC_CR2_RSTCAL; //calibrate ADC
- while(ADC1->CR2 & ADC_CR2_RSTCAL)
- ;
- ADC1->CR2 |= ADC_CR2_CAL;
- while(ADC1->CR2 & ADC_CR2_CAL)
- ;
+ ADC1->CR2 |= ADC_CR2_RSTCAL; //calibrate ADC
+ while(ADC1->CR2 & ADC_CR2_RSTCAL)
+ ;
+ ADC1->CR2 |= ADC_CR2_CAL;
+ while(ADC1->CR2 & ADC_CR2_CAL)
+ ;
- ADC1->CR2 |= ADC_CR2_EXTTRIG;
- ADC1->CR2 |= ADC_CR2_SWSTART; //start ADC conversion
+ ADC1->CR2 |= ADC_CR2_EXTTRIG;
+ ADC1->CR2 |= ADC_CR2_SWSTART; //start ADC conversion
- //prepare DMA
- DMA1_Channel2->CCR |= DMA_CCR_MSIZE_0; //16 bit memory region
- DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE_1;
- DMA1_Channel2->CCR |= DMA_CCR_PSIZE_0;
- DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE_1;
+ //prepare DMA
+ DMA1_Channel2->CCR |= DMA_CCR_MSIZE_0; //16 bit memory region
+ DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE_1;
+ DMA1_Channel2->CCR |= DMA_CCR_PSIZE_0;
+ DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE_1;
- DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC| DMA_CCR_TCIE; //circular mode, memory increment and interrupt
- DMA1_Channel2->CNDTR = 4; //4 samples
- DMA1_Channel2->CPAR = (uint32_t)&(ADC1->DR); //ADC data register address
- DMA1_Channel2->CMAR = (uint32_t)samples; //sample buffer address
- DMA1_Channel2->CCR |= DMA_CCR_EN; //enable DMA
+ DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC| DMA_CCR_TCIE; //circular mode, memory increment and interrupt
+ DMA1_Channel2->CNDTR = 4; //4 samples
+ DMA1_Channel2->CPAR = (uint32_t)&(ADC1->DR); //ADC data register address
+ DMA1_Channel2->CMAR = (uint32_t)samples; //sample buffer address
+ DMA1_Channel2->CCR |= DMA_CCR_EN; //enable DMA
- NVIC_EnableIRQ(DMA1_Channel2_IRQn);
+ NVIC_EnableIRQ(DMA1_Channel2_IRQn);
- //RX sampling timer
- TIM2->PSC = 8; //72/9=8 MHz
- TIM2->DIER |= TIM_DIER_UDE; //enable calling DMA on timer tick
+ //RX sampling timer
+ TIM2->PSC = 8; //72/9=8 MHz
+ TIM2->DIER |= TIM_DIER_UDE; //enable calling DMA on timer tick
- //TX DAC timer
- TIM1->PSC = 17; //72/18=4 MHz
- TIM1->DIER |= TIM_DIER_UIE;
+ //TX DAC timer
+ TIM1->PSC = 17; //72/18=4 MHz
+ TIM1->DIER |= TIM_DIER_UIE;
- //baudrate timer
- TIM3->PSC = 71; //72/72=1 MHz
- TIM3->DIER |= TIM_DIER_UIE;
+ //baudrate timer
+ TIM3->PSC = 71; //72/72=1 MHz
+ TIM3->DIER |= TIM_DIER_UIE;
- if((ModemConfig.modem == MODEM_1200) || (ModemConfig.modem == MODEM_1200_V23))
+ if((ModemConfig.modem == MODEM_1200) || (ModemConfig.modem == MODEM_1200_V23)
+#ifdef ENABLE_PSK
+ || (ModemConfig.modem == MODEM_BPSK_1200) || (ModemConfig.modem == MODEM_QPSK_1200)
+#endif
+ )
{
- demodCount = 2;
+
+
+ //use one modem in FX.25 mode
+ //FX.25 (RS) functions are not reentrant
+ //also they are BIG
+ if(
+#ifdef ENABLE_FX25
+ Ax25Config.fx25
+#else
+ 0
+#endif
+#ifdef ENABLE_PSK
+ || (ModemConfig.modem == MODEM_BPSK_1200) || (ModemConfig.modem == MODEM_QPSK_1200)
+#endif
+ )
+ demodCount = 1;
+ else
+
+ demodCount = 2;
N = N1200;
baudRate = 1200.f;
@@ -811,27 +805,36 @@ void ModemInit(void)
demodState[1].dcdDec = DCD1200_DEC;
demodState[1].dcdAdjust = DCD1200_PLLTUNE;
- demodState[0].prefilter = PREFILTER_NONE;
- demodState[0].lpf.coeffs = lpf1200;
- demodState[0].lpf.taps = sizeof(lpf1200) / sizeof(*lpf1200);
- demodState[0].lpf.gainShift = 0; //not important, output is always compared with 0
- demodState[1].lpf.coeffs = lpf1200;
+ demodState[1].prefilter = PREFILTER_NONE;
+ demodState[1].lpf.coeffs = (int16_t*)lpf1200;
demodState[1].lpf.taps = sizeof(lpf1200) / sizeof(*lpf1200);
- demodState[1].lpf.gainShift = 0; //not important, output is always compared with 0
+ demodState[1].lpf.gainShift = 15;
+
+ demodState[0].lpf.coeffs = (int16_t*)lpf1200;
+ demodState[0].lpf.taps = sizeof(lpf1200) / sizeof(*lpf1200);
+ demodState[0].lpf.gainShift = 15;
+ demodState[0].prefilter = PREFILTER_NONE;
+
if(ModemConfig.flatAudioIn) //when used with flat audio input, use deemphasis and flat modems
{
- demodState[1].prefilter = PREFILTER_DEEMPHASIS;
- demodState[1].bpf.coeffs = bpf1200Inv;
- demodState[1].bpf.taps = sizeof(bpf1200Inv) / sizeof(*bpf1200Inv);
- demodState[1].bpf.gainShift = 15;
+#ifdef ENABLE_FX25
+ if(Ax25Config.fx25)
+ demodState[0].prefilter = PREFILTER_NONE;
+ else
+#endif
+ demodState[0].prefilter = PREFILTER_DEEMPHASIS;
+ demodState[0].bpf.coeffs = (int16_t*)bpf1200Inv;
+ demodState[0].bpf.taps = sizeof(bpf1200Inv) / sizeof(*bpf1200Inv);
+ demodState[0].bpf.gainShift = 15;
+
}
else //when used with normal (filtered) audio input, use flat and preemphasis modems
{
- demodState[1].prefilter = PREFILTER_PREEMPHASIS;
- demodState[1].bpf.coeffs = bpf1200;
- demodState[1].bpf.taps = sizeof(bpf1200) / sizeof(*bpf1200);
- demodState[1].bpf.gainShift = 15;
+ demodState[0].prefilter = PREFILTER_PREEMPHASIS;
+ demodState[0].bpf.coeffs = (int16_t*)bpf1200;
+ demodState[0].bpf.taps = sizeof(bpf1200) / sizeof(*bpf1200);
+ demodState[0].bpf.gainShift = 15;
}
if(ModemConfig.modem == MODEM_1200) //Bell 202
@@ -865,14 +868,14 @@ void ModemInit(void)
demodState[0].dcdAdjust = DCD300_PLLTUNE;
demodState[0].prefilter = PREFILTER_FLAT;
- demodState[0].bpf.coeffs = bpf300;
+ demodState[0].bpf.coeffs = (int16_t*)bpf300;
demodState[0].bpf.taps = sizeof(bpf300) / sizeof(*bpf300);
demodState[0].bpf.gainShift = 16;
- demodState[0].lpf.coeffs = lpf300;
+ demodState[0].lpf.coeffs = (int16_t*)lpf300;
demodState[0].lpf.taps = sizeof(lpf300) / sizeof(*lpf300);
- demodState[0].lpf.gainShift = 0; //not important, output is always compared with 0
+ demodState[0].lpf.gainShift = 15;
- TIM2->ARR = 416; //8MHz / 416 =~19200 Hz (4*4800 Hz for 4x oversampling)
+ TIM2->ARR = 207; //8MHz / 208 =~38400 Hz (4*9600 Hz for 4x oversampling)
}
else if(ModemConfig.modem == MODEM_9600)
{
@@ -891,7 +894,7 @@ void ModemInit(void)
demodState[0].prefilter = PREFILTER_NONE;
//this filter will be used for RX and TX
- demodState[0].lpf.coeffs = lpf9600;
+ demodState[0].lpf.coeffs = (int16_t*)lpf9600;
demodState[0].lpf.taps = sizeof(lpf9600) / sizeof(*lpf9600);
demodState[0].lpf.gainShift = 16;
@@ -925,21 +928,21 @@ void ModemInit(void)
if(ModemConfig.usePWM)
{
- RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; //configure timer
+ RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; //configure timer
- GPIOB->CRL |= GPIO_CRL_CNF6_1; //configure pin for PWM
- GPIOB->CRL |= GPIO_CRL_MODE6;
- GPIOB->CRL &= ~GPIO_CRL_CNF6_0;
+ GPIOB->CRL |= GPIO_CRL_CNF6_1; //configure pin for PWM
+ GPIOB->CRL |= GPIO_CRL_MODE6;
+ GPIOB->CRL &= ~GPIO_CRL_CNF6_0;
- //set up PWM generation
- TIM4->PSC = 7; //72MHz/8=9MHz
- TIM4->ARR = 90; //9MHz/90=100kHz
+ //set up PWM generation
+ TIM4->PSC = 7; //72MHz/8=9MHz
+ TIM4->ARR = 90; //9MHz/90=100kHz
- TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;
- TIM4->CCER |= TIM_CCER_CC1E;
- TIM4->CCR1 = 44; //initial duty cycle
+ TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;
+ TIM4->CCER |= TIM_CCER_CC1E;
+ TIM4->CCR1 = 44; //initial duty cycle
- TIM4->CR1 |= TIM_CR1_CEN;
+ TIM4->CR1 |= TIM_CR1_CEN;
}
}
diff --git a/Src/fx25.c b/Src/fx25.c
index e693ced..9c84b2e 100644
--- a/Src/fx25.c
+++ b/Src/fx25.c
@@ -5,7 +5,10 @@
#include
#include "rs.h"
+#define FX25_RS_FCR 1
+
#define FX25_PREGENERATE_POLYS
+#define FX25_MAX_DISTANCE 10 //maximum Hamming distance when comparing tags
const struct Fx25Mode Fx25ModeList[11] =
{
@@ -22,8 +25,42 @@ const struct Fx25Mode Fx25ModeList[11] =
{.tag = 0x4A4ABEC4A724B796, .K = 64, .T = 64}
};
-const struct Fx25Mode* Fx25GetMode(uint16_t size)
+static inline uint8_t hammingDistance(uint64_t x, uint64_t y)
+{
+ uint8_t distance = 0;
+ for(uint8_t i = 0; i < 64; i++)
+ {
+ distance += (x ^ y) & 1;
+ x >>= 1;
+ y >>= 1;
+ }
+ return distance;
+}
+
+const struct Fx25Mode* Fx25GetModeForTag(uint64_t tag)
+{
+ struct Fx25Mode *closest = NULL;
+ uint8_t closestDistance = 255;
+ for(uint8_t i = 0; i < sizeof(Fx25ModeList) / sizeof(*Fx25ModeList); i++)
+ {
+ uint8_t distance = hammingDistance(tag, Fx25ModeList[i].tag);
+ if(distance == 0)
+ return &Fx25ModeList[i];
+ else if(distance < closestDistance)
+ {
+ closest = (struct Fx25Mode*)&Fx25ModeList[i];
+ closestDistance = distance;
+ }
+ }
+ if(closestDistance <= FX25_MAX_DISTANCE)
+ return closest;
+ else
+ return NULL;
+}
+
+const struct Fx25Mode* Fx25GetModeForSize(uint16_t size)
{
+ //use "UZ7HO Soundmodem standard" for choosing FX.25 mode
if(size <= 32)
return &Fx25ModeList[3];
else if(size <= 64)
@@ -41,41 +78,71 @@ const struct Fx25Mode* Fx25GetMode(uint16_t size)
}
#ifdef FX25_PREGENERATE_POLYS
-static uint8_t poly16[17], poly32[33], poly64[65];
+static struct LwFecRS rs16, rs32, rs64;
+#else
+static struct LwFecRS rs;
+#endif
+
+void Fx25Encode(uint8_t *buffer, const struct Fx25Mode *mode)
+{
+#ifdef FX25_PREGENERATE_POLYS
+ struct LwFecRS *rs = NULL;
+ switch(mode->T)
+ {
+ case 16:
+ rs = &rs16;
+ break;
+ case 32:
+ rs = &rs32;
+ break;
+ case 64:
+ rs = &rs64;
+ break;
+ default:
+ rs = &rs16;
+ break;
+ }
+ RsEncode(rs, buffer, mode->K);
#else
-static uint8_t poly[65];
+ RsInit(&rs, mode->T, FX25_RS_FCR);
+ RsEncode(&rs, buffer, mode->K);
#endif
-void Fx25AddParity(uint8_t *buffer, const struct Fx25Mode *mode)
+}
+
+bool Fx25Decode(uint8_t *buffer, const struct Fx25Mode *mode, uint8_t *fixed)
{
#ifdef FX25_PREGENERATE_POLYS
- uint8_t *poly = NULL;
+ struct LwFecRS *rs = NULL;
switch(mode->T)
{
case 16:
- poly = poly16;
+ rs = &rs16;
break;
case 32:
- poly = poly32;
+ rs = &rs32;
break;
case 64:
- poly = poly64;
+ rs = &rs64;
break;
default:
- poly = poly16;
+ rs = &rs16;
+ break;
}
+ return RsDecode(rs, buffer, mode->K, fixed);
#else
- RsGeneratePolynomial(mode->T, poly);
+ RsInit(&rs, mode->T, FX25_RS_FCR);
+ return RsDecode(&rs, buffer, mode->K, fixed);
#endif
- RsEncode(buffer, mode->K + mode->T, poly, mode->T);
+
}
void Fx25Init(void)
{
#ifdef FX25_PREGENERATE_POLYS
- RsGeneratePolynomial(16, poly16);
- RsGeneratePolynomial(32, poly32);
- RsGeneratePolynomial(64, poly64);
+ RsInit(&rs16, 16, FX25_RS_FCR);
+ RsInit(&rs32, 32, FX25_RS_FCR);
+ RsInit(&rs64, 64, FX25_RS_FCR);
#else
#endif
}
diff --git a/Src/main.c b/Src/main.c
index 2f37590..e447d3b 100644
--- a/Src/main.c
+++ b/Src/main.c
@@ -98,37 +98,27 @@ static void handleFrame(void)
uint8_t *buf;
uint16_t size = 0;
- uint16_t signalLevel = 0;
+ int8_t peak = 0;
+ int8_t valley = 0;
+ uint8_t signalLevel = 0;
+ uint8_t fixed = 0;
- while(Ax25ReadNextRxFrame(&buf, &size, &signalLevel))
+ while(Ax25ReadNextRxFrame(&buf, &size, &peak, &valley, &signalLevel, &fixed))
{
-
TermSendToAll(MODE_KISS, buf, size);
-
-
if(((UartUsb.mode == MODE_MONITOR) || (Uart1.mode == MODE_MONITOR) || (Uart2.mode == MODE_MONITOR)))
{
- //in general, the RMS of the frame is calculated (excluding preamble!)
- //it it calculated from samples ranging from -4095 to 4095 (amplitude of 4095)
- //that should give a RMS of around 2900 for pure sine wave
- //for pure square wave it should be equal to the amplitude (around 4095)
- //real data contains lots of imperfections (especially mark/space amplitude imbalance) and this value is far smaller than 2900 for standard frames
- //division by 9 was selected by trial and error to provide a value of 100(%) when the input signal had peak-peak voltage of 3.3V
- //TODO: this probably should be done in a different way, like some peak amplitude tracing
- signalLevel /= 9;
-
- if(signalLevel > 100)
+ if(signalLevel > 70)
{
- TermSendToAll(MODE_MONITOR, (uint8_t*)"\r\nInput level too high! Please reduce so most stations are around 50-70%.\r\n", 0);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"\r\nInput level too high! Please reduce so most stations are around 30-50%.\r\n", 0);
}
- else if(signalLevel < 10)
+ else if(signalLevel < 5)
{
- TermSendToAll(MODE_MONITOR, (uint8_t*)"\r\nInput level too low! Please increase so most stations are around 50-70%.\r\n", 0);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"\r\nInput level too low! Please increase so most stations are around 30-50%.\r\n", 0);
}
- TermSendToAll(MODE_MONITOR, (uint8_t*)"(AX.25) Frame received [", 0); //show which modem received the frame: [FP] (flat and preemphasized), [FD] (flat and deemphasized - in flat audio input mode)
- //[F_] (only flat), [_P] (only preemphasized) or [_D] (only deemphasized - in flat audio input mode)
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"(AX.25) Frame received [", 0);
for(uint8_t i = 0; i < ModemGetDemodulatorCount(); i++)
{
if(modemBitmap & (1 << i))
@@ -143,22 +133,33 @@ static void handleFrame(void)
TermSendToAll(MODE_MONITOR, (uint8_t*)"D", 1);
break;
case PREFILTER_FLAT:
- default:
TermSendToAll(MODE_MONITOR, (uint8_t*)"F", 1);
break;
+ case PREFILTER_NONE:
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"*", 1);
+ break;
}
}
else
TermSendToAll(MODE_MONITOR, (uint8_t*)"_", 1);
}
- TermSendToAll(MODE_MONITOR, (uint8_t*)"], signal level ", 0);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"], ", 0);
+ if(fixed != AX25_NOT_FX25)
+ {
+ TermSendNumberToAll(MODE_MONITOR, fixed);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)" bytes fixed, ", 0);
+ }
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"signal level ", 0);
TermSendNumberToAll(MODE_MONITOR, signalLevel);
- TermSendToAll(MODE_MONITOR, (uint8_t*)"%: ", 0);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"% (", 0);
+ TermSendNumberToAll(MODE_MONITOR, peak);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"%/", 0);
+ TermSendNumberToAll(MODE_MONITOR, valley);
+ TermSendToAll(MODE_MONITOR, (uint8_t*)"%): ", 0);
SendTNC2(buf, size);
TermSendToAll(MODE_MONITOR, (uint8_t*)"\r\n", 0);
-
}
diff --git a/lwfec b/lwfec
new file mode 160000
index 0000000..c4b8bbf
--- /dev/null
+++ b/lwfec
@@ -0,0 +1 @@
+Subproject commit c4b8bbf1ff6fa1b3f6fa1a584eab6fb42732a91b