Increase FM broadcast channels, memory channels and scan lists

- Increase FM broadcast memory channels from 20 to 48
- Increase memory channels from 200 to 768
- Increase scan lists from 3 to 10
- Update internal limits, mappings and related logic accordingly
- Minor cleanups and adjustments related to these changes
This commit is contained in:
Armel FAUVEAU committed 2026-01-12 00:46:52 +01:00
1 parent 8bb2915446
commit e5ca2265e4
33 files changed
+1018 -794

No files matched your search

+235 -37
View File
@@ -16,10 +16,6 @@
#ifdef ENABLE_AIRCOPY
//#if !defined(ENABLE_OVERLAY)
// #include "ARMCM0.h"
//#endif
#include "app/aircopy.h"
#include "audio.h"
#include "driver/bk4819.h"
@@ -31,6 +27,7 @@
#include "ui/helper.h"
#include "ui/inputbox.h"
#include "ui/ui.h"
#include <stddef.h>
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
#include "screenshot.h"
@@ -45,14 +42,92 @@ uint8_t gAirCopyIsSendMode;
uint16_t g_FSK_Buffer[36];
static uint16_t AIRCOPY_BlockToOffset(const uint16_t block)
{
if (block < AIRCOPY_MAIN_BLOCKS) {
return (uint16_t)(block * AIRCOPY_BLOCK_SIZE);
}
// ============================================================================
// Transfer Maps Definition
// ============================================================================
const uint16_t extraBlock = (uint16_t)(block - AIRCOPY_MAIN_BLOCKS);
return (uint16_t)(AIRCOPY_EXTRA_START + (extraBlock * AIRCOPY_BLOCK_SIZE));
#define AIRCOPY_BANK_SEGMENTS(bank) \
{ \
{ 0x0000 + (bank)*0x0800, 0x0000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
{ 0x4000 + (bank)*0x0800, 0x4000 + (bank)*0x0800 + 0x0800, AIRCOPY_WRITE_STRUCT }, \
{ 0x8000 + (bank)*0x0100, 0x8000 + (bank)*0x0100 + 0x0100, AIRCOPY_WRITE_BYTES }, \
}
#define AIRCOPY_STD_MAP(seg_array) \
{ \
.segments = seg_array, \
.num_segments = 3, \
.total_blocks = 68 \
}
// total_blocks = 68 (blocs of 64 bytes) because (16 bytes + 16 bytes + 2 bytes) * 128 = 4352 / 64 = 68
#define DECLARE_AIRCOPY_BANK(n) \
static const AIRCOPY_Segment_t AIRCOPY_Segments_Bank##n[] = \
AIRCOPY_BANK_SEGMENTS(n); \
\
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Bank##n = \
AIRCOPY_STD_MAP(AIRCOPY_Segments_Bank##n);
DECLARE_AIRCOPY_BANK(0)
DECLARE_AIRCOPY_BANK(1)
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
DECLARE_AIRCOPY_BANK(2)
DECLARE_AIRCOPY_BANK(3)
#endif
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
DECLARE_AIRCOPY_BANK(4)
DECLARE_AIRCOPY_BANK(5)
#endif
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
DECLARE_AIRCOPY_BANK(6)
DECLARE_AIRCOPY_BANK(7)
#endif
// For settings only
static const AIRCOPY_Segment_t AIRCOPY_Segments_Settings[] = {
{ 0xA000, 0xA160, AIRCOPY_WRITE_BYTES },
};
static const AIRCOPY_TransferMap_t AIRCOPY_Map_Settings = {
.segments = AIRCOPY_Segments_Settings,
.num_segments = 1,
.total_blocks = 6
};
// Finally
static const AIRCOPY_TransferMap_t *AIRCOPY_AvailableMaps[] = {
&AIRCOPY_Map_Bank0,
&AIRCOPY_Map_Bank1,
#if AIRCOPY_NUM_BANKS >= 4 // if 512 MR CHANNEL
&AIRCOPY_Map_Bank2,
&AIRCOPY_Map_Bank3,
#endif
#if AIRCOPY_NUM_BANKS >= 6 // if 758 MR CHANNEL
&AIRCOPY_Map_Bank4,
&AIRCOPY_Map_Bank5,
#endif
#if AIRCOPY_NUM_BANKS >= 8 // if 1024 MR CHANNEL
&AIRCOPY_Map_Bank6,
&AIRCOPY_Map_Bank7,
#endif
&AIRCOPY_Map_Settings,
};
#define AIRCOPY_NUM_MAPS (sizeof(AIRCOPY_AvailableMaps) / sizeof(AIRCOPY_AvailableMaps[0]))
// ============================================================================
// Helper Functions
// ============================================================================
const AIRCOPY_TransferMap_t* AIRCOPY_GetCurrentMap(void)
{
if (gAircopyCurrentMapIndex >= AIRCOPY_NUM_MAPS) {
gAircopyCurrentMapIndex = 0;
}
return AIRCOPY_AvailableMaps[gAircopyCurrentMapIndex];
}
static void AIRCOPY_clear()
@@ -66,9 +141,44 @@ static void AIRCOPY_clear()
#endif
}
static inline const AIRCOPY_Segment_t *AIRCOPY_FindSegmentForOffset(uint16_t off)
{
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
for (uint16_t i = 0; i < map->num_segments; i++)
{
const AIRCOPY_Segment_t *seg = &map->segments[i];
if (off >= seg->start_offset && off < seg->end_offset)
return seg;
}
return NULL;
}
static inline void AIRCOPY_CheckComplete(void)
{
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
uint16_t done = gAirCopyBlockNumber + gErrorsDuringAirCopy;
if (done >= map->total_blocks)
{
gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
#endif
}
}
// ============================================================================
// Send/Receive Functions
// ============================================================================
bool AIRCOPY_SendMessage(void)
{
static uint8_t gAircopySendCountdown = 1;
static uint16_t CurrentOffset = 0;
static uint16_t CurrentSegmentIndex = 0;
if (gAircopyState != AIRCOPY_TRANSFER) {
return 1;
@@ -78,9 +188,36 @@ bool AIRCOPY_SendMessage(void)
return 1;
}
g_FSK_Buffer[1] = AIRCOPY_BlockToOffset(gAirCopyBlockNumber);
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
EEPROM_ReadBuffer(g_FSK_Buffer[1], &g_FSK_Buffer[2], 64);
// Initialize on first call
if (gAirCopyBlockNumber == 0) {
CurrentSegmentIndex = 0;
CurrentOffset = map->segments[0].start_offset;
}
// Advance to next segment if current is done
while (CurrentSegmentIndex < map->num_segments &&
CurrentOffset >= map->segments[CurrentSegmentIndex].end_offset)
{
CurrentSegmentIndex++;
if (CurrentSegmentIndex < map->num_segments) {
CurrentOffset = map->segments[CurrentSegmentIndex].start_offset;
}
}
// Check if transfer is complete
if (CurrentSegmentIndex >= map->num_segments) {
gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
#endif
return 0;
}
// Send data from current offset
g_FSK_Buffer[1] = CurrentOffset;
EEPROM_ReadBuffer(CurrentOffset, &g_FSK_Buffer[2], 64);
g_FSK_Buffer[34] = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
@@ -88,20 +225,14 @@ bool AIRCOPY_SendMessage(void)
g_FSK_Buffer[i + 1] ^= Obfuscation[i % 8];
}
if (++gAirCopyBlockNumber >= AIRCOPY_TOTAL_BLOCKS) {
gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
#endif
//NVIC_SystemReset();
}
RADIO_SetTxParameters();
BK4819_SendFSKData(g_FSK_Buffer);
BK4819_SetupPowerAmplifier(0, 0);
BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
CurrentOffset += 64;
gAirCopyBlockNumber++;
gAircopySendCountdown = 30;
return 0;
@@ -118,10 +249,13 @@ void AIRCOPY_StorePacket(void)
uint16_t Status = BK4819_ReadRegister(BK4819_REG_0B);
BK4819_PrepareFSKReceive();
// Doc says bit 4 should be 1 = CRC OK, 0 = CRC FAIL, but original firmware checks for FAIL.
if ((Status & 0x0010U) != 0 || g_FSK_Buffer[0] != 0xABCD || g_FSK_Buffer[35] != 0xDCBA) {
gErrorsDuringAirCopy++;
BK4819_ResetFSK(); // <- important
BK4819_PrepareFSKReceive(); // <- re-arm proprement
AIRCOPY_CheckComplete();
return;
}
@@ -132,36 +266,67 @@ void AIRCOPY_StorePacket(void)
uint16_t Crc = CRC_Calculate(&g_FSK_Buffer[1], 2 + 64);
if (g_FSK_Buffer[34] != Crc) {
gErrorsDuringAirCopy++;
AIRCOPY_CheckComplete();
return;
}
uint16_t Offset = g_FSK_Buffer[1];
const AIRCOPY_TransferMap_t *map = AIRCOPY_GetCurrentMap();
const bool in_main = (Offset < AIRCOPY_MAIN_END);
const bool in_extra = (Offset >= AIRCOPY_EXTRA_START && Offset < AIRCOPY_EXTRA_END);
// Validate offset is in the map
bool validOffset = false;
for (uint16_t i = 0; i < map->num_segments; i++) {
if (Offset >= map->segments[i].start_offset && Offset < map->segments[i].end_offset) {
validOffset = true;
break;
}
}
if (!in_main && !in_extra) {
if (!validOffset) {
gErrorsDuringAirCopy++;
AIRCOPY_CheckComplete();
return;
}
const uint16_t *pData = &g_FSK_Buffer[2];
for (unsigned int i = 0; i < 8; i++) {
EEPROM_WriteBuffer(Offset, pData);
pData += 4;
Offset += 8;
const AIRCOPY_Segment_t *seg = AIRCOPY_FindSegmentForOffset(Offset);
if (seg == NULL) {
gErrorsDuringAirCopy++;
AIRCOPY_CheckComplete();
return;
}
if (Offset == AIRCOPY_EXTRA_END) {
gAircopyState = AIRCOPY_COMPLETE;
#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
getScreenShot(false);
#endif
if (seg->write_mode == AIRCOPY_WRITE_BYTES)
{
/* Raw bytes stream, written in 8-byte EEPROM chunks */
const uint8_t *p8 = (const uint8_t *)&g_FSK_Buffer[2];
for (unsigned int i = 0; i < 8; i++)
{
EEPROM_WriteBuffer(Offset + (i * 8), p8 + (i * 8));
}
}
else
{
/* Structured data path (kept as-is) */
const uint16_t *pData = &g_FSK_Buffer[2];
for (unsigned int i = 0; i < 8; i++)
{
EEPROM_WriteBuffer(Offset, pData);
pData += 4;
Offset += 8;
}
}
gAirCopyBlockNumber++;
AIRCOPY_CheckComplete();
}
// ============================================================================
// Key Processing
// ============================================================================
static void AIRCOPY_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
if (bKeyHeld || !bKeyPressed) {
@@ -258,6 +423,25 @@ static void AIRCOPY_Key_MENU(bool bKeyPressed, bool bKeyHeld)
gAircopyState = AIRCOPY_TRANSFER;
}
static void AIRCOPY_Key_UP_DOWN(bool bKeyPressed, bool bKeyHeld, int8_t Direction)
{
if (bKeyHeld || !bKeyPressed) {
return;
}
switch(Direction)
{
case 1:
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + 1) % AIRCOPY_NUM_MAPS;
break;
case -1:
gAircopyCurrentMapIndex = (gAircopyCurrentMapIndex + AIRCOPY_NUM_MAPS - 1) % AIRCOPY_NUM_MAPS;
break;
}
gRequestDisplayScreen = DISPLAY_AIRCOPY;
}
void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
switch (Key) {
@@ -279,9 +463,23 @@ void AIRCOPY_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
case KEY_EXIT:
AIRCOPY_Key_EXIT(bKeyPressed, bKeyHeld);
break;
case KEY_UP:
#ifdef ENABLE_NAVIG_LEFT_RIGHT
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
#else
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
#endif
break;
case KEY_DOWN:
#ifdef ENABLE_NAVIG_LEFT_RIGHT
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
#else
AIRCOPY_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
#endif
break;
default:
break;
}
}
#endif
#endif