diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..30bab2a
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..53624c9
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/fskmodem.c b/app/fskmodem.c
new file mode 100644
index 0000000..6f8924f
--- /dev/null
+++ b/app/fskmodem.c
@@ -0,0 +1,219 @@
+//
+// Created by bruno on 25.2.2025.
+//
+
+#include "fskmodem.h"
+
+uint16_t TONE2_FREQ;
+
+uint8_t seq = 0;
+DataPacket dataPacket;
+
+DataPacket inBoundPacket;
+
+uint8_t *dataPTR = dataPacket.data;
+
+SMSEnteringState gEnteringSMS = SMS_NOT_ENTERING;
+
+void MSG_ConfigureFSK(bool rx) {
+ BK4819_WriteRegister(BK4819_REG_70, TONE2_ENABLE_BIT | (96U << 0));
+
+ switch (gEeprom.FSKMode) {
+ case MOD_AFSK_2400:
+ TONE2_FREQ = 24779U; // Estimated
+ break;
+ case MOD_AFSK_1200:
+ TONE2_FREQ = 12389U;
+ break;
+ case MOD_FSK_700:
+ TONE2_FREQ = 7227U;
+ break;
+ case MOD_FSK_450:
+ TONE2_FREQ = 4646U;
+ break;
+ case MOD_FSK_1200_2400:
+ TONE2_FREQ = 12389U; // Default to 1200Hz, dynamic switching may be required
+ break;
+ case MOD_NOAA_SAME:
+ TONE2_FREQ = 2083U; // NOAA SAME uses 2083.3 Hz for mark and 1562.5 Hz for space
+ break;
+ }
+
+
+ BK4819_WriteRegister(BK4819_REG_72, TONE2_FREQ);
+
+// uint16_t fskConfig = FSK_ENABLE_BIT | FSK_RX_BW_1_2K | FSK_RX_GAIN_DEFAULT | FSK_PREAMBLE_TYPE_AA;
+// if (gEeprom.FSKMode == MOD_AFSK_1200) {
+// fskConfig = FSK_ENABLE_BIT | FSK_TX_MODE_FSK_1_2K_2_4K | FSK_RX_MODE_FSK_1_2K_2_4K_NOAA;
+// }
+
+ uint16_t fskConfig = FSK_ENABLE_BIT | FSK_PREAMBLE_TYPE_AA;
+
+ switch (gEeprom.FSKMode) {
+ case MOD_FSK_700:
+ case MOD_FSK_450:
+ case MOD_FSK_1200_2400:
+ fskConfig |= FSK_TX_MODE_FSK_1_2K_2_4K | FSK_RX_MODE_FSK_1_2K_2_4K_NOAA | FSK_RX_BW_1_2K;
+ break;
+ case MOD_AFSK_1200:
+ fskConfig |= FSK_TX_MODE_FFSK_1200_1800 | FSK_RX_MODE_FFSK_1200_1800 | FSK_RX_BW_FFSK_1200_1800;
+ break;
+ case MOD_AFSK_2400:
+ fskConfig |= FSK_TX_MODE_FFSK_1200_2400 | FSK_RX_MODE_FFSK_1200_2400 | FSK_RX_BW_2_4K_FFSK_1200_2400;
+ break;
+ case MOD_NOAA_SAME:
+ fskConfig |= FSK_TX_MODE_NOAA_SAME | FSK_RX_MODE_FSK_1_2K_2_4K_NOAA | FSK_RX_BW_NOAA_SAME;
+ break;
+ default:
+ // Unsupported mode; keep previous setting
+ break;
+ }
+
+
+ BK4819_WriteRegister(BK4819_REG_58, fskConfig);
+
+ BK4819_WriteRegister(BK4819_REG_5A, FSK_SYNC_BYTE0 << 8 | FSK_SYNC_BYTE1);
+ BK4819_WriteRegister(BK4819_REG_5B, FSK_SYNC_BYTE2 << 8 | FSK_SYNC_BYTE3);
+ BK4819_WriteRegister(BK4819_REG_5C, FSK_CRC_ON);
+
+ if (rx) {
+ BK4819_WriteRegister(BK4819_REG_5E, (64U << 3) | (1U << 0));
+ }
+
+ size_t size = sizeof(dataPacket);
+ if (rx) {
+ size = (((size + 1) / 2) * 2) + 2;
+ }
+ BK4819_WriteRegister(BK4819_REG_5D, (size << 8));
+
+ BK4819_FskClearFifo();
+
+ uint16_t fskParams = FSK_SYNC_LEN_BIT | ((rx ? 0U : 15U) << 4);
+ if (gCurrentVfo->SCRAMBLING_TYPE > 0) {
+ fskParams |= FSK_SCRAMBLE_ENABLE;
+ }
+ BK4819_WriteRegister(BK4819_REG_59, fskParams);
+
+ BK4819_WriteRegister(BK4819_REG_02, 0);
+}
+
+
+void MSG_EnableRX(const bool enable) {
+
+ if (enable) {
+ MSG_ConfigureFSK(true);
+
+ //if(gEeprom.MESSENGER_CONFIG.data.receive)
+ BK4819_FskEnableRx();
+ } else {
+ BK4819_WriteRegister(BK4819_REG_70, 0);
+ BK4819_WriteRegister(BK4819_REG_58, 0);
+ }
+}
+
+void MSG_FSKSendData() {
+
+ // turn off CTCSS/CDCSS during FFSK
+ const uint16_t css_val = BK4819_ReadRegister(BK4819_REG_51);
+ BK4819_WriteRegister(BK4819_REG_51, 0);
+
+ // set the FM deviation level
+ const uint16_t dev_val = BK4819_ReadRegister(BK4819_REG_40);
+
+ {
+ uint16_t deviation;
+ switch (gEeprom.VfoInfo[gEeprom.TX_VFO].CHANNEL_BANDWIDTH) {
+ case BK4819_FILTER_BW_WIDE:
+ deviation = 1300;
+ break; // 20k // measurements by kamilsss655
+ case BK4819_FILTER_BW_NARROW:
+ deviation = 1200;
+ break; // 10k
+ // case BK4819_FILTER_BW_NARROWAVIATION: deviation = 850; break; // 5k
+ // case BK4819_FILTER_BW_NARROWER: deviation = 850; break; // 5k
+ // case BK4819_FILTER_BW_NARROWEST: deviation = 850; break; // 5k
+ default:
+ deviation = 850;
+ break; // 5k
+ }
+
+ //BK4819_WriteRegister(0x40, (3u << 12) | (deviation & 0xfff));
+ BK4819_WriteRegister(BK4819_REG_40, (dev_val & 0xf000) | (deviation & 0xfff));
+ }
+
+ // REG_2B 0
+ //
+ // <15> 1 Enable CTCSS/CDCSS DC cancellation after FM Demodulation 1 = enable 0 = disable
+ // <14> 1 Enable AF DC cancellation after FM Demodulation 1 = enable 0 = disable
+ // <10> 0 AF RX HPF 300Hz filter 0 = enable 1 = disable
+ // <9> 0 AF RX LPF 3kHz filter 0 = enable 1 = disable
+ // <8> 0 AF RX de-emphasis filter 0 = enable 1 = disable
+ // <2> 0 AF TX HPF 300Hz filter 0 = enable 1 = disable
+ // <1> 0 AF TX LPF filter 0 = enable 1 = disable
+ // <0> 0 AF TX pre-emphasis filter 0 = enable 1 = disable
+ //
+ // disable the 300Hz HPF and FM pre-emphasis filter
+ //
+ const uint16_t filt_val = BK4819_ReadRegister(BK4819_REG_2B);
+ BK4819_WriteRegister(BK4819_REG_2B, (1u << 2) | (1u << 0));
+
+ MSG_ConfigureFSK(false);
+
+
+ SYSTEM_DelayMs(100);
+
+ { // load the entire packet data into the TX FIFO buffer
+ uint16_t *ptr = (uint16_t *) &dataPacket;
+ size_t wordCount = sizeof(dataPacket) / sizeof(uint16_t);
+
+ for (size_t i = 0; i < wordCount; i++) {
+ BK4819_WriteRegister(BK4819_REG_5F, ptr[i]);
+ }
+ }
+
+ // enable FSK TX
+ BK4819_FskEnableTx();
+
+ {
+ // allow up to 310ms for the TX to complete
+ // if it takes any longer then somethings gone wrong, we shut the TX down
+ unsigned int timeout = 1000 / 5;
+
+ while (timeout-- > 0) {
+ SYSTEM_DelayMs(5);
+ if (BK4819_ReadRegister(BK4819_REG_0C) & (1u << 0)) { // we have interrupt flags
+ BK4819_WriteRegister(BK4819_REG_02, 0);
+ if (BK4819_ReadRegister(BK4819_REG_02) & BK4819_REG_02_FSK_TX_FINISHED)
+ timeout = 0; // TX is complete
+ }
+ }
+ }
+ //BK4819_WriteRegister(BK4819_REG_02, 0);
+
+ SYSTEM_DelayMs(100);
+
+ // disable TX
+ MSG_ConfigureFSK(true);
+
+ // restore FM deviation level
+ BK4819_WriteRegister(BK4819_REG_40, dev_val);
+
+ // restore TX/RX filtering
+ BK4819_WriteRegister(BK4819_REG_2B, filt_val);
+
+ // restore the CTCSS/CDCSS setting
+ BK4819_WriteRegister(BK4819_REG_51, css_val);
+
+ SYSTEM_DelayMs(50);
+ APP_EndTransmission();
+ FUNCTION_Select(FUNCTION_FOREGROUND);
+ gUpdateStatus = true;
+ gUpdateDisplay = true;
+ gFlagEndTransmission = false;
+
+}
+
+void prepareDataPacket() {
+ dataPacket.src = gEeprom.FSKSRCAddress;
+ dataPacket.seq = seq++;
+}
\ No newline at end of file
diff --git a/app/fskmodem.h b/app/fskmodem.h
new file mode 100644
index 0000000..1ba844e
--- /dev/null
+++ b/app/fskmodem.h
@@ -0,0 +1,106 @@
+//
+// Created by bruno on 25.2.2025.
+//
+
+#ifndef UV_K5_FIRMWARE_CUSTOM_FSKMODEM_H
+#define UV_K5_FIRMWARE_CUSTOM_FSKMODEM_H
+
+#include
+#include
+#include "../driver/bk4819.h"
+#include "../driver/bk4819-regs.h"
+#include "../settings.h"
+#include "stddef.h"
+#include "../driver/system.h"
+#include "../misc.h"
+#include "../app/app.h"
+
+// REG_70 bit definitions
+#define TONE1_ENABLE_BIT (1U << 15)
+#define TONE1_GAIN_MASK (0x7FU << 8)
+#define TONE2_ENABLE_BIT (1U << 7)
+#define TONE2_GAIN_MASK (0x7FU << 0)
+
+// REG_58 bit definitions
+// FSK Tx Mode Selection (REG_58<15:13>)
+#define FSK_TX_MODE_FSK_1_2K_2_4K (0U << 13) // 000
+#define FSK_TX_MODE_FFSK_1200_1800 (1U << 13) // 001
+#define FSK_TX_MODE_FFSK_1200_2400 (3U << 13) // 011
+#define FSK_TX_MODE_NOAA_SAME (5U << 13) // 101
+
+// FSK Rx Mode Selection (REG_58<12:10>)
+#define FSK_RX_MODE_FSK_1_2K_2_4K_NOAA (0U << 10) // 000
+#define FSK_RX_MODE_FFSK_1200_1800 (7U << 10) // 111
+#define FSK_RX_MODE_FFSK_1200_2400 (4U << 10) // 100
+
+// FSK Rx Gain (REG_58<9:8>)
+#define FSK_RX_GAIN_DEFAULT (0U << 8) // 00
+#define FSK_RX_GAIN_TYPE_C (1U << 8) // 01
+#define FSK_RX_GAIN_TYPE_B (2U << 8) // 10
+#define FSK_UNKNOWN_MASK (3U << 6)
+// FSK Preamble Type Selection (REG_58<5:4>)
+#define FSK_PREAMBLE_TYPE_AA (3U << 4) // 11
+#define FSK_PREAMBLE_TYPE_55 (2U << 4) // 10
+#define FSK_PREAMBLE_TYPE_UNKNOWN (0U << 4) // 00
+// FSK Rx Bandwidth Setting (REG_58<3:1>)
+#define FSK_RX_BW_2_4K_FFSK_1200_2400 (4U << 1) // 100
+#define FSK_RX_BW_1_2K (0U << 1) // 000
+#define FSK_RX_BW_FFSK_1200_1800 (1U << 1) // 001
+#define FSK_RX_BW_NOAA_SAME (2U << 1) // 010
+#define FSK_ENABLE_BIT (1U << 0)
+
+// REG_59 bit definitions
+#define FSK_CLEAR_TX_FIFO (1U << 15)
+#define FSK_CLEAR_RX_FIFO (1U << 14)
+#define FSK_SCRAMBLE_ENABLE (1U << 13)
+#define FSK_RX_ENABLE (1U << 12)
+#define FSK_TX_ENABLE (1U << 11)
+#define FSK_INVERT_RX (1U << 10)
+#define FSK_INVERT_TX (1U << 9)
+#define FSK_PREAMBLE_LEN_MASK (15U << 4)
+#define FSK_SYNC_LEN_BIT (1U << 3)
+
+//#define FSK_SYNC_BYTE0 0x30
+//#define FSK_SYNC_BYTE1 0x72
+//#define FSK_SYNC_BYTE2 0x57
+//#define FSK_SYNC_BYTE3 0x6c
+
+#define FSK_SYNC_BYTE0 0x42
+#define FSK_SYNC_BYTE1 0x52
+#define FSK_SYNC_BYTE2 0x4E
+#define FSK_SYNC_BYTE3 0x51
+
+#define FSK_CRC_ON (1U << 6)
+#define FSK_CRC_OFF (0U << 6)
+
+#define DataPacketDataSize (36)
+
+typedef struct {
+ uint32_t dest;
+ uint32_t src;
+ uint8_t seq;
+ uint8_t flags;
+ uint8_t data[DataPacketDataSize];
+} DataPacket;
+
+void prepareDataPacket();
+
+void MSG_FSKSendData();
+
+void MSG_EnableRX(bool enable);
+
+extern DataPacket dataPacket;
+
+extern DataPacket inBoundPacket;
+
+typedef enum {
+ SMS_NOT_ENTERING,
+ SMS_ENTERING_DEST,
+ SMS_ENTERING_MESSAGE
+} SMSEnteringState;
+
+extern SMSEnteringState gEnteringSMS;
+
+extern uint8_t *dataPTR;
+
+#endif //UV_K5_FIRMWARE_CUSTOM_FSKMODEM_H