From 9fb03ae237e0810e82d3a7205e8268b756c27711 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 2 Apr 2026 11:23:01 +0200 Subject: [PATCH 1/5] Initial scaffold --- lib/I18n/translations/english.yaml | 19 + lib/Weather/WeatherClient.cpp | 334 +++++++++++++ lib/Weather/WeatherClient.h | 24 + lib/Weather/WeatherData.h | 56 +++ lib/Weather/WeatherIcons.cpp | 396 +++++++++++++++ lib/Weather/WeatherIcons.h | 79 +++ lib/Weather/WeatherSettingsStore.cpp | 96 ++++ lib/Weather/WeatherSettingsStore.h | 57 +++ src/activities/weather/WeatherActivity.cpp | 467 ++++++++++++++++++ src/activities/weather/WeatherActivity.h | 51 ++ .../weather/WeatherSettingsActivity.cpp | 290 +++++++++++ .../weather/WeatherSettingsActivity.h | 44 ++ 12 files changed, 1913 insertions(+) create mode 100644 lib/Weather/WeatherClient.cpp create mode 100644 lib/Weather/WeatherClient.h create mode 100644 lib/Weather/WeatherData.h create mode 100644 lib/Weather/WeatherIcons.cpp create mode 100644 lib/Weather/WeatherIcons.h create mode 100644 lib/Weather/WeatherSettingsStore.cpp create mode 100644 lib/Weather/WeatherSettingsStore.h create mode 100644 src/activities/weather/WeatherActivity.cpp create mode 100644 src/activities/weather/WeatherActivity.h create mode 100644 src/activities/weather/WeatherSettingsActivity.cpp create mode 100644 src/activities/weather/WeatherSettingsActivity.h diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index df91c1a1..b754c46d 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -290,3 +290,22 @@ STR_LINK: "[link]" STR_SCREENSHOT_BUTTON: "Take screenshot" STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)" +STR_WEATHER_LOCATION: "Location: " +STR_WEATHER_NO_LOCATION: "No location set for weather" +STR_WEATHER_FETCH_FAILED: "Failed to fetch weather" +STR_WEATHER_SETTINGS: "Settings" +STR_WEATHER_REFRESH: "Refresh Weather" +STR_WEATHER_FEELS_LIKE: "Feels like: " +STR_WEATHER_HUMIDITY: "Humidity: " +STR_WEATHER_WIND: "Wind: " +STR_WEATHER_PRESSURE: "Pressure: " +STR_WEATHER_LAST_UPDATED: "Last updated: " +STR_WEATHER_PRECIP: "Precipitation: " +STR_WEATHER_PRECIP_UNIT: "Units:" +STR_WEATHER_WIND_UNIT: "Units:" +STR_WEATHER_TEMP_UNIT: "Units:" +STR_WEATHER_48H_FORECAST: "48-hour forecast" +STR_WEATHER_SEARCH_CITY: "Search city" +STR_WEATHER_LONGITUDE: "Longitude: " +STR_WEATHER_LATITUDE: "Latitude: " +STR_WEATHER_SEARCH_RESULTS: "Search results" diff --git a/lib/Weather/WeatherClient.cpp b/lib/Weather/WeatherClient.cpp new file mode 100644 index 00000000..8c9a4383 --- /dev/null +++ b/lib/Weather/WeatherClient.cpp @@ -0,0 +1,334 @@ +#include "WeatherClient.h" + +#include +#include +#include + +#include + +#include "../../src/network/HttpDownloader.h" + +namespace { +constexpr char WEATHER_CACHE_FILE[] = "/.crosspoint/weather_cache.json"; + +std::string buildForecastUrl(const WeatherSettingsStore& settings) { + std::string url = "https://api.open-meteo.com/v1/forecast?"; + url += "latitude=" + std::to_string(settings.getLatitude()); + url += "&longitude=" + std::to_string(settings.getLongitude()); + url += + "¤t=temperature_2m,relative_humidity_2m,apparent_temperature," + "weather_code,wind_speed_10m,wind_direction_10m,is_day,precipitation,uv_index"; + url += "&hourly=temperature_2m,precipitation,precipitation_probability,weather_code,is_day"; + url += + "&daily=temperature_2m_max,temperature_2m_min,weather_code," + "precipitation_sum,sunrise,sunset,uv_index_max"; + url += "&timezone=auto&timeformat=unixtime"; + url += std::string("&temperature_unit=") + settings.getTempUnitParam(); + url += std::string("&wind_speed_unit=") + settings.getWindUnitParam(); + url += std::string("&precipitation_unit=") + settings.getPrecipUnitParam(); + url += "&forecast_days=" + std::to_string(settings.getForecastDays()); + // Request 48 hours of hourly data + url += "&forecast_hours=48"; + return url; +} +} // namespace + +WeatherData WeatherClient::getWeather(const WeatherSettingsStore& settings, bool forceRefresh) { + if (!settings.hasLocation()) { + WeatherData data; + data.errorMessage = "No location configured"; + return data; + } + + if (!forceRefresh) { + WeatherData cached; + if (loadCache(cached) && cached.valid) { + time_t now; + time(&now); + if (now - cached.fetchedAt < CACHE_TTL_SECONDS) { + LOG_DBG("WEA", "Using cached weather data (age: %ld s)", (long)(now - cached.fetchedAt)); + return cached; + } + LOG_DBG("WEA", "Cache expired (age: %ld s)", (long)(now - cached.fetchedAt)); + } + } + + return fetchFromApi(settings); +} + +WeatherData WeatherClient::fetchFromApi(const WeatherSettingsStore& settings) { + WeatherData data; + std::string url = buildForecastUrl(settings); + LOG_DBG("WEA", "Fetching weather from API"); + + std::string response; + if (!HttpDownloader::fetchUrl(url, response)) { + data.errorMessage = "Network error"; + LOG_ERR("WEA", "Failed to fetch weather data"); + return data; + } + + if (!parseWeatherJson(response, data)) { + LOG_ERR("WEA", "Failed to parse weather JSON"); + return data; + } + + data.valid = true; + time(&data.fetchedAt); + saveCache(data); + LOG_DBG("WEA", "Weather data fetched and cached"); + return data; +} + +bool WeatherClient::parseWeatherJson(const std::string& json, WeatherData& data) { + JsonDocument doc; + auto error = deserializeJson(doc, json); + if (error) { + data.errorMessage = "JSON parse error"; + LOG_ERR("WEA", "JSON parse error: %s", error.c_str()); + return false; + } + + // Check for API error + if (doc["error"] | false) { + data.errorMessage = doc["reason"] | "API error"; + LOG_ERR("WEA", "API error: %s", data.errorMessage.c_str()); + return false; + } + + data.timezone = doc["timezone"] | std::string(""); + data.utcOffsetSeconds = doc["utc_offset_seconds"] | 0; + + // Parse current weather + JsonObject current = doc["current"]; + if (current) { + data.current.temperature = current["temperature_2m"] | 0.0f; + data.current.apparentTemperature = current["apparent_temperature"] | 0.0f; + data.current.humidity = current["relative_humidity_2m"] | 0; + data.current.weatherCode = current["weather_code"] | 0; + data.current.windSpeed = current["wind_speed_10m"] | 0.0f; + data.current.windDirection = current["wind_direction_10m"] | 0; + data.current.precipitation = current["precipitation"] | 0.0f; + data.current.uvIndex = current["uv_index"] | 0.0f; + data.current.isDay = (current["is_day"] | 1) != 0; + } + + // Parse daily forecast + JsonObject daily = doc["daily"]; + if (daily) { + JsonArray times = daily["time"]; + JsonArray tempMax = daily["temperature_2m_max"]; + JsonArray tempMin = daily["temperature_2m_min"]; + JsonArray codes = daily["weather_code"]; + JsonArray precip = daily["precipitation_sum"]; + JsonArray sunrise = daily["sunrise"]; + JsonArray sunset = daily["sunset"]; + JsonArray uvMax = daily["uv_index_max"]; + + size_t count = times.size(); + data.daily.reserve(count); + for (size_t i = 0; i < count; i++) { + DailyForecast day; + day.date = times[i] | (time_t)0; + day.tempMax = tempMax[i] | 0.0f; + day.tempMin = tempMin[i] | 0.0f; + day.weatherCode = codes[i] | 0; + day.precipSum = precip[i] | 0.0f; + day.sunrise = sunrise[i] | (time_t)0; + day.sunset = sunset[i] | (time_t)0; + day.uvIndexMax = uvMax[i] | 0.0f; + data.daily.push_back(day); + } + } + + // Parse hourly forecast + JsonObject hourly = doc["hourly"]; + if (hourly) { + JsonArray times = hourly["time"]; + JsonArray temp = hourly["temperature_2m"]; + JsonArray precip = hourly["precipitation"]; + JsonArray precipProb = hourly["precipitation_probability"]; + JsonArray codes = hourly["weather_code"]; + JsonArray isDay = hourly["is_day"]; + + size_t count = times.size(); + data.hourly.reserve(count); + for (size_t i = 0; i < count; i++) { + HourlyForecast hour; + hour.time = times[i] | (time_t)0; + hour.temperature = temp[i] | 0.0f; + hour.precipitation = precip[i] | 0.0f; + hour.precipitationProbability = precipProb[i] | 0; + hour.weatherCode = codes[i] | 0; + hour.isDay = (isDay[i] | 1) != 0; + data.hourly.push_back(hour); + } + } + + return true; +} + +bool WeatherClient::saveCache(const WeatherData& data) { + Storage.mkdir("/.crosspoint"); + + JsonDocument doc; + doc["fetchedAt"] = data.fetchedAt; + doc["timezone"] = data.timezone; + doc["utcOffsetSeconds"] = data.utcOffsetSeconds; + + // Current + JsonObject cur = doc["current"].to(); + cur["temperature"] = data.current.temperature; + cur["apparentTemperature"] = data.current.apparentTemperature; + cur["humidity"] = data.current.humidity; + cur["weatherCode"] = data.current.weatherCode; + cur["windSpeed"] = data.current.windSpeed; + cur["windDirection"] = data.current.windDirection; + cur["precipitation"] = data.current.precipitation; + cur["uvIndex"] = data.current.uvIndex; + cur["isDay"] = data.current.isDay; + + // Daily + JsonArray dailyArr = doc["daily"].to(); + for (const auto& day : data.daily) { + JsonObject d = dailyArr.add(); + d["date"] = day.date; + d["tempMax"] = day.tempMax; + d["tempMin"] = day.tempMin; + d["weatherCode"] = day.weatherCode; + d["precipSum"] = day.precipSum; + d["uvIndexMax"] = day.uvIndexMax; + d["sunrise"] = day.sunrise; + d["sunset"] = day.sunset; + } + + // Hourly + JsonArray hourlyArr = doc["hourly"].to(); + for (const auto& hour : data.hourly) { + JsonObject h = hourlyArr.add(); + h["time"] = hour.time; + h["temperature"] = hour.temperature; + h["precipitation"] = hour.precipitation; + h["precipProb"] = hour.precipitationProbability; + h["weatherCode"] = hour.weatherCode; + h["isDay"] = hour.isDay; + } + + String json; + serializeJson(doc, json); + return Storage.writeFile(WEATHER_CACHE_FILE, json); +} + +bool WeatherClient::loadCache(WeatherData& data) { + if (!Storage.exists(WEATHER_CACHE_FILE)) { + return false; + } + + String json = Storage.readFile(WEATHER_CACHE_FILE); + if (json.isEmpty()) { + return false; + } + + JsonDocument doc; + auto error = deserializeJson(doc, json); + if (error) { + LOG_ERR("WEA", "Cache parse error: %s", error.c_str()); + return false; + } + + data.fetchedAt = doc["fetchedAt"] | (time_t)0; + data.timezone = doc["timezone"] | std::string(""); + data.utcOffsetSeconds = doc["utcOffsetSeconds"] | 0; + + // Current + JsonObject cur = doc["current"]; + if (cur) { + data.current.temperature = cur["temperature"] | 0.0f; + data.current.apparentTemperature = cur["apparentTemperature"] | 0.0f; + data.current.humidity = cur["humidity"] | 0; + data.current.weatherCode = cur["weatherCode"] | 0; + data.current.windSpeed = cur["windSpeed"] | 0.0f; + data.current.windDirection = cur["windDirection"] | 0; + data.current.precipitation = cur["precipitation"] | 0.0f; + data.current.uvIndex = cur["uvIndex"] | 0.0f; + data.current.isDay = cur["isDay"] | true; + } + + // Daily + JsonArray dailyArr = doc["daily"].as(); + for (JsonObject d : dailyArr) { + DailyForecast day; + day.date = d["date"] | (time_t)0; + day.tempMax = d["tempMax"] | 0.0f; + day.tempMin = d["tempMin"] | 0.0f; + day.weatherCode = d["weatherCode"] | 0; + day.precipSum = d["precipSum"] | 0.0f; + day.uvIndexMax = d["uvIndexMax"] | 0.0f; + day.sunrise = d["sunrise"] | (time_t)0; + day.sunset = d["sunset"] | (time_t)0; + data.daily.push_back(day); + } + + // Hourly + JsonArray hourlyArr = doc["hourly"].as(); + for (JsonObject h : hourlyArr) { + HourlyForecast hour; + hour.time = h["time"] | (time_t)0; + hour.temperature = h["temperature"] | 0.0f; + hour.precipitation = h["precipitation"] | 0.0f; + hour.precipitationProbability = h["precipProb"] | 0; + hour.weatherCode = h["weatherCode"] | 0; + hour.isDay = h["isDay"] | true; + data.hourly.push_back(hour); + } + + data.valid = true; + LOG_DBG("WEA", "Cache loaded, fetchedAt=%ld", (long)data.fetchedAt); + return true; +} + +std::vector WeatherClient::searchCity(const std::string& query) { + std::vector results; + + std::string url = "https://geocoding-api.open-meteo.com/v1/search?name="; + // Simple URL encoding for the query + for (char c : query) { + if (c == ' ') { + url += "%20"; + } else if (isalnum(c) || c == '-' || c == '_' || c == '.') { + url += c; + } else { + char buf[4]; + snprintf(buf, sizeof(buf), "%%%02X", (unsigned char)c); + url += buf; + } + } + url += "&count=5&language=en"; + + std::string response; + if (!HttpDownloader::fetchUrl(url, response)) { + LOG_ERR("WEA", "Geocoding request failed"); + return results; + } + + JsonDocument doc; + auto error = deserializeJson(doc, response); + if (error) { + LOG_ERR("WEA", "Geocoding parse error: %s", error.c_str()); + return results; + } + + JsonArray arr = doc["results"].as(); + for (JsonObject obj : arr) { + GeocodingResult r; + r.name = obj["name"] | std::string(""); + r.country = obj["country"] | std::string(""); + r.admin1 = obj["admin1"] | std::string(""); + r.latitude = obj["latitude"] | 0.0f; + r.longitude = obj["longitude"] | 0.0f; + results.push_back(r); + } + + LOG_DBG("WEA", "Geocoding found %zu results for '%s'", results.size(), query.c_str()); + return results; +} diff --git a/lib/Weather/WeatherClient.h b/lib/Weather/WeatherClient.h new file mode 100644 index 00000000..f2ae6e19 --- /dev/null +++ b/lib/Weather/WeatherClient.h @@ -0,0 +1,24 @@ +#pragma once +#include + +#include "WeatherData.h" +#include "WeatherSettingsStore.h" + +class WeatherClient { + public: + /// Fetch weather data, using cache if valid (< 30 min old). + /// If forceRefresh is true, always fetches from the API. + static WeatherData getWeather(const WeatherSettingsStore& settings, bool forceRefresh = false); + + /// Search for cities by name via Open-Meteo geocoding API. + /// Returns up to 5 results. + static std::vector searchCity(const std::string& query); + + private: + static WeatherData fetchFromApi(const WeatherSettingsStore& settings); + static bool parseWeatherJson(const std::string& json, WeatherData& data); + static bool saveCache(const WeatherData& data); + static bool loadCache(WeatherData& data); + + static constexpr int CACHE_TTL_SECONDS = 30 * 60; // 30 minutes +}; diff --git a/lib/Weather/WeatherData.h b/lib/Weather/WeatherData.h new file mode 100644 index 00000000..b593865c --- /dev/null +++ b/lib/Weather/WeatherData.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include +#include +#include + +struct CurrentWeather { + float temperature = 0; + float apparentTemperature = 0; + int humidity = 0; + int weatherCode = 0; + float windSpeed = 0; + int windDirection = 0; + float precipitation = 0; + float uvIndex = 0; + bool isDay = true; +}; + +struct DailyForecast { + time_t date = 0; + float tempMax = 0; + float tempMin = 0; + int weatherCode = 0; + float precipSum = 0; + float uvIndexMax = 0; + time_t sunrise = 0; + time_t sunset = 0; +}; + +struct HourlyForecast { + time_t time = 0; + float temperature = 0; + float precipitation = 0; + int precipitationProbability = 0; + int weatherCode = 0; + bool isDay = true; +}; + +struct WeatherData { + CurrentWeather current; + std::vector daily; + std::vector hourly; + std::string timezone; + int utcOffsetSeconds = 0; + time_t fetchedAt = 0; + bool valid = false; + std::string errorMessage; +}; + +struct GeocodingResult { + std::string name; + std::string country; + std::string admin1; // State/region + float latitude = 0; + float longitude = 0; +}; diff --git a/lib/Weather/WeatherIcons.cpp b/lib/Weather/WeatherIcons.cpp new file mode 100644 index 00000000..04aa5b57 --- /dev/null +++ b/lib/Weather/WeatherIcons.cpp @@ -0,0 +1,396 @@ +#include "WeatherIcons.h" + +// ============================================================================ +// 48x48 Large Weather Icons (1-bit, MSB-first, row-major) +// Each row = 48 pixels = 6 bytes. Total = 48 * 6 = 288 bytes per icon. +// Pixel ON (1) = white/clear, Pixel OFF (0) = black/drawn. +// These are "inverted" bitmaps - 0 bits are drawn as black pixels. +// ============================================================================ + +// Clear Day - Sun with rays +// clang-format off +static const uint8_t ICON_CLEAR_DAY_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, + 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, + 0xFF,0x8F,0xFE,0x7F,0xF1,0xFF, 0xFF,0x1F,0xFE,0x7F,0xF8,0xFF, 0xFF,0x3F,0x80,0x01,0xFC,0xFF, + 0xFF,0x7F,0x00,0x00,0xFE,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, + 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xE0,0xC0,0x00,0x00,0x03,0x07, + 0xE0,0x80,0x00,0x00,0x01,0x07, 0xF0,0x80,0x00,0x00,0x01,0x0F, 0xF8,0x80,0x00,0x00,0x01,0x1F, + 0xFC,0x80,0x00,0x00,0x01,0x3F, 0xFC,0x80,0x00,0x00,0x01,0x3F, 0xF8,0x80,0x00,0x00,0x01,0x1F, + 0xF0,0x80,0x00,0x00,0x01,0x0F, 0xE0,0x80,0x00,0x00,0x01,0x07, 0xE0,0xC0,0x00,0x00,0x03,0x07, + 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, + 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, + 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0x7F,0x00,0x00,0xFE,0xFF, 0xFF,0x3F,0x80,0x01,0xFC,0xFF, + 0xFF,0x1F,0xFE,0x7F,0xF8,0xFF, 0xFF,0x8F,0xFE,0x7F,0xF1,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, + 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, + 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Clear Night - Moon crescent +static const uint8_t ICON_CLEAR_NIGHT_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x0F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x03,0xFF,0xFF, + 0xFF,0xFF,0xE0,0x01,0xFF,0xFF, 0xFF,0xFF,0xC0,0x00,0xFF,0xFF, 0xFF,0xFF,0x80,0x00,0x7F,0xFF, + 0xFF,0xFF,0x00,0x00,0x3F,0xFF, 0xFF,0xFF,0x00,0x00,0x3F,0xFF, 0xFF,0xFE,0x00,0x00,0x1F,0xFF, + 0xFF,0xFE,0x00,0x00,0x3F,0xFF, 0xFF,0xFC,0x00,0x00,0x7F,0xFF, 0xFF,0xFC,0x00,0x00,0xFF,0xFF, + 0xFF,0xFC,0x00,0x01,0xFF,0xFF, 0xFF,0xF8,0x00,0x03,0xFF,0xFF, 0xFF,0xF8,0x00,0x07,0xFF,0xFF, + 0xFF,0xF8,0x00,0x07,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, + 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, + 0xFF,0xF8,0x00,0x0F,0xFF,0xFF, 0xFF,0xF8,0x00,0x07,0xFF,0xFF, 0xFF,0xF8,0x00,0x07,0xFF,0xFF, + 0xFF,0xF8,0x00,0x03,0xFF,0xFF, 0xFF,0xFC,0x00,0x01,0xFF,0xFF, 0xFF,0xFC,0x00,0x00,0xFF,0xFF, + 0xFF,0xFC,0x00,0x00,0x7F,0xFF, 0xFF,0xFE,0x00,0x00,0x3F,0xFF, 0xFF,0xFE,0x00,0x00,0x1F,0xFF, + 0xFF,0xFF,0x00,0x00,0x1F,0xFF, 0xFF,0xFF,0x00,0x00,0x3F,0xFF, 0xFF,0xFF,0x80,0x00,0x7F,0xFF, + 0xFF,0xFF,0xC0,0x00,0xFF,0xFF, 0xFF,0xFF,0xE0,0x01,0xFF,0xFF, 0xFF,0xFF,0xF0,0x03,0xFF,0xFF, + 0xFF,0xFF,0xFC,0x0F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Partly Cloudy Day - Sun behind cloud +static const uint8_t ICON_PARTLY_CLOUDY_DAY_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, + 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7F,0xFF,0xFF, 0xFF,0x9F,0xFE,0x7F,0xF9,0xFF, + 0xFF,0x3F,0xFE,0x7F,0xFC,0xFF, 0xFF,0x7F,0x80,0x01,0xFE,0xFF, 0xFF,0xFF,0x00,0x00,0xFF,0xFF, + 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xE0,0xE0,0x00,0x00,0x07,0x07, 0xF0,0xE0,0x00,0x00,0x07,0x0F, + 0xF8,0xE0,0x00,0x00,0x07,0x1F, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, + 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, + 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F, + 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, + 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Partly Cloudy Night - Moon behind cloud (reuse partly cloudy day for simplicity) +static const uint8_t* ICON_PARTLY_CLOUDY_NIGHT_48 = ICON_PARTLY_CLOUDY_DAY_48; + +// Overcast - Full cloud +static const uint8_t ICON_OVERCAST_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, + 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF, 0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, + 0xFF,0xFF,0x80,0x01,0xFF,0xFF, 0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0x00,0x00,0xFF,0xFF, + 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F, + 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xF0,0x00,0x00,0x0F,0xFF, + 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Rain - Cloud with rain drops +static const uint8_t ICON_RAIN_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF, + 0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, 0xFF,0xFF,0x80,0x01,0xFF,0xFF, + 0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xDF,0xBF,0x7F,0xFF, + 0xFF,0xF3,0xCF,0x9F,0x3F,0xFF, 0xFF,0xF3,0xCF,0x9F,0x3F,0xFF, 0xFF,0xE7,0xE7,0xCF,0x9F,0xFF, + 0xFF,0xE7,0xE7,0xCF,0x9F,0xFF, 0xFF,0xCF,0xF3,0xE7,0xCF,0xFF, 0xFF,0xCF,0xF3,0xE7,0xCF,0xFF, + 0xFF,0xCF,0xF3,0xE7,0xCF,0xFF, 0xFF,0xDF,0xFB,0xF7,0xEF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Snow - Cloud with snowflakes +static const uint8_t ICON_SNOW_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF, + 0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, 0xFF,0xFF,0x80,0x01,0xFF,0xFF, + 0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF9,0xE7,0xCF,0x3F,0xFF, + 0xFF,0xFC,0xF3,0xE7,0x9F,0xFF, 0xFF,0xFE,0x79,0xF3,0xCF,0xFF, 0xFF,0xFF,0x3C,0xF9,0xE7,0xFF, + 0xFF,0xFE,0x79,0xF3,0xCF,0xFF, 0xFF,0xFC,0xF3,0xE7,0x9F,0xFF, 0xFF,0xF9,0xE7,0xCF,0x3F,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Thunderstorm - Cloud with lightning bolt +static const uint8_t ICON_THUNDERSTORM_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFC,0x3F,0xFF,0xFF, 0xFF,0xFF,0xF0,0x0F,0xFF,0xFF, + 0xFF,0xFF,0xE0,0x07,0xFF,0xFF, 0xFF,0xFF,0xC0,0x03,0xFF,0xFF, 0xFF,0xFF,0x80,0x01,0xFF,0xFF, + 0xFF,0xFF,0x00,0x00,0xFF,0xFF, 0xFF,0xFE,0x00,0x00,0x7F,0xFF, 0xFF,0xF8,0x00,0x00,0x1F,0xFF, + 0xFF,0xF0,0x00,0x00,0x0F,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFE,0x00,0x00,0x00,0x00,0x7F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFC,0x00,0x00,0x00,0x00,0x3F, + 0xFC,0x00,0x00,0x00,0x00,0x3F, 0xFE,0x00,0x00,0x00,0x00,0x7F, 0xFF,0x00,0x00,0x00,0x00,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xE0,0x00,0x00,0x07,0xFF, 0xFF,0xFC,0x00,0x00,0x3F,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF8,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF1,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC7,0xFF,0xFF,0xFF, 0xFF,0xFF,0x80,0x3F,0xFF,0xFF, + 0xFF,0xFF,0x00,0x7F,0xFF,0xFF, 0xFF,0xFF,0xF0,0xFF,0xFF,0xFF, 0xFF,0xFF,0xE1,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xC3,0xFF,0xFF,0xFF, 0xFF,0xFF,0x87,0xFF,0xFF,0xFF, 0xFF,0xFF,0x0F,0xFF,0xFF,0xFF, + 0xFF,0xFF,0x1F,0xFF,0xFF,0xFF, 0xFF,0xFF,0x3F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// Fog - Horizontal lines +static const uint8_t ICON_FOG_48[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, + 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, + 0xFF,0xC0,0x00,0x00,0x03,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x80,0x00,0x00,0x01,0xFF, 0xFF,0x00,0x00,0x00,0x00,0xFF, 0xFF,0x80,0x00,0x00,0x01,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +// clang-format on + +// ============================================================================ +// 24x24 Small Weather Icons (1-bit, MSB-first) +// Each row = 24 pixels = 3 bytes. Total = 24 * 3 = 72 bytes per icon. +// ============================================================================ + +// clang-format off + +// Small Clear Day - Sun +static const uint8_t ICON_CLEAR_DAY_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, + 0xF3,0xE7,0xCF, 0xF9,0x00,0x9F, 0xFF,0x00,0xFF, 0xFE,0x00,0x7F, + 0xFC,0x00,0x3F, 0xFC,0x00,0x3F, 0xE0,0x00,0x07, 0xE0,0x00,0x07, + 0xE0,0x00,0x07, 0xE0,0x00,0x07, 0xFC,0x00,0x3F, 0xFC,0x00,0x3F, + 0xFE,0x00,0x7F, 0xFF,0x00,0xFF, 0xF9,0x00,0x9F, 0xF3,0xE7,0xCF, + 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xE7,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Clear Night - Moon +static const uint8_t ICON_CLEAR_NIGHT_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F, + 0xFF,0xC0,0x3F, 0xFF,0x80,0x3F, 0xFF,0x80,0x7F, 0xFF,0x00,0xFF, + 0xFF,0x01,0xFF, 0xFF,0x03,0xFF, 0xFF,0x03,0xFF, 0xFF,0x03,0xFF, + 0xFF,0x03,0xFF, 0xFF,0x03,0xFF, 0xFF,0x01,0xFF, 0xFF,0x00,0xFF, + 0xFF,0x80,0x7F, 0xFF,0x80,0x3F, 0xFF,0xC0,0x3F, 0xFF,0xE0,0x7F, + 0xFF,0xF0,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Partly Cloudy +static const uint8_t ICON_PARTLY_CLOUDY_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xE7,0xFF, 0xFB,0xE7,0xDF, 0xFD,0x00,0xBF, + 0xFF,0x00,0xFF, 0xFE,0x00,0x7F, 0xFE,0x00,0x7F, 0xFC,0x00,0x3F, + 0xFF,0x00,0x7F, 0xFE,0x00,0x3F, 0xFC,0x00,0x1F, 0xF8,0x00,0x0F, + 0xF0,0x00,0x0F, 0xF0,0x00,0x07, 0xE0,0x00,0x07, 0xE0,0x00,0x07, + 0xE0,0x00,0x07, 0xE0,0x00,0x07, 0xF0,0x00,0x0F, 0xF8,0x00,0x1F, + 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Overcast - Cloud +static const uint8_t ICON_OVERCAST_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, + 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F, 0xFF,0xC0,0x3F, 0xFF,0x80,0x1F, + 0xFF,0x00,0x1F, 0xFE,0x00,0x0F, 0xFC,0x00,0x07, 0xF8,0x00,0x07, + 0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, + 0xE0,0x00,0x03, 0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFE,0x00,0x7F, + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Rain +static const uint8_t ICON_RAIN_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F, + 0xFF,0x80,0x1F, 0xFF,0x00,0x0F, 0xFE,0x00,0x07, 0xFC,0x00,0x07, + 0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, + 0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF, + 0xFE,0xF7,0xBF, 0xFD,0xEF,0x7F, 0xFD,0xEF,0x7F, 0xFB,0xDF,0x7F, + 0xFB,0xDF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Snow +static const uint8_t ICON_SNOW_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F, + 0xFF,0x80,0x1F, 0xFF,0x00,0x0F, 0xFE,0x00,0x07, 0xFC,0x00,0x07, + 0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, + 0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF, + 0xFE,0xF7,0xBF, 0xFF,0x7B,0xDF, 0xFE,0xF7,0xBF, 0xFF,0x7B,0xDF, + 0xFE,0xF7,0xBF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Thunderstorm +static const uint8_t ICON_THUNDERSTORM_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xF0,0xFF, 0xFF,0xE0,0x7F, + 0xFF,0x80,0x1F, 0xFF,0x00,0x0F, 0xFE,0x00,0x07, 0xFC,0x00,0x07, + 0xF0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, 0xE0,0x00,0x03, + 0xF0,0x00,0x07, 0xF8,0x00,0x0F, 0xFF,0x00,0xFF, 0xFF,0xFF,0xFF, + 0xFF,0xF1,0xFF, 0xFF,0xE3,0xFF, 0xFF,0xC0,0xFF, 0xFF,0xF1,0xFF, + 0xFF,0xE3,0xFF, 0xFF,0xC7,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// Small Fog +static const uint8_t ICON_FOG_24[] = { + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xF0,0x00,0x0F, 0xE0,0x00,0x07, + 0xF0,0x00,0x0F, 0xFF,0xFF,0xFF, 0xF8,0x00,0x1F, 0xF0,0x00,0x0F, + 0xF8,0x00,0x1F, 0xFF,0xFF,0xFF, 0xF0,0x00,0x0F, 0xE0,0x00,0x07, + 0xF0,0x00,0x0F, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, +}; + +// clang-format on + +// ============================================================================ +// Lookup functions +// ============================================================================ + +const uint8_t* getWeatherIconLarge(WeatherIconType type) { + switch (type) { + case WeatherIconType::CLEAR_DAY: + return ICON_CLEAR_DAY_48; + case WeatherIconType::CLEAR_NIGHT: + return ICON_CLEAR_NIGHT_48; + case WeatherIconType::PARTLY_CLOUDY_DAY: + return ICON_PARTLY_CLOUDY_DAY_48; + case WeatherIconType::PARTLY_CLOUDY_NIGHT: + return ICON_PARTLY_CLOUDY_NIGHT_48; + case WeatherIconType::OVERCAST: + return ICON_OVERCAST_48; + case WeatherIconType::FOG: + return ICON_FOG_48; + case WeatherIconType::DRIZZLE: + case WeatherIconType::RAIN: + case WeatherIconType::SHOWERS: + return ICON_RAIN_48; + case WeatherIconType::SNOW: + return ICON_SNOW_48; + case WeatherIconType::THUNDERSTORM: + return ICON_THUNDERSTORM_48; + default: + return ICON_OVERCAST_48; + } +} + +const uint8_t* getWeatherIconSmall(WeatherIconType type) { + switch (type) { + case WeatherIconType::CLEAR_DAY: + return ICON_CLEAR_DAY_24; + case WeatherIconType::CLEAR_NIGHT: + return ICON_CLEAR_NIGHT_24; + case WeatherIconType::PARTLY_CLOUDY_DAY: + case WeatherIconType::PARTLY_CLOUDY_NIGHT: + return ICON_PARTLY_CLOUDY_24; + case WeatherIconType::OVERCAST: + return ICON_OVERCAST_24; + case WeatherIconType::FOG: + return ICON_FOG_24; + case WeatherIconType::DRIZZLE: + case WeatherIconType::RAIN: + case WeatherIconType::SHOWERS: + return ICON_RAIN_24; + case WeatherIconType::SNOW: + return ICON_SNOW_24; + case WeatherIconType::THUNDERSTORM: + return ICON_THUNDERSTORM_24; + default: + return ICON_OVERCAST_24; + } +} + +const char* getWeatherDescription(int wmoCode) { + switch (wmoCode) { + case 0: + return "Clear sky"; + case 1: + return "Mainly clear"; + case 2: + return "Partly cloudy"; + case 3: + return "Overcast"; + case 45: + return "Fog"; + case 48: + return "Rime fog"; + case 51: + return "Light drizzle"; + case 53: + return "Drizzle"; + case 55: + return "Dense drizzle"; + case 56: + return "Freezing drizzle"; + case 57: + return "Dense freezing drizzle"; + case 61: + return "Slight rain"; + case 63: + return "Moderate rain"; + case 65: + return "Heavy rain"; + case 66: + return "Freezing rain"; + case 67: + return "Heavy freezing rain"; + case 71: + return "Slight snow"; + case 73: + return "Moderate snow"; + case 75: + return "Heavy snow"; + case 77: + return "Snow grains"; + case 80: + return "Slight showers"; + case 81: + return "Moderate showers"; + case 82: + return "Violent showers"; + case 85: + return "Snow showers"; + case 86: + return "Heavy snow showers"; + case 95: + return "Thunderstorm"; + case 96: + return "Thunderstorm, hail"; + case 99: + return "Thunderstorm, heavy hail"; + default: + return "Unknown"; + } +} + +const char* getWindDirectionText(int degrees) { + // Normalize to 0-360 + degrees = ((degrees % 360) + 360) % 360; + if (degrees >= 338 || degrees < 23) return "N"; + if (degrees < 68) return "NE"; + if (degrees < 113) return "E"; + if (degrees < 158) return "SE"; + if (degrees < 203) return "S"; + if (degrees < 248) return "SW"; + if (degrees < 293) return "W"; + return "NW"; +} diff --git a/lib/Weather/WeatherIcons.h b/lib/Weather/WeatherIcons.h new file mode 100644 index 00000000..bcb60dfc --- /dev/null +++ b/lib/Weather/WeatherIcons.h @@ -0,0 +1,79 @@ +#pragma once +#include + +// Weather icon size constants +constexpr int WEATHER_ICON_LARGE = 48; // Large icon for current weather +constexpr int WEATHER_ICON_SMALL = 24; // Small icon for daily forecast + +// WMO weather code to icon category mapping +enum class WeatherIconType { + CLEAR_DAY, + CLEAR_NIGHT, + PARTLY_CLOUDY_DAY, + PARTLY_CLOUDY_NIGHT, + OVERCAST, + FOG, + DRIZZLE, + RAIN, + SNOW, + SHOWERS, + THUNDERSTORM, + UNKNOWN +}; + +// Map WMO weather code + day/night to icon type +inline WeatherIconType getWeatherIconType(int wmoCode, bool isDay) { + switch (wmoCode) { + case 0: + return isDay ? WeatherIconType::CLEAR_DAY : WeatherIconType::CLEAR_NIGHT; + case 1: + case 2: + return isDay ? WeatherIconType::PARTLY_CLOUDY_DAY : WeatherIconType::PARTLY_CLOUDY_NIGHT; + case 3: + return WeatherIconType::OVERCAST; + case 45: + case 48: + return WeatherIconType::FOG; + case 51: + case 53: + case 55: + case 56: + case 57: + return WeatherIconType::DRIZZLE; + case 61: + case 63: + case 65: + case 66: + case 67: + return WeatherIconType::RAIN; + case 71: + case 73: + case 75: + case 77: + return WeatherIconType::SNOW; + case 80: + case 81: + case 82: + case 85: + case 86: + return WeatherIconType::SHOWERS; + case 95: + case 96: + case 99: + return WeatherIconType::THUNDERSTORM; + default: + return WeatherIconType::UNKNOWN; + } +} + +// Get human-readable description for WMO weather code +const char* getWeatherDescription(int wmoCode); + +// Get the appropriate large icon bitmap (48x48, 1-bit, MSB first) +const uint8_t* getWeatherIconLarge(WeatherIconType type); + +// Get the appropriate small icon bitmap (24x24, 1-bit, MSB first) +const uint8_t* getWeatherIconSmall(WeatherIconType type); + +// Wind direction arrow text (N, NE, E, SE, S, SW, W, NW) +const char* getWindDirectionText(int degrees); diff --git a/lib/Weather/WeatherSettingsStore.cpp b/lib/Weather/WeatherSettingsStore.cpp new file mode 100644 index 00000000..52267936 --- /dev/null +++ b/lib/Weather/WeatherSettingsStore.cpp @@ -0,0 +1,96 @@ +#include "WeatherSettingsStore.h" + +#include +#include +#include + +WeatherSettingsStore WeatherSettingsStore::instance; + +namespace { +constexpr char WEATHER_SETTINGS_FILE[] = "/.crosspoint/weather_settings.json"; +} + +bool WeatherSettingsStore::saveToFile() const { + Storage.mkdir("/.crosspoint"); + + JsonDocument doc; + doc["latitude"] = latitude; + doc["longitude"] = longitude; + doc["locationName"] = locationName; + doc["tempUnit"] = static_cast(tempUnit); + doc["windUnit"] = static_cast(windUnit); + doc["precipUnit"] = static_cast(precipUnit); + doc["forecastDays"] = forecastDays; + + String json; + serializeJson(doc, json); + return Storage.writeFile(WEATHER_SETTINGS_FILE, json); +} + +bool WeatherSettingsStore::loadFromFile() { + if (!Storage.exists(WEATHER_SETTINGS_FILE)) { + LOG_DBG("WEA", "No weather settings file found"); + return false; + } + + String json = Storage.readFile(WEATHER_SETTINGS_FILE); + if (json.isEmpty()) { + return false; + } + + JsonDocument doc; + auto error = deserializeJson(doc, json); + if (error) { + LOG_ERR("WEA", "JSON parse error: %s", error.c_str()); + return false; + } + + latitude = doc["latitude"] | 0.0f; + longitude = doc["longitude"] | 0.0f; + locationName = doc["locationName"] | std::string(""); + tempUnit = static_cast(doc["tempUnit"] | (uint8_t)0); + windUnit = static_cast(doc["windUnit"] | (uint8_t)0); + precipUnit = static_cast(doc["precipUnit"] | (uint8_t)0); + forecastDays = doc["forecastDays"] | (uint8_t)3; + + if (forecastDays < 1) forecastDays = 1; + if (forecastDays > 5) forecastDays = 5; + + LOG_DBG("WEA", "Loaded weather settings: %s (%.2f, %.2f)", locationName.c_str(), latitude, longitude); + return true; +} + +void WeatherSettingsStore::setLocation(float lat, float lon, const std::string& name) { + latitude = lat; + longitude = lon; + locationName = name; + LOG_DBG("WEA", "Set location: %s (%.4f, %.4f)", name.c_str(), lat, lon); +} + +void WeatherSettingsStore::setForecastDays(uint8_t days) { + if (days < 1) days = 1; + if (days > 5) days = 5; + forecastDays = days; +} + +const char* WeatherSettingsStore::getTempUnitParam() const { + return tempUnit == WeatherTempUnit::FAHRENHEIT ? "fahrenheit" : "celsius"; +} + +const char* WeatherSettingsStore::getWindUnitParam() const { + switch (windUnit) { + case WeatherWindUnit::MS: + return "ms"; + case WeatherWindUnit::MPH: + return "mph"; + case WeatherWindUnit::KNOTS: + return "kn"; + case WeatherWindUnit::KMH: + default: + return "kmh"; + } +} + +const char* WeatherSettingsStore::getPrecipUnitParam() const { + return precipUnit == WeatherPrecipUnit::INCH ? "inch" : "mm"; +} diff --git a/lib/Weather/WeatherSettingsStore.h b/lib/Weather/WeatherSettingsStore.h new file mode 100644 index 00000000..50eacfed --- /dev/null +++ b/lib/Weather/WeatherSettingsStore.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include + +enum class WeatherTempUnit : uint8_t { CELSIUS = 0, FAHRENHEIT = 1 }; +enum class WeatherWindUnit : uint8_t { KMH = 0, MS = 1, MPH = 2, KNOTS = 3 }; +enum class WeatherPrecipUnit : uint8_t { MM = 0, INCH = 1 }; + +class WeatherSettingsStore { + private: + static WeatherSettingsStore instance; + + float latitude = 0; + float longitude = 0; + std::string locationName; + WeatherTempUnit tempUnit = WeatherTempUnit::CELSIUS; + WeatherWindUnit windUnit = WeatherWindUnit::KMH; + WeatherPrecipUnit precipUnit = WeatherPrecipUnit::MM; + uint8_t forecastDays = 3; + + WeatherSettingsStore() = default; + + public: + WeatherSettingsStore(const WeatherSettingsStore&) = delete; + WeatherSettingsStore& operator=(const WeatherSettingsStore&) = delete; + + static WeatherSettingsStore& getInstance() { return instance; } + + bool saveToFile() const; + bool loadFromFile(); + + // Location + void setLocation(float lat, float lon, const std::string& name); + float getLatitude() const { return latitude; } + float getLongitude() const { return longitude; } + const std::string& getLocationName() const { return locationName; } + bool hasLocation() const { return latitude != 0 || longitude != 0; } + + // Units + void setTempUnit(WeatherTempUnit unit) { tempUnit = unit; } + WeatherTempUnit getTempUnit() const { return tempUnit; } + void setWindUnit(WeatherWindUnit unit) { windUnit = unit; } + WeatherWindUnit getWindUnit() const { return windUnit; } + void setPrecipUnit(WeatherPrecipUnit unit) { precipUnit = unit; } + WeatherPrecipUnit getPrecipUnit() const { return precipUnit; } + + // Forecast + void setForecastDays(uint8_t days); + uint8_t getForecastDays() const { return forecastDays; } + + // API parameter strings + const char* getTempUnitParam() const; + const char* getWindUnitParam() const; + const char* getPrecipUnitParam() const; +}; + +#define WEATHER_SETTINGS WeatherSettingsStore::getInstance() diff --git a/src/activities/weather/WeatherActivity.cpp b/src/activities/weather/WeatherActivity.cpp new file mode 100644 index 00000000..74710f39 --- /dev/null +++ b/src/activities/weather/WeatherActivity.cpp @@ -0,0 +1,467 @@ +#include "WeatherActivity.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MappedInputManager.h" +#include "WeatherSettingsActivity.h" +#include "activities/network/WifiSelectionActivity.h" +#include "components/UITheme.h" +#include "fontIds.h" + +void WeatherActivity::onEnter() { + Activity::onEnter(); + + // Force landscape orientation for weather display + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + + state = State::LOADING_CACHE; + errorMessage.clear(); + forceRefresh = false; + requestUpdate(); + + loadAndDisplay(); +} + +void WeatherActivity::onExit() { + Activity::onExit(); + + // Restore orientation from settings + renderer.setOrientation(static_cast(SETTINGS.orientation)); + + WiFi.mode(WIFI_OFF); +} + +void WeatherActivity::loadAndDisplay() { + if (!WEATHER_SETTINGS.hasLocation()) { + state = State::ERROR; + errorMessage = tr(STR_WEATHER_NO_LOCATION); + requestUpdate(); + return; + } + + // Try cache first + WeatherData cached = WeatherClient::getWeather(WEATHER_SETTINGS, forceRefresh); + if (cached.valid) { + weatherData = std::move(cached); + state = State::WEATHER_DISPLAY; + requestUpdate(); + return; + } + + // Need fresh data - check WiFi + checkAndConnectWifi(); +} + +void WeatherActivity::checkAndConnectWifi() { + if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { + state = State::FETCHING; + requestUpdate(true); + fetchWeather(); + return; + } + + launchWifiSelection(); +} + +void WeatherActivity::launchWifiSelection() { + state = State::WIFI_SELECTION; + requestUpdate(); + + startActivityForResult(std::make_unique(renderer, mappedInput), + [this](const ActivityResult& result) { onWifiSelectionComplete(!result.isCancelled); }); +} + +void WeatherActivity::onWifiSelectionComplete(bool connected) { + // Re-apply landscape after returning from WiFi selection (which uses portrait) + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + + if (connected) { + state = State::FETCHING; + requestUpdate(true); + fetchWeather(); + } else { + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); + state = State::ERROR; + errorMessage = tr(STR_WIFI_CONN_FAILED); + requestUpdate(); + } +} + +void WeatherActivity::fetchWeather() { + weatherData = WeatherClient::getWeather(WEATHER_SETTINGS, true); + if (weatherData.valid) { + state = State::WEATHER_DISPLAY; + } else { + state = State::ERROR; + errorMessage = weatherData.errorMessage.empty() ? tr(STR_WEATHER_FETCH_FAILED) : weatherData.errorMessage.c_str(); + } + requestUpdate(); +} + +void WeatherActivity::loop() { + if (state == State::WIFI_SELECTION) { + return; // Handled by WifiSelectionActivity + } + + if (state == State::ERROR) { + if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + // Retry + if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { + state = State::FETCHING; + requestUpdate(true); + fetchWeather(); + } else { + launchWifiSelection(); + } + } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { + onGoHome(); + } + return; + } + + if (state == State::LOADING_CACHE || state == State::CHECK_WIFI || state == State::FETCHING) { + if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { + onGoHome(); + } + return; + } + + // WEATHER_DISPLAY state + if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { + onGoHome(); + } else if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + // Open weather settings + startActivityForResult(std::make_unique(renderer, mappedInput), + [this](const ActivityResult&) { + // Re-apply landscape after returning from settings + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + // Refresh after settings change + forceRefresh = true; + loadAndDisplay(); + }); + } else if (mappedInput.wasReleased(MappedInputManager::Button::Left) || + mappedInput.wasReleased(MappedInputManager::Button::Right)) { + // Manual refresh + forceRefresh = true; + loadAndDisplay(); + } +} + +void WeatherActivity::render(RenderLock&&) { + renderer.clearScreen(); + + const auto pageWidth = renderer.getScreenWidth(); // 800 in landscape + const auto pageHeight = renderer.getScreenHeight(); // 480 in landscape + + if (state == State::LOADING_CACHE || state == State::FETCHING) { + renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2, tr(STR_LOADING)); + const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + renderer.displayBuffer(); + return; + } + + if (state == State::ERROR) { + renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, tr(STR_ERROR_MSG)); + renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, errorMessage.c_str()); + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_RETRY), "", ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + renderer.displayBuffer(); + return; + } + + // DISPLAY state - Landscape layout (800x480) + // + // +------------------+------------------------------------+ + // | Current weather | Daily forecast cards | + // | (icon, temp, | [Day1] [Day2] [Day3] ... | + // | details) | | + // | ~200px wide | ~600px wide | + // +------------------+------------------------------------+ + // | | + // | 48-hour temperature + precipitation graph | + // | ~full width, ~200px high | + // +--------------------------------------------------------+ + // | [Back] [Settings] [Refresh] Button hints | + // +--------------------------------------------------------+ + + constexpr int leftPanelWidth = 220; + constexpr int buttonHintsHeight = 30; + constexpr int graphHeight = 190; + const int topSectionHeight = pageHeight - graphHeight - buttonHintsHeight; + + // Draw current conditions (left panel) + renderCurrentConditions(0, 0, leftPanelWidth, topSectionHeight); + + // Draw daily forecast (right of current conditions) + renderDailyForecast(leftPanelWidth, 0, pageWidth - leftPanelWidth, topSectionHeight); + + // Separator line + renderer.drawLine(0, topSectionHeight, pageWidth, topSectionHeight); + + // Draw hourly graph + renderHourlyGraph(0, topSectionHeight + 1, pageWidth, graphHeight - 1); + + // Separator line above button hints + renderer.drawLine(0, pageHeight - buttonHintsHeight, pageWidth, pageHeight - buttonHintsHeight); + + // Button hints + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_WEATHER_SETTINGS), tr(STR_WEATHER_REFRESH), ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + renderer.displayBuffer(); +} + +void WeatherActivity::renderCurrentConditions(int x, int y, int w, int h) { + const auto& cur = weatherData.current; + + // Location name + int textY = y + 15; + auto locationName = WEATHER_SETTINGS.getLocationName(); + if (!locationName.empty()) { + auto truncated = renderer.truncatedText(UI_10_FONT_ID, locationName.c_str(), w - 10); + renderer.drawText(UI_10_FONT_ID, x + 5, textY, truncated.c_str(), true, EpdFontFamily::BOLD); + textY += 20; + } + + // Last updated time + if (weatherData.fetchedAt > 0) { + struct tm timeinfo; + time_t localTime = weatherData.fetchedAt + weatherData.utcOffsetSeconds; + gmtime_r(&localTime, &timeinfo); + char timeBuf[32]; + snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min); + renderer.drawText(SMALL_FONT_ID, x + 5, textY, timeBuf); + textY += 15; + } + + // Weather icon (48x48) + auto iconType = getWeatherIconType(cur.weatherCode, cur.isDay); + const uint8_t* icon = getWeatherIconLarge(iconType); + int iconX = x + (w - WEATHER_ICON_LARGE) / 2; + renderer.drawImage(icon, iconX, textY, WEATHER_ICON_LARGE, WEATHER_ICON_LARGE); + textY += WEATHER_ICON_LARGE + 5; + + // Temperature (large) + char tempBuf[16]; + const char* unitSuffix = WEATHER_SETTINGS.getTempUnit() == WeatherTempUnit::CELSIUS ? "C" : "F"; + snprintf(tempBuf, sizeof(tempBuf), "%.1f %s", cur.temperature, unitSuffix); + renderer.drawCenteredText(UI_12_FONT_ID, textY, tempBuf, true, EpdFontFamily::BOLD); + textY += 22; + + // Feels like + snprintf(tempBuf, sizeof(tempBuf), "%.1f %s", cur.apparentTemperature, unitSuffix); + char feelsLikeBuf[48]; + snprintf(feelsLikeBuf, sizeof(feelsLikeBuf), "%s %s", tr(STR_WEATHER_FEELS_LIKE), tempBuf); + auto feelsText = renderer.truncatedText(SMALL_FONT_ID, feelsLikeBuf, w - 10); + int feelsWidth = renderer.getTextWidth(SMALL_FONT_ID, feelsText.c_str()); + renderer.drawText(SMALL_FONT_ID, x + (w - feelsWidth) / 2, textY, feelsText.c_str()); + textY += 16; + + // Weather description + const char* desc = getWeatherDescription(cur.weatherCode); + int descWidth = renderer.getTextWidth(SMALL_FONT_ID, desc); + renderer.drawText(SMALL_FONT_ID, x + (w - descWidth) / 2, textY, desc); + textY += 20; + + // Details grid + const char* windDir = getWindDirectionText(cur.windDirection); + char detailBuf[64]; + + // Wind + snprintf(detailBuf, sizeof(detailBuf), "%s: %.0f %s %s", tr(STR_WEATHER_WIND), cur.windSpeed, + WEATHER_SETTINGS.getWindUnitParam(), windDir); + renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + textY += 14; + + // Humidity + snprintf(detailBuf, sizeof(detailBuf), "%s: %d%%", tr(STR_WEATHER_HUMIDITY), cur.humidity); + renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + textY += 14; + + // Precipitation + snprintf(detailBuf, sizeof(detailBuf), "%s: %.1f %s", tr(STR_WEATHER_PRECIP), cur.precipitation, + WEATHER_SETTINGS.getPrecipUnitParam()); + renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + textY += 14; + + // UV Index + snprintf(detailBuf, sizeof(detailBuf), "UV: %.1f", cur.uvIndex); + renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + + // Vertical separator + renderer.drawLine(x + w, y, x + w, y + h); +} + +void WeatherActivity::renderDailyForecast(int x, int y, int w, int h) { + if (weatherData.daily.empty()) return; + + int numDays = static_cast(weatherData.daily.size()); + int cardWidth = w / numDays; + + for (int i = 0; i < numDays; i++) { + const auto& day = weatherData.daily[i]; + int cardX = x + i * cardWidth; + int textY = y + 10; + + // Day name + struct tm timeinfo; + time_t localDate = day.date + weatherData.utcOffsetSeconds; + gmtime_r(&localDate, &timeinfo); + char dayName[16]; + // Manual short day names to avoid locale issues on ESP32 + const char* dayNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + snprintf(dayName, sizeof(dayName), "%s", dayNames[timeinfo.tm_wday]); + + int dayNameWidth = renderer.getTextWidth(SMALL_FONT_ID, dayName, EpdFontFamily::BOLD); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - dayNameWidth) / 2, textY, dayName, true, EpdFontFamily::BOLD); + textY += 18; + + // Weather icon (24x24) + // Use isDay=true for daily forecasts + auto iconType = getWeatherIconType(day.weatherCode, true); + const uint8_t* icon = getWeatherIconSmall(iconType); + int iconX = cardX + (cardWidth - WEATHER_ICON_SMALL) / 2; + renderer.drawImage(icon, iconX, textY, WEATHER_ICON_SMALL, WEATHER_ICON_SMALL); + textY += WEATHER_ICON_SMALL + 5; + + // High / Low temp + char tempBuf[24]; + snprintf(tempBuf, sizeof(tempBuf), "%.0f/%.0f", day.tempMax, day.tempMin); + int tempWidth = renderer.getTextWidth(SMALL_FONT_ID, tempBuf); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - tempWidth) / 2, textY, tempBuf); + textY += 15; + + // Precipitation + if (day.precipSum > 0) { + char precipBuf[16]; + snprintf(precipBuf, sizeof(precipBuf), "%.1f%s", day.precipSum, WEATHER_SETTINGS.getPrecipUnitParam()); + int precipWidth = renderer.getTextWidth(SMALL_FONT_ID, precipBuf); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - precipWidth) / 2, textY, precipBuf); + } + + // Card separator + if (i < numDays - 1) { + renderer.drawLine(cardX + cardWidth, y, cardX + cardWidth, y + h); + } + } +} + +void WeatherActivity::renderHourlyGraph(int x, int y, int w, int h) { + if (weatherData.hourly.empty()) return; + + constexpr int leftMargin = 40; // Space for Y-axis labels + constexpr int rightMargin = 10; + constexpr int topMargin = 15; + constexpr int bottomMargin = 25; // Space for X-axis labels + constexpr int precipBarMaxHeight = 30; + + const int graphX = x + leftMargin; + const int graphY = y + topMargin; + const int graphW = w - leftMargin - rightMargin; + const int graphH = h - topMargin - bottomMargin - precipBarMaxHeight; + const int precipY = graphY + graphH; + + size_t numPoints = weatherData.hourly.size(); + if (numPoints < 2) return; + + // Find temperature range + float tempMin = weatherData.hourly[0].temperature; + float tempMax = tempMin; + float maxPrecip = 0; + for (const auto& hr : weatherData.hourly) { + tempMin = std::min(tempMin, hr.temperature); + tempMax = std::max(tempMax, hr.temperature); + maxPrecip = std::max(maxPrecip, hr.precipitation); + } + + // Add padding to temp range + float tempRange = tempMax - tempMin; + if (tempRange < 5.0f) { + float mid = (tempMax + tempMin) / 2.0f; + tempMin = mid - 2.5f; + tempMax = mid + 2.5f; + tempRange = 5.0f; + } + tempMin -= 1.0f; + tempMax += 1.0f; + tempRange = tempMax - tempMin; + + // Draw graph title + renderer.drawText(SMALL_FONT_ID, x + 5, y + 3, tr(STR_WEATHER_48H_FORECAST), true, EpdFontFamily::BOLD); + + // Draw Y-axis labels (temperature) + float tempStep = tempRange / 4.0f; + for (int i = 0; i <= 4; i++) { + float temp = tempMax - i * tempStep; + int labelY = graphY + i * graphH / 4; + char label[8]; + snprintf(label, sizeof(label), "%.0f", temp); + renderer.drawText(SMALL_FONT_ID, x + 2, labelY - 4, label); + // Grid line (dashed effect using short segments) + for (int gx = graphX; gx < graphX + graphW; gx += 8) { + renderer.drawPixel(gx, labelY); + } + } + + // Draw temperature line + int prevPx = -1, prevPy = -1; + for (size_t i = 0; i < numPoints; i++) { + int px = graphX + static_cast(i * graphW / (numPoints - 1)); + float normalized = (weatherData.hourly[i].temperature - tempMin) / tempRange; + int py = graphY + graphH - static_cast(normalized * graphH); + + if (prevPx >= 0) { + renderer.drawLine(prevPx, prevPy, px, py, 2, true); + } + prevPx = px; + prevPy = py; + } + + // Draw precipitation bars + if (maxPrecip > 0) { + for (size_t i = 0; i < numPoints; i++) { + float precip = weatherData.hourly[i].precipitation; + if (precip <= 0) continue; + + int px = graphX + static_cast(i * graphW / (numPoints - 1)); + int barH = static_cast((precip / maxPrecip) * precipBarMaxHeight); + if (barH < 1) barH = 1; + int barW = std::max(1, graphW / static_cast(numPoints) - 1); + renderer.fillRect(px - barW / 2, precipY + precipBarMaxHeight - barH, barW, barH); + } + } + + // Draw X-axis labels (every 6 hours) + for (size_t i = 0; i < numPoints; i += 6) { + int px = graphX + static_cast(i * graphW / (numPoints - 1)); + struct tm timeinfo; + time_t localTime = weatherData.hourly[i].time + weatherData.utcOffsetSeconds; + gmtime_r(&localTime, &timeinfo); + char label[8]; + snprintf(label, sizeof(label), "%02d:00", timeinfo.tm_hour); + renderer.drawText(SMALL_FONT_ID, px - 12, precipY + precipBarMaxHeight + 3, label); + + // Vertical grid line + for (int gy = graphY; gy < precipY + precipBarMaxHeight; gy += 8) { + renderer.drawPixel(px, gy); + } + } + + // Draw axes + renderer.drawLine(graphX, graphY, graphX, precipY + precipBarMaxHeight); // Y axis + renderer.drawLine(graphX, precipY + precipBarMaxHeight, graphX + graphW, + precipY + precipBarMaxHeight); // X axis +} diff --git a/src/activities/weather/WeatherActivity.h b/src/activities/weather/WeatherActivity.h new file mode 100644 index 00000000..6768d106 --- /dev/null +++ b/src/activities/weather/WeatherActivity.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include + +#include "../Activity.h" +#include "util/ButtonNavigator.h" + +/** + * Weather forecast display activity. + * Renders in landscape mode (800x480) with: + * - Current conditions (left panel) + * - Daily forecast cards (center) + * - 48-hour temperature + precipitation graph (bottom) + * + * On enter: loads cache -> if stale, connects WiFi -> fetches data -> displays. + * Keys: Back = return, Confirm = open settings, Left = refresh. + */ +class WeatherActivity final : public Activity { + public: + enum class State { LOADING_CACHE, CHECK_WIFI, WIFI_SELECTION, FETCHING, WEATHER_DISPLAY, ERROR }; + + explicit WeatherActivity(GfxRenderer& renderer, MappedInputManager& mappedInput) + : Activity("Weather", renderer, mappedInput) {} + + void onEnter() override; + void onExit() override; + void loop() override; + void render(RenderLock&&) override; + + private: + ButtonNavigator buttonNavigator; + State state = State::LOADING_CACHE; + WeatherData weatherData; + std::string errorMessage; + bool forceRefresh = false; + + void loadAndDisplay(); + void checkAndConnectWifi(); + void launchWifiSelection(); + void onWifiSelectionComplete(bool connected); + void fetchWeather(); + + // Render sub-sections (landscape 800x480) + void renderCurrentConditions(int x, int y, int w, int h); + void renderDailyForecast(int x, int y, int w, int h); + void renderHourlyGraph(int x, int y, int w, int h); + + bool preventAutoSleep() override { return true; } +}; diff --git a/src/activities/weather/WeatherSettingsActivity.cpp b/src/activities/weather/WeatherSettingsActivity.cpp new file mode 100644 index 00000000..01491992 --- /dev/null +++ b/src/activities/weather/WeatherSettingsActivity.cpp @@ -0,0 +1,290 @@ +#include "WeatherSettingsActivity.h" + +#include +#include +#include +#include +#include +#include + +#include "MappedInputManager.h" +#include "activities/network/WifiSelectionActivity.h" +#include "activities/util/KeyboardEntryActivity.h" +#include "components/UITheme.h" +#include "fontIds.h" + +namespace { +const StrId menuNames[] = { + StrId::STR_WEATHER_LOCATION, // 0: Search city + StrId::STR_WEATHER_LATITUDE, // 1: Manual latitude + StrId::STR_WEATHER_LONGITUDE, // 2: Manual longitude + StrId::STR_WEATHER_TEMP_UNIT, // 3: Temperature unit + StrId::STR_WEATHER_WIND_UNIT, // 4: Wind speed unit + StrId::STR_WEATHER_PRECIP_UNIT, // 5: Precipitation unit +}; +} // namespace + +void WeatherSettingsActivity::onEnter() { + Activity::onEnter(); + selectedIndex = 0; + showingSearchResults = false; + requestUpdate(); +} + +void WeatherSettingsActivity::onExit() { Activity::onExit(); } + +void WeatherSettingsActivity::loop() { + if (showingSearchResults) { + // Handle search results navigation + if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + showingSearchResults = false; + requestUpdate(); + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + if (!searchResults.empty() && selectedIndex < searchResults.size()) { + const auto& result = searchResults[selectedIndex]; + WEATHER_SETTINGS.setLocation(result.latitude, result.longitude, result.name + ", " + result.country); + WEATHER_SETTINGS.saveToFile(); + showingSearchResults = false; + selectedIndex = 0; + requestUpdate(); + } + return; + } + + buttonNavigator.onNext([this] { + selectedIndex = (selectedIndex + 1) % searchResults.size(); + requestUpdate(); + }); + + buttonNavigator.onPrevious([this] { + selectedIndex = (selectedIndex + searchResults.size() - 1) % searchResults.size(); + requestUpdate(); + }); + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + finish(); + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + handleSelection(); + return; + } + + buttonNavigator.onNext([this] { + selectedIndex = (selectedIndex + 1) % MENU_ITEMS; + requestUpdate(); + }); + + buttonNavigator.onPrevious([this] { + selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS; + requestUpdate(); + }); +} + +void WeatherSettingsActivity::handleSelection() { + switch (selectedIndex) { + case 0: + launchCitySearch(); + break; + case 1: + launchLatitudeEntry(); + break; + case 2: + launchLongitudeEntry(); + break; + case 3: + toggleTempUnit(); + break; + case 4: + toggleWindUnit(); + break; + case 5: + togglePrecipUnit(); + break; + } +} + +void WeatherSettingsActivity::launchCitySearch() { + startActivityForResult( + std::make_unique(renderer, mappedInput, tr(STR_WEATHER_SEARCH_CITY), "", 64, false), + [this](const ActivityResult& result) { + if (result.isCancelled) return; + + const auto& kb = std::get(result.data); + if (kb.text.empty()) return; + + // Need WiFi for geocoding + if (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0)) { + startActivityForResult(std::make_unique(renderer, mappedInput), + [this, query = kb.text](const ActivityResult& wifiResult) { + if (wifiResult.isCancelled) return; + searchResults = WeatherClient::searchCity(query); + if (searchResults.empty()) { + requestUpdate(); + return; + } + showingSearchResults = true; + selectedIndex = 0; + requestUpdate(); + }); + } else { + searchResults = WeatherClient::searchCity(kb.text); + if (searchResults.empty()) { + requestUpdate(); + return; + } + showingSearchResults = true; + selectedIndex = 0; + requestUpdate(); + } + }); +} + +void WeatherSettingsActivity::launchLatitudeEntry() { + std::string current = std::to_string(WEATHER_SETTINGS.getLatitude()); + startActivityForResult( + std::make_unique(renderer, mappedInput, tr(STR_WEATHER_LATITUDE), current, 12, false), + [this](const ActivityResult& result) { + if (!result.isCancelled) { + const auto& kb = std::get(result.data); + float lat = strtof(kb.text.c_str(), nullptr); + if (lat >= -90.0f && lat <= 90.0f) { + WEATHER_SETTINGS.setLocation(lat, WEATHER_SETTINGS.getLongitude(), WEATHER_SETTINGS.getLocationName()); + WEATHER_SETTINGS.saveToFile(); + } + } + }); +} + +void WeatherSettingsActivity::launchLongitudeEntry() { + std::string current = std::to_string(WEATHER_SETTINGS.getLongitude()); + startActivityForResult( + std::make_unique(renderer, mappedInput, tr(STR_WEATHER_LONGITUDE), current, 12, false), + [this](const ActivityResult& result) { + if (!result.isCancelled) { + const auto& kb = std::get(result.data); + float lon = strtof(kb.text.c_str(), nullptr); + if (lon >= -180.0f && lon <= 180.0f) { + WEATHER_SETTINGS.setLocation(WEATHER_SETTINGS.getLatitude(), lon, WEATHER_SETTINGS.getLocationName()); + WEATHER_SETTINGS.saveToFile(); + } + } + }); +} + +void WeatherSettingsActivity::toggleTempUnit() { + auto current = WEATHER_SETTINGS.getTempUnit(); + WEATHER_SETTINGS.setTempUnit(current == WeatherTempUnit::CELSIUS ? WeatherTempUnit::FAHRENHEIT + : WeatherTempUnit::CELSIUS); + WEATHER_SETTINGS.saveToFile(); + requestUpdate(); +} + +void WeatherSettingsActivity::toggleWindUnit() { + auto current = static_cast(WEATHER_SETTINGS.getWindUnit()); + WEATHER_SETTINGS.setWindUnit(static_cast((current + 1) % 4)); + WEATHER_SETTINGS.saveToFile(); + requestUpdate(); +} + +void WeatherSettingsActivity::togglePrecipUnit() { + auto current = WEATHER_SETTINGS.getPrecipUnit(); + WEATHER_SETTINGS.setPrecipUnit(current == WeatherPrecipUnit::MM ? WeatherPrecipUnit::INCH : WeatherPrecipUnit::MM); + WEATHER_SETTINGS.saveToFile(); + requestUpdate(); +} + +void WeatherSettingsActivity::render(RenderLock&&) { + renderer.clearScreen(); + + const auto& metrics = UITheme::getInstance().getMetrics(); + const auto pageWidth = renderer.getScreenWidth(); + const auto pageHeight = renderer.getScreenHeight(); + + if (showingSearchResults) { + // Display search results + GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, + tr(STR_WEATHER_SEARCH_RESULTS)); + + const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; + const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + + if (searchResults.empty()) { + renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, tr(STR_NO_ENTRIES)); + } else { + GUI.drawList( + renderer, Rect{0, contentTop, pageWidth, contentHeight}, static_cast(searchResults.size()), + static_cast(selectedIndex), + [this](int index) { + const auto& r = searchResults[index]; + std::string label = r.name; + if (!r.admin1.empty()) label += ", " + r.admin1; + if (!r.country.empty()) label += ", " + r.country; + return label; + }, + nullptr, nullptr, nullptr, true); + } + + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + renderer.displayBuffer(); + return; + } + + // Main settings menu + GUI.drawHeader(renderer, Rect{0, metrics.topPadding, pageWidth, metrics.headerHeight}, tr(STR_WEATHER_SETTINGS)); + + const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing; + const int contentHeight = pageHeight - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing * 2; + + GUI.drawList( + renderer, Rect{0, contentTop, pageWidth, contentHeight}, MENU_ITEMS, static_cast(selectedIndex), + [](int index) { return std::string(I18N.get(menuNames[index])); }, nullptr, nullptr, + [this](int index) -> std::string { + switch (index) { + case 0: { + auto name = WEATHER_SETTINGS.getLocationName(); + return name.empty() ? std::string(tr(STR_NOT_SET)) : name; + } + case 1: { + char buf[16]; + snprintf(buf, sizeof(buf), "%.4f", WEATHER_SETTINGS.getLatitude()); + return std::string(buf); + } + case 2: { + char buf[16]; + snprintf(buf, sizeof(buf), "%.4f", WEATHER_SETTINGS.getLongitude()); + return std::string(buf); + } + case 3: + return WEATHER_SETTINGS.getTempUnit() == WeatherTempUnit::CELSIUS ? "C" : "F"; + case 4: + switch (WEATHER_SETTINGS.getWindUnit()) { + case WeatherWindUnit::KMH: + return "km/h"; + case WeatherWindUnit::MS: + return "m/s"; + case WeatherWindUnit::MPH: + return "mph"; + case WeatherWindUnit::KNOTS: + return "kn"; + } + return "km/h"; + case 5: + return WEATHER_SETTINGS.getPrecipUnit() == WeatherPrecipUnit::MM ? "mm" : "in"; + } + return ""; + }, + true); + + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + renderer.displayBuffer(); +} diff --git a/src/activities/weather/WeatherSettingsActivity.h b/src/activities/weather/WeatherSettingsActivity.h new file mode 100644 index 00000000..e0367b73 --- /dev/null +++ b/src/activities/weather/WeatherSettingsActivity.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include + +#include "../Activity.h" +#include "util/ButtonNavigator.h" + +/** + * Settings submenu for weather configuration. + * Supports city search via geocoding, manual lat/lon entry, and unit selection. + */ +class WeatherSettingsActivity final : public Activity { + public: + explicit WeatherSettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput) + : Activity("WeatherSettings", renderer, mappedInput) {} + + void onEnter() override; + void onExit() override; + void loop() override; + void render(RenderLock&&) override; + + private: + ButtonNavigator buttonNavigator; + size_t selectedIndex = 0; + + // City search results (populated when user searches) + std::vector searchResults; + bool showingSearchResults = false; + + static constexpr int MENU_ITEMS = 6; // Location, Lat, Lon, TempUnit, WindUnit, PrecipUnit + + void handleSelection(); + void launchCitySearch(); + void launchLatitudeEntry(); + void launchLongitudeEntry(); + void toggleTempUnit(); + void toggleWindUnit(); + void togglePrecipUnit(); + + bool preventAutoSleep() override { return true; } +}; From 049a63a8c15fae2f11349e469043c518c9f19cda Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 2 Apr 2026 17:17:50 +0200 Subject: [PATCH 2/5] Add proper weather forecast screen --- README.md | 4 + lib/I18n/translations/english.yaml | 72 ++- lib/I18n/translations/german.yaml | 60 +++ lib/Weather/WeatherClient.cpp | 20 +- lib/Weather/WeatherIcons.cpp | 33 +- lib/Weather/WeatherIcons.h | 8 + lib/Weather/WeatherIcons48.h | 215 +++++++++ src/activities/ActivityManager.cpp | 3 + src/activities/ActivityManager.h | 1 + src/activities/home/HomeActivity.cpp | 13 +- src/activities/home/HomeActivity.h | 1 + src/activities/settings/SettingsActivity.cpp | 5 + src/activities/settings/SettingsActivity.h | 1 + src/activities/weather/WeatherActivity.cpp | 458 ++++++++++++++++--- src/activities/weather/WeatherActivity.h | 2 + src/main.cpp | 2 + src/network/HttpDownloader.cpp | 30 ++ 17 files changed, 833 insertions(+), 95 deletions(-) create mode 100644 lib/Weather/WeatherIcons48.h diff --git a/README.md b/README.md index b9093ea5..8b4f876b 100644 --- a/README.md +++ b/README.md @@ -177,3 +177,7 @@ CrossPoint Reader is **not affiliated with Xteink or any manufacturer of the X4 Huge shoutout to [**diy-esp32-epub-reader** by atomic14](https://github.com/atomic14/diy-esp32-epub-reader), which was a project I took a lot of inspiration from as I was making CrossPoint. + +## Third-Party Attribution + +- Weather icon artwork used by the Weather activity is adapted from [weather-icons by Erik Flowers](https://github.com/erikflowers/weather-icons). diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index b754c46d..be02b523 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -290,22 +290,60 @@ STR_LINK: "[link]" STR_SCREENSHOT_BUTTON: "Take screenshot" STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: " STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)" -STR_WEATHER_LOCATION: "Location: " +STR_WEATHER: "Weather" +STR_WEATHER_LOCATION: "Location" STR_WEATHER_NO_LOCATION: "No location set for weather" STR_WEATHER_FETCH_FAILED: "Failed to fetch weather" -STR_WEATHER_SETTINGS: "Settings" -STR_WEATHER_REFRESH: "Refresh Weather" -STR_WEATHER_FEELS_LIKE: "Feels like: " -STR_WEATHER_HUMIDITY: "Humidity: " -STR_WEATHER_WIND: "Wind: " -STR_WEATHER_PRESSURE: "Pressure: " -STR_WEATHER_LAST_UPDATED: "Last updated: " -STR_WEATHER_PRECIP: "Precipitation: " -STR_WEATHER_PRECIP_UNIT: "Units:" -STR_WEATHER_WIND_UNIT: "Units:" -STR_WEATHER_TEMP_UNIT: "Units:" -STR_WEATHER_48H_FORECAST: "48-hour forecast" -STR_WEATHER_SEARCH_CITY: "Search city" -STR_WEATHER_LONGITUDE: "Longitude: " -STR_WEATHER_LATITUDE: "Latitude: " -STR_WEATHER_SEARCH_RESULTS: "Search results" +STR_WEATHER_SETTINGS: "Weather Settings" +STR_WEATHER_SETTINGS_SHORT: "Settings" +STR_WEATHER_REFRESH: "Refresh" +STR_WEATHER_FEELS_LIKE: "Feels like" +STR_WEATHER_HUMIDITY: "Humidity" +STR_WEATHER_WIND: "Wind" +STR_WEATHER_PRESSURE: "Pressure" +STR_WEATHER_LAST_UPDATED: "Last updated" +STR_WEATHER_PRECIP: "Precip." +STR_WEATHER_PRECIP_UNIT: "Precipitation Unit" +STR_WEATHER_WIND_UNIT: "Wind Speed Unit" +STR_WEATHER_TEMP_UNIT: "Temperature Unit" +STR_WEATHER_48H_FORECAST: "48-Hour Forecast" +STR_WEATHER_SEARCH_CITY: "Search City" +STR_WEATHER_LONGITUDE: "Longitude" +STR_WEATHER_LATITUDE: "Latitude" +STR_WEATHER_SEARCH_RESULTS: "Search Results" +STR_WEATHER_DAY_MON: "Mon" +STR_WEATHER_DAY_TUE: "Tue" +STR_WEATHER_DAY_WED: "Wed" +STR_WEATHER_DAY_THU: "Thu" +STR_WEATHER_DAY_FRI: "Fri" +STR_WEATHER_DAY_SAT: "Sat" +STR_WEATHER_DAY_SUN: "Sun" +STR_WEATHER_DESC_CLEAR_SKY: "Clear sky" +STR_WEATHER_DESC_MAINLY_CLEAR: "Mainly clear" +STR_WEATHER_DESC_PARTLY_CLOUDY: "Partly cloudy" +STR_WEATHER_DESC_OVERCAST: "Overcast" +STR_WEATHER_DESC_FOG: "Fog" +STR_WEATHER_DESC_RIME_FOG: "Rime fog" +STR_WEATHER_DESC_LIGHT_DRIZZLE: "Light drizzle" +STR_WEATHER_DESC_DRIZZLE: "Drizzle" +STR_WEATHER_DESC_DENSE_DRIZZLE: "Dense drizzle" +STR_WEATHER_DESC_FREEZING_DRIZZLE: "Freezing drizzle" +STR_WEATHER_DESC_DENSE_FREEZING_DRIZZLE: "Dense freezing drizzle" +STR_WEATHER_DESC_SLIGHT_RAIN: "Slight rain" +STR_WEATHER_DESC_MODERATE_RAIN: "Moderate rain" +STR_WEATHER_DESC_HEAVY_RAIN: "Heavy rain" +STR_WEATHER_DESC_FREEZING_RAIN: "Freezing rain" +STR_WEATHER_DESC_HEAVY_FREEZING_RAIN: "Heavy freezing rain" +STR_WEATHER_DESC_SLIGHT_SNOW: "Slight snow" +STR_WEATHER_DESC_MODERATE_SNOW: "Moderate snow" +STR_WEATHER_DESC_HEAVY_SNOW: "Heavy snow" +STR_WEATHER_DESC_SNOW_GRAINS: "Snow grains" +STR_WEATHER_DESC_SLIGHT_SHOWERS: "Slight showers" +STR_WEATHER_DESC_MODERATE_SHOWERS: "Moderate showers" +STR_WEATHER_DESC_VIOLENT_SHOWERS: "Violent showers" +STR_WEATHER_DESC_SNOW_SHOWERS: "Snow showers" +STR_WEATHER_DESC_HEAVY_SNOW_SHOWERS: "Heavy snow showers" +STR_WEATHER_DESC_THUNDERSTORM: "Thunderstorm" +STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Thunderstorm, hail" +STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Thunderstorm, heavy hail" +STR_WEATHER_DESC_UNKNOWN: "Unknown" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index b53ff494..03bea99b 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -320,3 +320,63 @@ STR_UPTIME: "Laufzeit" STR_CHARGING: "Lädt" STR_GATHERING_DATA: "Daten werden gesammelt..." STR_READING: "Lese..." + +STR_WEATHER: "Wetter" +STR_WEATHER_LOCATION: "Ort" +STR_WEATHER_NO_LOCATION: "Kein Ort für Wetter festgelegt" +STR_WEATHER_FETCH_FAILED: "Wetterdaten konnten nicht geladen werden" +STR_WEATHER_SETTINGS: "Wettereinstellungen" +STR_WEATHER_SETTINGS_SHORT: "Einstell." +STR_WEATHER_REFRESH: "Aktual." +STR_WEATHER_FEELS_LIKE: "Gefühlt" +STR_WEATHER_HUMIDITY: "Luftfeuchte" +STR_WEATHER_WIND: "Wind" +STR_WEATHER_PRESSURE: "Luftdruck" +STR_WEATHER_LAST_UPDATED: "Zuletzt aktualisiert" +STR_WEATHER_PRECIP: "Niederschl." +STR_WEATHER_PRECIP_UNIT: "Niederschlagseinheit" +STR_WEATHER_WIND_UNIT: "Windgeschwindigkeitseinheit" +STR_WEATHER_TEMP_UNIT: "Temperatureinheit" +STR_WEATHER_48H_FORECAST: "48-Stunden-Vorhersage" +STR_WEATHER_SEARCH_CITY: "Stadt suchen" +STR_WEATHER_LONGITUDE: "Längengrad" +STR_WEATHER_LATITUDE: "Breitengrad" +STR_WEATHER_SEARCH_RESULTS: "Suchergebnisse" + +STR_WEATHER_DAY_MON: "Mo" +STR_WEATHER_DAY_TUE: "Di" +STR_WEATHER_DAY_WED: "Mi" +STR_WEATHER_DAY_THU: "Do" +STR_WEATHER_DAY_FRI: "Fr" +STR_WEATHER_DAY_SAT: "Sa" +STR_WEATHER_DAY_SUN: "So" + +STR_WEATHER_DESC_CLEAR_SKY: "Klarer Himmel" +STR_WEATHER_DESC_MAINLY_CLEAR: "Überwiegend klar" +STR_WEATHER_DESC_PARTLY_CLOUDY: "Teilweise bewölkt" +STR_WEATHER_DESC_OVERCAST: "Bedeckt" +STR_WEATHER_DESC_FOG: "Nebel" +STR_WEATHER_DESC_RIME_FOG: "Raureifnebel" +STR_WEATHER_DESC_LIGHT_DRIZZLE: "Leichter Nieselregen" +STR_WEATHER_DESC_DRIZZLE: "Nieselregen" +STR_WEATHER_DESC_DENSE_DRIZZLE: "Dichter Nieselregen" +STR_WEATHER_DESC_FREEZING_DRIZZLE: "Gefrierender Nieselregen" +STR_WEATHER_DESC_DENSE_FREEZING_DRIZZLE: "Dichter gefrierender Nieselregen" +STR_WEATHER_DESC_SLIGHT_RAIN: "Leichter Regen" +STR_WEATHER_DESC_MODERATE_RAIN: "Mäßiger Regen" +STR_WEATHER_DESC_HEAVY_RAIN: "Starker Regen" +STR_WEATHER_DESC_FREEZING_RAIN: "Gefrierender Regen" +STR_WEATHER_DESC_HEAVY_FREEZING_RAIN: "Starker gefrierender Regen" +STR_WEATHER_DESC_SLIGHT_SNOW: "Leichter Schneefall" +STR_WEATHER_DESC_MODERATE_SNOW: "Mäßiger Schneefall" +STR_WEATHER_DESC_HEAVY_SNOW: "Starker Schneefall" +STR_WEATHER_DESC_SNOW_GRAINS: "Schneegriesel" +STR_WEATHER_DESC_SLIGHT_SHOWERS: "Leichte Schauer" +STR_WEATHER_DESC_MODERATE_SHOWERS: "Mäßige Schauer" +STR_WEATHER_DESC_VIOLENT_SHOWERS: "Heftige Schauer" +STR_WEATHER_DESC_SNOW_SHOWERS: "Schneeschauer" +STR_WEATHER_DESC_HEAVY_SNOW_SHOWERS: "Starke Schneeschauer" +STR_WEATHER_DESC_THUNDERSTORM: "Gewitter" +STR_WEATHER_DESC_THUNDERSTORM_HAIL: "Gewitter mit Hagel" +STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL: "Gewitter mit starkem Hagel" +STR_WEATHER_DESC_UNKNOWN: "Unbekannt" diff --git a/lib/Weather/WeatherClient.cpp b/lib/Weather/WeatherClient.cpp index 8c9a4383..da2fb4dc 100644 --- a/lib/Weather/WeatherClient.cpp +++ b/lib/Weather/WeatherClient.cpp @@ -12,7 +12,9 @@ namespace { constexpr char WEATHER_CACHE_FILE[] = "/.crosspoint/weather_cache.json"; std::string buildForecastUrl(const WeatherSettingsStore& settings) { - std::string url = "https://api.open-meteo.com/v1/forecast?"; + // Open-Meteo supports plain HTTP. Using HTTP here avoids TLS handshake + // instability observed on some ESP32-C3 builds. + std::string url = "http://api.open-meteo.com/v1/forecast?"; url += "latitude=" + std::to_string(settings.getLatitude()); url += "&longitude=" + std::to_string(settings.getLongitude()); url += @@ -50,7 +52,15 @@ WeatherData WeatherClient::getWeather(const WeatherSettingsStore& settings, bool return cached; } LOG_DBG("WEA", "Cache expired (age: %ld s)", (long)(now - cached.fetchedAt)); + // Cache-first behavior: return stale cache and let the caller decide + // whether/when to perform a network refresh. + return cached; } + + LOG_DBG("WEA", "No cache available; caller should establish network and force refresh"); + WeatherData data; + data.errorMessage = "No cache"; + return data; } return fetchFromApi(settings); @@ -59,24 +69,30 @@ WeatherData WeatherClient::getWeather(const WeatherSettingsStore& settings, bool WeatherData WeatherClient::fetchFromApi(const WeatherSettingsStore& settings) { WeatherData data; std::string url = buildForecastUrl(settings); + LOG_DBG("WEA", "fetchFromApi[1] start"); + LOG_DBG("WEA", "fetchFromApi[2] url length=%zu", url.size()); LOG_DBG("WEA", "Fetching weather from API"); std::string response; + LOG_DBG("WEA", "fetchFromApi[3] HttpDownloader::fetchUrl before"); if (!HttpDownloader::fetchUrl(url, response)) { data.errorMessage = "Network error"; LOG_ERR("WEA", "Failed to fetch weather data"); return data; } + LOG_DBG("WEA", "fetchFromApi[4] HttpDownloader::fetchUrl after; bytes=%zu", response.size()); + LOG_DBG("WEA", "fetchFromApi[5] parseWeatherJson before"); if (!parseWeatherJson(response, data)) { LOG_ERR("WEA", "Failed to parse weather JSON"); return data; } + LOG_DBG("WEA", "fetchFromApi[6] parseWeatherJson after"); data.valid = true; time(&data.fetchedAt); saveCache(data); - LOG_DBG("WEA", "Weather data fetched and cached"); + LOG_DBG("WEA", "fetchFromApi[7] Weather data fetched and cached"); return data; } diff --git a/lib/Weather/WeatherIcons.cpp b/lib/Weather/WeatherIcons.cpp index 04aa5b57..b6acda6e 100644 --- a/lib/Weather/WeatherIcons.cpp +++ b/lib/Weather/WeatherIcons.cpp @@ -1,5 +1,11 @@ #include "WeatherIcons.h" +#include "WeatherIcons48.h" + +// Weather icon glyph source attribution: +// https://github.com/erikflowers/weather-icons +// 48x48 monochrome icon bitmaps in this file are adapted from that icon set. + // ============================================================================ // 48x48 Large Weather Icons (1-bit, MSB-first, row-major) // Each row = 48 pixels = 6 bytes. Total = 48 * 6 = 288 bytes per icon. @@ -178,6 +184,8 @@ static const uint8_t ICON_FOG_48[] = { // Each row = 24 pixels = 3 bytes. Total = 24 * 3 = 72 bytes per icon. // ============================================================================ +#if WEATHER_ENABLE_SMALL_ICONS + // clang-format off // Small Clear Day - Sun @@ -262,6 +270,8 @@ static const uint8_t ICON_FOG_24[] = { // clang-format on +#endif // WEATHER_ENABLE_SMALL_ICONS + // ============================================================================ // Lookup functions // ============================================================================ @@ -269,30 +279,32 @@ static const uint8_t ICON_FOG_24[] = { const uint8_t* getWeatherIconLarge(WeatherIconType type) { switch (type) { case WeatherIconType::CLEAR_DAY: - return ICON_CLEAR_DAY_48; + return WI48_CLEAR_DAY; case WeatherIconType::CLEAR_NIGHT: - return ICON_CLEAR_NIGHT_48; + return WI48_CLEAR_NIGHT; case WeatherIconType::PARTLY_CLOUDY_DAY: - return ICON_PARTLY_CLOUDY_DAY_48; + return WI48_PARTLY_CLOUDY_DAY; case WeatherIconType::PARTLY_CLOUDY_NIGHT: - return ICON_PARTLY_CLOUDY_NIGHT_48; + return WI48_PARTLY_CLOUDY_NIGHT; case WeatherIconType::OVERCAST: - return ICON_OVERCAST_48; + return WI48_OVERCAST; case WeatherIconType::FOG: - return ICON_FOG_48; + return WI48_FOG; case WeatherIconType::DRIZZLE: + return WI48_DRIZZLE; case WeatherIconType::RAIN: case WeatherIconType::SHOWERS: - return ICON_RAIN_48; + return WI48_RAIN; case WeatherIconType::SNOW: - return ICON_SNOW_48; + return WI48_SNOW; case WeatherIconType::THUNDERSTORM: - return ICON_THUNDERSTORM_48; + return WI48_THUNDERSTORM; default: - return ICON_OVERCAST_48; + return WI48_OVERCAST; } } +#if WEATHER_ENABLE_SMALL_ICONS const uint8_t* getWeatherIconSmall(WeatherIconType type) { switch (type) { case WeatherIconType::CLEAR_DAY: @@ -318,6 +330,7 @@ const uint8_t* getWeatherIconSmall(WeatherIconType type) { return ICON_OVERCAST_24; } } +#endif // WEATHER_ENABLE_SMALL_ICONS const char* getWeatherDescription(int wmoCode) { switch (wmoCode) { diff --git a/lib/Weather/WeatherIcons.h b/lib/Weather/WeatherIcons.h index bcb60dfc..ef928e39 100644 --- a/lib/Weather/WeatherIcons.h +++ b/lib/Weather/WeatherIcons.h @@ -1,6 +1,12 @@ #pragma once #include +// Set to 1 (for example via build flag -DWEATHER_ENABLE_SMALL_ICONS=1) +// to include 24x24 weather icon assets and API. +#ifndef WEATHER_ENABLE_SMALL_ICONS +#define WEATHER_ENABLE_SMALL_ICONS 0 +#endif + // Weather icon size constants constexpr int WEATHER_ICON_LARGE = 48; // Large icon for current weather constexpr int WEATHER_ICON_SMALL = 24; // Small icon for daily forecast @@ -73,7 +79,9 @@ const char* getWeatherDescription(int wmoCode); const uint8_t* getWeatherIconLarge(WeatherIconType type); // Get the appropriate small icon bitmap (24x24, 1-bit, MSB first) +#if WEATHER_ENABLE_SMALL_ICONS const uint8_t* getWeatherIconSmall(WeatherIconType type); +#endif // Wind direction arrow text (N, NE, E, SE, S, SW, W, NW) const char* getWindDirectionText(int degrees); diff --git a/lib/Weather/WeatherIcons48.h b/lib/Weather/WeatherIcons48.h new file mode 100644 index 00000000..b92bc221 --- /dev/null +++ b/lib/Weather/WeatherIcons48.h @@ -0,0 +1,215 @@ +#pragma once +#include + +// Generated from erikflowers/weather-icons (font + css mappings) +// 48x48, 1-bit, MSB-first, row-major, rotated for device orientation + +static const uint8_t WI48_CLEAR_DAY[] = { + 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFE, 0x7F, 0xF8, 0x7F, 0xFC, 0x7F, + 0xFC, 0x3F, 0xFC, 0xFF, 0xF8, 0x3F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFE, 0x0F, 0xFF, 0xFF, + 0xC0, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x8F, + 0xC0, 0x0F, 0xC3, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xC0, 0x7F, 0xFF, 0xFF, 0xF8, 0x3F, 0xF0, + 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, + 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFE, 0x1F, 0xFF, 0x00, 0xE1, 0xFF, 0xFE, 0x1F, 0x00, + 0x00, 0xE1, 0xFF, 0xFE, 0x1F, 0x00, 0x00, 0xE1, 0xFF, 0xFE, 0x1F, 0x00, 0x81, 0xE1, 0xFF, 0xFE, + 0x1F, 0x81, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, + 0x7F, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, 0x3F, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, 0xE0, 0x7F, 0xFF, + 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, + 0xFF, 0xFF, 0xFF, 0xCF, 0xC0, 0x0F, 0xE7, 0xFF, 0xFF, 0x8F, 0xF0, 0x3F, 0xC3, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, + 0xFC, 0x1F, 0xFC, 0xFF, 0xF0, 0x3F, 0xFC, 0x3F, 0xF8, 0x7F, 0xFC, 0x3F, 0xFE, 0x7F, 0xF8, 0x7F, + 0xFE, 0x7F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, +}; + +static const uint8_t WI48_CLEAR_NIGHT[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0xFE, 0x07, 0xFF, + 0xFF, 0xFF, 0xE1, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, + 0xC1, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xC1, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0xFF, 0xFF, 0x00, + 0x0F, 0xFF, 0xE0, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF0, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF0, 0xFF, + 0xFE, 0x01, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x07, + 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0x81, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xE0, 0x7F, 0xFE, + 0x07, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xF0, 0x03, 0x80, 0x1F, 0xFF, 0xFF, 0xFC, + 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +static const uint8_t WI48_PARTLY_CLOUDY_DAY[] = { + 0xFE, 0x3F, 0xFC, 0x3F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFE, 0x0F, 0xFF, 0xFF, + 0xF8, 0x3F, 0xFE, 0x07, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0x03, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x83, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xE3, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x07, 0xE0, + 0x00, 0x0F, 0xFF, 0xFC, 0x1F, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x3F, 0xE0, 0x7F, 0x03, 0xFF, 0xF8, + 0x7F, 0xC0, 0xFF, 0x81, 0xFF, 0xF0, 0x7F, 0xC1, 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0x83, 0xFF, 0xE0, + 0x00, 0x70, 0xFF, 0x83, 0xFF, 0xE0, 0x00, 0x70, 0xFF, 0x87, 0xFF, 0xF0, 0x00, 0x70, 0xFF, 0x87, + 0xFF, 0xF0, 0x80, 0xF0, 0xFF, 0x87, 0xFF, 0xF0, 0xFF, 0xF0, 0x7C, 0x07, 0xFF, 0xF0, 0xFF, 0xF0, + 0x78, 0x07, 0xFF, 0xF0, 0xFF, 0xF8, 0x30, 0x07, 0xFF, 0xF0, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xF0, + 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, + 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, + 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0x9E, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0x0E, 0x1F, 0xFF, 0xFF, 0xF0, + 0xFE, 0x0E, 0x1F, 0xFF, 0xFF, 0xF0, 0xFE, 0x1E, 0x1F, 0xFF, 0xFF, 0xF0, 0xFE, 0x1E, 0x1F, 0xFF, + 0xFF, 0xF0, 0xFF, 0x3E, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, + 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xE0, 0x0F, + 0xFF, 0xF0, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, + 0xFE, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xE0, +}; + +static const uint8_t WI48_PARTLY_CLOUDY_NIGHT[] = { + 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x60, + 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x08, 0x00, 0x01, 0xFF, 0xFF, 0xFC, + 0x0C, 0x00, 0x00, 0xFF, 0xFC, 0x60, 0x1C, 0x0F, 0xE0, 0x7F, 0xF8, 0x00, 0x38, 0x1F, 0xF0, 0x3F, + 0xF8, 0x00, 0x78, 0x3F, 0xF8, 0x3F, 0xF8, 0x01, 0xF0, 0x7F, 0xFC, 0x1F, 0xF8, 0x0F, 0xF0, 0x7F, + 0xFC, 0x1F, 0xF8, 0x7F, 0xF0, 0xFF, 0xFE, 0x1F, 0xF8, 0x7F, 0xF0, 0xFF, 0xFE, 0x1F, 0xF8, 0x3F, + 0xF0, 0xFF, 0xFE, 0x1F, 0xF8, 0x3F, 0x80, 0xFF, 0xFE, 0x1F, 0xFC, 0x1F, 0x00, 0xFF, 0xFE, 0x1F, + 0xFC, 0x1E, 0x00, 0xFF, 0xFE, 0x1F, 0xFE, 0x04, 0x03, 0xFF, 0xFE, 0x1F, 0xFE, 0x00, 0x1F, 0xFF, + 0xFE, 0x1F, 0xFF, 0x00, 0x3F, 0xFF, 0xFE, 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFE, 0x1F, 0xFF, 0xE0, + 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xE1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, 0x1F, + 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, + 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC1, + 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xE1, 0xFF, 0xFF, 0xFE, 0x1F, + 0xFF, 0xE0, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xE0, 0x7F, 0xFF, 0xFE, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, + 0xFE, 0x1F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFC, 0x01, 0xFF, 0xFE, 0x1F, 0xFF, 0xFE, + 0x00, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x00, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, 0x1F, + 0xFF, 0xFF, 0xF0, 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x3F, + 0xF8, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, + 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, +}; + +static const uint8_t WI48_OVERCAST[] = { + 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, + 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xFF, + 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x3F, 0x80, + 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0x00, 0x07, 0xFF, 0xFF, 0x87, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x0F, + 0xFC, 0x1F, 0x81, 0xFF, 0xFF, 0x1F, 0xFC, 0x3F, 0xE1, 0xFF, 0xFE, 0x1F, 0xF8, 0x7F, 0xF0, 0xFF, + 0xFE, 0x3F, 0xF8, 0x7F, 0xF0, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xF8, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, + 0xF8, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, 0xC0, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, + 0x80, 0xFF, 0xF8, 0xFF, 0xFF, 0x0E, 0x00, 0xFF, 0xF8, 0xFF, 0xFF, 0x06, 0x0F, 0xFF, 0xF8, 0xFF, + 0xFF, 0x8C, 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0x88, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, + 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, + 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, + 0xF8, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xF8, 0xFF, + 0xFF, 0xFF, 0x80, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, + 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xE1, 0xFF, 0xFF, 0xFF, + 0xFC, 0x1F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFF, +}; + +static const uint8_t WI48_FOG[] = { + 0xFF, 0xFF, 0xFD, 0xE7, 0xFF, 0x0F, 0xFF, 0xFF, 0xE1, 0xC3, 0xFF, 0x0F, 0xFF, 0xFF, 0xC1, 0xC3, + 0xFF, 0x0F, 0xFF, 0xFF, 0x81, 0xC3, 0xFF, 0x0F, 0xFF, 0xFF, 0x01, 0xC3, 0xFF, 0x0F, 0xFF, 0xFE, + 0x07, 0xC3, 0xFF, 0x0F, 0xFF, 0xFE, 0x0F, 0xC3, 0xCF, 0x0F, 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, + 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x3F, 0xC3, + 0x87, 0x0F, 0xFF, 0xFC, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xF0, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xC0, + 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0x80, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0x00, 0x3F, 0xC3, 0x87, 0x0F, + 0xFE, 0x07, 0xFF, 0xC3, 0x87, 0x0F, 0xFC, 0x0F, 0xFF, 0xC3, 0x87, 0x0F, 0xFC, 0x1F, 0xFF, 0xC3, + 0x87, 0x0F, 0xF8, 0x3F, 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x7F, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0x7F, + 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0x7F, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, + 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, + 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0x7F, 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x7F, + 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x3F, 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x3F, 0xFF, 0xC3, 0x87, 0x0F, + 0xFC, 0x1F, 0xFF, 0xC3, 0x87, 0x0F, 0xFE, 0x07, 0xFF, 0xC3, 0x87, 0x0F, 0xFE, 0x00, 0xFF, 0xC3, + 0x87, 0x0F, 0xFF, 0x00, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xC0, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xE0, + 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, + 0xFF, 0xFE, 0x0F, 0xC3, 0x87, 0x0F, 0xFF, 0xFE, 0x07, 0xC3, 0x87, 0x0F, 0xFF, 0xFF, 0x03, 0xC3, + 0x87, 0x1F, 0xFF, 0xFF, 0x81, 0xC3, 0x87, 0xFF, 0xFF, 0xFF, 0xC1, 0xC3, 0x87, 0xFF, 0xFF, 0xFF, + 0xE1, 0xC3, 0x87, 0xFF, 0xFF, 0xFF, 0xF9, 0xC7, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, +}; + +static const uint8_t WI48_DRIZZLE[] = { + 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, + 0x03, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x07, 0xFC, 0x0F, 0xFF, 0xFF, 0xFE, 0x0F, 0xFE, 0x0F, 0xFF, + 0xFF, 0xFC, 0x1F, 0xFF, 0x07, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x07, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, + 0x87, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xC0, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, + 0x00, 0x7F, 0xF8, 0x3F, 0xFF, 0xFE, 0x00, 0x3F, 0xF8, 0x3F, 0xE1, 0xFC, 0x00, 0x3F, 0xF0, 0x7F, + 0xC0, 0xF8, 0x00, 0x3F, 0xF0, 0x7F, 0xC0, 0xF0, 0x00, 0x3F, 0xF0, 0xFF, 0xE1, 0xF8, 0x00, 0x3F, + 0xF0, 0xFF, 0xFF, 0xFC, 0x00, 0x3F, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, + 0x80, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xF0, 0x7F, 0xFF, 0x83, 0xFF, 0xFF, 0xF0, 0x7F, + 0xFF, 0x03, 0xFF, 0xFF, 0xF8, 0x7F, 0xFC, 0x01, 0xFF, 0xFF, 0xF8, 0x3F, 0xFC, 0x01, 0xFF, 0xFF, + 0xF8, 0x1F, 0xFE, 0x03, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0x83, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xEF, + 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x07, 0xFF, + 0xFF, 0xFC, 0x1F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, 0x0F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFE, 0x07, 0xFC, + 0x1F, 0xFF, 0xFF, 0xFF, 0x01, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, +}; + +static const uint8_t WI48_RAIN[] = { + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, + 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, + 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0xFE, 0x07, 0xFF, 0xFF, 0xFE, 0x01, 0xFE, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFE, 0x00, 0x0F, 0xFF, + 0xF0, 0x0F, 0xFF, 0x00, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xE0, 0xFF, 0xFF, 0xFE, + 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFE, 0x03, 0xFF, 0xFF, 0x83, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0x87, 0xFF, 0xFE, 0x00, 0x07, 0xFF, + 0x87, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0x87, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0x87, 0xFF, 0xFF, 0xFF, + 0x00, 0x01, 0x87, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x83, 0xFF, 0xFE, 0x1F, 0xFE, 0x01, 0x83, 0xFF, + 0xFE, 0x01, 0xFF, 0xE3, 0xC3, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x00, 0x03, 0xFF, + 0xC0, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xE0, 0x7F, 0xFF, 0xF8, 0x00, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, + 0x80, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xF1, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x01, + 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, + 0xFF, 0xE0, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, + 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, + 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, +}; + +static const uint8_t WI48_SNOW[] = { + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, + 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, + 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFC, 0x3C, 0x3F, + 0xF0, 0x0F, 0xFF, 0xFC, 0x3C, 0x3F, 0xF0, 0x3F, 0xFF, 0xFC, 0x3C, 0x3F, 0xE0, 0xFF, 0xFF, 0xFE, + 0x7E, 0x7F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xE7, 0xE7, 0xF3, 0x87, 0xFF, 0xFF, 0xC3, 0xC3, 0xE1, + 0x87, 0xFF, 0xFF, 0xC3, 0xC3, 0xE1, 0x87, 0xFF, 0xFF, 0xC3, 0xC3, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, 0xC1, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, + 0xC0, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x01, + 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, + 0xFF, 0xE0, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, + 0x7F, 0xFF, 0xFF, 0xF8, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, + 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, +}; + +static const uint8_t WI48_THUNDERSTORM[] = { + 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x0F, + 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, + 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xFF, + 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xF8, + 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xC3, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, + 0xFC, 0x0F, 0xFF, 0xFF, 0xFC, 0x03, 0xFC, 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFC, 0x00, 0x0F, 0xFF, + 0xE0, 0x1F, 0xFE, 0x00, 0x03, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, + 0x01, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0x83, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0x07, 0xFF, + 0xFC, 0x07, 0xFF, 0xFF, 0x07, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, + 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0x0F, 0xFF, 0xFF, 0xFF, + 0x00, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0x07, 0xFF, 0xFD, 0xFF, 0xFF, 0x03, 0x07, 0xFF, + 0xFC, 0x7F, 0x9F, 0xFF, 0x87, 0xFF, 0xFC, 0x1F, 0x8F, 0xFF, 0x83, 0xFF, 0xFC, 0x07, 0x83, 0xFF, + 0x81, 0xFF, 0xFC, 0x01, 0x81, 0xFF, 0xC0, 0xFF, 0xFC, 0x00, 0x80, 0x7F, 0xE0, 0x7F, 0xFC, 0x00, + 0x00, 0x3F, 0xF0, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x03, 0xFE, 0x00, 0x04, 0x07, 0xFC, 0x03, + 0xFF, 0xC0, 0x07, 0xC1, 0xFF, 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC1, 0xFF, 0xF0, 0x07, 0xFF, + 0xFF, 0xE1, 0xFF, 0xF0, 0xC7, 0xFF, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xC1, + 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, + 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, +}; diff --git a/src/activities/ActivityManager.cpp b/src/activities/ActivityManager.cpp index 3cf115b1..eaa153b4 100644 --- a/src/activities/ActivityManager.cpp +++ b/src/activities/ActivityManager.cpp @@ -12,6 +12,7 @@ #include "reader/ReaderActivity.h" #include "settings/SettingsActivity.h" #include "util/FullScreenMessageActivity.h" +#include "weather/WeatherActivity.h" void ActivityManager::begin() { xTaskCreate(&renderTaskTrampoline, "ActivityManagerRender", @@ -196,6 +197,8 @@ void ActivityManager::goToFullScreenMessage(std::string message, EpdFontFamily:: replaceActivity(std::make_unique(renderer, mappedInput, std::move(message), style)); } +void ActivityManager::goToWeather() { replaceActivity(std::make_unique(renderer, mappedInput)); } + void ActivityManager::goHome() { replaceActivity(std::make_unique(renderer, mappedInput)); } void ActivityManager::pushActivity(std::unique_ptr&& activity) { diff --git a/src/activities/ActivityManager.h b/src/activities/ActivityManager.h index bc24c42e..a38e2985 100644 --- a/src/activities/ActivityManager.h +++ b/src/activities/ActivityManager.h @@ -86,6 +86,7 @@ class ActivityManager { void goToSleep(); void goToBoot(); void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR); + void goToWeather(); void goHome(); // This will move current activity to stack instead of deleting it diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 810cb50b..d314ecba 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -20,7 +20,7 @@ #include "fontIds.h" int HomeActivity::getMenuItemCount() const { - int count = 4; // File Browser, Recents, File transfer, Settings + int count = 5; // File Browser, Recents, File transfer, Weather, Settings if (!recentBooks.empty()) { count += recentBooks.size(); } @@ -192,6 +192,7 @@ void HomeActivity::loop() { const int recentsIdx = idx++; const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1; const int fileTransferIdx = idx++; + const int weatherIdx = idx++; const int settingsIdx = idx; if (selectorIndex < recentBooks.size()) { @@ -202,6 +203,8 @@ void HomeActivity::loop() { onRecentsOpen(); } else if (menuSelectedIndex == opdsLibraryIdx) { onOpdsBrowserOpen(); + } else if (menuSelectedIndex == weatherIdx) { + onWeatherOpen(); } else if (menuSelectedIndex == fileTransferIdx) { onFileTransferOpen(); } else if (menuSelectedIndex == settingsIdx) { @@ -226,11 +229,11 @@ void HomeActivity::render(RenderLock&&) { // Build menu items dynamically std::vector menuItems = {tr(STR_BROWSE_FILES), tr(STR_MENU_RECENT_BOOKS), tr(STR_FILE_TRANSFER), - tr(STR_SETTINGS_TITLE)}; - std::vector menuIcons = {Folder, Recent, Transfer, Settings}; + tr(STR_WEATHER), tr(STR_SETTINGS_TITLE)}; + std::vector menuIcons = {Folder, Recent, Library, Transfer, Settings}; if (hasOpdsUrl) { - // Insert OPDS Browser after File Browser + // Insert OPDS Browser after Recents (before File Transfer) menuItems.insert(menuItems.begin() + 2, tr(STR_OPDS_BROWSER)); menuIcons.insert(menuIcons.begin() + 2, Library); } @@ -269,3 +272,5 @@ void HomeActivity::onSettingsOpen() { activityManager.goToSettings(); } void HomeActivity::onFileTransferOpen() { activityManager.goToFileTransfer(); } void HomeActivity::onOpdsBrowserOpen() { activityManager.goToBrowser(); } + +void HomeActivity::onWeatherOpen() { activityManager.goToWeather(); } diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index e2b24bea..32d1c075 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -26,6 +26,7 @@ class HomeActivity final : public Activity { void onSettingsOpen(); void onFileTransferOpen(); void onOpdsBrowserOpen(); + void onWeatherOpen(); int getMenuItemCount() const; bool storeCoverBuffer(); // Store frame buffer for cover image diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index e5824a89..8fc98da3 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -14,6 +14,7 @@ #include "SettingsList.h" #include "StatusBarSettingsActivity.h" #include "activities/network/WifiSelectionActivity.h" +#include "activities/weather/WeatherSettingsActivity.h" #include "components/UITheme.h" #include "fontIds.h" @@ -52,6 +53,7 @@ void SettingsActivity::onEnter() { systemSettings.push_back(SettingInfo::Action(StrId::STR_CLEAR_READING_CACHE, SettingAction::ClearCache)); systemSettings.push_back(SettingInfo::Action(StrId::STR_CHECK_UPDATES, SettingAction::CheckForUpdates)); systemSettings.push_back(SettingInfo::Action(StrId::STR_LANGUAGE, SettingAction::Language)); + systemSettings.push_back(SettingInfo::Action(StrId::STR_WEATHER_SETTINGS, SettingAction::Weather)); readerSettings.push_back(SettingInfo::Action(StrId::STR_CUSTOMISE_STATUS_BAR, SettingAction::CustomiseStatusBar)); // Reset selection to first category @@ -192,6 +194,9 @@ void SettingsActivity::toggleCurrentSetting() { case SettingAction::Language: startActivityForResult(std::make_unique(renderer, mappedInput), resultHandler); break; + case SettingAction::Weather: + startActivityForResult(std::make_unique(renderer, mappedInput), resultHandler); + break; case SettingAction::None: // Do nothing break; diff --git a/src/activities/settings/SettingsActivity.h b/src/activities/settings/SettingsActivity.h index 9d5778a9..1eb5877a 100644 --- a/src/activities/settings/SettingsActivity.h +++ b/src/activities/settings/SettingsActivity.h @@ -21,6 +21,7 @@ enum class SettingAction { ClearCache, CheckForUpdates, Language, + Weather, }; struct SettingInfo { diff --git a/src/activities/weather/WeatherActivity.cpp b/src/activities/weather/WeatherActivity.cpp index 74710f39..d5183b36 100644 --- a/src/activities/weather/WeatherActivity.cpp +++ b/src/activities/weather/WeatherActivity.cpp @@ -9,9 +9,11 @@ #include #include +#include #include #include #include +#include #include "MappedInputManager.h" #include "WeatherSettingsActivity.h" @@ -19,6 +21,153 @@ #include "components/UITheme.h" #include "fontIds.h" +namespace { +inline bool getBitmapBit(const uint8_t* bitmap, const int size, const int x, const int y) { + const int rowBytes = size / 8; + const int idx = y * rowBytes + (x / 8); + const uint8_t mask = static_cast(0x80 >> (x % 8)); + return (bitmap[idx] & mask) != 0; +} + +inline void setBitmapBit(uint8_t* bitmap, const int size, const int x, const int y, const bool value) { + const int rowBytes = size / 8; + const int idx = y * rowBytes + (x / 8); + const uint8_t mask = static_cast(0x80 >> (x % 8)); + if (value) { + bitmap[idx] |= mask; + } else { + bitmap[idx] &= static_cast(~mask); + } +} + +void drawWeatherIconWithOrientation(const GfxRenderer& renderer, const uint8_t* icon, const int x, const int y, + const int size) { + // Weather activity uses LandscapeClockwise; rotate icon pixels 90deg so + // directional glyphs align with the landscape-oriented UI. + if (renderer.getOrientation() != GfxRenderer::Orientation::LandscapeClockwise) { + renderer.drawImage(icon, x, y, size, size); + return; + } + + constexpr int kLargeIconBytes = (WEATHER_ICON_LARGE * WEATHER_ICON_LARGE) / 8; + std::array rotatedRaw{}; + rotatedRaw.fill(0xFF); + + for (int srcY = 0; srcY < size; srcY++) { + for (int srcX = 0; srcX < size; srcX++) { + // 90deg counter-clockwise: (x, y) -> (y, size - 1 - x) + const int dstX = srcY; + const int dstY = size - 1 - srcX; + setBitmapBit(rotatedRaw.data(), size, dstX, dstY, getBitmapBit(icon, size, srcX, srcY)); + } + } + + // Re-center rotated icon by its painted-pixel bounds to avoid asymmetric + // source padding appearing as left-side clipping after rotation. + int minX = size, minY = size, maxX = -1, maxY = -1; + for (int py = 0; py < size; py++) { + for (int px = 0; px < size; px++) { + if (!getBitmapBit(rotatedRaw.data(), size, px, py)) { + minX = std::min(minX, px); + minY = std::min(minY, py); + maxX = std::max(maxX, px); + maxY = std::max(maxY, py); + } + } + } + + if (maxX < minX || maxY < minY) { + renderer.drawImage(rotatedRaw.data(), x, y, size, size); + return; + } + + const int bboxW = maxX - minX + 1; + const int bboxH = maxY - minY + 1; + const int targetMinX = (size - bboxW) / 2; + const int targetMinY = (size - bboxH) / 2; + const int shiftX = targetMinX - minX; + const int shiftY = targetMinY - minY; + + std::array rotatedCentered{}; + rotatedCentered.fill(0xFF); + for (int py = 0; py < size; py++) { + for (int px = 0; px < size; px++) { + const int dstX = px + shiftX; + const int dstY = py + shiftY; + if (dstX < 0 || dstX >= size || dstY < 0 || dstY >= size) { + continue; + } + setBitmapBit(rotatedCentered.data(), size, dstX, dstY, getBitmapBit(rotatedRaw.data(), size, px, py)); + } + } + + renderer.drawImage(rotatedCentered.data(), x, y, size, size); +} + +StrId getWeatherDescriptionStrId(const int wmoCode) { + switch (wmoCode) { + case 0: + return StrId::STR_WEATHER_DESC_CLEAR_SKY; + case 1: + return StrId::STR_WEATHER_DESC_MAINLY_CLEAR; + case 2: + return StrId::STR_WEATHER_DESC_PARTLY_CLOUDY; + case 3: + return StrId::STR_WEATHER_DESC_OVERCAST; + case 45: + return StrId::STR_WEATHER_DESC_FOG; + case 48: + return StrId::STR_WEATHER_DESC_RIME_FOG; + case 51: + return StrId::STR_WEATHER_DESC_LIGHT_DRIZZLE; + case 53: + return StrId::STR_WEATHER_DESC_DRIZZLE; + case 55: + return StrId::STR_WEATHER_DESC_DENSE_DRIZZLE; + case 56: + return StrId::STR_WEATHER_DESC_FREEZING_DRIZZLE; + case 57: + return StrId::STR_WEATHER_DESC_DENSE_FREEZING_DRIZZLE; + case 61: + return StrId::STR_WEATHER_DESC_SLIGHT_RAIN; + case 63: + return StrId::STR_WEATHER_DESC_MODERATE_RAIN; + case 65: + return StrId::STR_WEATHER_DESC_HEAVY_RAIN; + case 66: + return StrId::STR_WEATHER_DESC_FREEZING_RAIN; + case 67: + return StrId::STR_WEATHER_DESC_HEAVY_FREEZING_RAIN; + case 71: + return StrId::STR_WEATHER_DESC_SLIGHT_SNOW; + case 73: + return StrId::STR_WEATHER_DESC_MODERATE_SNOW; + case 75: + return StrId::STR_WEATHER_DESC_HEAVY_SNOW; + case 77: + return StrId::STR_WEATHER_DESC_SNOW_GRAINS; + case 80: + return StrId::STR_WEATHER_DESC_SLIGHT_SHOWERS; + case 81: + return StrId::STR_WEATHER_DESC_MODERATE_SHOWERS; + case 82: + return StrId::STR_WEATHER_DESC_VIOLENT_SHOWERS; + case 85: + return StrId::STR_WEATHER_DESC_SNOW_SHOWERS; + case 86: + return StrId::STR_WEATHER_DESC_HEAVY_SNOW_SHOWERS; + case 95: + return StrId::STR_WEATHER_DESC_THUNDERSTORM; + case 96: + return StrId::STR_WEATHER_DESC_THUNDERSTORM_HAIL; + case 99: + return StrId::STR_WEATHER_DESC_THUNDERSTORM_HEAVY_HAIL; + default: + return StrId::STR_WEATHER_DESC_UNKNOWN; + } +} +} // namespace + void WeatherActivity::onEnter() { Activity::onEnter(); @@ -28,6 +177,7 @@ void WeatherActivity::onEnter() { state = State::LOADING_CACHE; errorMessage.clear(); forceRefresh = false; + showRefreshPopup = false; requestUpdate(); loadAndDisplay(); @@ -53,13 +203,30 @@ void WeatherActivity::loadAndDisplay() { // Try cache first WeatherData cached = WeatherClient::getWeather(WEATHER_SETTINGS, forceRefresh); if (cached.valid) { + const bool shouldRefresh = forceRefresh; + if (!shouldRefresh) { + const time_t now = time(nullptr); + const long age = (cached.fetchedAt > 0) ? static_cast(now - cached.fetchedAt) : -1; + if (age >= 0 && age > 30L * 60L) { + LOG_DBG("WEA", "Displayed stale cache (age=%ld s); scheduling refresh", age); + } else { + LOG_DBG("WEA", "Displayed cache (age=%ld s)", age); + } + } + weatherData = std::move(cached); state = State::WEATHER_DISPLAY; requestUpdate(); + + // After first paint, refresh stale/non-forced data via WiFi-gated path. + if (!forceRefresh) { + checkAndConnectWifi(); + } return; } // Need fresh data - check WiFi + LOG_DBG("WEA", "No cache to display; proceeding to WiFi/network fetch path"); checkAndConnectWifi(); } @@ -85,11 +252,13 @@ void WeatherActivity::launchWifiSelection() { void WeatherActivity::onWifiSelectionComplete(bool connected) { // Re-apply landscape after returning from WiFi selection (which uses portrait) renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + LOG_DBG("WEA", "onWifiSelectionComplete connected=%d wifiStatus=%d", connected ? 1 : 0, (int)WiFi.status()); if (connected) { - state = State::FETCHING; + state = State::CHECK_WIFI; + wifiWaitStartedAtMs = millis(); + LOG_DBG("WEA", "state -> CHECK_WIFI (waitStart=%lu)", (unsigned long)wifiWaitStartedAtMs); requestUpdate(true); - fetchWeather(); } else { WiFi.disconnect(); WiFi.mode(WIFI_OFF); @@ -100,13 +269,26 @@ void WeatherActivity::onWifiSelectionComplete(bool connected) { } void WeatherActivity::fetchWeather() { + LOG_DBG("WEA", "fetchWeather[1] enter status=%d ip=%s", (int)WiFi.status(), WiFi.localIP().toString().c_str()); + if (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0)) { + LOG_ERR("WEA", "fetchWeather[2] WiFi not ready status=%d ip=%s", (int)WiFi.status(), + WiFi.localIP().toString().c_str()); + state = State::ERROR; + errorMessage = tr(STR_WIFI_CONN_FAILED); + requestUpdate(); + return; + } + + LOG_DBG("WEA", "fetchWeather[3] WeatherClient::getWeather before"); weatherData = WeatherClient::getWeather(WEATHER_SETTINGS, true); + LOG_DBG("WEA", "fetchWeather[4] WeatherClient::getWeather after valid=%d", weatherData.valid ? 1 : 0); if (weatherData.valid) { state = State::WEATHER_DISPLAY; } else { state = State::ERROR; errorMessage = weatherData.errorMessage.empty() ? tr(STR_WEATHER_FETCH_FAILED) : weatherData.errorMessage.c_str(); } + showRefreshPopup = false; requestUpdate(); } @@ -117,7 +299,16 @@ void WeatherActivity::loop() { if (state == State::ERROR) { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { - // Retry + // Open weather settings (useful when no location is configured) + startActivityForResult(std::make_unique(renderer, mappedInput), + [this](const ActivityResult&) { + renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); + forceRefresh = true; + loadAndDisplay(); + }); + } else if (mappedInput.wasReleased(MappedInputManager::Button::Left) || + mappedInput.wasReleased(MappedInputManager::Button::Right)) { + // Retry fetch if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { state = State::FETCHING; requestUpdate(true); @@ -132,6 +323,31 @@ void WeatherActivity::loop() { } if (state == State::LOADING_CACHE || state == State::CHECK_WIFI || state == State::FETCHING) { + if (state == State::CHECK_WIFI) { + LOG_DBG("WEA", "loop CHECK_WIFI status=%d ip=%s elapsed=%lu", (int)WiFi.status(), + WiFi.localIP().toString().c_str(), (unsigned long)(millis() - wifiWaitStartedAtMs)); + if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { + // Give lwIP/sockets a short window to settle after activity handoff. + if (wifiWaitStartedAtMs > 0 && millis() - wifiWaitStartedAtMs < 1200) { + LOG_DBG("WEA", "loop CHECK_WIFI waiting for settle window"); + return; + } + LOG_DBG("WEA", "state CHECK_WIFI -> FETCHING"); + state = State::FETCHING; + requestUpdate(true); + fetchWeather(); + return; + } + + if (wifiWaitStartedAtMs > 0 && millis() - wifiWaitStartedAtMs > 15000) { + LOG_ERR("WEA", "CHECK_WIFI timeout after %lu ms", (unsigned long)(millis() - wifiWaitStartedAtMs)); + state = State::ERROR; + errorMessage = tr(STR_WIFI_CONN_FAILED); + requestUpdate(); + return; + } + } + if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { onGoHome(); } @@ -153,9 +369,15 @@ void WeatherActivity::loop() { }); } else if (mappedInput.wasReleased(MappedInputManager::Button::Left) || mappedInput.wasReleased(MappedInputManager::Button::Right)) { - // Manual refresh - forceRefresh = true; - loadAndDisplay(); + // Manual refresh with popup feedback. + showRefreshPopup = true; + if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { + state = State::FETCHING; + requestUpdate(true); + fetchWeather(); + } else { + launchWifiSelection(); + } } } @@ -165,7 +387,7 @@ void WeatherActivity::render(RenderLock&&) { const auto pageWidth = renderer.getScreenWidth(); // 800 in landscape const auto pageHeight = renderer.getScreenHeight(); // 480 in landscape - if (state == State::LOADING_CACHE || state == State::FETCHING) { + if (state == State::LOADING_CACHE || (state == State::FETCHING && (!showRefreshPopup || !weatherData.valid))) { renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2, tr(STR_LOADING)); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); @@ -176,7 +398,7 @@ void WeatherActivity::render(RenderLock&&) { if (state == State::ERROR) { renderer.drawCenteredText(UI_12_FONT_ID, pageHeight / 2 - 20, tr(STR_ERROR_MSG)); renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 10, errorMessage.c_str()); - const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_RETRY), "", ""); + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_WEATHER_SETTINGS), tr(STR_WEATHER_REFRESH), ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); return; @@ -197,30 +419,34 @@ void WeatherActivity::render(RenderLock&&) { // | [Back] [Settings] [Refresh] Button hints | // +--------------------------------------------------------+ + const auto& metrics = UITheme::getInstance().getMetrics(); + // In LandscapeClockwise, physical bottom buttons are at the logical LEFT edge + const int contentX = metrics.buttonHintsHeight; + const int contentWidth = pageWidth - contentX; constexpr int leftPanelWidth = 220; - constexpr int buttonHintsHeight = 30; - constexpr int graphHeight = 190; - const int topSectionHeight = pageHeight - graphHeight - buttonHintsHeight; + constexpr int graphHeight = 240; + const int topSectionHeight = pageHeight - graphHeight; - // Draw current conditions (left panel) - renderCurrentConditions(0, 0, leftPanelWidth, topSectionHeight); + // Draw current conditions (left panel, offset by button hints) + renderCurrentConditions(contentX, 0, leftPanelWidth, topSectionHeight); // Draw daily forecast (right of current conditions) - renderDailyForecast(leftPanelWidth, 0, pageWidth - leftPanelWidth, topSectionHeight); + renderDailyForecast(contentX + leftPanelWidth, 0, contentWidth - leftPanelWidth, topSectionHeight); // Separator line - renderer.drawLine(0, topSectionHeight, pageWidth, topSectionHeight); + renderer.drawLine(contentX, topSectionHeight, pageWidth, topSectionHeight); // Draw hourly graph - renderHourlyGraph(0, topSectionHeight + 1, pageWidth, graphHeight - 1); - - // Separator line above button hints - renderer.drawLine(0, pageHeight - buttonHintsHeight, pageWidth, pageHeight - buttonHintsHeight); + renderHourlyGraph(contentX, topSectionHeight + 1, contentWidth, graphHeight - 1); // Button hints - const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_WEATHER_SETTINGS), tr(STR_WEATHER_REFRESH), ""); + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_WEATHER_SETTINGS_SHORT), tr(STR_WEATHER_REFRESH), ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + if (state == State::FETCHING && showRefreshPopup) { + GUI.drawPopup(renderer, tr(STR_LOADING_POPUP)); + } + renderer.displayBuffer(); } @@ -251,27 +477,28 @@ void WeatherActivity::renderCurrentConditions(int x, int y, int w, int h) { auto iconType = getWeatherIconType(cur.weatherCode, cur.isDay); const uint8_t* icon = getWeatherIconLarge(iconType); int iconX = x + (w - WEATHER_ICON_LARGE) / 2; - renderer.drawImage(icon, iconX, textY, WEATHER_ICON_LARGE, WEATHER_ICON_LARGE); + drawWeatherIconWithOrientation(renderer, icon, iconX, textY, WEATHER_ICON_LARGE); textY += WEATHER_ICON_LARGE + 5; // Temperature (large) char tempBuf[16]; const char* unitSuffix = WEATHER_SETTINGS.getTempUnit() == WeatherTempUnit::CELSIUS ? "C" : "F"; snprintf(tempBuf, sizeof(tempBuf), "%.1f %s", cur.temperature, unitSuffix); - renderer.drawCenteredText(UI_12_FONT_ID, textY, tempBuf, true, EpdFontFamily::BOLD); + int tempWidth = renderer.getTextWidth(UI_12_FONT_ID, tempBuf, EpdFontFamily::BOLD); + renderer.drawText(UI_12_FONT_ID, x + (w - tempWidth) / 2, textY, tempBuf, true, EpdFontFamily::BOLD); textY += 22; // Feels like snprintf(tempBuf, sizeof(tempBuf), "%.1f %s", cur.apparentTemperature, unitSuffix); char feelsLikeBuf[48]; - snprintf(feelsLikeBuf, sizeof(feelsLikeBuf), "%s %s", tr(STR_WEATHER_FEELS_LIKE), tempBuf); + snprintf(feelsLikeBuf, sizeof(feelsLikeBuf), "%s: %s", tr(STR_WEATHER_FEELS_LIKE), tempBuf); auto feelsText = renderer.truncatedText(SMALL_FONT_ID, feelsLikeBuf, w - 10); int feelsWidth = renderer.getTextWidth(SMALL_FONT_ID, feelsText.c_str()); renderer.drawText(SMALL_FONT_ID, x + (w - feelsWidth) / 2, textY, feelsText.c_str()); textY += 16; // Weather description - const char* desc = getWeatherDescription(cur.weatherCode); + const char* desc = I18N.get(getWeatherDescriptionStrId(cur.weatherCode)); int descWidth = renderer.getTextWidth(SMALL_FONT_ID, desc); renderer.drawText(SMALL_FONT_ID, x + (w - descWidth) / 2, textY, desc); textY += 20; @@ -279,27 +506,36 @@ void WeatherActivity::renderCurrentConditions(int x, int y, int w, int h) { // Details grid const char* windDir = getWindDirectionText(cur.windDirection); char detailBuf[64]; + const auto drawCenteredDetail = [&](const char* text) { + auto truncated = renderer.truncatedText(SMALL_FONT_ID, text, w - 10); + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, truncated.c_str()); + renderer.drawText(SMALL_FONT_ID, x + (w - textWidth) / 2, textY, truncated.c_str()); + }; // Wind snprintf(detailBuf, sizeof(detailBuf), "%s: %.0f %s %s", tr(STR_WEATHER_WIND), cur.windSpeed, WEATHER_SETTINGS.getWindUnitParam(), windDir); - renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + drawCenteredDetail(detailBuf); textY += 14; // Humidity snprintf(detailBuf, sizeof(detailBuf), "%s: %d%%", tr(STR_WEATHER_HUMIDITY), cur.humidity); - renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + drawCenteredDetail(detailBuf); textY += 14; // Precipitation - snprintf(detailBuf, sizeof(detailBuf), "%s: %.1f %s", tr(STR_WEATHER_PRECIP), cur.precipitation, - WEATHER_SETTINGS.getPrecipUnitParam()); - renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + if (std::fabs(cur.precipitation) < 0.0001f) { + snprintf(detailBuf, sizeof(detailBuf), "%s: --", tr(STR_WEATHER_PRECIP)); + } else { + snprintf(detailBuf, sizeof(detailBuf), "%s: %.1f %s", tr(STR_WEATHER_PRECIP), cur.precipitation, + WEATHER_SETTINGS.getPrecipUnitParam()); + } + drawCenteredDetail(detailBuf); textY += 14; // UV Index snprintf(detailBuf, sizeof(detailBuf), "UV: %.1f", cur.uvIndex); - renderer.drawText(SMALL_FONT_ID, x + 5, textY, detailBuf); + drawCenteredDetail(detailBuf); // Vertical separator renderer.drawLine(x + w, y, x + w, y + h); @@ -308,63 +544,113 @@ void WeatherActivity::renderCurrentConditions(int x, int y, int w, int h) { void WeatherActivity::renderDailyForecast(int x, int y, int w, int h) { if (weatherData.daily.empty()) return; + // Localized weekday names (indexed by tm_wday: 0=Sun..6=Sat) + const StrId dayNameIds[] = {StrId::STR_WEATHER_DAY_SUN, StrId::STR_WEATHER_DAY_MON, StrId::STR_WEATHER_DAY_TUE, + StrId::STR_WEATHER_DAY_WED, StrId::STR_WEATHER_DAY_THU, StrId::STR_WEATHER_DAY_FRI, + StrId::STR_WEATHER_DAY_SAT}; + + const char* unitSuffix = WEATHER_SETTINGS.getTempUnit() == WeatherTempUnit::CELSIUS ? "C" : "F"; int numDays = static_cast(weatherData.daily.size()); - int cardWidth = w / numDays; + + if (numDays <= 0) return; + + // Derive weekday progression from the first forecast day to avoid timezone + // conversion edge cases causing repeated day labels. + struct tm firstDayInfo; + time_t firstLocalDate = weatherData.daily[0].date + weatherData.utcOffsetSeconds; + gmtime_r(&firstLocalDate, &firstDayInfo); + const int firstWeekday = firstDayInfo.tm_wday; + + // Keep all day panels evenly split (distribute remainder 1px left-to-right) + const int baseDayWidth = w / numDays; + const int extraPx = w % numDays; + + int cardX = x; for (int i = 0; i < numDays; i++) { const auto& day = weatherData.daily[i]; - int cardX = x + i * cardWidth; + int cardWidth = baseDayWidth + (i < extraPx ? 1 : 0); int textY = y + 10; - // Day name + // Day name (localized) struct tm timeinfo; time_t localDate = day.date + weatherData.utcOffsetSeconds; gmtime_r(&localDate, &timeinfo); - char dayName[16]; - // Manual short day names to avoid locale issues on ESP32 - const char* dayNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - snprintf(dayName, sizeof(dayName), "%s", dayNames[timeinfo.tm_wday]); + const int weekdayIndex = (firstWeekday + i) % 7; + const char* dayName = I18N.get(dayNameIds[weekdayIndex]); - int dayNameWidth = renderer.getTextWidth(SMALL_FONT_ID, dayName, EpdFontFamily::BOLD); - renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - dayNameWidth) / 2, textY, dayName, true, EpdFontFamily::BOLD); + int dayNameWidth = renderer.getTextWidth(UI_10_FONT_ID, dayName, EpdFontFamily::BOLD); + renderer.drawText(UI_10_FONT_ID, cardX + (cardWidth - dayNameWidth) / 2, textY, dayName, true, EpdFontFamily::BOLD); + textY += 20; + + // Date (e.g. "Apr 3") - for all days + char dateBuf[16]; + const char* monthNames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + snprintf(dateBuf, sizeof(dateBuf), "%s %d", monthNames[timeinfo.tm_mon], timeinfo.tm_mday); + int dateWidth = renderer.getTextWidth(SMALL_FONT_ID, dateBuf); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - dateWidth) / 2, textY, dateBuf); textY += 18; - // Weather icon (24x24) - // Use isDay=true for daily forecasts + // Weather icon (48x48 for all days) auto iconType = getWeatherIconType(day.weatherCode, true); - const uint8_t* icon = getWeatherIconSmall(iconType); - int iconX = cardX + (cardWidth - WEATHER_ICON_SMALL) / 2; - renderer.drawImage(icon, iconX, textY, WEATHER_ICON_SMALL, WEATHER_ICON_SMALL); - textY += WEATHER_ICON_SMALL + 5; + const uint8_t* icon = getWeatherIconLarge(iconType); + int iconX = cardX + (cardWidth - WEATHER_ICON_LARGE) / 2; + drawWeatherIconWithOrientation(renderer, icon, iconX, textY, WEATHER_ICON_LARGE); + textY += WEATHER_ICON_LARGE + 8; - // High / Low temp - char tempBuf[24]; - snprintf(tempBuf, sizeof(tempBuf), "%.0f/%.0f", day.tempMax, day.tempMin); - int tempWidth = renderer.getTextWidth(SMALL_FONT_ID, tempBuf); - renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - tempWidth) / 2, textY, tempBuf); - textY += 15; + // Weather description (short) + const char* desc = I18N.get(getWeatherDescriptionStrId(day.weatherCode)); + auto truncDesc = renderer.truncatedText(SMALL_FONT_ID, desc, cardWidth - 8); + int descWidth = renderer.getTextWidth(SMALL_FONT_ID, truncDesc.c_str()); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - descWidth) / 2, textY, truncDesc.c_str()); + textY += 16; + + // High / Low temp with unit + char tempBuf[32]; + snprintf(tempBuf, sizeof(tempBuf), "%.0f / %.0f %s", day.tempMax, day.tempMin, unitSuffix); + int tempWidth = renderer.getTextWidth(SMALL_FONT_ID, tempBuf, EpdFontFamily::BOLD); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - tempWidth) / 2, textY, tempBuf, true, EpdFontFamily::BOLD); + textY += 16; // Precipitation - if (day.precipSum > 0) { - char precipBuf[16]; - snprintf(precipBuf, sizeof(precipBuf), "%.1f%s", day.precipSum, WEATHER_SETTINGS.getPrecipUnitParam()); - int precipWidth = renderer.getTextWidth(SMALL_FONT_ID, precipBuf); - renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - precipWidth) / 2, textY, precipBuf); + char precipBuf[24]; + if (std::fabs(day.precipSum) < 0.0001f) { + snprintf(precipBuf, sizeof(precipBuf), "%s: --", tr(STR_WEATHER_PRECIP)); + } else { + snprintf(precipBuf, sizeof(precipBuf), "%s: %.1f %s", tr(STR_WEATHER_PRECIP), day.precipSum, + WEATHER_SETTINGS.getPrecipUnitParam()); } + auto truncPrecip = renderer.truncatedText(SMALL_FONT_ID, precipBuf, cardWidth - 8); + int precipWidth = renderer.getTextWidth(SMALL_FONT_ID, truncPrecip.c_str()); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - precipWidth) / 2, textY, truncPrecip.c_str()); + textY += 16; + + // UV Index + char uvBuf[16]; + snprintf(uvBuf, sizeof(uvBuf), "UV: %.0f", day.uvIndexMax); + int uvWidth = renderer.getTextWidth(SMALL_FONT_ID, uvBuf); + renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - uvWidth) / 2, textY, uvBuf); // Card separator if (i < numDays - 1) { renderer.drawLine(cardX + cardWidth, y, cardX + cardWidth, y + h); } + + cardX += cardWidth; } } void WeatherActivity::renderHourlyGraph(int x, int y, int w, int h) { if (weatherData.hourly.empty()) return; - constexpr int leftMargin = 40; // Space for Y-axis labels + // Localized weekday names (indexed by tm_wday: 0=Sun..6=Sat) + const StrId dayNameIds[] = {StrId::STR_WEATHER_DAY_SUN, StrId::STR_WEATHER_DAY_MON, StrId::STR_WEATHER_DAY_TUE, + StrId::STR_WEATHER_DAY_WED, StrId::STR_WEATHER_DAY_THU, StrId::STR_WEATHER_DAY_FRI, + StrId::STR_WEATHER_DAY_SAT}; + + constexpr int leftMargin = 56; // Space for Y-axis labels with unit suffix constexpr int rightMargin = 10; - constexpr int topMargin = 15; + constexpr int topMargin = 28; // Keep heading clear of top temperature label constexpr int bottomMargin = 25; // Space for X-axis labels constexpr int precipBarMaxHeight = 30; @@ -377,6 +663,23 @@ void WeatherActivity::renderHourlyGraph(int x, int y, int w, int h) { size_t numPoints = weatherData.hourly.size(); if (numPoints < 2) return; + // Find day boundaries in hourly data (start index of each day). + std::vector dayStartIndices; + dayStartIndices.reserve(8); + dayStartIndices.push_back(0); + struct tm prevDayInfo; + time_t prevLocalTime = weatherData.hourly[0].time + weatherData.utcOffsetSeconds; + gmtime_r(&prevLocalTime, &prevDayInfo); + for (size_t i = 1; i < numPoints; i++) { + struct tm curDayInfo; + time_t localTime = weatherData.hourly[i].time + weatherData.utcOffsetSeconds; + gmtime_r(&localTime, &curDayInfo); + if (curDayInfo.tm_year != prevDayInfo.tm_year || curDayInfo.tm_yday != prevDayInfo.tm_yday) { + dayStartIndices.push_back(i); + prevDayInfo = curDayInfo; + } + } + // Find temperature range float tempMin = weatherData.hourly[0].temperature; float tempMax = tempMin; @@ -399,16 +702,40 @@ void WeatherActivity::renderHourlyGraph(int x, int y, int w, int h) { tempMax += 1.0f; tempRange = tempMax - tempMin; - // Draw graph title - renderer.drawText(SMALL_FONT_ID, x + 5, y + 3, tr(STR_WEATHER_48H_FORECAST), true, EpdFontFamily::BOLD); + // Draw graph title centered over the graph area. + const char* graphTitle = tr(STR_WEATHER_48H_FORECAST); + const int graphTitleWidth = renderer.getTextWidth(SMALL_FONT_ID, graphTitle, EpdFontFamily::BOLD); + const int graphTitleX = graphX + (graphW - graphTitleWidth) / 2; + renderer.drawText(SMALL_FONT_ID, graphTitleX, y + 3, graphTitle, true, EpdFontFamily::BOLD); + + // Draw weekday labels above the chart area at each day start. + for (size_t startIdx : dayStartIndices) { + struct tm dayInfo; + time_t localTime = weatherData.hourly[startIdx].time + weatherData.utcOffsetSeconds; + gmtime_r(&localTime, &dayInfo); + + const char* dayName = I18N.get(dayNameIds[dayInfo.tm_wday]); + const int dayLabelWidth = renderer.getTextWidth(SMALL_FONT_ID, dayName, EpdFontFamily::BOLD); + const int px = graphX + static_cast(startIdx * graphW / (numPoints - 1)); + int labelX = px + 2; + const int maxLabelX = graphX + graphW - dayLabelWidth - 2; + if (labelX > maxLabelX) { + labelX = maxLabelX; + } + if (labelX < graphX + 2) { + labelX = graphX + 2; + } + renderer.drawText(SMALL_FONT_ID, labelX, y + 14, dayName, true, EpdFontFamily::BOLD); + } // Draw Y-axis labels (temperature) float tempStep = tempRange / 4.0f; + const char* unitSuffix = WEATHER_SETTINGS.getTempUnit() == WeatherTempUnit::CELSIUS ? "C" : "F"; for (int i = 0; i <= 4; i++) { float temp = tempMax - i * tempStep; int labelY = graphY + i * graphH / 4; - char label[8]; - snprintf(label, sizeof(label), "%.0f", temp); + char label[16]; + snprintf(label, sizeof(label), "%.0f %s", temp, unitSuffix); renderer.drawText(SMALL_FONT_ID, x + 2, labelY - 4, label); // Grid line (dashed effect using short segments) for (int gx = graphX; gx < graphX + graphW; gx += 8) { @@ -444,6 +771,13 @@ void WeatherActivity::renderHourlyGraph(int x, int y, int w, int h) { } } + // Draw day separator lines for day boundaries after the first day. + for (size_t boundaryIdx = 1; boundaryIdx < dayStartIndices.size(); boundaryIdx++) { + const size_t i = dayStartIndices[boundaryIdx]; + const int px = graphX + static_cast(i * graphW / (numPoints - 1)); + renderer.drawLine(px, graphY, px, precipY + precipBarMaxHeight); + } + // Draw X-axis labels (every 6 hours) for (size_t i = 0; i < numPoints; i += 6) { int px = graphX + static_cast(i * graphW / (numPoints - 1)); diff --git a/src/activities/weather/WeatherActivity.h b/src/activities/weather/WeatherActivity.h index 6768d106..e28f51e1 100644 --- a/src/activities/weather/WeatherActivity.h +++ b/src/activities/weather/WeatherActivity.h @@ -35,6 +35,8 @@ class WeatherActivity final : public Activity { WeatherData weatherData; std::string errorMessage; bool forceRefresh = false; + bool showRefreshPopup = false; + uint32_t wifiWaitStartedAtMs = 0; void loadAndDisplay(); void checkAndConnectWifi(); diff --git a/src/main.cpp b/src/main.cpp index 75bf69c5..45545104 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include "KOReaderCredentialStore.h" #include "MappedInputManager.h" #include "RecentBooksStore.h" +#include "WeatherSettingsStore.h" #include "activities/Activity.h" #include "activities/ActivityManager.h" #include "components/UITheme.h" @@ -260,6 +261,7 @@ void setup() { SETTINGS.loadFromFile(); I18N.loadSettings(); KOREADER_STORE.loadFromFile(); + WEATHER_SETTINGS.loadFromFile(); UITheme::getInstance().reload(); ButtonNavigator::setMappedInputManager(mappedInputManager); diff --git a/src/network/HttpDownloader.cpp b/src/network/HttpDownloader.cpp index 799b8874..c1a3ad71 100644 --- a/src/network/HttpDownloader.cpp +++ b/src/network/HttpDownloader.cpp @@ -55,37 +55,52 @@ class FileWriteStream final : public Stream { bool HttpDownloader::fetchUrl(const std::string& url, Stream& outContent) { // Use NetworkClientSecure for HTTPS, regular NetworkClient for HTTP std::unique_ptr client; + LOG_DBG("HTTP", "fetchUrl[1] start (https=%d)", UrlUtils::isHttpsUrl(url) ? 1 : 0); if (UrlUtils::isHttpsUrl(url)) { auto* secureClient = new NetworkClientSecure(); + LOG_DBG("HTTP", "fetchUrl[2] created NetworkClientSecure"); secureClient->setInsecure(); + LOG_DBG("HTTP", "fetchUrl[3] setInsecure done"); client.reset(secureClient); } else { client.reset(new NetworkClient()); + LOG_DBG("HTTP", "fetchUrl[2] created NetworkClient"); } HTTPClient http; LOG_DBG("HTTP", "Fetching: %s", url.c_str()); + LOG_DBG("HTTP", "fetchUrl[4] begin() before"); http.begin(*client, url.c_str()); + LOG_DBG("HTTP", "fetchUrl[5] begin() after"); http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + LOG_DBG("HTTP", "fetchUrl[6] redirects configured"); http.addHeader("User-Agent", "CrossPoint-ESP32-" CROSSPOINT_VERSION); + LOG_DBG("HTTP", "fetchUrl[7] user-agent header added"); // Add Basic HTTP auth if credentials are configured if (strlen(SETTINGS.opdsUsername) > 0 && strlen(SETTINGS.opdsPassword) > 0) { std::string credentials = std::string(SETTINGS.opdsUsername) + ":" + SETTINGS.opdsPassword; String encoded = base64::encode(credentials.c_str()); http.addHeader("Authorization", "Basic " + encoded); + LOG_DBG("HTTP", "fetchUrl[8] auth header added"); } + LOG_DBG("HTTP", "fetchUrl[9] GET() before"); const int httpCode = http.GET(); + LOG_DBG("HTTP", "fetchUrl[10] GET() after code=%d", httpCode); if (httpCode != HTTP_CODE_OK) { LOG_ERR("HTTP", "Fetch failed: %d", httpCode); + LOG_DBG("HTTP", "fetchUrl[11] end() after GET failure"); http.end(); return false; } + LOG_DBG("HTTP", "fetchUrl[12] writeToStream() before"); http.writeToStream(&outContent); + LOG_DBG("HTTP", "fetchUrl[13] writeToStream() after"); + LOG_DBG("HTTP", "fetchUrl[14] end() before success return"); http.end(); LOG_DBG("HTTP", "Fetch success"); @@ -105,30 +120,41 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string& ProgressCallback progress) { // Use NetworkClientSecure for HTTPS, regular NetworkClient for HTTP std::unique_ptr client; + LOG_DBG("HTTP", "downloadToFile[1] start (https=%d)", UrlUtils::isHttpsUrl(url) ? 1 : 0); if (UrlUtils::isHttpsUrl(url)) { auto* secureClient = new NetworkClientSecure(); + LOG_DBG("HTTP", "downloadToFile[2] created NetworkClientSecure"); secureClient->setInsecure(); + LOG_DBG("HTTP", "downloadToFile[3] setInsecure done"); client.reset(secureClient); } else { client.reset(new NetworkClient()); + LOG_DBG("HTTP", "downloadToFile[2] created NetworkClient"); } HTTPClient http; LOG_DBG("HTTP", "Downloading: %s", url.c_str()); LOG_DBG("HTTP", "Destination: %s", destPath.c_str()); + LOG_DBG("HTTP", "downloadToFile[4] begin() before"); http.begin(*client, url.c_str()); + LOG_DBG("HTTP", "downloadToFile[5] begin() after"); http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + LOG_DBG("HTTP", "downloadToFile[6] redirects configured"); http.addHeader("User-Agent", "CrossPoint-ESP32-" CROSSPOINT_VERSION); + LOG_DBG("HTTP", "downloadToFile[7] user-agent header added"); // Add Basic HTTP auth if credentials are configured if (strlen(SETTINGS.opdsUsername) > 0 && strlen(SETTINGS.opdsPassword) > 0) { std::string credentials = std::string(SETTINGS.opdsUsername) + ":" + SETTINGS.opdsPassword; String encoded = base64::encode(credentials.c_str()); http.addHeader("Authorization", "Basic " + encoded); + LOG_DBG("HTTP", "downloadToFile[8] auth header added"); } + LOG_DBG("HTTP", "downloadToFile[9] GET() before"); const int httpCode = http.GET(); + LOG_DBG("HTTP", "downloadToFile[10] GET() after code=%d", httpCode); if (httpCode != HTTP_CODE_OK) { LOG_ERR("HTTP", "Download failed: %d", httpCode); http.end(); @@ -158,10 +184,14 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string& // Let HTTPClient handle chunked decoding and stream body bytes into the file. FileWriteStream fileStream(file, contentLength, progress); + LOG_DBG("HTTP", "downloadToFile[11] writeToStream() before"); const int writeResult = http.writeToStream(&fileStream); + LOG_DBG("HTTP", "downloadToFile[12] writeToStream() after result=%d", writeResult); file.close(); + LOG_DBG("HTTP", "downloadToFile[13] file closed"); http.end(); + LOG_DBG("HTTP", "downloadToFile[14] http end done"); if (writeResult < 0) { LOG_ERR("HTTP", "writeToStream error: %d", writeResult); From 706eb0e8e7de9e0026b46c85cf0a38a73be2473f Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 2 Apr 2026 18:16:49 +0200 Subject: [PATCH 3/5] Improve look and feel --- assets/weather-icons/svg/wi-cloudy.svg | 18 + assets/weather-icons/svg/wi-day-cloudy.svg | 26 ++ assets/weather-icons/svg/wi-day-sunny.svg | 27 ++ assets/weather-icons/svg/wi-fog.svg | 18 + .../weather-icons/svg/wi-night-alt-cloudy.svg | 19 + assets/weather-icons/svg/wi-night-clear.svg | 13 + assets/weather-icons/svg/wi-rain.svg | 23 ++ assets/weather-icons/svg/wi-snow.svg | 27 ++ assets/weather-icons/svg/wi-sprinkle.svg | 20 + assets/weather-icons/svg/wi-thunderstorm.svg | 21 ++ lib/Weather/WeatherIcons48.h | 345 +++++++++--------- scripts/convert_icon.py | 127 +++++-- scripts/generate_weather_icons.py | 277 ++++++++++++++ src/activities/weather/WeatherActivity.cpp | 96 +---- 14 files changed, 774 insertions(+), 283 deletions(-) create mode 100644 assets/weather-icons/svg/wi-cloudy.svg create mode 100644 assets/weather-icons/svg/wi-day-cloudy.svg create mode 100644 assets/weather-icons/svg/wi-day-sunny.svg create mode 100644 assets/weather-icons/svg/wi-fog.svg create mode 100644 assets/weather-icons/svg/wi-night-alt-cloudy.svg create mode 100644 assets/weather-icons/svg/wi-night-clear.svg create mode 100644 assets/weather-icons/svg/wi-rain.svg create mode 100644 assets/weather-icons/svg/wi-snow.svg create mode 100644 assets/weather-icons/svg/wi-sprinkle.svg create mode 100644 assets/weather-icons/svg/wi-thunderstorm.svg create mode 100644 scripts/generate_weather_icons.py diff --git a/assets/weather-icons/svg/wi-cloudy.svg b/assets/weather-icons/svg/wi-cloudy.svg new file mode 100644 index 00000000..c3eaf41d --- /dev/null +++ b/assets/weather-icons/svg/wi-cloudy.svg @@ -0,0 +1,18 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-day-cloudy.svg b/assets/weather-icons/svg/wi-day-cloudy.svg new file mode 100644 index 00000000..cf63543a --- /dev/null +++ b/assets/weather-icons/svg/wi-day-cloudy.svg @@ -0,0 +1,26 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-day-sunny.svg b/assets/weather-icons/svg/wi-day-sunny.svg new file mode 100644 index 00000000..eafcd77d --- /dev/null +++ b/assets/weather-icons/svg/wi-day-sunny.svg @@ -0,0 +1,27 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-fog.svg b/assets/weather-icons/svg/wi-fog.svg new file mode 100644 index 00000000..cf06f8f8 --- /dev/null +++ b/assets/weather-icons/svg/wi-fog.svg @@ -0,0 +1,18 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-night-alt-cloudy.svg b/assets/weather-icons/svg/wi-night-alt-cloudy.svg new file mode 100644 index 00000000..fcd1496d --- /dev/null +++ b/assets/weather-icons/svg/wi-night-alt-cloudy.svg @@ -0,0 +1,19 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-night-clear.svg b/assets/weather-icons/svg/wi-night-clear.svg new file mode 100644 index 00000000..053b2de9 --- /dev/null +++ b/assets/weather-icons/svg/wi-night-clear.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-rain.svg b/assets/weather-icons/svg/wi-rain.svg new file mode 100644 index 00000000..7ecef3fe --- /dev/null +++ b/assets/weather-icons/svg/wi-rain.svg @@ -0,0 +1,23 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-snow.svg b/assets/weather-icons/svg/wi-snow.svg new file mode 100644 index 00000000..56afebf9 --- /dev/null +++ b/assets/weather-icons/svg/wi-snow.svg @@ -0,0 +1,27 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-sprinkle.svg b/assets/weather-icons/svg/wi-sprinkle.svg new file mode 100644 index 00000000..c26b2075 --- /dev/null +++ b/assets/weather-icons/svg/wi-sprinkle.svg @@ -0,0 +1,20 @@ + + + + + diff --git a/assets/weather-icons/svg/wi-thunderstorm.svg b/assets/weather-icons/svg/wi-thunderstorm.svg new file mode 100644 index 00000000..52555ec7 --- /dev/null +++ b/assets/weather-icons/svg/wi-thunderstorm.svg @@ -0,0 +1,21 @@ + + + + + diff --git a/lib/Weather/WeatherIcons48.h b/lib/Weather/WeatherIcons48.h index b92bc221..d7e5f349 100644 --- a/lib/Weather/WeatherIcons48.h +++ b/lib/Weather/WeatherIcons48.h @@ -1,215 +1,196 @@ #pragma once #include -// Generated from erikflowers/weather-icons (font + css mappings) -// 48x48, 1-bit, MSB-first, row-major, rotated for device orientation +// Generated from erikflowers/weather-icons SVGs. +// 48x48, 1-bit, MSB-first, row-major. +// Regenerate with: python scripts/generate_weather_icons.py --fetch static const uint8_t WI48_CLEAR_DAY[] = { - 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFE, 0x7F, 0xF8, 0x7F, 0xFC, 0x7F, - 0xFC, 0x3F, 0xFC, 0xFF, 0xF8, 0x3F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFE, 0x0F, 0xFF, 0xFF, - 0xC0, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x8F, - 0xC0, 0x0F, 0xC3, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xC0, 0x7F, 0xFF, 0xFF, 0xF8, 0x3F, 0xF0, - 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, - 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFE, 0x1F, 0xFF, 0x00, 0xE1, 0xFF, 0xFE, 0x1F, 0x00, - 0x00, 0xE1, 0xFF, 0xFE, 0x1F, 0x00, 0x00, 0xE1, 0xFF, 0xFE, 0x1F, 0x00, 0x81, 0xE1, 0xFF, 0xFE, - 0x1F, 0x81, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, - 0x7F, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, 0x3F, 0xF0, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, 0xE0, 0x7F, 0xFF, - 0xFF, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, - 0xFF, 0xFF, 0xFF, 0xCF, 0xC0, 0x0F, 0xE7, 0xFF, 0xFF, 0x8F, 0xF0, 0x3F, 0xC3, 0xFF, 0xFF, 0x0F, - 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, - 0xFC, 0x1F, 0xFC, 0xFF, 0xF0, 0x3F, 0xFC, 0x3F, 0xF8, 0x7F, 0xFC, 0x3F, 0xFE, 0x7F, 0xF8, 0x7F, - 0xFE, 0x7F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFE, 0x7F, 0xF8, 0xFF, + 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xE7, 0xF8, 0x1F, 0xE7, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, + 0xFF, 0xFC, 0x1F, 0xF8, 0x3F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFE, 0x3F, 0xFF, + 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, 0xC0, 0xF8, 0xFF, 0xFF, 0x1F, 0x03, 0x80, 0x78, 0xFF, 0xFF, 0x1E, 0x01, + 0x80, 0x78, 0xFF, 0xFF, 0x1E, 0x01, 0xC0, 0xF8, 0xFF, 0xFF, 0x1F, 0x03, 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, + 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xF8, 0x3F, 0xFF, + 0xFF, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xE7, 0xF0, 0x0F, 0xE7, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, + 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0x7F, + 0xFE, 0x1F, 0xFE, 0x7F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_CLEAR_NIGHT[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, - 0xF0, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0xFE, 0x07, 0xFF, - 0xFF, 0xFF, 0xE1, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, - 0xC1, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xC1, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0xFF, 0xFF, 0x00, - 0x0F, 0xFF, 0xE0, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xF0, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF0, 0xFF, - 0xFE, 0x01, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, - 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x07, - 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, - 0xFF, 0x81, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xE0, 0x7F, 0xFE, - 0x07, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xF0, 0x03, 0x80, 0x1F, 0xFF, 0xFF, 0xFC, - 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xFF, + 0xFF, 0xC0, 0x03, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0x07, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x07, 0xFF, 0xFF, + 0xFE, 0x01, 0xFF, 0x03, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0x83, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0x83, 0xFF, 0xFF, + 0xF8, 0x1F, 0xFF, 0x81, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, + 0xE0, 0x7F, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x7F, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, + 0xC0, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xC0, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x00, 0x01, + 0xC1, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xC1, 0xFF, 0xFF, 0xFF, 0xE0, 0x01, 0x81, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, + 0x81, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, + 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0x7F, 0xFF, 0xFF, 0xFE, 0x07, + 0xE0, 0x3F, 0xFF, 0xFF, 0xFE, 0x07, 0xF0, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x0F, + 0xF8, 0x0F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFC, 0x07, 0xFF, 0xFF, 0xE0, 0x1F, 0xFC, 0x01, 0xFF, 0xFF, 0xC0, 0x3F, + 0xFE, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x00, 0x1F, 0xFC, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0xE0, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_PARTLY_CLOUDY_DAY[] = { - 0xFE, 0x3F, 0xFC, 0x3F, 0xFE, 0x3F, 0xFE, 0x1F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFE, 0x0F, 0xFF, 0xFF, - 0xF8, 0x3F, 0xFE, 0x07, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0x03, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x83, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xE3, 0xFF, - 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x07, 0xE0, - 0x00, 0x0F, 0xFF, 0xFC, 0x1F, 0xF0, 0x00, 0x07, 0xFF, 0xF8, 0x3F, 0xE0, 0x7F, 0x03, 0xFF, 0xF8, - 0x7F, 0xC0, 0xFF, 0x81, 0xFF, 0xF0, 0x7F, 0xC1, 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0x83, 0xFF, 0xE0, - 0x00, 0x70, 0xFF, 0x83, 0xFF, 0xE0, 0x00, 0x70, 0xFF, 0x87, 0xFF, 0xF0, 0x00, 0x70, 0xFF, 0x87, - 0xFF, 0xF0, 0x80, 0xF0, 0xFF, 0x87, 0xFF, 0xF0, 0xFF, 0xF0, 0x7C, 0x07, 0xFF, 0xF0, 0xFF, 0xF0, - 0x78, 0x07, 0xFF, 0xF0, 0xFF, 0xF8, 0x30, 0x07, 0xFF, 0xF0, 0xFF, 0xF8, 0x00, 0x1F, 0xFF, 0xF0, - 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, - 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, - 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0x9E, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0x0E, 0x1F, 0xFF, 0xFF, 0xF0, - 0xFE, 0x0E, 0x1F, 0xFF, 0xFF, 0xF0, 0xFE, 0x1E, 0x1F, 0xFF, 0xFF, 0xF0, 0xFE, 0x1E, 0x1F, 0xFF, - 0xFF, 0xF0, 0xFF, 0x3E, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, - 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF0, - 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xE0, 0x0F, - 0xFF, 0xF0, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, - 0xFE, 0x07, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xE0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xF3, 0xFF, 0x7F, + 0xFF, 0xFF, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x9F, 0xFF, 0xF8, 0x7F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, + 0xFF, 0xF8, 0x00, 0x1C, 0x1F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0x0F, 0xFF, 0xFF, 0xC1, 0xF0, 0x7F, 0x87, 0xFF, + 0xFF, 0x87, 0xFC, 0x3F, 0xC7, 0xFF, 0xFF, 0x8F, 0xFE, 0x3F, 0xE7, 0xFF, 0xFF, 0x1F, 0xFF, 0x1F, 0xE7, 0x83, + 0xFF, 0x1F, 0xFF, 0x1F, 0xE3, 0x81, 0xFE, 0x3F, 0xFF, 0x83, 0xE7, 0x83, 0xF8, 0x3F, 0xFF, 0x80, 0xE7, 0xFF, + 0xF0, 0x3F, 0xFF, 0x80, 0x07, 0xFF, 0xE1, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, + 0xC7, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, + 0x8F, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xCE, 0x7F, 0x8F, 0xFF, 0xFF, 0xFF, 0x8C, 0x3F, + 0xC7, 0xFF, 0xFF, 0xFF, 0x8E, 0x3F, 0xC7, 0xFF, 0xFF, 0xFF, 0x1F, 0x7F, 0xE1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, + 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_PARTLY_CLOUDY_NIGHT[] = { - 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x60, - 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x08, 0x00, 0x01, 0xFF, 0xFF, 0xFC, - 0x0C, 0x00, 0x00, 0xFF, 0xFC, 0x60, 0x1C, 0x0F, 0xE0, 0x7F, 0xF8, 0x00, 0x38, 0x1F, 0xF0, 0x3F, - 0xF8, 0x00, 0x78, 0x3F, 0xF8, 0x3F, 0xF8, 0x01, 0xF0, 0x7F, 0xFC, 0x1F, 0xF8, 0x0F, 0xF0, 0x7F, - 0xFC, 0x1F, 0xF8, 0x7F, 0xF0, 0xFF, 0xFE, 0x1F, 0xF8, 0x7F, 0xF0, 0xFF, 0xFE, 0x1F, 0xF8, 0x3F, - 0xF0, 0xFF, 0xFE, 0x1F, 0xF8, 0x3F, 0x80, 0xFF, 0xFE, 0x1F, 0xFC, 0x1F, 0x00, 0xFF, 0xFE, 0x1F, - 0xFC, 0x1E, 0x00, 0xFF, 0xFE, 0x1F, 0xFE, 0x04, 0x03, 0xFF, 0xFE, 0x1F, 0xFE, 0x00, 0x1F, 0xFF, - 0xFE, 0x1F, 0xFF, 0x00, 0x3F, 0xFF, 0xFE, 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFE, 0x1F, 0xFF, 0xE0, - 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xE1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, 0x1F, - 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, - 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC3, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC1, - 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xE1, 0xFF, 0xFF, 0xFE, 0x1F, - 0xFF, 0xE0, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xE0, 0x7F, 0xFF, 0xFE, 0x1F, 0xFF, 0xF0, 0x3F, 0xFF, - 0xFE, 0x1F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFE, 0x1F, 0xFF, 0xFC, 0x01, 0xFF, 0xFE, 0x1F, 0xFF, 0xFE, - 0x00, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x00, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, 0x1F, - 0xFF, 0xFF, 0xF0, 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x3F, - 0xF8, 0x3F, 0xFF, 0xFF, 0xF8, 0x1F, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, - 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF8, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xF8, 0xFF, + 0xFF, 0xFE, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xF0, 0x3F, 0x03, 0xF8, 0x7F, + 0xFF, 0xF0, 0xFF, 0xC1, 0xFC, 0x3F, 0xFF, 0xE1, 0xFF, 0xF0, 0xFC, 0x1F, 0xFF, 0xC3, 0xFF, 0xF8, 0x7E, 0x07, + 0xFF, 0xC3, 0xFF, 0xF8, 0x7F, 0x01, 0xFF, 0xC7, 0xFF, 0xFC, 0x7F, 0x83, 0xFF, 0x87, 0xFF, 0xFC, 0x07, 0xC3, + 0xFE, 0x0F, 0xFF, 0xFC, 0x01, 0xC3, 0xF8, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xF0, 0x0F, 0xFF, 0xFE, 0x80, 0x07, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, + 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xF0, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0xFE, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_OVERCAST[] = { - 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, - 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, - 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xFF, - 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x3F, 0x80, - 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0x00, 0x07, 0xFF, 0xFF, 0x87, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x0F, - 0xFC, 0x1F, 0x81, 0xFF, 0xFF, 0x1F, 0xFC, 0x3F, 0xE1, 0xFF, 0xFE, 0x1F, 0xF8, 0x7F, 0xF0, 0xFF, - 0xFE, 0x3F, 0xF8, 0x7F, 0xF0, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xF8, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, - 0xF8, 0xFF, 0xFE, 0x3F, 0xF8, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, 0xC0, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, - 0x80, 0xFF, 0xF8, 0xFF, 0xFF, 0x0E, 0x00, 0xFF, 0xF8, 0xFF, 0xFF, 0x06, 0x0F, 0xFF, 0xF8, 0xFF, - 0xFF, 0x8C, 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0x88, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, - 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, - 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, - 0xFF, 0xF1, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, - 0xF8, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC, - 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xF8, 0xFF, - 0xFF, 0xFF, 0x80, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, - 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xE1, 0xFF, 0xFF, 0xFF, - 0xFC, 0x1F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFE, 0x1F, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xC7, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xE3, 0xFF, + 0xFF, 0xF0, 0x00, 0xFF, 0xE3, 0xFF, 0xFF, 0xE0, 0x60, 0x7F, 0xF0, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xF0, 0x1F, + 0xFF, 0x87, 0xFE, 0x1F, 0xF0, 0x0F, 0xFF, 0x8F, 0xFF, 0x0F, 0xFF, 0x87, 0xFF, 0x1F, 0xFF, 0x8F, 0xFF, 0xE3, + 0xFF, 0x1F, 0xFF, 0x8F, 0xFF, 0xE3, 0xFC, 0x1F, 0xFF, 0xC0, 0x7F, 0xF1, 0xF8, 0x3F, 0xFF, 0xC0, 0x3F, 0xF1, + 0xE0, 0x7F, 0xFF, 0xFC, 0x1F, 0xF1, 0xE3, 0xFF, 0xFF, 0xFF, 0x0F, 0xF1, 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, + 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0x87, 0x8F, 0xFF, 0xFF, 0xFF, 0xE2, 0x0F, + 0x8F, 0xFF, 0xFF, 0xFF, 0xE2, 0x1F, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0x7F, 0x8F, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, + 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, + 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_FOG[] = { - 0xFF, 0xFF, 0xFD, 0xE7, 0xFF, 0x0F, 0xFF, 0xFF, 0xE1, 0xC3, 0xFF, 0x0F, 0xFF, 0xFF, 0xC1, 0xC3, - 0xFF, 0x0F, 0xFF, 0xFF, 0x81, 0xC3, 0xFF, 0x0F, 0xFF, 0xFF, 0x01, 0xC3, 0xFF, 0x0F, 0xFF, 0xFE, - 0x07, 0xC3, 0xFF, 0x0F, 0xFF, 0xFE, 0x0F, 0xC3, 0xCF, 0x0F, 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, - 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x3F, 0xC3, - 0x87, 0x0F, 0xFF, 0xFC, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xF0, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xC0, - 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0x80, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0x00, 0x3F, 0xC3, 0x87, 0x0F, - 0xFE, 0x07, 0xFF, 0xC3, 0x87, 0x0F, 0xFC, 0x0F, 0xFF, 0xC3, 0x87, 0x0F, 0xFC, 0x1F, 0xFF, 0xC3, - 0x87, 0x0F, 0xF8, 0x3F, 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x7F, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0x7F, - 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0x7F, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, - 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, - 0x87, 0x0F, 0xF0, 0xFF, 0xFF, 0xC3, 0x87, 0x0F, 0xF0, 0x7F, 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x7F, - 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x3F, 0xFF, 0xC3, 0x87, 0x0F, 0xF8, 0x3F, 0xFF, 0xC3, 0x87, 0x0F, - 0xFC, 0x1F, 0xFF, 0xC3, 0x87, 0x0F, 0xFE, 0x07, 0xFF, 0xC3, 0x87, 0x0F, 0xFE, 0x00, 0xFF, 0xC3, - 0x87, 0x0F, 0xFF, 0x00, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xC0, 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xE0, - 0x3F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, 0xFF, 0xFC, 0x1F, 0xC3, 0x87, 0x0F, - 0xFF, 0xFE, 0x0F, 0xC3, 0x87, 0x0F, 0xFF, 0xFE, 0x07, 0xC3, 0x87, 0x0F, 0xFF, 0xFF, 0x03, 0xC3, - 0x87, 0x1F, 0xFF, 0xFF, 0x81, 0xC3, 0x87, 0xFF, 0xFF, 0xFF, 0xC1, 0xC3, 0x87, 0xFF, 0xFF, 0xFF, - 0xE1, 0xC3, 0x87, 0xFF, 0xFF, 0xFF, 0xF9, 0xC7, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC1, 0xFF, 0xFF, + 0xFF, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFC, 0x7F, 0xFF, + 0xFF, 0xF8, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFE, 0x01, 0xFF, + 0xFF, 0x80, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0x1F, 0xFF, 0xFF, 0xFC, 0x3F, + 0xFE, 0x3F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0xC0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_DRIZZLE[] = { - 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, - 0x03, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x07, 0xFC, 0x0F, 0xFF, 0xFF, 0xFE, 0x0F, 0xFE, 0x0F, 0xFF, - 0xFF, 0xFC, 0x1F, 0xFF, 0x07, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x07, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, - 0x87, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, - 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, - 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xC0, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, - 0x00, 0x7F, 0xF8, 0x3F, 0xFF, 0xFE, 0x00, 0x3F, 0xF8, 0x3F, 0xE1, 0xFC, 0x00, 0x3F, 0xF0, 0x7F, - 0xC0, 0xF8, 0x00, 0x3F, 0xF0, 0x7F, 0xC0, 0xF0, 0x00, 0x3F, 0xF0, 0xFF, 0xE1, 0xF8, 0x00, 0x3F, - 0xF0, 0xFF, 0xFF, 0xFC, 0x00, 0x3F, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xF0, 0xFF, 0xFF, 0xFF, - 0x80, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xF0, 0x7F, 0xFF, 0x83, 0xFF, 0xFF, 0xF0, 0x7F, - 0xFF, 0x03, 0xFF, 0xFF, 0xF8, 0x7F, 0xFC, 0x01, 0xFF, 0xFF, 0xF8, 0x3F, 0xFC, 0x01, 0xFF, 0xFF, - 0xF8, 0x1F, 0xFE, 0x03, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0x83, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xEF, - 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xC0, - 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0x87, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x07, 0xFF, - 0xFF, 0xFC, 0x1F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFE, 0x0F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFE, 0x07, 0xFC, - 0x1F, 0xFF, 0xFF, 0xFF, 0x01, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, - 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, + 0xFF, 0xC7, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x3F, + 0xF0, 0x07, 0xFF, 0xFF, 0x00, 0x0F, 0xF0, 0x7F, 0xFF, 0x9F, 0xFE, 0x0F, 0xE1, 0xFF, 0xFF, 0x8F, 0xFF, 0x87, + 0xC3, 0xFF, 0xFF, 0x0F, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0x0F, 0xFF, 0xC3, 0xC7, 0xFF, 0xBF, 0x9F, 0xFF, 0xE3, + 0x87, 0xFF, 0x1F, 0xFF, 0xFF, 0xE1, 0x87, 0xFE, 0x1F, 0xFF, 0xFF, 0xE1, 0x87, 0xFE, 0x0F, 0xFF, 0xFF, 0xE1, + 0x87, 0xFC, 0x0F, 0xFF, 0xFF, 0xE1, 0xC7, 0xFC, 0x0F, 0xFF, 0xFF, 0xE3, 0xC3, 0xFE, 0x0F, 0x9F, 0xFF, 0xC3, + 0xC3, 0xFF, 0x1F, 0x0F, 0xFF, 0xC3, 0xE1, 0xFF, 0xFE, 0x07, 0xFF, 0x87, 0xF0, 0x7F, 0xFE, 0x07, 0xFE, 0x0F, + 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x0F, 0xFC, 0x03, 0xF0, 0x3F, 0xFE, 0x0F, 0xF8, 0x01, 0xF0, 0x7F, + 0xFF, 0xCF, 0xF8, 0x01, 0xF3, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_RAIN[] = { - 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, - 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, - 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, - 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, - 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, - 0xFE, 0x07, 0xFF, 0xFF, 0xFE, 0x01, 0xFE, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFE, 0x00, 0x0F, 0xFF, - 0xF0, 0x0F, 0xFF, 0x00, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0xE0, 0x00, 0xFF, 0xE0, 0xFF, 0xFF, 0xFE, - 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, - 0xFE, 0x03, 0xFF, 0xFF, 0x83, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0x87, 0xFF, 0xFE, 0x00, 0x07, 0xFF, - 0x87, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0x87, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0x87, 0xFF, 0xFF, 0xFF, - 0x00, 0x01, 0x87, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x83, 0xFF, 0xFE, 0x1F, 0xFE, 0x01, 0x83, 0xFF, - 0xFE, 0x01, 0xFF, 0xE3, 0xC3, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xC1, 0xFF, 0xFE, 0x00, 0x03, 0xFF, - 0xC0, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xE0, 0x7F, 0xFF, 0xF8, 0x00, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, - 0x80, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xF1, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x01, - 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, - 0xFF, 0xE0, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, - 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, - 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, + 0xFF, 0x87, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x3F, + 0xF0, 0x07, 0xFF, 0xFF, 0x00, 0x0F, 0xF0, 0x7F, 0xFF, 0xFF, 0xFE, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, + 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xDF, 0x9F, 0x3F, 0xE1, + 0x87, 0xFF, 0x8F, 0x1F, 0x1F, 0xE1, 0xC7, 0xFF, 0x8F, 0x0E, 0x1F, 0xE3, 0xC3, 0xFF, 0x0F, 0x1E, 0x1F, 0xC3, + 0xC1, 0xFF, 0x0E, 0x1E, 0x3F, 0x83, 0xE0, 0xFF, 0x1E, 0x1C, 0x3F, 0x87, 0xF0, 0x7E, 0x1E, 0x3C, 0x3E, 0x0F, + 0xF8, 0x0E, 0x1E, 0x3C, 0x70, 0x1F, 0xFC, 0x0E, 0x3C, 0x3C, 0x70, 0x3F, 0xFE, 0x0E, 0x3C, 0x38, 0x70, 0x7F, + 0xFF, 0xEC, 0x3C, 0x78, 0x7F, 0xFF, 0xFF, 0xFC, 0x38, 0x78, 0xFF, 0xFF, 0xFF, 0xFC, 0x78, 0x70, 0xFF, 0xFF, + 0xFF, 0xF8, 0x78, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0x78, 0xF1, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xF1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_SNOW[] = { - 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, - 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, - 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, - 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, - 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFC, 0x3C, 0x3F, - 0xF0, 0x0F, 0xFF, 0xFC, 0x3C, 0x3F, 0xF0, 0x3F, 0xFF, 0xFC, 0x3C, 0x3F, 0xE0, 0xFF, 0xFF, 0xFE, - 0x7E, 0x7F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xE7, 0xE7, 0xF3, 0x87, 0xFF, 0xFF, 0xC3, 0xC3, 0xE1, - 0x87, 0xFF, 0xFF, 0xC3, 0xC3, 0xE1, 0x87, 0xFF, 0xFF, 0xC3, 0xC3, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, 0xC1, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, - 0xC0, 0xFF, 0xFF, 0xFC, 0x3C, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x01, - 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, - 0xFF, 0xE0, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, - 0x7F, 0xFF, 0xFF, 0xF8, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, - 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xF8, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x7F, 0xFF, + 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, + 0xFF, 0x87, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x3F, + 0xF0, 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xF0, 0x7F, 0xFF, 0xFF, 0xFE, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, + 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, + 0xC3, 0xFF, 0xFE, 0x3F, 0xFF, 0x83, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0x07, 0xF0, 0x7F, 0xFE, 0x7F, 0xFE, 0x0F, + 0xF0, 0x0F, 0x3F, 0xFC, 0xF0, 0x1F, 0xFC, 0x0E, 0x1F, 0xF8, 0x70, 0x3F, 0xFE, 0x0F, 0x1F, 0xFC, 0x70, 0x7F, + 0xFF, 0xEF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFE, 0x1F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_THUNDERSTORM[] = { - 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x0F, - 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, - 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xFF, - 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC1, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xF8, - 0x7F, 0xFF, 0xFF, 0xC3, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xC3, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, - 0xFC, 0x0F, 0xFF, 0xFF, 0xFC, 0x03, 0xFC, 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFC, 0x00, 0x0F, 0xFF, - 0xE0, 0x1F, 0xFE, 0x00, 0x03, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x01, 0xFF, 0xC1, 0xFF, 0xFF, 0xFE, - 0x01, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0x83, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0x07, 0xFF, - 0xFC, 0x07, 0xFF, 0xFF, 0x07, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, - 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0x0F, 0xFF, 0xFF, 0xFF, - 0x00, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0x07, 0xFF, 0xFD, 0xFF, 0xFF, 0x03, 0x07, 0xFF, - 0xFC, 0x7F, 0x9F, 0xFF, 0x87, 0xFF, 0xFC, 0x1F, 0x8F, 0xFF, 0x83, 0xFF, 0xFC, 0x07, 0x83, 0xFF, - 0x81, 0xFF, 0xFC, 0x01, 0x81, 0xFF, 0xC0, 0xFF, 0xFC, 0x00, 0x80, 0x7F, 0xE0, 0x7F, 0xFC, 0x00, - 0x00, 0x3F, 0xF0, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x03, 0xFE, 0x00, 0x04, 0x07, 0xFC, 0x03, - 0xFF, 0xC0, 0x07, 0xC1, 0xFF, 0x03, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC1, 0xFF, 0xF0, 0x07, 0xFF, - 0xFF, 0xE1, 0xFF, 0xF0, 0xC7, 0xFF, 0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xC1, - 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, - 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xC0, 0xFF, 0xFF, + 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, + 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0x01, 0xFF, + 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xF8, 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x0F, + 0xE0, 0x7F, 0xFF, 0xFF, 0xFE, 0x07, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, + 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xF8, 0x0F, 0x9F, 0xBF, 0xE1, 0x87, 0xF8, 0x1F, 0x0F, 0x1F, 0xE1, + 0xC7, 0xF0, 0x1F, 0x1E, 0x1F, 0xE3, 0xC3, 0xF0, 0x3F, 0x1E, 0x1F, 0xC3, 0xC1, 0xE0, 0x3E, 0x1E, 0x3F, 0x83, + 0xE0, 0xE0, 0x7E, 0x1E, 0x3F, 0x07, 0xF0, 0x60, 0x7E, 0x3C, 0x3E, 0x0F, 0xF8, 0x00, 0xFE, 0x3C, 0x30, 0x1F, + 0xFC, 0x00, 0xFC, 0x3C, 0x70, 0x3F, 0xFF, 0x01, 0xFC, 0x3C, 0x70, 0x7F, 0xFF, 0x81, 0xFC, 0x78, 0x7F, 0xFF, + 0xFF, 0x80, 0x1C, 0x78, 0x7F, 0xFF, 0xFF, 0x00, 0x18, 0x78, 0xFF, 0xFF, 0xFF, 0x00, 0x38, 0x78, 0xFF, 0xFF, + 0xFF, 0xF0, 0x78, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0x78, 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xF1, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE7, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; diff --git a/scripts/convert_icon.py b/scripts/convert_icon.py index 56ea7d4b..81585636 100644 --- a/scripts/convert_icon.py +++ b/scripts/convert_icon.py @@ -3,36 +3,113 @@ import os from PIL import Image import cairosvg import io +import xml.etree.ElementTree as ET threshold = 128 + +def parse_svg_intrinsic_size(svg_data): + try: + root = ET.fromstring(svg_data) + except ET.ParseError: + return None, None + + viewbox = root.get("viewBox") or root.get("viewbox") + if viewbox: + parts = viewbox.replace(",", " ").split() + if len(parts) == 4: + try: + vb_w = float(parts[2]) + vb_h = float(parts[3]) + if vb_w > 0 and vb_h > 0: + return vb_w, vb_h + except ValueError: + pass + + def parse_len(value): + if not value: + return None + cleaned = "".join(ch for ch in value if ch.isdigit() or ch in ".-") + if not cleaned: + return None + try: + parsed = float(cleaned) + return parsed if parsed > 0 else None + except ValueError: + return None + + w = parse_len(root.get("width")) + h = parse_len(root.get("height")) + return w, h + + +def fit_inside_canvas(src_w, src_h, dst_w, dst_h): + if src_w <= 0 or src_h <= 0 or dst_w <= 0 or dst_h <= 0: + return dst_w, dst_h + + src_ratio = src_w / src_h + dst_ratio = dst_w / dst_h + + if src_ratio >= dst_ratio: + fit_w = dst_w + fit_h = max(1, int(round(fit_w / src_ratio))) + else: + fit_h = dst_h + fit_w = max(1, int(round(fit_h * src_ratio))) + + return fit_w, fit_h + + def svg_to_png_bytes(svg_path, width, height): - with open(svg_path, 'rb') as f: + with open(svg_path, "rb") as f: svg_data = f.read() - png_bytes = cairosvg.svg2png(bytestring=svg_data, output_width=width, output_height=height) + + src_w, src_h = parse_svg_intrinsic_size(svg_data) + render_w, render_h = fit_inside_canvas( + src_w or width, src_h or height, width, height + ) + + png_bytes = cairosvg.svg2png( + bytestring=svg_data, output_width=render_w, output_height=render_h + ) return png_bytes + +def center_on_canvas(img, width, height): + if img.mode != "RGBA": + img = img.convert("RGBA") + canvas = Image.new("RGBA", (width, height), (255, 255, 255, 255)) + x = (width - img.width) // 2 + y = (height - img.height) // 2 + canvas.paste(img, (x, y), img) + return canvas + + def load_image(path, width, height): ext = os.path.splitext(path)[1].lower() - if ext == '.svg': + if ext == ".svg": png_bytes = svg_to_png_bytes(path, width, height) - img = Image.open(io.BytesIO(png_bytes)) + img = Image.open(io.BytesIO(png_bytes)).convert("RGBA") + img = center_on_canvas(img, width, height) else: - img = Image.open(path) - img = img.convert('RGBA') - img = img.resize((width, height), Image.LANCZOS) + img = Image.open(path).convert("RGBA") + # Keep source aspect ratio and fit inside requested canvas. + fit = img.copy() + fit.thumbnail((width, height), Image.LANCZOS) + img = center_on_canvas(fit, width, height) # Flatten alpha: paste on white background - background = Image.new('RGBA', img.size, (255, 255, 255, 255)) + background = Image.new("RGBA", img.size, (255, 255, 255, 255)) background.paste(img, mask=img.split()[3]) img = background # Rotate 90 degrees counterclockwise img = img.rotate(90, expand=True) return img + def image_to_c_array(img, array_name): # Convert to grayscale, then threshold to get white=1, black=0 # Convert to grayscale - img = img.convert('L') + img = img.convert("L") width, height = img.size pixels = list(img.getdata()) packed = [] @@ -44,37 +121,39 @@ def image_to_c_array(img, array_name): v = pixels[y * width + x + b] # 1 for white, 0 for black bit = 1 if v >= threshold else 0 - byte |= (bit << (7 - b)) + byte |= bit << (7 - b) packed.append(byte) # Format as C array - c = f'#pragma once\n#include \n\n' - c += f'// size: {width}x{height}\n' - c += f'static const uint8_t {array_name}[] = {{\n ' + c = f"#pragma once\n#include \n\n" + c += f"// size: {width}x{height}\n" + c += f"static const uint8_t {array_name}[] = {{\n " for i, v in enumerate(packed): - c += f'0x{v:02X}, ' + c += f"0x{v:02X}, " if (i + 1) % 16 == 0: - c += '\n ' - c = c.rstrip(', \n') + '\n};\n' + c += "\n " + c = c.rstrip(", \n") + "\n};\n" return c + def main(): if len(sys.argv) < 5: - print('Usage: python convert_image.py input.png output_name width height') + print("Usage: python convert_image.py input.png output_name width height") sys.exit(1) input_path, output_name, width, height = sys.argv[1:5] - array_name = output_name.capitalize() + 'Icon' + array_name = output_name.capitalize() + "Icon" width, height = int(width), int(height) img = load_image(input_path, width, height) c_array = image_to_c_array(img, array_name) # Always save to src/components/icons/[output_name].h relative to project root project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - output_dir = os.path.join(project_root, 'src', 'components', 'icons') + output_dir = os.path.join(project_root, "src", "components", "icons") os.makedirs(output_dir, exist_ok=True) - output_path = os.path.join(output_dir, f'{output_name}.h') - with open(output_path, 'w') as f: + output_path = os.path.join(output_dir, f"{output_name}.h") + with open(output_path, "w") as f: f.write(c_array) - print(f'Wrote {output_path}') + print(f"Wrote {output_path}") -if __name__ == '__main__': - main() \ No newline at end of file + +if __name__ == "__main__": + main() diff --git a/scripts/generate_weather_icons.py b/scripts/generate_weather_icons.py new file mode 100644 index 00000000..c1ef2cb0 --- /dev/null +++ b/scripts/generate_weather_icons.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +""" +Generate lib/Weather/WeatherIcons48.h from SVG sources. + +By default, this script loads SVG files from assets/weather-icons/svg. +Use --fetch to download missing files from erikflowers/weather-icons. +""" + +import argparse +import io +import os +from pathlib import Path +import shutil +import subprocess +import tempfile +import urllib.request +import xml.etree.ElementTree as ET +import zipfile + +from PIL import Image + +try: + import cairosvg # type: ignore +except Exception: + cairosvg = None + + +SIZE = 48 +THRESHOLD = 128 +CONTENT_MARGIN = 1 +PROJECT_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_SVG_DIR = PROJECT_ROOT / "assets" / "weather-icons" / "svg" +DEFAULT_OUT = PROJECT_ROOT / "lib" / "Weather" / "WeatherIcons48.h" +UPSTREAM_BASE = "https://raw.githubusercontent.com/erikflowers/weather-icons/master/svg" +RESVG_ZIP_URL = ( + "https://github.com/linebender/resvg/releases/latest/download/resvg-win64.zip" +) +RESVG_EXE = PROJECT_ROOT / ".cache" / "resvg" / "resvg.exe" + +ICON_SOURCES = { + "WI48_CLEAR_DAY": "wi-day-sunny.svg", + "WI48_CLEAR_NIGHT": "wi-night-clear.svg", + "WI48_PARTLY_CLOUDY_DAY": "wi-day-cloudy.svg", + "WI48_PARTLY_CLOUDY_NIGHT": "wi-night-alt-cloudy.svg", + "WI48_OVERCAST": "wi-cloudy.svg", + "WI48_FOG": "wi-fog.svg", + "WI48_DRIZZLE": "wi-sprinkle.svg", + "WI48_RAIN": "wi-rain.svg", + "WI48_SNOW": "wi-snow.svg", + "WI48_THUNDERSTORM": "wi-thunderstorm.svg", +} + + +def parse_svg_intrinsic_size(svg_data): + try: + root = ET.fromstring(svg_data) + except ET.ParseError: + return None, None + + viewbox = root.get("viewBox") or root.get("viewbox") + if viewbox: + parts = viewbox.replace(",", " ").split() + if len(parts) == 4: + try: + vb_w = float(parts[2]) + vb_h = float(parts[3]) + if vb_w > 0 and vb_h > 0: + return vb_w, vb_h + except ValueError: + pass + + def parse_len(value): + if not value: + return None + cleaned = "".join(ch for ch in value if ch.isdigit() or ch in ".-") + if not cleaned: + return None + try: + parsed = float(cleaned) + return parsed if parsed > 0 else None + except ValueError: + return None + + return parse_len(root.get("width")), parse_len(root.get("height")) + + +def fit_inside_canvas(src_w, src_h, dst_w, dst_h): + if src_w <= 0 or src_h <= 0: + return dst_w, dst_h + + src_ratio = src_w / src_h + dst_ratio = dst_w / dst_h + if src_ratio >= dst_ratio: + fit_w = dst_w + fit_h = max(1, int(round(fit_w / src_ratio))) + else: + fit_h = dst_h + fit_w = max(1, int(round(fit_h * src_ratio))) + return fit_w, fit_h + + +def ensure_resvg_binary(): + if shutil.which("resvg"): + return Path(shutil.which("resvg")) + + if RESVG_EXE.exists(): + return RESVG_EXE + + RESVG_EXE.parent.mkdir(parents=True, exist_ok=True) + archive_path = RESVG_EXE.parent / "resvg.zip" + with urllib.request.urlopen(RESVG_ZIP_URL) as response: + archive_path.write_bytes(response.read()) + + with zipfile.ZipFile(archive_path, "r") as zf: + zf.extractall(RESVG_EXE.parent) + + found = list(RESVG_EXE.parent.rglob("resvg.exe")) + if not found: + raise RuntimeError("resvg.exe not found after extraction") + + if found[0] != RESVG_EXE: + RESVG_EXE.write_bytes(found[0].read_bytes()) + + return RESVG_EXE + + +def render_svg_with_resvg(svg_data, render_w, render_h): + exe = ensure_resvg_binary() + with tempfile.TemporaryDirectory() as tmp: + tmp_path = Path(tmp) + in_svg = tmp_path / "icon.svg" + out_png = tmp_path / "icon.png" + in_svg.write_bytes(svg_data) + + cmd = [ + str(exe), + "--width", + str(render_w), + "--height", + str(render_h), + str(in_svg), + str(out_png), + ] + subprocess.run(cmd, check=True, capture_output=True) + return out_png.read_bytes() + + +def render_svg_contain(svg_data, width, height): + src_w, src_h = parse_svg_intrinsic_size(svg_data) + render_w, render_h = fit_inside_canvas( + src_w or width, src_h or height, width, height + ) + + # Render larger first so trimming and re-fit preserve detail quality. + oversample = 4 + render_w *= oversample + render_h *= oversample + + if cairosvg is not None: + png_bytes = cairosvg.svg2png( + bytestring=svg_data, output_width=render_w, output_height=render_h + ) + else: + png_bytes = render_svg_with_resvg(svg_data, render_w, render_h) + + icon = Image.open(io.BytesIO(png_bytes)).convert("RGBA") + + # Trim transparent/empty margins so symbols use available icon area better. + alpha_bbox = icon.split()[3].getbbox() + if alpha_bbox is not None: + icon = icon.crop(alpha_bbox) + + max_w = max(1, width - 2 * CONTENT_MARGIN) + max_h = max(1, height - 2 * CONTENT_MARGIN) + icon.thumbnail((max_w, max_h), Image.Resampling.LANCZOS) + + canvas = Image.new("RGBA", (width, height), (255, 255, 255, 255)) + off_x = (width - icon.width) // 2 + off_y = (height - icon.height) // 2 + canvas.paste(icon, (off_x, off_y), icon) + + # Flatten alpha on white and convert to monochrome-friendly grayscale. + flat = Image.new("RGBA", canvas.size, (255, 255, 255, 255)) + flat.paste(canvas, mask=canvas.split()[3]) + return flat.convert("L") + + +def image_to_packed_bits(img): + width, height = img.size + pixels = img.tobytes() + packed = [] + for y in range(height): + for x in range(0, width, 8): + out = 0 + for b in range(8): + px = x + b + lum = pixels[y * width + px] if px < width else 255 + # 1-bit means white/clear (not drawn), 0-bit means black/drawn. + bit = 1 if lum >= THRESHOLD else 0 + out |= bit << (7 - b) + packed.append(out) + return packed + + +def format_array(name, data): + lines = [] + per_line = 16 + for i in range(0, len(data), per_line): + chunk = data[i : i + per_line] + lines.append(" " + ", ".join(f"0x{v:02X}" for v in chunk) + ",") + body = "\n".join(lines) + return f"static const uint8_t {name}[] = {{\n{body}\n}};\n" + + +def ensure_svg(path, fetch): + if path.exists(): + return + if not fetch: + raise FileNotFoundError(f"Missing SVG: {path}") + + path.parent.mkdir(parents=True, exist_ok=True) + url = f"{UPSTREAM_BASE}/{path.name}" + with urllib.request.urlopen(url) as response: + data = response.read() + path.write_bytes(data) + + +def generate(svg_dir, output_path, fetch): + arrays = [] + for symbol, filename in ICON_SOURCES.items(): + svg_path = svg_dir / filename + ensure_svg(svg_path, fetch) + svg_data = svg_path.read_bytes() + img = render_svg_contain(svg_data, SIZE, SIZE) + packed = image_to_packed_bits(img) + arrays.append(format_array(symbol, packed)) + + header = [ + "#pragma once", + "#include ", + "", + "// Generated from erikflowers/weather-icons SVGs.", + "// 48x48, 1-bit, MSB-first, row-major.", + "// Regenerate with: python scripts/generate_weather_icons.py --fetch", + "", + ] + header.extend(arrays) + + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text("\n".join(header) + "\n", encoding="utf-8") + + +def main(): + parser = argparse.ArgumentParser( + description="Generate WeatherIcons48.h from SVG files" + ) + parser.add_argument( + "--svg-dir", + type=Path, + default=DEFAULT_SVG_DIR, + help="Directory containing source SVG files", + ) + parser.add_argument( + "--output", type=Path, default=DEFAULT_OUT, help="Output header path" + ) + parser.add_argument( + "--fetch", action="store_true", help="Fetch missing SVG files from upstream" + ) + args = parser.parse_args() + + generate(args.svg_dir, args.output, args.fetch) + rel_out = os.path.relpath(args.output, PROJECT_ROOT) + print(f"Wrote {rel_out}") + + +if __name__ == "__main__": + main() diff --git a/src/activities/weather/WeatherActivity.cpp b/src/activities/weather/WeatherActivity.cpp index d5183b36..5c4502cb 100644 --- a/src/activities/weather/WeatherActivity.cpp +++ b/src/activities/weather/WeatherActivity.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -29,79 +28,18 @@ inline bool getBitmapBit(const uint8_t* bitmap, const int size, const int x, con return (bitmap[idx] & mask) != 0; } -inline void setBitmapBit(uint8_t* bitmap, const int size, const int x, const int y, const bool value) { - const int rowBytes = size / 8; - const int idx = y * rowBytes + (x / 8); - const uint8_t mask = static_cast(0x80 >> (x % 8)); - if (value) { - bitmap[idx] |= mask; - } else { - bitmap[idx] &= static_cast(~mask); - } -} - void drawWeatherIconWithOrientation(const GfxRenderer& renderer, const uint8_t* icon, const int x, const int y, const int size) { - // Weather activity uses LandscapeClockwise; rotate icon pixels 90deg so - // directional glyphs align with the landscape-oriented UI. - if (renderer.getOrientation() != GfxRenderer::Orientation::LandscapeClockwise) { - renderer.drawImage(icon, x, y, size, size); - return; - } - - constexpr int kLargeIconBytes = (WEATHER_ICON_LARGE * WEATHER_ICON_LARGE) / 8; - std::array rotatedRaw{}; - rotatedRaw.fill(0xFF); - for (int srcY = 0; srcY < size; srcY++) { for (int srcX = 0; srcX < size; srcX++) { - // 90deg counter-clockwise: (x, y) -> (y, size - 1 - x) - const int dstX = srcY; - const int dstY = size - 1 - srcX; - setBitmapBit(rotatedRaw.data(), size, dstX, dstY, getBitmapBit(icon, size, srcX, srcY)); - } - } - - // Re-center rotated icon by its painted-pixel bounds to avoid asymmetric - // source padding appearing as left-side clipping after rotation. - int minX = size, minY = size, maxX = -1, maxY = -1; - for (int py = 0; py < size; py++) { - for (int px = 0; px < size; px++) { - if (!getBitmapBit(rotatedRaw.data(), size, px, py)) { - minX = std::min(minX, px); - minY = std::min(minY, py); - maxX = std::max(maxX, px); - maxY = std::max(maxY, py); - } - } - } - - if (maxX < minX || maxY < minY) { - renderer.drawImage(rotatedRaw.data(), x, y, size, size); - return; - } - - const int bboxW = maxX - minX + 1; - const int bboxH = maxY - minY + 1; - const int targetMinX = (size - bboxW) / 2; - const int targetMinY = (size - bboxH) / 2; - const int shiftX = targetMinX - minX; - const int shiftY = targetMinY - minY; - - std::array rotatedCentered{}; - rotatedCentered.fill(0xFF); - for (int py = 0; py < size; py++) { - for (int px = 0; px < size; px++) { - const int dstX = px + shiftX; - const int dstY = py + shiftY; - if (dstX < 0 || dstX >= size || dstY < 0 || dstY >= size) { + // 0 bits are black (drawn) in these icon bitmaps. + if (getBitmapBit(icon, size, srcX, srcY)) { continue; } - setBitmapBit(rotatedCentered.data(), size, dstX, dstY, getBitmapBit(rotatedRaw.data(), size, px, py)); + + renderer.drawPixel(x + srcX, y + srcY); } } - - renderer.drawImage(rotatedCentered.data(), x, y, size, size); } StrId getWeatherDescriptionStrId(const int wmoCode) { @@ -193,6 +131,8 @@ void WeatherActivity::onExit() { } void WeatherActivity::loadAndDisplay() { + constexpr long CACHE_REFRESH_AGE_SECONDS = 30L * 60L; + if (!WEATHER_SETTINGS.hasLocation()) { state = State::ERROR; errorMessage = tr(STR_WEATHER_NO_LOCATION); @@ -203,23 +143,23 @@ void WeatherActivity::loadAndDisplay() { // Try cache first WeatherData cached = WeatherClient::getWeather(WEATHER_SETTINGS, forceRefresh); if (cached.valid) { - const bool shouldRefresh = forceRefresh; - if (!shouldRefresh) { - const time_t now = time(nullptr); - const long age = (cached.fetchedAt > 0) ? static_cast(now - cached.fetchedAt) : -1; - if (age >= 0 && age > 30L * 60L) { - LOG_DBG("WEA", "Displayed stale cache (age=%ld s); scheduling refresh", age); - } else { - LOG_DBG("WEA", "Displayed cache (age=%ld s)", age); - } + const time_t now = time(nullptr); + const long age = (cached.fetchedAt > 0) ? static_cast(now - cached.fetchedAt) : -1; + const bool cacheIsStale = (age >= 0 && age > CACHE_REFRESH_AGE_SECONDS) || age < 0; + const bool shouldBackgroundRefresh = forceRefresh || cacheIsStale; + + if (cacheIsStale) { + LOG_DBG("WEA", "Displayed stale cache (age=%ld s); scheduling refresh", age); + } else { + LOG_DBG("WEA", "Displayed fresh cache (age=%ld s); skip auto-refresh", age); } weatherData = std::move(cached); state = State::WEATHER_DISPLAY; requestUpdate(); - // After first paint, refresh stale/non-forced data via WiFi-gated path. - if (!forceRefresh) { + // Refresh only when forced or when cache is stale. + if (shouldBackgroundRefresh) { checkAndConnectWifi(); } return; @@ -300,6 +240,7 @@ void WeatherActivity::loop() { if (state == State::ERROR) { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { // Open weather settings (useful when no location is configured) + renderer.setOrientation(GfxRenderer::Orientation::Portrait); startActivityForResult(std::make_unique(renderer, mappedInput), [this](const ActivityResult&) { renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise); @@ -359,6 +300,7 @@ void WeatherActivity::loop() { onGoHome(); } else if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { // Open weather settings + renderer.setOrientation(GfxRenderer::Orientation::Portrait); startActivityForResult(std::make_unique(renderer, mappedInput), [this](const ActivityResult&) { // Re-apply landscape after returning from settings From 811b4807cdc87ddd2a62fdb645f3172a9c5c9b86 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 2 Apr 2026 18:28:24 +0200 Subject: [PATCH 4/5] Increase icon size a bit --- lib/Weather/WeatherIcons.cpp | 6 +- lib/Weather/WeatherIcons.h | 4 +- lib/Weather/WeatherIcons48.h | 481 ++++++++++++++------- scripts/generate_weather_icons.py | 8 +- src/activities/weather/WeatherActivity.cpp | 14 +- 5 files changed, 338 insertions(+), 175 deletions(-) diff --git a/lib/Weather/WeatherIcons.cpp b/lib/Weather/WeatherIcons.cpp index b6acda6e..64236d7d 100644 --- a/lib/Weather/WeatherIcons.cpp +++ b/lib/Weather/WeatherIcons.cpp @@ -4,11 +4,11 @@ // Weather icon glyph source attribution: // https://github.com/erikflowers/weather-icons -// 48x48 monochrome icon bitmaps in this file are adapted from that icon set. +// 64x64 monochrome icon bitmaps in this file are adapted from that icon set. // ============================================================================ -// 48x48 Large Weather Icons (1-bit, MSB-first, row-major) -// Each row = 48 pixels = 6 bytes. Total = 48 * 6 = 288 bytes per icon. +// 64x64 Large Weather Icons (1-bit, MSB-first, row-major) +// Each row = 64 pixels = 8 bytes. Total = 64 * 8 = 512 bytes per icon. // Pixel ON (1) = white/clear, Pixel OFF (0) = black/drawn. // These are "inverted" bitmaps - 0 bits are drawn as black pixels. // ============================================================================ diff --git a/lib/Weather/WeatherIcons.h b/lib/Weather/WeatherIcons.h index ef928e39..5e29f2e5 100644 --- a/lib/Weather/WeatherIcons.h +++ b/lib/Weather/WeatherIcons.h @@ -8,7 +8,7 @@ #endif // Weather icon size constants -constexpr int WEATHER_ICON_LARGE = 48; // Large icon for current weather +constexpr int WEATHER_ICON_LARGE = 64; // Large icon for current weather constexpr int WEATHER_ICON_SMALL = 24; // Small icon for daily forecast // WMO weather code to icon category mapping @@ -75,7 +75,7 @@ inline WeatherIconType getWeatherIconType(int wmoCode, bool isDay) { // Get human-readable description for WMO weather code const char* getWeatherDescription(int wmoCode); -// Get the appropriate large icon bitmap (48x48, 1-bit, MSB first) +// Get the appropriate large icon bitmap (64x64, 1-bit, MSB first) const uint8_t* getWeatherIconLarge(WeatherIconType type); // Get the appropriate small icon bitmap (24x24, 1-bit, MSB first) diff --git a/lib/Weather/WeatherIcons48.h b/lib/Weather/WeatherIcons48.h index d7e5f349..f5b0aa03 100644 --- a/lib/Weather/WeatherIcons48.h +++ b/lib/Weather/WeatherIcons48.h @@ -6,191 +6,352 @@ // Regenerate with: python scripts/generate_weather_icons.py --fetch static const uint8_t WI48_CLEAR_DAY[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFE, 0x7F, 0xF8, 0xFF, - 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, - 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xE7, 0xF8, 0x1F, 0xE7, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, - 0xFF, 0xFC, 0x1F, 0xF8, 0x3F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xF8, 0x7F, 0xFE, 0x3F, 0xFF, - 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, 0xC0, 0xF8, 0xFF, 0xFF, 0x1F, 0x03, 0x80, 0x78, 0xFF, 0xFF, 0x1E, 0x01, - 0x80, 0x78, 0xFF, 0xFF, 0x1E, 0x01, 0xC0, 0xF8, 0xFF, 0xFF, 0x1F, 0x03, 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, - 0xFF, 0xF8, 0x7F, 0xFE, 0x1F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xF8, 0x3F, 0xFF, - 0xFF, 0xFE, 0x0F, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xE7, 0xF0, 0x0F, 0xE7, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xC3, 0xFF, - 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xF0, 0x7F, - 0xFE, 0x1F, 0xFE, 0x7F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x3F, 0xFF, 0xE1, 0xFF, + 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, + 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, + 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, + 0xFF, 0xF8, 0x3F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x80, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xC0, 0x3F, 0x83, 0xFF, 0xFF, 0xC1, 0xFC, 0x03, + 0x80, 0x1F, 0x03, 0xFF, 0xFF, 0xE1, 0xF8, 0x01, 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, + 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, 0x80, 0x1F, 0x03, 0xFF, 0xFF, 0xE1, 0xF8, 0x01, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x01, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFE, 0x7F, 0xFC, 0x1F, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, + 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, + 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, + 0xFF, 0xC7, 0xFF, 0xFC, 0x3F, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_CLEAR_NIGHT[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, - 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xFF, - 0xFF, 0xC0, 0x03, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0x07, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x07, 0xFF, 0xFF, - 0xFE, 0x01, 0xFF, 0x03, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0x83, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0x83, 0xFF, 0xFF, - 0xF8, 0x1F, 0xFF, 0x81, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, - 0xE0, 0x7F, 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x7F, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, - 0xC0, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xC0, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x00, 0x01, - 0xC1, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xC1, 0xFF, 0xFF, 0xFF, 0xE0, 0x01, 0x81, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, - 0x81, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, - 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, - 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0x7F, 0xFF, 0xFF, 0xFE, 0x07, - 0xE0, 0x3F, 0xFF, 0xFF, 0xFE, 0x07, 0xF0, 0x1F, 0xFF, 0xFF, 0xFC, 0x0F, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x0F, - 0xF8, 0x0F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFC, 0x07, 0xFF, 0xFF, 0xE0, 0x1F, 0xFC, 0x01, 0xFF, 0xFF, 0xC0, 0x3F, - 0xFE, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x00, 0x1F, 0xFC, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFF, - 0xFF, 0xE0, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, - 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x06, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xC0, 0x00, 0x7E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, + 0xFE, 0x00, 0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, + 0xF0, 0x03, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, + 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, + 0xC0, 0x1F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, + 0xC0, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, + 0x80, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, + 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, + 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, + 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, + 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, + 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, + 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, + 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, + 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, + 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, + 0xFF, 0xC0, 0x00, 0x7F, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_PARTLY_CLOUDY_DAY[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xF3, 0xFF, 0x7F, - 0xFF, 0xFF, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x9F, 0xFF, 0xF8, 0x7F, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, - 0xFF, 0xF8, 0x00, 0x1C, 0x1F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0x0F, 0xFF, 0xFF, 0xC1, 0xF0, 0x7F, 0x87, 0xFF, - 0xFF, 0x87, 0xFC, 0x3F, 0xC7, 0xFF, 0xFF, 0x8F, 0xFE, 0x3F, 0xE7, 0xFF, 0xFF, 0x1F, 0xFF, 0x1F, 0xE7, 0x83, - 0xFF, 0x1F, 0xFF, 0x1F, 0xE3, 0x81, 0xFE, 0x3F, 0xFF, 0x83, 0xE7, 0x83, 0xF8, 0x3F, 0xFF, 0x80, 0xE7, 0xFF, - 0xF0, 0x3F, 0xFF, 0x80, 0x07, 0xFF, 0xE1, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, - 0xC7, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, - 0x8F, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xCE, 0x7F, 0x8F, 0xFF, 0xFF, 0xFF, 0x8C, 0x3F, - 0xC7, 0xFF, 0xFF, 0xFF, 0x8E, 0x3F, 0xC7, 0xFF, 0xFF, 0xFF, 0x1F, 0x7F, 0xE1, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, - 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x03, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF9, 0xFF, 0xF8, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xF8, 0x7F, + 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, + 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x7F, 0xFF, + 0xFF, 0xF8, 0x00, 0x00, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x1F, 0xE0, 0x3F, 0xF8, 0x3F, 0xFF, + 0xFF, 0xE0, 0x7F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, + 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFE, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, + 0xFF, 0x83, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, 0xFF, 0x87, 0xFF, 0xFF, 0x87, 0xFE, 0x1E, 0x00, + 0xFE, 0x07, 0xFF, 0xFF, 0x80, 0x3E, 0x1F, 0xEF, 0xF8, 0x07, 0xFF, 0xFF, 0x80, 0x0C, 0x1F, 0xFF, + 0xF0, 0x07, 0xFF, 0xFF, 0x80, 0x04, 0x3F, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xDC, 0x00, 0x3F, 0xFF, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x71, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x70, 0xFF, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x70, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x7F, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFC, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_PARTLY_CLOUDY_NIGHT[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF8, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xF8, 0xFF, - 0xFF, 0xFE, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xF0, 0x3F, 0x03, 0xF8, 0x7F, - 0xFF, 0xF0, 0xFF, 0xC1, 0xFC, 0x3F, 0xFF, 0xE1, 0xFF, 0xF0, 0xFC, 0x1F, 0xFF, 0xC3, 0xFF, 0xF8, 0x7E, 0x07, - 0xFF, 0xC3, 0xFF, 0xF8, 0x7F, 0x01, 0xFF, 0xC7, 0xFF, 0xFC, 0x7F, 0x83, 0xFF, 0x87, 0xFF, 0xFC, 0x07, 0xC3, - 0xFE, 0x0F, 0xFF, 0xFC, 0x01, 0xC3, 0xF8, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xF0, 0x0F, 0xFF, 0xFE, 0x80, 0x07, - 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, - 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, - 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, - 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xF0, 0x1F, 0xFF, 0xFF, 0xE0, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0xFE, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x0F, 0x83, 0xFF, + 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0x83, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0x83, 0xFF, + 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xC1, 0xFF, + 0xFF, 0xFF, 0x00, 0x1C, 0x00, 0x7F, 0xC0, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0x80, 0x3F, 0xE0, 0xFF, + 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xE0, 0x7F, 0xFF, 0xFC, 0x0F, 0xFF, 0xF8, 0x1F, 0xF0, 0x3F, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFC, 0x0F, 0xF0, 0x0F, 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x0F, 0xF8, 0x01, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFE, 0x07, 0xFC, 0x00, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0x07, 0xFE, 0x00, + 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0x03, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xC1, + 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0x81, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0x03, + 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x07, + 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, + 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, + 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_OVERCAST[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, - 0xFF, 0xFF, 0xFE, 0x1F, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xC7, 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xE3, 0xFF, - 0xFF, 0xF0, 0x00, 0xFF, 0xE3, 0xFF, 0xFF, 0xE0, 0x60, 0x7F, 0xF0, 0xFF, 0xFF, 0xC3, 0xFC, 0x3F, 0xF0, 0x1F, - 0xFF, 0x87, 0xFE, 0x1F, 0xF0, 0x0F, 0xFF, 0x8F, 0xFF, 0x0F, 0xFF, 0x87, 0xFF, 0x1F, 0xFF, 0x8F, 0xFF, 0xE3, - 0xFF, 0x1F, 0xFF, 0x8F, 0xFF, 0xE3, 0xFC, 0x1F, 0xFF, 0xC0, 0x7F, 0xF1, 0xF8, 0x3F, 0xFF, 0xC0, 0x3F, 0xF1, - 0xE0, 0x7F, 0xFF, 0xFC, 0x1F, 0xF1, 0xE3, 0xFF, 0xFF, 0xFF, 0x0F, 0xF1, 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, - 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0x87, 0x8F, 0xFF, 0xFF, 0xFF, 0xE2, 0x0F, - 0x8F, 0xFF, 0xFF, 0xFF, 0xE2, 0x1F, 0x8F, 0xFF, 0xFF, 0xFF, 0xE3, 0x7F, 0x8F, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, - 0xC7, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, - 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0x3F, 0xFC, 0x1F, 0xFF, + 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFE, 0x0F, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x3F, 0xFF, 0x00, 0xFF, + 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x3F, 0xFC, 0x0F, 0xFF, 0x80, 0x0F, + 0xFF, 0xE0, 0xFF, 0xFE, 0x07, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x03, + 0xFF, 0xC1, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, + 0xFF, 0xC3, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xE1, 0xFE, 0x03, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, + 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xF0, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xF0, + 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xF0, + 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xE1, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xC1, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0x83, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1C, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x07, + 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x3F, + 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, + 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, + 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_FOG[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC1, 0xFF, 0xFF, - 0xFF, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFC, 0x7F, 0xFF, - 0xFF, 0xF8, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xF8, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFE, 0x01, 0xFF, - 0xFF, 0x80, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0x1F, 0xFF, 0xFF, 0xFC, 0x3F, - 0xFE, 0x3F, 0xFF, 0xFF, 0xFE, 0x1F, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, - 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x01, 0xFF, - 0xC0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x01, - 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFC, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, + 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, + 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, + 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, + 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, + 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_DRIZZLE[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xF8, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, - 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, - 0xFF, 0xC7, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x3F, - 0xF0, 0x07, 0xFF, 0xFF, 0x00, 0x0F, 0xF0, 0x7F, 0xFF, 0x9F, 0xFE, 0x0F, 0xE1, 0xFF, 0xFF, 0x8F, 0xFF, 0x87, - 0xC3, 0xFF, 0xFF, 0x0F, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0x0F, 0xFF, 0xC3, 0xC7, 0xFF, 0xBF, 0x9F, 0xFF, 0xE3, - 0x87, 0xFF, 0x1F, 0xFF, 0xFF, 0xE1, 0x87, 0xFE, 0x1F, 0xFF, 0xFF, 0xE1, 0x87, 0xFE, 0x0F, 0xFF, 0xFF, 0xE1, - 0x87, 0xFC, 0x0F, 0xFF, 0xFF, 0xE1, 0xC7, 0xFC, 0x0F, 0xFF, 0xFF, 0xE3, 0xC3, 0xFE, 0x0F, 0x9F, 0xFF, 0xC3, - 0xC3, 0xFF, 0x1F, 0x0F, 0xFF, 0xC3, 0xE1, 0xFF, 0xFE, 0x07, 0xFF, 0x87, 0xF0, 0x7F, 0xFE, 0x07, 0xFE, 0x0F, - 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x0F, 0xFC, 0x03, 0xF0, 0x3F, 0xFE, 0x0F, 0xF8, 0x01, 0xF0, 0x7F, - 0xFF, 0xCF, 0xF8, 0x01, 0xF3, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, + 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, + 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, + 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, + 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x0F, 0xFF, 0xFF, 0xCF, 0xFF, 0xE0, 0x0F, + 0xE0, 0x3F, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x03, + 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x81, + 0x83, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xC1, 0x83, 0xFF, 0xFC, 0xFF, 0x87, 0xFF, 0xFF, 0xC1, + 0x03, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, + 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, + 0x03, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, + 0x83, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xE0, 0x1F, 0xCF, 0xFF, 0xFF, 0xC1, + 0x81, 0xFF, 0xF0, 0x3F, 0x87, 0xFF, 0xFF, 0x81, 0xC0, 0xFF, 0xF8, 0x7F, 0x03, 0xFF, 0xFF, 0x03, + 0xC0, 0x7F, 0xFF, 0xFE, 0x03, 0xFF, 0xFE, 0x03, 0xE0, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x07, + 0xF0, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xF0, 0x0F, 0xF8, 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0x80, 0x0F, + 0xFC, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0x80, 0x3F, 0xFE, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0x80, 0x7F, + 0xFF, 0x81, 0xFF, 0xF0, 0x00, 0x3F, 0x80, 0xFF, 0xFF, 0xE1, 0xFF, 0xF0, 0x00, 0x3F, 0x87, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_RAIN[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xF8, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, - 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, - 0xFF, 0x87, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x3F, - 0xF0, 0x07, 0xFF, 0xFF, 0x00, 0x0F, 0xF0, 0x7F, 0xFF, 0xFF, 0xFE, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, - 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xDF, 0x9F, 0x3F, 0xE1, - 0x87, 0xFF, 0x8F, 0x1F, 0x1F, 0xE1, 0xC7, 0xFF, 0x8F, 0x0E, 0x1F, 0xE3, 0xC3, 0xFF, 0x0F, 0x1E, 0x1F, 0xC3, - 0xC1, 0xFF, 0x0E, 0x1E, 0x3F, 0x83, 0xE0, 0xFF, 0x1E, 0x1C, 0x3F, 0x87, 0xF0, 0x7E, 0x1E, 0x3C, 0x3E, 0x0F, - 0xF8, 0x0E, 0x1E, 0x3C, 0x70, 0x1F, 0xFC, 0x0E, 0x3C, 0x3C, 0x70, 0x3F, 0xFE, 0x0E, 0x3C, 0x38, 0x70, 0x7F, - 0xFF, 0xEC, 0x3C, 0x78, 0x7F, 0xFF, 0xFF, 0xFC, 0x38, 0x78, 0xFF, 0xFF, 0xFF, 0xFC, 0x78, 0x70, 0xFF, 0xFF, - 0xFF, 0xF8, 0x78, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0x78, 0xF1, 0xFF, 0xFF, 0xFF, 0xFC, 0xF0, 0xF1, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x0F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, + 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, + 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, + 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, + 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, + 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFC, 0x3F, 0x8F, 0xE1, 0xFF, 0xE0, + 0x03, 0xFF, 0xFC, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, 0x03, 0xFF, 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, + 0x83, 0xFF, 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC1, 0x81, 0xFF, 0xF8, 0x3E, 0x07, 0xC1, 0xFF, 0x81, + 0x80, 0xFF, 0xF8, 0x3E, 0x0F, 0x81, 0xFF, 0x01, 0xC0, 0xFF, 0xF0, 0x3E, 0x0F, 0x83, 0xFF, 0x03, + 0xE0, 0x3F, 0xF0, 0x3E, 0x0F, 0x83, 0xFC, 0x07, 0xE0, 0x1F, 0xF0, 0x7C, 0x0F, 0x03, 0xF8, 0x07, + 0xF0, 0x01, 0xE0, 0x7C, 0x1F, 0x07, 0x80, 0x0F, 0xF8, 0x01, 0xE0, 0x7C, 0x1F, 0x07, 0x80, 0x1F, + 0xFC, 0x01, 0xE0, 0xF8, 0x1F, 0x07, 0x80, 0x3F, 0xFF, 0x01, 0xE0, 0xF8, 0x1E, 0x07, 0x80, 0xFF, + 0xFF, 0xC1, 0xC0, 0xF8, 0x3E, 0x0F, 0x83, 0xFF, 0xFF, 0xFD, 0xC0, 0xF8, 0x3E, 0x0F, 0xBF, 0xFF, + 0xFF, 0xFF, 0xC1, 0xF0, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x81, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x83, 0xE0, 0x78, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0xF8, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x87, 0xE0, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_SNOW[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xF8, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x7F, 0xFF, - 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, - 0xFF, 0x87, 0xFF, 0xFF, 0x03, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x3F, - 0xF0, 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xF0, 0x7F, 0xFF, 0xFF, 0xFE, 0x0F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, - 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, - 0xC3, 0xFF, 0xFE, 0x3F, 0xFF, 0x83, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, 0x07, 0xF0, 0x7F, 0xFE, 0x7F, 0xFE, 0x0F, - 0xF0, 0x0F, 0x3F, 0xFC, 0xF0, 0x1F, 0xFC, 0x0E, 0x1F, 0xF8, 0x70, 0x3F, 0xFE, 0x0F, 0x1F, 0xFC, 0x70, 0x7F, - 0xFF, 0xEF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFC, 0xFF, 0xFF, - 0xFF, 0xFE, 0x1F, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFC, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, + 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, + 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, + 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, + 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, + 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, + 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0x81, + 0x81, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x03, + 0xC0, 0x7F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x07, 0xE0, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF8, 0x07, + 0xF0, 0x01, 0xFD, 0xFF, 0xFF, 0x9F, 0x80, 0x0F, 0xF8, 0x01, 0xF0, 0xFF, 0xFF, 0x0F, 0x80, 0x1F, + 0xFC, 0x01, 0xF0, 0x7F, 0xFE, 0x07, 0x80, 0x3F, 0xFE, 0x01, 0xF0, 0x7F, 0xFE, 0x0F, 0x80, 0xFF, + 0xFF, 0x81, 0xF0, 0xFF, 0xFF, 0x0F, 0x83, 0xFF, 0xFF, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_THUNDERSTORM[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xC0, 0xFF, 0xFF, - 0xFF, 0xF0, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE1, 0xFF, 0xFC, 0x3F, 0xFF, - 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0x01, 0xFF, - 0xFE, 0x07, 0xFF, 0xFF, 0x00, 0x7F, 0xF8, 0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x0F, - 0xE0, 0x7F, 0xFF, 0xFF, 0xFE, 0x07, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, - 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x87, 0xF8, 0x0F, 0x9F, 0xBF, 0xE1, 0x87, 0xF8, 0x1F, 0x0F, 0x1F, 0xE1, - 0xC7, 0xF0, 0x1F, 0x1E, 0x1F, 0xE3, 0xC3, 0xF0, 0x3F, 0x1E, 0x1F, 0xC3, 0xC1, 0xE0, 0x3E, 0x1E, 0x3F, 0x83, - 0xE0, 0xE0, 0x7E, 0x1E, 0x3F, 0x07, 0xF0, 0x60, 0x7E, 0x3C, 0x3E, 0x0F, 0xF8, 0x00, 0xFE, 0x3C, 0x30, 0x1F, - 0xFC, 0x00, 0xFC, 0x3C, 0x70, 0x3F, 0xFF, 0x01, 0xFC, 0x3C, 0x70, 0x7F, 0xFF, 0x81, 0xFC, 0x78, 0x7F, 0xFF, - 0xFF, 0x80, 0x1C, 0x78, 0x7F, 0xFF, 0xFF, 0x00, 0x18, 0x78, 0xFF, 0xFF, 0xFF, 0x00, 0x38, 0x78, 0xFF, 0xFF, - 0xFF, 0xF0, 0x78, 0xF0, 0xFF, 0xFF, 0xFF, 0xF8, 0x78, 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, - 0xFF, 0xF1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xF1, 0xFF, 0xFF, 0xFF, - 0xFF, 0xE7, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x80, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, + 0xFF, 0xFC, 0x07, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, + 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, + 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, + 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, + 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, + 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x03, 0xFF, 0x00, 0x3F, 0x87, 0xE1, 0xFF, 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0x07, 0xE1, 0xFF, 0xC0, + 0x03, 0xFE, 0x00, 0x7F, 0x07, 0xC0, 0xFF, 0xC0, 0x83, 0xFE, 0x00, 0x7F, 0x07, 0xC1, 0xFF, 0xC1, + 0x81, 0xFE, 0x00, 0xFE, 0x07, 0xC1, 0xFF, 0x81, 0xC0, 0xFC, 0x00, 0xFE, 0x0F, 0x81, 0xFF, 0x01, + 0xC0, 0x7C, 0x01, 0xFE, 0x0F, 0x81, 0xFE, 0x03, 0xE0, 0x3C, 0x01, 0xFE, 0x0F, 0x83, 0xFC, 0x07, + 0xE0, 0x08, 0x03, 0xFC, 0x0F, 0x83, 0xF0, 0x07, 0xF0, 0x00, 0x03, 0xFC, 0x1F, 0x03, 0x80, 0x0F, + 0xF8, 0x00, 0x07, 0xFC, 0x1F, 0x07, 0x80, 0x1F, 0xFC, 0x00, 0x07, 0xFC, 0x1F, 0x07, 0x80, 0x3F, + 0xFF, 0x00, 0x0F, 0xF8, 0x1F, 0x07, 0x80, 0xFF, 0xFF, 0xC0, 0x1F, 0xF8, 0x3E, 0x07, 0x83, 0xFF, + 0xFF, 0xE0, 0x1F, 0xF8, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x38, 0x3E, 0x0F, 0xFF, 0xFF, + 0xFF, 0xC0, 0x00, 0x70, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xF0, 0x7C, 0x0F, 0xFF, 0xFF, + 0xFF, 0x80, 0x00, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0x03, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFE, 0x3F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x3F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; + diff --git a/scripts/generate_weather_icons.py b/scripts/generate_weather_icons.py index c1ef2cb0..6a35637d 100644 --- a/scripts/generate_weather_icons.py +++ b/scripts/generate_weather_icons.py @@ -25,9 +25,11 @@ except Exception: cairosvg = None -SIZE = 48 -THRESHOLD = 128 -CONTENT_MARGIN = 1 +SIZE = 64 +# Higher threshold slightly thickens dark icon strokes after antialiasing. +THRESHOLD = 160 +# Keep zero margin so rendered glyphs can use the full 48x48 canvas. +CONTENT_MARGIN = 0 PROJECT_ROOT = Path(__file__).resolve().parent.parent DEFAULT_SVG_DIR = PROJECT_ROOT / "assets" / "weather-icons" / "svg" DEFAULT_OUT = PROJECT_ROOT / "lib" / "Weather" / "WeatherIcons48.h" diff --git a/src/activities/weather/WeatherActivity.cpp b/src/activities/weather/WeatherActivity.cpp index 5c4502cb..a261a36d 100644 --- a/src/activities/weather/WeatherActivity.cpp +++ b/src/activities/weather/WeatherActivity.cpp @@ -346,17 +346,17 @@ void WeatherActivity::render(RenderLock&&) { return; } - // DISPLAY state - Landscape layout (800x480) + // DISPLAY state - Landscape layout (800x480 logical) // // +------------------+------------------------------------+ // | Current weather | Daily forecast cards | // | (icon, temp, | [Day1] [Day2] [Day3] ... | // | details) | | - // | ~200px wide | ~600px wide | + // | leftPanelWidth=220| right panel = contentWidth-220 | // +------------------+------------------------------------+ // | | - // | 48-hour temperature + precipitation graph | - // | ~full width, ~200px high | + // | hourly temperature + precipitation graph | + // | height = graphHeight (currently 240) | // +--------------------------------------------------------+ // | [Back] [Settings] [Refresh] Button hints | // +--------------------------------------------------------+ @@ -415,11 +415,11 @@ void WeatherActivity::renderCurrentConditions(int x, int y, int w, int h) { textY += 15; } - // Weather icon (48x48) + // Weather icon (WEATHER_ICON_LARGE x WEATHER_ICON_LARGE) auto iconType = getWeatherIconType(cur.weatherCode, cur.isDay); const uint8_t* icon = getWeatherIconLarge(iconType); int iconX = x + (w - WEATHER_ICON_LARGE) / 2; - drawWeatherIconWithOrientation(renderer, icon, iconX, textY, WEATHER_ICON_LARGE); + drawWeatherIconWithOrientation(renderer, icon, iconX, textY + 2, WEATHER_ICON_LARGE); textY += WEATHER_ICON_LARGE + 5; // Temperature (large) @@ -533,7 +533,7 @@ void WeatherActivity::renderDailyForecast(int x, int y, int w, int h) { renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - dateWidth) / 2, textY, dateBuf); textY += 18; - // Weather icon (48x48 for all days) + // Weather icon (WEATHER_ICON_LARGE for all days) auto iconType = getWeatherIconType(day.weatherCode, true); const uint8_t* icon = getWeatherIconLarge(iconType); int iconX = cardX + (cardWidth - WEATHER_ICON_LARGE) / 2; From d7bd88bec776f028395c551c91fcbc5b161cf908 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 2 Apr 2026 18:30:31 +0200 Subject: [PATCH 5/5] clang-format agnostic --- lib/Weather/WeatherIcons48.h | 592 ++++++++++++++---------------- scripts/generate_weather_icons.py | 1 + 2 files changed, 272 insertions(+), 321 deletions(-) diff --git a/lib/Weather/WeatherIcons48.h b/lib/Weather/WeatherIcons48.h index f5b0aa03..232fbfef 100644 --- a/lib/Weather/WeatherIcons48.h +++ b/lib/Weather/WeatherIcons48.h @@ -4,354 +4,304 @@ // Generated from erikflowers/weather-icons SVGs. // 48x48, 1-bit, MSB-first, row-major. // Regenerate with: python scripts/generate_weather_icons.py --fetch +// clang-format off static const uint8_t WI48_CLEAR_DAY[] = { - 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x3F, 0xFF, 0xE1, 0xFF, - 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, - 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, - 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, - 0xFF, 0xF8, 0x3F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x80, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, - 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, - 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xC0, 0x3F, 0x83, 0xFF, 0xFF, 0xC1, 0xFC, 0x03, - 0x80, 0x1F, 0x03, 0xFF, 0xFF, 0xE1, 0xF8, 0x01, 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, - 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, 0x80, 0x1F, 0x03, 0xFF, 0xFF, 0xE1, 0xF8, 0x01, - 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, - 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, - 0xFF, 0xFF, 0xE0, 0x3F, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0x01, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFE, 0x7F, 0xFC, 0x1F, 0xFF, - 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, - 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, - 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, - 0xFF, 0xC7, 0xFF, 0xFC, 0x3F, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, + 0x3F, 0xFF, 0xE1, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, + 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xF8, 0x3F, 0xF8, 0x3F, + 0xFC, 0x1F, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x01, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0xFC, 0x07, + 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, + 0xFF, 0xC1, 0xFF, 0xFF, 0xC0, 0x3F, 0x83, 0xFF, 0xFF, 0xC1, 0xFC, 0x03, 0x80, 0x1F, 0x03, 0xFF, 0xFF, 0xE1, 0xF8, + 0x01, 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, 0x00, 0x1F, 0x07, 0xFF, 0xFF, 0xE0, 0xF8, 0x00, 0x80, 0x1F, + 0x03, 0xFF, 0xFF, 0xE1, 0xF8, 0x01, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, + 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x3F, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x80, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xC0, 0x03, 0xFE, 0x3F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFE, + 0x7F, 0xFC, 0x1F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xFF, 0xFC, 0x07, + 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0x83, 0xFF, 0xFC, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x3F, + 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_CLEAR_NIGHT[] = { - 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, - 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x06, 0x03, 0xFF, 0xFF, 0xFF, - 0xFF, 0xC0, 0x00, 0x7E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, - 0xFF, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, - 0xFE, 0x00, 0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, - 0xF0, 0x03, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, - 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, - 0xC0, 0x1F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, - 0xC0, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, - 0x80, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, - 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01, - 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, - 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, - 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, - 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, - 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, - 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, - 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, - 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, - 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, - 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, - 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, - 0xFF, 0xC0, 0x00, 0x7F, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xFF, - 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, - 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE0, 0x00, 0x06, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x7E, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFE, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x07, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0x01, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, + 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0x80, + 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, + 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, + 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, + 0x7F, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0x80, + 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, + 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x01, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, + 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0x80, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x03, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x07, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xE0, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xF0, 0x03, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, + 0x3F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0x00, + 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFE, + 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_PARTLY_CLOUDY_DAY[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF9, 0xFF, 0xF8, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xF8, 0x7F, - 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, - 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x7F, 0xFF, - 0xFF, 0xF8, 0x00, 0x00, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x1F, 0xE0, 0x3F, 0xF8, 0x3F, 0xFF, - 0xFF, 0xE0, 0x7F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, - 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFE, 0x1F, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, - 0xFF, 0x83, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, 0xFF, 0x87, 0xFF, 0xFF, 0x87, 0xFE, 0x1E, 0x00, - 0xFE, 0x07, 0xFF, 0xFF, 0x80, 0x3E, 0x1F, 0xEF, 0xF8, 0x07, 0xFF, 0xFF, 0x80, 0x0C, 0x1F, 0xFF, - 0xF0, 0x07, 0xFF, 0xFF, 0x80, 0x04, 0x3F, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xDC, 0x00, 0x3F, 0xFF, - 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, - 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, - 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, - 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x71, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x70, 0xFF, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x70, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x7F, - 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFC, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, - 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, - 0xF8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, - 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF9, 0xFF, 0xF8, 0x7F, 0xFC, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, + 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xC0, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0x01, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xE0, 0x7F, 0xFF, 0xFF, + 0xF8, 0x00, 0x00, 0x7F, 0xF0, 0x7F, 0xFF, 0xFF, 0xF0, 0x1F, 0xE0, 0x3F, 0xF8, 0x3F, 0xFF, 0xFF, 0xE0, 0x7F, 0xF8, + 0x3F, 0xFC, 0x3F, 0xFF, 0xFF, 0xE0, 0xFF, 0xFC, 0x1F, 0xFC, 0x1F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFE, 0x0F, 0xFE, 0x1F, + 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, 0xFF, 0x83, 0xFF, 0xFF, 0x0F, 0xFE, 0x1E, 0x00, 0xFF, 0x87, + 0xFF, 0xFF, 0x87, 0xFE, 0x1E, 0x00, 0xFE, 0x07, 0xFF, 0xFF, 0x80, 0x3E, 0x1F, 0xEF, 0xF8, 0x07, 0xFF, 0xFF, 0x80, + 0x0C, 0x1F, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0x80, 0x04, 0x3F, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xDC, 0x00, 0x3F, 0xFF, + 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0x83, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, + 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x71, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF8, 0x70, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x70, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, + 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFC, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xE0, 0x7F, + 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_PARTLY_CLOUDY_NIGHT[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x0F, 0x83, 0xFF, - 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0x83, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0x83, 0xFF, - 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xC1, 0xFF, - 0xFF, 0xFF, 0x00, 0x1C, 0x00, 0x7F, 0xC0, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0x80, 0x3F, 0xE0, 0xFF, - 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xE0, 0x7F, 0xFF, 0xFC, 0x0F, 0xFF, 0xF8, 0x1F, 0xF0, 0x3F, - 0xFF, 0xF8, 0x1F, 0xFF, 0xFC, 0x0F, 0xF0, 0x0F, 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x0F, 0xF8, 0x01, - 0xFF, 0xF0, 0x3F, 0xFF, 0xFE, 0x07, 0xFC, 0x00, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0x07, 0xFE, 0x00, - 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0x03, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xC1, - 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0x81, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0x03, - 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x07, - 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, - 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, - 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, - 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, - 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, - 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, - 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, - 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, - 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, + 0xFC, 0x00, 0x00, 0x3F, 0x83, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, + 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x00, 0x1C, 0x00, 0x7F, 0xC0, 0xFF, + 0xFF, 0xFE, 0x01, 0xFF, 0x80, 0x3F, 0xE0, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xE0, 0x1F, 0xE0, 0x7F, 0xFF, 0xFC, 0x0F, + 0xFF, 0xF8, 0x1F, 0xF0, 0x3F, 0xFF, 0xF8, 0x1F, 0xFF, 0xFC, 0x0F, 0xF0, 0x0F, 0xFF, 0xF8, 0x3F, 0xFF, 0xFC, 0x0F, + 0xF8, 0x01, 0xFF, 0xF0, 0x3F, 0xFF, 0xFE, 0x07, 0xFC, 0x00, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0x07, 0xFE, 0x00, 0xFF, + 0xE0, 0x7F, 0xFF, 0xFF, 0x03, 0xFF, 0x81, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, 0xC1, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0x80, 0x07, 0x81, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0x03, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, + 0x03, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x07, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xC0, 0x7F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x07, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x83, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xC1, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x1F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x3F, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_OVERCAST[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0x3F, 0xFC, 0x1F, 0xFF, - 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFE, 0x0F, 0xFF, - 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x3F, 0xFF, 0x00, 0xFF, - 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x3F, 0xFC, 0x0F, 0xFF, 0x80, 0x0F, - 0xFF, 0xE0, 0xFF, 0xFE, 0x07, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x03, - 0xFF, 0xC1, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, - 0xFF, 0xC3, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xE1, 0xFE, 0x03, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, - 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xF0, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xF0, - 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xF0, - 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xE1, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xC1, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0x83, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1C, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x07, - 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x3F, - 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, - 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, - 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, - 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, - 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, - 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x03, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, + 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0x3F, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFE, 0x0F, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, + 0x80, 0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x3F, 0xFC, 0x0F, 0xFF, + 0x80, 0x0F, 0xFF, 0xE0, 0xFF, 0xFE, 0x07, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x03, 0xFF, + 0xC1, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xC3, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xC1, 0xFF, 0xC3, 0xFF, 0xFF, + 0xC0, 0xFF, 0xFF, 0xE1, 0xFE, 0x03, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xE0, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, + 0xF0, 0xF0, 0x07, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xF0, 0xE0, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xF0, 0xC0, 0x7F, + 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xF0, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xE0, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF8, 0x3F, 0xE1, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xC1, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0x83, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1C, 0x03, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x07, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0x18, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x18, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0x18, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0x83, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0, 0x7F, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_FOG[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFC, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, - 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, - 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, - 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, - 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, - 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, - 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, - 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFC, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFE, + 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, + 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, + 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xC0, + 0x01, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, + 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFC, 0x1F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_DRIZZLE[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, - 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, - 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, - 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, - 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, - 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, - 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x0F, 0xFF, 0xFF, 0xCF, 0xFF, 0xE0, 0x0F, - 0xE0, 0x3F, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x03, - 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x81, - 0x83, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xC1, 0x83, 0xFF, 0xFC, 0xFF, 0x87, 0xFF, 0xFF, 0xC1, - 0x03, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, - 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, - 0x03, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, - 0x83, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xE0, 0x1F, 0xCF, 0xFF, 0xFF, 0xC1, - 0x81, 0xFF, 0xF0, 0x3F, 0x87, 0xFF, 0xFF, 0x81, 0xC0, 0xFF, 0xF8, 0x7F, 0x03, 0xFF, 0xFF, 0x03, - 0xC0, 0x7F, 0xFF, 0xFE, 0x03, 0xFF, 0xFE, 0x03, 0xE0, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x07, - 0xF0, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xF0, 0x0F, 0xF8, 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0x80, 0x0F, - 0xFC, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0x80, 0x3F, 0xFE, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0x80, 0x7F, - 0xFF, 0x81, 0xFF, 0xF0, 0x00, 0x3F, 0x80, 0xFF, 0xFF, 0xE1, 0xFF, 0xF0, 0x00, 0x3F, 0x87, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, + 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, + 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, + 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0x80, 0x7F, + 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, + 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x0F, 0xFF, 0xFF, 0xCF, 0xFF, 0xE0, 0x0F, 0xE0, + 0x3F, 0xFF, 0xFF, 0x87, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0x87, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, + 0x03, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x81, 0x83, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, + 0xC1, 0x83, 0xFF, 0xFC, 0xFF, 0x87, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, + 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xE0, 0x1F, 0xFF, + 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, + 0x83, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xE0, 0x1F, 0xCF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xF0, + 0x3F, 0x87, 0xFF, 0xFF, 0x81, 0xC0, 0xFF, 0xF8, 0x7F, 0x03, 0xFF, 0xFF, 0x03, 0xC0, 0x7F, 0xFF, 0xFE, 0x03, 0xFF, + 0xFE, 0x03, 0xE0, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xFC, 0x07, 0xF0, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xF0, 0x0F, 0xF8, + 0x01, 0xFF, 0xF8, 0x00, 0xFF, 0x80, 0x0F, 0xFC, 0x01, 0xFF, 0xF0, 0x00, 0x7F, 0x80, 0x3F, 0xFE, 0x01, 0xFF, 0xF0, + 0x00, 0x7F, 0x80, 0x7F, 0xFF, 0x81, 0xFF, 0xF0, 0x00, 0x3F, 0x80, 0xFF, 0xFF, 0xE1, 0xFF, 0xF0, 0x00, 0x3F, 0x87, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_RAIN[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x0F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, - 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, - 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, - 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, - 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, - 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, - 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, - 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, - 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, - 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, - 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFC, 0x3F, 0x8F, 0xE1, 0xFF, 0xE0, - 0x03, 0xFF, 0xFC, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, 0x03, 0xFF, 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, - 0x83, 0xFF, 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC1, 0x81, 0xFF, 0xF8, 0x3E, 0x07, 0xC1, 0xFF, 0x81, - 0x80, 0xFF, 0xF8, 0x3E, 0x0F, 0x81, 0xFF, 0x01, 0xC0, 0xFF, 0xF0, 0x3E, 0x0F, 0x83, 0xFF, 0x03, - 0xE0, 0x3F, 0xF0, 0x3E, 0x0F, 0x83, 0xFC, 0x07, 0xE0, 0x1F, 0xF0, 0x7C, 0x0F, 0x03, 0xF8, 0x07, - 0xF0, 0x01, 0xE0, 0x7C, 0x1F, 0x07, 0x80, 0x0F, 0xF8, 0x01, 0xE0, 0x7C, 0x1F, 0x07, 0x80, 0x1F, - 0xFC, 0x01, 0xE0, 0xF8, 0x1F, 0x07, 0x80, 0x3F, 0xFF, 0x01, 0xE0, 0xF8, 0x1E, 0x07, 0x80, 0xFF, - 0xFF, 0xC1, 0xC0, 0xF8, 0x3E, 0x0F, 0x83, 0xFF, 0xFF, 0xFD, 0xC0, 0xF8, 0x3E, 0x0F, 0xBF, 0xFF, - 0xFF, 0xFF, 0xC1, 0xF0, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x81, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x83, 0xE0, 0x78, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0xF8, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x87, 0xE0, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, + 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, + 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, + 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, + 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE0, 0x07, 0xFF, 0xFC, 0x3F, 0x8F, 0xE1, 0xFF, 0xE0, 0x03, 0xFF, 0xFC, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, 0x03, 0xFF, + 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC0, 0x83, 0xFF, 0xF8, 0x1F, 0x07, 0xC1, 0xFF, 0xC1, 0x81, 0xFF, 0xF8, 0x3E, 0x07, + 0xC1, 0xFF, 0x81, 0x80, 0xFF, 0xF8, 0x3E, 0x0F, 0x81, 0xFF, 0x01, 0xC0, 0xFF, 0xF0, 0x3E, 0x0F, 0x83, 0xFF, 0x03, + 0xE0, 0x3F, 0xF0, 0x3E, 0x0F, 0x83, 0xFC, 0x07, 0xE0, 0x1F, 0xF0, 0x7C, 0x0F, 0x03, 0xF8, 0x07, 0xF0, 0x01, 0xE0, + 0x7C, 0x1F, 0x07, 0x80, 0x0F, 0xF8, 0x01, 0xE0, 0x7C, 0x1F, 0x07, 0x80, 0x1F, 0xFC, 0x01, 0xE0, 0xF8, 0x1F, 0x07, + 0x80, 0x3F, 0xFF, 0x01, 0xE0, 0xF8, 0x1E, 0x07, 0x80, 0xFF, 0xFF, 0xC1, 0xC0, 0xF8, 0x3E, 0x0F, 0x83, 0xFF, 0xFF, + 0xFD, 0xC0, 0xF8, 0x3E, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, + 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0x7C, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0x78, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xE0, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, + 0x87, 0xE0, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_SNOW[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFF, - 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, - 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, - 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, - 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, - 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, - 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, - 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, - 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, - 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, - 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0x81, - 0x81, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x03, - 0xC0, 0x7F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x07, 0xE0, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF8, 0x07, - 0xF0, 0x01, 0xFD, 0xFF, 0xFF, 0x9F, 0x80, 0x0F, 0xF8, 0x01, 0xF0, 0xFF, 0xFF, 0x0F, 0x80, 0x1F, - 0xFC, 0x01, 0xF0, 0x7F, 0xFE, 0x07, 0x80, 0x3F, 0xFE, 0x01, 0xF0, 0x7F, 0xFE, 0x0F, 0x80, 0xFF, - 0xFF, 0x81, 0xF0, 0xFF, 0xFF, 0x0F, 0x83, 0xFF, 0xFF, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, + 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0xF0, + 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, + 0xC0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, + 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xE0, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x81, 0xFF, 0xFF, 0xFE, 0x7F, + 0xFF, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0x03, + 0xC0, 0x7F, 0xFF, 0xFC, 0x1F, 0xFF, 0xFC, 0x07, 0xE0, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xF8, 0x07, 0xF0, 0x01, 0xFD, + 0xFF, 0xFF, 0x9F, 0x80, 0x0F, 0xF8, 0x01, 0xF0, 0xFF, 0xFF, 0x0F, 0x80, 0x1F, 0xFC, 0x01, 0xF0, 0x7F, 0xFE, 0x07, + 0x80, 0x3F, 0xFE, 0x01, 0xF0, 0x7F, 0xFE, 0x0F, 0x80, 0xFF, 0xFF, 0x81, 0xF0, 0xFF, 0xFF, 0x0F, 0x83, 0xFF, 0xFF, + 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x7F, 0xFE, + 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; static const uint8_t WI48_THUNDERSTORM[] = { - 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, - 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x80, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, - 0xFF, 0xFC, 0x07, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, - 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, - 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, - 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, - 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, - 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, - 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, - 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, - 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, - 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, - 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, - 0x03, 0xFF, 0x00, 0x3F, 0x87, 0xE1, 0xFF, 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0x07, 0xE1, 0xFF, 0xC0, - 0x03, 0xFE, 0x00, 0x7F, 0x07, 0xC0, 0xFF, 0xC0, 0x83, 0xFE, 0x00, 0x7F, 0x07, 0xC1, 0xFF, 0xC1, - 0x81, 0xFE, 0x00, 0xFE, 0x07, 0xC1, 0xFF, 0x81, 0xC0, 0xFC, 0x00, 0xFE, 0x0F, 0x81, 0xFF, 0x01, - 0xC0, 0x7C, 0x01, 0xFE, 0x0F, 0x81, 0xFE, 0x03, 0xE0, 0x3C, 0x01, 0xFE, 0x0F, 0x83, 0xFC, 0x07, - 0xE0, 0x08, 0x03, 0xFC, 0x0F, 0x83, 0xF0, 0x07, 0xF0, 0x00, 0x03, 0xFC, 0x1F, 0x03, 0x80, 0x0F, - 0xF8, 0x00, 0x07, 0xFC, 0x1F, 0x07, 0x80, 0x1F, 0xFC, 0x00, 0x07, 0xFC, 0x1F, 0x07, 0x80, 0x3F, - 0xFF, 0x00, 0x0F, 0xF8, 0x1F, 0x07, 0x80, 0xFF, 0xFF, 0xC0, 0x1F, 0xF8, 0x3E, 0x07, 0x83, 0xFF, - 0xFF, 0xE0, 0x1F, 0xF8, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x38, 0x3E, 0x0F, 0xFF, 0xFF, - 0xFF, 0xC0, 0x00, 0x70, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xF0, 0x7C, 0x0F, 0xFF, 0xFF, - 0xFF, 0x80, 0x00, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, - 0xFF, 0xFF, 0x03, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFE, 0x3F, 0xFF, 0xFF, - 0xFF, 0xFE, 0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFE, 0x3F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFC, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFC, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x1F, 0x80, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, + 0xFE, 0x01, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x0F, 0xFF, + 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0x81, 0xFF, + 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, + 0x7F, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, + 0xE0, 0x00, 0x7F, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x1F, + 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xC0, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0x87, 0xE1, 0xFF, + 0xE0, 0x03, 0xFF, 0x00, 0x3F, 0x07, 0xE1, 0xFF, 0xC0, 0x03, 0xFE, 0x00, 0x7F, 0x07, 0xC0, 0xFF, 0xC0, 0x83, 0xFE, + 0x00, 0x7F, 0x07, 0xC1, 0xFF, 0xC1, 0x81, 0xFE, 0x00, 0xFE, 0x07, 0xC1, 0xFF, 0x81, 0xC0, 0xFC, 0x00, 0xFE, 0x0F, + 0x81, 0xFF, 0x01, 0xC0, 0x7C, 0x01, 0xFE, 0x0F, 0x81, 0xFE, 0x03, 0xE0, 0x3C, 0x01, 0xFE, 0x0F, 0x83, 0xFC, 0x07, + 0xE0, 0x08, 0x03, 0xFC, 0x0F, 0x83, 0xF0, 0x07, 0xF0, 0x00, 0x03, 0xFC, 0x1F, 0x03, 0x80, 0x0F, 0xF8, 0x00, 0x07, + 0xFC, 0x1F, 0x07, 0x80, 0x1F, 0xFC, 0x00, 0x07, 0xFC, 0x1F, 0x07, 0x80, 0x3F, 0xFF, 0x00, 0x0F, 0xF8, 0x1F, 0x07, + 0x80, 0xFF, 0xFF, 0xC0, 0x1F, 0xF8, 0x3E, 0x07, 0x83, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, + 0xC0, 0x00, 0x38, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x70, 0x3E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0xF0, + 0x7C, 0x0F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xF0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xE0, 0x7C, 0x1F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE0, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, + 0x07, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xC0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0x3F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, + 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xEF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; - diff --git a/scripts/generate_weather_icons.py b/scripts/generate_weather_icons.py index 6a35637d..51a9b292 100644 --- a/scripts/generate_weather_icons.py +++ b/scripts/generate_weather_icons.py @@ -244,6 +244,7 @@ def generate(svg_dir, output_path, fetch): "// Generated from erikflowers/weather-icons SVGs.", "// 48x48, 1-bit, MSB-first, row-major.", "// Regenerate with: python scripts/generate_weather_icons.py --fetch", + "// clang-format off", "", ] header.extend(arrays)