diff --git a/.cproject b/.cproject
index 632be6e..b0d130b 100644
--- a/.cproject
+++ b/.cproject
@@ -17,16 +17,16 @@
-
-
-
-
+
+
+
-
-
-
+
+
+
@@ -44,12 +44,15 @@
+
-
+
+
@@ -69,16 +72,16 @@
-
+
-
-
-
-
-
+
+
+
+
+
@@ -86,10 +89,10 @@
-
-
-
-
+
+
+
+
@@ -97,18 +100,18 @@
-
+
-
-
-
-
+
+
+
+
+
@@ -157,12 +160,14 @@
+
+
-
+
@@ -182,16 +187,16 @@
-
+
-
-
-
-
+
+
+
+
@@ -199,10 +204,10 @@
-
-
+
+
-
+
@@ -210,18 +215,17 @@
-
+
-
-
-
-
+
+
+
+
@@ -231,17 +235,21 @@
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..dacf203
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "lwfec"]
+ path = lwfec
+ url = https://github.com/sq8vps/lwfec
diff --git a/Inc/ax25.h b/Inc/ax25.h
index fb26893..45ea226 100644
--- a/Inc/ax25.h
+++ b/Inc/ax25.h
@@ -21,9 +21,6 @@ along with VP-Digi. If not, see .
#include
#include
-
-
-
enum Ax25RxStage
{
RX_STAGE_IDLE = 0,
@@ -36,7 +33,8 @@ struct Ax25ProtoConfig
uint16_t txDelayLength; //TXDelay length in ms
uint16_t txTailLength; //TXTail length in ms
uint16_t quietTime; //Quiet time in ms
- uint8_t allowNonAprs; //allow non-APRS packets
+ uint8_t allowNonAprs : 1; //allow non-APRS packets
+ uint8_t fx25 : 1; //enable FX.25 (AX.25 + FEC)
};
extern struct Ax25ProtoConfig Ax25Config;
diff --git a/Inc/fx25.h b/Inc/fx25.h
new file mode 100644
index 0000000..21497ea
--- /dev/null
+++ b/Inc/fx25.h
@@ -0,0 +1,32 @@
+#ifndef FX25_H_
+#define FX25_H_
+
+#ifdef ENABLE_FX25
+
+#include
+
+#define FX25_MAX_BLOCK_SIZE 255
+
+struct Fx25Mode
+{
+ uint64_t tag; //correlation tag
+ uint16_t K; //data size
+ uint8_t T; //parity check size
+};
+
+extern const struct Fx25Mode Fx25ModeList[11];
+
+/**
+ * @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);
+
+void Fx25AddParity(uint8_t *buffer, const struct Fx25Mode *mode);
+
+void Fx25Init(void);
+
+#endif
+
+#endif /* FX25_H_ */
diff --git a/Src/ax25.c b/Src/ax25.c
index 83fe662..1d467bd 100644
--- a/Src/ax25.c
+++ b/Src/ax25.c
@@ -22,12 +22,21 @@ along with VP-Digi. If not, see .
#include "drivers/systick.h"
#include
#include "digipeater.h"
+#include "fx25.h"
struct Ax25ProtoConfig Ax25Config;
//values below must be kept consistent so that FRAME_BUFFER_SIZE >= FRAME_MAX_SIZE * FRAME_MAX_COUNT
-#define FRAME_MAX_SIZE (308) //single frame max length for RX
-//308 bytes is the theoretical max size assuming 2-byte Control, 256-byte info field and 5 digi address fields
+#ifndef ENABLE_FX25
+//for AX.25 308 bytes is the theoretical max size assuming 2-byte Control, 256-byte info field and 5 digi address fields
+#define FRAME_MAX_SIZE (308) //single frame max length
+#else
+//in FX.25 mode the block can be 255 bytes long at most, and the AX.25 frame itself must be even smaller
+//frames that are too long are sent as standard AX.25 frames
+//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
+#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
@@ -41,6 +50,9 @@ struct FrameHandle
uint16_t start;
uint16_t size;
uint16_t signalLevel;
+#ifdef ENABLE_FX25
+ struct Fx25Mode *fx25Mode;
+#endif
};
static uint8_t rxBuffer[FRAME_BUFFER_SIZE]; //circular buffer for received frames
@@ -58,18 +70,28 @@ static uint8_t txFrameHead = 0;
static uint8_t txFrameTail = 0;
static bool txFrameBufferFull = false;
+#ifdef ENABLE_FX25
+static uint8_t txFx25Buffer[FX25_MAX_BLOCK_SIZE];
+static uint8_t txTagByteIdx = 0;
+#endif
+
static uint8_t frameReceived; //a bitmap of receivers that received the frame
enum TxStage
{
- TX_STAGE_IDLE,
+ TX_STAGE_IDLE = 0,
TX_STAGE_PREAMBLE,
TX_STAGE_HEADER_FLAGS,
TX_STAGE_DATA,
TX_STAGE_CRC,
TX_STAGE_FOOTER_FLAGS,
TX_STAGE_TAIL,
+
+#ifdef ENABLE_FX25
+ //stages used in FX.25 mode additionally
+ TX_STAGE_CORRELATION_TAG,
+#endif
};
enum TxInitStage
@@ -170,24 +192,172 @@ void Ax25TxKiss(uint8_t *buf, uint16_t len)
}
}
+#ifdef ENABLE_FX25
+static void *writeFx25Frame(uint8_t *data, uint16_t size)
+{
+ //first calculate how big the frame can be
+ //this includes 2 flags, 2 CRC bytes and all bits added by bitstuffing
+ //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);
+ uint16_t requiredSize = size;
+ if(NULL != fx25Mode)
+ requiredSize = fx25Mode->K + fx25Mode->T;
+ else
+ return NULL; //frame will not fit in FX.25
+
+ uint16_t freeSize = GET_FREE_SIZE(FRAME_BUFFER_SIZE, txBufferHead, txBufferTail);
+ if(freeSize < requiredSize) //check if there is enough size to store full FX.25 (or AX.25) frame
+ {
+ return NULL; //if not, it may fit in standard AX.25
+ }
+
+ txFrame[txFrameHead].size = requiredSize;
+ txFrame[txFrameHead].start = txBufferHead;
+ txFrame[txFrameHead].fx25Mode = (struct Fx25Mode*)fx25Mode;
+
+ uint16_t index = 0;
+ //header flag
+ txFx25Buffer[index++] = 0x7E;
+
+ uint16_t crc = 0xFFFF;
+
+ uint8_t bits = 0; //bit counter within a byte
+ uint8_t bitstuff = 0;
+ for(uint16_t i = 0; i < size + 2; i++)
+ {
+ for(uint8_t k = 0; k < 8; k++)
+ {
+ txFx25Buffer[index] >>= 1;
+ bits++;
+ if(i < size) //frame data
+ {
+ if(data[i] >> k)
+ {
+ calculateCRC(1, &crc);
+ bitstuff++;
+ txFx25Buffer[index] |= 0x80;
+ }
+ else
+ {
+ calculateCRC(0, &crc);
+ bitstuff = 0;
+ }
+ }
+ else //crc
+ {
+ uint8_t c = 0;
+ if(i == size)
+ c = (crc & 0xFF) ^ 0xFF;
+ else
+ c = (crc >> 8) ^ 0xFF;
+
+ if(c >> k)
+ {
+ bitstuff++;
+ txFx25Buffer[index] |= 0x80;
+ }
+ else
+ {
+ bitstuff = 0;
+ }
+ }
+
+ if(bits == 8)
+ {
+ bits = 0;
+ index++;
+ }
+ if(bitstuff == 5)
+ {
+ bits++;
+ bitstuff = 0;
+ txFx25Buffer[index] >>= 1;
+ if(bits == 8)
+ {
+ bits = 0;
+ index++;
+ }
+ }
+ }
+ }
+
+ //pad with flags
+ while(index < fx25Mode->K)
+ {
+ for(uint8_t k = 0; k < 8; k++)
+ {
+ txFx25Buffer[index] >>= 1;
+ bits++;
+
+ if(0x7E >> k)
+ {
+ txFx25Buffer[index] |= 0x80;
+ }
+
+ if(bits == 8)
+ {
+ bits = 0;
+ index++;
+ }
+ }
+ }
+
+ Fx25AddParity(txFx25Buffer, fx25Mode);
+
+ for(uint16_t i = 0; i < (fx25Mode->K + fx25Mode->T); i++)
+ {
+ txBuffer[txBufferHead++] = txFx25Buffer[i];
+ txBufferHead %= FRAME_BUFFER_SIZE;
+ }
+
+ void *ret = &txFrame[txFrameHead];
+ txFrameHead++;
+ txFrameHead %= FRAME_MAX_COUNT;
+ if(txFrameHead == txFrameTail)
+ txFrameBufferFull = true;
+ return ret;
+}
+#endif
+
void *Ax25WriteTxFrame(uint8_t *data, uint16_t size)
{
while(txStage != TX_STAGE_IDLE)
;
- if((GET_FREE_SIZE(FRAME_BUFFER_SIZE, txBufferHead, txBufferTail) < size) || txFrameBufferFull)
+ if(txFrameBufferFull)
+ return NULL;
+
+#ifdef ENABLE_FX25
+ if(Ax25Config.fx25)
+ {
+ void *ret = writeFx25Frame(data, size);
+ if(ret)
+ return ret;
+ }
+#endif
+
+ if(GET_FREE_SIZE(FRAME_BUFFER_SIZE, txBufferHead, txBufferTail) < size)
{
return NULL;
}
-
txFrame[txFrameHead].size = size;
txFrame[txFrameHead].start = txBufferHead;
+
+#ifdef ENABLE_FX25
+ txFrame[txFrameHead].fx25Mode = NULL;
+#endif
+
+
for(uint16_t i = 0; i < size; i++)
{
txBuffer[txBufferHead++] = data[i];
txBufferHead %= FRAME_BUFFER_SIZE;
}
+
+
void *ret = &txFrame[txFrameHead];
txFrameHead++;
txFrameHead %= FRAME_MAX_COUNT;
@@ -366,18 +536,33 @@ uint8_t Ax25GetTxBit(void)
txBitIdx = 0;
if(txStage == TX_STAGE_PREAMBLE) //transmitting preamble (TXDelay)
{
- if(txDelayElapsed < txDelay) //still transmitting
+ if(txDelayElapsed < txDelay)
{
txByte = 0x7E;
txDelayElapsed++;
}
- else //now transmit initial flags
+ else
{
txDelayElapsed = 0;
- txStage = TX_STAGE_HEADER_FLAGS;
+ if(NULL != txFrame[txFrameTail].fx25Mode)
+ {
+ txStage = TX_STAGE_CORRELATION_TAG;
+ txTagByteIdx = 0;
+ }
+ else
+ txStage = TX_STAGE_HEADER_FLAGS;
}
-
}
+#ifdef ENABLE_FX25
+transmitTag:
+ if(txStage == TX_STAGE_CORRELATION_TAG) //FX.25 correlation tag
+ {
+ if(txTagByteIdx < 8)
+ txByte = (txFrame[txFrameTail].fx25Mode->tag >> (8 * txTagByteIdx)) & 0xFF;
+ else
+ txStage = TX_STAGE_DATA;
+ }
+#endif
if(txStage == TX_STAGE_HEADER_FLAGS) //transmitting initial flags
{
if(txFlagsElapsed < STATIC_HEADER_FLAG_COUNT)
@@ -401,7 +586,29 @@ transmitNormalData:
txByte = txBuffer[(txFrame[txFrameTail].start + txByteIdx) % FRAME_BUFFER_SIZE];
txByteIdx++;
}
- else //end of buffer, send CRC
+#ifdef ENABLE_FX25
+ else if(txFrame[txFrameTail].fx25Mode != NULL)
+ {
+ txFrameBufferFull = false;
+ txFrameTail++;
+ txFrameTail %= FRAME_MAX_COUNT;
+ txByteIdx = 0;
+ if((txFrameHead != txFrameTail) || txFrameBufferFull)
+ {
+ if(txFrame[txFrameTail].fx25Mode != NULL)
+ {
+ txStage = TX_STAGE_CORRELATION_TAG;
+ txTagByteIdx = 0;
+ goto transmitTag;
+ }
+ else
+ goto transmitNormalData;
+ }
+ else
+ goto transmitTail;
+ }
+#endif
+ else
{
txStage = TX_STAGE_CRC; //transmit CRC
txCrcByteIdx = 0;
@@ -409,6 +616,7 @@ transmitNormalData:
}
else //no more frames
{
+transmitTail:
txByteIdx = 0;
txBitIdx = 0;
txStage = TX_STAGE_TAIL;
@@ -440,10 +648,19 @@ transmitNormalData:
else
{
txFlagsElapsed = 0;
- txStage = TX_STAGE_DATA; //return to normal data transmission stage. There might be a next frame to transmit
txFrameBufferFull = false;
txFrameTail++;
txFrameTail %= FRAME_MAX_COUNT;
+ txByteIdx = 0;
+#ifdef ENABLE_FX25
+ if(((txFrameHead != txFrameTail) || txFrameBufferFull) && (txFrame[txFrameTail].fx25Mode != NULL))
+ {
+ txStage = TX_STAGE_CORRELATION_TAG;
+ txTagByteIdx = 0;
+ goto transmitTag;
+ }
+#endif
+ txStage = TX_STAGE_DATA; //return to normal data transmission stage. There might be a next frame to transmit
goto transmitNormalData;
}
}
@@ -471,8 +688,8 @@ transmitNormalData:
}
uint8_t txBit = 0;
-
- if((txStage == TX_STAGE_DATA) || (txStage == TX_STAGE_CRC)) //transmitting normal data or CRC
+ //transmitting normal data or CRC in AX.25 mode
+ if((NULL != txFrame[txFrameTail].fx25Mode) || (txStage == TX_STAGE_DATA) || (txStage == TX_STAGE_CRC))
{
if(txBitstuff == 5) //5 consecutive ones transmitted
{
@@ -498,7 +715,8 @@ transmitNormalData:
txBitIdx++;
}
}
- else //transmitting preamble or flags, don't calculate CRC, don't use bit stuffing
+ //transmitting in FX.25 mode or in AX.25 mode, but these are preamble or flags, don't calculate CRC, don't use bit stuffing
+ else
{
txBit = txByte & 1;
txByte >>= 1;
diff --git a/Src/fx25.c b/Src/fx25.c
new file mode 100644
index 0000000..e693ced
--- /dev/null
+++ b/Src/fx25.c
@@ -0,0 +1,83 @@
+
+#ifdef ENABLE_FX25
+
+#include "fx25.h"
+#include
+#include "rs.h"
+
+#define FX25_PREGENERATE_POLYS
+
+const struct Fx25Mode Fx25ModeList[11] =
+{
+ {.tag = 0xB74DB7DF8A532F3E, .K = 239, .T = 16},
+ {.tag = 0x26FF60A600CC8FDE, .K = 128, .T = 16},
+ {.tag = 0xC7DC0508F3D9B09E, .K = 64, .T = 16},
+ {.tag = 0x8F056EB4369660EE, .K = 32, .T = 16},
+ {.tag = 0x6E260B1AC5835FAE, .K = 223, .T = 32},
+ {.tag = 0xFF94DC634F1CFF4E, .K = 128, .T = 32},
+ {.tag = 0x1EB7B9CDBC09C00E, .K = 64, .T = 32},
+ {.tag = 0xDBF869BD2DBB1776, .K = 32, .T = 32},
+ {.tag = 0x3ADB0C13DEAE2836, .K = 191, .T = 64},
+ {.tag = 0xAB69DB6A543188D6, .K = 128, .T = 64},
+ {.tag = 0x4A4ABEC4A724B796, .K = 64, .T = 64}
+};
+
+const struct Fx25Mode* Fx25GetMode(uint16_t size)
+{
+ if(size <= 32)
+ return &Fx25ModeList[3];
+ else if(size <= 64)
+ return &Fx25ModeList[2];
+ else if(size <= 128)
+ return &Fx25ModeList[5];
+ else if(size <= 191)
+ return &Fx25ModeList[8];
+ else if(size <= 223)
+ return &Fx25ModeList[4];
+ else if(size <= 239)
+ return &Fx25ModeList[0];
+ else
+ return NULL; //frame too big, do not use FX.25
+}
+
+#ifdef FX25_PREGENERATE_POLYS
+static uint8_t poly16[17], poly32[33], poly64[65];
+#else
+static uint8_t poly[65];
+#endif
+
+void Fx25AddParity(uint8_t *buffer, const struct Fx25Mode *mode)
+{
+#ifdef FX25_PREGENERATE_POLYS
+ uint8_t *poly = NULL;
+ switch(mode->T)
+ {
+ case 16:
+ poly = poly16;
+ break;
+ case 32:
+ poly = poly32;
+ break;
+ case 64:
+ poly = poly64;
+ break;
+ default:
+ poly = poly16;
+ }
+#else
+ RsGeneratePolynomial(mode->T, poly);
+#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);
+#else
+#endif
+}
+
+#endif
diff --git a/Src/main.c b/Src/main.c
index d0ce825..2f37590 100644
--- a/Src/main.c
+++ b/Src/main.c
@@ -52,7 +52,9 @@ along with VP-Digi. If not, see .
#include "beacon.h"
#include "terminal.h"
#include "config.h"
-
+#ifdef ENABLE_FX25
+#include "fx25.h"
+#endif
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -232,6 +234,9 @@ int main(void)
ConfigRead();
Ax25Init();
+#ifdef ENABLE_FX25
+ Fx25Init();
+#endif
UartInit(&Uart1, USART1, Uart1.baudrate);
UartInit(&Uart2, USART2, Uart2.baudrate);
@@ -265,7 +270,6 @@ int main(void)
Ax25TransmitCheck(); //check for pending transmission request
-
if(UartUsb.rxType != DATA_NOTHING)
{
TermHandleSpecial(&UartUsb);
@@ -290,8 +294,7 @@ int main(void)
BeaconCheck(); //check beacons
-
- if(SysTickGet() > 0xFFFFF000)
+ if(SysTickGet() > 0xFFFFF000) //going to wrap around soon - hard reset the device
NVIC_SystemReset();
}
/* USER CODE END 3 */
diff --git a/bpf1200.c b/bpf1200.c
new file mode 100644
index 0000000..86123a6
--- /dev/null
+++ b/bpf1200.c
@@ -0,0 +1,42 @@
+/**************************************************************
+WinFilter version 0.8
+http://www.winfilter.20m.com
+akundert@hotmail.com
+
+Filter type: Band Pass
+Filter model: Rectangular Window
+Sampling Frequency: 9 KHz
+Fc1 and Fc2 Frequencies: 1.350000 KHz and 3.650000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+#define Ntap 8
+
+#define DCgain 65536
+
+__int16 fir(__int16 NewSample) {
+ __int16 FIRCoef[Ntap] = {
+ 2072,
+ -21528,
+ -1815,
+ 32566,
+ -1815,
+ -21528,
+ 2072,
+ 1547
+ };
+
+ static __int16 x[Ntap]; //input samples
+ __int32 y=0; //output sample
+ int n;
+
+ //shift the old samples
+ for(n=Ntap-1; n>0; n--)
+ x[n] = x[n-1];
+
+ //Calculate the new output
+ x[0] = NewSample;
+ for(n=0; n0; n--)
+ x[n] = x[n-1];
+
+ //Calculate the new output
+ x[0] = NewSample;
+ for(n=0; n