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"