diff --git a/data_embed/aprs-symbols-24-0.png b/data_embed/aprs-symbols-24-0.png
deleted file mode 100644
index 92bf114..0000000
Binary files a/data_embed/aprs-symbols-24-0.png and /dev/null differ
diff --git a/data_embed/aprs-symbols-24-1.png b/data_embed/aprs-symbols-24-1.png
deleted file mode 100644
index b83d4b1..0000000
Binary files a/data_embed/aprs-symbols-24-1.png and /dev/null differ
diff --git a/data_embed/aprs-symbols-48-0.png b/data_embed/aprs-symbols-48-0.png
new file mode 100644
index 0000000..79e6ef1
Binary files /dev/null and b/data_embed/aprs-symbols-48-0.png differ
diff --git a/data_embed/aprs-symbols-48-1.png b/data_embed/aprs-symbols-48-1.png
new file mode 100644
index 0000000..f7e1a78
Binary files /dev/null and b/data_embed/aprs-symbols-48-1.png differ
diff --git a/data_embed/aprs-symbols-48-2.png b/data_embed/aprs-symbols-48-2.png
new file mode 100644
index 0000000..ac8918a
Binary files /dev/null and b/data_embed/aprs-symbols-48-2.png differ
diff --git a/data_embed/script.js b/data_embed/script.js
index 0cb217d..31a417c 100644
--- a/data_embed/script.js
+++ b/data_embed/script.js
@@ -668,8 +668,9 @@ function setMapMessage(text) {
* "L&" Alternate table with overlay
*
* Sprite layout:
- * aprs-symbols-24-0.png = Primary table
- * aprs-symbols-24-1.png = Alternate table
+ * aprs-symbols-48-0.png = Primary table
+ * aprs-symbols-48-1.png = Alternate table
+ * aprs-symbols-48-2.png = Overlay characters
*/
function getAprsSymbolIcon(symbol, callsign = "") {
@@ -715,18 +716,23 @@ function getAprsSymbolIcon(symbol, callsign = "") {
const index = ascii - 33;
/*
- * Sprite sheet contains 16 columns.
- * Each cell is 24 x 24 px.
+ * Sprite sheet contains 16 columns x 6 rows.
+ * Each cell occupies 24x24 CSS px, but the underlying PNG assets are
+ * actually 48x48 px/cell (2x) for sharpness on high-DPI screens; the
+ * "background-size" below tells the browser to scale the sheet back
+ * down to its original 24px-per-cell layout, so all position math
+ * stays in 24px units regardless of source resolution.
*/
const columns = 16;
+ const sheetSize = "384px 144px"; // 16*24 x 6*24, same for all 3 sheets
const col = index % columns;
const row = Math.floor(index / columns);
const sprite =
table === "/"
- ? "/aprs-symbols-24-0.png"
- : "/aprs-symbols-24-1.png";
+ ? "/aprs-symbols-48-0.png"
+ : "/aprs-symbols-48-1.png";
const x = -(col * 24);
const y = -(row * 24);
@@ -752,28 +758,48 @@ function getAprsSymbolIcon(symbol, callsign = "") {
);
}
- overlayHtml = `
-
- ${displayOverlay}
-
- `;
+ /*
+ * The overlay sprite sheet does NOT follow the ascii-33 layout used
+ * by the primary/alternate tables above; it has its own grid:
+ * "0" at row 0 col 15, "1".."9" at row 1 col 0-8, "A".."P" at row 2,
+ * "Q".."Z" at row 3 col 0-9. Verified by hand against the PNG.
+ */
+ let overlayCol = null;
+ let overlayRow = null;
+
+ if (displayOverlay === "0") {
+ overlayCol = 15;
+ overlayRow = 0;
+ } else if (displayOverlay >= "1" && displayOverlay <= "9") {
+ overlayCol = displayOverlay.charCodeAt(0) - "1".charCodeAt(0);
+ overlayRow = 1;
+ } else if (displayOverlay >= "A" && displayOverlay <= "P") {
+ overlayCol = displayOverlay.charCodeAt(0) - "A".charCodeAt(0);
+ overlayRow = 2;
+ } else if (displayOverlay >= "Q" && displayOverlay <= "Z") {
+ overlayCol = displayOverlay.charCodeAt(0) - "Q".charCodeAt(0);
+ overlayRow = 3;
+ }
+
+ if (overlayCol !== null) {
+ const ox = -(overlayCol * 24);
+ const oy = -(overlayRow * 24);
+
+ overlayHtml = `
+
+ `;
+ }
}
return L.divIcon({
@@ -786,6 +812,7 @@ function getAprsSymbolIcon(symbol, callsign = "") {
background-image:url('${sprite}');
background-repeat:no-repeat;
background-position:${x}px ${y}px;
+ background-size:${sheetSize};
">
${overlayHtml}
@@ -796,12 +823,12 @@ function getAprsSymbolIcon(symbol, callsign = "") {
font:600 10px Arial,sans-serif;
line-height:12px;
white-space:nowrap;
- color:#fff;
+ color:#111;
text-shadow:
- -1px -1px 0 #000,
- 1px -1px 0 #000,
- -1px 1px 0 #000,
- 1px 1px 0 #000;
+ -1px -1px 0 #fff,
+ 1px -1px 0 #fff,
+ -1px 1px 0 #fff,
+ 1px 1px 0 #fff;
pointer-events:none;
">${callsign}
diff --git a/platformio.ini b/platformio.ini
index b4bf4b5..2bbf4ee 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -29,8 +29,9 @@ board_build.embed_files =
data_embed/leaflet.css.gz
data_embed/leaflet.js.gz
data_embed/favicon.png.gz
- data_embed/aprs-symbols-24-0.png.gz
- data_embed/aprs-symbols-24-1.png.gz
+ data_embed/aprs-symbols-48-0.png.gz
+ data_embed/aprs-symbols-48-1.png.gz
+ data_embed/aprs-symbols-48-2.png.gz
extra_scripts =
pre:tools/compress.py
debug_tool = esp-prog
\ No newline at end of file
diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp
index 8a23b27..e2bc68a 100644
--- a/src/LoRa_APRS_iGate.cpp
+++ b/src/LoRa_APRS_iGate.cpp
@@ -68,7 +68,7 @@ ___________________________________________________________________*/
#endif
-String versionDate = "2026-08-24";
+String versionDate = "2026-08-25";
String versionNumber = "4.0.1";
Configuration Config;
WiFiClient aprsIsClient;
diff --git a/src/web_utils.cpp b/src/web_utils.cpp
index ec81b87..c5c3b53 100644
--- a/src/web_utils.cpp
+++ b/src/web_utils.cpp
@@ -62,13 +62,17 @@ extern const unsigned char favicon_data[] asm("_binary_data_embed_favicon_png_gz
extern const unsigned char favicon_data_end[] asm("_binary_data_embed_favicon_png_gz_end");
extern const size_t favicon_data_len = favicon_data_end - favicon_data;
-extern const unsigned char aprs_symbols_24_0_data[] asm("_binary_data_embed_aprs_symbols_24_0_png_gz_start");
-extern const unsigned char aprs_symbols_24_0_data_end[] asm("_binary_data_embed_aprs_symbols_24_0_png_gz_end");
-extern const size_t aprs_symbols_24_0_data_len = aprs_symbols_24_0_data_end - aprs_symbols_24_0_data;
+extern const unsigned char aprs_symbols_48_0_data[] asm("_binary_data_embed_aprs_symbols_48_0_png_gz_start");
+extern const unsigned char aprs_symbols_48_0_data_end[] asm("_binary_data_embed_aprs_symbols_48_0_png_gz_end");
+extern const size_t aprs_symbols_48_0_data_len = aprs_symbols_48_0_data_end - aprs_symbols_48_0_data;
-extern const unsigned char aprs_symbols_24_1_data[] asm("_binary_data_embed_aprs_symbols_24_1_png_gz_start");
-extern const unsigned char aprs_symbols_24_1_data_end[] asm("_binary_data_embed_aprs_symbols_24_1_png_gz_end");
-extern const size_t aprs_symbols_24_1_data_len = aprs_symbols_24_1_data_end - aprs_symbols_24_1_data;
+extern const unsigned char aprs_symbols_48_1_data[] asm("_binary_data_embed_aprs_symbols_48_1_png_gz_start");
+extern const unsigned char aprs_symbols_48_1_data_end[] asm("_binary_data_embed_aprs_symbols_48_1_png_gz_end");
+extern const size_t aprs_symbols_48_1_data_len = aprs_symbols_48_1_data_end - aprs_symbols_48_1_data;
+
+extern const unsigned char aprs_symbols_48_2_data[] asm("_binary_data_embed_aprs_symbols_48_2_png_gz_start");
+extern const unsigned char aprs_symbols_48_2_data_end[] asm("_binary_data_embed_aprs_symbols_48_2_png_gz_end");
+extern const size_t aprs_symbols_48_2_data_len = aprs_symbols_48_2_data_end - aprs_symbols_48_2_data;
namespace WEB_Utils {
@@ -100,24 +104,36 @@ namespace WEB_Utils {
request->send(response);
}
- void handleAprsSymbols24Primary(AsyncWebServerRequest *request) {
+ void handleAprsSymbols48Primary(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(
200,
"image/png",
- (const uint8_t*)aprs_symbols_24_0_data,
- aprs_symbols_24_0_data_len
+ (const uint8_t*)aprs_symbols_48_0_data,
+ aprs_symbols_48_0_data_len
);
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", "max-age=3600");
request->send(response);
}
- void handleAprsSymbols24Alternate(AsyncWebServerRequest *request) {
+ void handleAprsSymbols48Alternate(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(
200,
"image/png",
- (const uint8_t*)aprs_symbols_24_1_data,
- aprs_symbols_24_1_data_len
+ (const uint8_t*)aprs_symbols_48_1_data,
+ aprs_symbols_48_1_data_len
+ );
+ response->addHeader("Content-Encoding", "gzip");
+ response->addHeader("Cache-Control", "max-age=3600");
+ request->send(response);
+ }
+
+ void handleAprsSymbols48Overlay(AsyncWebServerRequest *request) {
+ AsyncWebServerResponse *response = request->beginResponse(
+ 200,
+ "image/png",
+ (const uint8_t*)aprs_symbols_48_2_data,
+ aprs_symbols_48_2_data_len
);
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", "max-age=3600");
@@ -433,8 +449,9 @@ namespace WEB_Utils {
server.on("/leaflet.css", HTTP_GET, handleLeafletStyle);
server.on("/leaflet.js", HTTP_GET, handleLeafletScript);
server.on("/favicon.png", HTTP_GET, handleFavicon);
- server.on("/aprs-symbols-24-0.png", HTTP_GET, handleAprsSymbols24Primary);
- server.on("/aprs-symbols-24-1.png", HTTP_GET, handleAprsSymbols24Alternate);
+ server.on("/aprs-symbols-48-0.png", HTTP_GET, handleAprsSymbols48Primary);
+ server.on("/aprs-symbols-48-1.png", HTTP_GET, handleAprsSymbols48Alternate);
+ server.on("/aprs-symbols-48-2.png", HTTP_GET, handleAprsSymbols48Overlay);
OTA_Utils::setup(&server); // Include OTA Updater for WebServer
diff --git a/tools/compress.py b/tools/compress.py
index fe75697..9154103 100644
--- a/tools/compress.py
+++ b/tools/compress.py
@@ -29,8 +29,9 @@ files = [
'data_embed/leaflet.js',
'data_embed/leaflet.css',
'data_embed/favicon.png',
- 'data_embed/aprs-symbols-24-0.png',
- 'data_embed/aprs-symbols-24-1.png',
+ 'data_embed/aprs-symbols-48-0.png',
+ 'data_embed/aprs-symbols-48-1.png',
+ 'data_embed/aprs-symbols-48-2.png',
]
string_to_find_str = "String"