diff --git a/.gitignore b/.gitignore index 127d4495..cc2655e9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ assets tools/*.raw tools/*.png +features.json +tile_generator.py +tile_viewer.py diff --git a/README.md b/README.md index 1139f5f1..e0d78637 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ESP32 Based GPS Navigator (LVGL - LovyanGFX). ## Screenshots -||||| +||||| |:-:|:-:|:-:|:-:|
See more... @@ -148,43 +148,29 @@ On SD Card map tiles (256x256 PNG Format) should be stored, in these folders str |__________________ [ πŸ“ tile X folder (number) ] |_______________________ πŸ—ΊοΈ tile Y file.png -## SD Vectorized Map File structure +## SD Vectorized Map File structure -Using [OSM_Extract](https://github.com/aresta/OSM_Extract) you can generate binary map files to later create vector maps. Once generated, these files should be saved in the `mymap` folder on the SD card. +Vectorized maps for IceNav can be generated using the Tile-Generator utility, which is available on GitHub at [jgauchia/Tile-Generator](https://github.com/jgauchia/Tile-Generator). This script allows you to convert map data into the required vector tile format compatible with IceNav. Please refer to the Tile-Generator repository for detailed instructions and usage examples on generating and preparing your own vector map files. -The PBF files can be downloaded from the [geofabrik](https://download.geofabrik.de/) website. + [ πŸ“ VECTMAP ] + |________ [ πŸ“ zoom folder (number) ] + |__________________ [ πŸ“ tile X folder (number) ] + |_______________________ πŸ—ΊοΈ tile Y file.bin -The PBF files should be saved in the `pbf` directory. Once saved, you should select the region or boundaries for which the GeoJSON files will be generated. +## Mass Copy Script for Map Tiles -To obtain the boundaries use `osmconvert file.pbf --out-statistics`: -``` -lon min: -5.5203154 -lon max: 11.7825360 -lat min: 35.2703341 -lat max: 44.4078541 -``` - -or use [Bboxfinder](http://bboxfinder.com/) website drawing a box on desired area. - - -For generate GeoJSON files run inside `scripts` directory: +For efficient transfer of millions of map tiles to SD cards or external storage devices, IceNav includes a high-performance mass copy script. This script is optimized for copying large numbers of small files (such as map tiles) and can reduce transfer time from hours to minutes. -```bash -min_lon=123 -min_lat=123 -max_lon=123 -max_lat=123 - -./pbf_to_geojson.sh $min_lon $min_lat $max_lon $max_lat /pbf/clipped.pbf /maps/test -``` +**Key features:** +- βœ… **Optimized for millions of small files** - Much faster than traditional copy methods +- βœ… **Real-time progress monitoring** - See exactly what's being copied +- βœ… **Resumable transfers** - Continue interrupted copies +- βœ… **Built-in integrity verification** - Sample file verification +- βœ… **Performance metrics** - Speed and time statistics -For generate binary map files run inside `scripts` directory. -```bash -./extract_features.py $min_lon $min_lat $max_lon $max_lat /maps/test -``` -Once the process is completed, the maps will be inside the `maps/mymap` directory. Copy all folders to the SD card except the `test_imgs` directory into `VECTMAP` folder of the SD Card. +For detailed instructions on how to use the mass copy script, please refer to the [Mass Copy Tools Documentation](tools/mass_copy/README.md). -Please follow the instructions provided by [OSM_Extract](https://github.com/aresta/OSM_Extract) for any further questions. +Download link: [tools/mass_copy/rsync_copy.sh](tools/mass_copy/rsync_copy.sh) ## Firmware install @@ -244,7 +230,7 @@ Please follow the instructions provided by [OSM_Extract](https://github.com/ares ## CLI -IceNav has a basic CLI accessible via Serial and optionally via Telnet if enabled. When you access the CLI and type `help`, you should see the following commands: +IceNav has a basic CLI accessible via Serial and optionally via Telnet if enabled (port 11000). When you access the CLI and type `help`, you should see the following commands: ```bash clear: clear shell @@ -268,12 +254,13 @@ Some extra details: KEYNAME DEFINED VALUE ======= ======= ===== defZoom custom 17 Default zoom - fullScrMap custom true Fullscreen map vectMap custom false Vectorized map mapSpeed custom true Show speed meter in map mapScale custom true Show scale meter in map mapComp custom true Show compass in map mapCompRot custom true Rotate map with the compass + simNav custom false Indicates whether navigation simulation mode is enabled or disabled + fillPoly custom true Fill polygons in vectorized map gpsTX custom 43 GPS Tx gpio gpsRX custom 44 GPS Rx gpio defLAT custom 52.5200 Default latitude @@ -338,8 +325,8 @@ To access the Web File Server, simply use any browser and go to the following ad - [ ] Multiple IMU's and Compass module implementation - [X] Power saving - [X] Vector maps -- [ ] Google Maps navigation style (turn by turn) -- [x] Optimize code +- [X] Google Maps navigation style (turn by turn) +- [X] Optimize code - [X] Fix bugs! - [X] Web file server @@ -360,8 +347,6 @@ To access the Web File Server, simply use any browser and go to the following ad * Improved auto mainScreen selection from env variable preset thanks to [@hpsaturn](https://github.com/hpsaturn) * Improved getLat getLon from environment variables thanks to [@hpsaturn](https://github.com/hpsaturn) * 3DPrint case for an ESP32S3 Makerfabs Parallel board thanks to [@hpsaturn](https://github.com/hpsaturn) -* Vectorial Maps routines [ESP32_GPS](https://github.com/aresta/ESP32_GPS) thanks to [@aresta](https://github.com/aresta) -* OSM to binary vectorial maps [OSM_Extract](https://github.com/aresta/OSM_Extract) thanks to [@aresta](https://github.com/aresta) * Preferences Library [Easy Preferences](https://github.com/hpsaturn/easy-preferences) thanks to [@hpsaturn](https://github.com/hpsaturn) * Wifi CLI manager [esp32-wifi-cli](https://github.com/hpsaturn/esp32-wifi-cli) thanks to [@hpsaturn](https://github.com/hpsaturn) * Web file server based in [@smford](https://github.com/smford) [esp32-asyncwebserver-fileupload-example](https://github.com/smford/esp32-asyncwebserver-fileupload-example) diff --git a/data/TODO.png b/data/TODO.png deleted file mode 100644 index 2da8ccc3..00000000 Binary files a/data/TODO.png and /dev/null differ diff --git a/data/mapspeed.bin b/data/mapspeed.bin new file mode 100755 index 00000000..844eca46 Binary files /dev/null and b/data/mapspeed.bin differ diff --git a/data/navarrow.bin b/data/navarrow.bin new file mode 100755 index 00000000..677d2b09 Binary files /dev/null and b/data/navarrow.bin differ diff --git a/data/zoom.bin b/data/zoom.bin new file mode 100755 index 00000000..b1e67c12 Binary files /dev/null and b/data/zoom.bin differ diff --git a/data/zoomin.bin b/data/zoomin.bin new file mode 100644 index 00000000..46c460bc Binary files /dev/null and b/data/zoomin.bin differ diff --git a/data/zoomout.bin b/data/zoomout.bin new file mode 100644 index 00000000..58edd518 Binary files /dev/null and b/data/zoomout.bin differ diff --git a/images/dev/mapnav.jpg b/images/dev/mapnav.jpg new file mode 100644 index 00000000..1b638852 Binary files /dev/null and b/images/dev/mapnav.jpg differ diff --git a/images/dev/mapsettings.jpg b/images/dev/mapsettings.jpg index 54df554a..5d739f37 100644 Binary files a/images/dev/mapsettings.jpg and b/images/dev/mapsettings.jpg differ diff --git a/images/dev/rendermap.jpg b/images/dev/rendermap.jpg index 8ffe6743..c848b191 100644 Binary files a/images/dev/rendermap.jpg and b/images/dev/rendermap.jpg differ diff --git a/images/dev/vectormap.jpg b/images/dev/vectormap.jpg index 7d4d2ca6..ea60b312 100644 Binary files a/images/dev/vectormap.jpg and b/images/dev/vectormap.jpg differ diff --git a/include/hal.hpp b/include/hal.hpp index df4bfc4a..829277db 100644 --- a/include/hal.hpp +++ b/include/hal.hpp @@ -2,8 +2,8 @@ * @file hal.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Boards Pin definitions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -122,7 +122,7 @@ extern const uint8_t TCH_I2C_PORT = 0; extern const uint8_t TCH_I2C_SDA = GPIO_NUM_38; extern const uint8_t TCH_I2C_SCL = GPIO_NUM_39; - extern const uint8_t TCH_I2C_INT = GPIO_NUM_40; + extern const uint8_t TCH_I2C_INT = -1; extern const uint8_t SD_CS = GPIO_NUM_4; extern const uint8_t SD_MISO = GPIO_NUM_19; diff --git a/lib/battery/battery.cpp b/lib/battery/battery.cpp index 7ab7cbd2..58c2e8c0 100644 --- a/lib/battery/battery.cpp +++ b/lib/battery/battery.cpp @@ -2,82 +2,88 @@ * @file battery.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Battery monitor definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ - #include "battery.hpp" /** - * @brief Battery Class constructor - * + * @brief Constructs a Battery monitoring object for voltage measurement and percentage calculation. */ Battery::Battery() {} /** - * @brief Configure ADC Channel for battery reading + * @brief Initializes the ADC channel(s) required for battery voltage measurement. * + * @details Configures the hardware ADC based on the ESP32 chip (ADC1 or ADC2). */ void Battery::initADC() { #ifdef ADC1 - adc1_config_width(ADC_WIDTH_BIT_12); - adc1_config_channel_atten(BATT_PIN, ADC_ATTEN_DB_12); + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(BATT_PIN, ADC_ATTEN_DB_12); #endif #ifdef ADC2 - adc2_config_channel_atten(BATT_PIN, ADC_ATTEN_DB_12); + adc2_config_channel_atten(BATT_PIN, ADC_ATTEN_DB_12); #endif } /** - * @brief Set battery voltage levels + * @brief Sets the maximum and minimum voltage levels for battery charge calculation. * - * @param maxVoltage -> Full Charge voltage - * @param minVoltage -> Min Charge voltage + * @param maxVoltage Voltage considered as fully charged. + * @param minVoltage Voltage considered as minimum safe level. */ void Battery::setBatteryLevels(float maxVoltage, float minVoltage) { - batteryMax = maxVoltage; - batteryMin = minVoltage; + batteryMax = maxVoltage; + batteryMin = minVoltage; } /** - * @brief Read battery charge and return %. + * @brief Reads and computes the current battery charge as a percentage. + * + * @details Takes 100 ADC samples, averages them, compensates for the voltage divider, and calculates the charge percentage. + * Returns a value between 0 and 100 (values above 160 are treated as 0). * - * @return float -> % Charge + * @return float Percentage of battery charge (0–100% typically). */ float Battery::readBattery() { - long sum = 0; // Sum of samples taken - float voltage = 0.0; // Calculated voltage - float output = 0.0; // Output value + int32_t sum = 0; /**< Accumulator for ADC samples. */ + constexpr int samples = 100; /**< Number of ADC samples. */ + constexpr float invSamples = 1.0f / samples; /**< Precomputed inverse for averaging. */ + + /**< Collect ADC samples */ + for (int i = 0; i < samples; ++i) + { + #ifdef ADC1 + sum += static_cast(adc1_get_raw(BATT_PIN)); + #endif + + #ifdef ADC2 + int readRaw; + if (adc2_get_raw(BATT_PIN, ADC_WIDTH_BIT_12, &readRaw) == ESP_OK) + sum += static_cast(readRaw); + #endif + + ets_delay_us(100); /**< Slightly reduced delay for better responsiveness. */ + } - for (int i = 0; i < 100; i++) - { - #ifdef ADC1 - sum += static_cast(adc1_get_raw(BATT_PIN)); - #endif + /**< Convert ADC value to voltage */ + float voltage = (sum * invSamples) * (V_REF / 4096.0f); /**< Averaged and scaled ADC voltage. */ - #ifdef ADC2 - int readRaw; - esp_err_t r = adc2_get_raw(BATT_PIN, ADC_WIDTH_BIT_12, &readRaw); - if (r == ESP_OK) - sum += static_cast(readRaw); - #endif + /**< Compensate for voltage divider (R1 = R2 = 100kΞ©) */ + voltage *= 2.0f; - delayMicroseconds(150); - } + /**< Round to 2 decimal places */ + voltage = roundf(voltage * 100.0f) * 0.01f; - voltage = sum / 100.0; - // Custom board has a divider circuit - constexpr float R1 = 100000.0; // Resistance of R1 (100K) - constexpr float R2 = 100000.0; // Resistance of R2 (100K) - voltage = (voltage * V_REF) / 4096.0; - voltage = voltage / (R2 / (R1 + R2)); - voltage = roundf(voltage * 100) / 100; + /**< Calculate battery percentage */ + float output = ((voltage - batteryMin) / (batteryMax - batteryMin)) * 100.0f; - output = ((voltage - batteryMin) / (batteryMax - batteryMin)) * 100; - return (output <= 160) ? output : 0.0f; + /**< Clamp out-of-range values */ + return (output <= 160.0f) ? output : 0.0f; } diff --git a/lib/battery/battery.hpp b/lib/battery/battery.hpp index 670bde78..d86cc6b9 100644 --- a/lib/battery/battery.hpp +++ b/lib/battery/battery.hpp @@ -2,8 +2,8 @@ * @file battery.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Battery monitor definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -12,17 +12,24 @@ #include #include + +/** + * @class Battery + * @brief Provides battery voltage monitoring and charge estimation. + * + * @details Handles ADC initialization, voltage range configuration, and computes battery charge percentage. + */ class Battery { private: - float batteryMax; - float batteryMin; - static constexpr float V_REF = 3.9; // ADC reference voltage + float batteryMax; /**< Maximum (full charge) voltage. */ + float batteryMin; /**< Minimum (empty) voltage. */ + static constexpr float V_REF = 3.9; /**< ADC reference voltage. */ public: - Battery(); + Battery(); - void initADC(); - void setBatteryLevels(float maxVoltage, float minVoltage); - float readBattery(); + void initADC(); + void setBatteryLevels(float maxVoltage, float minVoltage); + float readBattery(); }; \ No newline at end of file diff --git a/lib/bme/bme.cpp b/lib/bme/bme.cpp index 3848704a..6140f3eb 100644 --- a/lib/bme/bme.cpp +++ b/lib/bme/bme.cpp @@ -2,8 +2,8 @@ * @file bme.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief BME280 Sensor functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "bme.hpp" @@ -12,16 +12,13 @@ Adafruit_BME280 bme = Adafruit_BME280(); -/** - * @brief Temperature reading values - * - */ -uint8_t tempValue = 0; -uint8_t tempOld = 0; +uint8_t tempValue = 0; /**< Stores the current temperature value from the BME280 sensor. */ +uint8_t tempOld = 0; /**< Stores the previous temperature value for comparison. */ /** - * @brief Init BME sensor + * @brief Initializes the BME280 sensor and sets up I2C communication. * + * @details Optionally allows advanced configuration for oversampling and filtering. */ void initBME() { diff --git a/lib/bme/bme.hpp b/lib/bme/bme.hpp index 06f9debb..72926cd1 100644 --- a/lib/bme/bme.hpp +++ b/lib/bme/bme.hpp @@ -2,8 +2,8 @@ * @file bme.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief BME280 Sensor functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -15,15 +15,10 @@ #include #include -/** - * @brief BME280 Address - * - */ -#define BME_ADDRESS 0x76 - -extern Adafruit_BME280 bme; -extern uint8_t tempValue; -extern uint8_t tempOld; +#define BME_ADDRESS 0x76 /**< I2C address for the BME280 sensor (default: 0x76). */ +extern Adafruit_BME280 bme; /**< Global instance of the BME280 sensor driver. */ +extern uint8_t tempValue; /**< Stores the latest temperature reading from the BME280 sensor. */ +extern uint8_t tempOld; /**< Stores the previous temperature value to detect changes. */ void initBME(); diff --git a/lib/cli/cli.cpp b/lib/cli/cli.cpp index 791bd681..cd538d8d 100644 --- a/lib/cli/cli.cpp +++ b/lib/cli/cli.cpp @@ -3,7 +3,7 @@ * @author @Hpsaturn * @brief Network CLI and custom internal commands * @version Using https://github.com/hpsaturn/esp32-wifi-cli.git - * @date 2025-05 + * @date 2025-06 */ #ifndef DISABLE_CLI @@ -27,234 +27,261 @@ static const char* TAG PROGMEM = "CLI"; extern Power power; /** - * @brief Reboot ESP + * @brief Reboots the ESP device. + * + * @details CLI command: reboot */ void wcli_reboot(char *args, Stream *response) { - ESP.restart(); + ESP.restart(); } /** - * @brief ESP Deep Sleep/shutdown + * @brief Puts ESP device into deep sleep (shutdown). + * + * CLI command: poweroff */ void wcli_poweroff(char *args, Stream *response) { - power.deviceShutdown(); + power.deviceShutdown(); } /** - * @brief Display device info + * @brief Displays device information such as memory, SPIFFS, PSRAM, flash, and GPS parameters. + * + * @details CLI command: info */ void wcli_info(char *args, Stream *response) { - setlocale(LC_NUMERIC, ""); - size_t totalSPIFFS, usedSPIFFS, freeSPIFFS = 0; - esp_spiffs_info(NULL, &totalSPIFFS, &usedSPIFFS); - freeSPIFFS = totalSPIFFS - usedSPIFFS; + setlocale(LC_NUMERIC, ""); + size_t totalSPIFFS, usedSPIFFS, freeSPIFFS = 0; + esp_spiffs_info(NULL, &totalSPIFFS, &usedSPIFFS); + freeSPIFFS = totalSPIFFS - usedSPIFFS; - response->println(); - wcli.status(response); - response->printf("Total Memory\t: %3.0iKb\r\n",ESP.getHeapSize()/1000); - response->printf("SPIFFS total\t: %u bytes\r\n", totalSPIFFS); - response->printf("SPIFFS used\t: %u bytes\r\n", usedSPIFFS); - response->printf("SPIFFS free\t: %u bytes\r\n", freeSPIFFS); - if (psramFound()) - { - response->printf("PSRAM total\t: %u bytes\r\n", ESP.getPsramSize()); - response->printf("PSRAM used\t: %u bytes\r\n", ESP.getPsramSize()-ESP.getFreePsram()); - response->printf("PSRAM free\t: %u bytes\r\n", ESP.getFreePsram()); - } - response->printf("Flash size\t: %u bytes\r\n", ESP.getFlashChipSize()); - response->printf("Program size\t: %u bytes\r\n", ESP.getSketchSize()); - if (enableWeb) - response->println("Web file server\t: \033[1;32menabled\033[0;37m"); - else - response->println("Web file server\t: \033[1;31mdisabled\033[0;37m"); - response->printf("\r\n"); - response->printf("GPS Baud rate\t: %i baud\r\n",gpsBaudDetected); - response->printf("GPS Tx GPIO:\t: %i\r\n",GPS_TX); - response->printf("GPS Rx GPIO:\t: %i\r\n",GPS_RX); + response->println(); + wcli.status(response); + response->printf("Total Memory\t: %3.0iKb\r\n",ESP.getHeapSize()/1000); + response->printf("SPIFFS total\t: %u bytes\r\n", totalSPIFFS); + response->printf("SPIFFS used\t: %u bytes\r\n", usedSPIFFS); + response->printf("SPIFFS free\t: %u bytes\r\n", freeSPIFFS); + if (psramFound()) + { + response->printf("PSRAM total\t: %u bytes\r\n", ESP.getPsramSize()); + response->printf("PSRAM used\t: %u bytes\r\n", ESP.getPsramSize()-ESP.getFreePsram()); + response->printf("PSRAM free\t: %u bytes\r\n", ESP.getFreePsram()); + } + response->printf("Flash size\t: %u bytes\r\n", ESP.getFlashChipSize()); + response->printf("Program size\t: %u bytes\r\n", ESP.getSketchSize()); + if (enableWeb) + response->println("Web file server\t: \033[1;32menabled\033[0;37m"); + else + response->println("Web file server\t: \033[1;31mdisabled\033[0;37m"); + response->printf("\r\n"); + response->printf("GPS Baud rate\t: %i baud\r\n",gpsBaudDetected); + response->printf("GPS Tx GPIO:\t: %i\r\n",GPS_TX); + response->printf("GPS Rx GPIO:\t: %i\r\n",GPS_RX); } /** - * @brief Clear user settings + * @brief Clears all user settings and resets device to factory defaults. + * + * @details CLI command: wipe */ void wcli_swipe(char *args, Stream *response) { - Pair operands = wcli.parseCommand(args); - String deviceId = operands.first(); - response->println("Clearing device to defaults.."); - wcli.clearSettings(); - cfg.clear(); - response->println("done"); + Pair operands = wcli.parseCommand(args); + String deviceId = operands.first(); + response->println("Clearing device to defaults.."); + wcli.clearSettings(); + cfg.clear(); + response->println("done"); } /** - * @brief Clear CLI console + * @brief Clears the CLI console. + * + * @details CLI command: clear */ void wcli_clear(char *args, Stream *response) { - wcli.shell->clear(); + wcli.shell->clear(); } /** - * @brief Take a screenshot + * @brief Captures a screenshot to SD card or sends it to a PC over WiFi. + * + * @details CLI command: scshot */ void wcli_scshot(char *args, Stream *response) { - Pair operands = wcli.parseCommand(args); - String ip = operands.first(); - uint16_t port = operands.second().toInt(); - - if (ip.isEmpty()) - { - response->println("Saving to SD.."); + Pair operands = wcli.parseCommand(args); + String ip = operands.first(); + uint16_t port = operands.second().toInt(); + + if (ip.isEmpty()) + { + response->println("Saving to SD.."); - waitScreenRefresh = true; - captureScreenshot(SCREENSHOT_TEMP_FILE, response); - waitScreenRefresh = false; - - response->println("Note: is possible to send it to a PC using: scshot ip port"); - } - else - { - if (!WiFi.isConnected()) - { - response->println("Please connect your WiFi first!"); - return; - } - response->printf("Sending screenshot to %s:%i..\r\n", ip.c_str(), port); + waitScreenRefresh = true; + captureScreenshot(SCREENSHOT_TEMP_FILE, response); + waitScreenRefresh = false; + + response->println("Note: is possible to send it to a PC using: scshot ip port"); + } + else + { + if (!WiFi.isConnected()) + { + response->println("Please connect your WiFi first!"); + return; + } + response->printf("Sending screenshot to %s:%i..\r\n", ip.c_str(), port); - waitScreenRefresh = true; - captureScreenshot(SCREENSHOT_TEMP_FILE, ip.c_str(), port, response); - waitScreenRefresh = false; - } + waitScreenRefresh = true; + captureScreenshot(SCREENSHOT_TEMP_FILE, ip.c_str(), port, response); + waitScreenRefresh = false; + } } /** - * @brief list of user preference key. This depends of EasyPreferences manifest. - * @author @Hpsaturn. Method migrated from CanAirIO project + * @brief Lists user preference keys and their values. + * @author @Hpsaturn. Method migrated from CanAirIO project + * + * @details Shows only basic keys unless 'all' is specified. + * + * @details CLI command: klist */ void wcli_klist(char *args, Stream *response) { - Pair operands = wcli.parseCommand(args); - String opt = operands.first(); - int key_count = PKEYS::KUSER+1; - if (opt.equals("all")) key_count = 0; // Only show the basic keys to configure - response->printf("\n%11s \t%s \t%s \r\n", "KEYNAME", "DEFINED", "VALUE"); - response->printf("\n%11s \t%s \t%s \r\n", "=======", "=======", "====="); + Pair operands = wcli.parseCommand(args); + String opt = operands.first(); + int key_count = PKEYS::KUSER+1; + if (opt.equals("all")) key_count = 0; // Only show the basic keys to configure + response->printf("\n%11s \t%s \t%s \r\n", "KEYNAME", "DEFINED", "VALUE"); + response->printf("\n%11s \t%s \t%s \r\n", "=======", "=======", "====="); - for (int i = key_count; i < PKEYS::KCOUNT; i++) - { - if (i == PKEYS::KUSER) continue; - String key = cfg.getKey((CONFKEYS)i); - bool isDefined = cfg.isKey(key); - String defined = isDefined ? "custom " : "default"; - String value = ""; - if (isDefined) value = cfg.getValue(key); - response->printf("%11s \t%s \t%s \r\n", key, defined.c_str(), value.c_str()); - } + for (int i = key_count; i < PKEYS::KCOUNT; i++) + { + if (i == PKEYS::KUSER) continue; + String key = cfg.getKey((CONFKEYS)i); + bool isDefined = cfg.isKey(key); + String defined = isDefined ? "custom " : "default"; + String value = ""; + if (isDefined) value = cfg.getValue(key); + response->printf("%11s \t%s \t%s \r\n", key, defined.c_str(), value.c_str()); + } } /** - * @brief set an user preference key. This depends of EasyPreferences manifest. + * @brief Sets a user preference key to a given value. * @author @Hpsaturn. Method migrated from CanAirIO project + * + * @details CLI command: kset */ void wcli_kset(char *args, Stream *response) { - Pair operands = wcli.parseCommand(args); - String key = operands.first(); - String v = operands.second(); - if (cfg.saveAuto(key,v)) - response->printf("saved key %s\t: %s\r\n", key, v); + Pair operands = wcli.parseCommand(args); + String key = operands.first(); + String v = operands.second(); + if (cfg.saveAuto(key,v)) + response->printf("saved key %s\t: %s\r\n", key, v); } - /** - * @brief Output NMEA sentences in CLI + * @brief Toggles outputting NMEA sentences in the CLI. + * + * @details CLI command: outnmea */ void wcli_outnmea(char *args, Stream *response) { - nmea_output_enable = !nmea_output_enable; + nmea_output_enable = !nmea_output_enable; } /** - * @brief Cancel NMEA Output + * @brief Cancels NMEA output (Ctrl+C handler). */ void wcli_abort_handler() { - if (nmea_output_enable) - { - nmea_output_enable = false; - delay(100); - Serial.println("\r\nCancel NMEA output!"); - } + if (nmea_output_enable) + { + nmea_output_enable = false; + delay(100); + Serial.println("\r\nCancel NMEA output!"); + } } /** - * @brief Webfile server enable/disable option + * @brief Enables or disables the Web file server. + * + * @details CLI command: webfile */ void wcli_webfile(char *args, Stream *response) { - Pair operands = wcli.parseCommand(args); - String commands = operands.first(); + Pair operands = wcli.parseCommand(args); + String commands = operands.first(); - if (commands.isEmpty()) - response->println(F("missing parameter use: webfile \033[1;32menable/disable\033[0;37m")); - else - { - if(commands.equals("enable")) - { - cfg.saveBool(PKEYS::KWEB_FILE, true); - response->println(""); - response->printf("Web file server \033[1;32menabled\033[0;37m\r\n"); - response->println("Please reboot device"); - } - if(commands.equals("disable")) - { - cfg.saveBool(PKEYS::KWEB_FILE, false); - response->println(""); - response->printf("Web file server \033[1;32mdisabled\033[0;37m\r\n"); - response->println("Please reboot device"); - } - } + if (commands.isEmpty()) + response->println(F("missing parameter use: webfile \033[1;32menable/disable\033[0;37m")); + else + { + if(commands.equals("enable")) + { + cfg.saveBool(PKEYS::KWEB_FILE, true); + response->println(""); + response->printf("Web file server \033[1;32menabled\033[0;37m\r\n"); + response->println("Please reboot device"); + } + if(commands.equals("disable")) + { + cfg.saveBool(PKEYS::KWEB_FILE, false); + response->println(""); + response->printf("Web file server \033[1;32mdisabled\033[0;37m\r\n"); + response->println("Please reboot device"); + } + } } +/** + * @brief Initializes the CLI remote shell (e.g., Telnet). + */ void initRemoteShell() { #ifndef DISABLE_CLI_TELNET - if (wcli.isTelnetRunning()) wcli.shellTelnet->attachLogo(logo); + if (wcli.isTelnetRunning()) wcli.shellTelnet->attachLogo(logo); #endif } +/** + * @brief Initializes the local CLI shell, adds core commands, and sets up the environment. + */ void initShell() { - wcli.shell->attachLogo(logo); - wcli.setSilentMode(true); - // Main Commands: - wcli.add("reboot", &wcli_reboot, "\tperform a ESP32 reboot"); - wcli.add("poweroff", &wcli_poweroff, "\tperform a ESP32 deep sleep"); - wcli.add("wipe", &wcli_swipe, "\t\twipe preferences to factory default"); - wcli.add("info", &wcli_info, "\t\tget device information"); - wcli.add("clear", &wcli_clear, "\t\tclear shell"); - wcli.add("scshot", &wcli_scshot, "\tscreenshot to SD or sending a PC"); - wcli.add("webfile", &wcli_webfile, "\tenable/disable Web file server"); - wcli.add("klist", &wcli_klist, "\t\tlist of user preferences. ('all' param show all)"); - wcli.add("kset", &wcli_kset, "\t\tset an user extra preference"); - wcli.add("outnmea", &wcli_outnmea, "\ttoggle GPS NMEA output (or Ctrl+C to stop)"); - wcli.shell->overrideAbortKey(&wcli_abort_handler); - wcli.begin("IceNav"); + wcli.shell->attachLogo(logo); + wcli.setSilentMode(true); + // Main Commands: + wcli.add("reboot", &wcli_reboot, "\tperform a ESP32 reboot"); + wcli.add("poweroff", &wcli_poweroff, "\tperform a ESP32 deep sleep"); + wcli.add("wipe", &wcli_swipe, "\t\twipe preferences to factory default"); + wcli.add("info", &wcli_info, "\t\tget device information"); + wcli.add("clear", &wcli_clear, "\t\tclear shell"); + wcli.add("scshot", &wcli_scshot, "\tscreenshot to SD or sending a PC"); + wcli.add("webfile", &wcli_webfile, "\tenable/disable Web file server"); + wcli.add("klist", &wcli_klist, "\t\tlist of user preferences. ('all' param show all)"); + wcli.add("kset", &wcli_kset, "\t\tset an user extra preference"); + wcli.add("outnmea", &wcli_outnmea, "\ttoggle GPS NMEA output (or Ctrl+C to stop)"); + wcli.shell->overrideAbortKey(&wcli_abort_handler); + wcli.begin("IceNav"); } /** - * @brief WiFi CLI init - **/ + * @brief Initializes the WiFi CLI, including local and remote shells. + */ void initCLI() { - Serial.begin(115200); - ESP_LOGV(TAG, "init CLI"); - initShell(); - initRemoteShell(); + Serial.begin(115200); + ESP_LOGV(TAG, "init CLI"); + initShell(); + initRemoteShell(); } #endif diff --git a/lib/cli/cli.hpp b/lib/cli/cli.hpp index 2cfe57cc..6b7c0e67 100644 --- a/lib/cli/cli.hpp +++ b/lib/cli/cli.hpp @@ -3,7 +3,7 @@ * @author @Hpsaturn * @brief Network CLI and custom internal commands * @version Using https://github.com/hpsaturn/esp32-wifi-cli.git - * @date 2025-05 + * @date 2025-06 */ #pragma once diff --git a/lib/cli/utils.h b/lib/cli/utils.h index b68d545d..48f5b199 100644 --- a/lib/cli/utils.h +++ b/lib/cli/utils.h @@ -2,8 +2,8 @@ * @file utils.h * @author @Hpsaturn * @brief Network CLI and custom internal commands - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -14,80 +14,106 @@ #include "tft.hpp" extern Storage storage; +/** + * @brief Path to the temporary file used for storing screenshots. + */ #define SCREENSHOT_TEMP_FILE "/sdcard/screenshot.png" -// Capture the screenshot and save it to the SD card +/** + * @brief Captures a screenshot from the TFT display and saves it to the SD card. + * + * @details Creates a PNG image of the current display, attempts to write it to the specified file, + * and prints the operation result to the response stream. + * + * @param filename Path where the screenshot will be saved. + * @param response Stream to send operation messages. + * @return true if the screenshot was successfully created and saved, false otherwise. + */ static bool captureScreenshot(const char *filename, Stream *response) { - size_t dlen; - uint8_t *png = (uint8_t *)tft.createPng(&dlen, 0, 0, tft.width(), tft.height()); - if (!png) - { - response->println("Filed to create PNG"); - return false; - } + size_t dlen; + uint8_t *png = (uint8_t *)tft.createPng(&dlen, 0, 0, tft.width(), tft.height()); + if (!png) + { + response->println("Filed to create PNG"); + return false; + } - FILE *file = storage.open(filename, "w"); - - bool result = false; - if (file) - { - size_t err = storage.write(file, (uint8_t *)png, dlen); - if (err != 0) - response->println("Screenshot saved"); - else - response->println("Error writing screenshot"); - free(png); - storage.close(file); - result = true; - } - else - response->println("Failed to open file for writing"); + FILE *file = storage.open(filename, "w"); + + bool result = false; + if (file) + { + size_t err = storage.write(file, (uint8_t *)png, dlen); + if (err != 0) + response->println("Screenshot saved"); + else + response->println("Error writing screenshot"); + free(png); + storage.close(file); + result = true; + } + else + response->println("Failed to open file for writing"); - return result; + return result; } -// WiFi client +/** + * @brief WiFi client used for network operations in the CLI. + */ static WiFiClient client; +/** + * @brief Captures a screenshot and sends it over WiFi to a remote PC. + * + * @details Connects to a specified IP and port, captures a screenshot using captureScreenshot(), + * then reads the file and sends its contents over the network connection. + * Reports status and errors to the response stream. + * + * @param filename Path where the screenshot will be saved and read from. + * @param pc_ip IP address of the remote PC to send the screenshot to. + * @param pc_port Port of the remote PC to send the screenshot to. + * @param response Stream to send operation messages. + */ static void captureScreenshot(const char *filename, const char *pc_ip, uint16_t pc_port, Stream *response) { - if (!client.connect(pc_ip, pc_port)) - { - response->println("Connection to server failed"); - return; - } + if (!client.connect(pc_ip, pc_port)) + { + response->println("Connection to server failed"); + return; + } - response->println("Connected to server"); + response->println("Connected to server"); - if (!captureScreenshot(filename, response)) - { - client.stop(); - return; - } + if (!captureScreenshot(filename, response)) + { + client.stop(); + return; + } - FILE* file = storage.open(filename, "r"); - if (!file) - { - response->println("Failed to open file for reading"); - client.stop(); - return; - } + FILE* file = storage.open(filename, "r"); + if (!file) + { + response->println("Failed to open file for reading"); + client.stop(); + return; + } - // Send the file data to the PC - while (storage.fileAvailable(file)) - { - size_t size = 0; - uint8_t buffer[512]; - size = storage.read(file, buffer, sizeof(buffer)); - if (size > 0) - { - client.write(buffer, size); - } - } + // Send the file data to the PC + while (storage.fileAvailable(file)) + { + size_t size = 0; + uint8_t buffer[512]; + size = storage.read(file, buffer, sizeof(buffer)); + if (size > 0) + { + client.write(buffer, size); + } + } - storage.close(file); - client.stop(); - response->println("Screenshot sent over WiFi"); + storage.close(file); + client.stop(); + response->println("Screenshot sent over WiFi"); } #endif \ No newline at end of file diff --git a/lib/compass/compass.cpp b/lib/compass/compass.cpp index 7b4fe9ae..f68dd451 100644 --- a/lib/compass/compass.cpp +++ b/lib/compass/compass.cpp @@ -1,8 +1,8 @@ /** * @file compass.cpp * @brief Compass definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "compass.hpp" @@ -10,301 +10,301 @@ static const char* TAG PROGMEM = "Compass"; #ifdef HMC5883L -DFRobot_QMC5883 comp = DFRobot_QMC5883(&Wire, HMC5883L_ADDRESS); + DFRobot_QMC5883 comp = DFRobot_QMC5883(&Wire, HMC5883L_ADDRESS); #endif #ifdef QMC5883 -DFRobot_QMC5883 comp = DFRobot_QMC5883(&Wire, QMC5883_ADDRESS); + DFRobot_QMC5883 comp = DFRobot_QMC5883(&Wire, QMC5883_ADDRESS); #endif #ifdef IMU_MPU9250 -MPU9250 IMU = MPU9250(Wire, 0x68); + MPU9250 IMU = MPU9250(Wire, 0x68); #endif /** - * @brief Compass Class constructor with default values - * + * @brief Compass class constructor with default filter and calibration values. */ Compass::Compass() - : declinationAngle(0.22), offX(0.0), offY(0.0), - headingSmooth(0.0), headingPrevious(0.0), - minX(0.0), maxX(0.0), minY(0.0), maxY(0.0), - kalmanFilterEnabled(true), - kalmanFilter(0.01, 0.1, 1.0, 0.0) + : declinationAngle(0.22), offX(0.0), offY(0.0), + headingSmooth(0.0), headingPrevious(0.0), + minX(0.0), maxX(0.0), minY(0.0), maxY(0.0), + kalmanFilterEnabled(true), + kalmanFilter(0.01, 0.1, 1.0, 0.0) { } /** - * @brief Compass Init - * + * @brief Initializes the compass/magnetometer sensor hardware and configuration. */ void Compass::init() { #ifdef HMC5883L - if (!comp.begin()) - comp.begin(); - comp.setDataRate(HMC5883L_DATARATE_15HZ); - comp.setSamples(HMC5883L_SAMPLES_1); + if (!comp.begin()) + comp.begin(); + comp.setDataRate(HMC5883L_DATARATE_15HZ); + comp.setSamples(HMC5883L_SAMPLES_1); #endif #ifdef QMC5883 - if (!comp.begin()) - comp.begin(); - comp.setDataRate(QMC5883_DATARATE_10HZ); - comp.setSamples(QMC5883_SAMPLES_1); + if (!comp.begin()) + comp.begin(); + comp.setDataRate(QMC5883_DATARATE_10HZ); + comp.setSamples(QMC5883_SAMPLES_1); #endif #ifdef IMU_MPU9250 - int status = IMU.begin(); - if (status < 0) - { - ESP_LOGE(TAG, "IMU initialization unsuccessful"); - ESP_LOGE(TAG, "Check IMU wiring or try cycling power"); - ESP_LOGE(TAG, "Status: %i", status); - } + int status = IMU.begin(); + if (status < 0) + { + ESP_LOGE(TAG, "IMU initialization unsuccessful"); + ESP_LOGE(TAG, "Check IMU wiring or try cycling power"); + ESP_LOGE(TAG, "Status: %i", status); + } #endif } /** - * @brief Configure ADC Channel for battery reading - * - * @param x - * @param y - * @param z + * @brief Reads raw X, Y, Z magnetometer data from the compass sensor. + * @param x Reference variable for X-axis. + * @param y Reference variable for Y-axis. + * @param z Reference variable for Z-axis. */ void Compass::read(float &x, float &y, float &z) { #ifdef HMC5883L - sVector_t mag = comp.readRaw(); - y = mag.YAxis; - x = mag.XAxis; - z = mag.ZAxis; + sVector_t mag = comp.readRaw(); + y = mag.YAxis; + x = mag.XAxis; + z = mag.ZAxis; #endif #ifdef QMC5883 - sVector_t mag = comp.readRaw(); - y = mag.YAxis; - x = mag.XAxis; - z = mag.ZAxis; + sVector_t mag = comp.readRaw(); + y = mag.YAxis; + x = mag.XAxis; + z = mag.ZAxis; #endif #ifdef IMU_MPU9250 - IMU.readSensor(); - x = IMU.getMagX_uT(); - y = IMU.getMagY_uT(); - z = IMU.getMagZ_uT(); + IMU.readSensor(); + x = IMU.getMagX_uT(); + y = IMU.getMagY_uT(); + z = IMU.getMagZ_uT(); #endif #ifdef ICENAV_BOARD - y = y * -1; + y = y * -1; #endif } /** - * @brief Get compass heading + * @brief Calculates the heading (in degrees) from the magnetometer data. + * + * @details Applies calibration offsets and applies a Kalman filter if enabled. * - * @return compass heading + * @return Heading in degrees. */ int Compass::getHeading() { - float y = 0.0; - float x = 0.0; - float z = 0.0; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; - read(x, y, z); - - float headingNoFilter = atan2(y - offY, x - offX); - headingNoFilter += declinationAngle; + read(x, y, z); - headingNoFilter = wrapToPi(headingNoFilter); + float hx = x - offX; + float hy = y - offY; - if (kalmanFilterEnabled) - { - headingNoFilter = unwrapFromPi(headingNoFilter, headingPrevious); - headingSmooth = kalmanFilter.update(headingNoFilter); - } - else - headingSmooth = headingNoFilter; + float heading = atan2f(hy, hx); + heading += declinationAngle; + heading = wrapToPi(heading); - headingPrevious = headingNoFilter; + if (kalmanFilterEnabled) + { + heading = unwrapFromPi(heading, headingPrevious); + headingSmooth = kalmanFilter.update(heading); + } + else + { + headingSmooth = heading; + } - float headingDegrees = (int)(headingSmooth * 180 / M_PI); + headingPrevious = heading; - if (headingDegrees < 0) - headingDegrees += 360; + int headingDeg = static_cast(headingSmooth * (180.0f / M_PI)); + if (headingDeg < 0) + headingDeg += 360; - return static_cast(headingDegrees); + return headingDeg; } /** - * @brief Check compass heading is update + * @brief Checks if the compass heading has been updated since the last reading. * - * @return true/flase + * @details Compares the current heading with the previous value to detect changes. + * + * @return true if updated, false otherwise. */ bool Compass::isUpdated() { - int currentDegrees = getHeading(); - if (currentDegrees != previousDegrees) - { - previousDegrees = currentDegrees; - return true; - } - return false; + int currentDegrees = getHeading(); + if (currentDegrees != previousDegrees) + { + previousDegrees = currentDegrees; + return true; + } + return false; } /** - * @brief Compass calibration + * @brief Performs compass calibration routine. * + * @details Guides the user through a calibration process using the screen and touch input. + * Saves the calculated X and Y offsets to persistent configuration. */ void Compass::calibrate() { - bool cal = 1; - float y = 0.0; - float x = 0.0; - float z = 0.0; - uint16_t touchX, touchY; - - static const lgfx::v1::GFXfont *fontSmall; - static const lgfx::v1::GFXfont *fontLarge; - -#ifdef LARGE_SCREEN - fontSmall = &fonts::DejaVu18; - fontLarge = &fonts::DejaVu40; - static const float scale = 1.0f; -#else - fontSmall = &fonts::DejaVu12; - fontLarge = &fonts::DejaVu24; - static const float scale = 0.75f; -#endif - - tft.drawCenterString("ROTATE THE DEVICE", tft.width() >> 1, 10 * scale, fontSmall); - tft.drawPngFile(PSTR("/spiffs/turn.png"), (tft.width() / 2) - 50, 60 * scale); - tft.drawCenterString("TOUCH TO START", tft.width() >> 1, 200 * scale, fontSmall); - tft.drawCenterString("COMPASS CALIBRATION", tft.width() >> 1, 230 * scale, fontSmall); - - while (!tft.getTouch(&touchX, &touchY)) - { - }; - delay(1000); - - unsigned long calTimeWas = millis(); - - read(x, y, z); - - maxX = minX = x; - maxY = minY = y; - - while (cal) - { - read(x, y, z); - - if (x > maxX) - maxX = x; - if (x < minX) - minX = x; - if (y > maxY) - maxY = y; - if (y < minY) - minY = y; - - int secmillis = millis() - calTimeWas; - int secs = (int)((COMPASS_CAL_TIME - secmillis + 1000) / 1000); - tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.setTextSize(3); - tft.setTextPadding(tft.textWidth("88")); - tft.drawNumber((COMPASS_CAL_TIME - secmillis) / 1000, (tft.width() >> 1), 280 * scale); - - if (secs == 0) - { - offX = (maxX + minX) / 2; - offY = (maxY + minY) / 2; - cal = 0; - } - } - - tft.setTextSize(1); - tft.drawCenterString("DONE!", tft.width() >> 1, 340 * scale, fontLarge); - tft.drawCenterString("TOUCH TO CONTINUE.", tft.width() >> 1, 380 * scale, fontSmall); - - while (!tft.getTouch(&touchX, &touchY)) - { - }; - - cfg.saveFloat(PKEYS::KCOMP_OFFSET_X, offX); - cfg.saveFloat(PKEYS::KCOMP_OFFSET_Y, offY); + bool cal = 1; + float y = 0.0; + float x = 0.0; + float z = 0.0; + uint16_t touchX, touchY; + + static const lgfx::v1::GFXfont *fontSmall; + static const lgfx::v1::GFXfont *fontLarge; + + #ifdef LARGE_SCREEN + fontSmall = &fonts::DejaVu18; + fontLarge = &fonts::DejaVu40; + static const float scale = 1.0f; + #else + fontSmall = &fonts::DejaVu12; + fontLarge = &fonts::DejaVu24; + static const float scale = 0.75f; + #endif + + tft.drawCenterString("ROTATE THE DEVICE", tft.width() >> 1, 10 * scale, fontSmall); + tft.drawPngFile(PSTR("/spiffs/turn.png"), (tft.width() / 2) - 50, 60 * scale); + tft.drawCenterString("TOUCH TO START", tft.width() >> 1, 200 * scale, fontSmall); + tft.drawCenterString("COMPASS CALIBRATION", tft.width() >> 1, 230 * scale, fontSmall); + + while (!tft.getTouch(&touchX, &touchY)) + { + }; + delay(1000); + + unsigned long calTimeWas = millis(); + + read(x, y, z); + + maxX = minX = x; + maxY = minY = y; + + while (cal) + { + read(x, y, z); + + if (x > maxX) + maxX = x; + if (x < minX) + minX = x; + if (y > maxY) + maxY = y; + if (y < minY) + minY = y; + + int secmillis = millis() - calTimeWas; + int secs = (int)((COMPASS_CAL_TIME - secmillis + 1000) / 1000); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(3); + tft.setTextPadding(tft.textWidth("88")); + tft.drawNumber((COMPASS_CAL_TIME - secmillis) / 1000, (tft.width() >> 1), 280 * scale); + + if (secs == 0) + { + offX = (maxX + minX) / 2; + offY = (maxY + minY) / 2; + cal = 0; + } + } + + tft.setTextSize(1); + tft.drawCenterString("DONE!", tft.width() >> 1, 340 * scale, fontLarge); + tft.drawCenterString("TOUCH TO CONTINUE.", tft.width() >> 1, 380 * scale, fontSmall); + + while (!tft.getTouch(&touchX, &touchY)) + { + }; + + cfg.saveFloat(PKEYS::KCOMP_OFFSET_X, offX); + cfg.saveFloat(PKEYS::KCOMP_OFFSET_Y, offY); } /** - * @brief Set compass declination angle - * - * @param angle -> Declination angle + * @brief Sets the magnetic declination angle for heading correction. + * @param angle Declination angle in radians. */ void Compass::setDeclinationAngle(float angle) { - declinationAngle = angle; + declinationAngle = angle; } /** - * @brief Set compass calibration offsets - * - * @param offsetX - * @param offsetY + * @brief Sets the calibration offsets for the X and Y axes. + * @param offsetX X-axis offset. + * @param offsetY Y-axis offset. */ void Compass::setOffsets(float offsetX, float offsetY) { - offX = offsetX; - offY = offsetY; + offX = offsetX; + offY = offsetY; } /** - * @brief Enable compass Kalman filter - * - * @param enable -> true/false + * @brief Enables or disables the Kalman filter for compass heading smoothing. + * @param enabled True to enable, false to disable. */ void Compass::enableKalmanFilter(bool enabled) { - kalmanFilterEnabled = enabled; + kalmanFilterEnabled = enabled; } /** - * @brief Set Kalman filter constants - * - * @param processNoise -> 0-1 - * @param measureNoise -> 0-1 + * @brief Sets the Kalman filter process and measurement noise constants. + * @param processNoise Process noise covariance (0-1). + * @param measureNoise Measurement noise covariance (0-1). */ void Compass::setKalmanFilterConst(float processNoise, float measureNoise) { - kalmanFilter.setConstants(processNoise, measureNoise); + kalmanFilter.setConstants(processNoise, measureNoise); } /** - * @brief Helper function to wrap angle to -pi to pi - * - * @param angle - * @return wrap angle + * @brief Wraps an angle to the range [-pi, pi]. + * @param angle Angle in radians. + * @return Wrapped angle in radians. */ float Compass::wrapToPi(float angle) { - while (angle < -M_PI) - angle += 2 * M_PI; - while (angle > M_PI) - angle -= 2 * M_PI; - return angle; + while (angle < -M_PI) + angle += 2 * M_PI; + while (angle > M_PI) + angle -= 2 * M_PI; + return angle; } /** - * @brief Helper function to unwrap angle to -pi to pi - * - * @param angle - * @param previousAngle - * @return unrap angle + * @brief Unwraps an angle to avoid discontinuity across the [-pi, pi] boundary. + * @param angle Current angle in radians. + * @param previousAngle Previous angle in radians. + * @return Unwrapped angle in radians. */ float Compass::unwrapFromPi(float angle, float previousAngle) { - float delta = angle - previousAngle; - if (delta > M_PI) - angle -= 2 * M_PI; - else if (delta < -M_PI) - angle += 2 * M_PI; - return angle; + float delta = angle - previousAngle; + if (delta > M_PI) + angle -= 2 * M_PI; + else if (delta < -M_PI) + angle += 2 * M_PI; + return angle; } diff --git a/lib/compass/compass.hpp b/lib/compass/compass.hpp index b226691e..ee504336 100644 --- a/lib/compass/compass.hpp +++ b/lib/compass/compass.hpp @@ -1,8 +1,8 @@ /** * @file compass.hpp * @brief Compass definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,96 +11,121 @@ #include #ifdef HMC5883L -#include -#define ENABLE_COMPASS + #include + #define ENABLE_COMPASS #endif #ifdef QMC5883 -#include -#define ENABLE_COMPASS + #include + #define ENABLE_COMPASS #endif #ifdef IMU_MPU9250 -#include -#define ENABLE_COMPASS -#define ENABLE_IMU ; + #include + #define ENABLE_COMPASS + #define ENABLE_IMU ; #endif -#define COMPASS_CAL_TIME 16000 +#define COMPASS_CAL_TIME 16000 /**< Compass calibration duration in milliseconds. */ +/** + * @class KalmanFilter + * @brief Implements a simple 1D Kalman filter for angle estimation. + */ class KalmanFilter { public: - KalmanFilter(float processNoise, float measurementNoise, float estimateError, float initialEstimate) - : q(processNoise), r(measurementNoise), p(estimateError), x(initialEstimate) {} - - float update(float measurement) - { - measurement = wrapToPi(measurement); - float y = measurement - x; - y = wrapToPi(y); - - p = p + q; - k = p / (p + r); - x = x + k * y; - - x = wrapToPi(x); - - p = (1 - k) * p; - return x; - } - - void setConstants(float processNoise, float measurementNoise) - { - q = processNoise; - r = measurementNoise; - } + KalmanFilter(float processNoise, float measurementNoise, float estimateError, float initialEstimate) + : q(processNoise), r(measurementNoise), p(estimateError), x(initialEstimate) {} + + /** + * @brief Updates the state estimate using the Kalman filter algorithm for angular measurements. + * + * @details Applies the Kalman filter update step, taking into account the wrapped angular measurement, + * and updates the internal state and covariance variables accordingly. + * + * @param measurement The new angle measurement to incorporate (in radians). + * @return float The updated state estimate (in radians, wrapped to [-Ο€, Ο€]). + */ + float update(float measurement) + { + measurement = wrapToPi(measurement); + float y = measurement - x; + y = wrapToPi(y); + + p = p + q; + k = p / (p + r); + x = x + k * y; + + x = wrapToPi(x); + + p = (1 - k) * p; + return x; + } + + /** + * @brief Sets the process and measurement noise constants for the Kalman filter. + * + * @details Updates the internal parameters for process noise covariance (q) and measurement noise covariance (r). + * + * @param processNoise Value for the process noise covariance. + * @param measurementNoise Value for the measurement noise covariance. + */ + void setConstants(float processNoise, float measurementNoise) + { + q = processNoise; + r = measurementNoise; + } private: - float q; // Process noise covariance - float r; // Measurement noise covariance - float p; // Estimate error covariance - float k; // Kalman gain - float x; // Value - - float wrapToPi(float angle) - { - while (angle < -M_PI) - angle += 2 * M_PI; - while (angle > M_PI) - angle -= 2 * M_PI; - return angle; - } + float q; /**< Process noise covariance (Q), representing the process variance. */ + float r; /**< Measurement noise covariance (R), representing the sensor variance. */ + float p; /**< Estimate error covariance (P), representing the estimated error. */ + float k; /**< Kalman gain (K), used to update the state estimate. */ + float x; /**< Value (X), the current state estimate. */ + + float wrapToPi(float angle) + { + while (angle < -M_PI) + angle += 2 * M_PI; + while (angle > M_PI) + angle -= 2 * M_PI; + return angle; + } }; +/** + * @class Compass + * @brief Provides high-level interface for compass (magnetometer) sensor management and heading calculation. + */ class Compass { public: - Compass(); - void init(); - void read(float &x, float &y, float &z); - int getHeading(); - bool isUpdated(); - void calibrate(); - void setDeclinationAngle(float angle); - void setOffsets(float offsetX, float offsetY); - void enableKalmanFilter(bool enabled); - void setKalmanFilterConst(float processNoise, float measureNoise); + Compass(); + void init(); + void read(float &x, float &y, float &z); + int getHeading(); + bool isUpdated(); + void calibrate(); + void setDeclinationAngle(float angle); + void setOffsets(float offsetX, float offsetY); + void enableKalmanFilter(bool enabled); + void setKalmanFilterConst(float processNoise, float measureNoise); private: - float declinationAngle; - float offX; - float offY; - float headingSmooth; - float headingPrevious; - float minX; - float maxX; - float minY; - float maxY; - bool kalmanFilterEnabled; - KalmanFilter kalmanFilter; - int previousDegrees; - - float wrapToPi(float angle); - float unwrapFromPi(float angle, float previousAngle); + float declinationAngle; /**< Magnetic declination angle (in radians or degrees, depending on use). */ + float offX; /**< Magnetometer offset for X axis. */ + float offY; /**< Magnetometer offset for Y axis. */ + float headingSmooth; /**< Smoothed heading value. */ + float headingPrevious; /**< Previous heading value (for smoothing or change detection). */ + float minX; /**< Minimum observed value for X axis (for calibration). */ + float maxX; /**< Maximum observed value for X axis (for calibration). */ + float minY; /**< Minimum observed value for Y axis (for calibration). */ + float maxY; /**< Maximum observed value for Y axis (for calibration). */ + bool kalmanFilterEnabled; /**< True if the Kalman filter is enabled for heading smoothing. */ + KalmanFilter kalmanFilter; /**< Kalman filter instance used for heading estimation. */ + int previousDegrees; /**< Previous heading in degrees (integer value). */ + + float wrapToPi(float angle); + float unwrapFromPi(float angle, float previousAngle); }; \ No newline at end of file diff --git a/lib/gps/gps.cpp b/lib/gps/gps.cpp index 6ac2b0b5..c1a64fd4 100644 --- a/lib/gps/gps.cpp +++ b/lib/gps/gps.cpp @@ -2,231 +2,259 @@ * @file gps.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief GPS definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "gps.hpp" #include "lvgl.h" #include "widgets.hpp" -extern lv_obj_t *sunriseLabel; -bool setTime = true; -bool isGpsFixed = false; -bool isTimeFixed = false; -long gpsBaudDetected = 0; -bool nmea_output_enable = false; -gps_fix fix; -NMEAGPS GPS; +extern lv_obj_t *sunriseLabel; /**< Label object for displaying the sunrise time. */ +bool setTime = true; /**< Indicates if the system time should be set from GPS. */ +bool isGpsFixed = false; /**< Indicates whether a valid GPS fix has been acquired. */ +bool isTimeFixed = false; /**< Indicates whether the system time has been fixed using GPS. */ +long gpsBaudDetected = 0; /**< Detected GPS baud rate. */ +bool nmea_output_enable = false; /**< Enables or disables NMEA output. */ +gps_fix fix; /**< Latest parsed GPS fix data. */ +NMEAGPS GPS; /**< NMEAGPS parser instance. */ static const char* TAG PROGMEM = "GPS"; Gps::Gps() {} + /** - * @brief Init GPS and custom NMEA parsing + * @brief Init GPS and custom NMEA parsing. * + * @details Initializes the GPS port with the appropriate baud rate and buffer size. + * If a specific baud rate is not set (gpsBaud != 4), it uses the predefined baud rate array. + * Otherwise, it attempts to auto-detect the baud rate. */ void Gps::init() { - gpsPort.setRxBufferSize(1024); + gpsPort.setRxBufferSize(1024); - if (gpsBaud != 4) - gpsPort.begin(GPS_BAUD[gpsBaud], SERIAL_8N1, GPS_RX, GPS_TX); - else - { - gpsBaudDetected = autoBaud(); + if (gpsBaud != 4) + gpsPort.begin(GPS_BAUD[gpsBaud], SERIAL_8N1, GPS_RX, GPS_TX); + else + { + gpsBaudDetected = autoBaud(); - if (gpsBaudDetected != 0) - gpsPort.begin(gpsBaudDetected, SERIAL_8N1, GPS_RX, GPS_TX); - } + if (gpsBaudDetected != 0) + gpsPort.begin(gpsBaudDetected, SERIAL_8N1, GPS_RX, GPS_TX); + } #ifdef AT6558D_GPS - // FACTORY RESET - // gpsPort.println("$PCAS10,3*1F\r\n"); - // gpsPort.flush(); - // delay(100); - - // GPS - // gpsPort.println("$PCAS04,1*18\r\n") - - // GPS+GLONASS - // gpsPort.println("$PCAS04,5*1C\r\n"); - - // GPS+BDS+GLONASS - gpsPort.println("$PCAS04,7*1E\r\n"); - gpsPort.flush(); - delay(100); - - // Update Rate - gpsPort.println(GPS_RATE_PCAS[gpsUpdate]); - gpsPort.flush(); - delay(100); - - // Set NMEA 4.1 - gpsPort.println("$PCAS05,2*1A\r\n"); - gpsPort.flush(); - delay(100); + // FACTORY RESET + // gpsPort.println("$PCAS10,3*1F\r\n"); + // gpsPort.flush(); + // delay(100); + + // GPS + // gpsPort.println("$PCAS04,1*18\r\n") + + // GPS+GLONASS + // gpsPort.println("$PCAS04,5*1C\r\n"); + + // GPS+BDS+GLONASS + gpsPort.println("$PCAS04,7*1E\r\n"); + gpsPort.flush(); + delay(100); + + // Update Rate + gpsPort.println(GPS_RATE_PCAS[gpsUpdate]); + gpsPort.flush(); + delay(100); + + // Set NMEA 4.1 + gpsPort.println("$PCAS05,2*1A\r\n"); + gpsPort.flush(); + delay(100); #endif - } /** - * @brief return latitude from GPS or sys env pre-built variable - * - * @return latitude or 0.0 if not defined + * @brief Return latitude from GPS or system environment pre-built variable. + * + * @details Returns the current latitude using the GPS fix if available, otherwise uses the system configuration + * or a predefined default value. Returns 0.0 if latitude is not defined. + * + * @return Latitude value or 0.0 if not defined. */ -double Gps::getLat() +float Gps::getLat() { - { - if (fix.valid.location) - return fix.latitude(); - else if (cfg.getDouble(PKEYS::KLAT_DFL, 0.0) != 0.0) - return cfg.getDouble(PKEYS::KLAT_DFL, 0.0); - else - { -#ifdef DEFAULT_LAT - return DEFAULT_LAT; -#else - return 0.0; -#endif - } - } + { + if (fix.valid.location) + return fix.latitude(); + else if (cfg.getFloat(PKEYS::KLAT_DFL, 0.0) != 0.0) + return cfg.getFloat(PKEYS::KLAT_DFL, 0.0); + else + { + #ifdef DEFAULT_LAT + return DEFAULT_LAT; + #else + return 0.0; + #endif + } + } } /** - * @brief return longitude from GPS or sys env pre-built variable - * - * @return longitude or 0.0 if not defined + * @brief Return longitude from GPS or system environment pre-built variable. + * + * @details Returns the current longitude using the GPS fix if available, otherwise uses the system configuration + * or a predefined default value. Returns 0.0 if longitude is not defined. + * + * @return Longitude value or 0.0 if not defined. */ -double Gps::getLon() +float Gps::getLon() { - if (fix.valid.location) - return fix.longitude(); - else if (cfg.getDouble(PKEYS::KLON_DFL, 0.0) != 0.0) - return cfg.getDouble(PKEYS::KLON_DFL, 0.0); - else - { -#ifdef DEFAULT_LON - return DEFAULT_LON; -#else - return 0.0; -#endif - } + if (fix.valid.location) + return fix.longitude(); + else if (cfg.getFloat(PKEYS::KLON_DFL, 0.0) != 0.0) + return cfg.getFloat(PKEYS::KLON_DFL, 0.0); + else + { + #ifdef DEFAULT_LON + return DEFAULT_LON; + #else + return 0.0; + #endif + } } /** - * @brief Get GPS parsed data + * @brief Get GPS parsed data. * + * @details Updates the GPS data structure with the latest parsed values from the GPS fix. + * Handles fix status, satellite information, time/date updates, position, altitude, speed, + * heading, dilution of precision values, and updates satellite tracker positions and status. */ void Gps::getGPSData() { - // GPS Fix - if (fix.status != gps_fix::STATUS_NONE) - isGpsFixed = true; - - // GPS Not fixed - if (fix.status == gps_fix::STATUS_NONE) - isGpsFixed = false; - - // Satellite Count - gpsData.satellites = fix.satellites; - - // Fix Mode - gpsData.fixMode = fix.status; - - // Time and Date - if (fix.valid.time && fix.valid.date) - { - if (!setTime) - { - log_v("Get date, time, Sunrise and Sunset"); - // Set ESP RTC - Local time - String TZ = cfg.isKey(CONFKEYS::KDEF_TZ) ? cfg.getString(CONFKEYS::KDEF_TZ, TZ) : "UTC"; - setLocalTime(fix.dateTime,getPosixTZ(TZ.c_str())); - // Calculate Sunrise and Sunset only one time when date & time was valid - calculateSun(); - setTime = true; - lv_obj_send_event(sunriseLabel, LV_EVENT_VALUE_CHANGED, NULL); - } - } - - // Altitude - if (fix.valid.altitude) - gpsData.altitude = fix.alt.whole; - - // Speed - if (fix.valid.speed) - gpsData.speed = (uint16_t)fix.speed_kph(); - - // Latitude and Longitude - if (fix.valid.location) - { - gpsData.latitude = getLat(); - gpsData.longitude = getLon(); - } - - // Heading - if (fix.valid.heading) - gpsData.heading = (uint16_t)fix.heading(); - - // HDOP , PDOP , VDOP - if (fix.valid.hdop) - gpsData.hdop = (float)fix.hdop / 1000; - if (fix.valid.pdop) - gpsData.pdop = (float)fix.pdop / 1000; - if (fix.valid.vdop) - gpsData.vdop = (float)fix.vdop / 1000; - - // Satellite info - gpsData.satInView = (uint8_t)GPS.sat_count; - for (uint8_t i = 0; i < gpsData.satInView; i++) - { - satTracker[i].satNum = (uint8_t)GPS.satellites[i].id; - satTracker[i].elev = (uint8_t)GPS.satellites[i].elevation; - satTracker[i].azim = (uint16_t)GPS.satellites[i].azimuth; - satTracker[i].snr = (uint8_t)GPS.satellites[i].snr; - satTracker[i].active = GPS.satellites[i].tracked; - strncpy(satTracker[i].talker_id, GPS.satellites[i].talker_id, 3); - int H = canvasRadius * (90 - satTracker[i].elev) / 90; - satTracker[i].posX = canvasCenter_X + H * sin(DEG2RAD(satTracker[i].azim)); - satTracker[i].posY = canvasCenter_Y - H * cos(DEG2RAD(satTracker[i].azim)); - } + // GPS Fix + if (fix.status != gps_fix::STATUS_NONE) + isGpsFixed = true; + + // GPS Not fixed + if (fix.status == gps_fix::STATUS_NONE) + isGpsFixed = false; + + // Satellite Count + gpsData.satellites = fix.satellites; + + // Fix Mode + gpsData.fixMode = fix.status; + + // Time and Date + if (fix.valid.time && fix.valid.date) + { + if (!setTime) + { + log_v("Get date, time, Sunrise and Sunset"); + // Set ESP RTC - Local time + String TZ = cfg.isKey(CONFKEYS::KDEF_TZ) ? cfg.getString(CONFKEYS::KDEF_TZ, TZ) : "UTC"; + setLocalTime(fix.dateTime,getPosixTZ(TZ.c_str())); + // Calculate Sunrise and Sunset only one time when date & time was valid + calculateSun(); + setTime = true; + lv_obj_send_event(sunriseLabel, LV_EVENT_VALUE_CHANGED, NULL); + } + } + + // Altitude + if (fix.valid.altitude) + gpsData.altitude = fix.alt.whole; + + // Speed + if (fix.valid.speed) + gpsData.speed = (uint16_t)fix.speed_kph(); + + // Latitude and Longitude + if (fix.valid.location) + { + gpsData.latitude = getLat(); + gpsData.longitude = getLon(); + } + + // Heading + if (fix.valid.heading) + gpsData.heading = (uint16_t)fix.heading(); + + // HDOP , PDOP , VDOP + if (fix.valid.hdop) + gpsData.hdop = (float)fix.hdop / 1000; + if (fix.valid.pdop) + gpsData.pdop = (float)fix.pdop / 1000; + if (fix.valid.vdop) + gpsData.vdop = (float)fix.vdop / 1000; + + // // Satellite info + gpsData.satInView = (uint8_t)GPS.sat_count; + for (uint8_t i = 0; i < gpsData.satInView; i++) + { + satTracker[i].satNum = (uint8_t)GPS.satellites[i].id; + satTracker[i].elev = (uint8_t)GPS.satellites[i].elevation; + satTracker[i].azim = (uint16_t)GPS.satellites[i].azimuth; + satTracker[i].snr = (uint8_t)GPS.satellites[i].snr; + satTracker[i].active = GPS.satellites[i].tracked; + strncpy(satTracker[i].talker_id, GPS.satellites[i].talker_id, 3); + + int H = canvasRadius * (90 - satTracker[i].elev) / 90; + + float azimRad = DEG2RAD((float)satTracker[i].azim); + float sinAzim = lutInit ? sinLUT(azimRad) : sinf(azimRad); + float cosAzim = lutInit ? cosLUT(azimRad) : cosf(azimRad); + + satTracker[i].posX = canvasCenter_X + H * sinAzim; + satTracker[i].posY = canvasCenter_Y - H * cosAzim; + } + } + + /** - * @brief return pulse rate from RX GPS pin - * - * @return pulse rate + * @brief Detect the baud rate of the incoming GPS signal on a given RX pin. + * + * @details Measures the duration of low pulses on the RX line to estimate the baud rate of the connected GPS device. + * Returns the shortest measured pulse width as the likely bit duration. + * + * @param rxPin The GPIO pin number used for receiving GPS data. + * @return long The estimated baud rate (bit duration in microseconds). */ long Gps::detectRate(int rxPin) { - long rate = 10000, x = 2000; - pinMode(rxPin, INPUT); // make sure Serial in is a input pin - digitalWrite(rxPin, HIGH); // pull up enabled just for noise protection - - for (int i = 0; i < 5; i++) - { - x = pulseIn(rxPin, LOW, 125000); // measure the next zero bit width - if (x < 1) - continue; - rate = x < rate ? x : rate; - } - return rate; + long rate = 10000, x = 2000; + pinMode(rxPin, INPUT); // make sure Serial in is a input pin + digitalWrite(rxPin, HIGH); // pull up enabled just for noise protection + + for (int i = 0; i < 5; i++) + { + x = pulseIn(rxPin, LOW, 125000); // measure the next zero bit width + if (x < 1) + continue; + rate = x < rate ? x : rate; + } + return rate; } /** - * @brief Detect GPS Baudrate - * - * @return baudrate + * @brief Detect GPS Baudrate. + * + * @details Measures the pulse width on the GPS RX pin multiple times to estimate the baud rate. + * Maps the measured pulse width to the nearest standard baud rate value. + * + * @return long Detected baud rate, or 0 if detection failed. */ long Gps::autoBaud() { - long rate = detectRate(GPS_RX) + detectRate(GPS_RX) + detectRate(GPS_RX); - rate = rate / 3l; - long baud = 0; - /* - Time Baud Rate + long rate = detectRate(GPS_RX) + detectRate(GPS_RX) + detectRate(GPS_RX); + rate = rate / 3l; + long baud = 0; + /* + Time Baud Rate 3333Β΅s (3.3ms)300 833Β΅s 1200 416Β΅s 2400 @@ -239,138 +267,283 @@ long Gps::autoBaud() 17.3Β΅s 57600 8Β΅s 115200 Megas min is about 10uS? so there may be some inaccuracy - */ - if (rate < 12) - baud = 115200; - else if (rate < 20) - baud = 57600; - else if (rate < 30) - baud = 38400; - else if (rate < 40) - baud = 28800; - else if (rate < 60) - baud = 19200; - else if (rate < 80) - baud = 14400; - else if (rate < 150) - baud = 9600; - else if (rate < 300) - baud = 4800; - else if (rate < 600) - baud = 2400; - else if (rate < 1200) - baud = 1200; - else - baud = 0; - - return baud; + */ + if (rate < 12) + baud = 115200; + else if (rate < 20) + baud = 57600; + else if (rate < 30) + baud = 38400; + else if (rate < 40) + baud = 28800; + else if (rate < 60) + baud = 19200; + else if (rate < 80) + baud = 14400; + else if (rate < 150) + baud = 9600; + else if (rate < 300) + baud = 4800; + else if (rate < 600) + baud = 2400; + else if (rate < 1200) + baud = 1200; + else + baud = 0; + + return baud; } /** - * @brief Check if the speed has changed - * - * @return true if speed has changed, false otherwise + * @brief Check if the speed has changed. + * + * @details Compares the current speed with the previous value and updates the previous value if changed. + * + * @return true if speed has changed, false otherwise. */ bool Gps::isSpeedChanged() { - if (gpsData.speed != previousSpeed) - { - previousSpeed = gpsData.speed; - return true; - } - return false; + if (gpsData.speed != previousSpeed) + { + previousSpeed = gpsData.speed; + return true; + } + return false; } /** - * @brief Check if the altitude has changed - * - * @return true if altitude has changed, false otherwise + * @brief Check if the altitude has changed. + * + * @details Compares the current altitude with the previous value and updates the previous value if changed. + * + * @return true if altitude has changed, false otherwise. */ bool Gps::isAltitudeChanged() { - if (gpsData.altitude != previousAltitude) - { - previousAltitude = gpsData.altitude; - return true; - } - return false; + if (gpsData.altitude != previousAltitude) + { + previousAltitude = gpsData.altitude; + return true; + } + return false; } + /** - * @brief Check if the latitude or longitude has changed - * - * @return true if latitude or longitude has changed, false otherwise + * @brief Check if the latitude or longitude has changed. + * + * @details Compares the current latitude and longitude with the previous values and updates them if changed. + * + * @return true if latitude or longitude has changed, false otherwise. */ bool Gps::hasLocationChange() { - if (gpsData.latitude != previousLatitude || gpsData.longitude != previousLongitude) - { - previousLatitude = gpsData.latitude; - previousLongitude = gpsData.longitude; - return true; - } - return false; + if (gpsData.latitude != previousLatitude || gpsData.longitude != previousLongitude) + { + previousLatitude = gpsData.latitude; + previousLongitude = gpsData.longitude; + return true; + } + return false; } /** - * @brief Check if the PDOP, HDOP, or VDOP has changed - * - * @return true if PDOP, HDOP, or VDOP has changed, false otherwise + * @brief Check if the PDOP, HDOP, or VDOP has changed. + * + * @details Compares the current DOP values with the previous values and updates them if changed. + * + * @return true if PDOP, HDOP, or VDOP has changed, false otherwise. */ bool Gps::isDOPChanged() { - if (gpsData.pdop != previousPdop || gpsData.hdop != previousHdop || gpsData.vdop != previousVdop) - { - previousPdop = gpsData.pdop; - previousHdop = gpsData.hdop; - previousVdop = gpsData.vdop; - return true; - } - return false; + if (gpsData.pdop != previousPdop || gpsData.hdop != previousHdop || gpsData.vdop != previousVdop) + { + previousPdop = gpsData.pdop; + previousHdop = gpsData.hdop; + previousVdop = gpsData.vdop; + return true; + } + return false; } /** - * @brief set local time from GPS time and TZ - * - * @param gpsTime NeoGPS::time_t to convert - * @param tz timezone TZ + * @brief Set system local time from GPS time and timezone. + * + * @details Converts the provided GPS time to a struct tm, sets the system time, applies the timezone, + * and logs both the local and UTC time. Also calculates and stores the UTC offset in gpsData.UTC. + * + * @param gpsTime The GPS time (NeoGPS::time_t) to set as system time. + * @param tz The timezone string (POSIX TZ format). */ void Gps::setLocalTime(NeoGPS::time_t gpsTime, const char* tz) { - struct tm timeinfo; - timeinfo.tm_year = (2000 + gpsTime.year) - 1900; - timeinfo.tm_mon = gpsTime.month - 1; - timeinfo.tm_mday = gpsTime.date; - timeinfo.tm_hour = gpsTime.hours; - timeinfo.tm_min = gpsTime.minutes; - timeinfo.tm_sec = gpsTime.seconds; - struct timeval now = { .tv_sec = mktime(&timeinfo) }; - settimeofday(&now, NULL); - - setenv("TZ",tz,1); - tzset(); - - time_t tLocal = time(NULL); - time_t tUTC = time(NULL); - struct tm local_tm; - struct tm UTC_tm; - struct tm *tmLocal = localtime_r(&tLocal, &local_tm); - struct tm *tmUTC = gmtime_r(&tUTC, &UTC_tm); - - char buffer[100]; - strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %Z", tmLocal); - ESP_LOGI(TAG, "Current local time: %s",buffer); - strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %Z", tmUTC); - ESP_LOGI(TAG, "Current UTC time: %s", buffer); - - - int UTC = tmLocal->tm_hour - tmUTC->tm_hour; - if (UTC > 12) - UTC -= 24; - else if (UTC < -12) - UTC += 24; - - gpsData.UTC = UTC; - - ESP_LOGI(TAG, "UTC: %i", UTC); -} \ No newline at end of file + struct tm timeinfo; + timeinfo.tm_year = (2000 + gpsTime.year) - 1900; + timeinfo.tm_mon = gpsTime.month - 1; + timeinfo.tm_mday = gpsTime.date; + timeinfo.tm_hour = gpsTime.hours; + timeinfo.tm_min = gpsTime.minutes; + timeinfo.tm_sec = gpsTime.seconds; + struct timeval now = { .tv_sec = mktime(&timeinfo) }; + settimeofday(&now, NULL); + + setenv("TZ",tz,1); + tzset(); + + time_t tLocal = time(NULL); + time_t tUTC = time(NULL); + struct tm local_tm; + struct tm UTC_tm; + struct tm *tmLocal = localtime_r(&tLocal, &local_tm); + struct tm *tmUTC = gmtime_r(&tUTC, &UTC_tm); + + char buffer[100]; + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %Z", tmLocal); + ESP_LOGI(TAG, "Current local time: %s",buffer); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %Z", tmUTC); + ESP_LOGI(TAG, "Current UTC time: %s", buffer); + + + int UTC = tmLocal->tm_hour - tmUTC->tm_hour; + if (UTC > 12) + UTC -= 24; + else if (UTC < -12) + UTC += 24; + + gpsData.UTC = UTC; + + ESP_LOGI(TAG, "UTC: %i", UTC); +} + +/** + * @brief Simulates a GPS signal over a preloaded track. + * + * @details Advances through the provided track data, simulating GPS coordinates and heading. + * Applies random offset noise and smoothing to emulate realistic GPS signal behavior. + * Updates the simulated GPS data every second if the step distance is above a threshold. + * + * @param trackData Vector of wayPoints representing the preloaded GPX track. + * @param speed Simulated speed in km/h to assign to the GPS data. + * @param refresh Simulation update rate refresh in ms + */ +void Gps::simFakeGPS(const std::vector& trackData, uint16_t speed, uint16_t refresh) +{ + if (millis() - lastSimulationTime > refresh) + { + lastSimulationTime = millis(); + + if (simulationIndex < (int)trackData.size() - 2) + { + if (simulationIndex == 0) + { + // --- First point: initialize simulation state --- + smoothedLat = trackData[0].lat; + smoothedLon = trackData[0].lon; + lastSimLat = smoothedLat; + lastSimLon = smoothedLon; + filteredHeading = 0.0f; + accumulatedDist = 0.0f; // Reset accumulated distance for new track + + gpsData.latitude = smoothedLat; + gpsData.longitude = smoothedLon; + gpsData.heading = filteredHeading; + gpsData.speed = speed; + } + else + { + float rawLat = trackData[simulationIndex].lat; + float rawLon = trackData[simulationIndex].lon; + + // Calculate expected distance based on speed and time + float expectedDist = (speed * 1000.0f) / 3600.0f; // Convert km/h to m/s + + // Advance through track points until we've covered the expected distance + int currentIndex = simulationIndex; + int pointsAdvanced = 0; + const float maxSegmentDist = 100.0f; // Filter out unrealistic jumps (>100m) + + // Add expected distance to accumulated distance + accumulatedDist += expectedDist; + + while (currentIndex < (int)trackData.size() - 1 && pointsAdvanced < 10) { // Limit to prevent infinite loops + int nextIndex = currentIndex + 1; + float segmentDist = calcDist(trackData[currentIndex].lat, trackData[currentIndex].lon, + trackData[nextIndex].lat, trackData[nextIndex].lon); + + // Skip unrealistic jumps or duplicate points + if (segmentDist > maxSegmentDist || segmentDist < 0.1f) { + currentIndex = nextIndex; + continue; + } + + // Check if we can advance to this point + if (segmentDist <= accumulatedDist) { + accumulatedDist -= segmentDist; + currentIndex = nextIndex; + pointsAdvanced++; + } else { + break; // Not enough accumulated distance + } + } + + // Update simulation index to the final point + simulationIndex = currentIndex; + + // Update position to the final point + rawLat = trackData[simulationIndex].lat; + rawLon = trackData[simulationIndex].lon; + + // --- Apply smoothing BEFORE adding noise --- + smoothedLat = posAlpha * rawLat + (1.0f - posAlpha) * smoothedLat; + smoothedLon = posAlpha * rawLon + (1.0f - posAlpha) * smoothedLon; + + // --- Small noise to simulate GPS jitter --- + float latOffset = random(-3, 3) / 100000.0f; // Reduced noise for simulation + float lonOffset = random(-3, 3) / 100000.0f; + + float noisyLat = smoothedLat + latOffset; + float noisyLon = smoothedLon + lonOffset; + + // --- Realistic heading based on track direction --- + // Look ahead based on speed (faster = further lookahead) + int lookAhead = min(max(3, speed / 20), (int)trackData.size() - simulationIndex - 1); + int targetIdx = simulationIndex + lookAhead; + + if (targetIdx < (int)trackData.size()) { + // Calculate heading towards future track point + float targetHeading = calcCourse(smoothedLat, smoothedLon, + trackData[targetIdx].lat, + trackData[targetIdx].lon); + + if (simulationIndex > 1) { + // Smooth transition to target heading (faster adaptation for higher speeds) + float headingDiff = calcAngleDiff(targetHeading, filteredHeading); + float adaptationRate = min(0.3f, 0.1f + (speed / 200.0f)); // 0.1-0.3 based on speed + filteredHeading += adaptationRate * headingDiff; + } else { + // Initialize with target heading + filteredHeading = targetHeading; + } + + // Normalize final heading + if (filteredHeading < 0.0f) filteredHeading += 360.0f; + if (filteredHeading >= 360.0f) filteredHeading -= 360.0f; + } + + // --- Final output --- + gpsData.latitude = noisyLat; + gpsData.longitude = noisyLon; + gpsData.heading = filteredHeading; + gpsData.speed = speed; + + lastSimLat = rawLat; + lastSimLon = rawLon; + } + simulationIndex++; + } + else + { + ESP_LOGI(TAG,"End of GPS signal simulation"); + } + } +} diff --git a/lib/gps/gps.hpp b/lib/gps/gps.hpp index 6b884700..f1b853da 100644 --- a/lib/gps/gps.hpp +++ b/lib/gps/gps.hpp @@ -2,106 +2,143 @@ * @file gps.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief GPS definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once +#include "globalGpxDef.h" #include #include #include "settings.hpp" +#include -#define DEG2RAD(a) ((a) / (180 / M_PI)) // Convert degrees to radians -#define RAD2DEG(a) ((a) * (180 / M_PI)) // Convert radians to degrees -extern uint8_t GPS_TX; -extern uint8_t GPS_RX; +extern uint8_t GPS_TX; /**< GPS TX pin number. */ +extern uint8_t GPS_RX; /**< GPS RX pin number. */ -#define MAX_SATELLITES 120 -#define MAX_SATELLLITES_IN_VIEW 32 +#define MAX_SATELLITES 120 /**< Maximum number of satellites supported. */ +#define MAX_SATELLLITES_IN_VIEW 32 /**< Maximum number of satellites in view. */ -#define DEBUG_PORT Serial -#define gpsPort Serial2 -#define GPS_PORT_NAME "Serial2" -extern gps_fix fix; -extern NMEAGPS GPS; +#define DEBUG_PORT Serial /**< Serial port used for debug output. */ +#define gpsPort Serial2 /**< Serial port used for GPS communication. */ +#define GPS_PORT_NAME "Serial2" /**< Name of the GPS serial port. */ -extern bool setTime; -void calculateSun(); -const char* getPosixTZ(const char* name); +extern gps_fix fix; /**< Latest parsed GPS fix data. */ +extern NMEAGPS GPS; /**< NMEAGPS parser instance. */ -extern bool isGpsFixed; -extern bool isTimeFixed; -extern long gpsBaudDetected; -extern bool nmea_output_enable; +extern bool setTime; /**< Indicates if time should be set from GPS. */ +void calculateSun(); /**< Calculates sunrise and sunset times based on current GPS data. */ -static unsigned long GPS_BAUD[] PROGMEM = {4800, 9600, 19200, 0}; -static const char *GPS_BAUD_PCAS[] PROGMEM = {"$PCAS01,0*1C\r\n", "$PCAS01,1*1D\r\n", "$PCAS01,2*1E\r\n"}; -static const char *GPS_RATE_PCAS[] PROGMEM = {"$PCAS02,1000*2E\r\n", "$PCAS02,500*1A\r\n", "$PCAS02,250*18\r\n", "$PCAS02,200*1D\r\n", "$PCAS02,100*1E\r\n"}; +const char* getPosixTZ(const char* name); /**< Gets the POSIX time zone string for a given time zone name. @param name Time zone name (e.g., "CET", "UTC"). @return POSIX TZ string. */ + +extern bool isGpsFixed; /**< Indicates whether a valid GPS fix has been acquired. */ +extern bool isTimeFixed; /**< Indicates whether the system time has been fixed using GPS. */ +extern long gpsBaudDetected; /**< Detected GPS baud rate. */ +extern bool nmea_output_enable; /**< Enables or disables NMEA output. */ + + +static unsigned long GPS_BAUD[] PROGMEM = {4800, 9600, 19200, 0}; /**< Supported GPS baud rates. */ +static const char *GPS_BAUD_PCAS[] PROGMEM = {"$PCAS01,0*1C\r\n", "$PCAS01,1*1D\r\n", "$PCAS01,2*1E\r\n"}; /**< NMEA command strings to set baud rate for PCAS modules. */ +static const char *GPS_RATE_PCAS[] PROGMEM = {"$PCAS02,1000*2E\r\n", "$PCAS02,500*1A\r\n", "$PCAS02,250*18\r\n", "$PCAS02,200*1D\r\n", "$PCAS02,100*1E\r\n"}; /**< NMEA command strings to set update rate for PCAS modules. */ /** * @brief Satellite Constellation Canvas Definition * */ -static const uint8_t canvasOffset = 15; -static const uint8_t canvasSize = 180; -static const uint8_t canvasCenter_X = canvasSize / 2; -static const uint8_t canvasCenter_Y = canvasSize / 2; -static const uint8_t canvasRadius = canvasCenter_X - canvasOffset; +static const uint8_t canvasOffset = 15; /**< Offset from the edge to start drawing the satellite constellation canvas */ +static const uint8_t canvasSize = 180; /**< Total size (width and height) of the constellation canvas */ +static const uint8_t canvasCenter_X = canvasSize / 2; /**< X coordinate of the canvas center */ +static const uint8_t canvasCenter_Y = canvasSize / 2; /**< Y coordinate of the canvas center */ +static const uint8_t canvasRadius = canvasCenter_X - canvasOffset; /**< Radius of the drawable area for the constellation */ + +/** + * @class GPS + * @brief Provides high-level access to GPS data and configuration. + * + * @details This class manages the GPS module, parsing NMEA data, and provides methods to access + * location, speed, satellite count, and other information. + */ class Gps { public: - Gps(); - void init(); - double getLat(); - double getLon(); - void getGPSData(); - long detectRate(int rxPin); - long autoBaud(); - bool isSpeedChanged(); - bool isAltitudeChanged(); - bool hasLocationChange(); - bool isDOPChanged(); - void setLocalTime(NeoGPS::time_t gpsTime, const char* tz); - - struct GPSDATA - { - uint8_t satellites; - uint8_t fixMode; - int16_t altitude; - uint16_t speed; - double latitude; - double longitude; - uint16_t heading; - float hdop; - float pdop; - float vdop; - uint8_t satInView; - char sunriseHour[6]; - char sunsetHour[6]; - int UTC; - } gpsData; - - struct SV - { - bool active; - uint8_t satNum; - uint8_t elev; - uint16_t azim; - uint8_t snr; - uint16_t posX; - uint16_t posY; - char talker_id[3]; - } satTracker[MAX_SATELLITES]; + Gps(); + void init(); + float getLat(); + float getLon(); + void getGPSData(); + long detectRate(int rxPin); + long autoBaud(); + bool isSpeedChanged(); + bool isAltitudeChanged(); + bool hasLocationChange(); + bool isDOPChanged(); + void setLocalTime(NeoGPS::time_t gpsTime, const char* tz); + void simFakeGPS(const std::vector& trackData, uint16_t speed, uint16_t refresh); + + /** + * @struct GPSDATA + * @brief Holds parsed GPS data for easy access. + */ + struct GPSDATA + { + uint8_t satellites; /**< Number of satellites used for fix. */ + uint8_t fixMode; /**< GPS fix mode. */ + int16_t altitude; /**< Altitude in meters. */ + uint16_t speed; /**< Speed in km/h or knots. */ + float latitude; /**< Latitude in decimal degrees. */ + float longitude; /**< Longitude in decimal degrees. */ + uint16_t heading; /**< Heading in degrees. */ + float hdop; /**< Horizontal dilution of precision. */ + float pdop; /**< Position dilution of precision. */ + float vdop; /**< Vertical dilution of precision. */ + uint8_t satInView; /**< Number of satellites in view. */ + char sunriseHour[6]; /**< Sunrise time as string (HH:MM). */ + char sunsetHour[6]; /**< Sunset time as string (HH:MM). */ + int UTC; /**< UTC offset. */ + } gpsData; + + /** + * @struct SV + * @brief Holds information about a tracked satellite. + */ + struct SV + { + bool active; /**< True if the satellite is active. */ + uint8_t satNum; /**< Satellite number. */ + uint8_t elev; /**< Elevation in degrees. */ + uint16_t azim; /**< Azimuth in degrees. */ + uint8_t snr; /**< Signal-to-noise ratio. */ + uint16_t posX; /**< X position for display/map. */ + uint16_t posY; /**< Y position for display/map. */ + char talker_id[3]; /**< NMEA talker ID. */ + } satTracker[MAX_SATELLITES]; private: - uint16_t previousSpeed; - int16_t previousAltitude; - double previousLatitude; - double previousLongitude; - float previousHdop; - float previousPdop; - float previousVdop; + uint16_t previousSpeed; /**< Previous speed value for change detection. */ + int16_t previousAltitude; /**< Previous altitude value for change detection. */ + float previousLatitude; /**< Previous latitude for change detection. */ + float previousLongitude; /**< Previous longitude for change detection. */ + float previousHdop; /**< Previous HDOP for change detection. */ + float previousPdop; /**< Previous PDOP for change detection. */ + float previousVdop; /**< Previous VDOP for change detection. */ + + /** + * @brief Variables for "fake" GPS signal from loaded track (simulation) + * + */ + const float posAlpha = 0.6f; /**< Position smoothing factor, range 0 (no smoothing) to 1 (full smoothing) */ + const float headAlpha = 0.5f; /**< Heading smoothing factor, controls how fast heading adapts */ + const float minStepDist = 3.0f; /**< Minimum distance in meters between simulation steps to update */ + const int headingLookahead = 5; /**< Number of track points ahead used to calculate the heading */ + float smoothedLat = 0.0f; /**< Smoothed latitude after filtering */ + float smoothedLon = 0.0f; /**< Smoothed longitude after filtering */ + float filteredHeading = 0.0f; /**< Smoothed heading after filtering */ + float lastSimLat = 0.0f; /**< Last simulated latitude used for step distance */ + float lastSimLon = 0.0f; /**< Last simulated longitude used for step distance */ + float accumulatedDist = 0.0f; /**< Accumulated distance for multi-second simulation */ + int simulationIndex = 0; /**< Current index in track simulation */ + unsigned long lastSimulationTime = 0; /**< Timestamp of last simulation update in milliseconds */ }; \ No newline at end of file diff --git a/lib/gpx/src/globalGpxDef.h b/lib/gpx/src/globalGpxDef.h index 3738b20d..46a00eb7 100644 --- a/lib/gpx/src/globalGpxDef.h +++ b/lib/gpx/src/globalGpxDef.h @@ -2,8 +2,8 @@ * @file globalGpxDef.h * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Global GPX Variables - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,53 +11,64 @@ #include #include -static const char* wptFile PROGMEM = "/sdcard/WPT/waypoint.gpx"; -static const char* wptFolder PROGMEM = "/sdcard/WPT"; -static const char* trkFolder PROGMEM = "/sdcard/TRK"; +static const char* wptFile PROGMEM = "/sdcard/WPT/waypoint.gpx"; /**< Path to the waypoint GPX file on the SD card. */ +static const char* wptFolder PROGMEM = "/sdcard/WPT"; /**< Path to the waypoint folder on the SD card. */ +static const char* trkFolder PROGMEM = "/sdcard/TRK"; /**< Path to the track folder on the SD card. */ /** * @brief Waypoint action enum * + * @details Enumeration of possible actions for GPX waypoints. */ enum gpxAction_t { - WPT_NONE, - WPT_ADD, - GPX_LOAD, - GPX_EDIT, - GPX_DEL, + WPT_NONE, /**< No waypoint action. */ + WPT_ADD, /**< Add a new waypoint. */ + GPX_LOAD, /**< Load waypoints from GPX file. */ + GPX_EDIT, /**< Edit an existing waypoint. */ + GPX_DEL, /**< Delete a waypoint. */ }; -/** - * @brief Waypoint Action - * - */ -extern uint8_t gpxAction; +extern uint8_t gpxAction; /**< Indicates the current GPX waypoint action to be performed. */ /** * @brief Waypoint Structure * + * @details Stores information related to a GPS waypoint. */ struct wayPoint { - double lat; - double lon; - float ele; - char* time; - char* name; - char* desc; - char* src; - char* sym; - char* type; - uint8_t sat; - float hdop; - float vdop; - float pdop; + float lat; /**< Latitude of the waypoint. */ + float lon; /**< Longitude of the waypoint. */ + float ele; /**< Elevation of the waypoint. */ + char* time; /**< Timestamp of the waypoint (ISO 8601). */ + char* name; /**< Name of the waypoint. */ + char* desc; /**< Description of the waypoint. */ + char* src; /**< Source of the waypoint data. */ + char* sym; /**< Symbol associated with the waypoint. */ + char* type; /**< Type/category of the waypoint. */ + uint8_t sat; /**< Number of satellites used for this fix. */ + float hdop; /**< Horizontal dilution of precision. */ + float vdop; /**< Vertical dilution of precision. */ + float pdop; /**< Position dilution of precision. */ }; +/** + * @brief Track turn points structure + * + * @details Structure representing a track turn point + */ +struct TurnPoint +{ + int idx; /**< Index of the track point */ + float angle; /**< Turn angle at this point (positive = right, negative = left) */ + float distance; /**< Distance from start to this point (in meters) */ +}; + /** * @Brief GPX header file format * + * @details Static string containing the standard GPX 1.0 file header, to be used when creating new GPX files. */ static const char* gpxHeader PROGMEM = { "\n" "> Map where the key is the file name and the value is a vector of tag element value. + * @return std::map> Map where the key is the file name and the value is a vector of tag element values. */ std::map> GPXParser::getTagElementList(const char* tag, const char* element, const std::string& folderPath) { - std::map> elementsByFile; - - DIR* dir = opendir(folderPath.c_str()); - if (!dir) - { - ESP_LOGE(TAGGPX, "Failed to open folder: %s", folderPath.c_str()); - return elementsByFile; - } - - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) - { - if (entry->d_type == DT_REG) - { - std::string fileName = entry->d_name; - - if (fileName.size() >= 4 && fileName.substr(fileName.size() - 4) == ".gpx") - { - std::string filePath = folderPath + "/" + fileName; - - GPXParser parser(filePath.c_str()); - std::vector elementValue; - - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result == tinyxml2::XML_SUCCESS) - { - tinyxml2::XMLElement* root = doc.RootElement(); - if (root) - { - for (tinyxml2::XMLElement* Tag = root->FirstChildElement(tag); Tag!= nullptr; Tag = Tag->NextSiblingElement(tag)) - { - tinyxml2::XMLElement* valueElement = Tag->FirstChildElement(element); - if (valueElement) - { - const char* value = valueElement->GetText(); - if (value) - elementValue.push_back(value); - } - } - } - else - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - } - else - ESP_LOGE(TAGGPX, "Failed to load GPX file: %s", filePath.c_str()); - - elementsByFile[fileName] = elementValue; - } - } - } - - closedir(dir); - return elementsByFile; + std::map> elementsByFile; + + DIR* dir = opendir(folderPath.c_str()); + if (!dir) + { + ESP_LOGE(TAGGPX, "Failed to open folder: %s", folderPath.c_str()); + return elementsByFile; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) + { + if (entry->d_type == DT_REG) + { + std::string fileName = entry->d_name; + + if (fileName.size() >= 4 && fileName.substr(fileName.size() - 4) == ".gpx") + { + std::string filePath = folderPath + "/" + fileName; + + GPXParser parser(filePath.c_str()); + std::vector elementValue; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result == tinyxml2::XML_SUCCESS) + { + tinyxml2::XMLElement* root = doc.RootElement(); + if (root) + { + for (tinyxml2::XMLElement* Tag = root->FirstChildElement(tag); Tag!= nullptr; Tag = Tag->NextSiblingElement(tag)) + { + tinyxml2::XMLElement* valueElement = Tag->FirstChildElement(element); + if (valueElement) + { + const char* value = valueElement->GetText(); + if (value) + elementValue.push_back(value); + } + } + } + else + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + } + else + ESP_LOGE(TAGGPX, "Failed to load GPX file: %s", filePath.c_str()); + + elementsByFile[fileName] = elementValue; + } + } + } + + closedir(dir); + return elementsByFile; } /** * @brief Delete a tag from the GPX file with specified name value * - * @param tag - * @param name The name value to delete - * @return true if the waypoint was deleted successfully, false otherwise + * @details Loads the GPX file, searches for the first tag whose element matches the provided name, + * deletes it, and saves the file. Returns true if deletion was successful, false otherwise. + * + * @param tag The XML tag to search for (e.g., "wpt" for waypoint). + * @param name The name value to match for deletion. + * @return true if the tag was deleted successfully, false otherwise. */ bool GPXParser::deleteTagByName(const char* tag, const char* name) { - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* root = doc.RootElement(); - if (!root) - { - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - return false; - } - - for (tinyxml2::XMLElement* Tag = root->FirstChildElement(tag); Tag != nullptr; Tag = Tag->NextSiblingElement(tag)) - { - tinyxml2::XMLElement* nameElement = Tag->FirstChildElement(gpxNameElem); - if (nameElement && strcmp(nameElement->GetText(), name) == 0) - { - root->DeleteChild(Tag); - result = doc.SaveFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); - return false; - } - - // if (!root->FirstChildElement(gpxWaypointTag)) - // { - // if (!root->FirstChildElement(gpxTrackTag)) - // { - // if (remove(filePath.c_str()) != 0) - // { - // ESP_LOGE(TAGGPX, "Failed to delete file: %s", filePath.c_str()); - // return false; - // } - // ESP_LOGI(TAGGPX, "File %s deleted as it had no waypoints or tracks left", filePath.c_str()); - // } - // else - // { - // ESP_LOGI(TAGGPX, "File %s not deleted as it contains tracks but no waypoints", filePath.c_str()); - // } - // } - return true; - } - } - - ESP_LOGE(TAGGPX, "Tag with name %s not found in file: %s", name, filePath.c_str()); - return false; + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) + { + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + return false; + } + + for (tinyxml2::XMLElement* Tag = root->FirstChildElement(tag); Tag != nullptr; Tag = Tag->NextSiblingElement(tag)) + { + tinyxml2::XMLElement* nameElement = Tag->FirstChildElement(gpxNameElem); + if (nameElement && strcmp(nameElement->GetText(), name) == 0) + { + root->DeleteChild(Tag); + result = doc.SaveFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); + return false; + } + + // if (!root->FirstChildElement(gpxWaypointTag)) + // { + // if (!root->FirstChildElement(gpxTrackTag)) + // { + // if (remove(filePath.c_str()) != 0) + // { + // ESP_LOGE(TAGGPX, "Failed to delete file: %s", filePath.c_str()); + // return false; + // } + // ESP_LOGI(TAGGPX, "File %s deleted as it had no waypoints or tracks left", filePath.c_str()); + // } + // else + // { + // ESP_LOGI(TAGGPX, "File %s not deleted as it contains tracks but no waypoints", filePath.c_str()); + // } + // } + return true; + } + } + + ESP_LOGE(TAGGPX, "Tag with name %s not found in file: %s", name, filePath.c_str()); + return false; } /** * @brief Retrieve waypoint details for a given name from a GPX file * + * @details Searches the GPX file for a waypoint with the given name and returns its details. + * * @param name Waypoint name to search for - * @return wayPoint Struct containing waypoint details + * @return wayPoint Struct containing waypoint details (fields are zeroed/empty if not found) */ wayPoint GPXParser::getWaypointInfo(const char* name) { - wayPoint wp = {0}; - - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); - return wp; - } - - tinyxml2::XMLElement* root = doc.RootElement(); - if (!root) - { - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - return wp; - } - - tinyxml2::XMLElement* wpt = nullptr; - for (wpt = root->FirstChildElement(gpxWaypointTag); wpt != nullptr; wpt = wpt->NextSiblingElement(gpxWaypointTag)) - { - tinyxml2::XMLElement* nameElement = wpt->FirstChildElement(gpxNameElem); - if (nameElement && strcmp(nameElement->GetText(), name) == 0) - { - wp.name = strdup(nameElement->GetText()); - break; - } - } - - if (wpt) - { - wpt->QueryDoubleAttribute(gpxLatElem, &wp.lat); - wpt->QueryDoubleAttribute(gpxLonElem, &wp.lon); - - tinyxml2::XMLElement* element = nullptr; - - element = wpt->FirstChildElement(gpxEleElem); - if (element) - wp.ele = static_cast(element->DoubleText()); - - element = wpt->FirstChildElement(gpxTimeElem); - if (element) - wp.time = strdup(element->GetText()); - - element = wpt->FirstChildElement(gpxDescElem); - if (element) - wp.desc = strdup(element->GetText()); - - element = wpt->FirstChildElement(gpxSrcElem); - if (element) - wp.src = strdup(element->GetText()); - - element = wpt->FirstChildElement(gpxSymElem); - if (element) - wp.sym = strdup(element->GetText()); - - element = wpt->FirstChildElement(gpxTypeElem); - if (element) - wp.type = strdup(element->GetText()); - - element = wpt->FirstChildElement(gpxSatElem); - if (element) - wp.sat = static_cast(element->UnsignedText()); - - element = wpt->FirstChildElement(gpxHdopElem); - if (element) - wp.hdop = static_cast(element->DoubleText()); - - element = wpt->FirstChildElement(gpxVdopElem); - if (element) - wp.vdop = static_cast(element->DoubleText()); - - element = wpt->FirstChildElement(gpxPdopElem); - if (element) - wp.pdop = static_cast(element->DoubleText()); - } - else - ESP_LOGE(TAGGPX, "Waypoint with name %s not found in file: %s", name, filePath.c_str()); - - return wp; + wayPoint wp = {0}; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); + return wp; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) + { + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + return wp; + } + + tinyxml2::XMLElement* wpt = nullptr; + for (wpt = root->FirstChildElement(gpxWaypointTag); wpt != nullptr; wpt = wpt->NextSiblingElement(gpxWaypointTag)) + { + tinyxml2::XMLElement* nameElement = wpt->FirstChildElement(gpxNameElem); + if (nameElement && strcmp(nameElement->GetText(), name) == 0) + { + wp.name = strdup(nameElement->GetText()); + break; + } + } + + if (wpt) + { + wpt->QueryFloatAttribute(gpxLatElem, &wp.lat); + wpt->QueryFloatAttribute(gpxLonElem, &wp.lon); + + tinyxml2::XMLElement* element = nullptr; + + element = wpt->FirstChildElement(gpxEleElem); + if (element) + wp.ele = static_cast(element->DoubleText()); + + element = wpt->FirstChildElement(gpxTimeElem); + if (element) + wp.time = strdup(element->GetText()); + + element = wpt->FirstChildElement(gpxDescElem); + if (element) + wp.desc = strdup(element->GetText()); + + element = wpt->FirstChildElement(gpxSrcElem); + if (element) + wp.src = strdup(element->GetText()); + + element = wpt->FirstChildElement(gpxSymElem); + if (element) + wp.sym = strdup(element->GetText()); + + element = wpt->FirstChildElement(gpxTypeElem); + if (element) + wp.type = strdup(element->GetText()); + + element = wpt->FirstChildElement(gpxSatElem); + if (element) + wp.sat = static_cast(element->UnsignedText()); + + element = wpt->FirstChildElement(gpxHdopElem); + if (element) + wp.hdop = static_cast(element->DoubleText()); + + element = wpt->FirstChildElement(gpxVdopElem); + if (element) + wp.vdop = static_cast(element->DoubleText()); + + element = wpt->FirstChildElement(gpxPdopElem); + if (element) + wp.pdop = static_cast(element->DoubleText()); + } + else + ESP_LOGE(TAGGPX, "Waypoint with name %s not found in file: %s", name, filePath.c_str()); + + return wp; } - + /** * @brief Add a new waypoint to the GPX file * + * @details Adds a new waypoint with the provided details to the GPX file. All required GPX elements are populated, + * and the waypoint is inserted at the end of the waypoint list. Returns true if the waypoint was added successfully, + * false otherwise. + * * @param wp Waypoint struct containing the waypoint details * @return true if the waypoint was added successfully, false otherwise */ bool GPXParser::addWaypoint(const wayPoint& wp) { - time_t tUTCwpt = time(NULL); - struct tm UTCwpt_tm; - struct tm *tmUTCwpt = gmtime_r(&tUTCwpt, &UTCwpt_tm); - char textFmt[30]; - strftime(textFmt, sizeof(textFmt), "%Y-%m-%dT%H:%M:%SZ", tmUTCwpt); - - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* root = doc.RootElement(); - if (!root) - { - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* newWpt = doc.NewElement(gpxWaypointTag); - newWpt->SetAttribute(gpxLatElem, formatDouble(wp.lat, 6).c_str()); - newWpt->SetAttribute(gpxLonElem, formatDouble(wp.lon, 6).c_str()); - - tinyxml2::XMLElement* element = nullptr; - - element = doc.NewElement(gpxEleElem); - element->SetText(wp.ele); - newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxTimeElem); - element->SetText(textFmt); - newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxNameElem); - element->SetText(wp.name ? wp.name : ""); - newWpt->InsertEndChild(element); - - // element = doc.NewElement(gpxDescElem); - // element->SetText(wp.desc ? wp.desc : ""); - // newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxSrcElem); - element->SetText(wp.src ? wp.src : "IceNav"); - newWpt->InsertEndChild(element); - - // element = doc.NewElement(gpxSymElem); - // element->SetText(wp.sym ? wp.sym : ""); - // newWpt->InsertEndChild(element); - - // element = doc.NewElement(gpxTypeElem); - // element->SetText(wp.type ? wp.type : ""); - // newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxSatElem); - element->SetText(wp.sat); - newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxHdopElem); - element->SetText(wp.hdop); - newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxVdopElem); - element->SetText(wp.vdop); - newWpt->InsertEndChild(element); - - element = doc.NewElement(gpxPdopElem); - element->SetText(wp.pdop); - newWpt->InsertEndChild(element); - - tinyxml2::XMLElement* lastWpt = root->LastChildElement(gpxWaypointTag); - if (lastWpt) - root->InsertAfterChild(lastWpt, newWpt); - else - root->InsertFirstChild(newWpt); - - result = doc.SaveFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); - return false; - } - return result == tinyxml2::XML_SUCCESS; + time_t tUTCwpt = time(NULL); + struct tm UTCwpt_tm; + struct tm *tmUTCwpt = gmtime_r(&tUTCwpt, &UTCwpt_tm); + char textFmt[30]; + strftime(textFmt, sizeof(textFmt), "%Y-%m-%dT%H:%M:%SZ", tmUTCwpt); + + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) + { + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* newWpt = doc.NewElement(gpxWaypointTag); + newWpt->SetAttribute(gpxLatElem, formatFloat(wp.lat, 6).c_str()); + newWpt->SetAttribute(gpxLonElem, formatFloat(wp.lon, 6).c_str()); + + tinyxml2::XMLElement* element = nullptr; + + element = doc.NewElement(gpxEleElem); + element->SetText(wp.ele); + newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxTimeElem); + element->SetText(textFmt); + newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxNameElem); + element->SetText(wp.name ? wp.name : ""); + newWpt->InsertEndChild(element); + + // element = doc.NewElement(gpxDescElem); + // element->SetText(wp.desc ? wp.desc : ""); + // newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxSrcElem); + element->SetText(wp.src ? wp.src : "IceNav"); + newWpt->InsertEndChild(element); + + // element = doc.NewElement(gpxSymElem); + // element->SetText(wp.sym ? wp.sym : ""); + // newWpt->InsertEndChild(element); + + // element = doc.NewElement(gpxTypeElem); + // element->SetText(wp.type ? wp.type : ""); + // newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxSatElem); + element->SetText(wp.sat); + newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxHdopElem); + element->SetText(wp.hdop); + newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxVdopElem); + element->SetText(wp.vdop); + newWpt->InsertEndChild(element); + + element = doc.NewElement(gpxPdopElem); + element->SetText(wp.pdop); + newWpt->InsertEndChild(element); + + tinyxml2::XMLElement* lastWpt = root->LastChildElement(gpxWaypointTag); + if (lastWpt) + root->InsertAfterChild(lastWpt, newWpt); + else + root->InsertFirstChild(newWpt); + + result = doc.SaveFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); + return false; + } + return result == tinyxml2::XML_SUCCESS; } /** - * @brief Load GPX track data and store coordinates from '' structure. +* @brief Load GPX track data and store coordinates from '' structure. +* +* @details Loads all track points from the `` structure in the GPX file, extracting latitude and longitude for each ``, +* and stores them as `wayPoint` structures in the provided vector. Returns true if the operation was successful, or false on error. +* +* @param trackData Vector to store track points (each point is a wayPoint). +* @return true if the track data was loaded successfully, false otherwise. +*/ +bool GPXParser::loadTrack(std::vector& trackData) +{ + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) + { + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + return false; + } + + // Iterate through elements + for (tinyxml2::XMLElement* trk = root->FirstChildElement(gpxTrackTag); trk != nullptr; trk = trk->NextSiblingElement(gpxTrackTag)) + { + // Iterate through elements + for (tinyxml2::XMLElement* trkseg = trk->FirstChildElement("trkseg"); trkseg != nullptr; trkseg = trkseg->NextSiblingElement("trkseg")) + { + // Iterate through elements + for (tinyxml2::XMLElement* trkpt = trkseg->FirstChildElement("trkpt"); trkpt != nullptr; trkpt = trkpt->NextSiblingElement("trkpt")) + { + wayPoint point = {0}; + + // Extract latitude and longitude + trkpt->QueryFloatAttribute(gpxLatElem, &point.lat); + trkpt->QueryFloatAttribute(gpxLonElem, &point.lon); + + // // Extract optional elements + // tinyxml2::XMLElement* ele = trkpt->FirstChildElement("ele"); + // if (ele) point.ele = static_cast(ele->DoubleText()); + + // tinyxml2::XMLElement* time = trkpt->FirstChildElement("time"); + // if (time) point.time = strdup(time->GetText()); + + trackData.push_back(point); + } + } + } + + return true; +} + +// /** +// * @brief Detect turn points in a track using a sliding window approach. +// * +// * This function processes the given track using a sliding window of configurable size. +// * It calculates the angle between the start and end segments of the window to detect turns. +// * A turn point is added if the global angle exceeds `thresholdDeg` and the total distance +// * within the window is above `minDist`. Sharp turns above `sharpTurnDeg` are always included, +// * regardless of distance. +// * +// * @param thresholdDeg Minimum angle difference (in degrees) to consider a turn. +// * @param minDist Minimum total distance (in meters) within the window to validate a turn. +// * @param sharpTurnDeg Angle (in degrees) above which any turn is considered sharp and relevant. +// * @param windowSize Number of points before and after the center point to define the sliding window. +// * @param trackData Vector of wayPoint structures representing the track to analyze. +// * @return std::vector List of detected turn points: +// * - index: index in trackData of the turn +// * - angle: angle difference at turn (degrees, positive = right, negative = left) +// * - accumDist: accumulated distance from the start of the track to this turn (meters) +// */ +// std::vector GPXParser::getTurnPointsSlidingWindow( +// float thresholdDeg, float minDist, float sharpTurnDeg, +// int windowSize, const std::vector& trackData) +// { +// std::vector turnPoints; +// float accumDist = 0.0f; + +// if (trackData.size() < 2 * windowSize + 1) +// return turnPoints; + +// for (size_t i = windowSize; i < trackData.size() - windowSize; ++i) +// { +// float distWindow = 0.0f; +// for (int j = int(i - windowSize); j < int(i + windowSize); ++j) +// distWindow += calcDist(trackData[j].lat, trackData[j].lon, +// trackData[j+1].lat, trackData[j+1].lon); + +// float brgStart = calcCourse(trackData[i - windowSize].lat, trackData[i - windowSize].lon, +// trackData[i].lat, trackData[i].lon); +// float brgEnd = calcCourse(trackData[i].lat, trackData[i].lon, +// trackData[i + windowSize].lat, trackData[i + windowSize].lon); +// float diff = calcAngleDiff(brgEnd, brgStart); + +// accumDist += calcDist(trackData[i-1].lat, trackData[i-1].lon, +// trackData[i].lat, trackData[i].lon); + +// if (std::fabs(diff) > sharpTurnDeg) +// { +// turnPoints.push_back({static_cast(i), diff, accumDist}); +// continue; +// } +// if (distWindow < minDist) +// continue; +// if (std::fabs(diff) > thresholdDeg) +// turnPoints.push_back({static_cast(i), diff, accumDist}); +// } +// return turnPoints; +// } + +/** + * @brief Detects turn points in a GPX track using a sliding window approach. + * + * @details This function analyzes a track using a window of N points before and after each point + * to estimate the turning angle between segments. It avoids false positives by skipping + * windows with abnormally large jumps (e.g., GPS noise or corrupt data). * - * @param trackData Vector to store track points (each point is a wayPoint). - * @return true if the track data was loaded successfully, false otherwise. + * @param thresholdDeg Minimum angle (deg) to consider a turn. + * @param minDist Minimum distance (m) of the whole window to consider a valid curve. + * @param sharpTurnDeg Angle threshold (deg) for forced sharp turns. + * @param windowSize Number of points before/after to use in the window. + * @param trackData The vector of waypoints forming the track. + * @return A vector of TurnPoint structures. */ -bool GPXParser::loadTrack(std::vector& trackData) +std::vector GPXParser::getTurnPointsSlidingWindow( + float thresholdDeg, float minDist, float sharpTurnDeg, + int windowSize, const std::vector& trackData) { - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* root = doc.RootElement(); - if (!root) - { - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - return false; - } - - // Iterate through elements - for (tinyxml2::XMLElement* trk = root->FirstChildElement(gpxTrackTag); trk != nullptr; trk = trk->NextSiblingElement(gpxTrackTag)) - { - // Iterate through elements - for (tinyxml2::XMLElement* trkseg = trk->FirstChildElement("trkseg"); trkseg != nullptr; trkseg = trkseg->NextSiblingElement("trkseg")) + std::vector turnPoints; + float accumDist = 0.0f; + + if (trackData.size() < 2 * windowSize + 1) + return turnPoints; + + for (size_t i = windowSize; i < trackData.size() - windowSize; ++i) { - // Iterate through elements - for (tinyxml2::XMLElement* trkpt = trkseg->FirstChildElement("trkpt"); trkpt != nullptr; trkpt = trkpt->NextSiblingElement("trkpt")) - { - wayPoint point = {0}; + float distWindow = 0.0f; + bool skipWindow = false; + + for (int j = int(i - windowSize); j < int(i + windowSize); ++j) + { + float d = calcDist(trackData[j].lat, trackData[j].lon, + trackData[j + 1].lat, trackData[j + 1].lon); + + if (d > 200.0f) + { // Skip if suspicious jump + skipWindow = true; + break; + } - // Extract latitude and longitude - trkpt->QueryDoubleAttribute(gpxLatElem, &point.lat); - trkpt->QueryDoubleAttribute(gpxLonElem, &point.lon); + distWindow += d; + } + + if (skipWindow) + continue; - // // Extract optional elements - // tinyxml2::XMLElement* ele = trkpt->FirstChildElement("ele"); - // if (ele) point.ele = static_cast(ele->DoubleText()); + float brgStart = calcCourse(trackData[i - windowSize].lat, trackData[i - windowSize].lon, + trackData[i].lat, trackData[i].lon); + float brgEnd = calcCourse(trackData[i].lat, trackData[i].lon, + trackData[i + windowSize].lat, trackData[i + windowSize].lon); + float diff = calcAngleDiff(brgEnd, brgStart); - // tinyxml2::XMLElement* time = trkpt->FirstChildElement("time"); - // if (time) point.time = strdup(time->GetText()); + accumDist += calcDist(trackData[i - 1].lat, trackData[i - 1].lon, + trackData[i].lat, trackData[i].lon); - trackData.push_back(point); - } + if (std::fabs(diff) > sharpTurnDeg) + { + turnPoints.push_back({static_cast(i), diff, accumDist}); + continue; + } + + if (distWindow < minDist) + continue; + + if (std::fabs(diff) > thresholdDeg) + turnPoints.push_back({static_cast(i), diff, accumDist}); } - } - return true; + return turnPoints; } - diff --git a/lib/gpx/src/gpxParser.hpp b/lib/gpx/src/gpxParser.hpp index 19f4cdda..d34f4b61 100644 --- a/lib/gpx/src/gpxParser.hpp +++ b/lib/gpx/src/gpxParser.hpp @@ -2,8 +2,8 @@ * @file gpxParser.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief GPX Parser class - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -19,184 +19,189 @@ #include "esp_log.h" #include "tinyxml2.h" #include "globalGpxDef.h" +#include "gpsMath.hpp" static const char* TAGGPX PROGMEM = "GPXParser"; -static const char* gpxWaypointTag PROGMEM = "wpt"; -static const char* gpxTrackTag PROGMEM = "trk"; -static const char* gpxNameElem PROGMEM = "name"; -static const char* gpxLatElem PROGMEM = "lat"; -static const char* gpxLonElem PROGMEM = "lon"; -static const char* gpxEleElem PROGMEM ="ele"; -static const char* gpxTimeElem PROGMEM = "time"; -static const char* gpxDescElem PROGMEM = "desc"; -static const char* gpxSrcElem PROGMEM = "src"; -static const char* gpxSymElem PROGMEM = "sym"; -static const char* gpxTypeElem PROGMEM = "type"; -static const char* gpxSatElem PROGMEM = "sat"; -static const char* gpxHdopElem PROGMEM = "hdop"; -static const char* gpxVdopElem PROGMEM = "vdop"; -static const char* gpxPdopElem PROGMEM = "pdop"; +static const char* gpxWaypointTag PROGMEM = "wpt"; /**< GPX waypoint tag. */ +static const char* gpxTrackTag PROGMEM = "trk"; /**< GPX track tag. */ +static const char* gpxNameElem PROGMEM = "name"; /**< GPX name element. */ +static const char* gpxLatElem PROGMEM = "lat"; /**< GPX latitude attribute. */ +static const char* gpxLonElem PROGMEM = "lon"; /**< GPX longitude attribute. */ +static const char* gpxEleElem PROGMEM = "ele"; /**< GPX elevation element. */ +static const char* gpxTimeElem PROGMEM = "time"; /**< GPX time element. */ +static const char* gpxDescElem PROGMEM = "desc"; /**< GPX description element. */ +static const char* gpxSrcElem PROGMEM = "src"; /**< GPX source element. */ +static const char* gpxSymElem PROGMEM = "sym"; /**< GPX symbol element. */ +static const char* gpxTypeElem PROGMEM = "type"; /**< GPX type element. */ +static const char* gpxSatElem PROGMEM = "sat"; /**< GPX satellites element. */ +static const char* gpxHdopElem PROGMEM = "hdop"; /**< GPX horizontal dilution of precision element. */ +static const char* gpxVdopElem PROGMEM = "vdop"; /**< GPX vertical dilution of precision element. */ +static const char* gpxPdopElem PROGMEM = "pdop"; /**< GPX position dilution of precision element. */ +/** + * @class GPXParser + * @brief GPX file parser and editor class. + */ class GPXParser { - public: - GPXParser(const char* filePath); - GPXParser(); - ~GPXParser(); +public: + GPXParser(const char* filePath); + GPXParser(); + ~GPXParser(); - - template - bool editTagAttrOrElem(const char* tag, const char* attribute, const char* element, const T& oldValue, const T& newValue); - template - bool insertTagAttrOrElem(const char* tag, const char* attribute, const char* element, const T& value); + template + bool editTagAttrOrElem(const char* tag, const char* attribute, const char* element, const T& oldValue, const T& newValue); + template + bool insertTagAttrOrElem(const char* tag, const char* attribute, const char* element, const T& value); static std::map> getTagElementList(const char* tag, const char* element, const std::string& folderPath); bool deleteTagByName(const char* tag, const char* name); wayPoint getWaypointInfo(const char* name); bool addWaypoint(const wayPoint& wp); bool loadTrack(std::vector& trackData); + std::vector getTurnPointsSlidingWindow(float thresholdDeg, float minDist, float sharpTurnDeg,int windowSize, const std::vector& trackData); - std::string filePath; + std::string filePath; }; /** - * @brief Edit tag attribute or element value in the GPX file - * - * @param tag - * @param attribute nullptr for edit element - * @param element nullptr for edit attribute - * @param oldValue The old value of the attribute or element - * @param newValue The new value of the attribute or element - * @return true if the tag attribute or element value was edited successfully, false otherwise - */ +* @brief Edit a tag, attribute, or element in the GPX file. +* +* @param tag XML tag name. +* @param attribute XML attribute name (can be nullptr). +* @param element XML element name (can be nullptr). +* @param oldValue Old value to find. +* @param newValue New value to replace with. +* @return true if the edit was successful, false otherwise. +*/ template bool GPXParser::editTagAttrOrElem(const char* tag, const char* attribute, const char* element, const T& oldValue, const T& newValue) { - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* root = doc.RootElement(); - if (!root) - { - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - return false; - } - - std::ostringstream oldValueStream, newValueStream; - oldValueStream << oldValue; - newValueStream << newValue; - std::string oldValueStr = oldValueStream.str(); - std::string newValueStr = newValueStream.str(); - - for (tinyxml2::XMLElement* tagElement = root->FirstChildElement(tag); tagElement != nullptr; tagElement = tagElement->NextSiblingElement(tag)) - { - if (attribute) - { - const char* attrValue = tagElement->Attribute(attribute); - if (attrValue && oldValueStr == attrValue) - { - tagElement->SetAttribute(attribute, newValueStr.c_str()); - result = doc.SaveFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); - return false; - } - return true; - } - } - else if (element) - { - tinyxml2::XMLElement* childElement = tagElement->FirstChildElement(element); - if (childElement && childElement->GetText() && oldValueStr == childElement->GetText()) - { - childElement->SetText(newValueStr.c_str()); - result = doc.SaveFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); - return false; - } - return true; - } - } - } - - ESP_LOGE(TAGGPX, "Attribute/Element '%s' with value '%s' not found for tag '%s' in file: %s", - attribute ? attribute : element, oldValueStr.c_str(), tag, filePath.c_str()); - return false; + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) + { + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + return false; + } + + std::ostringstream oldValueStream, newValueStream; + oldValueStream << oldValue; + newValueStream << newValue; + std::string oldValueStr = oldValueStream.str(); + std::string newValueStr = newValueStream.str(); + + for (tinyxml2::XMLElement* tagElement = root->FirstChildElement(tag); tagElement != nullptr; tagElement = tagElement->NextSiblingElement(tag)) + { + if (attribute) + { + const char* attrValue = tagElement->Attribute(attribute); + if (attrValue && oldValueStr == attrValue) + { + tagElement->SetAttribute(attribute, newValueStr.c_str()); + result = doc.SaveFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); + return false; + } + return true; + } + } + else if (element) + { + tinyxml2::XMLElement* childElement = tagElement->FirstChildElement(element); + if (childElement && childElement->GetText() && oldValueStr == childElement->GetText()) + { + childElement->SetText(newValueStr.c_str()); + result = doc.SaveFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to save file: %s", filePath.c_str()); + return false; + } + return true; + } + } + } + + ESP_LOGE(TAGGPX, "Attribute/Element '%s' with value '%s' not found for tag '%s' in file: %s", + attribute ? attribute : element, oldValueStr.c_str(), tag, filePath.c_str()); + return false; } /** - * @brief Insert the tag attribute or element value in the GPX file - * - * @param tag - * @param attribute nullptr for insert element - * @param element nullptr for insert attribute - * @param value value of the attribute or element - * @return true if the tag attribute or element value was inserted successfully, false otherwise - */ +* @brief Insert a tag, attribute, or element in the GPX file. +* +* @param tag XML tag name. +* @param attribute XML attribute name (can be nullptr). +* @param element XML element name (can be nullptr). +* @param value Value to insert. +* @return true if the insert was successful, false otherwise. +*/ template bool GPXParser::insertTagAttrOrElem(const char* tag, const char* attribute, const char* element, const T& value) { - tinyxml2::XMLDocument doc; - tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* root = doc.RootElement(); - if (!root) - { - ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); - return false; - } - - tinyxml2::XMLElement* tagElement = root->FirstChildElement(tag); - if (!tagElement) - { - ESP_LOGE(TAGGPX, "Tag '%s' not found in file: %s", tag, filePath.c_str()); - return false; - } - - std::ostringstream valueStream; - valueStream << value; - std::string valueStr = valueStream.str(); - - if (attribute) - tagElement->SetAttribute(attribute, valueStr.c_str()); - else if (element) - { - tinyxml2::XMLElement* childElement = tagElement->FirstChildElement(element); - if (!childElement) - { - childElement = doc.NewElement(element); - if (!childElement) - { - ESP_LOGE(TAGGPX, "Failed to create new element '%s'", element); - return false; - } - tagElement->InsertEndChild(childElement); - } - childElement->SetText(valueStr.c_str()); - } - - result = doc.SaveFile(filePath.c_str()); - if (result != tinyxml2::XML_SUCCESS) - { - ESP_LOGE(TAGGPX, "Failed to save file after inserting attribute/element: %s", filePath.c_str()); - return false; - } - - ESP_LOGI(TAGGPX, "Successfully inserted attribute/element '%s' into tag '%s'", attribute ? attribute : element, tag); - return true; + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to load file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (!root) + { + ESP_LOGE(TAGGPX, "Failed to get root element in file: %s", filePath.c_str()); + return false; + } + + tinyxml2::XMLElement* tagElement = root->FirstChildElement(tag); + if (!tagElement) + { + ESP_LOGE(TAGGPX, "Tag '%s' not found in file: %s", tag, filePath.c_str()); + return false; + } + + std::ostringstream valueStream; + valueStream << value; + std::string valueStr = valueStream.str(); + + if (attribute) + tagElement->SetAttribute(attribute, valueStr.c_str()); + else if (element) + { + tinyxml2::XMLElement* childElement = tagElement->FirstChildElement(element); + if (!childElement) + { + childElement = doc.NewElement(element); + if (!childElement) + { + ESP_LOGE(TAGGPX, "Failed to create new element '%s'", element); + return false; + } + tagElement->InsertEndChild(childElement); + } + childElement->SetText(valueStr.c_str()); + } + + result = doc.SaveFile(filePath.c_str()); + if (result != tinyxml2::XML_SUCCESS) + { + ESP_LOGE(TAGGPX, "Failed to save file after inserting attribute/element: %s", filePath.c_str()); + return false; + } + + ESP_LOGI(TAGGPX, "Successfully inserted attribute/element '%s' into tag '%s'", attribute ? attribute : element, tag); + return true; } \ No newline at end of file diff --git a/lib/gui/src/buttonBar.cpp b/lib/gui/src/buttonBar.cpp index 71dd7223..01cc0271 100644 --- a/lib/gui/src/buttonBar.cpp +++ b/lib/gui/src/buttonBar.cpp @@ -2,8 +2,8 @@ * @file buttonBar.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Button Bar - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "buttonBar.hpp" @@ -15,336 +15,343 @@ bool isTrackOpt; bool isOptionLoaded = false; bool isBarOpen = false; -lv_obj_t *settingsScreen; -lv_obj_t *buttonBar; -lv_obj_t *menuBtn; +lv_obj_t *settingsScreen; /**< Settings screen object. */ +lv_obj_t *buttonBar; /**< Button bar object. */ +lv_obj_t *menuBtn; /**< Menu button object. */ /** - * @brief Button events + * @brief Handles button events on the button bar, triggering actions and UI changes based on the selected option. * - * @param event + * @param event Pointer to the LVGL event structure. */ void buttonBarEvent(lv_event_t *event) { - lv_obj_t * obj = (lv_obj_t*)lv_event_get_target(event); - lv_obj_t * buttonBar = lv_obj_get_parent(obj); - if(lv_obj_get_width(buttonBar) > LV_HOR_RES / 2) - { - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, buttonBar); - lv_anim_set_exec_cb(&a, hideShowAnim); - lv_anim_set_values(&a, 256, 0); - lv_anim_set_duration(&a, 250); - lv_anim_start(&a); - } + lv_obj_t * obj = (lv_obj_t*)lv_event_get_target(event); + lv_obj_t * buttonBar = lv_obj_get_parent(obj); + if(lv_obj_get_width(buttonBar) > LV_HOR_RES / 2) + { + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, buttonBar); + lv_anim_set_exec_cb(&a, hideShowAnim); + lv_anim_set_values(&a, 256, 0); + lv_anim_set_duration(&a, 250); + lv_anim_start(&a); + } - char *option = (char *)lv_event_get_user_data(event); + char *option = (char *)lv_event_get_user_data(event); - if (strcmp(option,"addwpt") == 0) - { - gpxAction = WPT_ADD; - isMainScreen = false; - mapView.redrawMap = false; - lv_textarea_set_text(gpxTagValue, ""); - isScreenRotated = false; - lv_obj_set_width(gpxTagValue, tft.width() -10); - updateWaypoint(gpxAction); - lv_screen_load(gpxDetailScreen); - } + if (strcmp(option,"addwpt") == 0) + { + gpxAction = WPT_ADD; + isMainScreen = false; + mapView.redrawMap = false; + lv_textarea_set_text(gpxTagValue, ""); + isScreenRotated = false; + lv_obj_set_width(gpxTagValue, tft.width() -10); + updateWaypoint(gpxAction); + lv_screen_load(gpxDetailScreen); + } - if (strcmp(option,"waypoint") == 0) - { - isWaypointOpt = true; - isTrackOpt = false; - if (!isOptionLoaded) - { - isOptionLoaded = true; - loadOptions(); - } - } - if (strcmp(option,"track") == 0) - { - isTrackOpt = true; - isWaypointOpt = false; - if (!isOptionLoaded) - { - isOptionLoaded = true; - loadOptions(); - } - } - if (strcmp(option,"settings") == 0) - { - isMainScreen = false; - lv_screen_load(settingsScreen); - } + if (strcmp(option,"waypoint") == 0) + { + isWaypointOpt = true; + isTrackOpt = false; + if (!isOptionLoaded) + { + isOptionLoaded = true; + lv_obj_add_flag(menuBtn,LV_OBJ_FLAG_HIDDEN); + lv_obj_set_size(buttonBar, 50 * scaleBut, 50 * scaleBut); + lv_obj_add_flag(buttonBar,LV_OBJ_FLAG_HIDDEN); + loadOptions(); + } + } + if (strcmp(option,"track") == 0) + { + isTrackOpt = true; + isWaypointOpt = false; + if (!isOptionLoaded) + { + isOptionLoaded = true; + lv_obj_add_flag(menuBtn,LV_OBJ_FLAG_HIDDEN); + lv_obj_set_size(buttonBar, 50 * scaleBut, 50 * scaleBut); + lv_obj_add_flag(buttonBar,LV_OBJ_FLAG_HIDDEN); + loadOptions(); + } + } + if (strcmp(option,"settings") == 0) + { + isMainScreen = false; + lv_screen_load(settingsScreen); + } } /** - * @brief Options events + * @brief Handles option events, triggering actions such as load, edit, delete, or exit based on the selected option. * - * @param event + * @param event Pointer to the LVGL event structure. */ void optionEvent(lv_event_t *event) { - char *action = (char *)lv_event_get_user_data(event); - if (strcmp(action,"load") == 0) - { - gpxAction = GPX_LOAD; - isMainScreen = false; - lv_obj_del(option); - updateGpxListScreen(); - if (isTrackOpt) - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Tracks"); - if (isWaypointOpt) - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); - lv_screen_load(listGPXScreen); - } - if (strcmp(action,"edit") == 0) - { - gpxAction = GPX_EDIT; - isMainScreen = false; - lv_obj_del(option); - updateGpxListScreen(); - if (isTrackOpt) - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Tracks"); - if (isWaypointOpt) - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); - lv_screen_load(listGPXScreen); - } - if (strcmp(action,"delete") == 0) - { - gpxAction = GPX_DEL; - isMainScreen = false; - lv_obj_del(option); - updateGpxListScreen(); - if (isTrackOpt) - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Tracks"); - if (isWaypointOpt) - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); - lv_screen_load(listGPXScreen); - } - if (strcmp(action,"exit") == 0) - { - isMainScreen = true; - isTrackOpt = false; - isWaypointOpt = false; - lv_obj_del(option); - } + char *action = (char *)lv_event_get_user_data(event); + if (strcmp(action,"load") == 0) + { + gpxAction = GPX_LOAD; + isMainScreen = false; + lv_obj_del(option); + updateGpxListScreen(); + if (isTrackOpt) + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Tracks"); + if (isWaypointOpt) + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); + lv_screen_load(listGPXScreen); + } + if (strcmp(action,"edit") == 0) + { + gpxAction = GPX_EDIT; + isMainScreen = false; + lv_obj_del(option); + updateGpxListScreen(); + if (isTrackOpt) + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Tracks"); + if (isWaypointOpt) + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); + lv_screen_load(listGPXScreen); + } + if (strcmp(action,"delete") == 0) + { + gpxAction = GPX_DEL; + isMainScreen = false; + lv_obj_del(option); + updateGpxListScreen(); + if (isTrackOpt) + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Tracks"); + if (isWaypointOpt) + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); + lv_screen_load(listGPXScreen); + } + if (strcmp(action,"exit") == 0) + { + isMainScreen = true; + isTrackOpt = false; + isWaypointOpt = false; + lv_obj_del(option); + lv_obj_clear_flag(menuBtn,LV_OBJ_FLAG_HIDDEN); + } - isOptionLoaded = false; - isWaypointOpt = false; - isTrackOpt = false; - mapView.redrawMap = true; + isOptionLoaded = false; + isWaypointOpt = false; + isTrackOpt = false; + mapView.redrawMap = true; } /** - * @brief Hide/Show Animation + * @brief Hide/Show animation for UI objects, adjusts width and toggles visibility and related flags. * - * @param event + * @param var Pointer to the LVGL object. + * @param v Animation value (typically width). */ void hideShowAnim(void * var, int32_t v) { - lv_obj_t * obj = (lv_obj_t*)var; - int32_t max_w = lv_obj_get_width(lv_obj_get_parent(obj)) - LV_DPX(4); - int32_t w; - w = lv_map(v, 0, 256, LV_DPX(60) * scaleBut, max_w); - lv_obj_set_width(obj, w); - if (v == 0) - { - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - isBarOpen = false; - isScrolled = true; - } - else - { - lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - isBarOpen = true; - isScrolled = false; - } + lv_obj_t * obj = (lv_obj_t*)var; + int32_t max_w = lv_obj_get_width(lv_obj_get_parent(obj)) - LV_DPX(4); + int32_t w; + w = lv_map(v, 0, 256, LV_DPX(60) * scaleBut, max_w); + lv_obj_set_width(obj, w); + if (v == 0) + { + lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); + isBarOpen = false; + isScrolled = true; + } + else + { + lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); + isBarOpen = true; + isScrolled = false; + } } /** - * @brief Hide/Show buttons event + * @brief Handles hide/show events for buttons, toggling the button bar visibility with animation. * - * @param event + * @param event Pointer to the LVGL event structure. */ void hideShowEvent(lv_event_t * e) { - if(lv_event_get_code(e) == LV_EVENT_CLICKED) - { - lv_obj_t * buttonBar = (lv_obj_t *)lv_event_get_user_data(e); - if(lv_obj_get_width(buttonBar) < LV_HOR_RES / 2) - { - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, buttonBar); - lv_anim_set_exec_cb(&a, hideShowAnim); - lv_anim_set_values(&a, 0, 256); - lv_anim_set_duration(&a, 150); - lv_anim_start(&a); - } - else - { - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, buttonBar); - lv_anim_set_exec_cb(&a, hideShowAnim); - lv_anim_set_values(&a, 256, 0); - lv_anim_set_duration(&a, 150); - lv_anim_start(&a); - } - } + if(lv_event_get_code(e) == LV_EVENT_CLICKED) + { + lv_obj_t * buttonBar = (lv_obj_t *)lv_event_get_user_data(e); + if(lv_obj_get_width(buttonBar) < LV_HOR_RES / 2) + { + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, buttonBar); + lv_anim_set_exec_cb(&a, hideShowAnim); + lv_anim_set_values(&a, 0, 256); + lv_anim_set_duration(&a, 150); + lv_anim_start(&a); + } + else + { + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, buttonBar); + lv_anim_set_exec_cb(&a, hideShowAnim); + lv_anim_set_values(&a, 256, 0); + lv_anim_set_duration(&a, 150); + lv_anim_start(&a); + } + } } /** - * @brief Create button bar screen + * @brief Creates and configures the floating button bar screen with menu and action buttons. * */ void createButtonBarScr() { - // Button Bar - buttonBar = lv_obj_create(mainScreen); - lv_obj_remove_style_all(buttonBar); - lv_obj_set_flex_flow(buttonBar, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(buttonBar, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_FLOATING); - lv_obj_set_style_radius(buttonBar, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_border_color(buttonBar, lv_color_white(), 0); - lv_obj_set_style_border_width(buttonBar, 1, 0); - lv_obj_set_style_border_opa(buttonBar,LV_OPA_20,0); - lv_obj_set_style_bg_color(buttonBar, lv_color_black(), 0); - lv_obj_set_style_bg_opa(buttonBar, 230, 0); - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_FLOATING); - lv_obj_set_size(buttonBar, 50 * scaleBut, 50 * scaleBut); - lv_obj_align(buttonBar, LV_ALIGN_BOTTOM_RIGHT, 0, -LV_DPX(14) ); - lv_obj_add_flag(buttonBar,LV_OBJ_FLAG_HIDDEN); + // Button Bar + buttonBar = lv_obj_create(mainScreen); + lv_obj_remove_style_all(buttonBar); + lv_obj_set_flex_flow(buttonBar, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(buttonBar, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_FLOATING); + lv_obj_set_style_radius(buttonBar, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_border_color(buttonBar, lv_color_white(), 0); + lv_obj_set_style_border_width(buttonBar, 1, 0); + lv_obj_set_style_border_opa(buttonBar,LV_OPA_20,0); + lv_obj_set_style_bg_color(buttonBar, lv_color_black(), 0); + lv_obj_set_style_bg_opa(buttonBar, 210, 0); + lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_FLOATING); + lv_obj_set_size(buttonBar, 50 * scaleBut, 50 * scaleBut); + lv_obj_align(buttonBar, LV_ALIGN_BOTTOM_RIGHT, 0, 0 ); + lv_obj_add_flag(buttonBar,LV_OBJ_FLAG_HIDDEN); - static lv_style_t style; - lv_style_init(&style); - lv_style_set_pad_column(&style, 10); - lv_obj_add_style(buttonBar, &style, 0); + static lv_style_t style; + lv_style_init(&style); + lv_style_set_pad_column(&style, 10); + lv_obj_add_style(buttonBar, &style, 0); - menuBtn = lv_img_create(mainScreen); - lv_img_set_src(menuBtn, menuIconFile); - lv_obj_add_flag(menuBtn, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); - lv_img_set_zoom(menuBtn,buttonScale); - lv_obj_update_layout(menuBtn); - lv_obj_add_event_cb(menuBtn, hideShowEvent, LV_EVENT_ALL, buttonBar); - lv_obj_set_size(menuBtn, 48 * scaleBut, 48 * scaleBut); - lv_obj_align(menuBtn, LV_ALIGN_BOTTOM_RIGHT, 0, -LV_DPX(15)); + menuBtn = lv_img_create(mainScreen); + lv_img_set_src(menuBtn, menuIconFile); + lv_obj_add_flag(menuBtn, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_img_set_zoom(menuBtn,buttonScale); + lv_obj_update_layout(menuBtn); + lv_obj_add_event_cb(menuBtn, hideShowEvent, LV_EVENT_ALL, buttonBar); + lv_obj_set_size(menuBtn, 48 * scaleBut, 48 * scaleBut); + lv_obj_align(menuBtn, LV_ALIGN_BOTTOM_RIGHT, 0, 0); - lv_obj_t *imgBtn; - - // Add Waypoint Button - imgBtn = lv_img_create(buttonBar); - lv_img_set_src(imgBtn,addWptIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"addwpt"); + lv_obj_t *imgBtn; + + // Add Waypoint Button + imgBtn = lv_img_create(buttonBar); + lv_img_set_src(imgBtn,addWptIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"addwpt"); - // Waypoint Button - imgBtn = lv_img_create(buttonBar); - lv_img_set_src(imgBtn, waypointIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"waypoint"); - - // Track Button - imgBtn = lv_img_create(buttonBar); - lv_img_set_src(imgBtn, trackIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"track"); - - // Settings Button - imgBtn = lv_img_create(buttonBar); - lv_img_set_src(imgBtn, settingsIconFile); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"settings"); + // Waypoint Button + imgBtn = lv_img_create(buttonBar); + lv_img_set_src(imgBtn, waypointIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"waypoint"); + + // Track Button + imgBtn = lv_img_create(buttonBar); + lv_img_set_src(imgBtn, trackIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"track"); + + // Settings Button + imgBtn = lv_img_create(buttonBar); + lv_img_set_src(imgBtn, settingsIconFile); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_event_cb(imgBtn, buttonBarEvent, LV_EVENT_PRESSED, (char*)"settings"); } /** - * @brief Load waypoint, track options modal dialog. + * @brief Loads the waypoint/track options modal dialog and creates option buttons. * */ void loadOptions() { - option = lv_obj_create(lv_scr_act()); - lv_obj_remove_style_all(option); + option = lv_obj_create(lv_scr_act()); + lv_obj_remove_style_all(option); - lv_obj_set_style_radius(option, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_border_color(option, lv_color_white(), 0); - lv_obj_set_style_border_width(option, 1, 0); - lv_obj_set_style_border_opa(option,LV_OPA_20,0); - lv_obj_set_style_bg_color(option, lv_color_black(), 0); - lv_obj_set_style_bg_opa(option, 255, 0); - lv_obj_set_flex_flow(option, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(option, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_set_size(option, TFT_WIDTH, 50 * scaleBut); - lv_obj_clear_flag(option, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_align(option, LV_ALIGN_BOTTOM_LEFT, 0, -10); + lv_obj_set_style_radius(option, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_border_color(option, lv_color_white(), 0); + lv_obj_set_style_border_width(option, 1, 0); + lv_obj_set_style_border_opa(option,LV_OPA_20,0); + lv_obj_set_style_bg_color(option, lv_color_black(), 0); + lv_obj_set_style_bg_opa(option, 210, 0); + lv_obj_set_flex_flow(option, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(option, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_size(option, TFT_WIDTH, 50 * scaleBut); + lv_obj_clear_flag(option, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_align(option, LV_ALIGN_BOTTOM_LEFT, 0, 0); - static lv_style_t style; - lv_style_init(&style); - lv_style_set_pad_column(&style, 10); - lv_obj_add_style(option, &style, 0); + static lv_style_t style; + lv_style_init(&style); + lv_style_set_pad_column(&style, 10); + lv_obj_add_style(option, &style, 0); + + lv_obj_t *imgBtn; + + // if (isTrackOpt) + // { + // // Save Button + // imgBtn = lv_img_create(option); + // lv_img_set_src(imgBtn, saveIconFile); + // lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + // lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"save"); + // } + + // Load Button + imgBtn = lv_img_create(option); + lv_img_set_src(imgBtn, loadIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_align(imgBtn,LV_ALIGN_BOTTOM_LEFT, 0, -40); + lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"load"); - - lv_obj_t *imgBtn; - - // if (isTrackOpt) - // { - // // Save Button - // imgBtn = lv_img_create(option); - // lv_img_set_src(imgBtn, saveIconFile); - // lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - // lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"save"); - // } - - // Load Button - imgBtn = lv_img_create(option); - lv_img_set_src(imgBtn, loadIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_align(imgBtn,LV_ALIGN_BOTTOM_LEFT, 0, -40); - lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"load"); + // Edit Button + imgBtn = lv_img_create(option); + lv_img_set_src(imgBtn, editIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"edit"); - // Edit Button - imgBtn = lv_img_create(option); - lv_img_set_src(imgBtn, editIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"edit"); + // Delete Button + imgBtn = lv_img_create(option); + lv_img_set_src(imgBtn, deleteIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"delete"); - // Delete Button - imgBtn = lv_img_create(option); - lv_img_set_src(imgBtn, deleteIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"delete"); - - // Exit Button - imgBtn = lv_img_create(option); - lv_img_set_src(imgBtn, exitIconFile); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"exit"); + // Exit Button + imgBtn = lv_img_create(option); + lv_img_set_src(imgBtn, exitIconFile); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(imgBtn, optionEvent, LV_EVENT_PRESSED, (char*)"exit"); } diff --git a/lib/gui/src/buttonBar.hpp b/lib/gui/src/buttonBar.hpp index 09cdd760..bb0b30a8 100644 --- a/lib/gui/src/buttonBar.hpp +++ b/lib/gui/src/buttonBar.hpp @@ -2,8 +2,8 @@ * @file buttonBar.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Button Bar - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -13,23 +13,24 @@ #include "gpxDetailScr.hpp" #include "gpxScr.hpp" -static const char *waypointIconFile PROGMEM = "/wpt.bin"; // Waypoint icon -static const char *trackIconFile PROGMEM = "/track.bin"; // Track icon -static const char *settingsIconFile PROGMEM = "/settings.bin"; // Settings icon -static const char *saveIconFile PROGMEM = "/save.bin"; // Save icon -static const char *loadIconFile PROGMEM = "/load.bin"; // Load icon -static const char *editIconFile PROGMEM = "/edit.bin"; // Edit icon -static const char *deleteIconFile PROGMEM = "/delete.bin"; // Delete icon -static const char *menuIconFile PROGMEM = "/menu.bin"; // Menu icon -static const char *addWptIconFile PROGMEM = "/addwpt.bin"; // Add Waypoint icon -static const char *exitIconFile PROGMEM = "/exit.bin"; // Exit icon +static const char *waypointIconFile PROGMEM = "/wpt.bin"; /**< Waypoint icon file path. */ +static const char *trackIconFile PROGMEM = "/track.bin"; /**< Track icon file path. */ +static const char *settingsIconFile PROGMEM = "/settings.bin"; /**< Settings icon file path. */ +static const char *saveIconFile PROGMEM = "/save.bin"; /**< Save icon file path. */ +static const char *loadIconFile PROGMEM = "/load.bin"; /**< Load icon file path. */ +static const char *editIconFile PROGMEM = "/edit.bin"; /**< Edit icon file path. */ +static const char *deleteIconFile PROGMEM = "/delete.bin"; /**< Delete icon file path. */ +static const char *menuIconFile PROGMEM = "/menu.bin"; /**< Menu icon file path. */ +static const char *addWptIconFile PROGMEM = "/addwpt.bin"; /**< Add Waypoint icon file path. */ +static const char *exitIconFile PROGMEM = "/exit.bin"; /**< Exit icon file path. */ -static lv_obj_t *option; +static lv_obj_t *option; /**< Pointer to the currently selected option object (LVGL). */ + +extern bool isWaypointOpt; /**< Waypoint Option selected. */ +extern bool isTrackOpt; /**< Track Option selected. */ +extern bool isOptionLoaded; /**< Option selected & loaded. */ +extern bool isBarOpen; /**< Flag to determine if Button Bar is open. */ -extern bool isWaypointOpt; // Waypoint Option selected -extern bool isTrackOpt; // Track Option selected -extern bool isOptionLoaded; // Option selected & loaded -extern bool isBarOpen; // Flag to determine if Button Bar is open void buttonBarEvent(lv_event_t *event); void optionEvent(lv_event_t *event); diff --git a/lib/gui/src/deviceSettingsScr.cpp b/lib/gui/src/deviceSettingsScr.cpp index 0706052c..fc72a676 100644 --- a/lib/gui/src/deviceSettingsScr.cpp +++ b/lib/gui/src/deviceSettingsScr.cpp @@ -2,44 +2,44 @@ * @file deviceSettingsScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Device Settings Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "deviceSettingsScr.hpp" -lv_obj_t *deviceSettingsScreen; // Device Settings Screen +lv_obj_t *deviceSettingsScreen; /**< Device Settings Screen. */ /** - * @brief Device Settings Events - * - * @param event + * @brief Handles device settings events, updating configuration based on user selection. + * + * @param event Pointer to the LVGL event structure. */ static void deviceSettingsEvent(lv_event_t *event) { - lv_obj_t *obj = (lv_obj_t*)lv_event_get_target(event); - char *option = (char *)lv_event_get_user_data(event); - if (strcmp(option,"speed") == 0) - { - gpsBaud = lv_dropdown_get_selected(obj); - saveGPSBaud(gpsBaud); - } - if (strcmp(option,"rate") == 0) - { - gpsUpdate = lv_dropdown_get_selected(obj); - saveGPSUpdateRate(gpsUpdate); - } - if (strcmp(option, "back") == 0) - { - cfg.saveUInt(PKEYS::KDEF_BRIGT, defBright); - lv_screen_load(settingsScreen); - } + lv_obj_t *obj = (lv_obj_t*)lv_event_get_target(event); + char *option = (char *)lv_event_get_user_data(event); + if (strcmp(option,"speed") == 0) + { + gpsBaud = lv_dropdown_get_selected(obj); + saveGPSBaud(gpsBaud); + } + if (strcmp(option,"rate") == 0) + { + gpsUpdate = lv_dropdown_get_selected(obj); + saveGPSUpdateRate(gpsUpdate); + } + if (strcmp(option, "back") == 0) + { + cfg.saveUInt(PKEYS::KDEF_BRIGT, defBright); + lv_screen_load(settingsScreen); + } } /** - * @brief Brightness callback + * @brief Brightness slider event callback, updates the display brightness. * - * @param e + * @param e Pointer to the LVGL event structure. */ static void brightnessEvent(lv_event_t *e) { @@ -49,154 +49,163 @@ static void brightnessEvent(lv_event_t *e) } /** - * @brief Upgrade firmware event - * - * @param event + * @brief Handles the upgrade firmware event, checks for firmware file and updates accordingly. + * + * @param event Pointer to the LVGL event structure. */ static void upgradeEvent(lv_event_t *event) { - createMsgUpgrade(); - lv_screen_load(msgUpgrade); - if (!checkFileUpgrade()) - { - lv_label_set_text_static(msgUprgdText, LV_SYMBOL_WARNING " No Firmware found!"); - } - else - { - lv_label_set_text_static(msgUprgdText, LV_SYMBOL_WARNING " Firmware found!"); - lv_obj_clear_flag(btnMsgUpgrade,LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(contMeter,LV_OBJ_FLAG_HIDDEN); - } + createMsgUpgrade(); + lv_screen_load(msgUpgrade); + if (!checkFileUpgrade()) + { + lv_label_set_text_static(msgUprgdText, LV_SYMBOL_WARNING " No Firmware found!"); + } + else + { + lv_label_set_text_static(msgUprgdText, LV_SYMBOL_WARNING " Firmware found!"); + lv_obj_clear_flag(btnMsgUpgrade,LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(contMeter,LV_OBJ_FLAG_HIDDEN); + } } /** - * @brief Create brightness text + * @brief Creates and configures the brightness value label * */ static lv_obj_t *createBrightText(lv_obj_t *parent, const char *icon, const char *txt) { - lv_obj_t *obj = lv_menu_cont_create(parent); - - lv_obj_t *img = NULL; - lv_obj_t *label = NULL; - - if (icon) - { - img = lv_img_create(obj); - lv_img_set_src(img, icon); - } - - if (txt) - { - label = lv_label_create(obj); - lv_label_set_text(label, txt); - lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_obj_set_flex_grow(label, 1); - } - - if (icon && txt) - { - lv_obj_add_flag(img, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK); - lv_obj_swap(img, label); - } - - return obj; + lv_obj_t *obj = lv_menu_cont_create(parent); + + lv_obj_t *img = NULL; + lv_obj_t *label = NULL; + + if (icon) + { + img = lv_img_create(obj); + lv_img_set_src(img, icon); + } + + if (txt) + { + label = lv_label_create(obj); + lv_label_set_text(label, txt); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_flex_grow(label, 1); + } + + if (icon && txt) + { + lv_obj_add_flag(img, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK); + lv_obj_swap(img, label); + } + + return obj; } /** - * @brief Create brightness slider + * @brief Creates a brightness slider with a label and icon, attaches an event callback, and returns the slider object. * + * @param parent Parent LVGL object. + * @param icon Icon to display next to the slider. + * @param txt Text label for the slider. + * @param min Minimum slider value. + * @param max Maximum slider value. + * @param val Starting slider value. + * @param cb Event callback function. + * @param filter Event code filter for the callback. + * @return Pointer to the created LVGL slider object. */ static lv_obj_t *createBrightSlider(lv_obj_t *parent, const char *icon, const char *txt, int32_t min, int32_t max, int32_t val, lv_event_cb_t cb, lv_event_code_t filter) { - lv_obj_t *obj = createBrightText(parent, icon, txt); + lv_obj_t *obj = createBrightText(parent, icon, txt); - lv_obj_t *slider = lv_slider_create(obj); - lv_obj_set_width(slider,TFT_WIDTH - 80); - lv_slider_set_range(slider, min, max); - lv_slider_set_value(slider, val, LV_ANIM_OFF); + lv_obj_t *slider = lv_slider_create(obj); + lv_obj_set_width(slider,TFT_WIDTH - 80); + lv_slider_set_range(slider, min, max); + lv_slider_set_value(slider, val, LV_ANIM_OFF); - if (cb != NULL) - lv_obj_add_event_cb(slider, cb, filter, NULL); + if (cb != NULL) + lv_obj_add_event_cb(slider, cb, filter, NULL); - if (icon == NULL) - lv_obj_add_flag(slider, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK); + if (icon == NULL) + lv_obj_add_flag(slider, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK); - return slider; + return slider; } /** - * @brief Create Device Settings screen + * @brief Creates the Device Settings screen * */ void createDeviceSettingsScr() { - // Device Settings Screen - deviceSettingsScreen = lv_obj_create(NULL); - deviceSettingsOptions = lv_list_create(deviceSettingsScreen); - lv_obj_set_size(deviceSettingsOptions, TFT_WIDTH, TFT_HEIGHT - 60); - - lv_obj_t *label; - lv_obj_t *list; - lv_obj_t *btn; - lv_obj_t *dropdown; - - // GPS Speed - list = lv_list_add_btn(deviceSettingsOptions, NULL, "GPS\nSpeed"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_OUT_LEFT_BOTTOM); - dropdown = lv_dropdown_create(list); - lv_dropdown_set_options(dropdown, "4800\n9600\n19200\nAUTO"); - lv_dropdown_set_selected(dropdown, gpsBaud); - lv_obj_t* item = lv_dropdown_get_list(dropdown); - lv_obj_set_style_bg_color(item, lv_color_hex(objectColor), LV_PART_SELECTED | LV_STATE_CHECKED); - lv_obj_align_to(dropdown, list, LV_ALIGN_OUT_RIGHT_MID, 0, 0); - lv_obj_set_width(dropdown,TFT_WIDTH / 3); - lv_obj_add_event_cb(dropdown, deviceSettingsEvent, LV_EVENT_VALUE_CHANGED, (char*)"speed"); - - // GPS Update rate - list = lv_list_add_btn(deviceSettingsOptions, NULL, "GPS\nUpdate rate"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - dropdown = lv_dropdown_create(list); - lv_dropdown_set_options(dropdown, "1 Hz\n2 Hz\n4 Hz\n5 Hz\n10 Hz"); - lv_dropdown_set_selected(dropdown, gpsUpdate); - item = lv_dropdown_get_list(dropdown); - lv_obj_set_style_bg_color(item, lv_color_hex(objectColor), LV_PART_SELECTED | LV_STATE_CHECKED); - #ifndef AT6558D_GPS - lv_obj_set_style_text_color(list, lv_palette_darken(LV_PALETTE_GREY, 2), 0); - lv_obj_add_state(list, LV_STATE_DISABLED); - lv_obj_set_style_text_color(dropdown, lv_palette_darken(LV_PALETTE_GREY, 2), 0); - lv_obj_add_state(dropdown, LV_STATE_DISABLED); - #endif - lv_obj_set_width(dropdown,TFT_WIDTH / 3); - lv_obj_align_to(dropdown, list, LV_ALIGN_OUT_RIGHT_MID, 0, 0); - lv_obj_add_event_cb(dropdown, deviceSettingsEvent, LV_EVENT_VALUE_CHANGED, (char*)"rate"); - - // Upgrade button - list = lv_list_add_btn(deviceSettingsOptions, NULL, NULL); - btn = lv_btn_create(list); - lv_obj_set_size(btn, TFT_WIDTH - 45, 40 * scale); - label = lv_label_create(btn); - lv_obj_set_style_text_font(label, fontLarge, 0); - lv_label_set_text_static(label, "Firmware Upgrade"); - lv_obj_center(label); - lv_obj_add_event_cb(btn, upgradeEvent, LV_EVENT_CLICKED, NULL); - - // Brightness Slider - createBrightSlider(deviceSettingsOptions, LV_SYMBOL_SETTINGS, "Brightness", 5, 255, defBright, brightnessEvent, LV_EVENT_VALUE_CHANGED); - - // Back button - btn = lv_btn_create(deviceSettingsScreen); - lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); - label = lv_label_create(btn); - lv_obj_set_style_text_font(label, fontLarge, 0); - lv_label_set_text_static(label, "Back"); - lv_obj_center(label); - lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -10); - lv_obj_add_event_cb(btn, deviceSettingsEvent, LV_EVENT_CLICKED, (char*)"back"); + // Device Settings Screen + deviceSettingsScreen = lv_obj_create(NULL); + deviceSettingsOptions = lv_list_create(deviceSettingsScreen); + lv_obj_set_size(deviceSettingsOptions, TFT_WIDTH, TFT_HEIGHT - 60); + + lv_obj_t *label; + lv_obj_t *list; + lv_obj_t *btn; + lv_obj_t *dropdown; + + // GPS Speed + list = lv_list_add_btn(deviceSettingsOptions, NULL, "GPS\nSpeed"); + lv_obj_set_style_text_font(list, fontOptions, 0); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_OUT_LEFT_BOTTOM); + dropdown = lv_dropdown_create(list); + lv_dropdown_set_options(dropdown, "4800\n9600\n19200\nAUTO"); + lv_dropdown_set_selected(dropdown, gpsBaud); + lv_obj_t* item = lv_dropdown_get_list(dropdown); + lv_obj_set_style_bg_color(item, lv_color_hex(objectColor), LV_PART_SELECTED | LV_STATE_CHECKED); + lv_obj_align_to(dropdown, list, LV_ALIGN_OUT_RIGHT_MID, 0, 0); + lv_obj_set_width(dropdown,TFT_WIDTH / 3); + lv_obj_add_event_cb(dropdown, deviceSettingsEvent, LV_EVENT_VALUE_CHANGED, (char*)"speed"); + + // GPS Update rate + list = lv_list_add_btn(deviceSettingsOptions, NULL, "GPS\nUpdate rate"); + lv_obj_set_style_text_font(list, fontOptions, 0); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + dropdown = lv_dropdown_create(list); + lv_dropdown_set_options(dropdown, "1 Hz\n2 Hz\n4 Hz\n5 Hz\n10 Hz"); + lv_dropdown_set_selected(dropdown, gpsUpdate); + item = lv_dropdown_get_list(dropdown); + lv_obj_set_style_bg_color(item, lv_color_hex(objectColor), LV_PART_SELECTED | LV_STATE_CHECKED); + #ifndef AT6558D_GPS + lv_obj_set_style_text_color(list, lv_palette_darken(LV_PALETTE_GREY, 2), 0); + lv_obj_add_state(list, LV_STATE_DISABLED); + lv_obj_set_style_text_color(dropdown, lv_palette_darken(LV_PALETTE_GREY, 2), 0); + lv_obj_add_state(dropdown, LV_STATE_DISABLED); + #endif + lv_obj_set_width(dropdown,TFT_WIDTH / 3); + lv_obj_align_to(dropdown, list, LV_ALIGN_OUT_RIGHT_MID, 0, 0); + lv_obj_add_event_cb(dropdown, deviceSettingsEvent, LV_EVENT_VALUE_CHANGED, (char*)"rate"); + + // Upgrade button + list = lv_list_add_btn(deviceSettingsOptions, NULL, NULL); + btn = lv_btn_create(list); + lv_obj_set_size(btn, TFT_WIDTH - 45, 40 * scale); + label = lv_label_create(btn); + lv_obj_set_style_text_font(label, fontLarge, 0); + lv_label_set_text_static(label, "Firmware Upgrade"); + lv_obj_center(label); + lv_obj_add_event_cb(btn, upgradeEvent, LV_EVENT_CLICKED, NULL); + + // Brightness Slider + createBrightSlider(deviceSettingsOptions, LV_SYMBOL_SETTINGS, "Brightness", 5, 255, defBright, brightnessEvent, LV_EVENT_VALUE_CHANGED); + + // Back button + btn = lv_btn_create(deviceSettingsScreen); + lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); + label = lv_label_create(btn); + lv_obj_set_style_text_font(label, fontLarge, 0); + lv_label_set_text_static(label, "Back"); + lv_obj_center(label); + lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -10); + lv_obj_add_event_cb(btn, deviceSettingsEvent, LV_EVENT_CLICKED, (char*)"back"); } diff --git a/lib/gui/src/deviceSettingsScr.hpp b/lib/gui/src/deviceSettingsScr.hpp index 82b92845..55afc378 100644 --- a/lib/gui/src/deviceSettingsScr.hpp +++ b/lib/gui/src/deviceSettingsScr.hpp @@ -2,8 +2,8 @@ * @file deviceSettingsScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Device Settings Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -13,7 +13,7 @@ #include "lvglSetup.hpp" #include "firmUpgrade.hpp" -static lv_obj_t *deviceSettingsOptions; +static lv_obj_t *deviceSettingsOptions; /**< Device settings options */ static void deviceSettingsEvent(lv_event_t *event); static void brightnessEvent(lv_event_t *e); diff --git a/lib/gui/src/globalGuiDef.h b/lib/gui/src/globalGuiDef.h index 3cbc0b7c..2e156c8c 100644 --- a/lib/gui/src/globalGuiDef.h +++ b/lib/gui/src/globalGuiDef.h @@ -1,8 +1,8 @@ /** * @file globalGuiDef.h * @brief Global GUI Variables - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,94 +11,90 @@ #include "tft.hpp" #include "storage.hpp" -/** - * @Brief LVGL Display Driver - * - */ -extern lv_display_t *display; +extern lv_display_t *display; /**< LVGL Display Driver */ /** * @brief Screens definitions * */ -extern lv_obj_t *mainScreen; // Main Screen -extern lv_obj_t *tilesScreen; // Tiles Screen -extern lv_obj_t *notifyBarIcons; // Notify Bar Icons -extern lv_obj_t *notifyBarHour; // Notify Bar Hour -extern lv_obj_t *settingsScreen; // Settings Screen -extern lv_obj_t *mapSettingsScreen; // Map Settings Screen -extern lv_obj_t *deviceSettingsScreen; // Device Settings Screen -extern lv_obj_t *gpxDetailScreen; // Add Waypoint Screen -extern lv_obj_t *listGPXScreen; // List Waypoint Screen - -extern lv_group_t * scrGroup; // Screen group -extern lv_group_t * keyGroup; // GPIO group - -extern bool needReboot; // Flag to force device reboot -extern bool isSearchingSat; // Flag to indicate that is searching satellites -extern lv_obj_t *buttonBar; // Button Bar -extern lv_obj_t *menuBtn; // Button Menu -extern lv_obj_t *gpxTagValue; // Add / Edit Waypoint screen text area -extern bool isScreenRotated; // Flag to know if screen is rotated - -extern lv_obj_t *powerMsg; // Power Message +extern lv_obj_t *mainScreen; /**< Main Screen */ +extern lv_obj_t *tilesScreen; /**< Tiles Screen */ +extern lv_obj_t *notifyBarIcons; /**< Notify Bar Icons */ +extern lv_obj_t *notifyBarHour; /**< Notify Bar Hour */ +extern lv_obj_t *settingsScreen; /**< Settings Screen */ +extern lv_obj_t *mapSettingsScreen; /**< Map Settings Screen */ +extern lv_obj_t *deviceSettingsScreen; /**< Device Settings Screen */ +extern lv_obj_t *gpxDetailScreen; /**< Add Waypoint Screen */ +extern lv_obj_t *listGPXScreen; /**< List Waypoint Screen */ + +extern lv_group_t *scrGroup; /**< Screen group */ +extern lv_group_t *keyGroup; /**< GPIO group */ + +extern bool needReboot; /**< Flag to force device reboot */ +extern bool isSearchingSat; /**< Flag to indicate that device is searching for satellites */ +extern lv_obj_t *buttonBar; /**< Button Bar */ +extern lv_obj_t *menuBtn; /**< Button Menu */ +extern lv_obj_t *gpxTagValue; /**< Add/Edit Waypoint screen text area */ +extern bool isScreenRotated; /**< Flag to know if screen is rotated */ +extern bool isTrackLoaded; /**< Flag to know if track is loaded */ extern Storage storage; #ifdef LARGE_SCREEN - static const lv_font_t *fontDefault = &lv_font_montserrat_14; - static const lv_font_t *fontSmall = &lv_font_montserrat_10; - static const lv_font_t *fontSatInfo = &lv_font_montserrat_16; - static const lv_font_t *fontMedium = &lv_font_montserrat_16; - static const lv_font_t *fontLarge = &lv_font_montserrat_20; - static const lv_font_t *fontLargeMedium = &lv_font_montserrat_24; - static const lv_font_t *fontVeryLarge = &lv_font_montserrat_48; - static const lv_font_t *fontOptions = &lv_font_montserrat_18; - static const float scale = 1.0f; - static const float scaleBut = 1.0f; - static const float scaleSatInfo = 1.0f; + static const lv_font_t *fontDefault = &lv_font_montserrat_14; /**< Default font for large screens */ + static const lv_font_t *fontSmall = &lv_font_montserrat_10; /**< Small font for large screens */ + static const lv_font_t *fontSatInfo = &lv_font_montserrat_16; /**< Satellite info font for large screens */ + static const lv_font_t *fontMedium = &lv_font_montserrat_16; /**< Medium font for large screens */ + static const lv_font_t *fontLarge = &lv_font_montserrat_20; /**< Large font for large screens */ + static const lv_font_t *fontLargeMedium = &lv_font_montserrat_24; /**< Large-medium font for large screens */ + static const lv_font_t *fontVeryLarge = &lv_font_montserrat_48; /**< Very large font for large screens */ + static const lv_font_t *fontOptions = &lv_font_montserrat_18; /**< Options font for large screens */ + static const float scale = 1.0f; /**< UI scale for large screens */ + static const float scaleBut = 1.0f; /**< Button scale for large screens */ + static const float scaleSatInfo = 1.0f; /**< Satellite info scale for large screens */ #else - static const lv_font_t *fontDefault = &lv_font_montserrat_12; - static const lv_font_t *fontSmall = &lv_font_montserrat_8; - static const lv_font_t *fontSatInfo = &lv_font_montserrat_10; - static const lv_font_t *fontMedium = &lv_font_montserrat_14; - static const lv_font_t *fontLarge = &lv_font_montserrat_12; - static const lv_font_t *fontLargeMedium = &lv_font_montserrat_16; - static const lv_font_t *fontVeryLarge = &lv_font_montserrat_38; - static const lv_font_t *fontOptions = &lv_font_montserrat_12; - static const float scale = 0.75f; - static const float scaleBut = 0.60f; - static const float scaleSatInfo = 0.80f; + static const lv_font_t *fontDefault = &lv_font_montserrat_12; /**< Default font for small screens */ + static const lv_font_t *fontSmall = &lv_font_montserrat_8; /**< Small font for small screens */ + static const lv_font_t *fontSatInfo = &lv_font_montserrat_10; /**< Satellite info font for small screens */ + static const lv_font_t *fontMedium = &lv_font_montserrat_14; /**< Medium font for small screens */ + static const lv_font_t *fontLarge = &lv_font_montserrat_12; /**< Large font for small screens */ + static const lv_font_t *fontLargeMedium = &lv_font_montserrat_16; /**< Large-medium font for small screens */ + static const lv_font_t *fontVeryLarge = &lv_font_montserrat_38; /**< Very large font for small screens */ + static const lv_font_t *fontOptions = &lv_font_montserrat_12; /**< Options font for small screens */ + static const float scale = 0.75f; /**< UI scale for small screens */ + static const float scaleBut = 0.60f; /**< Button scale for small screens */ + static const float scaleSatInfo = 0.80f; /**< Satellite info scale for small screens */ #endif -static const int iconScale = LV_SCALE_NONE * scale; -static const int buttonScale = LV_SCALE_NONE * scaleBut; + +static const int iconScale = LV_SCALE_NONE * scale; /**< Icon scale factor */ +static const int buttonScale = LV_SCALE_NONE * scaleBut; /**< Button scale factor */ /** - * @brief Get PNG width and height + * @brief Get PNG width and height from file. * - * @param filename -> PNG file name - * @param width -> PNG width - * @param height -> PNG height - * @return true/false if file doesn't exists + * @param filename PNG file name. + * @param width Pointer to store PNG width. + * @param height Pointer to store PNG height. + * @return true if the file exists and dimensions are read, false otherwise. */ static bool getPngSize(const char* filename, uint16_t *width, uint16_t *height) { - FILE* file = storage.open(filename, "r"); + FILE* file = storage.open(filename, "r"); - if (!file) - return false; + if (!file) + return false; - uint8_t table[32]; + uint8_t table[32]; - fread(table, sizeof(uint8_t), 32, file); + fread(table, sizeof(uint8_t), 32, file); - *width=table[16]*256*256*256+table[17]*256*256+table[18]*256+table[19]; - *height=table[20]*256*256*256+table[21]*256*256+table[22]*256+table[23]; + *width=table[16]*256*256*256+table[17]*256*256+table[18]*256+table[19]; + *height=table[20]*256*256*256+table[21]*256*256+table[22]*256+table[23]; - storage.close(file); + storage.close(file); - return true; + return true; } diff --git a/lib/gui/src/gpxDetailScr.cpp b/lib/gui/src/gpxDetailScr.cpp index d1722966..29ab5fcd 100644 --- a/lib/gui/src/gpxDetailScr.cpp +++ b/lib/gui/src/gpxDetailScr.cpp @@ -2,8 +2,8 @@ * @file gpxDetailScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - GPX Tag detail Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "gpxDetailScr.hpp" @@ -16,7 +16,7 @@ String gpxFileFolder; // bool gpxWaypoint; // bool gpxTrack; -lv_obj_t *gpxDetailScreen; +lv_obj_t *gpxDetailScreen; /**< Waypoint detail screen */ lv_obj_t *gpxTag; lv_obj_t *gpxTagValue; lv_obj_t *labelLat; @@ -26,284 +26,286 @@ lv_obj_t *labelLonValue; bool isScreenRotated = false; /** - * @brief GPX Detail Screen event + * @brief GPX Detail Screen event handler. Handles key and ready/cancel events for adding or editing GPX waypoints. * - * @param event + * @param event LVGL event pointer. */ static void gpxDetailScreenEvent(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); - lv_obj_t *tagName = (lv_obj_t *)lv_event_get_target(event); + lv_event_code_t code = lv_event_get_code(event); + lv_obj_t *tagName = (lv_obj_t *)lv_event_get_target(event); - #ifdef TDECK_ESP32S3 - if (code == LV_EVENT_KEY) - { - if ( lv_indev_get_key(lv_indev_active()) == 13 ) // Enter Key - { - createWptFile(); - GPXParser gpx; + #ifdef TDECK_ESP32S3 + if (code == LV_EVENT_KEY) + { + if ( lv_indev_get_key(lv_indev_active()) == 13 ) // Enter Key + { + createWptFile(); + GPXParser gpx; - switch (gpxAction) - { - case WPT_ADD: - addWpt.name = (char *)lv_textarea_get_text(tagName); - if (strcmp(addWpt.name,"") != 0) - { - gpx.filePath = wptFile; - gpx.addWaypoint(addWpt); - } - break; - case GPX_EDIT: - char *newName = (char *)lv_textarea_get_text(tagName); - if (strcmp(loadWpt.name, newName) != 0) - { - gpx.filePath = gpxFileFolder.c_str(); - if (gpxWaypoint) - gpx.editTagAttrOrElem(gpxWaypointTag, nullptr, gpxNameElem, loadWpt.name, newName); - if (gpxTrack) - gpx.editTagAttrOrElem(gpxTrackTag, nullptr, gpxNameElem, loadWpt.name, newName); - } - break; - } + switch (gpxAction) + { + case WPT_ADD: + addWpt.name = (char *)lv_textarea_get_text(tagName); + if (strcmp(addWpt.name,"") != 0) + { + gpx.filePath = wptFile; + gpx.addWaypoint(addWpt); + } + break; + case GPX_EDIT: + char *newName = (char *)lv_textarea_get_text(tagName); + if (strcmp(loadWpt.name, newName) != 0) + { + gpx.filePath = gpxFileFolder.c_str(); + if (gpxWaypoint) + gpx.editTagAttrOrElem(gpxWaypointTag, nullptr, gpxNameElem, loadWpt.name, newName); + if (gpxTrack) + gpx.editTagAttrOrElem(gpxTrackTag, nullptr, gpxNameElem, loadWpt.name, newName); + } + break; + } - isMainScreen = true; - mapView.redrawMap = true; - gpxAction = WPT_NONE; - lv_refr_now(display); - loadMainScreen(); - } + isMainScreen = true; + mapView.redrawMap = true; + gpxAction = WPT_NONE; + lv_refr_now(display); + loadMainScreen(); + } - if ( lv_indev_get_key(lv_indev_active()) == 35 ) // # Key (ESCAPE) - { - isMainScreen = true; - mapView.redrawMap = true; - gpxAction = WPT_NONE; - lv_refr_now(display); - loadMainScreen(); - } - } - #endif + if ( lv_indev_get_key(lv_indev_active()) == 35 ) // # Key (ESCAPE) + { + isMainScreen = true; + mapView.redrawMap = true; + gpxAction = WPT_NONE; + lv_refr_now(display); + loadMainScreen(); + } + } + #endif - if (code == LV_EVENT_READY) - { - if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270) - { - tft.setRotation(0); - lv_display_set_rotation(display,LV_DISPLAY_ROTATION_0); - } - - createWptFile(); - GPXParser gpx; + if (code == LV_EVENT_READY) + { + if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270) + { + tft.setRotation(0); + lv_display_set_rotation(display,LV_DISPLAY_ROTATION_0); + } + + createWptFile(); + GPXParser gpx; - switch (gpxAction) - { - case WPT_ADD: - addWpt.name = (char *)lv_textarea_get_text(tagName); - if (strcmp(addWpt.name,"") != 0) - { - gpx.filePath = wptFile; - gpx.addWaypoint(addWpt); - } - break; - case GPX_EDIT: - char *newName = (char *)lv_textarea_get_text(tagName); - if (strcmp(loadWpt.name, newName) != 0) - { - gpx.filePath = gpxFileFolder.c_str(); - if (gpxWaypoint) - gpx.editTagAttrOrElem(gpxWaypointTag, nullptr, gpxNameElem, loadWpt.name, newName); - if (gpxTrack) - gpx.editTagAttrOrElem(gpxTrackTag, nullptr, gpxNameElem, loadWpt.name, newName); - } - break; - } + switch (gpxAction) + { + case WPT_ADD: + addWpt.name = (char *)lv_textarea_get_text(tagName); + if (strcmp(addWpt.name,"") != 0) + { + gpx.filePath = wptFile; + gpx.addWaypoint(addWpt); + } + break; + case GPX_EDIT: + char *newName = (char *)lv_textarea_get_text(tagName); + if (strcmp(loadWpt.name, newName) != 0) + { + gpx.filePath = gpxFileFolder.c_str(); + if (gpxWaypoint) + gpx.editTagAttrOrElem(gpxWaypointTag, nullptr, gpxNameElem, loadWpt.name, newName); + if (gpxTrack) + gpx.editTagAttrOrElem(gpxTrackTag, nullptr, gpxNameElem, loadWpt.name, newName); + } + break; + } - isMainScreen = true; - mapView.redrawMap = true; - gpxAction = WPT_NONE; - lv_refr_now(display); - loadMainScreen(); - } + isMainScreen = true; + mapView.redrawMap = true; + gpxAction = WPT_NONE; + lv_refr_now(display); + loadMainScreen(); + } - if (code == LV_EVENT_CANCEL) - { - if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270) - { - tft.setRotation(0); - lv_display_set_rotation(display,LV_DISPLAY_ROTATION_0); - } - isMainScreen = true; - mapView.redrawMap = true; - gpxAction = WPT_NONE; - lv_refr_now(display); - loadMainScreen(); - } + if (code == LV_EVENT_CANCEL) + { + if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270) + { + tft.setRotation(0); + lv_display_set_rotation(display,LV_DISPLAY_ROTATION_0); + } + isMainScreen = true; + mapView.redrawMap = true; + gpxAction = WPT_NONE; + lv_refr_now(display); + loadMainScreen(); + } } /** - * @brief Rotate Screen event + * @brief Rotate Screen event handler. Toggles the screen rotation state and updates the display accordingly. * - * @param event + * @param event LVGL event pointer. */ static void rotateScreen(lv_event_t *event) { - isScreenRotated = !isScreenRotated; - log_v("%d",isScreenRotated); - if (isScreenRotated) - { - tft.setRotation(1); - lv_display_set_rotation(display, LV_DISPLAY_ROTATION_270); - } - else - { - tft.setRotation(0); - lv_display_set_rotation(display, LV_DISPLAY_ROTATION_0); - } - lv_obj_set_width(gpxTagValue, tft.width() -10); - lv_refr_now(display); + isScreenRotated = !isScreenRotated; + log_v("%d",isScreenRotated); + if (isScreenRotated) + { + tft.setRotation(1); + lv_display_set_rotation(display, LV_DISPLAY_ROTATION_270); + } + else + { + tft.setRotation(0); + lv_display_set_rotation(display, LV_DISPLAY_ROTATION_0); + } + lv_obj_set_width(gpxTagValue, tft.width() -10); + lv_refr_now(display); } /** - * @brief GPX Tag Name event + * @brief GPX Waypoint Name event handler. Handles click event for adding or editing the GPX waypoint name. * - * @param event + * @param event LVGL event pointer. */ static void gpxTagNameEvent(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); + lv_event_code_t code = lv_event_get_code(event); - if(code == LV_EVENT_CLICKED) - { - createWptFile(); - GPXParser gpx; + if(code == LV_EVENT_CLICKED) + { + createWptFile(); + GPXParser gpx; - switch (gpxAction) - { - case WPT_ADD: - addWpt.name = (char *)lv_textarea_get_text(gpxTagValue); - if (strcmp(addWpt.name,"") != 0) - { - gpx.filePath = wptFile; - gpx.addWaypoint(addWpt); - } - case GPX_EDIT: - char *newName = (char *)lv_textarea_get_text(gpxTagValue); - if (strcmp(loadWpt.name, newName) != 0) - { - gpx.filePath = gpxFileFolder.c_str(); - if (gpxWaypoint) - gpx.editTagAttrOrElem(gpxWaypointTag, nullptr, gpxNameElem, loadWpt.name, newName); - if (gpxTrack) - gpx.editTagAttrOrElem(gpxTrackTag, nullptr, gpxNameElem, loadWpt.name, newName); - } - break; - } + switch (gpxAction) + { + case WPT_ADD: + addWpt.name = (char *)lv_textarea_get_text(gpxTagValue); + if (strcmp(addWpt.name,"") != 0) + { + gpx.filePath = wptFile; + gpx.addWaypoint(addWpt); + } + case GPX_EDIT: + char *newName = (char *)lv_textarea_get_text(gpxTagValue); + if (strcmp(loadWpt.name, newName) != 0) + { + gpx.filePath = gpxFileFolder.c_str(); + if (gpxWaypoint) + gpx.editTagAttrOrElem(gpxWaypointTag, nullptr, gpxNameElem, loadWpt.name, newName); + if (gpxTrack) + gpx.editTagAttrOrElem(gpxTrackTag, nullptr, gpxNameElem, loadWpt.name, newName); + } + break; + } - isMainScreen = true; - mapView.redrawMap = true; - gpxAction = WPT_NONE; - lv_refr_now(display); - loadMainScreen(); - } + isMainScreen = true; + mapView.redrawMap = true; + gpxAction = WPT_NONE; + lv_refr_now(display); + loadMainScreen(); + } } /** - * @brief Update current waypoint + * @brief Update current waypoint. * - * @param action add or edit action + * @param action Action type (add or edit). */ void updateWaypoint(uint8_t action) { - switch (action) - { - case WPT_ADD: - addWpt.lat = gps.gpsData.latitude; - addWpt.lon = gps.gpsData.longitude; - addWpt.ele = gps.gpsData.altitude; - addWpt.sat = gps.gpsData.satellites; - addWpt.hdop = gps.gpsData.hdop; - addWpt.pdop = gps.gpsData.pdop; - addWpt.vdop = gps.gpsData.vdop; - lv_label_set_text_static(labelLatValue, latFormatString(addWpt.lat)); - lv_label_set_text_static(labelLonValue, lonFormatString(addWpt.lon)); - break; - case GPX_EDIT: - lv_label_set_text_static(labelLatValue, latFormatString(loadWpt.lat)); - lv_label_set_text_static(labelLonValue, lonFormatString(loadWpt.lon)); - break; - default: - break; - } + switch (action) + { + case WPT_ADD: + addWpt.lat = gps.gpsData.latitude; + addWpt.lon = gps.gpsData.longitude; + addWpt.ele = gps.gpsData.altitude; + addWpt.sat = gps.gpsData.satellites; + addWpt.hdop = gps.gpsData.hdop; + addWpt.pdop = gps.gpsData.pdop; + addWpt.vdop = gps.gpsData.vdop; + lv_label_set_text_static(labelLatValue, latFormatString(addWpt.lat)); + lv_label_set_text_static(labelLonValue, lonFormatString(addWpt.lon)); + break; + case GPX_EDIT: + lv_label_set_text_static(labelLatValue, latFormatString(loadWpt.lat)); + lv_label_set_text_static(labelLonValue, lonFormatString(loadWpt.lon)); + break; + default: + break; + } } /** * @brief Create Waypoint screen * + * @details Initializes the GPX Detail screen, including the text area for waypoint name, + * rotation button (if not TDECK_ESP32S3), and labels for latitude and longitude values. */ void createGpxDetailScreen() { - gpxDetailScreen = lv_obj_create(NULL); + gpxDetailScreen = lv_obj_create(NULL); - gpxTagValue = lv_textarea_create(gpxDetailScreen); - lv_textarea_set_one_line(gpxTagValue, true); - lv_obj_align(gpxTagValue, LV_ALIGN_TOP_MID, 0, 40); - lv_obj_set_width(gpxTagValue, tft.width() - 10); - lv_obj_add_state(gpxTagValue, LV_STATE_FOCUSED); - lv_obj_add_event_cb(gpxTagValue, gpxDetailScreenEvent, LV_EVENT_ALL, gpxDetailScreen); - #ifndef TDECK_ESP32S3 - lv_obj_t *keyboard = lv_keyboard_create(gpxDetailScreen); - lv_keyboard_set_mode(keyboard,LV_KEYBOARD_MODE_TEXT_UPPER); - lv_keyboard_set_textarea(keyboard, gpxTagValue); - #endif + gpxTagValue = lv_textarea_create(gpxDetailScreen); + lv_textarea_set_one_line(gpxTagValue, true); + lv_obj_align(gpxTagValue, LV_ALIGN_TOP_MID, 0, 40); + lv_obj_set_width(gpxTagValue, tft.width() - 10); + lv_obj_add_state(gpxTagValue, LV_STATE_FOCUSED); + lv_obj_add_event_cb(gpxTagValue, gpxDetailScreenEvent, LV_EVENT_ALL, gpxDetailScreen); + #ifndef TDECK_ESP32S3 + lv_obj_t *keyboard = lv_keyboard_create(gpxDetailScreen); + lv_keyboard_set_mode(keyboard,LV_KEYBOARD_MODE_TEXT_UPPER); + lv_keyboard_set_textarea(keyboard, gpxTagValue); + #endif - #ifdef TDECK_ESP32S3 - lv_group_add_obj(scrGroup, gpxTagValue); - lv_group_focus_obj(gpxTagValue); - #endif + #ifdef TDECK_ESP32S3 + lv_group_add_obj(scrGroup, gpxTagValue); + lv_group_focus_obj(gpxTagValue); + #endif - #ifndef TDECK_ESP32S3 - // Rotate Screen button - static lv_style_t editBtnStyleOn; - lv_style_init(&editBtnStyleOn); - lv_style_set_bg_color(&editBtnStyleOn, lv_color_black()); - lv_style_set_text_color(&editBtnStyleOn, lv_color_white()); - lv_obj_t *rotateScreenBtn = lv_button_create(gpxDetailScreen); - lv_obj_add_style(rotateScreenBtn, &editBtnStyleOn, LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_align(rotateScreenBtn, LV_ALIGN_TOP_RIGHT, -10, 5); - lv_obj_add_flag(rotateScreenBtn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(rotateScreenBtn, rotateScreen, LV_EVENT_CLICKED, NULL); - lv_obj_set_size(rotateScreenBtn, 40, 35); - lv_obj_t *rotateScreenLbl = lv_label_create(rotateScreenBtn); - lv_label_set_text(rotateScreenLbl, LV_SYMBOL_LOOP); - lv_obj_center(rotateScreenLbl); - #endif + #ifndef TDECK_ESP32S3 + // Rotate Screen button + static lv_style_t editBtnStyleOn; + lv_style_init(&editBtnStyleOn); + lv_style_set_bg_color(&editBtnStyleOn, lv_color_black()); + lv_style_set_text_color(&editBtnStyleOn, lv_color_white()); + lv_obj_t *rotateScreenBtn = lv_button_create(gpxDetailScreen); + lv_obj_add_style(rotateScreenBtn, &editBtnStyleOn, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_align(rotateScreenBtn, LV_ALIGN_TOP_RIGHT, -10, 5); + lv_obj_add_flag(rotateScreenBtn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(rotateScreenBtn, rotateScreen, LV_EVENT_CLICKED, NULL); + lv_obj_set_size(rotateScreenBtn, 40, 35); + lv_obj_t *rotateScreenLbl = lv_label_create(rotateScreenBtn); + lv_label_set_text(rotateScreenLbl, LV_SYMBOL_LOOP); + lv_obj_center(rotateScreenLbl); + #endif - gpxTag = lv_label_create(gpxDetailScreen); - lv_obj_set_style_text_font(gpxTag, fontOptions, 0); - lv_label_set_text_static(gpxTag, LV_SYMBOL_LEFT " Waypoint Name:"); - lv_obj_center(gpxTag); - lv_obj_align(gpxTag,LV_ALIGN_TOP_LEFT,10,10); - lv_obj_add_flag(gpxTag, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_event_cb(gpxTag, gpxTagNameEvent, LV_EVENT_ALL, NULL); + gpxTag = lv_label_create(gpxDetailScreen); + lv_obj_set_style_text_font(gpxTag, fontOptions, 0); + lv_label_set_text_static(gpxTag, LV_SYMBOL_LEFT " Waypoint Name:"); + lv_obj_center(gpxTag); + lv_obj_align(gpxTag,LV_ALIGN_TOP_LEFT,10,10); + lv_obj_add_flag(gpxTag, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(gpxTag, gpxTagNameEvent, LV_EVENT_ALL, NULL); - labelLat = lv_label_create(gpxDetailScreen); - lv_obj_set_style_text_font(labelLat, fontOptions, 0); - lv_label_set_text_static(labelLat, "Lat:"); - lv_obj_set_pos(labelLat, 10, 90); + labelLat = lv_label_create(gpxDetailScreen); + lv_obj_set_style_text_font(labelLat, fontOptions, 0); + lv_label_set_text_static(labelLat, "Lat:"); + lv_obj_set_pos(labelLat, 10, 90); - labelLon = lv_label_create(gpxDetailScreen); - lv_obj_set_style_text_font(labelLon, fontOptions, 0); - lv_label_set_text_static(labelLon, "Lon:"); - lv_obj_set_pos(labelLon, 10, 120); + labelLon = lv_label_create(gpxDetailScreen); + lv_obj_set_style_text_font(labelLon, fontOptions, 0); + lv_label_set_text_static(labelLon, "Lon:"); + lv_obj_set_pos(labelLon, 10, 120); - labelLatValue = lv_label_create(gpxDetailScreen); - lv_obj_set_style_text_font(labelLatValue, fontOptions, 0); - lv_label_set_text_static(labelLatValue, latFormatString(addWpt.lat)); - lv_obj_set_pos(labelLatValue, 60, 90); - - labelLonValue = lv_label_create(gpxDetailScreen); - lv_obj_set_style_text_font(labelLonValue, fontOptions, 0); - lv_label_set_text_static(labelLonValue, lonFormatString(addWpt.lon)); - lv_obj_set_pos(labelLonValue, 60, 120); + labelLatValue = lv_label_create(gpxDetailScreen); + lv_obj_set_style_text_font(labelLatValue, fontOptions, 0); + lv_label_set_text_static(labelLatValue, latFormatString(addWpt.lat)); + lv_obj_set_pos(labelLatValue, 60, 90); + + labelLonValue = lv_label_create(gpxDetailScreen); + lv_obj_set_style_text_font(labelLonValue, fontOptions, 0); + lv_label_set_text_static(labelLonValue, lonFormatString(addWpt.lon)); + lv_obj_set_pos(labelLonValue, 60, 120); } diff --git a/lib/gui/src/gpxDetailScr.hpp b/lib/gui/src/gpxDetailScr.hpp index d2b9411e..7d37930a 100644 --- a/lib/gui/src/gpxDetailScr.hpp +++ b/lib/gui/src/gpxDetailScr.hpp @@ -2,8 +2,8 @@ * @file gpxDetailScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - GPX Tag detail Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -12,13 +12,13 @@ #include "gpxScr.hpp" #include "mainScr.hpp" -extern lv_obj_t *gpxTagValue; -extern lv_obj_t *gpxTag; -extern lv_obj_t *labelLat; -extern lv_obj_t *labelLatValue; -extern lv_obj_t *labelLon; -extern lv_obj_t *labelLonValue; -extern bool isScreenRotated; +extern lv_obj_t *gpxTagValue; /**< Add/Edit Waypoint name screen text area */ +extern lv_obj_t *gpxTag; /**< Name Tag label */ +extern lv_obj_t *labelLat; /**< Latitude label */ +extern lv_obj_t *labelLatValue; /**< Latitude value label */ +extern lv_obj_t *labelLon; /**< Longitude label */ +extern lv_obj_t *labelLonValue; /**< Longitude value label */ +extern bool isScreenRotated; /**< Flag to know if screen is rotated */ void loadMainScreen(); diff --git a/lib/gui/src/gpxScr.cpp b/lib/gui/src/gpxScr.cpp index f04435ff..bed7970f 100644 --- a/lib/gui/src/gpxScr.cpp +++ b/lib/gui/src/gpxScr.cpp @@ -2,8 +2,8 @@ * @file gpxScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - GPX list screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "gpxScr.hpp" @@ -16,201 +16,214 @@ extern bool isWaypointOpt; extern bool isTrackOpt; bool gpxWaypoint = false; bool gpxTrack = false; -extern std::vector trackData; +bool isTrackLoaded = false; -lv_obj_t *listGPXScreen; // Add Waypoint Screen +extern std::vector trackData; /**< Vector containing track waypoints */ +extern std::vector turnPoints; /**< Vector containing turn points */ + +lv_obj_t *listGPXScreen; /**< Add Waypoint screen */ static const char* TAG PROGMEM = "GPX List Screen"; /** - * @brief Way point list event - * + * @brief Waypoint list event handler. Handles long-press events on the GPX waypoint list for load, edit, or delete actions. + * + * @param event LVGL event pointer. */ void gpxListEvent(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); - lv_obj_t *obj = (lv_obj_t *)lv_event_get_current_target(event); - uint32_t row; - uint32_t col; - - if (code == LV_EVENT_LONG_PRESSED) - { - lv_table_get_selected_cell(obj, &row, &col); - - if (row != 0) - { - String sel = String(lv_table_get_cell_value(obj, row, col)); - String gpxName = sel.substring(6,sel.length()); - String gpxFile = String(lv_table_get_cell_value(obj, row, col+1)); - - if (gpxWaypoint) - gpxFileFolder = String(wptFolder) + "/" + gpxFile; - else if (gpxTrack) - gpxFileFolder = String(trkFolder) + "/" + gpxFile; - - GPXParser gpx(gpxFileFolder.c_str()); - - if (!sel.isEmpty()) - { - switch (gpxAction) - { - case GPX_LOAD: - if (gpxWaypoint) - { - loadWpt = gpx.getWaypointInfo(gpxName.c_str()); - LV_IMG_DECLARE(navup); - lv_img_set_src(arrowNav, &navup); - - if (loadWpt.lat != 0 && loadWpt.lon != 0) - { - lv_obj_clear_flag(navTile, LV_OBJ_FLAG_HIDDEN); - - lv_label_set_text_fmt(latNav, "%s", latFormatString(loadWpt.lat)); - lv_label_set_text_fmt(lonNav, "%s", lonFormatString(loadWpt.lon)); - lv_label_set_text_fmt(nameNav, "%s", loadWpt.name); - - mapView.setWaypoint(loadWpt.lat, loadWpt.lon); - mapView.updateMap(); - - lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); - } - else - lv_obj_add_flag(navTile, LV_OBJ_FLAG_HIDDEN); - } - - if (gpxTrack) - { - gpx.loadTrack(trackData); - mapView.updateMap(); - lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); - } - - loadMainScreen(); - break; - case GPX_EDIT: - isMainScreen = false; - mapView.redrawMap = false; - - if (gpxWaypoint) - { - loadWpt = gpx.getWaypointInfo(gpxName.c_str()); - lv_textarea_set_text(gpxTagValue, loadWpt.name); - lv_label_set_text_static(gpxTag, LV_SYMBOL_LEFT " Waypoint Name:"); - lv_obj_clear_flag(labelLat, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(labelLatValue, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(labelLon, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(labelLonValue, LV_OBJ_FLAG_HIDDEN); - } - - if (gpxTrack) - { - loadWpt.name = strdup(gpxName.c_str()); - lv_textarea_set_text(gpxTagValue, loadWpt.name); - lv_label_set_text_static(gpxTag, LV_SYMBOL_LEFT " Track Name:"); - lv_obj_add_flag(labelLat, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(labelLatValue, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(labelLon, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(labelLonValue, LV_OBJ_FLAG_HIDDEN); - } - - isScreenRotated = false; - lv_obj_set_width(gpxTagValue, tft.width() - 10); - updateWaypoint(gpxAction); - lv_screen_load(gpxDetailScreen); - - break; - case GPX_DEL: - if (gpxWaypoint) - gpx.deleteTagByName(gpxWaypointTag, gpxName.c_str()); - if (gpxTrack) - gpx.deleteTagByName(gpxTrackTag, gpxName.c_str()); - - loadMainScreen(); - break; - default: - break; - } - } - } - else if (row == 0) - { - lv_obj_add_flag(navTile, LV_OBJ_FLAG_HIDDEN); - loadMainScreen(); - } - } + lv_event_code_t code = lv_event_get_code(event); + lv_obj_t *obj = (lv_obj_t *)lv_event_get_current_target(event); + uint32_t row; + uint32_t col; + + if (code == LV_EVENT_LONG_PRESSED) + { + lv_table_get_selected_cell(obj, &row, &col); + + if (row != 0) + { + String sel = String(lv_table_get_cell_value(obj, row, col)); + String gpxName = sel.substring(6,sel.length()); + String gpxFile = String(lv_table_get_cell_value(obj, row, col+1)); + + if (gpxWaypoint) + gpxFileFolder = String(wptFolder) + "/" + gpxFile; + else if (gpxTrack) + gpxFileFolder = String(trkFolder) + "/" + gpxFile; + + GPXParser gpx(gpxFileFolder.c_str()); + + if (!sel.isEmpty()) + { + switch (gpxAction) + { + case GPX_LOAD: + showMsg(LV_SYMBOL_DOWNLOAD, " Loading data..."); + if (gpxWaypoint) + { + loadWpt = gpx.getWaypointInfo(gpxName.c_str()); + LV_IMG_DECLARE(navup); + lv_img_set_src(arrowNav, &navup); + + if (loadWpt.lat != 0 && loadWpt.lon != 0) + { + lv_obj_clear_flag(navTile, LV_OBJ_FLAG_HIDDEN); + + lv_label_set_text_fmt(latNav, "%s", latFormatString(loadWpt.lat)); + lv_label_set_text_fmt(lonNav, "%s", lonFormatString(loadWpt.lon)); + lv_label_set_text_fmt(nameNav, "%s", loadWpt.name); + + mapView.setWaypoint(loadWpt.lat, loadWpt.lon); + mapView.updateMap(); + + lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); + } + else + lv_obj_add_flag(navTile, LV_OBJ_FLAG_HIDDEN); + } + + if (gpxTrack) + { + gpx.loadTrack(trackData); + turnPoints = gpx.getTurnPointsSlidingWindow(18.0f, 10, 70.0f, 5, trackData); + isTrackLoaded = true; + lv_obj_clear_flag(turnByTurn,LV_OBJ_FLAG_HIDDEN); + mapView.updateMap(); + lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); + } + closeMsg(); + loadMainScreen(); + break; + case GPX_EDIT: + isMainScreen = false; + mapView.redrawMap = false; + + if (gpxWaypoint) + { + loadWpt = gpx.getWaypointInfo(gpxName.c_str()); + lv_textarea_set_text(gpxTagValue, loadWpt.name); + lv_label_set_text_static(gpxTag, LV_SYMBOL_LEFT " Waypoint Name:"); + lv_obj_clear_flag(labelLat, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(labelLatValue, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(labelLon, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(labelLonValue, LV_OBJ_FLAG_HIDDEN); + } + + if (gpxTrack) + { + loadWpt.name = strdup(gpxName.c_str()); + lv_textarea_set_text(gpxTagValue, loadWpt.name); + lv_label_set_text_static(gpxTag, LV_SYMBOL_LEFT " Track Name:"); + lv_obj_add_flag(labelLat, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(labelLatValue, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(labelLon, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(labelLonValue, LV_OBJ_FLAG_HIDDEN); + } + + isScreenRotated = false; + lv_obj_set_width(gpxTagValue, tft.width() - 10); + updateWaypoint(gpxAction); + lv_screen_load(gpxDetailScreen); + + break; + case GPX_DEL: + if (gpxWaypoint) + gpx.deleteTagByName(gpxWaypointTag, gpxName.c_str()); + if (gpxTrack) + gpx.deleteTagByName(gpxTrackTag, gpxName.c_str()); + + loadMainScreen(); + break; + default: + break; + } + } + } + else if (row == 0) + { + lv_obj_add_flag(navTile, LV_OBJ_FLAG_HIDDEN); + loadMainScreen(); + } + } } /** * @brief Create List Waypoint Screen * + * @details Initializes the GPX Waypoint List screen with a two-column table, + * sets up column widths, headers, styles, and attaches the event callback for handling user actions. */ void createGpxListScreen() { - listGPXScreen = lv_table_create(NULL); - lv_table_set_col_cnt(listGPXScreen, 2); - lv_table_set_column_width(listGPXScreen,1,400); - lv_obj_set_size(listGPXScreen, TFT_WIDTH, TFT_HEIGHT); - lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); - lv_table_set_cell_value(listGPXScreen, 0, 1, LV_SYMBOL_FILE " File"); - lv_table_set_column_width(listGPXScreen, 0, TFT_WIDTH); - lv_obj_add_event_cb(listGPXScreen, gpxListEvent, LV_EVENT_ALL, NULL); - lv_obj_set_style_pad_ver(listGPXScreen, 15, LV_PART_ITEMS); - lv_obj_set_style_border_width(listGPXScreen, 1, LV_PART_ITEMS); - lv_obj_set_style_border_color(listGPXScreen, lv_color_hex(0x303030), LV_PART_ITEMS); - lv_obj_set_style_border_side(listGPXScreen, LV_BORDER_SIDE_BOTTOM, LV_PART_ITEMS | LV_STATE_DEFAULT); - lv_obj_set_style_bg_color(listGPXScreen, lv_color_hex(0x303030), LV_PART_ITEMS | LV_STATE_PRESSED); - lv_obj_set_style_bg_opa(listGPXScreen, LV_OPA_100, LV_PART_ITEMS | LV_STATE_PRESSED); + listGPXScreen = lv_table_create(NULL); + lv_table_set_col_cnt(listGPXScreen, 2); + lv_table_set_column_width(listGPXScreen,1,400); + lv_obj_set_size(listGPXScreen, TFT_WIDTH, TFT_HEIGHT); + lv_table_set_cell_value(listGPXScreen, 0, 0, LV_SYMBOL_LEFT " Waypoints"); + lv_table_set_cell_value(listGPXScreen, 0, 1, LV_SYMBOL_FILE " File"); + lv_table_set_column_width(listGPXScreen, 0, TFT_WIDTH); + lv_obj_add_event_cb(listGPXScreen, gpxListEvent, LV_EVENT_ALL, NULL); + lv_obj_set_style_pad_ver(listGPXScreen, 15, LV_PART_ITEMS); + lv_obj_set_style_border_width(listGPXScreen, 1, LV_PART_ITEMS); + lv_obj_set_style_border_color(listGPXScreen, lv_color_hex(0x303030), LV_PART_ITEMS); + lv_obj_set_style_border_side(listGPXScreen, LV_BORDER_SIDE_BOTTOM, LV_PART_ITEMS | LV_STATE_DEFAULT); + lv_obj_set_style_bg_color(listGPXScreen, lv_color_hex(0x303030), LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_set_style_bg_opa(listGPXScreen, LV_OPA_100, LV_PART_ITEMS | LV_STATE_PRESSED); } /** - * @brief Update List Waypoint Screen from file + * @brief Update List Waypoint Screen from file. * + * @details Refreshes the GPX list screen by loading and displaying waypoints or tracks from the corresponding files and folders. */ void updateGpxListScreen() { - lv_obj_clean(listGPXScreen); - lv_table_set_row_count(listGPXScreen, 1); - isMainScreen = false; - - if (isWaypointOpt) - { - gpxWaypoint = true; - gpxTrack = false; - uint16_t totalGpx = 1; - std::map> waypointByFile = GPXParser::getTagElementList(gpxWaypointTag, gpxNameElem, wptFolder); - - for (std::map>::const_iterator it = waypointByFile.begin(); it != waypointByFile.end(); ++it) - { - const std::string& fileName = it->first; - const std::vector& waypointNames = it->second; - - for (const std::string& gpxTagValue : waypointNames) - { - lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 0, LV_SYMBOL_GPS " - %s", gpxTagValue.c_str()); - lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 1, "%s", fileName.c_str()); - totalGpx++; - } - } - } - - if (isTrackOpt) - { - gpxWaypoint = false; - gpxTrack = true; - uint16_t totalGpx = 1; - std::map> tracksByFile = GPXParser::getTagElementList(gpxTrackTag, gpxNameElem, trkFolder); - - for (std::map>::const_iterator it = tracksByFile.begin(); it != tracksByFile.end(); ++it) - { - const std::string& fileName = it->first; - const std::vector& trackNames = it->second; - - for (const std::string& trackName : trackNames) - { - lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 0, LV_SYMBOL_SHUFFLE " - %s", trackName.c_str()); - lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 1, "%s", fileName.c_str()); - totalGpx++; - } - } - } + lv_obj_clean(listGPXScreen); + lv_table_set_row_count(listGPXScreen, 1); + isMainScreen = false; + + showMsg(LV_SYMBOL_DOWNLOAD," Getting files..."); + if (isWaypointOpt) + { + gpxWaypoint = true; + gpxTrack = false; + uint16_t totalGpx = 1; + std::map> waypointByFile = GPXParser::getTagElementList(gpxWaypointTag, gpxNameElem, wptFolder); + + for (std::map>::const_iterator it = waypointByFile.begin(); it != waypointByFile.end(); ++it) + { + const std::string& fileName = it->first; + const std::vector& waypointNames = it->second; + + for (const std::string& gpxTagValue : waypointNames) + { + lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 0, LV_SYMBOL_GPS " - %s", gpxTagValue.c_str()); + lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 1, "%s", fileName.c_str()); + totalGpx++; + } + } + } + + if (isTrackOpt) + { + gpxWaypoint = false; + gpxTrack = true; + uint16_t totalGpx = 1; + std::map> tracksByFile = GPXParser::getTagElementList(gpxTrackTag, gpxNameElem, trkFolder); + + for (std::map>::const_iterator it = tracksByFile.begin(); it != tracksByFile.end(); ++it) + { + const std::string& fileName = it->first; + const std::vector& trackNames = it->second; + + for (const std::string& trackName : trackNames) + { + lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 0, LV_SYMBOL_SHUFFLE " - %s", trackName.c_str()); + lv_table_set_cell_value_fmt(listGPXScreen, totalGpx, 1, "%s", fileName.c_str()); + totalGpx++; + } + } + } + closeMsg(); } \ No newline at end of file diff --git a/lib/gui/src/gpxScr.hpp b/lib/gui/src/gpxScr.hpp index 4de8d287..2786d015 100644 --- a/lib/gui/src/gpxScr.hpp +++ b/lib/gui/src/gpxScr.hpp @@ -2,8 +2,8 @@ * @file gpxScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - GPX list screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -13,9 +13,9 @@ #include "gpxParser.hpp" #include "buttonBar.hpp" -extern String gpxFileFolder; -extern bool gpxTrack; -extern bool gpxWaypoint; +extern String gpxFileFolder; /**< GPX file folder path */ +extern bool gpxTrack; /**< Track mode flag */ +extern bool gpxWaypoint; /**< Waypoint mode flag */ void loadMainScreen(); void updateWaypoint(uint8_t action); diff --git a/lib/gui/src/mainScr.cpp b/lib/gui/src/mainScr.cpp index 0788cf64..7ef975be 100644 --- a/lib/gui/src/mainScr.cpp +++ b/lib/gui/src/mainScr.cpp @@ -2,33 +2,33 @@ * @file mainScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Main Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "mainScr.hpp" -bool isMainScreen = false; // Flag to indicate main screen is selected -bool isScrolled = true; // Flag to indicate when tileview was scrolled -bool isReady = false; // Flag to indicate when tileview scroll was finished -bool isScrollingMap = false; // Flag to indicate if map is scrolling -bool canScrollMap = false; // Flag to indicate whet can scroll map -uint8_t activeTile = 0; // Current active tile -uint8_t gpxAction = WPT_NONE; // Current Waypoint Action -int heading = 0; // Heading value (Compass or GPS) -extern uint32_t DOUBLE_TOUCH_EVENT; +bool isMainScreen = false; +bool isScrolled = true; +bool isReady = false; +bool isScrollingMap = false; +bool canScrollMap = false; +uint8_t activeTile = 0; +uint8_t gpxAction = WPT_NONE; +int heading = 0; + +extern uint32_t DOUBLE_TOUCH_EVENT; /**< Event code for double touch gesture */ extern Compass compass; extern Gps gps; extern wayPoint loadWpt; #ifdef LARGE_SCREEN -uint8_t toolBarOffset = 100; -uint8_t toolBarSpace = 60; -#endif -#ifndef LARGE_SCREEN -uint8_t toolBarOffset = 80; -uint8_t toolBarSpace = 50; + uint8_t toolBarOffset = 100; /**< Toolbar offset for large screens */ + uint8_t toolBarSpace = 60; /**< Toolbar spacing for large screens */ +#else + uint8_t toolBarOffset = 80; /**< Toolbar offset for standard screens */ + uint8_t toolBarSpace = 50; /**< Toolbar spacing for standard screens */ #endif lv_obj_t *tilesScreen; @@ -36,575 +36,519 @@ lv_obj_t *compassTile; lv_obj_t *navTile; lv_obj_t *mapTile; lv_obj_t *satTrackTile; -lv_obj_t *btnFullScreen; lv_obj_t *btnZoomIn; lv_obj_t *btnZoomOut; -Maps mapView; +lv_obj_t *mapCanvas; /**< LVGL for the map canvas */ +lv_layer_t canvasMapLayer; /**< LVGL drawing layer for the map canvas */ + +extern Maps mapView; /** * @brief Update compass screen event * - * @param event + * @details Updates the compass screen UI elements (heading, coordinates, altitude, speed, sunrise/sunset) with current GPS and heading data when the relevant event is triggered. + * + * @param event LVGL event pointer. */ void updateCompassScr(lv_event_t *event) { - lv_obj_t *obj = (lv_obj_t *)lv_event_get_current_target(event); - if (obj == compassHeading) - { - lv_label_set_text_fmt(compassHeading, "%5d\xC2\xB0", heading); - lv_img_set_angle(compassImg, -(heading * 10)); - } - if (obj == latitude) - lv_label_set_text_fmt(latitude, "%s", latFormatString(gps.gpsData.latitude)); - if (obj == longitude) - lv_label_set_text_fmt(longitude, "%s", lonFormatString(gps.gpsData.longitude)); - if (obj == altitude) - lv_label_set_text_fmt(obj, "%4d m.", gps.gpsData.altitude); - if (obj == speedLabel) - lv_label_set_text_fmt(obj, "%3d Km/h", gps.gpsData.speed); - if (obj == sunriseLabel) - { - lv_label_set_text_static(obj, gps.gpsData.sunriseHour); - lv_label_set_text_static(sunsetLabel, gps.gpsData.sunsetHour); - } + lv_obj_t *obj = (lv_obj_t *)lv_event_get_current_target(event); + if (obj == compassHeading) + { + lv_label_set_text_fmt(compassHeading, "%5d\xC2\xB0", heading); + lv_img_set_angle(compassImg, -(heading * 10)); + } + if (obj == latitude) + lv_label_set_text_fmt(latitude, "%s", latFormatString(gps.gpsData.latitude)); + if (obj == longitude) + lv_label_set_text_fmt(longitude, "%s", lonFormatString(gps.gpsData.longitude)); + if (obj == altitude) + lv_label_set_text_fmt(obj, "%4d m.", gps.gpsData.altitude); + if (obj == speedLabel) + lv_label_set_text_fmt(obj, "%3d Km/h", gps.gpsData.speed); + if (obj == sunriseLabel) + { + lv_label_set_text_static(obj, gps.gpsData.sunriseHour); + lv_label_set_text_static(sunsetLabel, gps.gpsData.sunsetHour); + } } /** - * @brief Get the active tile + * @brief Show Map Widgets. * - * @param event + * @details Displays or hides map-related UI widgets based on map user settings */ -void getActTile(lv_event_t *event) +void showMapWidgets() { - if (isReady) - { - isScrolled = true; - mapView.redrawMap = true; - - if (activeTile == MAP) - { - mapView.createMapScrSprites(); - if (mapSet.mapFullScreen) - { - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(menuBtn, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(notifyBarHour, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(notifyBarIcons, LV_OBJ_FLAG_HIDDEN); - } - else - { - lv_obj_clear_flag(notifyBarHour, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(notifyBarIcons, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(menuBtn, LV_OBJ_FLAG_HIDDEN); - - if (isBarOpen) - lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - else - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - } - } - else if (activeTile != MAP) - { - lv_obj_clear_flag(menuBtn, LV_OBJ_FLAG_HIDDEN); - - if (isBarOpen) - lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - } - } - else - isReady = true; - - lv_obj_t *actTile = lv_tileview_get_tile_act(tilesScreen); - lv_coord_t tileX = lv_obj_get_x(actTile) / TFT_WIDTH; - activeTile = tileX; + lv_obj_clear_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(zoomWidget, LV_OBJ_FLAG_HIDDEN); + if (mapSet.showMapSpeed) + lv_obj_clear_flag(mapSpeed,LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(mapSpeed,LV_OBJ_FLAG_HIDDEN); + if (mapSet.showMapCompass) + lv_obj_clear_flag(miniCompass,LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(miniCompass,LV_OBJ_FLAG_HIDDEN); + if (mapSet.showMapScale) + lv_obj_clear_flag(scaleWidget,LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(scaleWidget,LV_OBJ_FLAG_HIDDEN); } /** - * @brief Tile start scrolling event + * @brief Hide Map Widgets. * - * @param event + * @details Hides all map-related UI widgets on the screen. */ -void scrollTile(lv_event_t *event) +void hideMapWidgets() { - isScrolled = false; - isReady = false; - mapView.redrawMap = false; - - if (mapSet.mapFullScreen) - { - lv_obj_clear_flag(notifyBarHour, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(notifyBarIcons, LV_OBJ_FLAG_HIDDEN); - } + lv_obj_add_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(zoomWidget, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(mapSpeed,LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(miniCompass,LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(scaleWidget,LV_OBJ_FLAG_HIDDEN); +} - mapView.deleteMapScrSprites(); +/** + * @brief Get the active tile + * + * @details Handles the tileview scroll event, updates the active tile index, and manages map/widget visibility and bar status. + * + * @param event LVGL event pointer. + */ +void getActTile(lv_event_t *event) +{ + if (isReady) + { + isScrolled = true; + mapView.redrawMap = true; + + if (activeTile == MAP) + { + mapView.createMapScrSprites(); + if (mapView.isMapFound) + showMapWidgets(); + else + hideMapWidgets(); + } + + if (isBarOpen) + lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); + } + else + isReady = true; + + lv_obj_t *actTile = lv_tileview_get_tile_act(tilesScreen); + lv_coord_t tileX = lv_obj_get_x(actTile) / TFT_WIDTH; + activeTile = tileX; } /** - * @brief Update Main Screen + * @brief Tile start scrolling event + * + * @details Handles the beginning of a tile scroll event by resetting scroll and map redraw flags and deleting map screen sprites. * + * @param event LVGL event pointer. */ -void updateMainScreen(lv_timer_t *t) +void scrollTile(lv_event_t *event) { - if (isScrolled && isMainScreen) - { - switch (activeTile) - { - case COMPASS: - #ifdef ENABLE_COMPASS - if (!waitScreenRefresh) - heading = compass.getHeading(); - if (compass.isUpdated()) - lv_obj_send_event(compassHeading, LV_EVENT_VALUE_CHANGED, NULL); - #endif - #ifndef ENABLE_COMPASS - heading = gps.gpsData.heading; - lv_obj_send_event(compassHeading, LV_EVENT_VALUE_CHANGED, NULL); - #endif - if (gps.hasLocationChange()) - { - lv_obj_send_event(latitude, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_send_event(longitude, LV_EVENT_VALUE_CHANGED, NULL); - } - if (gps.isAltitudeChanged()) - lv_obj_send_event(altitude, LV_EVENT_VALUE_CHANGED, NULL); - if (gps.isSpeedChanged()) - lv_obj_send_event(speedLabel, LV_EVENT_VALUE_CHANGED, NULL); - break; - - case MAP: - #ifdef ENABLE_COMPASS - heading = compass.getHeading(); - #endif - lv_obj_send_event(mapTile, LV_EVENT_VALUE_CHANGED, NULL); - break; - - case NAV: - lv_obj_send_event(navTile, LV_EVENT_VALUE_CHANGED, NULL); - break; - - case SATTRACK: - lv_obj_send_event(satTrackTile, LV_EVENT_VALUE_CHANGED, NULL); - break; - - default: - break; - } - } + isScrolled = false; + isReady = false; + mapView.redrawMap = false; + mapView.deleteMapScrSprites(); } /** - * @brief Map Gesture Event + * @brief Update Main Screen. * - * @param event + * @details Periodically updates the active main screen tiles and its widgets */ -void gestureEvent(lv_event_t *event) +void updateMainScreen(lv_timer_t *t) { - lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active()); - - if (showMapToolBar) - { - // if (activeTile == MAP && isMainScreen) - // { - // switch (dir) - // { - // case LV_DIR_LEFT: - // // mapView.panMap(1,0); - // mapView.scrollMap(30,0); - // break; - // case LV_DIR_RIGHT: - // // mapView.panMap(-1,0); - // mapView.scrollMap(-30,0); - // break; - // case LV_DIR_TOP: - // //mapView.panMap(0,1); - // mapView.scrollMap(0,30); - // break; - // case LV_DIR_BOTTOM: - // // mapView.panMap(0,-1); - // mapView.scrollMap(0,-30); - // break; - // } - // } - } + if (isScrolled && isMainScreen) + { + switch (activeTile) + { + case COMPASS: + #ifdef ENABLE_COMPASS + if (!waitScreenRefresh) + heading = compass.getHeading(); + if (compass.isUpdated()) + lv_obj_send_event(compassHeading, LV_EVENT_VALUE_CHANGED, NULL); + #endif + #ifndef ENABLE_COMPASS + heading = gps.gpsData.heading; + lv_obj_send_event(compassHeading, LV_EVENT_VALUE_CHANGED, NULL); + #endif + if (gps.hasLocationChange()) + { + lv_obj_send_event(latitude, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_send_event(longitude, LV_EVENT_VALUE_CHANGED, NULL); + } + if (gps.isAltitudeChanged()) + lv_obj_send_event(altitude, LV_EVENT_VALUE_CHANGED, NULL); + if (gps.isSpeedChanged()) + lv_obj_send_event(speedLabel, LV_EVENT_VALUE_CHANGED, NULL); + break; + + case MAP: + #ifdef ENABLE_COMPASS + if (mapSet.mapRotationComp) + heading = compass.getHeading(); + else + heading = gps.gpsData.heading; + #else + heading = gps.gpsData.heading; + #endif + lv_obj_send_event(mapTile, LV_EVENT_VALUE_CHANGED, NULL); + break; + + case NAV: + lv_obj_send_event(navTile, LV_EVENT_VALUE_CHANGED, NULL); + break; + + case SATTRACK: + lv_obj_send_event(satTrackTile, LV_EVENT_VALUE_CHANGED, NULL); + break; + + default: + break; + } + } } /** * @brief Update map event * - * @param event + * @details Handles map update events by generating and displaying the map (vector or render), updating map speed, scale, and compass widgets according to current settings. + * + * @param event LVGL event pointer. */ void updateMap(lv_event_t *event) { - if (mapSet.vectorMap) - mapView.generateVectorMap(zoom); - else - mapView.generateRenderMap(zoom); - - if (mapView.redrawMap) - mapView.displayMap(); + mapView.generateMap(zoom); + + if (mapView.redrawMap) + { + mapView.displayMap(); + lv_canvas_set_buffer(mapCanvas, mapView.mapBuffer, tft.width(), tft.height()-27, LV_COLOR_FORMAT_RGB565_SWAPPED); + } + + if (mapSet.showMapSpeed) + lv_label_set_text_fmt(mapSpeedLabel, "%3d", gps.gpsData.speed); + + if (mapSet.showMapScale) + lv_label_set_text_fmt(scaleLabel, "%s", map_scale[zoom]); + + if (mapSet.showMapCompass) + { + if (mapSet.compassRotation) + lv_img_set_angle(mapCompassImg, -(heading * 10)); + } } /** - * @brief Update Satellite Tracking + * @brief Update Satellite Tracking. * - * @param event + * @details Handles satellite tracking update events by refreshing DOP, altitude labels, and updating satellite SNR and sky plots. */ void updateSatTrack(lv_event_t *event) { - if (gps.isDOPChanged()) - { - lv_label_set_text_fmt(pdopLabel, "PDOP: %.1f", gps.gpsData.pdop); - lv_label_set_text_fmt(hdopLabel, "HDOP: %.1f", gps.gpsData.hdop); - lv_label_set_text_fmt(vdopLabel, "VDOP: %.1f", gps.gpsData.vdop); - } - - if (gps.isAltitudeChanged()) - lv_label_set_text_fmt(altLabel, "ALT: %4dm.", gps.gpsData.altitude); - - drawSatSNR(); - drawSatSky(); + if (gps.isDOPChanged()) + { + lv_label_set_text_fmt(pdopLabel, "PDOP: %.1f", gps.gpsData.pdop); + lv_label_set_text_fmt(hdopLabel, "HDOP: %.1f", gps.gpsData.hdop); + lv_label_set_text_fmt(vdopLabel, "VDOP: %.1f", gps.gpsData.vdop); + } + + if (gps.isAltitudeChanged()) + lv_label_set_text_fmt(altLabel, "ALT: %4dm.", gps.gpsData.altitude); + + drawSatSNR(); + drawSatSky(); } /** - * @brief Map Tool Bar Event + * @brief Map Tool Bar Event. * - * @param event + * @details Handles map toolbar visibility toggling, zoom button states, map scrollability, and map centering on GPS. + * + * @param event LVGL event pointer. */ void mapToolBarEvent(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); - - showMapToolBar = !showMapToolBar; - canScrollMap = !canScrollMap; - - if (!mapSet.mapFullScreen) - { - lv_obj_set_pos(btnFullScreen, 10, mapView.mapScrHeight - toolBarOffset); - lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrHeight - (toolBarOffset + toolBarSpace)); - lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrHeight - (toolBarOffset + (2 * toolBarSpace))); - } - else - { - lv_obj_set_pos(btnFullScreen, 10, mapView.mapScrFull - (toolBarOffset + 24)); - lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrFull - (toolBarOffset + toolBarSpace + 24)); - lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrFull - (toolBarOffset + (2 * toolBarSpace) + 24)); - } - - if (!showMapToolBar) - { - lv_obj_clear_flag(btnFullScreen, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(btnZoomOut, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(btnZoomIn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); - mapView.centerOnGps(gps.gpsData.latitude, gps.gpsData.longitude); - } - else - { - lv_obj_add_flag(btnFullScreen, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_flag(btnZoomOut, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_flag(btnZoomIn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); - } + lv_event_code_t code = lv_event_get_code(event); + + showMapToolBar = !showMapToolBar; + canScrollMap = !canScrollMap; + + if (!showMapToolBar) + { + lv_obj_clear_flag(btnZoomOut, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_clear_flag(btnZoomIn, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_add_flag(btnZoomOut, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(btnZoomIn, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); + mapView.centerOnGps(gps.gpsData.latitude, gps.gpsData.longitude); + lv_obj_clear_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + } + else + { + lv_obj_add_flag(btnZoomOut, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_add_flag(btnZoomIn, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_clear_flag(btnZoomOut, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(btnZoomIn, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); + if (!mapView.followGps) + lv_obj_add_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + else + lv_obj_clear_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + } } /** - * @brief Scrool Map Event + * @brief Scroll Map Event. + * + * @details Handles map scrolling gestures, updating the map view position * - * @param event + * @param event LVGL event pointer. */ void scrollMapEvent(lv_event_t *event) { - if (canScrollMap) - { - lv_event_code_t code = lv_event_get_code(event); - lv_indev_t * indev = lv_event_get_indev(event); - static int last_x = 0, last_y = 0; - static int dx = 0, dy = 0; - lv_point_t p; - - - switch (code) - { - case LV_EVENT_PRESSED: - { - lv_indev_get_point(indev, &p); - last_x = p.x; - last_y = p.y; - isScrollingMap = true; - break; - } - - case LV_EVENT_PRESSING: - { - lv_indev_get_point(indev, &p); - - int dx = p.x - last_x; - int dy = p.y - last_y; - - const int SCROLL_THRESHOLD = 5; - - if (abs(dx) > SCROLL_THRESHOLD || abs(dy) > SCROLL_THRESHOLD) - { - mapView.scrollMap(-dx, -dy); - last_x = p.x; - last_y = p.y; - } - break; - } - - case LV_EVENT_PRESS_LOST: - { - isScrollingMap = false; - break; - } - } - } + if (canScrollMap) + { + lv_event_code_t code = lv_event_get_code(event); + lv_indev_t * indev = lv_event_get_indev(event); + static int last_x = 0, last_y = 0; + static int dx = 0, dy = 0; + lv_point_t p; + + + switch (code) + { + case LV_EVENT_PRESSED: + { + lv_indev_get_point(indev, &p); + last_x = p.x; + last_y = p.y; + isScrollingMap = true; + break; + } + + case LV_EVENT_PRESSING: + { + lv_indev_get_point(indev, &p); + + int dx = p.x - last_x; + int dy = p.y - last_y; + + const int SCROLL_THRESHOLD = 5; + + if (abs(dx) > SCROLL_THRESHOLD || abs(dy) > SCROLL_THRESHOLD) + { + mapView.scrollMap(-dx, -dy); + lv_obj_add_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + last_x = p.x; + last_y = p.y; + } + break; + } + + case LV_EVENT_PRESS_LOST: + { + lv_obj_clear_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + isScrollingMap = false; + break; + } + } + } } - - /** - * @brief Full Screen Event Toolbar + * @brief Zoom Event Toolbar. * - * @param event - */ -void fullScreenEvent(lv_event_t *event) -{ - mapSet.mapFullScreen = !mapSet.mapFullScreen; - - if (!mapSet.mapFullScreen) - { - lv_obj_set_pos(btnFullScreen, 10, mapView.mapScrHeight - toolBarOffset); - lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrHeight - (toolBarOffset + toolBarSpace)); - lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrHeight - (toolBarOffset + (2 * toolBarSpace))); - - if (isBarOpen) - lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - else - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - - lv_obj_clear_flag(menuBtn, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(notifyBarHour, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(notifyBarIcons, LV_OBJ_FLAG_HIDDEN); - } - else - { - lv_obj_set_pos(btnFullScreen, 10, mapView.mapScrFull - (toolBarOffset + 24)); - lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrFull - (toolBarOffset + toolBarSpace + 24)); - lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrFull - (toolBarOffset + (2 * toolBarSpace) + 24)); - lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(menuBtn, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(notifyBarHour, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(notifyBarIcons, LV_OBJ_FLAG_HIDDEN); - } - - mapView.deleteMapScrSprites(); - mapView.createMapScrSprites(); - - mapView.redrawMap = true; - - lv_obj_invalidate(tilesScreen); - lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); -} - -/** - * @brief Zoom In Event Toolbar + * @details Handles zoom in/out toolbar events, updates zoom level, manages map position, and refreshes the map * - * @param event + * @param event LVGL event pointer. */ -void zoomInEvent(lv_event_t *event) +void zoomEvent(lv_event_t *event) { - if (!mapSet.vectorMap) - { - if (zoom >= minZoom && zoom < maxZoom) - zoom++; - } - else - { - zoom--; - mapView.isPosMoved = true; - if (zoom < 1) - { - zoom = 1; - mapView.isPosMoved = false; - } - } - - lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); + lv_obj_t *obj = (lv_obj_t *)lv_event_get_current_target(event); + if ( obj == btnZoomIn ) + { + if (zoom >= minZoom && zoom < maxZoom) + zoom++; + } + else if ( obj == btnZoomOut ) + { + if (zoom <= maxZoom && zoom > minZoom) + zoom--; + } + lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); + lv_label_set_text_fmt(zoomLabel, "%2d", zoom); } /** - * @brief Zoom Out Event Toolbar + * @brief Handles navigation waypoint screen updates * - * @param event + * @param event LVGL event pointer. */ -void zoomOutEvent(lv_event_t *event) +void updateNavEvent(lv_event_t *event) { - if (!mapSet.vectorMap) - { - if (zoom <= maxZoom && zoom > minZoom) - zoom--; - } - else - { - zoom++; - mapView.isPosMoved = true; - if (zoom > MAX_ZOOM) - { - zoom = MAX_ZOOM; - mapView.isPosMoved = false; - } - } - - lv_obj_send_event(mapTile, LV_EVENT_REFRESH, NULL); + int wptDistance = (int)calcDist(gps.gpsData.latitude, gps.gpsData.longitude, loadWpt.lat, loadWpt.lon); + lv_label_set_text_fmt(distNav, "%d m.", wptDistance); + + if (wptDistance == 0) + { + LV_IMG_DECLARE(navfinish); + lv_img_set_src(arrowNav, &navfinish); + lv_img_set_angle(arrowNav, 0); + } + else + { + #ifdef ENABLE_COMPASS + float wptCourse = calcCourse(gps.gpsData.latitude, gps.gpsData.longitude, loadWpt.lat, loadWpt.lon) - compass.getHeading(); + #endif + #ifndef ENABLE_COMPASS + float wptCourse = calcCourse(gps.gpsData.latitude, gps.gpsData.longitude, loadWpt.lat, loadWpt.lon) - gps.gpsData.heading; + #endif + lv_img_set_angle(arrowNav, (wptCourse * 10)); + } } /** - * @brief Navigation update event + * @brief Create Canvas for Map. + * + * @details Initializes and creates the canvas object for rendering the map on the specified screen. * - * @param event + * @param screen Pointer to the LVGL screen object. */ -void updateNavEvent(lv_event_t *event) +void createMapCanvas(_lv_obj_t *screen) { - int wptDistance = (int)calcDist(gps.gpsData.latitude, gps.gpsData.longitude, loadWpt.lat, loadWpt.lon); - lv_label_set_text_fmt(distNav, "%d m.", wptDistance); - - if (wptDistance == 0) - { - LV_IMG_DECLARE(navfinish); - lv_img_set_src(arrowNav, &navfinish); - lv_img_set_angle(arrowNav, 0); - } - else - { -#ifdef ENABLE_COMPASS - double wptCourse = calcCourse(gps.gpsData.latitude, gps.gpsData.longitude, loadWpt.lat, loadWpt.lon) - compass.getHeading(); -#endif -#ifndef ENABLE_COMPASS - double wptCourse = calcCourse(gps.gpsData.latitude, gps.gpsData.longitude, loadWpt.lat, loadWpt.lon) - gps.gpsData.heading; -#endif - lv_img_set_angle(arrowNav, (wptCourse * 10)); - } + // static lv_color_t *cbuf = (lv_color_t*)heap_caps_aligned_alloc(16, (tft.width()*tft.height()*sizeof(lv_color_t)), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);; + mapCanvas = lv_canvas_create(screen); + // lv_canvas_set_buffer(mapCanvas, cbuf, tft.width(), tft.height(), LV_COLOR_FORMAT_RGB565); + // lv_canvas_fill_bg(mapCanvas, lv_color_white(), LV_OPA_100); + // lv_canvas_init_layer(constCanvas, &canvasMapLayer); } /** - * @brief Create Main Screen + * @brief Create Main Screen. * + * @details Initializes and configures the main screen and its tiles, widgets, and event callbacks */ void createMainScr() { - mainScreen = lv_obj_create(NULL); - - // Main Screen Tiles - tilesScreen = lv_tileview_create(mainScreen); - compassTile = lv_tileview_add_tile(tilesScreen, 0, 0, LV_DIR_RIGHT); - mapTile = lv_tileview_add_tile(tilesScreen, 1, 0, (lv_dir_t)(LV_DIR_LEFT | LV_DIR_RIGHT)); - navTile = lv_tileview_add_tile(tilesScreen, 2, 0, (lv_dir_t)(LV_DIR_LEFT | LV_DIR_RIGHT)); - lv_obj_add_flag(navTile, LV_OBJ_FLAG_HIDDEN); - satTrackTile = lv_tileview_add_tile(tilesScreen, 3, 0, LV_DIR_LEFT); - lv_obj_set_size(tilesScreen, TFT_WIDTH, TFT_HEIGHT - 25); - lv_obj_set_pos(tilesScreen, 0, 25); - static lv_style_t styleScroll; - lv_style_init(&styleScroll); - lv_style_set_bg_color(&styleScroll, lv_color_hex(0xFFFFFF)); - lv_obj_add_style(tilesScreen, &styleScroll, LV_PART_SCROLLBAR); - // Main Screen Events - lv_obj_add_event_cb(tilesScreen, getActTile, LV_EVENT_SCROLL_END, NULL); - lv_obj_add_event_cb(tilesScreen, scrollTile, LV_EVENT_SCROLL_BEGIN, NULL); - - // Compass Tile - - // Compass Widget - compassWidget(compassTile); - // Position widget - positionWidget(compassTile); - // Altitude widget - altitudeWidget(compassTile); - // Speed widget - speedWidget(compassTile); - // Sunrise/Sunset widget - sunWidget(compassTile); - - // Compass Tile Events - lv_obj_add_event_cb(compassHeading, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(latitude, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(longitude, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(altitude, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(speedLabel, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(sunriseLabel, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(sunsetLabel, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); - - // Map Tile Toolbar - btnFullScreen = lv_btn_create(mapTile); - lv_obj_remove_style_all(btnFullScreen); - lv_obj_set_size(btnFullScreen, 48, 48); - lv_obj_add_event_cb(btnFullScreen, fullScreenEvent, LV_EVENT_CLICKED, NULL); - - btnZoomOut = lv_btn_create(mapTile); - lv_obj_remove_style_all(btnZoomOut); - lv_obj_set_size(btnZoomOut, 48, 48); - lv_obj_add_event_cb(btnZoomOut, zoomOutEvent, LV_EVENT_CLICKED, NULL); - - btnZoomIn = lv_btn_create(mapTile); - lv_obj_remove_style_all(btnZoomIn); - lv_obj_set_size(btnZoomIn, 48, 48); - lv_obj_add_event_cb(btnZoomIn, zoomInEvent, LV_EVENT_CLICKED, NULL); - - if (!mapSet.mapFullScreen) - { - lv_obj_set_pos(btnFullScreen, 10, mapView.mapScrHeight - toolBarOffset); - lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrHeight - (toolBarOffset + toolBarSpace)); - lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrHeight - (toolBarOffset + (2 * toolBarSpace))); - } - else - { - lv_obj_set_pos(btnFullScreen, 10, mapView.mapScrFull - (toolBarOffset + 24)); - lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrFull - (toolBarOffset + toolBarSpace + 24)); - lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrFull - (toolBarOffset + (2 * toolBarSpace) + 24)); - } - - if (!showMapToolBar) - { - lv_obj_clear_flag(btnFullScreen, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(btnZoomOut, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(btnZoomIn, LV_OBJ_FLAG_CLICKABLE); - } - else - { - lv_obj_add_flag(btnFullScreen, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_flag(btnZoomOut, LV_OBJ_FLAG_CLICKABLE); - lv_obj_add_flag(btnZoomIn, LV_OBJ_FLAG_CLICKABLE); - } - - // Map Tile Events - lv_obj_add_event_cb(mapTile, updateMap, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_add_event_cb(mainScreen, gestureEvent, LV_EVENT_GESTURE, NULL); - DOUBLE_TOUCH_EVENT = lv_event_register_id(); - lv_obj_add_event_cb(mapTile, mapToolBarEvent, (lv_event_code_t)DOUBLE_TOUCH_EVENT, NULL); - lv_obj_add_event_cb(mapTile, scrollMapEvent, LV_EVENT_ALL, NULL); - - // Navigation Tile - navigationScr(navTile); - - // Navigation Tile Events - lv_obj_add_event_cb(navTile, updateNavEvent, LV_EVENT_VALUE_CHANGED, NULL); - - // Satellite Tracking and info Tile - satelliteScr(satTrackTile); -#ifdef BOARD_HAS_PSRAM -#ifndef TDECK_ESP32S3 - createConstCanvas(satTrackTile); - drawSatConst(); - lv_obj_set_pos(constCanvas, (TFT_WIDTH / 2) - canvasCenter_X, 240); -#endif -#ifdef TDECK_ESP32S3 - createConstCanvas(constMsg); - lv_obj_align(constCanvas, LV_ALIGN_CENTER, 0, 0); - drawSatConst(); -#endif -#endif - - // Satellite Tracking Event - lv_obj_add_event_cb(satTrackTile, updateSatTrack, LV_EVENT_VALUE_CHANGED, NULL); + mainScreen = lv_obj_create(NULL); + + // Main Screen Tiles + tilesScreen = lv_tileview_create(mainScreen); + compassTile = lv_tileview_add_tile(tilesScreen, 0, 0, LV_DIR_RIGHT); + mapTile = lv_tileview_add_tile(tilesScreen, 1, 0, (lv_dir_t)(LV_DIR_LEFT | LV_DIR_RIGHT)); + navTile = lv_tileview_add_tile(tilesScreen, 2, 0, (lv_dir_t)(LV_DIR_LEFT | LV_DIR_RIGHT)); + lv_obj_add_flag(navTile, LV_OBJ_FLAG_HIDDEN); + satTrackTile = lv_tileview_add_tile(tilesScreen, 3, 0, LV_DIR_LEFT); + lv_obj_set_size(tilesScreen, TFT_WIDTH, TFT_HEIGHT - 25); + lv_obj_set_pos(tilesScreen, 0, 25); + static lv_style_t styleScroll; + lv_style_init(&styleScroll); + lv_style_set_bg_color(&styleScroll, lv_color_hex(0xFFFFFF)); + lv_obj_add_style(tilesScreen, &styleScroll, LV_PART_SCROLLBAR); + // Main Screen Events + lv_obj_add_event_cb(tilesScreen, getActTile, LV_EVENT_SCROLL_END, NULL); + lv_obj_add_event_cb(tilesScreen, scrollTile, LV_EVENT_SCROLL_BEGIN, NULL); + + // ********** Compass Tile ********** + // Compass Widget + compassWidget(compassTile); + // Position widget + positionWidget(compassTile); + // Altitude widget + altitudeWidget(compassTile); + // Speed widget + speedWidget(compassTile); + // Sunrise/Sunset widget + sunWidget(compassTile); + // Compass Tile Events + lv_obj_add_event_cb(compassHeading, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(latitude, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(longitude, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(altitude, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(speedLabel, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(sunriseLabel, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(sunsetLabel, updateCompassScr, LV_EVENT_VALUE_CHANGED, NULL); + + // ********** Map Tile ********** + // Map Canvas + createMapCanvas(mapTile); + // Navigation Arrow Widget + navArrowWidget(mapTile); + // Map zoom Widget + mapZoomWidget(mapTile); + // Map speed Widget + mapSpeedWidget(mapTile); + // Map compass Widget + mapCompassWidget(mapTile); + // Map scale Widget + mapScaleWidget(mapTile); + // Turn by Turn navigation widget + turnByTurnWidget(mapTile); + // Map Tile Toolbar + btnZoomOut = lv_img_create(mapTile); + lv_img_set_src(btnZoomOut, zoomOutIconFile); + lv_img_set_zoom(btnZoomOut,buttonScale); + lv_obj_update_layout(btnZoomOut); + lv_obj_set_size(btnZoomOut, 48 * scaleBut, 48 * scaleBut); + btnZoomIn = lv_img_create(mapTile); + lv_img_set_src(btnZoomIn, zoomInIconFile); + lv_img_set_zoom(btnZoomIn,buttonScale); + lv_obj_update_layout(btnZoomIn); + lv_obj_set_size(btnZoomIn, 48 * scaleBut, 48 * scaleBut); + lv_obj_set_pos(btnZoomOut, 10, mapView.mapScrHeight - toolBarOffset); + lv_obj_set_pos(btnZoomIn, 10, mapView.mapScrHeight - (toolBarOffset + toolBarSpace)); + if (!showMapToolBar) + { + lv_obj_clear_flag(btnZoomOut, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_clear_flag(btnZoomIn, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_add_flag(btnZoomOut, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(btnZoomIn, LV_OBJ_FLAG_HIDDEN); + } + else + { + lv_obj_add_flag(btnZoomOut, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_add_flag(btnZoomIn, (lv_obj_flag_t)(LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE)); + lv_obj_clear_flag(btnZoomOut, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(btnZoomIn, LV_OBJ_FLAG_HIDDEN); + } + // Map Tile Events + lv_obj_add_event_cb(mapTile, updateMap, LV_EVENT_VALUE_CHANGED, NULL); + DOUBLE_TOUCH_EVENT = lv_event_register_id(); + lv_obj_add_event_cb(mapTile, mapToolBarEvent, (lv_event_code_t)DOUBLE_TOUCH_EVENT, NULL); + lv_obj_add_event_cb(mapTile, scrollMapEvent, LV_EVENT_ALL, NULL); + lv_obj_add_event_cb(btnZoomOut, zoomEvent, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(btnZoomIn, zoomEvent, LV_EVENT_CLICKED, NULL); + + // ********** Navigation Tile ********** + navigationScr(navTile); + // Navigation Tile Events + lv_obj_add_event_cb(navTile, updateNavEvent, LV_EVENT_VALUE_CHANGED, NULL); + + // ********** Satellite Tracking and info Tile ********** + satelliteScr(satTrackTile); + #ifdef BOARD_HAS_PSRAM + #ifndef TDECK_ESP32S3 + createConstCanvas(satTrackTile); + drawSatConst(); + lv_obj_set_pos(constCanvas, (TFT_WIDTH / 2) - canvasCenter_X, 240); + #endif + #ifdef TDECK_ESP32S3 + createConstCanvas(constMsg); + lv_obj_align(constCanvas, LV_ALIGN_CENTER, 0, 0); + drawSatConst(); + #endif + #endif + // Satellite Tracking Event + lv_obj_add_event_cb(satTrackTile, updateSatTrack, LV_EVENT_VALUE_CHANGED, NULL); } diff --git a/lib/gui/src/mainScr.hpp b/lib/gui/src/mainScr.hpp index df23c724..d439750d 100644 --- a/lib/gui/src/mainScr.hpp +++ b/lib/gui/src/mainScr.hpp @@ -3,7 +3,7 @@ * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Main Screen * @version 0.1.9 - * @date 2025-05 + * @date 2025-06 */ #pragma once @@ -16,46 +16,54 @@ #include "navScr.hpp" #include "satInfoScr.hpp" #include "compass.hpp" +#include "navigation.hpp" -extern lv_timer_t *mainTimer; // Main Screen Timer -#define UPDATE_MAINSCR_PERIOD 30 // Main Screen update time +extern lv_timer_t *mainTimer; /**< Main Screen Timer */ +#define UPDATE_MAINSCR_PERIOD 30 /**< Main Screen update period (ms) */ -extern bool isScrolled; // Flag to indicate when tileview was scrolled -extern bool isMainScreen; // Flag to indicate main screen is selected -extern bool isReady; // Flag to indicate when tileview scroll was finished -extern bool canScrollMap; // Flag to indicate whet can scroll map -extern bool isScrollingMap; // Flag to indicate if map is scrolling -static TFT_eSprite zoomSprite = TFT_eSprite(&tft); // Zoom sprite +extern bool isScrolled; /**< Flag to indicate when tileview was scrolled */ +extern bool isMainScreen; /**< Flag to indicate main screen is selected */ +extern bool isReady; /**< Flag to indicate when tileview scroll was finished */ +extern bool canScrollMap; /**< Flag to indicate if map can be scrolled */ +extern bool isScrollingMap; /**< Flag to indicate if map is scrolling */ -extern uint8_t activeTile; // Active Tile in TileView control -extern int heading; // Heading value (Compass or GPS) +extern uint8_t activeTile; /**< Active Tile in TileView control */ +extern int heading; /**< Heading value (Compass or GPS) */ +static const char *zoomInIconFile PROGMEM = "/zoomin.bin"; /**< Zoom in icon file path */ +static const char *zoomOutIconFile PROGMEM = "/zoomout.bin"; /**< Zoom out icon file path */ + +/** + * @brief Enum for identifying different tile screens in the application. + */ enum tileName { - COMPASS, - MAP, - NAV, - SATTRACK, + COMPASS, /**< Compass screen */ + MAP, /**< Map screen */ + NAV, /**< Navigation screen */ + SATTRACK, /**< Satellite track screen */ }; /** * @brief Main Screen Tiles * + * @details LVGL tile objects for main application screens. */ -extern lv_obj_t *compassTile; -extern lv_obj_t *navTile; -extern lv_obj_t *mapTile; -extern lv_obj_t *satTrackTile; +extern lv_obj_t *compassTile; /**< Compass screen tile */ +extern lv_obj_t *navTile; /**< Navigation screen tile */ +extern lv_obj_t *mapTile; /**< Map screen tile */ +extern lv_obj_t *satTrackTile; /**< Satellite track screen tile */ /** * @brief Map Toolbar Buttons * + * @details Toolbar button objects and toolbar layout configuration. */ -extern lv_obj_t *btnFullScreen; -extern lv_obj_t *btnZoomIn; -extern lv_obj_t *btnZoomOut; -extern uint8_t toolBarOffset; -extern uint8_t toolBarSpace; +extern lv_obj_t *btnZoomIn; /**< Toolbar button for zooming in */ +extern lv_obj_t *btnZoomOut; /**< Toolbar button for zooming out */ +extern uint8_t toolBarOffset; /**< Offset for toolbar positioning */ +extern uint8_t toolBarSpace; /**< Space between toolbar buttons */ + void updateCompassScr(lv_event_t * event); @@ -63,15 +71,14 @@ void getActTile(lv_event_t *event); void scrollTile(lv_event_t *event); void updateMainScreen(lv_timer_t *t); -void gestureEvent(lv_event_t *event); void updateMap(lv_event_t *event); void updateSatTrack(lv_event_t *event); void mapToolBarEvent(lv_event_t *event); void scrollMapEvent(lv_event_t *event); -void fullScreenEvent(lv_event_t *event); -void zoomOutEvent(lv_event_t *event); -void zoomInEvent(lv_event_t *event); +void zoomEvent(lv_event_t *event); void updateNavEvent(lv_event_t *event); - +void createMapCanvas(_lv_obj_t *screen); +void showMapWidgets(); +void hideMapWidgets(); void createMainScr(); \ No newline at end of file diff --git a/lib/gui/src/mapSettingsScr.cpp b/lib/gui/src/mapSettingsScr.cpp index cafb6bd5..a3bd6698 100644 --- a/lib/gui/src/mapSettingsScr.cpp +++ b/lib/gui/src/mapSettingsScr.cpp @@ -2,255 +2,251 @@ * @file mapSettingsScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Map Settings screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "mapSettingsScr.hpp" -lv_obj_t *mapSettingsScreen; // Map Settings Screen +lv_obj_t *mapSettingsScreen; /**< Map Settings Screen. */ + +extern lv_obj_t *mapSpeed; +extern lv_obj_t *miniCompass; +extern lv_obj_t *mapCompassImg; +extern lv_obj_t *scaleWidget; +extern lv_obj_t *zoomLabel; extern Maps mapView; /** - * @brief Map Settings Events + * @brief Handles events for map settings UI elements, updating configuration, UI visibility, and persistent storage as needed. * - * @param event + * @param event LVGL event pointer. */ static void mapSettingsEvents(lv_event_t *event) { - lv_obj_t *obj = lv_event_get_target_obj(event); - lv_event_code_t code = lv_event_get_code(event); - - if (obj == btnBack) - { - if (needReboot) - { - cfg.saveBool(PKEYS::KMAP_VECTOR, mapSet.vectorMap); - cfg.saveUInt(PKEYS::KDEF_ZOOM, defaultZoom); - lv_obj_delete(mapSettingsScreen); - showRestartScr(); - } - else - lv_screen_load(settingsScreen); - } - - if (obj == zoomBtnUp) - { - if (code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) - { - lv_spinbox_increment(zoomLevel); - defaultZoom = (uint8_t)lv_spinbox_get_value(zoomLevel); - zoom = defaultZoom; - mapView.isPosMoved = true; - cfg.saveUInt(PKEYS::KDEF_ZOOM, defaultZoom); - } - } - - if (obj == zoomBtnDown) - { - if (code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) - { - lv_spinbox_decrement(zoomLevel); - defaultZoom = (uint8_t)lv_spinbox_get_value(zoomLevel); - zoom = defaultZoom; - mapView.isPosMoved = true; - cfg.saveUInt(PKEYS::KDEF_ZOOM, defaultZoom); - } - } - - if (obj == mapType) - { - mapSet.vectorMap = lv_obj_has_state(obj, LV_STATE_CHECKED); - if (!mapSet.vectorMap) - { - minZoom = 6; - maxZoom = 17; - } - else - { - minZoom = 1; - maxZoom = 4; - } - lv_spinbox_set_range(zoomLevel, minZoom, maxZoom); - defaultZoom = (uint8_t)lv_spinbox_get_value(zoomLevel); - zoom = defaultZoom; - needReboot = true; - } - - if (obj == mapSwitch) - { - mapSet.mapRotationComp = lv_obj_has_state(obj, LV_STATE_CHECKED); - cfg.saveBool(PKEYS::KMAP_ROT_MODE, mapSet.mapRotationComp); - } - - if (obj == checkCompass) - { - mapSet.showMapCompass = lv_obj_has_state(obj, LV_STATE_CHECKED); - cfg.saveBool(PKEYS::KMAP_COMPASS, mapSet.showMapCompass); - } - - if (obj == checkCompassRot) - { - mapSet.compassRotation = lv_obj_has_state(obj, LV_STATE_CHECKED); - cfg.saveBool(PKEYS::KMAP_COMP_ROT, mapSet.compassRotation); - } - - if (obj == checkSpeed) - { - mapSet.showMapSpeed = lv_obj_has_state(obj, LV_STATE_CHECKED); - cfg.saveBool(PKEYS::KMAP_SPEED, mapSet.showMapSpeed); - } - - if (obj == checkScale) - { - mapSet.showMapScale = lv_obj_has_state(obj, LV_STATE_CHECKED); - cfg.saveBool(PKEYS::KMAP_SCALE, mapSet.showMapScale); - } - - if (obj == checkFullScreen) - { - mapSet.mapFullScreen = lv_obj_has_state(obj, LV_STATE_CHECKED); - cfg.saveBool(PKEYS::KMAP_MODE, mapSet.mapFullScreen); - needReboot = true; - } + lv_obj_t *obj = lv_event_get_target_obj(event); + lv_event_code_t code = lv_event_get_code(event); + + if (obj == btnBack) + { + if (needReboot) + { + cfg.saveBool(PKEYS::KMAP_VECTOR, mapSet.vectorMap); + cfg.saveUInt(PKEYS::KDEF_ZOOM, defaultZoom); + lv_obj_delete(mapSettingsScreen); + showRestartScr(); + } + else + lv_screen_load(settingsScreen); + } + + if (obj == zoomBtnUp) + { + if (code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) + { + lv_spinbox_increment(zoomLevel); + defaultZoom = (uint8_t)lv_spinbox_get_value(zoomLevel); + zoom = defaultZoom; + lv_label_set_text_fmt(zoomLabel, "%2d", zoom); + cfg.saveUInt(PKEYS::KDEF_ZOOM, defaultZoom); + } + } + + if (obj == zoomBtnDown) + { + if (code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) + { + lv_spinbox_decrement(zoomLevel); + defaultZoom = (uint8_t)lv_spinbox_get_value(zoomLevel); + zoom = defaultZoom; + lv_label_set_text_fmt(zoomLabel, "%2d", zoom); + cfg.saveUInt(PKEYS::KDEF_ZOOM, defaultZoom); + } + } + + if (obj == mapType) + { + mapSet.vectorMap = lv_obj_has_state(obj, LV_STATE_CHECKED); + minZoom = 6; + maxZoom = 17; + lv_spinbox_set_range(zoomLevel, minZoom, maxZoom); + defaultZoom = (uint8_t)lv_spinbox_get_value(zoomLevel); + zoom = defaultZoom; + lv_label_set_text_fmt(zoomLabel, "%2d", zoom); + needReboot = true; + } + + if (obj == mapSwitch) + { + mapSet.mapRotationComp = lv_obj_has_state(obj, LV_STATE_CHECKED); + cfg.saveBool(PKEYS::KMAP_ROT_MODE, mapSet.mapRotationComp); + } + + if (obj == checkCompass) + { + mapSet.showMapCompass = lv_obj_has_state(obj, LV_STATE_CHECKED); + cfg.saveBool(PKEYS::KMAP_COMPASS, mapSet.showMapCompass); + if (mapSet.showMapCompass) + lv_obj_clear_flag(miniCompass,LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(miniCompass,LV_OBJ_FLAG_HIDDEN); + } + + if (obj == checkCompassRot) + { + mapSet.compassRotation = lv_obj_has_state(obj, LV_STATE_CHECKED); + cfg.saveBool(PKEYS::KMAP_COMP_ROT, mapSet.compassRotation); + if (!mapSet.compassRotation); + lv_img_set_angle(mapCompassImg, 0); + } + + if (obj == checkSpeed) + { + mapSet.showMapSpeed = lv_obj_has_state(obj, LV_STATE_CHECKED); + cfg.saveBool(PKEYS::KMAP_SPEED, mapSet.showMapSpeed); + if (mapSet.showMapSpeed) + lv_obj_clear_flag(mapSpeed,LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(mapSpeed,LV_OBJ_FLAG_HIDDEN); + } + + if (obj == checkScale) + { + mapSet.showMapScale = lv_obj_has_state(obj, LV_STATE_CHECKED); + cfg.saveBool(PKEYS::KMAP_SCALE, mapSet.showMapScale); + if (mapSet.showMapScale) + lv_obj_clear_flag(scaleWidget,LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(scaleWidget,LV_OBJ_FLAG_HIDDEN); + } } /** - * @brief Create Map Settings screen + * @brief Create Map Settings screen. * + * @details Initializes map settings screen, including controls for map type, rotation, zoom, and display options. */ void createMapSettingsScr() { - // Map Settings Screen - mapSettingsScreen = lv_obj_create(NULL); - mapSettingsOptions = lv_list_create(mapSettingsScreen); - lv_obj_set_size(mapSettingsOptions, TFT_WIDTH, TFT_HEIGHT - 60); - - lv_obj_t *label; - - // Map Type - list = lv_list_add_btn(mapSettingsOptions, NULL, "Map Type\nRENDER/VECTOR"); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - lv_obj_set_style_text_font(list, fontOptions, 0); - mapType = lv_switch_create(list); - label = lv_label_create(mapType); - lv_label_set_text_static(label, "V R"); - lv_obj_center(label); - if (mapSet.vectorMap) - lv_obj_add_state(mapType, LV_STATE_CHECKED); - else - lv_obj_clear_state(mapType, LV_STATE_CHECKED); - lv_obj_align_to(mapType, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_obj_add_event_cb(mapType, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Map Rotation - list = lv_list_add_btn(mapSettingsOptions, NULL, "Map Rotation Mode\nHEADING/COMPASS"); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - lv_obj_set_style_text_font(list, fontOptions, 0); - mapSwitch = lv_switch_create(list); - label = lv_label_create(mapSwitch); - lv_label_set_text_static(label, "C H"); - lv_obj_center(label); - if (mapSet.mapRotationComp) - lv_obj_add_state(mapSwitch, LV_STATE_CHECKED); - else - lv_obj_clear_state(mapSwitch, LV_STATE_CHECKED); - lv_obj_align_to(mapSwitch, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_obj_add_event_cb(mapSwitch, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Default zoom level - list = lv_list_add_btn(mapSettingsOptions, NULL, "Default\nZoom Level"); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - lv_obj_set_style_text_font(list, fontOptions, 0); - - zoomBtnUp = lv_btn_create(list); - lv_obj_set_size(zoomBtnUp, 40 * scale, 40 * scale); - lv_obj_align_to(zoomBtnUp, list, LV_ALIGN_LEFT_MID, 0, 0); - lv_obj_set_style_bg_image_src(zoomBtnUp, LV_SYMBOL_PLUS, 0); - lv_obj_add_event_cb(zoomBtnUp, mapSettingsEvents, LV_EVENT_ALL, NULL); - - zoomLevel = lv_spinbox_create(list); - lv_spinbox_set_range(zoomLevel, minZoom, maxZoom); - lv_obj_set_width(zoomLevel, 40 * scale); - lv_obj_clear_flag(zoomLevel, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_text_font(zoomLevel, fontLarge, 0); - lv_spinbox_set_value(zoomLevel, defaultZoom); - lv_spinbox_set_digit_format(zoomLevel, 2, 0); - lv_obj_align_to(zoomLevel, list, LV_ALIGN_RIGHT_MID, 0, 0); - objHideCursor(zoomLevel); - - zoomBtnDown = lv_btn_create(list); - lv_obj_set_size(zoomBtnDown, 40 * scale, 40 * scale); - lv_obj_align_to(zoomBtnDown, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_obj_set_style_bg_image_src(zoomBtnDown, LV_SYMBOL_MINUS, 0); - lv_obj_add_event_cb(zoomBtnDown, mapSettingsEvents, LV_EVENT_ALL, NULL); - - // Show Full Screen Map - list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Full Screen Map"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - checkFullScreen = lv_checkbox_create(list); - lv_obj_align_to(checkFullScreen, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_checkbox_set_text(checkFullScreen, " "); - lv_obj_add_state(checkFullScreen, mapSet.mapFullScreen); - lv_obj_add_event_cb(checkFullScreen, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Show Compass - list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Compass"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - checkCompass = lv_checkbox_create(list); - lv_obj_align_to(checkCompass, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_checkbox_set_text(checkCompass, " "); - lv_obj_add_state(checkCompass, mapSet.showMapCompass); - lv_obj_add_event_cb(checkCompass, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Compass Rotation - list = lv_list_add_btn(mapSettingsOptions, NULL, "Compass Rotation"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - checkCompassRot = lv_checkbox_create(list); - lv_obj_align_to(checkCompassRot, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_checkbox_set_text(checkCompassRot, " "); - lv_obj_add_state(checkCompassRot, mapSet.compassRotation); - lv_obj_add_event_cb(checkCompassRot, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Show Speed - list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Speed"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - checkSpeed = lv_checkbox_create(list); - lv_obj_align_to(checkSpeed, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_checkbox_set_text(checkSpeed, " "); - lv_obj_add_state(checkSpeed, mapSet.showMapSpeed); - lv_obj_add_event_cb(checkSpeed, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Show Map Scale - list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Map Scale"); - lv_obj_set_style_text_font(list, fontOptions, 0); - lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_align(list, LV_ALIGN_LEFT_MID); - checkScale = lv_checkbox_create(list); - lv_obj_align_to(checkScale, list, LV_ALIGN_RIGHT_MID, 0, 0); - lv_checkbox_set_text(checkScale, " "); - lv_obj_add_state(checkScale, mapSet.showMapScale); - lv_obj_add_event_cb(checkScale, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); - - // Back button - btnBack = lv_btn_create(mapSettingsScreen); - lv_obj_set_size(btnBack, TFT_WIDTH - 30, 40 * scale); - label = lv_label_create(btnBack); - lv_obj_set_style_text_font(label, fontLarge, 0); - lv_label_set_text_static(label, "Back"); - lv_obj_center(label); - lv_obj_align(btnBack, LV_ALIGN_BOTTOM_MID, 0, -10); - lv_obj_add_event_cb(btnBack, mapSettingsEvents, LV_EVENT_CLICKED, NULL); + // Map Settings Screen + mapSettingsScreen = lv_obj_create(NULL); + mapSettingsOptions = lv_list_create(mapSettingsScreen); + lv_obj_set_size(mapSettingsOptions, TFT_WIDTH, TFT_HEIGHT - 60); + + lv_obj_t *label; + + // Map Type + list = lv_list_add_btn(mapSettingsOptions, NULL, "Map Type\nRENDER/VECTOR"); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + lv_obj_set_style_text_font(list, fontOptions, 0); + mapType = lv_switch_create(list); + label = lv_label_create(mapType); + lv_label_set_text_static(label, "V R"); + lv_obj_center(label); + if (mapSet.vectorMap) + lv_obj_add_state(mapType, LV_STATE_CHECKED); + else + lv_obj_clear_state(mapType, LV_STATE_CHECKED); + lv_obj_align_to(mapType, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_obj_add_event_cb(mapType, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); + + // Map Rotation + list = lv_list_add_btn(mapSettingsOptions, NULL, "Map Rotation Mode\nHEADING/COMPASS"); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + lv_obj_set_style_text_font(list, fontOptions, 0); + mapSwitch = lv_switch_create(list); + label = lv_label_create(mapSwitch); + lv_label_set_text_static(label, "C H"); + lv_obj_center(label); + if (mapSet.mapRotationComp) + lv_obj_add_state(mapSwitch, LV_STATE_CHECKED); + else + lv_obj_clear_state(mapSwitch, LV_STATE_CHECKED); + lv_obj_align_to(mapSwitch, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_obj_add_event_cb(mapSwitch, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); + + // Default zoom level + list = lv_list_add_btn(mapSettingsOptions, NULL, "Default\nZoom Level"); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + lv_obj_set_style_text_font(list, fontOptions, 0); + + zoomBtnUp = lv_btn_create(list); + lv_obj_set_size(zoomBtnUp, 40 * scale, 40 * scale); + lv_obj_align_to(zoomBtnUp, list, LV_ALIGN_LEFT_MID, 0, 0); + lv_obj_set_style_bg_image_src(zoomBtnUp, LV_SYMBOL_PLUS, 0); + lv_obj_add_event_cb(zoomBtnUp, mapSettingsEvents, LV_EVENT_ALL, NULL); + + zoomLevel = lv_spinbox_create(list); + lv_spinbox_set_range(zoomLevel, minZoom, maxZoom); + lv_obj_set_width(zoomLevel, 40 * scale); + lv_obj_clear_flag(zoomLevel, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_text_font(zoomLevel, fontLarge, 0); + lv_spinbox_set_value(zoomLevel, defaultZoom); + lv_spinbox_set_digit_format(zoomLevel, 2, 0); + lv_obj_align_to(zoomLevel, list, LV_ALIGN_RIGHT_MID, 0, 0); + objHideCursor(zoomLevel); + + zoomBtnDown = lv_btn_create(list); + lv_obj_set_size(zoomBtnDown, 40 * scale, 40 * scale); + lv_obj_align_to(zoomBtnDown, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_obj_set_style_bg_image_src(zoomBtnDown, LV_SYMBOL_MINUS, 0); + lv_obj_add_event_cb(zoomBtnDown, mapSettingsEvents, LV_EVENT_ALL, NULL); + + // Show Compass + list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Compass"); + lv_obj_set_style_text_font(list, fontOptions, 0); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + checkCompass = lv_checkbox_create(list); + lv_obj_align_to(checkCompass, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_checkbox_set_text(checkCompass, " "); + lv_obj_add_state(checkCompass, mapSet.showMapCompass); + lv_obj_add_event_cb(checkCompass, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); + + // Compass Rotation + list = lv_list_add_btn(mapSettingsOptions, NULL, "Compass Rotation"); + lv_obj_set_style_text_font(list, fontOptions, 0); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + checkCompassRot = lv_checkbox_create(list); + lv_obj_align_to(checkCompassRot, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_checkbox_set_text(checkCompassRot, " "); + lv_obj_add_state(checkCompassRot, mapSet.compassRotation); + lv_obj_add_event_cb(checkCompassRot, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); + + // Show Speed + list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Speed"); + lv_obj_set_style_text_font(list, fontOptions, 0); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + checkSpeed = lv_checkbox_create(list); + lv_obj_align_to(checkSpeed, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_checkbox_set_text(checkSpeed, " "); + lv_obj_add_state(checkSpeed, mapSet.showMapSpeed); + lv_obj_add_event_cb(checkSpeed, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); + + // Show Map Scale + list = lv_list_add_btn(mapSettingsOptions, NULL, "Show Map Scale"); + lv_obj_set_style_text_font(list, fontOptions, 0); + lv_obj_clear_flag(list, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_align(list, LV_ALIGN_LEFT_MID); + checkScale = lv_checkbox_create(list); + lv_obj_align_to(checkScale, list, LV_ALIGN_RIGHT_MID, 0, 0); + lv_checkbox_set_text(checkScale, " "); + lv_obj_add_state(checkScale, mapSet.showMapScale); + lv_obj_add_event_cb(checkScale, mapSettingsEvents, LV_EVENT_VALUE_CHANGED, NULL); + + // Back button + btnBack = lv_btn_create(mapSettingsScreen); + lv_obj_set_size(btnBack, TFT_WIDTH - 30, 40 * scale); + label = lv_label_create(btnBack); + lv_obj_set_style_text_font(label, fontLarge, 0); + lv_label_set_text_static(label, "Back"); + lv_obj_center(label); + lv_obj_align(btnBack, LV_ALIGN_BOTTOM_MID, 0, -10); + lv_obj_add_event_cb(btnBack, mapSettingsEvents, LV_EVENT_CLICKED, NULL); } diff --git a/lib/gui/src/mapSettingsScr.hpp b/lib/gui/src/mapSettingsScr.hpp index 24a1da17..863403c7 100644 --- a/lib/gui/src/mapSettingsScr.hpp +++ b/lib/gui/src/mapSettingsScr.hpp @@ -2,8 +2,8 @@ * @file mapSettingsScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Map Settings screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -13,19 +13,20 @@ #include "settings.hpp" #include "maps.hpp" -static lv_obj_t *mapSettingsOptions; -static lv_obj_t *mapSwitch; -static lv_obj_t *mapType; -static lv_obj_t *zoomLevel; -static lv_obj_t *btnBack; -static lv_obj_t *zoomBtnUp; -static lv_obj_t *zoomBtnDown; -static lv_obj_t *list; -static lv_obj_t *checkCompass; -static lv_obj_t *checkCompassRot; -static lv_obj_t *checkSpeed; -static lv_obj_t *checkScale; -static lv_obj_t *checkFullScreen; +static lv_obj_t *mapSettingsOptions; /**< Map settings options container object. */ +static lv_obj_t *mapSwitch; /**< Map enable/disable switch object. */ +static lv_obj_t *mapType; /**< Map type selection object. */ +static lv_obj_t *zoomLevel; /**< Zoom level selection object. */ +static lv_obj_t *btnBack; /**< Back button object in map settings. */ +static lv_obj_t *zoomBtnUp; /**< Zoom level increment button object. */ +static lv_obj_t *zoomBtnDown; /**< Zoom level decrement button object. */ +static lv_obj_t *list; /**< List object for settings options. */ +static lv_obj_t *checkCompass; /**< Checkbox for enabling compass display. */ +static lv_obj_t *checkCompassRot; /**< Checkbox for enabling compass rotation. */ +static lv_obj_t *checkSpeed; /**< Checkbox for displaying speed on the map. */ +static lv_obj_t *checkScale; /**< Checkbox for displaying map scale. */ +static lv_obj_t *checkFullScreen; /**< Checkbox for enabling fullscreen map display. */ + static void mapSettingsEvents(lv_event_t *event); void createMapSettingsScr(); diff --git a/lib/gui/src/navScr.cpp b/lib/gui/src/navScr.cpp index f6523cca..9dcfefa5 100644 --- a/lib/gui/src/navScr.cpp +++ b/lib/gui/src/navScr.cpp @@ -2,8 +2,8 @@ * @file navScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Navigation screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "navScr.hpp" @@ -15,120 +15,120 @@ lv_obj_t *distNav; lv_obj_t *arrowNav; /** - * @brief Navigation screen + * @brief Creates and initializes the navigation screen, adding labels and widgets for navigation information display. * - * @param screen + * @param screen LVGL screen object to which the navigation UI elements are added. */ #ifndef TDECK_ESP32S3 void navigationScr(_lv_obj_t *screen) { - lv_obj_t * label; - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Navigation to:"); - lv_obj_center(label); - lv_obj_align(label,LV_ALIGN_TOP_LEFT,10, 20); - - nameNav = lv_label_create(screen); - lv_obj_set_style_text_font(nameNav, fontLargeMedium, 0); - //lv_label_set_text_fmt(nameNav, "%s",""); - lv_label_set_long_mode(nameNav, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_obj_set_width(nameNav,TFT_WIDTH-10); - lv_obj_set_pos(nameNav,10, 55); - - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Lat:"); - lv_obj_set_pos(label, 10, 90); - - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Lon:"); - lv_obj_set_pos(label, 10, 120); - - latNav = lv_label_create(screen); - lv_obj_set_style_text_font(latNav, fontOptions, 0); - lv_label_set_text_fmt(latNav, "%s", ""); - lv_obj_set_pos(latNav, 60, 90); - - lonNav = lv_label_create(screen); - lv_obj_set_style_text_font(lonNav, fontOptions, 0); - lv_label_set_text_fmt(lonNav, "%s", ""); - lv_obj_set_pos(lonNav, 60, 120); - - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Distance"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, -50); - - distNav = lv_label_create(screen); - lv_obj_set_style_text_font(distNav, fontVeryLarge, 0); - lv_label_set_text_fmt(distNav,"%d m.", 0); - lv_obj_align(distNav,LV_ALIGN_CENTER, 0, -5); - - arrowNav = lv_img_create(screen); - lv_img_set_zoom(arrowNav,iconScale); - lv_obj_update_layout(arrowNav); - lv_obj_align(arrowNav,LV_ALIGN_CENTER, 0, 100); - - LV_IMG_DECLARE(navup); - lv_img_set_src(arrowNav, &navup); - lv_img_set_pivot(arrowNav, 50, 50) ; + lv_obj_t * label; + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Navigation to:"); + lv_obj_center(label); + lv_obj_align(label,LV_ALIGN_TOP_LEFT,10, 20); + + nameNav = lv_label_create(screen); + lv_obj_set_style_text_font(nameNav, fontLargeMedium, 0); + //lv_label_set_text_fmt(nameNav, "%s",""); + lv_label_set_long_mode(nameNav, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(nameNav,TFT_WIDTH-10); + lv_obj_set_pos(nameNav,10, 55); + + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Lat:"); + lv_obj_set_pos(label, 10, 90); + + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Lon:"); + lv_obj_set_pos(label, 10, 120); + + latNav = lv_label_create(screen); + lv_obj_set_style_text_font(latNav, fontOptions, 0); + lv_label_set_text_fmt(latNav, "%s", ""); + lv_obj_set_pos(latNav, 60, 90); + + lonNav = lv_label_create(screen); + lv_obj_set_style_text_font(lonNav, fontOptions, 0); + lv_label_set_text_fmt(lonNav, "%s", ""); + lv_obj_set_pos(lonNav, 60, 120); + + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Distance"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, -50); + + distNav = lv_label_create(screen); + lv_obj_set_style_text_font(distNav, fontVeryLarge, 0); + lv_label_set_text_fmt(distNav,"%d m.", 0); + lv_obj_align(distNav,LV_ALIGN_CENTER, 0, -5); + + arrowNav = lv_img_create(screen); + lv_img_set_zoom(arrowNav,iconScale); + lv_obj_update_layout(arrowNav); + lv_obj_align(arrowNav,LV_ALIGN_CENTER, 0, 100); + + LV_IMG_DECLARE(navup); + lv_img_set_src(arrowNav, &navup); + lv_img_set_pivot(arrowNav, 50, 50) ; } #endif #ifdef TDECK_ESP32S3 void navigationScr(_lv_obj_t *screen) { - lv_obj_t * label; - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Navigation to:"); - lv_obj_center(label); - lv_obj_align(label,LV_ALIGN_TOP_LEFT,10, 20); - - nameNav = lv_label_create(screen); - lv_obj_set_style_text_font(nameNav, fontLargeMedium, 0); - //lv_label_set_text_fmt(nameNav, "%s",""); - lv_label_set_long_mode(nameNav, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_obj_set_width(nameNav,TFT_WIDTH-10); - lv_obj_set_pos(nameNav,10, 37); - - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Lat:"); - lv_obj_set_pos(label, 10, 70); - - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Lon:"); - lv_obj_set_pos(label, 10, 90); - - latNav = lv_label_create(screen); - lv_obj_set_style_text_font(latNav, fontOptions, 0); - lv_label_set_text_fmt(latNav, "%s", ""); - lv_obj_set_pos(latNav, 60, 70); - - lonNav = lv_label_create(screen); - lv_obj_set_style_text_font(lonNav, fontOptions, 0); - lv_label_set_text_fmt(lonNav, "%s", ""); - lv_obj_set_pos(lonNav, 60, 90); - - label = lv_label_create(screen); - lv_obj_set_style_text_font(label, fontOptions, 0); - lv_label_set_text_static(label, "Distance"); - lv_obj_set_pos(label,10, 120); - - distNav = lv_label_create(screen); - lv_obj_set_style_text_font(distNav, fontVeryLarge, 0); - lv_label_set_text_fmt(distNav,"%d m.", 0); - lv_obj_set_pos(distNav,10, 140); - - arrowNav = lv_img_create(screen); - lv_obj_set_pos(arrowNav,TFT_WIDTH - 100, 35); - - LV_IMG_DECLARE(navup); - lv_img_set_src(arrowNav, &navup); - lv_img_set_pivot(arrowNav, 50, 50) ; + lv_obj_t * label; + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Navigation to:"); + lv_obj_center(label); + lv_obj_align(label,LV_ALIGN_TOP_LEFT,10, 20); + + nameNav = lv_label_create(screen); + lv_obj_set_style_text_font(nameNav, fontLargeMedium, 0); + //lv_label_set_text_fmt(nameNav, "%s",""); + lv_label_set_long_mode(nameNav, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(nameNav,TFT_WIDTH-10); + lv_obj_set_pos(nameNav,10, 37); + + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Lat:"); + lv_obj_set_pos(label, 10, 70); + + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Lon:"); + lv_obj_set_pos(label, 10, 90); + + latNav = lv_label_create(screen); + lv_obj_set_style_text_font(latNav, fontOptions, 0); + lv_label_set_text_fmt(latNav, "%s", ""); + lv_obj_set_pos(latNav, 60, 70); + + lonNav = lv_label_create(screen); + lv_obj_set_style_text_font(lonNav, fontOptions, 0); + lv_label_set_text_fmt(lonNav, "%s", ""); + lv_obj_set_pos(lonNav, 60, 90); + + label = lv_label_create(screen); + lv_obj_set_style_text_font(label, fontOptions, 0); + lv_label_set_text_static(label, "Distance"); + lv_obj_set_pos(label,10, 120); + + distNav = lv_label_create(screen); + lv_obj_set_style_text_font(distNav, fontVeryLarge, 0); + lv_label_set_text_fmt(distNav,"%d m.", 0); + lv_obj_set_pos(distNav,10, 140); + + arrowNav = lv_img_create(screen); + lv_obj_set_pos(arrowNav,TFT_WIDTH - 100, 35); + + LV_IMG_DECLARE(navup); + lv_img_set_src(arrowNav, &navup); + lv_img_set_pivot(arrowNav, 50, 50) ; } #endif \ No newline at end of file diff --git a/lib/gui/src/navScr.hpp b/lib/gui/src/navScr.hpp index d310dd6e..b24204a5 100644 --- a/lib/gui/src/navScr.hpp +++ b/lib/gui/src/navScr.hpp @@ -2,8 +2,8 @@ * @file navScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Navigation screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -12,13 +12,14 @@ #include "navup.h" /** - * @brief Navigation Tile screen objects + * @brief Navigation Tile screen objects. * + * @details References to objects used in the navigation tile for displaying waypoint name, latitude, longitude, distance, and navigation arrow. */ -extern lv_obj_t *nameNav; -extern lv_obj_t *latNav; -extern lv_obj_t *lonNav; -extern lv_obj_t *distNav; -extern lv_obj_t *arrowNav; +extern lv_obj_t *nameNav; /**< Navigation waypoint name label. */ +extern lv_obj_t *latNav; /**< Navigation latitude label. */ +extern lv_obj_t *lonNav; /**< Navigation longitude label. */ +extern lv_obj_t *distNav; /**< Navigation distance label. */ +extern lv_obj_t *arrowNav; /**< Navigation arrow image object. */ void navigationScr(_lv_obj_t *screen); \ No newline at end of file diff --git a/lib/gui/src/notifyBar.cpp b/lib/gui/src/notifyBar.cpp index d6dd1f96..95965d78 100644 --- a/lib/gui/src/notifyBar.cpp +++ b/lib/gui/src/notifyBar.cpp @@ -2,202 +2,203 @@ * @file notifyBar.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Notify Bar Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "notifyBar.hpp" -lv_obj_t *mainScreen; -lv_obj_t *notifyBarIcons; -lv_obj_t *notifyBarHour; +lv_obj_t *mainScreen; /**< Main screen */ +lv_obj_t *notifyBarIcons; /**< Notification bar icons container object. */ +lv_obj_t *notifyBarHour; /**< Notification bar hour display object. */ Storage storage; Battery battery; extern Gps gps; /** - * @brief Update notify bar event + * @brief Handles notify bar update events, refreshing display elements * - * @param event + * @param event LVGL event pointer. */ void updateNotifyBar(lv_event_t *event) { - lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(event); - - if (obj == gpsTime) - { - struct tm local_tm; - time_t localTime = time(NULL); - struct tm *now = localtime_r(&localTime,&local_tm); - - lv_label_set_text_fmt(obj, timeFormat, now->tm_hour, now->tm_min, now->tm_sec); - } - -#ifdef ENABLE_TEMP - if (obj == temp) - lv_label_set_text_fmt(obj, "%02d\xC2\xB0", tempValue); -#endif - - if (obj == gpsCount) - lv_label_set_text_fmt(obj, LV_SYMBOL_GPS "%2d", gps.gpsData.satellites); - - if (obj == battIcon) - { - if (battLevel <= 160 && battLevel > 140) - lv_label_set_text_static(obj, " " LV_SYMBOL_CHARGE); - else if (battLevel <= 140 && battLevel > 80) - lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_FULL); - else if (battLevel <= 80 && battLevel > 60) - lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_3); - else if (battLevel <= 60 && battLevel > 40) - lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_2); - else if (battLevel <= 40 && battLevel > 20) - lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_1); - else if (battLevel <= 20) - lv_label_set_text(obj, LV_SYMBOL_BATTERY_EMPTY); - } - - if (obj == gpsFixMode) - { - switch (gps.gpsData.fixMode) - { - case gps_fix::STATUS_NONE: - lv_label_set_text_static(obj, "----"); - break; - case gps_fix::STATUS_STD: - lv_label_set_text_static(obj, " 3D "); - break; - case gps_fix::STATUS_DGPS: - lv_label_set_text_static(obj, "DGPS"); - break; - case gps_fix::STATUS_PPS: - lv_label_set_text_static(obj, "PPS"); - break; - case gps_fix::STATUS_RTK_FLOAT: - lv_label_set_text_static(obj, "RTK"); - break; - case gps_fix::STATUS_RTK_FIXED: - lv_label_set_text_static(obj, "RTK"); - break; - case gps_fix::STATUS_TIME_ONLY: - lv_label_set_text_static(obj, "TIME"); - break; - case gps_fix::STATUS_EST: - lv_label_set_text_static(obj, "EST"); - break; - } - } - - if (obj == wifi) - { - if (WiFi.status() == WL_CONNECTED) - lv_label_set_text_static(obj, LV_SYMBOL_WIFI); - else - lv_label_set_text_static(obj," "); - } + lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(event); + + if (obj == gpsTime) + { + struct tm local_tm; + time_t localTime = time(NULL); + struct tm *now = localtime_r(&localTime,&local_tm); + + lv_label_set_text_fmt(obj, timeFormat, now->tm_hour, now->tm_min, now->tm_sec); + } + + #ifdef ENABLE_TEMP + if (obj == temp) + lv_label_set_text_fmt(obj, "%02d\xC2\xB0", tempValue); + #endif + + if (obj == gpsCount) + lv_label_set_text_fmt(obj, LV_SYMBOL_GPS "%2d", gps.gpsData.satellites); + + if (obj == battIcon) + { + if (battLevel <= 160 && battLevel > 140) + lv_label_set_text_static(obj, " " LV_SYMBOL_CHARGE); + else if (battLevel <= 140 && battLevel > 80) + lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_FULL); + else if (battLevel <= 80 && battLevel > 60) + lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_3); + else if (battLevel <= 60 && battLevel > 40) + lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_2); + else if (battLevel <= 40 && battLevel > 20) + lv_label_set_text_static(obj, LV_SYMBOL_BATTERY_1); + else if (battLevel <= 20) + lv_label_set_text(obj, LV_SYMBOL_BATTERY_EMPTY); + } + + if (obj == gpsFixMode) + { + switch (gps.gpsData.fixMode) + { + case gps_fix::STATUS_NONE: + lv_label_set_text_static(obj, "----"); + break; + case gps_fix::STATUS_STD: + lv_label_set_text_static(obj, " 3D "); + break; + case gps_fix::STATUS_DGPS: + lv_label_set_text_static(obj, "DGPS"); + break; + case gps_fix::STATUS_PPS: + lv_label_set_text_static(obj, "PPS"); + break; + case gps_fix::STATUS_RTK_FLOAT: + lv_label_set_text_static(obj, "RTK"); + break; + case gps_fix::STATUS_RTK_FIXED: + lv_label_set_text_static(obj, "RTK"); + break; + case gps_fix::STATUS_TIME_ONLY: + lv_label_set_text_static(obj, "TIME"); + break; + case gps_fix::STATUS_EST: + lv_label_set_text_static(obj, "EST"); + break; + } + } + + if (obj == wifi) + { + if (WiFi.status() == WL_CONNECTED) + lv_label_set_text_static(obj, LV_SYMBOL_WIFI); + else + lv_label_set_text_static(obj," "); + } } /** - * @brief Update notify bar info timer + * @brief Periodic timer callback to update notify bar information * */ void updateNotifyBarTimer(lv_timer_t *t) { - lv_obj_send_event(gpsTime, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_send_event(gpsCount, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_send_event(gpsFixMode, LV_EVENT_VALUE_CHANGED, NULL); - lv_obj_send_event(wifi, LV_EVENT_VALUE_CHANGED, NULL); - - if (isGpsFixed) - lv_led_toggle(gpsFix); - else - lv_led_off(gpsFix); - - #ifdef ENABLE_TEMP - tempValue = (uint8_t)(bme.readTemperature() + tempOffset); - if (tempValue != tempOld) - { - lv_obj_send_event(temp, LV_EVENT_VALUE_CHANGED, NULL); - tempOld = tempValue; - } - #endif - - battLevel = battery.readBattery(); - if (battLevel != battLevelOld) - { - lv_obj_send_event(battIcon, LV_EVENT_VALUE_CHANGED, NULL); - battLevelOld = battLevel; - } + lv_obj_send_event(gpsTime, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_send_event(gpsCount, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_send_event(gpsFixMode, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_send_event(wifi, LV_EVENT_VALUE_CHANGED, NULL); + + if (isGpsFixed) + lv_led_toggle(gpsFix); + else + lv_led_off(gpsFix); + + #ifdef ENABLE_TEMP + tempValue = (uint8_t)(bme.readTemperature() + tempOffset); + if (tempValue != tempOld) + { + lv_obj_send_event(temp, LV_EVENT_VALUE_CHANGED, NULL); + tempOld = tempValue; + } + #endif + + battLevel = battery.readBattery(); + if (battLevel != battLevelOld) + { + lv_obj_send_event(battIcon, LV_EVENT_VALUE_CHANGED, NULL); + battLevelOld = battLevel; + } } /** - * @brief Create a notify bar + * @brief Create a notify bar. * + * @details Initializes and lays out the notification bar on the main screen, adding UI elements */ void createNotifyBar() { - notifyBarIcons = lv_obj_create(mainScreen); - lv_obj_set_size(notifyBarIcons, (TFT_WIDTH / 3) * 2 , 24); - lv_obj_set_pos(notifyBarIcons, (TFT_WIDTH / 3) + 1, 0); - lv_obj_set_flex_flow(notifyBarIcons, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(notifyBarIcons, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(notifyBarIcons, LV_OBJ_FLAG_SCROLLABLE); - - notifyBarHour = lv_obj_create(mainScreen); - lv_obj_set_size(notifyBarHour, TFT_WIDTH / 3 , 24); - lv_obj_set_pos(notifyBarHour, 0, 0); - lv_obj_set_flex_flow(notifyBarHour, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(notifyBarHour, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(notifyBarHour, LV_OBJ_FLAG_SCROLLABLE); - - static lv_style_t styleBar; - lv_style_init(&styleBar); - lv_style_set_bg_opa(&styleBar, LV_OPA_0); - lv_style_set_border_opa(&styleBar, LV_OPA_0); - lv_style_set_text_font(&styleBar, fontDefault); - lv_obj_add_style(notifyBarIcons, &styleBar, LV_PART_MAIN); - lv_obj_add_style(notifyBarHour, &styleBar, LV_PART_MAIN); - - gpsTime = lv_label_create(notifyBarHour); - lv_obj_set_style_text_font(gpsTime, fontLarge, 0); - lv_label_set_text_fmt(gpsTime, timeFormat, 0, 0, 0); - lv_obj_add_event_cb(gpsTime, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); - - wifi = lv_label_create(notifyBarIcons); - lv_label_set_text_static(wifi, " "); - lv_obj_add_event_cb(wifi, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); - - #ifdef ENABLE_TEMP - temp = lv_label_create(notifyBarIcons); - lv_label_set_text_static(temp, "--\xC2\xB0"); - lv_obj_add_event_cb(temp, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); - #endif - - if (storage.getSdLoaded()) - { - sdCard = lv_label_create(notifyBarIcons); - lv_label_set_text_static(sdCard, LV_SYMBOL_SD_CARD); - } - - gpsCount = lv_label_create(notifyBarIcons); - lv_label_set_text_fmt(gpsCount, LV_SYMBOL_GPS "%2d", 0); - lv_obj_add_event_cb(gpsCount, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); - - gpsFix = lv_led_create(notifyBarIcons); - lv_led_set_color(gpsFix, lv_palette_main(LV_PALETTE_RED)); - lv_obj_set_size(gpsFix, 7, 7); - lv_led_off(gpsFix); - - gpsFixMode = lv_label_create(notifyBarIcons); - lv_obj_set_style_text_font(gpsFixMode, fontSmall, 0); - lv_label_set_text_static(gpsFixMode, "----"); - lv_obj_add_event_cb(gpsFixMode, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); - - battIcon = lv_label_create(notifyBarIcons); - lv_label_set_text_static(battIcon, LV_SYMBOL_BATTERY_EMPTY); - lv_obj_add_event_cb(battIcon, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); - - lv_timer_t *timerNotifyBar = lv_timer_create(updateNotifyBarTimer, UPDATE_NOTIFY_PERIOD, NULL); - lv_timer_ready(timerNotifyBar); + notifyBarIcons = lv_obj_create(mainScreen); + lv_obj_set_size(notifyBarIcons, (TFT_WIDTH / 3) * 2 , 24); + lv_obj_set_pos(notifyBarIcons, (TFT_WIDTH / 3) + 1, 0); + lv_obj_set_flex_flow(notifyBarIcons, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(notifyBarIcons, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(notifyBarIcons, LV_OBJ_FLAG_SCROLLABLE); + + notifyBarHour = lv_obj_create(mainScreen); + lv_obj_set_size(notifyBarHour, TFT_WIDTH / 3 , 24); + lv_obj_set_pos(notifyBarHour, 0, 0); + lv_obj_set_flex_flow(notifyBarHour, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(notifyBarHour, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(notifyBarHour, LV_OBJ_FLAG_SCROLLABLE); + + static lv_style_t styleBar; + lv_style_init(&styleBar); + lv_style_set_bg_opa(&styleBar, LV_OPA_0); + lv_style_set_border_opa(&styleBar, LV_OPA_0); + lv_style_set_text_font(&styleBar, fontDefault); + lv_obj_add_style(notifyBarIcons, &styleBar, LV_PART_MAIN); + lv_obj_add_style(notifyBarHour, &styleBar, LV_PART_MAIN); + + gpsTime = lv_label_create(notifyBarHour); + lv_obj_set_style_text_font(gpsTime, fontLarge, 0); + lv_label_set_text_fmt(gpsTime, timeFormat, 0, 0, 0); + lv_obj_add_event_cb(gpsTime, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); + + wifi = lv_label_create(notifyBarIcons); + lv_label_set_text_static(wifi, " "); + lv_obj_add_event_cb(wifi, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); + + #ifdef ENABLE_TEMP + temp = lv_label_create(notifyBarIcons); + lv_label_set_text_static(temp, "--\xC2\xB0"); + lv_obj_add_event_cb(temp, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); + #endif + + if (storage.getSdLoaded()) + { + sdCard = lv_label_create(notifyBarIcons); + lv_label_set_text_static(sdCard, LV_SYMBOL_SD_CARD); + } + + gpsCount = lv_label_create(notifyBarIcons); + lv_label_set_text_fmt(gpsCount, LV_SYMBOL_GPS "%2d", 0); + lv_obj_add_event_cb(gpsCount, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); + + gpsFix = lv_led_create(notifyBarIcons); + lv_led_set_color(gpsFix, lv_palette_main(LV_PALETTE_RED)); + lv_obj_set_size(gpsFix, 7, 7); + lv_led_off(gpsFix); + + gpsFixMode = lv_label_create(notifyBarIcons); + lv_obj_set_style_text_font(gpsFixMode, fontSmall, 0); + lv_label_set_text_static(gpsFixMode, "----"); + lv_obj_add_event_cb(gpsFixMode, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); + + battIcon = lv_label_create(notifyBarIcons); + lv_label_set_text_static(battIcon, LV_SYMBOL_BATTERY_EMPTY); + lv_obj_add_event_cb(battIcon, updateNotifyBar, LV_EVENT_VALUE_CHANGED, NULL); + + lv_timer_t *timerNotifyBar = lv_timer_create(updateNotifyBarTimer, UPDATE_NOTIFY_PERIOD, NULL); + lv_timer_ready(timerNotifyBar); } diff --git a/lib/gui/src/notifyBar.hpp b/lib/gui/src/notifyBar.hpp index 4223e437..40ce4c27 100644 --- a/lib/gui/src/notifyBar.hpp +++ b/lib/gui/src/notifyBar.hpp @@ -2,8 +2,8 @@ * @file notifyBar.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Notify Bar Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -15,26 +15,28 @@ #include "settings.hpp" /** - * @brief Notify Bar screen objects + * @brief Notify Bar screen objects. * + * @details Objects used to display information in the notification bar */ -static lv_obj_t *gpsTime; // Time -static lv_obj_t *gpsCount; // Satellite count -static lv_obj_t *gpsFix; // Satellite fix -static lv_obj_t *gpsFixMode; // Satellite fix mode -static lv_obj_t *battIcon; // Battery level -static lv_obj_t *sdCard; // SD card icon -static lv_obj_t *temp; // Temperature -static lv_obj_t *wifi; // Wifi +static lv_obj_t *gpsTime; /**< Time display object. */ +static lv_obj_t *gpsCount; /**< Satellite count object. */ +static lv_obj_t *gpsFix; /**< Satellite fix status object. */ +static lv_obj_t *gpsFixMode; /**< Satellite fix mode object. */ +static lv_obj_t *battIcon; /**< Battery level icon object. */ +static lv_obj_t *sdCard; /**< SD card icon object. */ +static lv_obj_t *temp; /**< Temperature display object. */ +static lv_obj_t *wifi; /**< WiFi status object. */ -static float battLevel = 0; -static float battLevelOld = 0; +static float battLevel = 0; /**< Current battery level value. */ +static float battLevelOld = 0; /**< Previous battery level value. */ -#define UPDATE_NOTIFY_PERIOD 1000 // Notify Bar update time +#define UPDATE_NOTIFY_PERIOD 1000 /**< Notify Bar update time in milliseconds. */ /** - * @brief Temperature values + * @brief Temperature values. * + * @details Format string for displaying time in HH:MM:SS format */ static const char* timeFormat PROGMEM = "%02d:%02d:%02d"; diff --git a/lib/gui/src/satInfoScr.cpp b/lib/gui/src/satInfoScr.cpp index eeb06917..e885b2cb 100644 --- a/lib/gui/src/satInfoScr.cpp +++ b/lib/gui/src/satInfoScr.cpp @@ -2,8 +2,8 @@ * @file satInfoScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Satellite info screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "satInfoScr.hpp" @@ -24,471 +24,486 @@ Gps gps; /** * @brief Draw Text on SNR Chart * - * @param text -> Text - * @param layer - * @param p - * @param coords - * @param color - * @param font - * @param offset + * @details Draws a text label onto a specified layer of the SNR chart using the provided font, color, and position. + * + * @param text Pointer to the text string to display. + * @param layer Pointer to the drawing layer. + * @param p Pointer to the position (lv_point_t) where the text should be drawn. + * @param coords Pointer to the base area coordinates. + * @param color Text color (lv_color_t). + * @param font Pointer to the font to use. + * @param offset Vertical offset from the base position. */ void drawTextOnLayer(const char * text, lv_layer_t * layer, lv_point_t * p, lv_area_t * coords, lv_color_t color, const void * font, int16_t offset) { - lv_draw_rect_dsc_t draw_rect_dsc; - lv_draw_rect_dsc_init(&draw_rect_dsc); - - draw_rect_dsc.bg_opa = LV_OPA_TRANSP; - draw_rect_dsc.radius = 0; - draw_rect_dsc.bg_image_symbol_font = font; - draw_rect_dsc.bg_image_src = text; - draw_rect_dsc.bg_image_recolor = color; - - lv_area_t a; - a.x1 = coords->x1 + p->x - 10; - a.x2 = coords->x1 + p->x + 10; - a.y1 = coords->y1 + p->y + 10 - offset; - a.y2 = a.y1 - 20; - - lv_draw_rect(layer, &draw_rect_dsc, &a); + lv_draw_rect_dsc_t draw_rect_dsc; + lv_draw_rect_dsc_init(&draw_rect_dsc); + + draw_rect_dsc.bg_opa = LV_OPA_TRANSP; + draw_rect_dsc.radius = 0; + draw_rect_dsc.bg_image_symbol_font = font; + draw_rect_dsc.bg_image_src = text; + draw_rect_dsc.bg_image_recolor = color; + + lv_area_t a; + a.x1 = coords->x1 + p->x - 10; + a.x2 = coords->x1 + p->x + 10; + a.y1 = coords->y1 + p->y + 10 - offset; + a.y2 = a.y1 - 20; + + lv_draw_rect(layer, &draw_rect_dsc, &a); } /** - * @brief SNR Bar draw event + * @brief SNR Bar draw event. + * + * @details Handles the drawing of the Signal-to-Noise Ratio (SNR) bar chart for satellites. Colors each bar depending on the GNSS constellation + * and whether the satellite is active. After drawing, overlays signal values and satellite IDs on the chart. * - * @param event + * @param event LVGL event pointer. */ void satelliteBarDrawEvent(lv_event_t * event) { - lv_event_code_t e = lv_event_get_code(event); - lv_obj_t * obj = (lv_obj_t *)lv_event_get_target(event); - if (e == LV_EVENT_DRAW_TASK_ADDED) - { - lv_draw_task_t * drawTask = lv_event_get_draw_task(event); - lv_draw_dsc_base_t * base_dsc = (lv_draw_dsc_base_t *)lv_draw_task_get_draw_dsc(drawTask); - if(base_dsc->part == LV_PART_ITEMS) - { - uint16_t dscId = base_dsc->id2; - //Change color/border of bar depending on GNSS and if SV is in use - if (lv_draw_task_get_type(drawTask) == LV_DRAW_TASK_TYPE_FILL) - { - lv_draw_fill_dsc_t * fill_dsc = lv_draw_task_get_fill_dsc(drawTask); - if(fill_dsc) - { - if ( strcmp(gps.satTracker[dscId].talker_id,"GP") == 0 ) - fill_dsc->color = gps.satTracker[dscId].active == true ? GP_INACTIVE_COLOR : GP_ACTIVE_COLOR; - if ( strcmp(gps.satTracker[dscId].talker_id,"GL") == 0 ) - fill_dsc->color = gps.satTracker[dscId].active == true ? GL_INACTIVE_COLOR : GL_ACTIVE_COLOR; - if ( strcmp(gps.satTracker[dscId].talker_id,"BD") == 0 ) - fill_dsc->color = gps.satTracker[dscId].active == true ? BD_INACTIVE_COLOR : BD_ACTIVE_COLOR; - } - } - } - } - - if (e == LV_EVENT_DRAW_POST_END) - { - lv_layer_t * layer = lv_event_get_layer(event); - char buf[16]; - - for (uint16_t i = 0; i < gps.gpsData.satInView; i++) - { - lv_area_t chartObjCoords; - lv_obj_get_coords(obj, &chartObjCoords); - lv_point_t p; - lv_chart_get_point_pos_by_id(obj, lv_chart_get_series_next(obj, NULL), i, &p); - - //Draw signal at top of bar - if (gps.satTracker[i].snr > 0) - { - lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"%d", gps.satTracker[i].snr); - drawTextOnLayer(buf, layer, &p, &chartObjCoords, lv_color_white(), fontSmall, 15); - } - - //Draw Satellite ID - lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"%d", gps.satTracker[i].satNum); - drawTextOnLayer(buf, layer, &p, &chartObjCoords, lv_color_white(), fontSmall, (chartObjCoords.y1 + p.y) - chartObjCoords.y2 + 10); - } - } + lv_event_code_t e = lv_event_get_code(event); + lv_obj_t * obj = (lv_obj_t *)lv_event_get_target(event); + if (e == LV_EVENT_DRAW_TASK_ADDED) + { + lv_draw_task_t * drawTask = lv_event_get_draw_task(event); + lv_draw_dsc_base_t * base_dsc = (lv_draw_dsc_base_t *)lv_draw_task_get_draw_dsc(drawTask); + if(base_dsc->part == LV_PART_ITEMS) + { + uint16_t dscId = base_dsc->id2; + //Change color/border of bar depending on GNSS and if SV is in use + if (lv_draw_task_get_type(drawTask) == LV_DRAW_TASK_TYPE_FILL) + { + lv_draw_fill_dsc_t * fill_dsc = lv_draw_task_get_fill_dsc(drawTask); + if(fill_dsc) + { + if ( strcmp(gps.satTracker[dscId].talker_id,"GP") == 0 ) + fill_dsc->color = gps.satTracker[dscId].active == true ? GP_INACTIVE_COLOR : GP_ACTIVE_COLOR; + if ( strcmp(gps.satTracker[dscId].talker_id,"GL") == 0 ) + fill_dsc->color = gps.satTracker[dscId].active == true ? GL_INACTIVE_COLOR : GL_ACTIVE_COLOR; + if ( strcmp(gps.satTracker[dscId].talker_id,"BD") == 0 ) + fill_dsc->color = gps.satTracker[dscId].active == true ? BD_INACTIVE_COLOR : BD_ACTIVE_COLOR; + } + } + } + } + + if (e == LV_EVENT_DRAW_POST_END) + { + lv_layer_t * layer = lv_event_get_layer(event); + char buf[16]; + + for (uint16_t i = 0; i < gps.gpsData.satInView; i++) + { + lv_area_t chartObjCoords; + lv_obj_get_coords(obj, &chartObjCoords); + lv_point_t p; + lv_chart_get_point_pos_by_id(obj, lv_chart_get_series_next(obj, NULL), i, &p); + + //Draw signal at top of bar + if (gps.satTracker[i].snr > 0) + { + lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"%d", gps.satTracker[i].snr); + drawTextOnLayer(buf, layer, &p, &chartObjCoords, lv_color_white(), fontSmall, 15); + } + + //Draw Satellite ID + lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"%d", gps.satTracker[i].satNum); + drawTextOnLayer(buf, layer, &p, &chartObjCoords, lv_color_white(), fontSmall, (chartObjCoords.y1 + p.y) - chartObjCoords.y2 + 10); + } + } } /** - * @brief SNR Long press event for show constellation Map (only for T-DECK) + * @brief SNR long press event for showing constellation map (only for T-DECK). + * + * @details Handles the long press event on the SNR bar to display the constellation map message, specifically for the T-DECK device. * - * @param event + * @param event LVGL event pointer. */ void constSatEvent(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); + lv_event_code_t code = lv_event_get_code(event); - if (code == LV_EVENT_LONG_PRESSED) - lv_obj_clear_flag(constMsg,LV_OBJ_FLAG_HIDDEN); + if (code == LV_EVENT_LONG_PRESSED) + lv_obj_clear_flag(constMsg,LV_OBJ_FLAG_HIDDEN); } /** - * @brief Event for hide constellation Map (only for T-DECK) + * @brief Event for hiding the constellation map (only for T-DECK). * - * @param event + * @details Handles the long press event to hide the constellation map message on the T-DECK device. + * + * @param event LVGL event pointer. */ void closeConstSatEvent(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); + lv_event_code_t code = lv_event_get_code(event); - if (code == LV_EVENT_LONG_PRESSED) - lv_obj_add_flag(constMsg,LV_OBJ_FLAG_HIDDEN); + if (code == LV_EVENT_LONG_PRESSED) + lv_obj_add_flag(constMsg,LV_OBJ_FLAG_HIDDEN); } - /** +/** * @brief Create Canvas for Satellite Constellation * - * @param screen + * @details Initializes and creates the canvas object for rendering the satellite constellation on the specified screen, + * allocates the required buffer, and sets up the drawing layer. + * + * @param screen Pointer to the LVGL screen object where the canvas will be created. */ void createConstCanvas(_lv_obj_t *screen) { - static lv_color_t *cbuf = (lv_color_t*)heap_caps_aligned_alloc(16, (canvasSize*canvasSize*sizeof(lv_color_t)), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);; - constCanvas = lv_canvas_create(screen); - lv_canvas_set_buffer(constCanvas, cbuf, canvasSize, canvasSize, LV_COLOR_FORMAT_RGB565); - lv_canvas_fill_bg(constCanvas, lv_color_black(), LV_OPA_100); + static lv_color_t *cbuf = (lv_color_t*)heap_caps_aligned_alloc(16, (canvasSize*canvasSize*sizeof(lv_color_t)), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);; + constCanvas = lv_canvas_create(screen); + lv_canvas_set_buffer(constCanvas, cbuf, canvasSize, canvasSize, LV_COLOR_FORMAT_RGB565); + lv_canvas_fill_bg(constCanvas, lv_color_black(), LV_OPA_100); - lv_canvas_init_layer(constCanvas, &canvasLayer); + lv_canvas_init_layer(constCanvas, &canvasLayer); } - /** +/** * @brief Satellite info screen * - * @param screen + * @details Creates and lays out the satellite information screen, + * + * @param screen Pointer to the LVGL screen object where the satellite info screen will be created. */ #ifndef TDECK_ESP32S3 void satelliteScr(_lv_obj_t *screen) { - lv_obj_t *infoGrid = lv_obj_create(screen); - lv_obj_set_width(infoGrid,TFT_WIDTH); - lv_obj_set_height(infoGrid,35); - lv_obj_set_flex_align(infoGrid, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(infoGrid, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_flex_flow(infoGrid, LV_FLEX_FLOW_ROW_WRAP); - lv_obj_set_pos(infoGrid,0,190); - - static lv_style_t styleGrid; - lv_style_init(&styleGrid); - lv_style_set_bg_opa(&styleGrid, LV_OPA_0); - lv_style_set_border_opa(&styleGrid, LV_OPA_0); - lv_obj_add_style(infoGrid, &styleGrid, LV_PART_MAIN); - - pdopLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(pdopLabel, fontDefault, 0); - lv_label_set_text_fmt(pdopLabel, "PDOP: %.1f", 0); - - hdopLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(hdopLabel, fontDefault, 0); - lv_label_set_text_fmt(hdopLabel, "HDOP: %.1f", 0); - - vdopLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(vdopLabel, fontDefault, 0); - lv_label_set_text_fmt(vdopLabel, "VDOP: %.1f", 0); - - altLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(altLabel, fontDefault, 0); - lv_label_set_text_fmt(altLabel, "ALT: %4dm.", 0); - - lv_obj_t * barCont = lv_obj_create(screen); - lv_obj_set_size(barCont, TFT_WIDTH, 180); - lv_obj_set_pos(barCont, 0, 5); - lv_obj_t * wrapper = lv_obj_create(barCont); - lv_obj_remove_style_all(wrapper); - lv_obj_set_size(wrapper, TFT_WIDTH * 2, 120); - - lv_obj_t * gnssLabel = lv_label_create(barCont); - lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); - lv_obj_set_pos(gnssLabel, 0, 127); - lv_obj_set_width(gnssLabel,90); - lv_obj_set_style_bg_color(gnssLabel, GP_ACTIVE_COLOR, 0); - lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); - lv_obj_set_style_border_color(gnssLabel, GP_INACTIVE_COLOR, 0); - lv_obj_set_style_border_width(gnssLabel, 1, 0); - lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); - lv_label_set_text(gnssLabel, "GPS"); - lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); - - gnssLabel = lv_label_create(barCont); - lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); - lv_obj_set_pos(gnssLabel, 95, 127); - lv_obj_set_width(gnssLabel,90); - lv_obj_set_style_bg_color(gnssLabel, GL_ACTIVE_COLOR, 0); - lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); - lv_obj_set_style_border_color(gnssLabel, GL_INACTIVE_COLOR, 0); - lv_obj_set_style_border_width(gnssLabel, 1, 0); - lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); - lv_label_set_text(gnssLabel, "GLONASS"); - lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); - - gnssLabel = lv_label_create(barCont); - lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); - lv_obj_set_pos(gnssLabel, 190, 127); - lv_obj_set_width(gnssLabel,90); - lv_obj_set_style_bg_color(gnssLabel, BD_ACTIVE_COLOR, 0); - lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); - lv_obj_set_style_border_color(gnssLabel, BD_INACTIVE_COLOR, 0); - lv_obj_set_style_border_width(gnssLabel, 1, 0); - lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); - lv_label_set_text(gnssLabel, "BEIDOU"); - lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); - - satelliteBar = lv_chart_create(wrapper); - lv_obj_set_size(satelliteBar, TFT_WIDTH * 2, 120); - lv_chart_set_div_line_count(satelliteBar, 10, 0); - lv_chart_set_range(satelliteBar, LV_CHART_AXIS_PRIMARY_Y, 0, 60); - satelliteBarSerie = lv_chart_add_series(satelliteBar, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y); - lv_chart_set_type(satelliteBar, LV_CHART_TYPE_BAR); - lv_obj_set_style_pad_all(satelliteBar, 0, LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_set_style_pad_gap(satelliteBar, -7, LV_PART_ITEMS | LV_STATE_DEFAULT); - lv_obj_set_style_pad_column(satelliteBar, 2, 0); - lv_chart_set_point_count(satelliteBar, MAX_SATELLLITES_IN_VIEW ); - lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_TASK_ADDED, NULL); - lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_POST_END, NULL); - lv_obj_add_flag(satelliteBar, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); + lv_obj_t *infoGrid = lv_obj_create(screen); + lv_obj_set_width(infoGrid,TFT_WIDTH); + lv_obj_set_height(infoGrid,35); + lv_obj_set_flex_align(infoGrid, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(infoGrid, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(infoGrid, LV_FLEX_FLOW_ROW_WRAP); + lv_obj_set_pos(infoGrid,0,190); + + static lv_style_t styleGrid; + lv_style_init(&styleGrid); + lv_style_set_bg_opa(&styleGrid, LV_OPA_0); + lv_style_set_border_opa(&styleGrid, LV_OPA_0); + lv_obj_add_style(infoGrid, &styleGrid, LV_PART_MAIN); + + pdopLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(pdopLabel, fontDefault, 0); + lv_label_set_text_fmt(pdopLabel, "PDOP: %.1f", 0); + + hdopLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(hdopLabel, fontDefault, 0); + lv_label_set_text_fmt(hdopLabel, "HDOP: %.1f", 0); + + vdopLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(vdopLabel, fontDefault, 0); + lv_label_set_text_fmt(vdopLabel, "VDOP: %.1f", 0); + + altLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(altLabel, fontDefault, 0); + lv_label_set_text_fmt(altLabel, "ALT: %4dm.", 0); + + lv_obj_t * barCont = lv_obj_create(screen); + lv_obj_set_size(barCont, TFT_WIDTH, 180); + lv_obj_set_pos(barCont, 0, 5); + lv_obj_t * wrapper = lv_obj_create(barCont); + lv_obj_remove_style_all(wrapper); + lv_obj_set_size(wrapper, TFT_WIDTH * 2, 120); + + lv_obj_t * gnssLabel = lv_label_create(barCont); + lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); + lv_obj_set_pos(gnssLabel, 0, 127); + lv_obj_set_width(gnssLabel,90); + lv_obj_set_style_bg_color(gnssLabel, GP_ACTIVE_COLOR, 0); + lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); + lv_obj_set_style_border_color(gnssLabel, GP_INACTIVE_COLOR, 0); + lv_obj_set_style_border_width(gnssLabel, 1, 0); + lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); + lv_label_set_text(gnssLabel, "GPS"); + lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); + + gnssLabel = lv_label_create(barCont); + lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); + lv_obj_set_pos(gnssLabel, 95, 127); + lv_obj_set_width(gnssLabel,90); + lv_obj_set_style_bg_color(gnssLabel, GL_ACTIVE_COLOR, 0); + lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); + lv_obj_set_style_border_color(gnssLabel, GL_INACTIVE_COLOR, 0); + lv_obj_set_style_border_width(gnssLabel, 1, 0); + lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); + lv_label_set_text(gnssLabel, "GLONASS"); + lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); + + gnssLabel = lv_label_create(barCont); + lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); + lv_obj_set_pos(gnssLabel, 190, 127); + lv_obj_set_width(gnssLabel,90); + lv_obj_set_style_bg_color(gnssLabel, BD_ACTIVE_COLOR, 0); + lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); + lv_obj_set_style_border_color(gnssLabel, BD_INACTIVE_COLOR, 0); + lv_obj_set_style_border_width(gnssLabel, 1, 0); + lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); + lv_label_set_text(gnssLabel, "BEIDOU"); + lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); + + satelliteBar = lv_chart_create(wrapper); + lv_obj_set_size(satelliteBar, TFT_WIDTH * 2, 120); + lv_chart_set_div_line_count(satelliteBar, 10, 0); + lv_chart_set_range(satelliteBar, LV_CHART_AXIS_PRIMARY_Y, 0, 60); + satelliteBarSerie = lv_chart_add_series(satelliteBar, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y); + lv_chart_set_type(satelliteBar, LV_CHART_TYPE_BAR); + lv_obj_set_style_pad_all(satelliteBar, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_gap(satelliteBar, -7, LV_PART_ITEMS | LV_STATE_DEFAULT); + lv_obj_set_style_pad_column(satelliteBar, 2, 0); + lv_chart_set_point_count(satelliteBar, MAX_SATELLLITES_IN_VIEW ); + lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_TASK_ADDED, NULL); + lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_POST_END, NULL); + lv_obj_add_flag(satelliteBar, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); } #endif #ifdef TDECK_ESP32S3 void satelliteScr(_lv_obj_t *screen) { - lv_obj_t *infoGrid = lv_obj_create(screen); - lv_obj_set_width(infoGrid,TFT_WIDTH); - lv_obj_set_height(infoGrid,35); - lv_obj_set_flex_align(infoGrid, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(infoGrid, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_flex_flow(infoGrid, LV_FLEX_FLOW_ROW_WRAP); - lv_obj_set_pos(infoGrid,0,150); - - static lv_style_t styleGrid; - lv_style_init(&styleGrid); - lv_style_set_bg_opa(&styleGrid, LV_OPA_0); - lv_style_set_border_opa(&styleGrid, LV_OPA_0); - lv_obj_add_style(infoGrid, &styleGrid, LV_PART_MAIN); - - pdopLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(pdopLabel, fontDefault, 0); - lv_label_set_text_fmt(pdopLabel, "PDOP: %.1f", 0); - - hdopLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(hdopLabel, fontDefault, 0); - lv_label_set_text_fmt(hdopLabel, "HDOP: %.1f", 0); - - vdopLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(vdopLabel, fontDefault, 0); - lv_label_set_text_fmt(vdopLabel, "VDOP: %.1f", 0); - - altLabel = lv_label_create(infoGrid); - lv_obj_set_style_text_font(altLabel, fontDefault, 0); - lv_label_set_text_fmt(altLabel, "ALT: %4dm.", 0); - - lv_obj_t * barCont = lv_obj_create(screen); - lv_obj_set_size(barCont, TFT_WIDTH, 145); - lv_obj_set_pos(barCont, 0, 5); - - lv_obj_t * wrapper = lv_obj_create(barCont); - lv_obj_remove_style_all(wrapper); - lv_obj_set_size(wrapper, TFT_WIDTH * 2, 100); - - lv_obj_t * gnssLabel = lv_label_create(barCont); - lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); - lv_obj_set_pos(gnssLabel, 0, 102); - lv_obj_set_width(gnssLabel,90); - lv_obj_set_style_bg_color(gnssLabel, GP_ACTIVE_COLOR, 0); - lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); - lv_obj_set_style_border_color(gnssLabel, GP_INACTIVE_COLOR, 0); - lv_obj_set_style_border_width(gnssLabel, 1, 0); - lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); - lv_label_set_text(gnssLabel, "GPS"); - lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); - - gnssLabel = lv_label_create(barCont); - lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); - lv_obj_set_pos(gnssLabel, 95, 102); - lv_obj_set_width(gnssLabel,90); - lv_obj_set_style_bg_color(gnssLabel, GL_ACTIVE_COLOR, 0); - lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); - lv_obj_set_style_border_color(gnssLabel, GL_INACTIVE_COLOR, 0); - lv_obj_set_style_border_width(gnssLabel, 1, 0); - lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); - lv_label_set_text(gnssLabel, "GLONASS"); - lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); - - gnssLabel = lv_label_create(barCont); - lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); - lv_obj_set_pos(gnssLabel, 190, 102); - lv_obj_set_width(gnssLabel,90); - lv_obj_set_style_bg_color(gnssLabel, BD_ACTIVE_COLOR, 0); - lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); - lv_obj_set_style_border_color(gnssLabel, BD_INACTIVE_COLOR, 0); - lv_obj_set_style_border_width(gnssLabel, 1, 0); - lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); - lv_label_set_text(gnssLabel, "BEIDOU"); - lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); - - satelliteBar = lv_chart_create(wrapper); - lv_obj_set_size(satelliteBar, TFT_WIDTH * 2, 100); - lv_chart_set_div_line_count(satelliteBar, 10, 0); - lv_chart_set_range(satelliteBar, LV_CHART_AXIS_PRIMARY_Y, 0, 60); - satelliteBarSerie = lv_chart_add_series(satelliteBar, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y); - lv_chart_set_type(satelliteBar, LV_CHART_TYPE_BAR); - lv_obj_set_style_pad_all(satelliteBar, 0, LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_set_style_pad_gap(satelliteBar, -7, LV_PART_ITEMS | LV_STATE_DEFAULT); - lv_obj_set_style_pad_column(satelliteBar, 2, 0); - lv_chart_set_point_count(satelliteBar, MAX_SATELLLITES_IN_VIEW ); - lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_TASK_ADDED, NULL); - lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_POST_END, NULL); - lv_obj_add_flag(satelliteBar, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); - lv_obj_add_event_cb(satelliteBar, constSatEvent, LV_EVENT_LONG_PRESSED, NULL); - - constMsg = lv_msgbox_create(screen); - lv_obj_set_size(constMsg, 180, 185); - lv_obj_set_align(constMsg,LV_ALIGN_CENTER); - lv_obj_add_flag(constMsg,LV_OBJ_FLAG_HIDDEN); - lv_obj_add_event_cb(constMsg, closeConstSatEvent, LV_EVENT_LONG_PRESSED, NULL); + lv_obj_t *infoGrid = lv_obj_create(screen); + lv_obj_set_width(infoGrid,TFT_WIDTH); + lv_obj_set_height(infoGrid,35); + lv_obj_set_flex_align(infoGrid, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(infoGrid, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(infoGrid, LV_FLEX_FLOW_ROW_WRAP); + lv_obj_set_pos(infoGrid,0,150); + + static lv_style_t styleGrid; + lv_style_init(&styleGrid); + lv_style_set_bg_opa(&styleGrid, LV_OPA_0); + lv_style_set_border_opa(&styleGrid, LV_OPA_0); + lv_obj_add_style(infoGrid, &styleGrid, LV_PART_MAIN); + + pdopLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(pdopLabel, fontDefault, 0); + lv_label_set_text_fmt(pdopLabel, "PDOP: %.1f", 0); + + hdopLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(hdopLabel, fontDefault, 0); + lv_label_set_text_fmt(hdopLabel, "HDOP: %.1f", 0); + + vdopLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(vdopLabel, fontDefault, 0); + lv_label_set_text_fmt(vdopLabel, "VDOP: %.1f", 0); + + altLabel = lv_label_create(infoGrid); + lv_obj_set_style_text_font(altLabel, fontDefault, 0); + lv_label_set_text_fmt(altLabel, "ALT: %4dm.", 0); + + lv_obj_t * barCont = lv_obj_create(screen); + lv_obj_set_size(barCont, TFT_WIDTH, 145); + lv_obj_set_pos(barCont, 0, 5); + + lv_obj_t * wrapper = lv_obj_create(barCont); + lv_obj_remove_style_all(wrapper); + lv_obj_set_size(wrapper, TFT_WIDTH * 2, 100); + + lv_obj_t * gnssLabel = lv_label_create(barCont); + lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); + lv_obj_set_pos(gnssLabel, 0, 102); + lv_obj_set_width(gnssLabel,90); + lv_obj_set_style_bg_color(gnssLabel, GP_ACTIVE_COLOR, 0); + lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); + lv_obj_set_style_border_color(gnssLabel, GP_INACTIVE_COLOR, 0); + lv_obj_set_style_border_width(gnssLabel, 1, 0); + lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); + lv_label_set_text(gnssLabel, "GPS"); + lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); + + gnssLabel = lv_label_create(barCont); + lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); + lv_obj_set_pos(gnssLabel, 95, 102); + lv_obj_set_width(gnssLabel,90); + lv_obj_set_style_bg_color(gnssLabel, GL_ACTIVE_COLOR, 0); + lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); + lv_obj_set_style_border_color(gnssLabel, GL_INACTIVE_COLOR, 0); + lv_obj_set_style_border_width(gnssLabel, 1, 0); + lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); + lv_label_set_text(gnssLabel, "GLONASS"); + lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); + + gnssLabel = lv_label_create(barCont); + lv_obj_set_style_text_font(gnssLabel, fontSatInfo, 0); + lv_obj_set_pos(gnssLabel, 190, 102); + lv_obj_set_width(gnssLabel,90); + lv_obj_set_style_bg_color(gnssLabel, BD_ACTIVE_COLOR, 0); + lv_obj_set_style_bg_opa(gnssLabel, LV_OPA_100, 0); + lv_obj_set_style_border_color(gnssLabel, BD_INACTIVE_COLOR, 0); + lv_obj_set_style_border_width(gnssLabel, 1, 0); + lv_obj_set_style_border_opa(gnssLabel, LV_OPA_100, 0); + lv_label_set_text(gnssLabel, "BEIDOU"); + lv_obj_set_style_text_align(gnssLabel, LV_TEXT_ALIGN_CENTER, 0); + + satelliteBar = lv_chart_create(wrapper); + lv_obj_set_size(satelliteBar, TFT_WIDTH * 2, 100); + lv_chart_set_div_line_count(satelliteBar, 10, 0); + lv_chart_set_range(satelliteBar, LV_CHART_AXIS_PRIMARY_Y, 0, 60); + satelliteBarSerie = lv_chart_add_series(satelliteBar, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y); + lv_chart_set_type(satelliteBar, LV_CHART_TYPE_BAR); + lv_obj_set_style_pad_all(satelliteBar, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_gap(satelliteBar, -7, LV_PART_ITEMS | LV_STATE_DEFAULT); + lv_obj_set_style_pad_column(satelliteBar, 2, 0); + lv_chart_set_point_count(satelliteBar, MAX_SATELLLITES_IN_VIEW ); + lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_TASK_ADDED, NULL); + lv_obj_add_event_cb(satelliteBar, satelliteBarDrawEvent, LV_EVENT_DRAW_POST_END, NULL); + lv_obj_add_flag(satelliteBar, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); + lv_obj_add_event_cb(satelliteBar, constSatEvent, LV_EVENT_LONG_PRESSED, NULL); + + constMsg = lv_msgbox_create(screen); + lv_obj_set_size(constMsg, 180, 185); + lv_obj_set_align(constMsg,LV_ALIGN_CENTER); + lv_obj_add_flag(constMsg,LV_OBJ_FLAG_HIDDEN); + lv_obj_add_event_cb(constMsg, closeConstSatEvent, LV_EVENT_LONG_PRESSED, NULL); } #endif /** * @brief Draw Satellite SNR Bars * - * + * @details Updates the SNR (Signal-to-Noise Ratio) bar chart with current satellite data. */ void drawSatSNR() { - for (int i = 0; i < MAX_SATELLLITES_IN_VIEW ; i++) - { - lv_chart_set_value_by_id(satelliteBar, satelliteBarSerie, i, LV_CHART_POINT_NONE); - } + for (int i = 0; i < MAX_SATELLLITES_IN_VIEW ; i++) + { + lv_chart_set_value_by_id(satelliteBar, satelliteBarSerie, i, LV_CHART_POINT_NONE); + } - for (int i = 0; i < gps.gpsData.satInView; ++i) - { - lv_chart_set_value_by_id(satelliteBar, satelliteBarSerie, i, gps.satTracker[i].snr); - } + for (int i = 0; i < gps.gpsData.satInView; ++i) + { + lv_chart_set_value_by_id(satelliteBar, satelliteBarSerie, i, gps.satTracker[i].snr); + } - lv_chart_refresh(satelliteBar); + lv_chart_refresh(satelliteBar); } /** * @brief Draw Satellite Constellation * - * + * @details Draws the satellite constellation grid on the canvas, including concentric circles, cross lines, and cardinal direction labels. */ void drawSatConst() { - // Draw Circles - lv_draw_arc_dsc_t dscArc; - lv_draw_arc_dsc_init(&dscArc); - dscArc.color = CONSTEL_COLOR; - dscArc.width = 2; - dscArc.center.x = canvasCenter_X; - dscArc.center.y = canvasCenter_Y; - dscArc.start_angle = 0; - dscArc.end_angle = 360; - dscArc.radius = canvasRadius; - lv_draw_arc(&canvasLayer, &dscArc); - dscArc.radius = ( canvasRadius * 2 ) / 3; - lv_draw_arc(&canvasLayer, &dscArc); - dscArc.radius = canvasRadius / 3 ; - lv_draw_arc(&canvasLayer, &dscArc); - - // Draw Lines - lv_draw_line_dsc_t dscLine; - lv_draw_line_dsc_init(&dscLine); - dscLine.color = CONSTEL_COLOR; - dscLine.width = 2; - dscLine.round_end = 1; - dscLine.round_start = 1; - dscLine.p1.x = canvasCenter_X; - dscLine.p1.y = canvasOffset; - dscLine.p2.x = canvasCenter_X; - dscLine.p2.y = canvasSize-canvasOffset; - lv_draw_line(&canvasLayer, &dscLine); - dscLine.p1.x = canvasOffset; - dscLine.p1.y = canvasCenter_Y; - dscLine.p2.x = canvasSize-canvasOffset; - dscLine.p2.y = canvasCenter_Y; - lv_draw_line(&canvasLayer, &dscLine); - - // Draw Text Coordinates - lv_draw_label_dsc_t dscLabel; - lv_draw_label_dsc_init(&dscLabel); - dscLabel.color = CONSTEL_COLOR; - dscLabel.opa = LV_OPA_100; - dscLabel.font = &lv_font_montserrat_12; - dscLabel.text = "N"; - lv_area_t labelPos = {canvasCenter_X-5, 0, canvasCenter_X+5, 0}; - lv_draw_label(&canvasLayer, &dscLabel, &labelPos); - dscLabel.text = "S"; - labelPos = {canvasCenter_X-4, canvasSize-15, canvasCenter_X+4, canvasSize}; - lv_draw_label(&canvasLayer, &dscLabel, &labelPos); - dscLabel.text = "E"; - labelPos = {canvasSize-12, canvasCenter_Y-7 , canvasSize, canvasCenter_Y+7}; - lv_draw_label(&canvasLayer, &dscLabel, &labelPos); - dscLabel.text = "W"; - labelPos = {0, canvasCenter_Y-7, canvasSize - 10, canvasCenter_Y+7}; - lv_draw_label(&canvasLayer, &dscLabel, &labelPos); - - // Finish Canvas Draw - lv_canvas_finish_layer(constCanvas, &canvasLayer); + // Draw Circles + lv_draw_arc_dsc_t dscArc; + lv_draw_arc_dsc_init(&dscArc); + dscArc.color = CONSTEL_COLOR; + dscArc.width = 2; + dscArc.center.x = canvasCenter_X; + dscArc.center.y = canvasCenter_Y; + dscArc.start_angle = 0; + dscArc.end_angle = 360; + dscArc.radius = canvasRadius; + lv_draw_arc(&canvasLayer, &dscArc); + dscArc.radius = ( canvasRadius * 2 ) / 3; + lv_draw_arc(&canvasLayer, &dscArc); + dscArc.radius = canvasRadius / 3 ; + lv_draw_arc(&canvasLayer, &dscArc); + + // Draw Lines + lv_draw_line_dsc_t dscLine; + lv_draw_line_dsc_init(&dscLine); + dscLine.color = CONSTEL_COLOR; + dscLine.width = 2; + dscLine.round_end = 1; + dscLine.round_start = 1; + dscLine.p1.x = canvasCenter_X; + dscLine.p1.y = canvasOffset; + dscLine.p2.x = canvasCenter_X; + dscLine.p2.y = canvasSize-canvasOffset; + lv_draw_line(&canvasLayer, &dscLine); + dscLine.p1.x = canvasOffset; + dscLine.p1.y = canvasCenter_Y; + dscLine.p2.x = canvasSize-canvasOffset; + dscLine.p2.y = canvasCenter_Y; + lv_draw_line(&canvasLayer, &dscLine); + + // Draw Text Coordinates + lv_draw_label_dsc_t dscLabel; + lv_draw_label_dsc_init(&dscLabel); + dscLabel.color = CONSTEL_COLOR; + dscLabel.opa = LV_OPA_100; + dscLabel.font = &lv_font_montserrat_12; + dscLabel.text = "N"; + lv_area_t labelPos = {canvasCenter_X-5, 0, canvasCenter_X+5, 0}; + lv_draw_label(&canvasLayer, &dscLabel, &labelPos); + dscLabel.text = "S"; + labelPos = {canvasCenter_X-4, canvasSize-15, canvasCenter_X+4, canvasSize}; + lv_draw_label(&canvasLayer, &dscLabel, &labelPos); + dscLabel.text = "E"; + labelPos = {canvasSize-12, canvasCenter_Y-7 , canvasSize, canvasCenter_Y+7}; + lv_draw_label(&canvasLayer, &dscLabel, &labelPos); + dscLabel.text = "W"; + labelPos = {0, canvasCenter_Y-7, canvasSize - 10, canvasCenter_Y+7}; + lv_draw_label(&canvasLayer, &dscLabel, &labelPos); + + // Finish Canvas Draw + lv_canvas_finish_layer(constCanvas, &canvasLayer); } /** * @brief Draw Satellite Position in Constellation * - * + * @details Draws the position of each satellite within the constellation canvas, including their colored circles (based on constellation and activity) + * and overlays their satellite numbers at their respective positions. */ void drawSatSky() { - lv_canvas_fill_bg(constCanvas, lv_color_black(), LV_OPA_100); - drawSatConst(); - - lv_layer_t satPosLayer; - lv_canvas_init_layer(constCanvas, &satPosLayer); - - // Draw Satellite - lv_draw_arc_dsc_t dscSat; - lv_draw_arc_dsc_init(&dscSat); - dscSat.width = 8; - dscSat.start_angle = 0; - dscSat.end_angle = 360; - dscSat.radius = 8; - dscSat.opa = LV_OPA_70; - - for (int i = 0; i < gps.gpsData.satInView; i++) - { - if ( strcmp(gps.satTracker[i].talker_id,"GP") == 0 ) - dscSat.color = gps.satTracker[i].active == true ? GP_INACTIVE_COLOR : GP_ACTIVE_COLOR; - if ( strcmp(gps.satTracker[i].talker_id,"GL") == 0 ) - dscSat.color = gps.satTracker[i].active == true ? GL_INACTIVE_COLOR : GL_ACTIVE_COLOR; - if ( strcmp(gps.satTracker[i].talker_id,"BD") == 0 ) - dscSat.color = gps.satTracker[i].active == true ? BD_INACTIVE_COLOR : BD_ACTIVE_COLOR; - dscSat.center.x = gps.satTracker[i].posX; - dscSat.center.y = gps.satTracker[i].posY; - lv_draw_arc(&satPosLayer, &dscSat); - - // Draw Satellite Number - char buf[16]; - lv_draw_rect_dsc_t draw_rect_dsc; - lv_draw_rect_dsc_init(&draw_rect_dsc); - - lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"%d", gps.satTracker[i].satNum); - - draw_rect_dsc.bg_opa = LV_OPA_TRANSP; - draw_rect_dsc.radius = 0; - draw_rect_dsc.bg_image_symbol_font = &lv_font_montserrat_8; - draw_rect_dsc.bg_image_src = buf; - draw_rect_dsc.bg_image_recolor = lv_color_white(); - - lv_area_t a; - a.x1 = gps.satTracker[i].posX-8; - a.x2 = gps.satTracker[i].posX+8; - a.y1 = gps.satTracker[i].posY-5; - a.y2 = gps.satTracker[i].posY+4; - - lv_draw_rect(&satPosLayer, &draw_rect_dsc, &a); - - // Finish Canvas Draw - lv_canvas_finish_layer(constCanvas, &satPosLayer); - } + lv_canvas_fill_bg(constCanvas, lv_color_black(), LV_OPA_100); + drawSatConst(); + + lv_layer_t satPosLayer; + lv_canvas_init_layer(constCanvas, &satPosLayer); + + // Draw Satellite + lv_draw_arc_dsc_t dscSat; + lv_draw_arc_dsc_init(&dscSat); + dscSat.width = 8; + dscSat.start_angle = 0; + dscSat.end_angle = 360; + dscSat.radius = 8; + dscSat.opa = LV_OPA_70; + + for (int i = 0; i < gps.gpsData.satInView; i++) + { + if ( strcmp(gps.satTracker[i].talker_id,"GP") == 0 ) + dscSat.color = gps.satTracker[i].active == true ? GP_INACTIVE_COLOR : GP_ACTIVE_COLOR; + if ( strcmp(gps.satTracker[i].talker_id,"GL") == 0 ) + dscSat.color = gps.satTracker[i].active == true ? GL_INACTIVE_COLOR : GL_ACTIVE_COLOR; + if ( strcmp(gps.satTracker[i].talker_id,"BD") == 0 ) + dscSat.color = gps.satTracker[i].active == true ? BD_INACTIVE_COLOR : BD_ACTIVE_COLOR; + dscSat.center.x = gps.satTracker[i].posX; + dscSat.center.y = gps.satTracker[i].posY; + lv_draw_arc(&satPosLayer, &dscSat); + + // Draw Satellite Number + char buf[16]; + lv_draw_rect_dsc_t draw_rect_dsc; + lv_draw_rect_dsc_init(&draw_rect_dsc); + + lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"%d", gps.satTracker[i].satNum); + + draw_rect_dsc.bg_opa = LV_OPA_TRANSP; + draw_rect_dsc.radius = 0; + draw_rect_dsc.bg_image_symbol_font = &lv_font_montserrat_8; + draw_rect_dsc.bg_image_src = buf; + draw_rect_dsc.bg_image_recolor = lv_color_white(); + + lv_area_t a; + a.x1 = gps.satTracker[i].posX-8; + a.x2 = gps.satTracker[i].posX+8; + a.y1 = gps.satTracker[i].posY-5; + a.y2 = gps.satTracker[i].posY+4; + + lv_draw_rect(&satPosLayer, &draw_rect_dsc, &a); + + // Finish Canvas Draw + lv_canvas_finish_layer(constCanvas, &satPosLayer); + } } \ No newline at end of file diff --git a/lib/gui/src/satInfoScr.hpp b/lib/gui/src/satInfoScr.hpp index 97697b32..feec998f 100644 --- a/lib/gui/src/satInfoScr.hpp +++ b/lib/gui/src/satInfoScr.hpp @@ -2,8 +2,8 @@ * @file satInfoScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Satellite info screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -14,37 +14,42 @@ /** * @brief Satellite SV Colors * + * Color definitions for active and inactive states of satellite systems in the UI. */ // GPS -#define GP_ACTIVE_COLOR lv_color_hex(0x104828) -#define GP_INACTIVE_COLOR lv_color_hex(0x229954) +#define GP_ACTIVE_COLOR lv_color_hex(0x104828) /**< Active color for GPS satellites. */ +#define GP_INACTIVE_COLOR lv_color_hex(0x229954) /**< Inactive color for GPS satellites. */ + // GLONASS -#define GL_ACTIVE_COLOR lv_color_hex(0x11364d) -#define GL_INACTIVE_COLOR lv_color_hex(0x2471a3) +#define GL_ACTIVE_COLOR lv_color_hex(0x11364d) /**< Active color for GLONASS satellites. */ +#define GL_INACTIVE_COLOR lv_color_hex(0x2471a3) /**< Inactive color for GLONASS satellites. */ + // BEIDOU -#define BD_ACTIVE_COLOR lv_color_hex(0x3b1c48) -#define BD_INACTIVE_COLOR lv_color_hex(0x7d3c98) +#define BD_ACTIVE_COLOR lv_color_hex(0x3b1c48) /**< Active color for BEIDOU satellites. */ +#define BD_INACTIVE_COLOR lv_color_hex(0x7d3c98) /**< Inactive color for BEIDOU satellites. */ /** - * @brief Satellite Tracking Tile screen objects + * @brief Satellite Tracking Tile screen objects. * + * @details Objects used in the satellite tracking tile */ -extern lv_obj_t *pdopLabel; -extern lv_obj_t *hdopLabel; -extern lv_obj_t *vdopLabel; -extern lv_obj_t *altLabel; -extern lv_obj_t *constCanvas; -extern lv_obj_t *satelliteBar; -extern lv_chart_series_t *satelliteBarSerie; -extern lv_obj_t *constMsg; +extern lv_obj_t *pdopLabel; /**< Label for PDOP value. */ +extern lv_obj_t *hdopLabel; /**< Label for HDOP value. */ +extern lv_obj_t *vdopLabel; /**< Label for VDOP value. */ +extern lv_obj_t *altLabel; /**< Label for altitude value. */ +extern lv_obj_t *constCanvas; /**< Canvas for constellation visualization. */ +extern lv_obj_t *satelliteBar; /**< Chart object for satellite signal bar. */ +extern lv_chart_series_t *satelliteBarSerie; /**< Series for satellite signal bar chart. */ +extern lv_obj_t *constMsg; /**< Label for constellation status messages. */ /** - * @brief Satellite Constellation Layer Canvas Definition + * @brief Satellite Constellation Layer Canvas Definition. * + * @details Definitions for the color and layers used for drawing the satellite constellation on the canvas. */ - #define CONSTEL_COLOR lv_color_hex(0x515a5a) - extern lv_layer_t canvasLayer; - extern lv_layer_t satLayer; +#define CONSTEL_COLOR lv_color_hex(0x515a5a) /**< Color used for the constellation canvas background. */ +extern lv_layer_t canvasLayer; /**< Layer for the base canvas drawing. */ +extern lv_layer_t satLayer; /**< Layer for satellite elements. */ void drawTextOnLayer(const char * text, lv_layer_t * layer, lv_point_t * p, lv_area_t * coords, lv_color_t color, const void * font, int16_t offset); diff --git a/lib/gui/src/searchSatScr.cpp b/lib/gui/src/searchSatScr.cpp index 6ff602f0..6a63525d 100644 --- a/lib/gui/src/searchSatScr.cpp +++ b/lib/gui/src/searchSatScr.cpp @@ -2,118 +2,124 @@ * @file searchSatScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - GPS satellite search screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "searchSatScr.hpp" -static unsigned long millisActual = 0; -static bool skipSearch = false; -bool isSearchingSat = true; -extern uint8_t activeTile; -lv_timer_t *mainTimer; // Main Screen Timer +static unsigned long millisActual = 0; /**< Stores the current timestamp in milliseconds */ +static bool skipSearch = false; /**< Flag to indicate if satellite search should be skipped */ +bool isSearchingSat = true; /**< Flag to indicate if satellite search is in progress */ +extern uint8_t activeTile; /**< Index of the currently active tile */ +lv_timer_t *mainTimer; /**< Main Screen Timer */ /** * @brief Button events * - * @param event + * @details Handles button events for the search screen. + * + * @param event LVGL event pointer. */ void buttonEvent(lv_event_t *event) { - char *option = (char *)lv_event_get_user_data(event); - if (strcmp(option,"skip") == 0) - skipSearch = true; - if (strcmp(option,"settings") == 0) - lv_screen_load(settingsScreen); - lv_timer_resume(mainTimer); + char *option = (char *)lv_event_get_user_data(event); + if (strcmp(option,"skip") == 0) + skipSearch = true; + if (strcmp(option,"settings") == 0) + lv_screen_load(settingsScreen); + lv_timer_resume(mainTimer); } /** * @brief Search valid GPS signal * + * @details Checks for a valid GPS fix or a skip command. + * + * @param searchTimer LVGL timer pointer associated with the satellite search. */ void searchGPS(lv_timer_t *searchTimer) { - if (isGpsFixed) - { - millisActual = millis(); - while (millis() < millisActual + 500) - ; - lv_timer_del(searchTimer); - lv_timer_resume(mainTimer); - isSearchingSat = false; - loadMainScreen(); - } + if (isGpsFixed) + { + millisActual = millis(); + while (millis() < millisActual + 500) + ; + lv_timer_del(searchTimer); + lv_timer_resume(mainTimer); + isSearchingSat = false; + loadMainScreen(); + } - if (skipSearch) - { - lv_timer_del(searchTimer); - isSearchingSat = false; - zoom = defaultZoom; - activeTile = 3; - lv_tileview_set_tile_by_index(tilesScreen, 3, 0, LV_ANIM_OFF); - loadMainScreen(); - } + if (skipSearch) + { + lv_timer_del(searchTimer); + isSearchingSat = false; + zoom = defaultZoom; + activeTile = 3; + lv_tileview_set_tile_by_index(tilesScreen, 3, 0, LV_ANIM_OFF); + loadMainScreen(); + } } /** * @brief Create Satellite Search Screen * + * @details Creates the satellite search screen */ void createSearchSatScr() { - searchTimer = lv_timer_create(searchGPS, 100, NULL); - lv_timer_ready(searchTimer); - lv_timer_pause(mainTimer); + searchTimer = lv_timer_create(searchGPS, 100, NULL); + lv_timer_ready(searchTimer); + lv_timer_pause(mainTimer); - searchSatScreen = lv_obj_create(NULL); + searchSatScreen = lv_obj_create(NULL); - lv_obj_t *label = lv_label_create(searchSatScreen); - lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0); - lv_label_set_text(label, textSearch); - lv_obj_set_align(label, LV_ALIGN_CENTER); - lv_obj_set_y(label, -100); + lv_obj_t *label = lv_label_create(searchSatScreen); + lv_obj_set_style_text_font(label, &lv_font_montserrat_18, 0); + lv_label_set_text(label, textSearch); + lv_obj_set_align(label, LV_ALIGN_CENTER); + lv_obj_set_y(label, -100); - lv_obj_t *spinner = lv_spinner_create(searchSatScreen); - lv_obj_set_size(spinner, 130, 130); - lv_spinner_set_anim_params(spinner, 2000, 200); - lv_obj_center(spinner); + lv_obj_t *spinner = lv_spinner_create(searchSatScreen); + lv_obj_set_size(spinner, 130, 130); + lv_spinner_set_anim_params(spinner, 2000, 200); + lv_obj_center(spinner); - lv_obj_t *satImg = lv_img_create(searchSatScreen); - lv_img_set_src(satImg, satIconFile); - lv_obj_set_align(satImg, LV_ALIGN_CENTER); + lv_obj_t *satImg = lv_img_create(searchSatScreen); + lv_img_set_src(satImg, satIconFile); + lv_obj_set_align(satImg, LV_ALIGN_CENTER); - // Button Bar - lv_obj_t *buttonBar = lv_obj_create(searchSatScreen); - lv_obj_set_size(buttonBar, TFT_WIDTH, 68 * scaleBut); - lv_obj_set_pos(buttonBar, 0, TFT_HEIGHT - 80 * scaleBut); - lv_obj_set_flex_flow(buttonBar, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(buttonBar, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_SCROLLABLE); - static lv_style_t styleBar; - lv_style_init(&styleBar); - lv_style_set_bg_opa(&styleBar, LV_OPA_0); - lv_style_set_border_opa(&styleBar, LV_OPA_0); - lv_obj_add_style(buttonBar, &styleBar, LV_PART_MAIN); + // Button Bar + lv_obj_t *buttonBar = lv_obj_create(searchSatScreen); + lv_obj_set_size(buttonBar, TFT_WIDTH, 68 * scaleBut); + lv_obj_set_pos(buttonBar, 0, TFT_HEIGHT - 80 * scaleBut); + lv_obj_set_flex_flow(buttonBar, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(buttonBar, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(buttonBar, LV_OBJ_FLAG_SCROLLABLE); + static lv_style_t styleBar; + lv_style_init(&styleBar); + lv_style_set_bg_opa(&styleBar, LV_OPA_0); + lv_style_set_border_opa(&styleBar, LV_OPA_0); + lv_obj_add_style(buttonBar, &styleBar, LV_PART_MAIN); - lv_obj_t *imgBtn; - - // Settings Button - imgBtn = lv_img_create(buttonBar); - lv_img_set_src(imgBtn, confIconFile); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_event_cb(imgBtn, buttonEvent, LV_EVENT_PRESSED, (char*)"settings"); - - // Skip Button - imgBtn = lv_img_create(buttonBar); - lv_img_set_src(imgBtn, skipIconFile); - lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); - lv_img_set_zoom(imgBtn,buttonScale); - lv_obj_update_layout(imgBtn); - lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); - lv_obj_add_event_cb(imgBtn, buttonEvent, LV_EVENT_PRESSED, (char*)"skip"); + lv_obj_t *imgBtn; + + // Settings Button + imgBtn = lv_img_create(buttonBar); + lv_img_set_src(imgBtn, confIconFile); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_event_cb(imgBtn, buttonEvent, LV_EVENT_PRESSED, (char*)"settings"); + + // Skip Button + imgBtn = lv_img_create(buttonBar); + lv_img_set_src(imgBtn, skipIconFile); + lv_obj_add_flag(imgBtn, LV_OBJ_FLAG_CLICKABLE); + lv_img_set_zoom(imgBtn,buttonScale); + lv_obj_update_layout(imgBtn); + lv_obj_set_style_size(imgBtn,48 * scaleBut, 48 * scaleBut, 0); + lv_obj_add_event_cb(imgBtn, buttonEvent, LV_EVENT_PRESSED, (char*)"skip"); } diff --git a/lib/gui/src/searchSatScr.hpp b/lib/gui/src/searchSatScr.hpp index ff638140..b1c5fd1f 100644 --- a/lib/gui/src/searchSatScr.hpp +++ b/lib/gui/src/searchSatScr.hpp @@ -2,8 +2,8 @@ * @file searchSatScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - GPS satellite search screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,12 +11,12 @@ #include "gps.hpp" #include "globalGuiDef.h" -extern lv_obj_t *searchSatScreen; // Search Satellite Screen -static lv_timer_t *searchTimer; -static const char* textSearch PROGMEM = "Searching for satellites"; -static const char* satIconFile PROGMEM = "/sat.bin"; -static const char *skipIconFile PROGMEM = "/skip.bin"; // Skip icon -static const char *confIconFile PROGMEM = "/settings.bin"; // Settings icon +extern lv_obj_t *searchSatScreen; /**< Search Satellite Screen */ +static lv_timer_t *searchTimer; /**< Timer for satellite search process */ +static const char* textSearch PROGMEM = "Searching for satellites"; /**< Search status message */ +static const char* satIconFile PROGMEM = "/sat.bin"; /**< Path to satellite icon file */ +static const char *skipIconFile PROGMEM = "/skip.bin"; /**< Path to skip icon file */ +static const char *confIconFile PROGMEM = "/settings.bin"; /**< Path to settings icon file */ void loadMainScreen(); void searchGPS(lv_timer_t *searchTimer); diff --git a/lib/gui/src/settingsScr.cpp b/lib/gui/src/settingsScr.cpp index 2ae2a427..db5d50e2 100644 --- a/lib/gui/src/settingsScr.cpp +++ b/lib/gui/src/settingsScr.cpp @@ -2,153 +2,165 @@ * @file settingsScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Settings Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "settingsScr.hpp" -bool needReboot = false; +bool needReboot = false; /**< Flag to indicate if a system reboot is required */ + extern Compass compass; /** * @brief Back button event * - * @param event + * @details Handles the back button action + * + * @param event LVGL event pointer. */ static void back(lv_event_t *event) { - if (isSearchingSat) - { - lv_timer_pause(mainTimer); - lv_screen_load(searchSatScreen); - } - else - loadMainScreen(); + if (isSearchingSat) + { + lv_timer_pause(mainTimer); + lv_screen_load(searchSatScreen); + } + else + loadMainScreen(); } /** * @brief Touch Calibration * - * @param event + * @details Handles the touch calibration event. + * + * @param event LVGL event pointer. */ static void touchCalib(lv_event_t *event) { - repeatCalib = true; - tft.fillScreen(TFT_BLACK); - touchCalibrate(); - repeatCalib = false; - isMainScreen = false; - tft.fillScreen(TFT_BLACK); - lv_screen_load(settingsScreen); - lv_obj_invalidate(lv_scr_act()); + repeatCalib = true; + tft.fillScreen(TFT_BLACK); + touchCalibrate(); + repeatCalib = false; + isMainScreen = false; + tft.fillScreen(TFT_BLACK); + lv_screen_load(settingsScreen); + lv_obj_invalidate(lv_scr_act()); } /** * @brief Compass Calibration * - * @param event + * @details Handles the compass calibration event. + * + * @param event LVGL event pointer. */ #ifdef ENABLE_COMPASS static void compassCalib(lv_event_t *event) { - tft.fillScreen(TFT_BLACK); - compass.calibrate(); - tft.fillScreen(TFT_BLACK); - isMainScreen = false; - lv_screen_load(settingsScreen); - lv_obj_invalidate(lv_scr_act()); + tft.fillScreen(TFT_BLACK); + compass.calibrate(); + tft.fillScreen(TFT_BLACK); + isMainScreen = false; + lv_screen_load(settingsScreen); + lv_obj_invalidate(lv_scr_act()); } #endif /** * @brief Map Setting * - * @param event + * @details Handles the map settings event by loading the map settings screen. + * + * @param event LVGL event pointer. */ static void mapSettings(lv_event_t *event) { - lv_screen_load(mapSettingsScreen); + lv_screen_load(mapSettingsScreen); } /** * @brief Device Settings * - * @param event + * @details Handles the device settings event by loading the device settings screen. + * + * @param event LVGL event pointer. */ static void deviceSettings(lv_event_t *event) { - lv_screen_load(deviceSettingsScreen); + lv_screen_load(deviceSettingsScreen); } /** * @brief Create Settings screen * + * @details Creates the settings screen */ void createSettingsScr() { - // Settings Screen - settingsScreen = lv_obj_create(NULL); - settingsButtons = lv_obj_create(settingsScreen); - lv_obj_set_size(settingsButtons, TFT_WIDTH, TFT_HEIGHT); - lv_obj_set_flex_align(settingsButtons, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_set_style_pad_row(settingsButtons, 20, 0); - lv_obj_set_flex_flow(settingsButtons, LV_FLEX_FLOW_COLUMN); - static lv_style_t styleSettings; - lv_style_init(&styleSettings); - lv_style_set_bg_opa(&styleSettings, LV_OPA_0); - lv_style_set_border_opa(&styleSettings, LV_OPA_0); - lv_obj_add_style(settingsButtons, &styleSettings, LV_PART_MAIN); + // Settings Screen + settingsScreen = lv_obj_create(NULL); + settingsButtons = lv_obj_create(settingsScreen); + lv_obj_set_size(settingsButtons, TFT_WIDTH, TFT_HEIGHT); + lv_obj_set_flex_align(settingsButtons, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_pad_row(settingsButtons, 20, 0); + lv_obj_set_flex_flow(settingsButtons, LV_FLEX_FLOW_COLUMN); + static lv_style_t styleSettings; + lv_style_init(&styleSettings); + lv_style_set_bg_opa(&styleSettings, LV_OPA_0); + lv_style_set_border_opa(&styleSettings, LV_OPA_0); + lv_obj_add_style(settingsButtons, &styleSettings, LV_PART_MAIN); - lv_obj_t *btnLabel; - lv_obj_t *btn; + lv_obj_t *btnLabel; + lv_obj_t *btn; - #ifdef ENABLE_COMPASS - // Compass Calibration - btn = lv_btn_create(settingsButtons); - lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); - btnLabel = lv_label_create(btn); - lv_obj_set_style_text_font(btnLabel, fontLarge, 0); - lv_label_set_text_static(btnLabel, "Compass Calibration"); - lv_obj_center(btnLabel); - lv_obj_add_event_cb(btn, compassCalib, LV_EVENT_CLICKED, NULL); - #endif + #ifdef ENABLE_COMPASS + // Compass Calibration + btn = lv_btn_create(settingsButtons); + lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); + btnLabel = lv_label_create(btn); + lv_obj_set_style_text_font(btnLabel, fontLarge, 0); + lv_label_set_text_static(btnLabel, "Compass Calibration"); + lv_obj_center(btnLabel); + lv_obj_add_event_cb(btn, compassCalib, LV_EVENT_CLICKED, NULL); + #endif - #ifdef TOUCH_INPUT - // Touch Calibration - btn = lv_btn_create(settingsButtons); - lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); - btnLabel = lv_label_create(btn); - lv_obj_set_style_text_font(btnLabel, fontLarge, 0); - lv_label_set_text_static(btnLabel, "Touch Calibration"); - lv_obj_center(btnLabel); - lv_obj_add_event_cb(btn, touchCalib, LV_EVENT_CLICKED, NULL); - #endif + #ifdef TOUCH_INPUT + // Touch Calibration + btn = lv_btn_create(settingsButtons); + lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); + btnLabel = lv_label_create(btn); + lv_obj_set_style_text_font(btnLabel, fontLarge, 0); + lv_label_set_text_static(btnLabel, "Touch Calibration"); + lv_obj_center(btnLabel); + lv_obj_add_event_cb(btn, touchCalib, LV_EVENT_CLICKED, NULL); + #endif - // Map Settings - btn = lv_btn_create(settingsButtons); - lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); - btnLabel = lv_label_create(btn); - lv_obj_set_style_text_font(btnLabel, fontLarge, 0); - lv_label_set_text_static(btnLabel, "Map Settings"); - lv_obj_center(btnLabel); - lv_obj_add_event_cb(btn, mapSettings, LV_EVENT_CLICKED, NULL); + // Map Settings + btn = lv_btn_create(settingsButtons); + lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); + btnLabel = lv_label_create(btn); + lv_obj_set_style_text_font(btnLabel, fontLarge, 0); + lv_label_set_text_static(btnLabel, "Map Settings"); + lv_obj_center(btnLabel); + lv_obj_add_event_cb(btn, mapSettings, LV_EVENT_CLICKED, NULL); - // Device Settings - btn = lv_btn_create(settingsButtons); - lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); - btnLabel = lv_label_create(btn); - lv_obj_set_style_text_font(btnLabel, fontLarge, 0); - lv_label_set_text_static(btnLabel, "Device Settings"); - lv_obj_center(btnLabel); - lv_obj_add_event_cb(btn, deviceSettings, LV_EVENT_CLICKED, NULL); + // Device Settings + btn = lv_btn_create(settingsButtons); + lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); + btnLabel = lv_label_create(btn); + lv_obj_set_style_text_font(btnLabel, fontLarge, 0); + lv_label_set_text_static(btnLabel, "Device Settings"); + lv_obj_center(btnLabel); + lv_obj_add_event_cb(btn, deviceSettings, LV_EVENT_CLICKED, NULL); - // Back button - btn = lv_btn_create(settingsButtons); - lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); - btnLabel = lv_label_create(btn); - lv_obj_set_style_text_font(btnLabel, fontLarge, 0); - lv_label_set_text_static(btnLabel, "Back"); - lv_obj_center(btnLabel); - lv_obj_add_event_cb(btn, back, LV_EVENT_CLICKED, NULL); + // Back button + btn = lv_btn_create(settingsButtons); + lv_obj_set_size(btn, TFT_WIDTH - 30, 40 * scale); + btnLabel = lv_label_create(btn); + lv_obj_set_style_text_font(btnLabel, fontLarge, 0); + lv_label_set_text_static(btnLabel, "Back"); + lv_obj_center(btnLabel); + lv_obj_add_event_cb(btn, back, LV_EVENT_CLICKED, NULL); } diff --git a/lib/gui/src/settingsScr.hpp b/lib/gui/src/settingsScr.hpp index d6ad6400..6f392ed1 100644 --- a/lib/gui/src/settingsScr.hpp +++ b/lib/gui/src/settingsScr.hpp @@ -2,8 +2,8 @@ * @file settingsScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Settings Screen - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -21,5 +21,5 @@ static void compassCalib(lv_event_t *event); static void mapSettings(lv_event_t *event); static void deviceSettings(lv_event_t *event); -static lv_obj_t *settingsButtons; +static lv_obj_t *settingsButtons; /**< Object holding all settings screen buttons */ void createSettingsScr(); diff --git a/lib/gui/src/splashScr.cpp b/lib/gui/src/splashScr.cpp index 19f8695d..1abe17e6 100644 --- a/lib/gui/src/splashScr.cpp +++ b/lib/gui/src/splashScr.cpp @@ -2,101 +2,94 @@ * @file splashScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Splash screen - NOT LVGL - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "splashScr.hpp" -static unsigned long millisActual = 0; +static unsigned long millisActual = 0; /**< Current value of the system timer in milliseconds */ extern Maps mapView; extern Gps gps; /** * @brief Splash screen * + * @details Displays the splash screen with logo, device information, Preloads the map and initializes display settings. */ void splashScreen() { - // Preload Map - if (mapSet.vectorMap) - { - mapView.isPosMoved = true; - mapView.generateVectorMap(zoom); - } - else - { - mapView.currentMapTile = mapView.getMapTile(gps.gpsData.longitude, gps.gpsData.latitude, zoom, 0, 0); - mapView.generateRenderMap(zoom); - } + // Preload Map + mapView.currentMapTile = mapView.getMapTile(gps.gpsData.longitude, gps.gpsData.latitude, zoom, 0, 0); + mapView.generateMap(zoom); - setTime = false; + setTime = false; - tft.fillScreen(TFT_BLACK); - millisActual = millis(); - tft.setBrightness(0); + tft.fillScreen(TFT_BLACK); + millisActual = millis(); + tft.setBrightness(0); - static uint16_t pngHeight = 0; - static uint16_t pngWidth = 0; + static uint16_t pngHeight = 0; + static uint16_t pngWidth = 0; - getPngSize(logoFile, &pngWidth, &pngHeight); - tft.drawPngFile(logoFile, (tft.width() / 2) - (pngWidth / 2), (tft.height() / 2) - pngHeight); + getPngSize(logoFile, &pngWidth, &pngHeight); + tft.drawPngFile(logoFile, (tft.width() / 2) - (pngWidth / 2), (tft.height() / 2) - pngHeight); - tft.setTextSize(1); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); - tft.drawCenterString("Map data from OpenStreetMap.", tft.width() >> 1, TFT_HEIGHT - 120); - tft.drawCenterString("(c) OpenStreetMap", tft.width() >> 1, TFT_HEIGHT - 110); - tft.drawCenterString("(c) OpenStreetMap contributors", tft.width() >> 1, TFT_HEIGHT - 100); + tft.drawCenterString("Map data from OpenStreetMap.", tft.width() >> 1, TFT_HEIGHT - 120); + tft.drawCenterString("(c) OpenStreetMap", tft.width() >> 1, TFT_HEIGHT - 110); + tft.drawCenterString("(c) OpenStreetMap contributors", tft.width() >> 1, TFT_HEIGHT - 100); - char statusString[50] = ""; - tft.setTextColor(TFT_YELLOW, TFT_BLACK); + char statusString[50] = ""; + tft.setTextColor(TFT_YELLOW, TFT_BLACK); - memset(&statusString[0], 0, sizeof(statusString)); - sprintf(statusString, statusLine1, ESP.getChipModel(), ESP.getCpuFreqMHz()); - tft.drawString(statusString, 0, TFT_HEIGHT - 50); + memset(&statusString[0], 0, sizeof(statusString)); + sprintf(statusString, statusLine1, ESP.getChipModel(), ESP.getCpuFreqMHz()); + tft.drawString(statusString, 0, TFT_HEIGHT - 50); - memset(&statusString[0], 0, sizeof(statusString)); - sprintf(statusString, statusLine2, (ESP.getFreeHeap() / 1024), (ESP.getFreeHeap() * 100) / ESP.getHeapSize()); - tft.drawString(statusString, 0, TFT_HEIGHT - 40); + memset(&statusString[0], 0, sizeof(statusString)); + sprintf(statusString, statusLine2, (ESP.getFreeHeap() / 1024), (ESP.getFreeHeap() * 100) / ESP.getHeapSize()); + tft.drawString(statusString, 0, TFT_HEIGHT - 40); - memset(&statusString[0], 0, sizeof(statusString)); - sprintf(statusString, statusLine3, ESP.getPsramSize(), ESP.getPsramSize() - ESP.getFreePsram()); - tft.drawString(statusString, 0, TFT_HEIGHT - 30); + memset(&statusString[0], 0, sizeof(statusString)); + sprintf(statusString, statusLine3, ESP.getPsramSize(), ESP.getPsramSize() - ESP.getFreePsram()); + tft.drawString(statusString, 0, TFT_HEIGHT - 30); - memset(&statusString[0], 0, sizeof(statusString)); - sprintf(statusString, statusLine4, String(VERSION), String(REVISION)); - tft.drawString(statusString, 0, TFT_HEIGHT - 20); + memset(&statusString[0], 0, sizeof(statusString)); + sprintf(statusString, statusLine4, String(VERSION), String(REVISION)); + tft.drawString(statusString, 0, TFT_HEIGHT - 20); - memset(&statusString[0], 0, sizeof(statusString)); - sprintf(statusString, statusLine5, String(FLAVOR)); - tft.drawString(statusString, 0, TFT_HEIGHT - 10); + memset(&statusString[0], 0, sizeof(statusString)); + sprintf(statusString, statusLine5, String(FLAVOR)); + tft.drawString(statusString, 0, TFT_HEIGHT - 10); - memset(&statusString[0], 0, sizeof(statusString)); - tft.setTextColor(TFT_WHITE, TFT_BLACK); + memset(&statusString[0], 0, sizeof(statusString)); + tft.setTextColor(TFT_WHITE, TFT_BLACK); - const uint8_t maxBrightness = 255; + const uint8_t maxBrightness = 255; - for (uint8_t fadeIn = 0; fadeIn <= (maxBrightness - 1); fadeIn++) - { - tft.setBrightness(fadeIn); - millisActual = millis(); - while (millis() < millisActual + 15); - } + for (uint8_t fadeIn = 0; fadeIn <= (maxBrightness - 1); fadeIn++) + { + tft.setBrightness(fadeIn); + millisActual = millis(); + while (millis() < millisActual + 15); + } - millisActual = millis(); - while (millis() < millisActual + 100); + millisActual = millis(); + while (millis() < millisActual + 100); - for (uint8_t fadeOut = maxBrightness; fadeOut > 0; fadeOut--) - { - tft.setBrightness(fadeOut); - millisActual = millis(); - while (millis() < millisActual + 15); - } + for (uint8_t fadeOut = maxBrightness; fadeOut > 0; fadeOut--) + { + tft.setBrightness(fadeOut); + millisActual = millis(); + while (millis() < millisActual + 15); + } - tft.fillScreen(TFT_BLACK); + tft.fillScreen(TFT_BLACK); - while (millis() < millisActual + 100); + while (millis() < millisActual + 100); - tft.setBrightness(defBright); + tft.setBrightness(defBright); } diff --git a/lib/gui/src/splashScr.hpp b/lib/gui/src/splashScr.hpp index 8c8a04fe..178f0b10 100644 --- a/lib/gui/src/splashScr.hpp +++ b/lib/gui/src/splashScr.hpp @@ -2,8 +2,8 @@ * @file splashScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Splash screen - NOT LVGL - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -14,14 +14,15 @@ #include "gps.hpp" #ifdef LARGE_SCREEN -static const char* logoFile PROGMEM = "/spiffs/LOGO_LARGE.png"; + static const char* logoFile PROGMEM = "/spiffs/LOGO_LARGE.png"; /**< Path to the large logo image for large screens */ #else -static const char* logoFile PROGMEM = "/spiffs/LOGO_SMALL.png"; + static const char* logoFile PROGMEM = "/spiffs/LOGO_SMALL.png"; /**< Path to the small logo image for smaller screens */ #endif -static const char* statusLine1 PROGMEM = "Model:%s %dMhz"; -static const char* statusLine2 PROGMEM = "Free mem:%dK %d%%"; -static const char* statusLine3 PROGMEM = "PSRAM: %d - Used PSRAM: %d"; -static const char* statusLine4 PROGMEM = "Firmware v.%s rev.%s"; -static const char* statusLine5 PROGMEM = "ENV: %s"; + +static const char* statusLine1 PROGMEM = "Model:%s %dMhz"; /**< Format string for model and CPU frequency */ +static const char* statusLine2 PROGMEM = "Free mem:%dK %d%%"; /**< Format string for free memory in KB and percentage */ +static const char* statusLine3 PROGMEM = "PSRAM: %d - Used PSRAM: %d"; /**< Format string for PSRAM total and used */ +static const char* statusLine4 PROGMEM = "Firmware v.%s rev.%s"; /**< Format string for firmware version and revision */ +static const char* statusLine5 PROGMEM = "ENV: %s"; /**< Format string for environment information */ void splashScreen(); diff --git a/lib/gui/src/upgradeScr.cpp b/lib/gui/src/upgradeScr.cpp index bbd86f1b..e13ee44d 100644 --- a/lib/gui/src/upgradeScr.cpp +++ b/lib/gui/src/upgradeScr.cpp @@ -2,8 +2,8 @@ * @file upgradeScr.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL Firmware upgrade messages - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "upgradeScr.hpp" @@ -16,73 +16,71 @@ /** * @brief Upgrade Back Message Event - * */ void msgBackEvent(lv_event_t *event) { - lv_screen_load(deviceSettingsScreen); + lv_screen_load(deviceSettingsScreen); } /** * @brief Upgrade Message Event - * */ void msgUpgrdEvent(lv_event_t *event) { - lv_obj_add_flag(btnMsgBack,LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(btnMsgUpgrade, LV_OBJ_FLAG_HIDDEN); - onUpgrdStart(); - onUpgrdEnd(); + lv_obj_add_flag(btnMsgBack,LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(btnMsgUpgrade, LV_OBJ_FLAG_HIDDEN); + onUpgrdStart(); + onUpgrdEnd(); } - /** * @brief Create Upgrade Message - * + * + * @details Creates the firmware upgrade message dialog */ void createMsgUpgrade() { - msgUpgrade = lv_obj_create(NULL); - lv_obj_t *msgBox = lv_msgbox_create(msgUpgrade); - lv_msgbox_add_title(msgBox, "Firmware Upgrade"); - lv_obj_t *content = lv_msgbox_get_content(msgBox); - lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_set_height(msgBox,200); + msgUpgrade = lv_obj_create(NULL); + lv_obj_t *msgBox = lv_msgbox_create(msgUpgrade); + lv_msgbox_add_title(msgBox, "Firmware Upgrade"); + lv_obj_t *content = lv_msgbox_get_content(msgBox); + lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_height(msgBox,200); - lv_obj_t *contText = lv_obj_create(content); - lv_obj_set_size(contText, lv_pct(100), LV_SIZE_CONTENT); - lv_obj_set_flex_flow(contText, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(contText, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(contText,LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_border_color(contText,lv_color_black(),0); + lv_obj_t *contText = lv_obj_create(content); + lv_obj_set_size(contText, lv_pct(100), LV_SIZE_CONTENT); + lv_obj_set_flex_flow(contText, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(contText, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(contText,LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_border_color(contText,lv_color_black(),0); - msgUprgdText = lv_label_create(contText); - lv_label_set_text_static(msgUprgdText,""); - lv_obj_set_style_text_font(msgUprgdText, fontLarge, 0); + msgUprgdText = lv_label_create(contText); + lv_label_set_text_static(msgUprgdText,""); + lv_obj_set_style_text_font(msgUprgdText, fontLarge, 0); - contMeter = lv_obj_create(content); - lv_obj_set_size(contMeter, lv_pct(100), LV_SIZE_CONTENT); - lv_obj_set_flex_flow(contMeter, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(contMeter, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(contMeter,LV_OBJ_FLAG_SCROLLABLE); - lv_obj_set_style_border_color(contMeter,lv_color_black(),0); - lv_obj_add_flag(contMeter,LV_OBJ_FLAG_HIDDEN); + contMeter = lv_obj_create(content); + lv_obj_set_size(contMeter, lv_pct(100), LV_SIZE_CONTENT); + lv_obj_set_flex_flow(contMeter, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(contMeter, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(contMeter,LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_border_color(contMeter,lv_color_black(),0); + lv_obj_add_flag(contMeter,LV_OBJ_FLAG_HIDDEN); - static uint32_t objectColor = 0x303030; - static lv_style_t styleBtn; - lv_style_init(&styleBtn); - lv_style_set_bg_color(&styleBtn, lv_color_hex(objectColor)); - lv_style_set_border_color(&styleBtn, lv_color_hex(objectColor)); + static uint32_t objectColor = 0x303030; + static lv_style_t styleBtn; + lv_style_init(&styleBtn); + lv_style_set_bg_color(&styleBtn, lv_color_hex(objectColor)); + lv_style_set_border_color(&styleBtn, lv_color_hex(objectColor)); - btnMsgBack = lv_msgbox_add_footer_button(msgBox, "Back"); - lv_obj_add_event_cb(btnMsgBack, msgBackEvent, LV_EVENT_CLICKED, NULL); - lv_obj_set_style_text_font(btnMsgBack, fontLarge, 0); - lv_obj_add_style(btnMsgBack, &styleBtn, 0); + btnMsgBack = lv_msgbox_add_footer_button(msgBox, "Back"); + lv_obj_add_event_cb(btnMsgBack, msgBackEvent, LV_EVENT_CLICKED, NULL); + lv_obj_set_style_text_font(btnMsgBack, fontLarge, 0); + lv_obj_add_style(btnMsgBack, &styleBtn, 0); - btnMsgUpgrade = lv_msgbox_add_footer_button(msgBox, "UPGRADE"); - lv_obj_add_event_cb(btnMsgUpgrade, msgUpgrdEvent, LV_EVENT_CLICKED, NULL); - lv_obj_set_style_text_font(btnMsgUpgrade, fontLarge, 0); - lv_obj_add_style(btnMsgUpgrade, &styleBtn, 0); - lv_obj_add_flag(btnMsgUpgrade,LV_OBJ_FLAG_HIDDEN); + btnMsgUpgrade = lv_msgbox_add_footer_button(msgBox, "UPGRADE"); + lv_obj_add_event_cb(btnMsgUpgrade, msgUpgrdEvent, LV_EVENT_CLICKED, NULL); + lv_obj_set_style_text_font(btnMsgUpgrade, fontLarge, 0); + lv_obj_add_style(btnMsgUpgrade, &styleBtn, 0); + lv_obj_add_flag(btnMsgUpgrade,LV_OBJ_FLAG_HIDDEN); } \ No newline at end of file diff --git a/lib/gui/src/upgradeScr.hpp b/lib/gui/src/upgradeScr.hpp index 20631466..1788499e 100644 --- a/lib/gui/src/upgradeScr.hpp +++ b/lib/gui/src/upgradeScr.hpp @@ -2,8 +2,8 @@ * @file upgradeScr.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL Firmware upgrade messages - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -12,11 +12,11 @@ #include "firmUpgrade.hpp" #include "globalGuiDef.h" -extern lv_obj_t *msgUpgrade; -extern lv_obj_t *msgUprgdText; -extern lv_obj_t *btnMsgBack; -extern lv_obj_t *btnMsgUpgrade; -extern lv_obj_t *contMeter; +extern lv_obj_t *msgUpgrade; /**< Upgrade message object */ +extern lv_obj_t *msgUprgdText; /**< Text label inside the upgrade message */ +extern lv_obj_t *btnMsgBack; /**< Back button inside the message dialog */ +extern lv_obj_t *btnMsgUpgrade; /**< Upgrade action button inside the message dialog */ +extern lv_obj_t *contMeter; /**< Meter container object */ void msgBackEvent(lv_event_t *event); void msgUpgrdEvent(lv_event_t *event); diff --git a/lib/gui/src/widgets.cpp b/lib/gui/src/widgets.cpp index 71c60a61..787a7425 100644 --- a/lib/gui/src/widgets.cpp +++ b/lib/gui/src/widgets.cpp @@ -2,8 +2,8 @@ * @file widgets.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Widgets - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "widgets.hpp" @@ -16,240 +16,408 @@ lv_obj_t *altitude; lv_obj_t *speedLabel; lv_obj_t *sunriseLabel; lv_obj_t *sunsetLabel; +lv_obj_t *navArrow; +lv_obj_t *zoomLabel; +lv_obj_t *zoomWidget; +lv_obj_t *mapSpeedLabel; +lv_obj_t *mapSpeed; +lv_obj_t *miniCompass; +lv_obj_t *mapCompassImg; +lv_obj_t *scaleWidget; +lv_obj_t *scaleLabel; +lv_obj_t *turnByTurn; +lv_obj_t *turnDistLabel; +lv_obj_t *turnImg; + +LV_IMG_DECLARE(straight); +LV_IMG_DECLARE(slleft); +LV_IMG_DECLARE(slright); +LV_IMG_DECLARE(tleft); +LV_IMG_DECLARE(tright); +LV_IMG_DECLARE(uleft); +LV_IMG_DECLARE(uright); +LV_IMG_DECLARE(finish); extern Gps gps; /** * @brief Edit Screen Event (drag widgets) - * - * @param event + * + * @param event Pointer to the LVGL event structure */ void editWidget(lv_event_t *event) { - lv_event_code_t code = lv_event_get_code(event); - - if (code == LV_EVENT_LONG_PRESSED) - canMoveWidget = !canMoveWidget; + lv_event_code_t code = lv_event_get_code(event); + + if (code == LV_EVENT_LONG_PRESSED) + canMoveWidget = !canMoveWidget; } /** * @brief Unselect widget * - * @param event + * @param event Pointer to the LVGL event structure */ void unselectWidget(lv_event_t *event) { - if (canMoveWidget) - { - lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(event); - if (widgetSelected) + if (canMoveWidget) { - objUnselect(obj); - char *widget = (char *)lv_event_get_user_data(event); - saveWidgetPos(widget, newX, newY); - canMoveWidget = !canMoveWidget; - lv_obj_add_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); - widgetSelected = false; + lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(event); + if (widgetSelected) + { + objUnselect(obj); + char *widget = (char *)lv_event_get_user_data(event); + saveWidgetPos(widget, newX, newY); + canMoveWidget = !canMoveWidget; + lv_obj_add_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); + widgetSelected = false; + } + isScrolled = true; } - isScrolled = true; - } } /** * @brief Drag widget event * - * @param event + * @param event Pointer to the LVGL event structure */ void dragWidget(lv_event_t *event) { - if (canMoveWidget) - { - isScrolled = false; - lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(event); - if (!widgetSelected) + if (canMoveWidget) { - objSelect(obj); - lv_obj_clear_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); - widgetSelected = true; - } + isScrolled = false; + lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(event); + if (!widgetSelected) + { + objSelect(obj); + lv_obj_clear_flag(tilesScreen, LV_OBJ_FLAG_SCROLLABLE); + widgetSelected = true; + } - lv_indev_t *indev = lv_indev_get_act(); - if (indev == NULL) - return; - - lv_point_t vect; - lv_indev_get_vect(indev, &vect); - - lv_coord_t x = lv_obj_get_x(obj) + vect.x; - lv_coord_t y = lv_obj_get_y(obj) + vect.y; - lv_coord_t width = lv_obj_get_width(obj); - lv_coord_t height = lv_obj_get_height(obj); - - // Limit drag area - if (x > 0 && y > 0 && (x + width) < TFT_WIDTH && (y + height) < TFT_HEIGHT - 25) - { - lv_obj_set_pos(obj, x, y); - newX = x; - newY = y; + lv_indev_t *indev = lv_indev_get_act(); + if (indev == NULL) + return; + + lv_point_t vect; + lv_indev_get_vect(indev, &vect); + + lv_coord_t x = lv_obj_get_x(obj) + vect.x; + lv_coord_t y = lv_obj_get_y(obj) + vect.y; + lv_coord_t width = lv_obj_get_width(obj); + lv_coord_t height = lv_obj_get_height(obj); + + // Limit drag area + if (x > 0 && y > 0 && (x + width) < TFT_WIDTH && (y + height) < TFT_HEIGHT - 25) + { + lv_obj_set_pos(obj, x, y); + newX = x; + newY = y; + } } - } } /** * @brief Position widget * - * @param screen + * @param screen Pointer to the LVGL screen object where the widget will be created */ -void positionWidget(_lv_obj_t *screen) +void positionWidget(lv_obj_t *screen) { - lv_obj_t *obj = lv_obj_create(screen); - lv_obj_set_height(obj,40); - lv_obj_set_pos(obj, coordPosX, coordPosY); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); - latitude = lv_label_create(obj); - lv_obj_set_style_text_font(latitude, fontMedium, 0); - lv_label_set_text_fmt(latitude, "%s", latFormatString(gps.gpsData.latitude)); - longitude = lv_label_create(obj); - lv_obj_set_style_text_font(longitude, fontMedium, 0); - lv_label_set_text_fmt(longitude, "%s", lonFormatString(gps.gpsData.longitude)); - lv_obj_t *img = lv_img_create(obj); - lv_img_set_src(img, positionIconFile); - lv_img_set_zoom(img,iconScale); - lv_obj_update_layout(latitude); - lv_obj_update_layout(img); - lv_obj_set_width(obj, lv_obj_get_width(latitude) + 40); - lv_obj_align(latitude, LV_ALIGN_TOP_LEFT, 15, -12); - lv_obj_align(longitude, LV_ALIGN_TOP_LEFT, 15, 3); - lv_obj_align(img, LV_ALIGN_TOP_LEFT, -15, -10); - objUnselect(obj); - lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); - lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Coords_"); - lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Coords_"); + lv_obj_t *obj = lv_obj_create(screen); + lv_obj_set_height(obj,40); + lv_obj_set_pos(obj, coordPosX, coordPosY); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + latitude = lv_label_create(obj); + lv_obj_set_style_text_font(latitude, fontMedium, 0); + lv_label_set_text_fmt(latitude, "%s", latFormatString(gps.gpsData.latitude)); + longitude = lv_label_create(obj); + lv_obj_set_style_text_font(longitude, fontMedium, 0); + lv_label_set_text_fmt(longitude, "%s", lonFormatString(gps.gpsData.longitude)); + lv_obj_t *img = lv_img_create(obj); + lv_img_set_src(img, positionIconFile); + lv_img_set_zoom(img,iconScale); + lv_obj_update_layout(latitude); + lv_obj_update_layout(img); + lv_obj_set_width(obj, lv_obj_get_width(latitude) + 40); + lv_obj_align(latitude, LV_ALIGN_TOP_LEFT, 15, -12); + lv_obj_align(longitude, LV_ALIGN_TOP_LEFT, 15, 3); + lv_obj_align(img, LV_ALIGN_TOP_LEFT, -15, -10); + objUnselect(obj); + lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); + lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Coords_"); + lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Coords_"); } /** * @brief Compass widget * - * @param screen + * @param screen Pointer to the LVGL screen object where the widget will be created */ -void compassWidget(_lv_obj_t *screen) +void compassWidget(lv_obj_t *screen) { - lv_obj_t *obj = lv_obj_create(screen); - lv_obj_set_size(obj, 200 * scale, 200 * scale); - lv_obj_set_pos(obj, compassPosX, compassPosY); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_t *img = lv_img_create(obj); - lv_img_set_src(img, arrowIconFile); - lv_obj_align(img, LV_ALIGN_CENTER, 0, -30); - lv_img_set_zoom(img,iconScale); - lv_obj_update_layout(img); - LV_IMG_DECLARE(bruj); - compassImg = lv_img_create(obj); - lv_img_set_src(compassImg, &bruj); - lv_img_set_zoom(compassImg,iconScale); - lv_obj_update_layout(compassImg); - lv_obj_align_to(compassImg, obj, LV_ALIGN_CENTER, 0, 0); - lv_img_set_pivot(compassImg, 100, 100) ; - compassHeading = lv_label_create(obj); - lv_obj_set_height(compassHeading,38); - lv_obj_align(compassHeading, LV_ALIGN_CENTER, 0, 20); - lv_obj_set_style_text_font(compassHeading, fontVeryLarge, 0); - lv_label_set_text_static(compassHeading, "---\xC2\xB0"); - objUnselect(obj); - lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); - lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Compass_"); - lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Compass_"); + lv_obj_t *obj = lv_obj_create(screen); + lv_obj_set_size(obj, 200 * scale, 200 * scale); + lv_obj_set_pos(obj, compassPosX, compassPosY); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_t *img = lv_img_create(obj); + lv_img_set_src(img, arrowIconFile); + lv_obj_align(img, LV_ALIGN_CENTER, 0, -30); + lv_img_set_zoom(img,iconScale); + lv_obj_update_layout(img); + LV_IMG_DECLARE(bruj); + compassImg = lv_img_create(obj); + lv_img_set_src(compassImg, &bruj); + lv_img_set_zoom(compassImg,iconScale); + lv_obj_update_layout(compassImg); + lv_obj_align_to(compassImg, obj, LV_ALIGN_CENTER, 0, 0); + lv_img_set_pivot(compassImg, 100, 100) ; + compassHeading = lv_label_create(obj); + lv_obj_set_height(compassHeading,38); + lv_obj_align(compassHeading, LV_ALIGN_CENTER, 0, 20); + lv_obj_set_style_text_font(compassHeading, fontVeryLarge, 0); + lv_label_set_text_static(compassHeading, "---\xC2\xB0"); + objUnselect(obj); + lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); + lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Compass_"); + lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Compass_"); } /** - * @brief Compass widget + * @brief Altitude widget * - * @param screen + * @param screen Pointer to the LVGL screen object where the widget will be created */ -void altitudeWidget(_lv_obj_t *screen) +void altitudeWidget(lv_obj_t *screen) { - lv_obj_t *obj = lv_obj_create(screen); - lv_obj_set_height(obj, 40 * scale); - lv_obj_set_pos(obj, altitudePosX, altitudePosY); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); - altitude = lv_label_create(obj); - lv_obj_set_style_text_font(altitude, fontLargeMedium, 0); - lv_label_set_text_static(altitude, "0 m."); - lv_obj_t *img = lv_img_create(obj); - lv_img_set_src(img, altitudeIconFile); - lv_img_set_zoom(img,iconScale); - lv_obj_update_layout(altitude); - lv_obj_update_layout(img); - lv_obj_set_width(obj, 140); - lv_obj_align(img, LV_ALIGN_LEFT_MID, -15, 0); - lv_obj_align(altitude, LV_ALIGN_CENTER, 15, 0); - objUnselect(obj); - lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); - lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Altitude_"); - lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Altitude_"); + lv_obj_t *obj = lv_obj_create(screen); + lv_obj_set_height(obj, 40 * scale); + lv_obj_set_pos(obj, altitudePosX, altitudePosY); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_t *img = lv_img_create(obj); + lv_img_set_src(img, altitudeIconFile); + lv_img_set_zoom(img,iconScale); + lv_obj_update_layout(img); + lv_obj_set_width(obj, 150); + altitude = lv_label_create(obj); + lv_obj_set_style_text_font(altitude, fontLargeMedium, 0); + lv_label_set_text_static(altitude, "0 m."); + lv_obj_update_layout(altitude); + objUnselect(obj); + lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); + lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Altitude_"); + lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Altitude_"); } /** * @brief Speed widget * - * @param screen + * @param screen Pointer to the LVGL screen object where the widget will be created */ -void speedWidget(_lv_obj_t *screen) +void speedWidget(lv_obj_t *screen) { - lv_obj_t *obj = lv_obj_create(screen); - lv_obj_set_height(obj, 40 * scale); - lv_obj_set_pos(obj, speedPosX, speedPosY); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); - speedLabel = lv_label_create(obj); - lv_obj_set_style_text_font(speedLabel, fontLargeMedium, 0); - lv_label_set_text_static(speedLabel, "0 Km/h"); - lv_obj_t *img = lv_img_create(obj); - lv_img_set_src(img, speedIconFile); - lv_img_set_zoom(img,iconScale); - lv_obj_update_layout(speedLabel); - lv_obj_update_layout(img); - lv_obj_set_width(obj, 160); - lv_obj_align(img, LV_ALIGN_LEFT_MID, -10, 0); - lv_obj_align(speedLabel, LV_ALIGN_CENTER, 20, 0); - objUnselect(obj); - lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); - lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Speed_"); - lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Speed_"); + lv_obj_t *obj = lv_obj_create(screen); + lv_obj_set_height(obj, 40 * scale); + lv_obj_set_pos(obj, speedPosX, speedPosY); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_t *img = lv_img_create(obj); + lv_img_set_src(img, speedIconFile); + lv_img_set_zoom(img,iconScale); + lv_obj_update_layout(img); + lv_obj_set_width(obj, 170); + speedLabel = lv_label_create(obj); + lv_obj_set_style_text_font(speedLabel, fontLargeMedium, 0); + lv_label_set_text_static(speedLabel, "0 Km/h"); + lv_obj_update_layout(speedLabel); + objUnselect(obj); + lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); + lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Speed_"); + lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Speed_"); } /** * @brief Sunrise/Sunset widget * - * @param screen + * @param screen Pointer to the LVGL screen object where the widget will be created */ -void sunWidget(_lv_obj_t *screen) +void sunWidget(lv_obj_t *screen) { - lv_obj_t *obj = lv_obj_create(screen); - lv_obj_set_size(obj, 70, 60); - lv_obj_set_pos(obj, sunPosX, sunPosY); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); - - sunriseLabel = lv_label_create(obj); - lv_obj_align(sunriseLabel, LV_ALIGN_TOP_RIGHT, 16, -2); - lv_label_set_text_static(sunriseLabel, ""); - sunsetLabel = lv_label_create(obj); - lv_obj_align(sunsetLabel,LV_ALIGN_BOTTOM_RIGHT, 16, 10); - lv_label_set_text_static(sunsetLabel, ""); - - lv_obj_t *img; - img = lv_img_create(obj); - lv_img_set_src(img, sunriseIconFile); - lv_img_set_zoom(img,iconScale); - lv_obj_update_layout(img); - lv_obj_align(img, LV_ALIGN_TOP_LEFT, -10, -10); - img = lv_img_create(obj); - lv_img_set_src(img, sunsetIconFile); - lv_img_set_zoom(img,iconScale); - lv_obj_update_layout(img); - lv_obj_align(img, LV_ALIGN_BOTTOM_LEFT, -10, 10); - - objUnselect(obj); - lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); - lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Sun_"); - lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Sun_"); + lv_obj_t *obj = lv_obj_create(screen); + lv_obj_set_size(obj, 100, 60 * scale); + lv_obj_set_pos(obj, sunPosX, sunPosY); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_WRAP); + lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_t *img; + img = lv_img_create(obj); + lv_img_set_src(img, sunriseIconFile); + lv_img_set_zoom(img,iconScale); + lv_obj_update_layout(img); + sunriseLabel = lv_label_create(obj); + lv_label_set_text_static(sunriseLabel, "--:--"); + img = lv_img_create(obj); + lv_obj_update_layout(sunriseLabel); + lv_img_set_src(img, sunsetIconFile); + lv_img_set_zoom(img,iconScale); + lv_obj_update_layout(img); + sunsetLabel = lv_label_create(obj); + lv_label_set_text_static(sunsetLabel, "--:--"); + lv_obj_update_layout(sunsetLabel); + objUnselect(obj); + lv_obj_add_event_cb(obj, editWidget, LV_EVENT_LONG_PRESSED, NULL); + lv_obj_add_event_cb(obj, dragWidget, LV_EVENT_PRESSING, (char *)"Sun_"); + lv_obj_add_event_cb(obj, unselectWidget, LV_EVENT_RELEASED, (char *)"Sun_"); +} + +/** + * @brief Map navigation arrow widget + * + * @param screen Pointer to the LVGL screen object where the widget will be created + */ +void navArrowWidget(lv_obj_t *screen) +{ + navArrow = lv_img_create(screen); + lv_img_set_src(navArrow, navArrowIconFile); + lv_obj_align(navArrow, LV_ALIGN_CENTER, 0, 0); + lv_obj_add_flag(navArrow, LV_OBJ_FLAG_HIDDEN); +} + +/** + * @brief Map zoom widget + * + * @param screen Pointer to the LVGL screen object where the widget will be created + */ +void mapZoomWidget(lv_obj_t *screen) +{ + zoomWidget = lv_obj_create(screen); + lv_obj_set_size(zoomWidget, 64, 32); + lv_obj_clear_flag(zoomWidget, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(zoomWidget, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(zoomWidget, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_bg_color(zoomWidget, lv_color_black(), 0); + lv_obj_set_style_bg_opa(zoomWidget, 128, 0); + lv_obj_set_style_border_color(zoomWidget, lv_color_black(), 0); + lv_obj_set_style_border_width(zoomWidget, 1, 0); + lv_obj_set_style_border_opa(zoomWidget,128,0); + lv_obj_t *img = lv_img_create(zoomWidget); + lv_img_set_src(img, zoomIconFile); + zoomLabel = lv_label_create(zoomWidget); + lv_obj_set_style_text_font(zoomLabel, &lv_font_montserrat_20, 0); + lv_label_set_text_fmt(zoomLabel, "%2d", zoom); + lv_obj_add_flag(zoomWidget,LV_OBJ_FLAG_HIDDEN); +} + +/** + * @brief Create and configure the map speed widget. + * + * @param screen Pointer to the LVGL screen object where the map speed widget will be created. + */ +void mapSpeedWidget(lv_obj_t *screen) +{ + mapSpeed = lv_obj_create(screen); + lv_obj_set_size(mapSpeed, 100, 32); + lv_obj_clear_flag(mapSpeed, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(mapSpeed, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(mapSpeed, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_bg_color(mapSpeed, lv_color_black(), 0); + lv_obj_set_style_bg_opa(mapSpeed, 128, 0); + lv_obj_set_style_border_color(mapSpeed, lv_color_black(), 0); + lv_obj_set_style_border_width(mapSpeed, 1, 0); + lv_obj_set_style_border_opa(mapSpeed,128,0); + lv_obj_align(mapSpeed, LV_ALIGN_BOTTOM_LEFT, 0, -1); + lv_obj_t *img = lv_img_create(mapSpeed); + lv_img_set_src(img, mapSpeedIconFile); + mapSpeedLabel = lv_label_create(mapSpeed); + lv_obj_set_style_text_font(mapSpeedLabel, &lv_font_montserrat_20, 0); + lv_label_set_text_fmt(mapSpeedLabel, "%3d", 0); + lv_obj_add_flag(mapSpeed,LV_OBJ_FLAG_HIDDEN); +} + +/** + * @brief Map compass widget + * + * @param screen Pointer to the LVGL screen object where the map compass widget will be created. + */ +void mapCompassWidget(lv_obj_t *screen) +{ + miniCompass = lv_obj_create(screen); + lv_obj_set_size(miniCompass, 50, 50); + lv_obj_clear_flag(miniCompass, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_bg_color(miniCompass, lv_color_black(), 0); + lv_obj_set_style_bg_opa(miniCompass, 128, 0); + lv_obj_set_style_border_color(miniCompass, lv_color_black(), 0); + lv_obj_set_style_border_width(miniCompass, 1, 0); + lv_obj_set_style_border_opa(miniCompass,128,0); + lv_obj_set_align(miniCompass, LV_ALIGN_TOP_RIGHT); + mapCompassImg = lv_img_create(miniCompass); + LV_IMG_DECLARE(compassMap); + lv_img_set_src(mapCompassImg, &compassMap); + lv_obj_set_align(mapCompassImg, LV_ALIGN_CENTER); + lv_obj_add_flag(miniCompass,LV_OBJ_FLAG_HIDDEN); +} + +/** + * @brief Map scale widget + * + * @param screen Pointer to the LVGL screen object where the map scale widget will be created. + */ +void mapScaleWidget(lv_obj_t *screen) +{ + scaleWidget = lv_obj_create(screen); + lv_obj_set_size(scaleWidget, 100, 32); + lv_obj_clear_flag(scaleWidget, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(scaleWidget, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(scaleWidget, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_bg_color(scaleWidget, lv_color_black(), 0); + lv_obj_set_style_bg_opa(scaleWidget, 128, 0); + lv_obj_set_style_border_color(scaleWidget, lv_color_black(), 0); + lv_obj_set_style_border_width(scaleWidget, 1, 0); + lv_obj_set_style_border_opa(scaleWidget,128,0); + lv_obj_align(scaleWidget, LV_ALIGN_BOTTOM_LEFT, 102, -1); + lv_obj_add_flag(scaleWidget,LV_OBJ_FLAG_HIDDEN); + scaleLabel = lv_label_create(scaleWidget); + lv_obj_set_style_text_font(scaleLabel, &lv_font_montserrat_12, 0); + lv_label_set_text_fmt(scaleLabel, "%s", map_scale[zoom]); + lv_obj_t * scale = lv_scale_create(scaleWidget); + lv_scale_set_mode(scale, LV_SCALE_MODE_HORIZONTAL_BOTTOM); + lv_scale_set_label_show(scale, false); + lv_obj_set_size(scale, 60, 10); + lv_scale_set_total_tick_count(scale, 2); + lv_scale_set_major_tick_every(scale, 2); + lv_scale_set_range(scale, 10, 20); +} + +/** + * @brief Turn By Turn Navigation widget + * + * @param screen Pointer to the LVGL screen object where the navigationwidget will be created. + */ +void turnByTurnWidget(lv_obj_t *screen) +{ + turnByTurn = lv_obj_create(screen); + lv_obj_set_size(turnByTurn,60,100); + lv_obj_clear_flag(turnByTurn, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(turnByTurn, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(turnByTurn, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_bg_opa(turnByTurn, 128, 0); + lv_obj_set_style_border_color(turnByTurn, lv_color_black(), 0); + lv_obj_set_style_border_width(turnByTurn, 1, 0); + lv_obj_set_style_border_opa(turnByTurn,128,0); + lv_obj_align(turnByTurn, LV_ALIGN_TOP_RIGHT, 0, 60); + + + turnImg = lv_img_create(turnByTurn); + lv_img_set_src(turnImg, &straight); + + turnDistLabel = lv_label_create(turnByTurn); + lv_obj_set_style_text_font(turnDistLabel, &lv_font_montserrat_18, 0); + lv_label_set_text_fmt(turnDistLabel, "%4d", 0); + lv_obj_t *obj = lv_label_create(turnByTurn); + lv_obj_set_style_text_font(obj, &lv_font_montserrat_18, 0); + lv_label_set_text_static(obj,"m."); + + lv_obj_add_flag(turnByTurn,LV_OBJ_FLAG_HIDDEN); } \ No newline at end of file diff --git a/lib/gui/src/widgets.hpp b/lib/gui/src/widgets.hpp index 39ab2d9b..7bac9c71 100644 --- a/lib/gui/src/widgets.hpp +++ b/lib/gui/src/widgets.hpp @@ -2,8 +2,8 @@ * @file widgets.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL - Widgets - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,40 +11,64 @@ #include "lvglFuncs.hpp" #include "gpsMath.hpp" #include "settings.hpp" +#include "mapVars.h" /** * @brief Widget objects * + * @details Declarations for UI widget objects */ -extern lv_obj_t *latitude; -extern lv_obj_t *longitude; -extern lv_obj_t *compassHeading; -extern lv_obj_t *compassImg; -extern lv_obj_t *altitude; -extern lv_obj_t *speedLabel; -extern lv_obj_t *sunriseLabel; -extern lv_obj_t *sunsetLabel; +extern lv_obj_t *latitude; /**< Latitude label */ +extern lv_obj_t *longitude; /**< Longitude label */ +extern lv_obj_t *compassHeading; /**< Compass heading label */ +extern lv_obj_t *compassImg; /**< Compass image object */ +extern lv_obj_t *altitude; /**< Altitude label */ +extern lv_obj_t *speedLabel; /**< Speed label */ +extern lv_obj_t *sunriseLabel; /**< Sunrise time label */ +extern lv_obj_t *sunsetLabel; /**< Sunset time label */ +extern lv_obj_t *navArrow; /**< Navigation arrow object */ +extern lv_obj_t *zoomLabel; /**< Zoom level label */ +extern lv_obj_t *zoomWidget; /**< Zoom widget object */ +extern lv_obj_t *mapSpeedLabel; /**< Map speed label */ +extern lv_obj_t *mapSpeed; /**< Map speed value object */ +extern lv_obj_t *miniCompass; /**< Mini compass widget */ +extern lv_obj_t *mapCompassImg; /**< Map compass image object */ +extern lv_obj_t *scaleWidget; /**< Scale widget object */ +extern lv_obj_t *scaleLabel; /**< Scale label */ +extern lv_obj_t *turnByTurn; /**< Turn-by-Turn navigation widget*/ +extern lv_obj_t *turnDistLabel; /**< Label object showing turn distance */ +extern lv_obj_t *turnImg; /**< Image object for turn indication */ void editWidget(lv_event_t *event); void dragWidget(lv_event_t *event); void unselectWidget(lv_event_t *event); -static bool widgetSelected = false; -static bool canMoveWidget = false; -static lv_coord_t newX = 0; -static lv_coord_t newY = 0; - -extern bool isScrolled; // Flag to indicate when tileview was scrolled - -static const char *arrowIconFile PROGMEM = "/arrow.bin"; // Compass Arrow Icon -static const char *positionIconFile PROGMEM = "/pin.bin"; // Position Icon -static const char *altitudeIconFile PROGMEM = "/altit.bin"; // Altitude Icon -static const char *speedIconFile PROGMEM = "/speed.bin"; // Speed Icon -static const char *sunriseIconFile PROGMEM = "/sunrise.bin"; // Sunrise Icon -static const char *sunsetIconFile PROGMEM = "/sunset.bin"; // Sunset Icon - -void positionWidget(_lv_obj_t *screen); -void compassWidget(_lv_obj_t *screen); -void altitudeWidget(_lv_obj_t *screen); -void speedWidget(_lv_obj_t *screen); -void sunWidget(_lv_obj_t *screen); \ No newline at end of file +static bool widgetSelected = false; /**< Widget selection state */ +static bool canMoveWidget = false; /**< Permission to move widget */ +static lv_coord_t newX = 0; /**< New X coordinate for widget movement */ +static lv_coord_t newY = 0; /**< New Y coordinate for widget movement */ + +extern bool isScrolled; /**< Flag to indicate when tileview was scrolled */ + +static const char *arrowIconFile PROGMEM = "/arrow.bin"; /**< Compass arrow icon */ +static const char *positionIconFile PROGMEM = "/pin.bin"; /**< Position icon */ +static const char *altitudeIconFile PROGMEM = "/altit.bin"; /**< Altitude icon */ +static const char *speedIconFile PROGMEM = "/speed.bin"; /**< Speed icon */ +static const char *sunriseIconFile PROGMEM = "/sunrise.bin"; /**< Sunrise icon */ +static const char *sunsetIconFile PROGMEM = "/sunset.bin"; /**< Sunset icon */ +static const char *navArrowIconFile PROGMEM = "/navarrow.bin"; /**< Navigation arrow icon */ +static const char *zoomIconFile PROGMEM = "/zoom.bin"; /**< Zoom map icon */ +static const char *mapSpeedIconFile PROGMEM = "/mapspeed.bin"; /**< Speed map icon */ + + +void positionWidget(lv_obj_t *screen); +void compassWidget(lv_obj_t *screen); +void altitudeWidget(lv_obj_t *screen); +void speedWidget(lv_obj_t *screen); +void sunWidget(lv_obj_t *screen); +void navArrowWidget(lv_obj_t *screen); +void mapZoomWidget(lv_obj_t *screen); +void mapSpeedWidget(lv_obj_t *screen); +void mapCompassWidget(lv_obj_t *screen); +void mapScaleWidget(lv_obj_t *screen); +void turnByTurnWidget(lv_obj_t *screen); \ No newline at end of file diff --git a/lib/images/src/collapse.c b/lib/images/src/collapse.c deleted file mode 100644 index c7d761ab..00000000 --- a/lib/images/src/collapse.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "collapse.h" -#include - -// array size is 4608 -const unsigned char collapse[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file diff --git a/lib/images/src/collapse.h b/lib/images/src/collapse.h deleted file mode 100644 index 2a523cf9..00000000 --- a/lib/images/src/collapse.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char collapse[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/compass.c b/lib/images/src/compass.c old mode 100644 new mode 100755 index e79ef43a..1e8cb75e --- a/lib/images/src/compass.c +++ b/lib/images/src/compass.c @@ -1,54 +1,125 @@ - #include +#include "lvgl.h" +#include "compass.h" +#include -// array size is 526 -const unsigned char mini_compass[] PROGMEM = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,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,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,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,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif -}; \ No newline at end of file +#ifndef LV_ATTRIBUTE_IMAGE_COMPASS +#define LV_ATTRIBUTE_IMAGE_COMPASS +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_COMPASS uint8_t compass_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 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, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 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, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfb, 0xfc, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0x8e, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0xad, 0xfb, 0xf0, 0x45, 0x00, 0x0e, 0xf1, 0xff, 0xff, 0xf1, 0x0e, 0x00, 0x34, 0xe7, 0xfc, 0xaf, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x71, 0xff, 0xff, 0xff, 0xff, 0x71, 0x00, 0xad, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xa4, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa4, 0x04, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x04, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa5, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x9c, 0x13, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, 0x11, 0xa2, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xed, 0x77, 0x14, 0x00, 0x00, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x00, 0x00, 0x19, 0x80, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xf9, 0xff, 0xff, 0xff, 0xfe, 0x9c, 0x14, 0x00, 0x00, 0x00, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x39, 0x00, 0x00, 0x00, 0x1a, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xf3, 0xff, 0xff, 0xff, 0xf5, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf7, 0xff, 0xff, 0xff, 0xf4, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xdf, 0xff, 0xff, 0xff, 0xee, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xf1, 0xff, 0xff, 0xff, 0xe3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0xff, 0xff, 0xff, 0xf5, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xf4, 0xff, 0xff, 0xff, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xff, 0xff, 0xff, 0xff, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xf1, 0xff, 0xff, 0xff, 0xf6, 0x8c, 0x8c, 0xf6, 0xff, 0xff, 0xff, 0xf1, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xff, 0xff, 0xff, 0xff, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xfa, 0x99, 0x1b, 0x00, 0x00, 0x16, 0x88, 0xf8, 0xff, 0xff, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xff, 0xff, 0xff, 0xcc, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0xff, 0xff, 0xff, 0xf1, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdf, 0xfc, 0xa0, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xa0, 0xfc, 0xdf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xf1, 0xff, 0xff, 0xff, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xad, 0xff, 0xff, 0xff, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xaa, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xaa, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xff, 0xff, 0xff, 0xb3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0xf5, 0xff, 0xff, 0xf9, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xf7, 0xff, 0xff, 0xf9, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x55, 0xff, 0xff, 0xff, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xff, 0xff, 0xff, 0x5a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xff, 0xff, 0xff, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xbd, 0xff, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0xff, 0xff, 0xff, 0xc3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe7, 0xff, 0xff, 0xf6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0xff, 0xff, 0xee, 0x00, 0x00, 0x00, + 0x56, 0xdb, 0xee, 0xff, 0xff, 0xff, 0xfd, 0xee, 0xe9, 0xa4, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xb5, 0xec, 0xee, 0xfc, 0xff, 0xff, 0xff, 0xee, 0xd0, 0x43, + 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, + 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, + 0x53, 0xd9, 0xee, 0xff, 0xff, 0xff, 0xfd, 0xee, 0xe9, 0xa3, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xb3, 0xea, 0xee, 0xfc, 0xff, 0xff, 0xff, 0xee, 0xcf, 0x42, + 0x00, 0x00, 0x00, 0xec, 0xff, 0xff, 0xf7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xf2, 0xff, 0xff, 0xf3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc9, 0xff, 0xff, 0xff, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x97, 0xff, 0xff, 0xff, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xff, 0xff, 0xff, 0x9c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x13, 0xfb, 0xff, 0xff, 0xfb, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xf8, 0xff, 0xff, 0xfc, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb6, 0xff, 0xff, 0xff, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xf2, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xef, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0xff, 0xff, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0xff, 0xff, 0xff, 0xd2, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xff, 0xff, 0xff, 0xfe, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xfe, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xff, 0xff, 0xff, 0xf7, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xf5, 0xff, 0xff, 0xff, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xe4, 0xff, 0xff, 0xff, 0xe9, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xe8, 0xff, 0xff, 0xff, 0xe4, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xf7, 0xff, 0xff, 0xff, 0xf6, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x7b, 0x79, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xf6, 0xff, 0xff, 0xff, 0xf7, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xfc, 0xff, 0xff, 0xff, 0xfe, 0x9e, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0xff, 0xff, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x9f, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xef, 0x77, 0x15, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xff, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x15, 0x77, 0xef, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xac, 0x5a, 0x27, 0x02, 0xeb, 0xff, 0xff, 0xeb, 0x02, 0x27, 0x5b, 0xac, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xfc, 0xff, 0xff, 0xfc, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xab, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4e, 0xb9, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xb7, 0x4d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x5f, 0x9c, 0xcf, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xcf, 0x9c, 0x5f, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xff, 0xff, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xe4, 0xe3, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t compassMap = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(compass_map), + compass_map, +}; diff --git a/lib/images/src/compass.h b/lib/images/src/compass.h index ca147b9b..ff083f57 100644 --- a/lib/images/src/compass.h +++ b/lib/images/src/compass.h @@ -1,4 +1,13 @@ #pragma once #include +#include "lvgl.h" -extern unsigned char mini_compass[] PROGMEM; \ No newline at end of file +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_COMPASS +#define LV_ATTRIBUTE_IMAGE_COMPASS +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_COMPASS uint8_t compass_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/expand.c b/lib/images/src/expand.c deleted file mode 100644 index 58787fe6..00000000 --- a/lib/images/src/expand.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "expand.h" -#include - -// array size is 4608 -const unsigned char expand[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file diff --git a/lib/images/src/expand.h b/lib/images/src/expand.h deleted file mode 100644 index 4a716c6a..00000000 --- a/lib/images/src/expand.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char expand[] PROGMEM ; \ No newline at end of file diff --git a/lib/images/src/finish.c b/lib/images/src/finish.c new file mode 100755 index 00000000..b4e7ecca --- /dev/null +++ b/lib/images/src/finish.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "finish.h" +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_FINISH +#define LV_ATTRIBUTE_IMAGE_FINISH +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_FINISH uint8_t finish_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x27, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x5f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x75, 0x95, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6b, 0xff, 0xff, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x33, 0x33, 0x41, 0x2f, 0x1d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xb8, 0x00, 0x00, 0x00, 0x0c, 0x5a, 0x99, 0xd6, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xcc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0xff, 0xff, 0xd7, 0x00, 0x13, 0x91, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0xff, 0xff, 0xf6, 0x01, 0x27, 0xff, 0xff, 0xff, 0xff, 0xe9, 0xbd, 0xa4, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0x16, 0x07, 0xfd, 0xff, 0x84, 0x25, 0x00, 0x00, 0x00, 0x61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xff, 0x35, 0x00, 0xe3, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xff, 0xff, 0x54, 0x00, 0xc2, 0xff, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb8, 0x27, 0x20, 0x11, 0x11, 0x1b, 0x22, 0x2f, 0x46, 0x5b, 0x7c, 0xac, 0xdd, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xff, 0xff, 0x73, 0x00, 0xa1, 0xff, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0x92, 0x00, 0x7f, 0xff, 0x96, 0x00, 0x00, 0x00, 0x00, 0x09, 0xb2, 0xbe, 0xa2, 0x8b, 0x88, 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xcc, 0xd0, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0xff, 0xff, 0xb1, 0x00, 0x5e, 0xff, 0xc1, 0x4b, 0x90, 0xc1, 0xeb, 0xff, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xae, 0x31, 0x12, 0x00, 0x00, 0x54, 0xff, 0xfb, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xd0, 0x00, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xae, 0x00, 0x00, 0x00, 0x00, 0x33, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xff, 0xff, 0xef, 0x00, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x11, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xff, 0x0f, 0x01, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0xef, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xff, 0xff, 0x2e, 0x00, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x01, 0x14, 0x22, 0x22, 0x1c, 0x5b, 0xff, 0xff, 0xdf, 0xc7, 0xdd, 0xf3, 0xff, 0x25, 0x00, 0x16, 0x34, 0x69, 0xec, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0x4d, 0x00, 0xb6, 0xff, 0xff, 0xe1, 0x9d, 0x6a, 0x40, 0x21, 0xee, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x08, 0xae, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xff, 0xff, 0x6c, 0x00, 0x94, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xff, 0xff, 0x8b, 0x00, 0x74, 0xff, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xff, 0xff, 0xaa, 0x00, 0x52, 0xff, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xff, 0xff, 0xc9, 0x00, 0x30, 0xff, 0xf9, 0x03, 0x00, 0x00, 0x00, 0x00, 0x54, 0xff, 0xff, 0xfd, 0xee, 0xf3, 0xff, 0xff, 0xff, 0x91, 0x5b, 0x47, 0x38, 0x4b, 0xc5, 0xab, 0x90, 0x6f, 0x67, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xff, 0xff, 0xe8, 0x00, 0x0f, 0xff, 0xff, 0x21, 0x00, 0x22, 0x61, 0x94, 0xb1, 0x1c, 0x07, 0x00, 0x00, 0x00, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x0b, 0xfe, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xf9, 0xff, 0xfe, 0x09, 0x00, 0xed, 0xff, 0xbd, 0xe2, 0xff, 0xff, 0xff, 0xfb, 0x06, 0x00, 0x00, 0x00, 0x00, 0x57, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xff, 0xff, 0x27, 0x00, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x74, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0x46, 0x00, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x98, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0x65, 0x00, 0x88, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xff, 0xb3, 0x74, 0xa8, 0xc4, 0xd4, 0xb6, 0x34, 0x53, 0x85, 0xc8, 0xfe, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x85, 0x00, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xd3, 0xe7, 0xe4, 0xdb, 0xc5, 0xf4, 0xff, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xff, 0xff, 0xa3, 0x00, 0x46, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xff, 0xff, 0xc3, 0x00, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x00, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0xff, 0xff, 0xe2, 0x00, 0x06, 0xfc, 0xfa, 0xc7, 0x9a, 0x74, 0x4d, 0x38, 0x24, 0x0e, 0x00, 0x35, 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x00, 0x00, 0x00, 0x00, 0x93, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xfd, 0xff, 0xfc, 0x05, 0x00, 0x37, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xc7, 0x89, 0x76, 0x66, 0x6e, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xff, 0xff, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x9a, 0xbd, 0xd5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xdc, 0xbd, 0x99, 0x62, 0x2a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xff, 0xff, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x20, 0x22, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0xff, 0xff, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xff, 0xff, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xff, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xff, 0xff, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xff, 0xff, 0xf8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xff, 0xff, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0xff, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x2c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t finish = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(finish_map), + finish_map, +}; diff --git a/lib/images/src/finish.h b/lib/images/src/finish.h new file mode 100644 index 00000000..5d417b13 --- /dev/null +++ b/lib/images/src/finish.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_FINISH +#define LV_ATTRIBUTE_IMAGE_FINISH +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_FINISH uint8_t finish_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/move.c b/lib/images/src/move.c deleted file mode 100644 index 5294f5fb..00000000 --- a/lib/images/src/move.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "move.h" -#include - -// array size is 4608 -const unsigned char move[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; \ No newline at end of file diff --git a/lib/images/src/move.h b/lib/images/src/move.h deleted file mode 100644 index 7c6c1808..00000000 --- a/lib/images/src/move.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char move[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/navigation.c b/lib/images/src/navigation.c deleted file mode 100644 index 07459d7f..00000000 --- a/lib/images/src/navigation.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "navigation.h" -#include - -// array size is 526 -const unsigned char navigation[] PROGMEM = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x35,0x03,0x36,0x03,0x36,0x03,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x56,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x76,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x02,0xf7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0xef,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x16,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x57,0x03,0xb9,0x03,0xb9,0x03,0x57,0x03,0x36,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x36,0x03,0x57,0x03,0x78,0x03,0x78,0x03,0x78,0x03,0x78,0x03,0x57,0x03,0x36,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x03,0x36,0x03,0x36,0x03,0x57,0x03,0x37,0x03,0x57,0x03,0x57,0x03,0x37,0x03,0x56,0x03,0x56,0x03,0x37,0x03,0x36,0x03,0x36,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x03,0x15,0x03,0x36,0x02,0xb5,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xb5,0x03,0x36,0x03,0x15,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 - -}; - diff --git a/lib/images/src/navigation.h b/lib/images/src/navigation.h deleted file mode 100644 index 4da98b2e..00000000 --- a/lib/images/src/navigation.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char navigation[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/outtrack.c b/lib/images/src/outtrack.c new file mode 100755 index 00000000..a664e6c4 --- /dev/null +++ b/lib/images/src/outtrack.c @@ -0,0 +1,125 @@ +#include "lvgl.h" +#include "outtrack.h" +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_OUTTRACK +#define LV_ATTRIBUTE_IMAGE_OUTTRACK +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_OUTTRACK uint8_t outtrack_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x84, 0xe9, 0x84, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x45, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0x84, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc7, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0x84, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x85, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x45, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xc4, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa5, 0xf1, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa0, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa0, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x2e, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x84, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa5, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xc5, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa0, 0xfa, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x00, 0xfc, 0x00, 0x00, 0xa5, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xe9, 0x84, 0xe9, 0xa4, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa0, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xf1, 0xa4, 0xe9, 0xa4, 0xe9, 0xa4, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0xe9, 0xa0, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xe9, 0xa4, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xff, 0xff, 0x49, 0x00, 0x00, 0x00, 0xd1, 0xff, 0xff, 0x9d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xff, 0xff, 0xff, 0x4c, 0x00, 0xca, 0xff, 0xff, 0xff, 0xff, 0x9d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x46, 0xff, 0xff, 0xff, 0xff, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x51, 0xff, 0xff, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t outtrack = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(outtrack_map), + outtrack_map, +}; diff --git a/lib/images/src/outtrack.h b/lib/images/src/outtrack.h new file mode 100644 index 00000000..c1eeeda4 --- /dev/null +++ b/lib/images/src/outtrack.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_OUTTRACK +#define LV_ATTRIBUTE_IMAGE_OUTTRACK +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_OUTTRACK uint8_t outtrack_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/slleft.c b/lib/images/src/slleft.c new file mode 100755 index 00000000..b2e593b6 --- /dev/null +++ b/lib/images/src/slleft.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "slleft.h" +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_SLLEFT +#define LV_ATTRIBUTE_IMAGE_SLLEFT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_SLLEFT uint8_t slleft_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x00, 0x8a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x00, 0x00, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t slleft = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(slleft_map), + slleft_map, +}; diff --git a/lib/images/src/slleft.h b/lib/images/src/slleft.h new file mode 100644 index 00000000..fda84a9e --- /dev/null +++ b/lib/images/src/slleft.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_SLLEFT +#define LV_ATTRIBUTE_IMAGE_SLLEFT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_SLLEFT uint8_t slleft_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/slright.c b/lib/images/src/slright.c new file mode 100755 index 00000000..f33183f8 --- /dev/null +++ b/lib/images/src/slright.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "slright.h" +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_SLRIGHT +#define LV_ATTRIBUTE_IMAGE_SLRIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_SLRIGHT uint8_t slright_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x4f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x00, 0x4f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x00, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t slright = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(slright_map), + slright_map, +}; diff --git a/lib/images/src/slright.h b/lib/images/src/slright.h new file mode 100644 index 00000000..31bc122d --- /dev/null +++ b/lib/images/src/slright.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_SLRIGHT +#define LV_ATTRIBUTE_IMAGE_SLRIGHT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_SLRIGHT uint8_t slright_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/speed.c b/lib/images/src/speed.c deleted file mode 100644 index da303479..00000000 --- a/lib/images/src/speed.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "speed.h" -#include - -// array size is 526 -const unsigned char speed_ico[] PROGMEM = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; \ No newline at end of file diff --git a/lib/images/src/speed.h b/lib/images/src/speed.h deleted file mode 100644 index bb95a2a5..00000000 --- a/lib/images/src/speed.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char speed_ico[] PROGMEM ; \ No newline at end of file diff --git a/lib/images/src/straight.c b/lib/images/src/straight.c new file mode 100755 index 00000000..af2fe423 --- /dev/null +++ b/lib/images/src/straight.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "straight.h" +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_STRAIGHT +#define LV_ATTRIBUTE_IMAGE_STRAIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_STRAIGHT uint8_t straight_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x40, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0xe4, 0xff, 0xff, 0xff, 0xff, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0xe3, 0xff, 0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xe1, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe3, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t straight = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(straight_map), + straight_map, +}; + diff --git a/lib/images/src/straight.h b/lib/images/src/straight.h new file mode 100644 index 00000000..bbef77b8 --- /dev/null +++ b/lib/images/src/straight.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_STRAIGHT +#define LV_ATTRIBUTE_IMAGE_STRAIGHT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_STRAIGHT uint8_t straight_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/tleft.c b/lib/images/src/tleft.c new file mode 100755 index 00000000..39aa7178 --- /dev/null +++ b/lib/images/src/tleft.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "tleft.h" +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_TLEFT +#define LV_ATTRIBUTE_IMAGE_TLEFT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_TLEFT uint8_t tleft_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x08, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xff, 0xff, 0xff, 0xff, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t tleft = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(tleft_map), + tleft_map, +}; \ No newline at end of file diff --git a/lib/images/src/tleft.h b/lib/images/src/tleft.h new file mode 100644 index 00000000..b3065089 --- /dev/null +++ b/lib/images/src/tleft.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_TLEFT +#define LV_ATTRIBUTE_IMAGE_TLEFT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_TLEFT uint8_t tleft_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/tright.c b/lib/images/src/tright.c new file mode 100755 index 00000000..73323fd9 --- /dev/null +++ b/lib/images/src/tright.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "tright.h" +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_TRIGHT +#define LV_ATTRIBUTE_IMAGE_TRIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_TRIGHT uint8_t tright_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x20, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xff, 0xff, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xff, 0xff, 0xff, 0xff, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xff, 0xff, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t tright = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(tright_map), + tright_map, +}; diff --git a/lib/images/src/tright.h b/lib/images/src/tright.h new file mode 100644 index 00000000..0d78673f --- /dev/null +++ b/lib/images/src/tright.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_TRIGHT +#define LV_ATTRIBUTE_IMAGE_TRIGHT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_TRIGHT uint8_t tright_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/uleft.c b/lib/images/src/uleft.c new file mode 100755 index 00000000..06376fa5 --- /dev/null +++ b/lib/images/src/uleft.c @@ -0,0 +1,126 @@ +#include "lvgl.h" +#include "uleft.h" +#include + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_ULEFT +#define LV_ATTRIBUTE_IMAGE_ULEFT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_ULEFT uint8_t uleft_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x95, 0x89, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xff, 0xff, 0xff, 0xff, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0xff, 0xff, 0xff, 0xff, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0xff, 0xff, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x13, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xff, 0x1c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x18, 0xff, 0xff, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xaf, 0xaf, 0xaf, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t uleft = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(uleft_map), + uleft_map, +}; diff --git a/lib/images/src/uleft.h b/lib/images/src/uleft.h new file mode 100644 index 00000000..37b797e5 --- /dev/null +++ b/lib/images/src/uleft.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_ULEFT +#define LV_ATTRIBUTE_IMAGE_ULEFT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_ULEFT uint8_t uleft_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/uright.c b/lib/images/src/uright.c new file mode 100755 index 00000000..f340981f --- /dev/null +++ b/lib/images/src/uright.c @@ -0,0 +1,125 @@ +#include "lvgl.h" +#include "uright.h" +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_URIGHT +#define LV_ATTRIBUTE_IMAGE_URIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_URIGHT uint8_t uright_map[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd7, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x95, 0x89, 0xba, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xff, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x36, 0x00, 0x00, 0x00, 0xb3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0x46, 0x00, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0xff, 0xff, 0xff, 0xff, 0x4f, 0x00, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xaf, 0xaf, 0xaf, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t uright = +{ + { + LV_IMAGE_HEADER_MAGIC, + LV_COLOR_FORMAT_RGB565A8, + 0, + 48, + 48, + 96, + }, + sizeof(uright_map), + uright_map, +}; diff --git a/lib/images/src/uright.h b/lib/images/src/uright.h new file mode 100644 index 00000000..8445e4a9 --- /dev/null +++ b/lib/images/src/uright.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "lvgl.h" + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_URIGHT +#define LV_ATTRIBUTE_IMAGE_URIGHT +#endif + +extern const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_URIGHT uint8_t uright_map[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/zoom.c b/lib/images/src/zoom.c deleted file mode 100644 index d51b48f2..00000000 --- a/lib/images/src/zoom.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "zoom.h" -#include - -// array size is 526 -const unsigned char zoom_ico[] PROGMEM = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; \ No newline at end of file diff --git a/lib/images/src/zoom.h b/lib/images/src/zoom.h deleted file mode 100644 index a1c127fa..00000000 --- a/lib/images/src/zoom.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char zoom_ico[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/zoomin.c b/lib/images/src/zoomin.c deleted file mode 100644 index f0932852..00000000 --- a/lib/images/src/zoomin.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "zoomin.h" -#include - -// array size is 4608 -const unsigned char zoomin[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file diff --git a/lib/images/src/zoomin.h b/lib/images/src/zoomin.h deleted file mode 100644 index 24b4408c..00000000 --- a/lib/images/src/zoomin.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char zoomin[] PROGMEM; \ No newline at end of file diff --git a/lib/images/src/zoomout.c b/lib/images/src/zoomout.c deleted file mode 100644 index 1b90d472..00000000 --- a/lib/images/src/zoomout.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "zoomout.h" -#include - -// array size is 4608 -const unsigned char zoomout[] PROGMEM = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file diff --git a/lib/images/src/zoomout.h b/lib/images/src/zoomout.h deleted file mode 100644 index ad2426da..00000000 --- a/lib/images/src/zoomout.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const unsigned char zoomout[] PROGMEM; \ No newline at end of file diff --git a/lib/imu/imu.cpp b/lib/imu/imu.cpp index 3cdbc46c..295b7d28 100644 --- a/lib/imu/imu.cpp +++ b/lib/imu/imu.cpp @@ -2,29 +2,30 @@ * @file imu.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief IMU definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "imu.hpp" #ifdef MPU6050 -Adafruit_MPU6050 mpu; + Adafruit_MPU6050 mpu; #endif #ifdef ENABLE_IMU static const char* TAG PROGMEM = "IMU"; + /** - * @brief Init IMU + * @brief Initialize the IMU (Inertial Measurement Unit) * */ void initIMU() { - if (!mpu.begin()) - ESP_LOGE(TAG, "Failed to init IMU"); - else - ESP_LOGI(TAG, "IMU init"); + if (!mpu.begin()) + ESP_LOGE(TAG, "Failed to init IMU"); + else + ESP_LOGI(TAG, "IMU init"); } #endif \ No newline at end of file diff --git a/lib/imu/imu.hpp b/lib/imu/imu.hpp index 8ccf623a..690c0743 100644 --- a/lib/imu/imu.hpp +++ b/lib/imu/imu.hpp @@ -2,18 +2,18 @@ * @file imu.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief IMU definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once #ifdef MPU6050 -#include -extern Adafruit_MPU6050 mpu; -#define ENABLE_IMU + #include + extern Adafruit_MPU6050 mpu; + #define ENABLE_IMU #endif #ifdef ENABLE_IMU -void initIMU(); + void initIMU(); #endif \ No newline at end of file diff --git a/lib/lvgl/lv_conf.h b/lib/lvgl/lv_conf.h index 7c32b1b7..ade04f8d 100644 --- a/lib/lvgl/lv_conf.h +++ b/lib/lvgl/lv_conf.h @@ -58,7 +58,7 @@ * - LV_STDLIB_RTTHREAD: RT-Thread implementation * - LV_STDLIB_CUSTOM: Implement the functions externally */ -#define LV_USE_STDLIB_SPRINTF LV_STDLIB_CLIB +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN #define LV_STDINT_INCLUDE #define LV_STDDEF_INCLUDE @@ -116,6 +116,14 @@ #if LV_USE_OS == LV_OS_CUSTOM #define LV_OS_CUSTOM_INCLUDE #endif +#if LV_USE_OS == LV_OS_FREERTOS + /* + * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + * than unblocking a task using an intermediary object such as a binary semaphore. + * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + */ + #define LV_USE_FREERTOS_TASK_NOTIFY 1 +#endif /*======================== * RENDERING CONFIGURATION @@ -141,11 +149,26 @@ /** The target buffer size for simple layer chunks. */ #define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (64 * 1024) /**< [bytes]*/ +/* Limit the max allocated memory for simple and transformed layers. + * It should be at least `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` sized but if transformed layers are also used + * it should be enough to store the largest widget too (width x height x 4 area). + * Set it to 0 to have no limit. */ +#define LV_DRAW_LAYER_MAX_MEMORY 0 /**< No limit by default [bytes]*/ + /** Stack size of drawing thread. * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. */ #define LV_DRAW_THREAD_STACK_SIZE (8 * 1024) /**< [bytes]*/ +/** Thread priority of the drawing task. + * Higher values mean higher priority. + * Can use values from lv_thread_prio_t enum in lv_os.h: LV_THREAD_PRIO_LOWEST, + * LV_THREAD_PRIO_LOW, LV_THREAD_PRIO_MID, LV_THREAD_PRIO_HIGH, LV_THREAD_PRIO_HIGHEST + * Make sure the priority value aligns with the OS-specific priority levels. + * On systems with limited priority levels (e.g., FreeRTOS), a higher value can improve + * rendering performance but might cause other tasks to starve. */ +#define LV_DRAW_THREAD_PRIO LV_THREAD_PRIO_HIGH + #define LV_USE_DRAW_SW 1 #if LV_USE_DRAW_SW == 1 /* @@ -203,6 +226,28 @@ #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 0 #endif +/*Use TSi's aka (Think Silicon) NemaGFX */ +#define LV_USE_NEMA_GFX 0 + +#if LV_USE_NEMA_GFX + /** Select which NemaGFX HAL to use. Possible options: + * - LV_NEMA_HAL_CUSTOM + * - LV_NEMA_HAL_STM32 */ + #define LV_USE_NEMA_HAL LV_NEMA_HAL_CUSTOM + #if LV_USE_NEMA_HAL == LV_NEMA_HAL_STM32 + #define LV_NEMA_STM32_HAL_INCLUDE + #endif + + /*Enable Vector Graphics Operations. Available only if NemaVG library is present*/ + #define LV_USE_NEMA_VG 0 + #if LV_USE_NEMA_VG + /*Define application's resolution used for VG related buffer allocation */ + #define LV_NEMA_GFX_MAX_RESX 800 + #define LV_NEMA_GFX_MAX_RESY 600 + #endif +#endif + + /** Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ #define LV_USE_DRAW_VGLITE 0 @@ -222,13 +267,22 @@ /** Enable VGLite asserts. */ #define LV_USE_VGLITE_ASSERT 0 + + /** Enable VGLite error checks. */ + #define LV_USE_VGLITE_CHECK_ERROR 0 #endif /** Use NXP's PXP on iMX RTxxx platforms. */ -#define LV_USE_DRAW_PXP 0 +#define LV_USE_PXP 0 -#if LV_USE_DRAW_PXP - #if LV_USE_OS +#if LV_USE_PXP + /** Use PXP for drawing.*/ + #define LV_USE_DRAW_PXP 1 + + /** Use PXP to rotate display.*/ + #define LV_USE_ROTATE_PXP 0 + + #if LV_USE_DRAW_PXP && LV_USE_OS /** Use additional draw thread for PXP processing.*/ #define LV_USE_PXP_DRAW_THREAD 1 #endif @@ -237,6 +291,24 @@ #define LV_USE_PXP_ASSERT 0 #endif + +/** Use NXP's G2D on MPU platforms. */ +#define LV_USE_DRAW_G2D 0 + +#if LV_USE_DRAW_G2D + /** Maximum number of buffers that can be stored for G2D draw unit. + * Includes the frame buffers and assets. */ + #define LV_G2D_HASH_TABLE_SIZE 50 + + #if LV_USE_OS + /** Use additional draw thread for G2D processing.*/ + #define LV_USE_G2D_DRAW_THREAD 1 + #endif + + /** Enable G2D asserts. */ + #define LV_USE_G2D_ASSERT 0 +#endif + /** Use Renesas Dave2D on RA platforms. */ #define LV_USE_DRAW_DAVE2D 0 @@ -269,6 +341,22 @@ #define LV_VG_LITE_STROKE_CACHE_CNT 32 #endif + +/** Accelerate blends, fills, etc. with STM32 DMA2D */ +#define LV_USE_DRAW_DMA2D 0 + +#if LV_USE_DRAW_DMA2D + #define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h" + + /* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler` + * upon receiving the DMA2D global interrupt + */ + #define LV_USE_DRAW_DMA2D_INTERRUPT 0 +#endif + +/** Draw using cached OpenGLES textures */ +#define LV_USE_DRAW_OPENGLES 0 + /*======================= * FEATURE CONFIGURATION *=======================*/ @@ -365,11 +453,11 @@ * If size is not set to 0, the decoder will fail to decode when the cache is full. * If size is 0, the cache function is not enabled and the decoded memory will be * released immediately after use. */ -#define LV_CACHE_DEF_SIZE (32 * 1024) +#define LV_CACHE_DEF_SIZE 0 /** Default number of image header cache entries. The cache is used to store the headers of images * The main logic is like `LV_CACHE_DEF_SIZE` but for image headers. */ -#define LV_IMAGE_HEADER_CACHE_DEF_CNT 15 +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 20 /** Number of stops allowed per gradient. Increase this to allow more stops. * This adds (sizeof(lv_color_t) + 1) bytes per additional stop. */ @@ -513,6 +601,8 @@ #define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /**< Hebrew, Arabic, Persian letters and all their forms */ #define LV_FONT_SIMSUN_14_CJK 0 /**< 1000 most common CJK radicals */ #define LV_FONT_SIMSUN_16_CJK 0 /**< 1000 most common CJK radicals */ +#define LV_FONT_SOURCE_HAN_SANS_SC_14_CJK 0 /**< 1338 most common CJK radicals */ +#define LV_FONT_SOURCE_HAN_SANS_SC_16_CJK 0 /**< 1338 most common CJK radicals */ /** Pixel perfect monospaced fonts */ #define LV_FONT_UNSCII_8 0 @@ -586,6 +676,9 @@ * In these languages characters should be replaced with another form based on their position in the text */ #define LV_USE_ARABIC_PERSIAN_CHARS 0 +/*The control character to use for signaling text recoloring*/ +#define LV_TXT_COLOR_CMD "#" + /*================== * WIDGETS *================*/ @@ -689,6 +782,8 @@ #define LV_USE_WIN 1 +#define LV_USE_3DTEXTURE 0 + /*================== * THEMES *==================*/ @@ -732,7 +827,7 @@ /* File system interfaces for common APIs */ /** Setting a default driver letter allows skipping the driver prefix in filepaths. */ -#define LV_FS_DEFAULT_DRIVE_LETTER 'F' +#define LV_FS_DEFAULT_DRIVER_LETTER 'F' /** API for fopen, fread, etc. */ #define LV_USE_FS_STDIO 0 @@ -813,7 +908,6 @@ #define LV_GIF_CACHE_DECODE_DATA 0 #endif - /** Decode bin images to RAM */ #define LV_BIN_DECODER_RAM_LOAD 1 @@ -870,6 +964,10 @@ #if LV_USE_FFMPEG /** Dump input information to stderr */ #define LV_FFMPEG_DUMP_FORMAT 0 + /** Use lvgl file path in FFmpeg Player widget + * You won't be able to open URLs after enabling this feature. + * Note that FFmpeg image decoder will always use lvgl file system. */ + #define LV_FFMPEG_PLAYER_USE_LV_FS 0 #endif /*================== @@ -929,6 +1027,39 @@ /** Profiler end point function with custom tag */ #define LV_PROFILER_END_TAG LV_PROFILER_BUILTIN_END_TAG + + /*Enable layout profiler*/ + #define LV_PROFILER_LAYOUT 1 + + /*Enable disp refr profiler*/ + #define LV_PROFILER_REFR 1 + + /*Enable draw profiler*/ + #define LV_PROFILER_DRAW 1 + + /*Enable indev profiler*/ + #define LV_PROFILER_INDEV 1 + + /*Enable decoder profiler*/ + #define LV_PROFILER_DECODER 1 + + /*Enable font profiler*/ + #define LV_PROFILER_FONT 1 + + /*Enable fs profiler*/ + #define LV_PROFILER_FS 1 + + /*Enable style profiler*/ + #define LV_PROFILER_STYLE 0 + + /*Enable timer profiler*/ + #define LV_PROFILER_TIMER 1 + + /*Enable cache profiler*/ + #define LV_PROFILER_CACHE 1 + + /*Enable event profiler*/ + #define LV_PROFILER_EVENT 1 #endif /** 1: Enable Monkey test */ @@ -985,6 +1116,20 @@ #endif +/** Enable emulated input devices, time emulation, and screenshot compares. */ +#define LV_USE_TEST 0 +#if LV_USE_TEST + +/** Enable `lv_test_screenshot_compare`. + * Requires libpng and a few MB of extra RAM. */ +#define LV_USE_TEST_SCREENSHOT_COMPARE 0 +#endif /*LV_USE_TEST*/ + +/** Enable loading XML UIs runtime */ +#define LV_USE_XML 0 + +/*1: Enable color filter style*/ +#define LV_USE_COLOR_FILTER 0 /*================== * DEVICES *==================*/ @@ -1015,8 +1160,12 @@ /** Use Wayland to open a window and handle input on Linux or BSD desktops */ #define LV_USE_WAYLAND 0 #if LV_USE_WAYLAND - #define LV_WAYLAND_WINDOW_DECORATIONS 0 /**< Draw client side window decorations only necessary on Mutter/GNOME */ - #define LV_WAYLAND_WL_SHELL 0 /**< Use the legacy wl_shell protocol instead of the default XDG shell */ + #define LV_WAYLAND_BUF_COUNT 1 /**< Use 1 for single buffer with partial render mode or 2 for double buffer with full render mode*/ + #define LV_WAYLAND_USE_DMABUF 0 /**< Use DMA buffers for frame buffers. Requires LV_DRAW_USE_G2D */ + #define LV_WAYLAND_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL /**< DMABUF supports LV_DISPLAY_RENDER_MODE_FULL and LV_DISPLAY_RENDER_MODE_DIRECT*/ + /**< When LV_WAYLAND_USE_DMABUF is disabled, only LV_DISPLAY_RENDER_MODE_PARTIAL is supported*/ + #define LV_WAYLAND_WINDOW_DECORATIONS 0 /**< Draw client side window decorations only necessary on Mutter/GNOME. Not supported using DMABUF*/ + #define LV_WAYLAND_WL_SHELL 0 /**< Use the legacy wl_shell protocol instead of the default XDG shell*/ #endif /** Driver for /dev/fb */ @@ -1026,12 +1175,18 @@ #define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL #define LV_LINUX_FBDEV_BUFFER_COUNT 0 #define LV_LINUX_FBDEV_BUFFER_SIZE 60 + #define LV_LINUX_FBDEV_MMAP 1 #endif /** Use Nuttx to open window and handle touchscreen */ #define LV_USE_NUTTX 0 #if LV_USE_NUTTX + #define LV_USE_NUTTX_INDEPENDENT_IMAGE_HEAP 0 + + /** Use independent image heap for default draw buffer */ + #define LV_NUTTX_DEFAULT_DRAW_BUF_USE_INDEPENDENT_IMAGE_HEAP 0 + #define LV_USE_NUTTX_LIBUV 0 /** Use Nuttx custom init API to open window and handle touchscreen */ @@ -1046,11 +1201,23 @@ /** Driver for /dev/input */ #define LV_USE_NUTTX_TOUCHSCREEN 0 + + /*Touchscreen cursor size in pixels(<=0: disable cursor)*/ + #define LV_NUTTX_TOUCHSCREEN_CURSOR_SIZE 0 #endif /** Driver for /dev/dri/card */ #define LV_USE_LINUX_DRM 0 +#if LV_USE_LINUX_DRM + + /* Use the MESA GBM library to allocate DMA buffers that can be + * shared across sub-systems and libraries using the Linux DMA-BUF API. + * The GBM library aims to provide a platform independent memory management system + * it supports the major GPU vendors - This option requires linking with libgbm */ + #define LV_LINUX_DRM_GBM_BUFFERS 0 +#endif + /** Interface for TFT_eSPI */ #define LV_USE_TFT_ESPI 0 @@ -1076,15 +1243,35 @@ #define LV_USE_ST7789 0 #define LV_USE_ST7796 0 #define LV_USE_ILI9341 0 +#define LV_USE_FT81X 0 + +#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) + #define LV_USE_GENERIC_MIPI 1 +#else + #define LV_USE_GENERIC_MIPI 0 +#endif -#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) /** Driver for Renesas GLCD */ #define LV_USE_RENESAS_GLCDC 0 +/** Driver for ST LTDC */ +#define LV_USE_ST_LTDC 0 +#if LV_USE_ST_LTDC + /* Only used for partial. */ + #define LV_ST_LTDC_USE_DMA2D_FLUSH 0 +#endif + /** LVGL Windows backend */ #define LV_USE_WINDOWS 0 +/** LVGL UEFI backend */ +#define LV_USE_UEFI 0 +#if LV_USE_UEFI + #define LV_USE_UEFI_INCLUDE "myefi.h" /**< Header that hides the actual framework (EDK2, gnu-efi, ...) */ + #define LV_UEFI_USE_MEMORY_SERVICES 0 /**< Use the memory functions from the boot services table */ +#endif + /** Use OpenGL to open window on PC and handle mouse and keyboard */ #define LV_USE_OPENGLES 0 #if LV_USE_OPENGLES @@ -1104,50 +1291,76 @@ /** Enable examples to be built with the library. */ #define LV_BUILD_EXAMPLES 0 +/** Build the demos */ +#define LV_BUILD_DEMOS 0 + /*=================== * DEMO USAGE ====================*/ -/** Show some widgets. This might be required to increase `LV_MEM_SIZE`. */ -#define LV_USE_DEMO_WIDGETS 0 - -/** Demonstrate usage of encoder and keyboard. */ -#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 - -/** Benchmark your system */ -#define LV_USE_DEMO_BENCHMARK 0 - -/** Render test for each primitive. - * - Requires at least 480x272 display. */ -#define LV_USE_DEMO_RENDER 0 - -/** Stress test for LVGL */ -#define LV_USE_DEMO_STRESS 0 - -/** Music player demo */ -#define LV_USE_DEMO_MUSIC 0 -#if LV_USE_DEMO_MUSIC - #define LV_DEMO_MUSIC_SQUARE 0 - #define LV_DEMO_MUSIC_LANDSCAPE 0 - #define LV_DEMO_MUSIC_ROUND 0 - #define LV_DEMO_MUSIC_LARGE 0 - #define LV_DEMO_MUSIC_AUTO_PLAY 0 -#endif - -/** Flex layout demo */ -#define LV_USE_DEMO_FLEX_LAYOUT 0 - -/** Smart-phone like multi-language demo */ -#define LV_USE_DEMO_MULTILANG 0 - -/** Widget transformation demo */ -#define LV_USE_DEMO_TRANSFORM 0 - -/** Demonstrate scroll settings */ -#define LV_USE_DEMO_SCROLL 0 +#if LV_BUILD_DEMOS + /** Show some widgets. This might be required to increase `LV_MEM_SIZE`. */ + #define LV_USE_DEMO_WIDGETS 0 + + /** Demonstrate usage of encoder and keyboard. */ + #define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 + + /** Benchmark your system */ + #define LV_USE_DEMO_BENCHMARK 0 + + #if LV_USE_DEMO_BENCHMARK + /** Use fonts where bitmaps are aligned 16 byte and has Nx16 byte stride */ + #define LV_DEMO_BENCHMARK_ALIGNED_FONTS 0 + #endif -/** Vector graphic demo */ -#define LV_USE_DEMO_VECTOR_GRAPHIC 0 + /** Render test for each primitive. + * - Requires at least 480x272 display. */ + #define LV_USE_DEMO_RENDER 0 + + /** Stress test for LVGL */ + #define LV_USE_DEMO_STRESS 0 + + /** Music player demo */ + #define LV_USE_DEMO_MUSIC 0 + #if LV_USE_DEMO_MUSIC + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 + #endif + + /** Vector graphic demo */ + #define LV_USE_DEMO_VECTOR_GRAPHIC 0 + + /*--------------------------- + * Demos from lvgl/lv_demos + ---------------------------*/ + + /** Flex layout demo */ + #define LV_USE_DEMO_FLEX_LAYOUT 0 + + /** Smart-phone like multi-language demo */ + #define LV_USE_DEMO_MULTILANG 0 + + /** Widget transformation demo */ + #define LV_USE_DEMO_TRANSFORM 0 + + /** Demonstrate scroll settings */ + #define LV_USE_DEMO_SCROLL 0 + + /*E-bike demo with Lottie animations (if LV_USE_LOTTIE is enabled)*/ + #define LV_USE_DEMO_EBIKE 0 + #if LV_USE_DEMO_EBIKE + #define LV_DEMO_EBIKE_PORTRAIT 0 /*0: for 480x270..480x320, 1: for 480x800..720x1280*/ + #endif + + /** High-resolution demo */ + #define LV_USE_DEMO_HIGH_RES 0 + + /* Smart watch demo */ + #define LV_USE_DEMO_SMARTWATCH 0 +#endif /* LV_BUILD_DEMOS */ /*--END OF LV_CONF_H--*/ diff --git a/lib/lvgl/src/lvglFuncs.cpp b/lib/lvgl/src/lvglFuncs.cpp index 412cf460..4a48c724 100644 --- a/lib/lvgl/src/lvglFuncs.cpp +++ b/lib/lvgl/src/lvglFuncs.cpp @@ -2,91 +2,127 @@ * @file lvglFuncs.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL custom functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "lvglFuncs.hpp" +lv_obj_t *msgDialog; /**< Message dialog object. */ + /** - * @brief Custom LVGL function to hide cursor + * @brief Custom LVGL function to hide the object's cursor. * - * @param LVGL object + * @param obj Pointer to the LVGL object whose cursor will be hidden. */ void objHideCursor(_lv_obj_t *obj) { - static lv_style_t style1; - lv_style_init(&style1); - lv_style_set_bg_opa(&style1, LV_OPA_TRANSP); - lv_style_set_text_opa(&style1, LV_OPA_TRANSP); - lv_obj_add_style(obj, &style1, LV_PART_CURSOR); + static lv_style_t style1; + lv_style_init(&style1); + lv_style_set_bg_opa(&style1, LV_OPA_TRANSP); + lv_style_set_text_opa(&style1, LV_OPA_TRANSP); + lv_obj_add_style(obj, &style1, LV_PART_CURSOR); - static lv_style_t style2; - lv_style_init(&style2); - lv_style_set_bg_opa(&style2, LV_OPA_100); - lv_style_set_text_opa(&style2, LV_OPA_100); - lv_obj_add_style(obj, &style2, LV_PART_CURSOR | LV_STATE_FOCUS_KEY); - lv_obj_add_style(obj, &style2, LV_PART_CURSOR | LV_STATE_FOCUSED); + static lv_style_t style2; + lv_style_init(&style2); + lv_style_set_bg_opa(&style2, LV_OPA_100); + lv_style_set_text_opa(&style2, LV_OPA_100); + lv_obj_add_style(obj, &style2, LV_PART_CURSOR | LV_STATE_FOCUS_KEY); + lv_obj_add_style(obj, &style2, LV_PART_CURSOR | LV_STATE_FOCUSED); } /** - * @brief Custom LVGL function to select widget + * @brief Custom LVGL function to select a widget. + * + * @details Applies a highlight style to the specified LVGL object to indicate that it is selected. * - * @param obj + * @param obj Pointer to the LVGL object to be selected. */ void objSelect(_lv_obj_t *obj) { - static lv_style_t styleWidget; - lv_style_init(&styleWidget); - lv_style_set_bg_color(&styleWidget, lv_color_hex(0xB8B8B8)); - lv_style_set_bg_opa(&styleWidget, LV_OPA_20); - lv_style_set_border_opa(&styleWidget, LV_OPA_100); - lv_obj_add_style(obj, &styleWidget, LV_PART_MAIN); + static lv_style_t styleWidget; + lv_style_init(&styleWidget); + lv_style_set_bg_color(&styleWidget, lv_color_hex(0xB8B8B8)); + lv_style_set_bg_opa(&styleWidget, LV_OPA_20); + lv_style_set_border_opa(&styleWidget, LV_OPA_100); + lv_obj_add_style(obj, &styleWidget, LV_PART_MAIN); } /** - * @brief Custom LVGL function to unselect widget - * - * @param obj + * @brief Custom LVGL function to unselect a widget. + * + * @details Removes the selection highlight by applying a transparent style to the specified LVGL object. + * + * @param obj Pointer to the LVGL object to be unselected. */ void objUnselect(_lv_obj_t *obj) { - static lv_style_t styleWidget; - lv_style_init(&styleWidget); - lv_style_set_bg_color(&styleWidget, lv_color_black()); - lv_style_set_bg_opa(&styleWidget, LV_OPA_0); - lv_style_set_border_opa(&styleWidget, LV_OPA_0); - lv_obj_add_style(obj, &styleWidget, LV_PART_MAIN); + static lv_style_t styleWidget; + lv_style_init(&styleWidget); + lv_style_set_bg_color(&styleWidget, lv_color_black()); + lv_style_set_bg_opa(&styleWidget, LV_OPA_0); + lv_style_set_border_opa(&styleWidget, LV_OPA_0); + lv_obj_add_style(obj, &styleWidget, LV_PART_MAIN); } /** - * @brief Restart timer callback - * - * @param timer + * @brief Restart timer callback. + * + * @param timer Pointer to the LVGL timer object that triggered the callback. */ void restartTimerCb(lv_timer_t *timer) { - if (lv_timer_get_idle() != 0) - ESP.restart(); + if (lv_timer_get_idle() != 0) + ESP.restart(); } /** - * @brief Show restart Screen - * + * @brief Show restart screen. + * */ void showRestartScr() { - lv_obj_t *restartScr = lv_obj_create(NULL); - lv_obj_set_size(restartScr, TFT_WIDTH, TFT_HEIGHT); - lv_obj_t *restartMsg = lv_msgbox_create(restartScr); - lv_obj_set_width(restartMsg,TFT_WIDTH - 20); - lv_obj_set_align(restartMsg,LV_ALIGN_CENTER); - lv_obj_set_style_text_font(restartMsg, fontDefault, 0); - lv_obj_t *labelText = lv_msgbox_get_content(restartMsg); - lv_obj_set_style_text_align(labelText, LV_TEXT_ALIGN_CENTER, 0); - lv_msgbox_add_text(restartMsg, LV_SYMBOL_WARNING " This device will restart shortly"); - lv_screen_load(restartScr); - lv_timer_t *restartTimer; - restartTimer = lv_timer_create(restartTimerCb, 3000, NULL); - lv_timer_reset(restartTimer); + lv_obj_t *restartScr = lv_obj_create(NULL); + lv_obj_set_size(restartScr, TFT_WIDTH, TFT_HEIGHT); + lv_obj_t *restartMsg = lv_msgbox_create(restartScr); + lv_obj_set_width(restartMsg,TFT_WIDTH - 20); + lv_obj_set_align(restartMsg,LV_ALIGN_CENTER); + lv_obj_set_style_text_font(restartMsg, fontDefault, 0); + lv_obj_t *labelText = lv_msgbox_get_content(restartMsg); + lv_obj_set_style_text_align(labelText, LV_TEXT_ALIGN_CENTER, 0); + lv_msgbox_add_text(restartMsg, LV_SYMBOL_WARNING " This device will restart shortly"); + lv_screen_load(restartScr); + lv_timer_t *restartTimer; + restartTimer = lv_timer_create(restartTimerCb, 3000, NULL); + lv_timer_reset(restartTimer); } + +/** + * @brief Show message dialog + * + * @param symbol LVGL symbol font + * @param message Message + */ +void showMsg(const char* symbol, const char* message) +{ + msgDialog = lv_msgbox_create(lv_scr_act()); + lv_obj_set_width(msgDialog,TFT_WIDTH); + lv_obj_set_align(msgDialog,LV_ALIGN_CENTER); + lv_obj_set_style_text_font(msgDialog, fontDefault, 0); + lv_obj_t *labelText = lv_msgbox_get_content(msgDialog); + lv_obj_set_style_text_align(labelText, LV_TEXT_ALIGN_CENTER, 0); + char msg[100]; + snprintf(msg, sizeof(msg), "%s %s", symbol, message); + lv_msgbox_add_text(msgDialog, msg); + lv_obj_invalidate(msgDialog); + lv_refr_now(display); +} + +/** + * @brief Close message dialog. + * + */ +void closeMsg() +{ + lv_obj_del(msgDialog); +} \ No newline at end of file diff --git a/lib/lvgl/src/lvglFuncs.hpp b/lib/lvgl/src/lvglFuncs.hpp index d14b206b..01981d5b 100644 --- a/lib/lvgl/src/lvglFuncs.hpp +++ b/lib/lvgl/src/lvglFuncs.hpp @@ -2,8 +2,8 @@ * @file lvglFuncs.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL custom functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -16,3 +16,5 @@ void objSelect(_lv_obj_t *obj); void objUnselect(_lv_obj_t *obj); void restartTimerCb(lv_timer_t *timer); void showRestartScr(); +void showMsg(const char* symbol, const char* message); +void closeMsg(); diff --git a/lib/lvgl/src/lvglSetup.cpp b/lib/lvgl/src/lvglSetup.cpp index 12043f24..4b5e4f69 100644 --- a/lib/lvgl/src/lvglSetup.cpp +++ b/lib/lvgl/src/lvglSetup.cpp @@ -2,247 +2,252 @@ * @file lvglSetup.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL Screen implementation - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "lvglSetup.hpp" -lv_display_t *display; +lv_display_t *display; /**< LVGL display driver */ -lv_obj_t *searchSatScreen; // Search Satellite Screen -lv_style_t styleThemeBkg; // New Main Background Style -lv_style_t styleObjectBkg; // New Objects Background Color -lv_style_t styleObjectSel; // New Objects Selected Color +lv_obj_t *searchSatScreen; /**< Search Satellite Screen object. */ +lv_style_t styleThemeBkg; /**< Main background style object. */ +lv_style_t styleObjectBkg; /**< Object background style. */ +lv_style_t styleObjectSel; /**< Object selected style. */ -lv_group_t *scrGroup; // Screen group -lv_group_t *keyGroup; // GPIO group -lv_obj_t *powerMsg; // Power Message +lv_group_t *scrGroup; /**< LVGL group for screen. */ +lv_group_t *keyGroup; /**< LVGL group for GPIO keys. */ Power power; -uint32_t DOUBLE_TOUCH_EVENT; +uint32_t DOUBLE_TOUCH_EVENT; /**< Event identifier for double touch gesture. */ + +Maps mapView; + +// Initialize memory pools after object creation +void initializeMapsPools() +{ + // Memory monitoring is now handled internally +} /** - * @brief LVGL display update + * @brief LVGL display flush callback. * + * @details Updates the specified area of the display with the provided pixel map. + * Flush callback for an LVGL display driver, and utilizes DMA for fast image transfer. + * + * @param disp Pointer to the LVGL display driver. + * @param area Area of the display to update. + * @param px_map Pointer to the pixel data to be displayed. */ void IRAM_ATTR displayFlush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) { - uint32_t w = (area->x2 - area->x1 + 1); - uint32_t h = (area->y2 - area->y1 + 1); + const uint32_t w = area->x2 - area->x1 + 1; + const uint32_t h = area->y2 - area->y1 + 1; - tft.setSwapBytes(true); - tft.setAddrWindow(area->x1, area->y1, w, h); - tft.pushImageDMA(area->x1, area->y1, area->x2 - area->x1 + 1, area->y2 - area->y1 + 1, (uint16_t*)px_map); - tft.setSwapBytes(false); + tft.setSwapBytes(true); + tft.setAddrWindow(area->x1, area->y1, w, h); + tft.pushImageDMA(area->x1, area->y1, w, h, reinterpret_cast(px_map)); + tft.setSwapBytes(false); - lv_display_flush_ready(disp); + lv_display_flush_ready(disp); } /** - * @brief LVGL touch read + * @brief Reads and processes LVGL touch input, handling gestures and events. * + * @param indev_driver Pointer to the LVGL input device driver. + * @param data Pointer to the structure that will be populated with touch data. */ void IRAM_ATTR touchRead(lv_indev_t *indev_driver, lv_indev_data_t *data) { - lgfx::touch_point_t touchRaw[TOUCH_MAX_POINTS]; - static lgfx::touch_point_t touchPrev[TOUCH_MAX_POINTS]; - static bool prevValid = false; - static bool pinchActive = false; - static int lastZoomDir = ZOOM_NONE; - static unsigned long lastTime = 0; - - int count = tft.getTouch(touchRaw, TOUCH_MAX_POINTS); - - unsigned long now = millis(); - float dt_ms = (now > lastTime) ? (float)(now - lastTime) : 1.0f; - - if (count == 0) - { - data->state = LV_INDEV_STATE_RELEASED; - - if (pinchActive && lastZoomDir != 0) - { - if (lastZoomDir == ZOOM_IN) - lv_obj_send_event(btnZoomIn,LV_EVENT_CLICKED,NULL); - else if (lastZoomDir == ZOOM_OUT) - lv_obj_send_event(btnZoomOut,LV_EVENT_CLICKED,NULL); - } - pinchActive = false; - prevValid = false; - lastZoomDir = ZOOM_NONE; - lastTime = now; - - if (countTouchReleases) - { - countTouchReleases = false; - uint32_t touchReleaseTime = millis(); - if (!firstTouchReleaseTime) - firstTouchReleaseTime = touchReleaseTime; - numberTouchReleases++; - } - - if (millis() - firstTouchReleaseTime > TOUCH_DOUBLE_TOUCH_INTERVAL) - { - if (numberTouchReleases == 2) - { - if (activeTile == MAP) - lv_obj_send_event(mapTile, (lv_event_code_t)DOUBLE_TOUCH_EVENT, NULL); - } - - numberTouchReleases = 0; - firstTouchReleaseTime = 0; - } - } - else - { - if (count == 1) - { - if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_0) - { - data->point.x = touchRaw[count-1].x; - data->point.y = touchRaw[count-1].y; - } - else if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270) - { - data->point.x = TFT_WIDTH - touchRaw[count-1].y; - data->point.y = touchRaw[count-1].x; - } - - countTouchReleases = true; - pinchActive = false; - prevValid = false; - lastZoomDir = ZOOM_NONE; - lastTime = now; - data->state = LV_INDEV_STATE_PRESSED; - } - else if (count == 2) - { - if (prevValid) - { - zoom_dir zoomDir = pinchZoom(touchPrev, touchRaw, dt_ms); - if (zoomDir != ZOOM_NONE && showMapToolBar) - { - pinchActive = true; - lastZoomDir = zoomDir; - } - } - touchPrev[0] = touchRaw[0]; - touchPrev[1] = touchRaw[1]; - prevValid = true; - lastTime = now; - } - } + lgfx::touch_point_t touchRaw[TOUCH_MAX_POINTS]; + static lgfx::touch_point_t touchPrev[TOUCH_MAX_POINTS]; + static bool prevValid = false; + static bool pinchActive = false; + static int lastZoomDir = ZOOM_NONE; + static unsigned long lastTime = 0; + + int count = tft.getTouch(touchRaw, TOUCH_MAX_POINTS); + + unsigned long now = millis(); + float dt_ms = (now > lastTime) ? (float)(now - lastTime) : 1.0f; + + if (count == 0) + { + data->state = LV_INDEV_STATE_RELEASED; + + if (pinchActive && lastZoomDir != 0) + { + if (lastZoomDir == ZOOM_IN) + lv_obj_send_event(btnZoomIn,LV_EVENT_CLICKED,NULL); + else if (lastZoomDir == ZOOM_OUT) + lv_obj_send_event(btnZoomOut,LV_EVENT_CLICKED,NULL); + } + pinchActive = false; + prevValid = false; + lastZoomDir = ZOOM_NONE; + lastTime = now; + + if (countTouchReleases) + { + countTouchReleases = false; + uint32_t touchReleaseTime = millis(); + if (!firstTouchReleaseTime) + firstTouchReleaseTime = touchReleaseTime; + numberTouchReleases++; + } + + if (millis() - firstTouchReleaseTime > TOUCH_DOUBLE_TOUCH_INTERVAL) + { + if (numberTouchReleases == 2) + { + if (activeTile == MAP) + lv_obj_send_event(mapTile, (lv_event_code_t)DOUBLE_TOUCH_EVENT, NULL); + } + + numberTouchReleases = 0; + firstTouchReleaseTime = 0; + } + } + else + { + if (count == 1) + { + if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_0) + { + data->point.x = touchRaw[count-1].x; + data->point.y = touchRaw[count-1].y; + } + else if (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270) + { + data->point.x = TFT_WIDTH - touchRaw[count-1].y; + data->point.y = touchRaw[count-1].x; + } + + countTouchReleases = true; + pinchActive = false; + prevValid = false; + lastZoomDir = ZOOM_NONE; + lastTime = now; + data->state = LV_INDEV_STATE_PRESSED; + } + else if (count == 2) + { + if (prevValid) + { + zoom_dir zoomDir = pinchZoom(touchPrev, touchRaw, dt_ms); + if (zoomDir != ZOOM_NONE && showMapToolBar) + { + pinchActive = true; + lastZoomDir = zoomDir; + } + } + touchPrev[0] = touchRaw[0]; + touchPrev[1] = touchRaw[1]; + prevValid = true; + lastTime = now; + } + } } #ifdef TDECK_ESP32S3 /** - * @brief LVGL T-DECK keyboard read + * @brief Reads a key value from the T-DECK keyboard via I2C. * + * @return The key value read from the keyboard. */ uint32_t keypadGetKey() { - char key_ch = 0; - Wire.requestFrom(0x55, 1); - while (Wire.available() > 0) - { - key_ch = Wire.read(); - } - return key_ch; + char key_ch = 0; + Wire.requestFrom(0x55, 1); + while (Wire.available() > 0) + { + key_ch = Wire.read(); + } + return key_ch; } /** - * @brief LVGL T-DECK keyboard read + * @brief Reads key input from the T-DECK keyboard for LVGL input device. * + * @param indev_driver Pointer to the LVGL input device driver. + * @param data Pointer to the structure that will be populated with key data. */ void IRAM_ATTR keypadRead(lv_indev_t *indev_driver, lv_indev_data_t *data) { - static uint32_t last_key = 0; - uint32_t act_key; - act_key = keypadGetKey(); - if (act_key != 0) - { - data->state = LV_INDEV_STATE_PRESSED; - last_key = act_key; - } - else - data->state = LV_INDEV_STATE_RELEASED; - - data->key = last_key; + static uint32_t last_key = 0; + uint32_t act_key; + act_key = keypadGetKey(); + if (act_key != 0) + { + data->state = LV_INDEV_STATE_PRESSED; + last_key = act_key; + } + else + data->state = LV_INDEV_STATE_RELEASED; + + data->key = last_key; } #endif #ifdef POWER_SAVE -extern const uint8_t BOARD_BOOT_PIN; +extern const uint8_t BOARD_BOOT_PIN; /**< GPIO pin number used for board boot functionality. */ /** -* @brief LVGL GPIO read -* -*/ + * @brief Reads GPIO button state for LVGL input device. + * + * @param indev_driver Pointer to the LVGL input device driver. + * @param data Pointer to the structure that will be populated with key data. + */ void IRAM_ATTR gpioRead(lv_indev_t *indev_driver, lv_indev_data_t *data) { - uint8_t currentStat = gpioGetBut(); - - if (currentStat == 0) - { - data->key = LV_KEY_ENTER; - data->state = LV_INDEV_STATE_PRESSED; - } - else - { - data->key = 0; - data->state = LV_INDEV_STATE_RELEASED; - } + uint8_t currentStat = gpioGetBut(); + + if (currentStat == 0) + { + data->key = LV_KEY_ENTER; + data->state = LV_INDEV_STATE_PRESSED; + } + else + { + data->key = 0; + data->state = LV_INDEV_STATE_RELEASED; + } } /** -* @brief LVGL GPIO long read event -* -*/ + * @brief Handles LVGL GPIO long press event for shutdown message and device power-off. + * + * @param event Pointer to the LVGL event structure. + */ void gpioLongEvent(lv_event_t *event) { - powerMsg = lv_msgbox_create(lv_scr_act()); - lv_obj_set_width(powerMsg,TFT_WIDTH); - lv_obj_set_align(powerMsg,LV_ALIGN_CENTER); - lv_obj_set_style_text_font(powerMsg, fontDefault, 0); - lv_obj_t *labelText = lv_msgbox_get_content(powerMsg); - lv_obj_set_style_text_align(labelText, LV_TEXT_ALIGN_CENTER, 0); - lv_msgbox_add_text(powerMsg, LV_SYMBOL_WARNING " This device will shutdown shortly"); - lv_obj_invalidate(powerMsg); - lv_refr_now(display); - vTaskDelay(2000); - power.deviceShutdown(); + showMsg(LV_SYMBOL_WARNING, " This device will shutdown shortly"); + vTaskDelay(2000); + power.deviceShutdown(); } /** -* @brief LVGL GPIO short read event -* -*/ + * @brief Handles LVGL GPIO short press event for sleep message and device suspend. + * + * @param event Pointer to the LVGL event structure. + */ void gpioClickEvent(lv_event_t *event) { - lv_indev_reset_long_press(lv_indev_active()); - lv_indev_reset(NULL,lv_scr_act()); - powerMsg = lv_msgbox_create(lv_scr_act()); - lv_obj_set_width(powerMsg,TFT_WIDTH); - lv_obj_set_align(powerMsg,LV_ALIGN_CENTER); - lv_obj_set_style_text_font(powerMsg, fontDefault, 0); - lv_obj_t *labelText = lv_msgbox_get_content(powerMsg); - lv_obj_set_style_text_align(labelText, LV_TEXT_ALIGN_CENTER, 0); - lv_msgbox_add_text(powerMsg, LV_SYMBOL_WARNING " This device will sleep shortly"); - lv_obj_invalidate(powerMsg); - lv_refr_now(display); - vTaskDelay(2000); - power.deviceSuspend(); + showMsg(LV_SYMBOL_WARNING, " This device will sleep shortly"); + vTaskDelay(2000); + power.deviceSuspend(); } /** -* @brief LVGL GPIO read -* -*/ + * @brief Reads the state of the GPIO button pin. + * + * @return The current state of the GPIO button pin. + */ uint8_t gpioGetBut() { - return digitalRead(BOARD_BOOT_PIN); + return digitalRead(BOARD_BOOT_PIN); } #endif @@ -255,164 +260,179 @@ uint8_t gpioGetBut() */ void applyModifyTheme(lv_theme_t *th, lv_obj_t *obj) { - LV_UNUSED(th); - if (!lv_obj_check_type(obj, &lv_led_class)) - { - if (!lv_obj_check_type(obj, &lv_button_class)) - lv_obj_add_style(obj, &styleThemeBkg, 0); - if (lv_obj_check_type(obj, &lv_button_class)) - lv_obj_add_style(obj, &styleObjectBkg, 0); - if (lv_obj_check_type(obj, &lv_switch_class)) - { - lv_obj_add_style(obj, &styleObjectBkg, 0); - lv_obj_add_style(obj, &styleObjectBkg, LV_PART_INDICATOR | LV_STATE_CHECKED); - } - if (lv_obj_check_type(obj, &lv_checkbox_class)) - { - lv_obj_add_style(obj, &styleThemeBkg, LV_PART_INDICATOR | LV_STATE_DEFAULT); - lv_obj_add_style(obj, &styleObjectBkg, LV_PART_INDICATOR | LV_STATE_CHECKED); - } - } + LV_UNUSED(th); + if (!lv_obj_check_type(obj, &lv_led_class)) + { + if (!lv_obj_check_type(obj, &lv_button_class)) + lv_obj_add_style(obj, &styleThemeBkg, 0); + if (lv_obj_check_type(obj, &lv_button_class)) + lv_obj_add_style(obj, &styleObjectBkg, 0); + if (lv_obj_check_type(obj, &lv_switch_class)) + { + lv_obj_add_style(obj, &styleObjectBkg, 0); + lv_obj_add_style(obj, &styleObjectBkg, LV_PART_INDICATOR | LV_STATE_CHECKED); + } + if (lv_obj_check_type(obj, &lv_checkbox_class)) + { + lv_obj_add_style(obj, &styleThemeBkg, LV_PART_INDICATOR | LV_STATE_DEFAULT); + lv_obj_add_style(obj, &styleObjectBkg, LV_PART_INDICATOR | LV_STATE_CHECKED); + } + } } /** - * @brief Custom Dark theme + * @brief Apply Custom Dark theme styles to the given object. + * + * @details Assigns custom styles to various LVGL objects (button, switch, checkbox, etc.) * + * @param th Pointer to the LVGL theme (unused). + * @param obj Pointer to the LVGL object to style. */ void modifyTheme() { - /*Initialize the styles*/ - lv_style_init(&styleThemeBkg); - lv_style_set_bg_color(&styleThemeBkg, lv_color_black()); - lv_style_set_border_color(&styleThemeBkg, lv_color_hex(objectColor)); - lv_style_init(&styleObjectBkg); - lv_style_set_bg_color(&styleObjectBkg, lv_color_hex(objectColor)); - lv_style_set_border_color(&styleObjectBkg, lv_color_hex(objectColor)); - lv_style_init(&styleObjectSel); - lv_style_set_bg_color(&styleObjectSel, lv_color_hex(0x757575)); - - /*Initialize the new theme from the current theme*/ - lv_theme_t *th_act = lv_disp_get_theme(NULL); - static lv_theme_t th_new; - th_new = *th_act; - - /*Set the parent theme and the style apply callback for the new theme*/ - lv_theme_set_parent(&th_new, th_act); - lv_theme_set_apply_cb(&th_new, applyModifyTheme); - - /*Assign the new theme to the current display*/ - lv_disp_set_theme(NULL, &th_new); + /*Initialize the styles*/ + lv_style_init(&styleThemeBkg); + lv_style_set_bg_color(&styleThemeBkg, lv_color_black()); + lv_style_set_border_color(&styleThemeBkg, lv_color_hex(objectColor)); + lv_style_init(&styleObjectBkg); + lv_style_set_bg_color(&styleObjectBkg, lv_color_hex(objectColor)); + lv_style_set_border_color(&styleObjectBkg, lv_color_hex(objectColor)); + lv_style_init(&styleObjectSel); + lv_style_set_bg_color(&styleObjectSel, lv_color_hex(0x757575)); + + /*Initialize the new theme from the current theme*/ + lv_theme_t *th_act = lv_disp_get_theme(NULL); + static lv_theme_t th_new; + th_new = *th_act; + + /*Set the parent theme and the style apply callback for the new theme*/ + lv_theme_set_parent(&th_new, th_act); + lv_theme_set_apply_cb(&th_new, applyModifyTheme); + + /*Assign the new theme to the current display*/ + lv_disp_set_theme(NULL, &th_new); } /** - * @brief Setting up tick task for lvgl - * - * @param arg + * @brief Setting up tick task for LVGL. Increments the LVGL tick counter. + * + * @param arg Pointer to optional argument (unused). */ void lv_tick_task(void *arg) { - (void)arg; - lv_tick_inc(LV_TICK_PERIOD_MS); + (void)arg; + lv_tick_inc(LV_TICK_PERIOD_MS); } /** - * @brief Init LVGL + * @brief Initialize LVGL, display, input devices, themes, and main screens. * + * @details Initialize the LVGL library, sets up the display buffers using PSRAM or RAM, + * configures input devices (touch, keypad, GPIO), applies the custom theme, creates main UI screens, + * and starts the periodic timer for LVGL ticks. */ void initLVGL() { - lv_init(); - - display = lv_display_create(TFT_WIDTH, TFT_HEIGHT); - lv_display_set_flush_cb(display, displayFlush); - lv_display_set_flush_wait_cb(display, NULL); - - size_t DRAW_BUF_SIZE = 0; - - #ifdef BOARD_HAS_PSRAM - assert(ESP.getFreePsram()); - - if ( ESP.getPsramSize() >= 4000000 ) - // >4Mb PSRAM - DRAW_BUF_SIZE = TFT_WIDTH * TFT_HEIGHT * sizeof(lv_color_t); - else - // 2Mb PSRAM - DRAW_BUF_SIZE = ( TFT_WIDTH * TFT_HEIGHT * sizeof(lv_color_t) / 8); - - log_v("LVGL: allocating %u bytes PSRAM for draw buffer",DRAW_BUF_SIZE * 2); - lv_color_t * drawBuf1 = (lv_color_t *)heap_caps_aligned_alloc(16, DRAW_BUF_SIZE, MALLOC_CAP_SPIRAM); - lv_color_t * drawBuf2 = (lv_color_t *)heap_caps_aligned_alloc(16, DRAW_BUF_SIZE, MALLOC_CAP_SPIRAM); - lv_display_set_buffers(display, drawBuf1, drawBuf2, DRAW_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL); - #else - DRAW_BUF_SIZE = TFT_WIDTH * TFT_HEIGHT / 10 * sizeof(lv_color_t); - log_v("LVGL: allocating %u bytes RAM for draw buffer",DRAW_BUF_SIZE); - lv_color_t * drawBuf1[DRAW_BUF_SIZE / 4]; - lv_display_set_buffers(display, drawBuf1, NULL, DRAW_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL); - #endif - - #ifdef TOUCH_INPUT - lv_indev_t *indev_drv = lv_indev_create(); - lv_indev_set_type(indev_drv, LV_INDEV_TYPE_POINTER); - // lv_indev_set_long_press_time(indev_drv, 1500); - lv_indev_set_read_cb(indev_drv, touchRead); - #endif - - #ifdef TDECK_ESP32S3 - scrGroup = lv_group_create(); - lv_group_set_default(scrGroup); - lv_indev_t *indev_keypad = lv_indev_create(); - lv_indev_set_type(indev_keypad, LV_INDEV_TYPE_KEYPAD); - lv_indev_set_read_cb(indev_keypad, keypadRead); - lv_indev_set_group(indev_keypad, lv_group_get_default()); - #endif - - #ifdef POWER_SAVE - lv_indev_t *indev_gpio = lv_indev_create(); - lv_indev_set_type(indev_gpio, LV_INDEV_TYPE_KEYPAD); - lv_indev_set_read_cb(indev_gpio, gpioRead); - lv_indev_set_long_press_time(indev_gpio, longPressTime); - - keyGroup = lv_group_create(); - lv_group_add_obj(keyGroup,lv_scr_act()); - lv_indev_set_group(indev_gpio, keyGroup); - - lv_indev_add_event_cb(indev_gpio, gpioLongEvent, LV_EVENT_LONG_PRESSED, NULL); - lv_indev_add_event_cb(indev_gpio, gpioClickEvent, LV_EVENT_SHORT_CLICKED, NULL); - #endif - - // Create Main Timer - mainTimer = lv_timer_create(updateMainScreen, UPDATE_MAINSCR_PERIOD, NULL); - lv_timer_ready(mainTimer); - - modifyTheme(); - - // Create Screens - createSearchSatScr(); - createMainScr(); - createNotifyBar(); - createSettingsScr(); - createMapSettingsScr(); - createDeviceSettingsScr(); - createButtonBarScr(); - createGpxDetailScreen(); - createGpxListScreen(); - - // Create and start a periodic timer interrupt to call lv_tick_inc - const esp_timer_create_args_t periodic_timer_args = { .callback = &lv_tick_task, .name = "periodic_gui" }; - esp_timer_handle_t periodic_timer; - ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); - ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000)); + lv_init(); + + // Initialize Maps memory pools + initializeMapsPools(); + + display = lv_display_create(TFT_WIDTH, TFT_HEIGHT); + lv_display_set_flush_cb(display, displayFlush); + lv_display_set_flush_wait_cb(display, NULL); + + size_t DRAW_BUF_SIZE = 0; + + #ifdef BOARD_HAS_PSRAM + assert(ESP.getFreePsram()); + + if ( ESP.getPsramSize() >= 4000000 ) + // >4Mb PSRAM + DRAW_BUF_SIZE = TFT_WIDTH * TFT_HEIGHT * sizeof(lv_color_t); + else + // 2Mb PSRAM + DRAW_BUF_SIZE = ( TFT_WIDTH * TFT_HEIGHT * sizeof(lv_color_t) / 8); + + log_v("LVGL: allocating %u bytes PSRAM for draw buffer",DRAW_BUF_SIZE * 2); + lv_color_t * drawBuf1 = (lv_color_t *)heap_caps_aligned_alloc(16, DRAW_BUF_SIZE, MALLOC_CAP_SPIRAM); + lv_color_t * drawBuf2 = (lv_color_t *)heap_caps_aligned_alloc(16, DRAW_BUF_SIZE, MALLOC_CAP_SPIRAM); + lv_display_set_buffers(display, drawBuf1, drawBuf2, DRAW_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL); + #else + DRAW_BUF_SIZE = TFT_WIDTH * TFT_HEIGHT / 10 * sizeof(lv_color_t); + log_v("LVGL: allocating %u bytes RAM for draw buffer",DRAW_BUF_SIZE); + lv_color_t * drawBuf1[DRAW_BUF_SIZE / 4]; + lv_display_set_buffers(display, drawBuf1, NULL, DRAW_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL); + #endif + + #ifdef TOUCH_INPUT + lv_indev_t *indev_drv = lv_indev_create(); + lv_indev_set_type(indev_drv, LV_INDEV_TYPE_POINTER); + // lv_indev_set_long_press_time(indev_drv, 1500); + lv_indev_set_read_cb(indev_drv, touchRead); + #endif + + #ifdef TDECK_ESP32S3 + scrGroup = lv_group_create(); + lv_group_set_default(scrGroup); + lv_indev_t *indev_keypad = lv_indev_create(); + lv_indev_set_type(indev_keypad, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_read_cb(indev_keypad, keypadRead); + lv_indev_set_group(indev_keypad, lv_group_get_default()); + #endif + + #ifdef POWER_SAVE + lv_indev_t *indev_gpio = lv_indev_create(); + lv_indev_set_type(indev_gpio, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_read_cb(indev_gpio, gpioRead); + lv_indev_set_long_press_time(indev_gpio, longPressTime); + + keyGroup = lv_group_create(); + lv_group_add_obj(keyGroup,lv_scr_act()); + lv_indev_set_group(indev_gpio, keyGroup); + + lv_indev_add_event_cb(indev_gpio, gpioLongEvent, LV_EVENT_LONG_PRESSED, NULL); + lv_indev_add_event_cb(indev_gpio, gpioClickEvent, LV_EVENT_SHORT_CLICKED, NULL); + #endif + + // Create Main Timer + mainTimer = lv_timer_create(updateMainScreen, UPDATE_MAINSCR_PERIOD, NULL); + lv_timer_ready(mainTimer); + + modifyTheme(); + + // Create Screens + createSearchSatScr(); + createMainScr(); + createNotifyBar(); + createSettingsScr(); + createMapSettingsScr(); + createDeviceSettingsScr(); + createButtonBarScr(); + createGpxDetailScreen(); + createGpxListScreen(); + + // Create and start a periodic timer interrupt to call lv_tick_inc + const esp_timer_create_args_t periodic_timer_args = { .callback = &lv_tick_task, .name = "periodic_gui" }; + esp_timer_handle_t periodic_timer; + ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000)); } /** * @brief Load GPS Main Screen - * */ void loadMainScreen() { - isMainScreen = true; - isScrolled = true; - isSearchingSat = false; - gpxAction = WPT_NONE; - lv_screen_load(mainScreen); + isMainScreen = true; + isScrolled = true; + isSearchingSat = false; + gpxAction = WPT_NONE; + lv_obj_clear_flag(menuBtn,LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(buttonBar, LV_OBJ_FLAG_HIDDEN); + if (mapView.isMapFound) + lv_obj_clear_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(navArrow, LV_OBJ_FLAG_HIDDEN); + lv_screen_load(mainScreen); } diff --git a/lib/lvgl/src/lvglSetup.hpp b/lib/lvgl/src/lvglSetup.hpp index 63eda4c6..c4fe2b12 100644 --- a/lib/lvgl/src/lvglSetup.hpp +++ b/lib/lvgl/src/lvglSetup.hpp @@ -2,8 +2,8 @@ * @file lvglSetup.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LVGL Screen implementation - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -23,12 +23,9 @@ #include "deviceSettingsScr.hpp" #include "mapSettingsScr.hpp" #include "gestures.hpp" +#include "maps.hpp" -/** - * @brief Default display driver definition - * - */ -static uint32_t objectColor = 0x303030; +static uint32_t objectColor = 0x303030; /**< Default display driver color definition. */ void IRAM_ATTR displayFlush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map); void IRAM_ATTR touchRead(lv_indev_t *indev_driver, lv_indev_data_t *data); @@ -37,7 +34,7 @@ void IRAM_ATTR touchRead(lv_indev_t *indev_driver, lv_indev_data_t *data); uint32_t keypadGetKey(); #endif #ifdef POWER_SAVE - static const uint16_t longPressTime = 1000; // Long press time + static const uint16_t longPressTime = 1000; /**< Long press time threshold in milliseconds. */ void IRAM_ATTR gpioRead(lv_indev_t *indev_driver, lv_indev_data_t *data); void gpioLongEvent(lv_event_t *event); void gpioClickEvent(lv_event_t *event); diff --git a/lib/maps/src/mapVars.h b/lib/maps/src/mapVars.h index 46018019..08b576ea 100644 --- a/lib/maps/src/mapVars.h +++ b/lib/maps/src/mapVars.h @@ -2,116 +2,36 @@ * @file mapsVars.h * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Maps vars and structures - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once - /** - * @brief Map Widgets images. - * - */ #include "bruj.h" -#include "navigation.h" -#include "waypoint.h" #include "compass.h" -#include "zoom.h" -#include "speed.h" -#include "expand.h" -#include "collapse.h" -#include "zoomin.h" -#include "zoomout.h" +#include "waypoint.h" #include "navfinish.h" -#include "move.h" +#include "straight.h" +#include "slleft.h" +#include "slright.h" +#include "tleft.h" +#include "tright.h" +#include "uleft.h" +#include "uright.h" +#include "finish.h" +#include "outtrack.h" #include "globalGpxDef.h" -/** - * @brief Point in 16 bits projected coordinates (x,y) Vector Maps - * - */ -struct Point16 -{ - Point16(){}; - Point16(int16_t x, int16_t y) : x(x), y(y){}; - Point16 operator-(const Point16 p) { return Point16(x - p.x, y - p.y); }; - Point16 operator+(const Point16 p) { return Point16(x + p.x, y + p.y); }; - Point16(char *coordsPair); - int16_t x; - int16_t y; -}; - -/** - * @brief Point in 32 bits projected coordinates (x,y) Vector Maps - * - */ -struct Point32 -{ - Point32(){}; - Point32(int32_t x, int32_t y) : x(x), y(y){}; - Point32(Point16 p) : x(p.x), y(p.y){}; - Point32 operator-(const Point32 p) { return Point32(x - p.x, y - p.y); }; - Point32 operator+(const Point32 p) { return Point32(x + p.x, y + p.y); }; - Point16 toPoint16() { return Point16(x, y); }; - bool operator==(const Point32 p) { return x == p.x && y == p.y; }; - int32_t x; - int32_t y; -}; - -/** - * @brief Bounding Box - * - */ -struct BBox -{ - BBox(){}; - BBox(Point32 min, Point32 max) : min(min), max(max){}; - BBox operator-(const Point32 p) { return BBox(min - p, max - p); }; - bool containsPoint(const Point32 p); - bool intersects(const BBox b); - Point32 min; - Point32 max; -}; - -static const String mapVectorFolder PROGMEM = "/sdcard/VECTMAP/"; // Vector Map Files Folder -static const char *mapRenderFolder PROGMEM = "/sdcard/MAP/%u/%u/%u.png"; // Render Maps file folder -static const char *noMapFile PROGMEM = "/spiffs/NOMAP.png"; // No map image file +static const char *mapVectorFolder PROGMEM = "/sdcard/VECTMAP/%u/%u/%u.bin"; /**< Vector Map Files Folder */ +static const char *mapRenderFolder PROGMEM = "/sdcard/MAP/%u/%u/%u.png"; /**< Render Maps file folder */ +static const char *noMapFile PROGMEM = "/spiffs/NOMAP.png"; /**< No map image file */ static const char *map_scale[] PROGMEM = {"5000 Km", "2500 Km", "1500 Km", - "700 Km", "350 Km", "150 Km", - "100 Km", "40 Km", "20 Km", - "10 Km", "5 Km", "2,5 Km", - "1,5 Km", "700 m", "350 m", - "150 m", "80 m", "40 m", - "20 m", "10 m"}; // Scale label for map - - -/** - * @brief Vector map object colours - * - */ -static const uint16_t WHITE = 0xFFFF; -static const uint16_t BLACK = 0x0000; -static const uint16_t GREEN = 0x76EE; -static const uint16_t GREENCLEAR = 0x9F93; -static const uint16_t GREENCLEAR2 = 0xCF6E; -static const uint16_t BLUE = 0x227E; -static const uint16_t BLUECLEAR = 0x6D3E; -static const uint16_t CYAN = 0xB7FF; -static const uint16_t VIOLET = 0xAA1F; -static const uint16_t ORANGE = 0xFCC2; -static const uint16_t GRAY = 0x94B2; -static const uint16_t GRAYCLEAR = 0xAD55; -static const uint16_t GRAYCLEAR2 = 0xD69A; -static const uint16_t BROWN = 0xAB00; -static const uint16_t YELLOWCLEAR = 0xFFF5; -static const uint16_t BACKGROUND_COLOR = 0xEF5D; - -/** - * @brief Vector maps memory definition - * - */ -#define MAPBLOCKS_MAX 6 // max blocks in memory -#define MAPBLOCK_SIZE_BITS 12 // 4096 x 4096 coords (~meters) per block -#define MAPFOLDER_SIZE_BITS 4 // 16 x 16 map blocks per folder -#define MAX_ZOOM 4 // Vector max MAX_ZOOM \ No newline at end of file + "700 Km", "350 Km", "150 Km", + "100 Km", "40 Km", "20 Km", + "10 Km", "5 Km", "2,5 Km", + "1,5 Km", "700 m", "350 m", + "150 m", "80 m", "40 m", + "20 m", "10 m" + }; /**< Scale label for map */ diff --git a/lib/maps/src/maps.cpp b/lib/maps/src/maps.cpp index d588252b..ffd87577 100644 --- a/lib/maps/src/maps.cpp +++ b/lib/maps/src/maps.cpp @@ -1,1380 +1,2503 @@ /** * @file maps.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) - Render Maps - * @author @aresta - https://github.com/aresta/ESP32_GPS - Vector Maps * @brief Maps draw class - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "maps.hpp" +#include +#include +#include +#include +#include extern Compass compass; extern Gps gps; extern Storage storage; -extern std::vector trackData; +extern std::vector trackData; /**< Vector containing track waypoints */ const char* TAG PROGMEM = "Maps"; -extern Point16::Point16(char *coordsPair) -{ - char *next; - x = (int16_t)round(strtod(coordsPair, &next)); // 1st coord // TODO: change by strtol and test - y = (int16_t)round(strtod(++next, NULL)); // 2nd coord -} +uint16_t Maps::currentDrawColor = TFT_WHITE; +uint8_t Maps::PALETTE[256] = {0}; +uint32_t Maps::PALETTE_SIZE = 0; + +// Tile cache system static variables +std::vector Maps::tileCache; +size_t Maps::maxCachedTiles = 0; +uint32_t Maps::cacheAccessCounter = 0; + +// Unified memory pool system static variables (experimental) +std::vector Maps::unifiedPool; +SemaphoreHandle_t Maps::unifiedPoolMutex = nullptr; +size_t Maps::maxUnifiedPoolEntries = 0; +uint32_t Maps::unifiedPoolHitCount = 0; +uint32_t Maps::unifiedPoolMissCount = 0; + +// Memory monitoring static variables +uint32_t Maps::totalMemoryAllocations = 0; +uint32_t Maps::totalMemoryDeallocations = 0; +uint32_t Maps::peakMemoryUsage = 0; +uint32_t Maps::currentMemoryUsage = 0; +uint32_t Maps::poolEfficiencyScore = 0; +uint32_t Maps::lastStatsUpdate = 0; + +// Polygon optimization system static variables +bool Maps::polygonCullingEnabled = true; +bool Maps::optimizedScanlineEnabled = true; +uint32_t Maps::polygonRenderCount = 0; +uint32_t Maps::polygonCulledCount = 0; +uint32_t Maps::polygonOptimizedCount = 0; + + +// Efficient batch rendering static variables +Maps::RenderBatch* Maps::activeBatch = nullptr; +size_t Maps::maxBatchSize = 0; +uint32_t Maps::batchRenderCount = 0; +uint32_t Maps::batchOptimizationCount = 0; +uint32_t Maps::batchFlushCount = 0; -bool BBox::containsPoint(const Point32 p) { return p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y; } - -bool BBox::intersects(BBox b) -{ - if (b.min.x > max.x || b.max.x < min.x || b.min.y > max.y || b.max.y < min.y) - return false; - return true; -} /** * @brief Map Class constructor * + * @details Initializes the Maps class with default polygon filling enabled and logs completion status. */ -Maps::Maps() {} - -// Render Map Private section +Maps::Maps() : fillPolygons(true) +{ + ESP_LOGI(TAG, "Maps constructor completed"); +} /** * @brief Get pixel X position from OpenStreetMap Render map longitude * - * @param f_lon -> longitude - * @param zoom -> zoom - * @param tileSize -> tile size - * @return X position + * @details Converts a longitude value to the corresponding pixel X position + * for a given zoom level and tile size in an OpenStreetMap render map. + * + * @param f_lon Longitude coordinate. + * @param zoom Zoom level. + * @param tileSize Size of the map tile in pixels. + * @return Pixel X position within the tile. */ uint16_t Maps::lon2posx(float f_lon, uint8_t zoom, uint16_t tileSize) { - return ((uint16_t)(((f_lon + 180.0) / 360.0 * (pow(2.0, zoom)) * tileSize)) % tileSize); + // Use direct calculation (more efficient than complex transformation matrices) + return static_cast(((f_lon + 180.0f) / 360.0f * (1 << zoom) * tileSize)) % tileSize; } /** * @brief Get pixel Y position from OpenStreetMap Render map latitude * - * @param f_lat -> latitude - * @param zoom -> zoom - * @param tileSize -> tile size - * @return Y position + * @details Converts a latitude value to the corresponding pixel Y position + * for a given zoom level and tile size in an OpenStreetMap render map. + * + * @param f_lat Latitude coordinate. + * @param zoom Zoom level. + * @param tileSize Size of the map tile in pixels. + * @return Pixel Y position within the tile. */ uint16_t Maps::lat2posy(float f_lat, uint8_t zoom, uint16_t tileSize) { - return ((uint16_t)(((1.0 - log(tan(f_lat * M_PI / 180.0) + 1.0 / cos(f_lat * M_PI / 180.0)) / M_PI) / 2.0 * (pow(2.0, zoom)) * tileSize)) % tileSize); + // Use direct calculation (more efficient than complex transformation matrices) + float lat_rad = f_lat * static_cast(M_PI) / 180.0f; + float siny = tanf(lat_rad) + 1.0f / cosf(lat_rad); + float merc_n = logf(siny); + float scale = (1 << zoom) * tileSize; + return static_cast(((1.0f - merc_n / static_cast(M_PI)) / 2.0f * scale)) % tileSize; } /** * @brief Get TileX for OpenStreetMap files * - * @param f_lon -> longitude - * @param zoom -> zoom - * @return X value (folder) + * @details Converts a longitude value to the corresponding tile X index (folder) for OpenStreetMap files, + * for a given zoom level. + * + * @param f_lon Longitude coordinate. + * @param zoom Zoom level. + * @return X tile index (folder). */ -uint32_t Maps::lon2tilex(double f_lon, uint8_t zoom) +uint32_t Maps::lon2tilex(float f_lon, uint8_t zoom) { - double rawTile = (f_lon + 180.0) / 360.0 * pow(2.0, zoom); - rawTile += 1e-6; - return (uint32_t)(floor(rawTile)); + float rawTile = (f_lon + 180.0f) / 360.0f * (1 << zoom); + rawTile += 1e-6f; + return static_cast(rawTile); } /** * @brief Get TileY for OpenStreetMap files * - * @param f_lat -> latitude - * @param zoom -> zoom - * @return Y value (file) + * @details Converts a latitude value to the corresponding tile Y index (file) for OpenStreetMap files, + * for a given zoom level. + * + * @param f_lat Latitude coordinate. + * @param zoom Zoom level. + * @return Y tile index (file). */ -uint32_t Maps::lat2tiley(double f_lat, uint8_t zoom) +uint32_t Maps::lat2tiley(float f_lat, uint8_t zoom) { - double rawTile = (1.0 - log(tan(f_lat * M_PI / 180.0) + 1.0 / cos(f_lat * M_PI / 180.0)) / M_PI) / 2.0 * pow(2.0, zoom); - rawTile += 1e-6; - return (uint32_t)(floor(rawTile)); + float lat_rad = f_lat * static_cast(M_PI) / 180.0f; + float siny = tanf(lat_rad) + 1.0f / cosf(lat_rad); + float merc_n = logf(siny); + float rawTile = (1.0f - merc_n / static_cast(M_PI)) / 2.0f * (1 << zoom); + rawTile += 1e-6f; + return static_cast(rawTile); } /** * @brief Get Longitude from OpenStreetMap files * - * @param tileX -> tile X - * @param zoom -> zoom - * @return longitude + * @details Converts a tile X index to the corresponding longitude value for OpenStreetMap files, + * for a given zoom level. + * + * @param tileX Tile X index. + * @param zoom Zoom level. + * @return Longitude coordinate. */ -double Maps::tilex2lon(uint32_t tileX, uint8_t zoom) +float Maps::tilex2lon(uint32_t tileX, uint8_t zoom) { - return tileX / pow(2.0, zoom) * 360.0 - 180.0; + return static_cast(tileX) * 360.0f / (1 << zoom) - 180.0f; } /** * @brief Get Latitude from OpenStreetMap files * - * @param tileX -> tile Y - * @param zoom -> zoom - * @return latitude + * @details Converts a tile Y index to the corresponding latitude value for OpenStreetMap files, + * for a given zoom level. + * + * @param tileY Tile Y index. + * @param zoom Zoom level. + * @return Latitude coordinate. */ -double Maps::tiley2lat(uint32_t tileY, uint8_t zoom) +float Maps::tiley2lat(uint32_t tileY, uint8_t zoom) { - double n = M_PI - 2.0 * M_PI * tileY / pow(2.0, zoom); - return 180.0 / M_PI * atan(sinh(n)); + float scale = static_cast(1 << zoom); + float n = static_cast(M_PI) * (1.0f - 2.0f * static_cast(tileY) / scale); + return 180.0f / static_cast(M_PI) * atanf(sinhf(n)); } /** * @brief Get the map tile structure from GPS Coordinates * - * @param lon -> Longitude - * @param lat -> Latitude - * @param zoomLevel -> zoom level - * @param offsetX -> Tile Offset X - * @param offsetY -> Tile Offset Y - * @return MapTile -> Map Tile structure + * @details Constructs a MapTile structure from the given GPS coordinates, zoom level, and optional tile offsets. + * + * @param lon Longitude coordinate. + * @param lat Latitude coordinate. + * @param zoomLevel Zoom level. + * @param offsetX Tile offset X. + * @param offsetY Tile offset Y. + * @return MapTile structure containing tile indices, file path, zoom, and GPS coordinates. */ -Maps::MapTile Maps::getMapTile(double lon, double lat, uint8_t zoomLevel, int8_t offsetX, int8_t offsetY) +Maps::MapTile Maps::getMapTile(float lon, float lat, uint8_t zoomLevel, int8_t offsetX, int8_t offsetY) { - char tileFile[255]; - uint32_t x = Maps::lon2tilex(lon, zoomLevel) + offsetX; - uint32_t y = Maps::lat2tiley(lat, zoomLevel) + offsetY; - - sprintf(tileFile, mapRenderFolder, zoomLevel, x, y); - MapTile data; - strcpy(data.file,tileFile); - data.tilex = x; - data.tiley = y; - data.zoom = zoomLevel; - data.lat = lat; - data.lon = lon; - return data; + MapTile data; + data.tilex = Maps::lon2tilex(lon, zoomLevel) + offsetX; + data.tiley = Maps::lat2tiley(lat, zoomLevel) + offsetY; + data.zoom = zoomLevel; + data.lat = lat; + data.lon = lon; + + if (mapSet.vectorMap) + snprintf(data.file, sizeof(data.file), mapVectorFolder, zoomLevel, data.tilex, data.tiley); + else + snprintf(data.file, sizeof(data.file), mapRenderFolder, zoomLevel, data.tilex, data.tiley); + + return data; } -// Vector Map Private section +/** + * @brief Get min and max longitude and latitude from tile + * + * @details Returns the geographic boundaries (min/max longitude and latitude) for the specified tile coordinates and zoom level. + * + * @param tileX Tile X coordinate. + * @param tileY Tile Y coordinate. + * @param zoom Zoom level. + * @return tileBounds Structure containing min and max longitude and latitude. + */ +Maps::tileBounds Maps::getTileBounds(uint32_t tileX, uint32_t tileY, uint8_t zoom) +{ + tileBounds bounds; + bounds.lon_min = Maps::tilex2lon(tileX, zoom); + bounds.lat_min = Maps::tiley2lat(tileY + 1, zoom); + bounds.lon_max = Maps::tilex2lon(tileX + 1, zoom); + bounds.lat_max = Maps::tiley2lat(tileY, zoom); + return bounds; +} /** - * @brief Get pixel Y position from OpenStreetMap Vector map latitude + * @brief Check if coordinates are in map bounds * - * @param lat -> latitude - * @return Y position + * @details Checks if the latitude and longitude are within the specified tileBounds. + * + * @param lat Latitude to check. + * @param lon Longitude to check. + * @param bound Map bounds to check against. + * @return true if the coordinate is inside the bounds, false otherwise. */ -double Maps::lat2y(double lat) +bool Maps::isCoordInBounds(float lat, float lon, tileBounds bound) { - return log(tan(DEG2RAD(lat) / 2 + M_PI / 4)) * EARTH_RADIUS; + return (lat >= bound.lat_min && lat <= bound.lat_max && + lon >= bound.lon_min && lon <= bound.lon_max); } /** - * @brief Get pixel X position from OpenStreetMap Vector map longitude + * @brief Convert GPS Coordinates to screen position (with offsets) * - * @param lon -> longitude - * @return X position + * @details Converts the given longitude and latitude to screen coordinates at a specific zoom level and tile size. + * + * @param lon Longitude in degrees. + * @param lat Latitude in degrees. + * @param zoomLevel Zoom level of the map. + * @param tileSize Size of a single map tile in pixels. + * @return ScreenCoord Screen position (x, y) corresponding to the GPS coordinates. */ -double Maps::lon2x(double lon) +Maps::ScreenCoord Maps::coord2ScreenPos(float lon, float lat, uint8_t zoomLevel, uint16_t tileSize) { - return DEG2RAD(lon) * EARTH_RADIUS; + ScreenCoord data; + data.posX = Maps::lon2posx(lon, zoomLevel, tileSize); + data.posY = Maps::lat2posy(lat, zoomLevel, tileSize); + return data; } /** - * @brief Get longitude from X position in Vector Map (Mercator projection) + * @brief Get position X,Y in render map for a coordinate * - * @param x -> X position - * @return longitude + * @details Converts latitude and longitude into pixel X,Y positions on the rendered map, given the map bounds. + * + * @param lat Latitude of the coordinate. + * @param lon Longitude of the coordinate. + * @param bound Map bounds (min/max lat/lon) of the tile. + * @param pixelX Pointer to store the computed X position on the map. + * @param pixelY Pointer to store the computed Y position on the map. */ -double Maps::mercatorX2lon(double x) +void Maps::coords2map(float lat, float lon, tileBounds bound, uint16_t *pixelX, uint16_t *pixelY) { - return (x / EARTH_RADIUS) * (180.0 / M_PI); + float lon_ratio = (lon - bound.lon_min) / (bound.lon_max - bound.lon_min); + float lat_ratio = (bound.lat_max - lat) / (bound.lat_max - bound.lat_min); + + *pixelX = (uint16_t)(lon_ratio * Maps::tileWidth); + *pixelY = (uint16_t)(lat_ratio * Maps::tileHeight); } /** - * @brief Get latitude from Y position in Vector Map (Mercator projection) + * @brief Load No Map Image * - * @param y -> Y position - * @return latitude + * @details Draws a "No Map Found" PNG image + * + * @param map Reference to the TFT_eSprite map object. */ -double Maps::mercatorY2lat(double y) +void Maps::showNoMap(TFT_eSprite &map) { - return (atan(sinh(y / EARTH_RADIUS))) * (180.0 / M_PI); + map.drawPngFile(noMapFile, (Maps::mapScrWidth / 2) - 50, (Maps::mapScrHeight / 2) - 50); + map.drawCenterString("NO MAP FOUND", (Maps::mapScrWidth / 2), (Maps::mapScrHeight >> 1) + 65, &fonts::DejaVu18); } /** - * @brief Points to screen coordinates + * @brief Init map size * - * @param pxy - * @param screenCenterxy - * @return int16_t + * @details Initializes the map screen size and allocates buffer space for rendering the map. + * Also resets map tile state and navigation arrow position, and sets default map bounds. + * + * @param mapHeight Screen map size height. + * @param mapWidth Screen map size width. */ -int16_t Maps::toScreenCoord(const int32_t pxy, const int32_t screenCenterxy) +void Maps::initMap(uint16_t mapHeight, uint16_t mapWidth) { - return round((double)(pxy - screenCenterxy) / zoom) + (double)Maps::tileWidth / 2; + Maps::mapScrHeight = mapHeight; + Maps::mapScrWidth = mapWidth; + + // Reserve PSRAM for buffer map + Maps::mapTempSprite.deleteSprite(); + Maps::mapTempSprite.createSprite(tileHeight, tileWidth); + + Maps::oldMapTile = {}; // Old Map tile coordinates and zoom + Maps::currentMapTile = {}; // Current Map tile coordinates and zoom + Maps::roundMapTile = {}; // Boundaries Map tiles + Maps::navArrowPosition = {0, 0}; // Map Arrow position + + Maps::totalBounds = {90.0, -90.0, 180.0, -180.0}; + + // Initialize tile cache system + initTileCache(); + + // Initialize background preload system + + // Initialize polygon optimizations + // Initialize polygon optimization system + polygonCullingEnabled = true; + optimizedScanlineEnabled = false; + polygonRenderCount = 0; + polygonCulledCount = 0; + polygonOptimizedCount = 0; + + // Initialize batch rendering + initBatchRendering(); } /** - * @brief Returns int16 or 0 if empty + * @brief Delete map screen and release PSRAM * - * @param file - * @return int16_t + * @details Deletes the main map sprite to free up PSRAM. */ -int16_t Maps::parseInt16(char *file) +void Maps::deleteMapScrSprites() { - char num[16]; - uint8_t i; - char c; - i = 0; - c = file[Maps::idx]; - if (c == '\n') - return 0; - while (c >= '0' && c <= '9') - { - assert(i < 15); - c = file[Maps::idx]; - num[i] = c; - Maps::idx++; - i++; - - c = file[Maps::idx]; - } - num[i] = '\0'; - - if (c != ';' && c != ',' && c != '\n') - { - ESP_LOGE(TAG, "parseInt16 error: %c %i", c, c); - ESP_LOGE(TAG, "Num: [%s]", num); - while (1); - } - try - { - Maps::idx++; - return std::stoi(num); - } - catch (std::invalid_argument) - { - ESP_LOGE(TAG, "parseInt16 invalid_argument: [%c] [%s]", c, num); - } - catch (std::out_of_range) - { - ESP_LOGE(TAG, "parseInt16 out_of_range: [%c] [%s]", c, num); - } - return -1; + Maps::mapSprite.deleteSprite(); + + // Clear tile cache to free memory + clearTileCache(); + + + // Clear memory pool + clearUnifiedPool(); } /** - * @brief Returns the string until terminator char or newline. The terminator character is not included but consumed from stream. + * @brief Create map screen * - * @param file - * @param terminator - * @param str + * @details Creates the main map sprite with the current screen width and height, allocating memory for rendering. */ -void Maps::parseStrUntil(char *file, char terminator, char *str) +void Maps::createMapScrSprites() { - uint8_t i; - char c; - i = 0; - c = file[Maps::idx]; - while (c != terminator && c != '\n') - { - assert(i < 29); - str[i] = c; - Maps::idx++; - i++; - c = file[Maps::idx]; - } - str[i] = '\0'; - Maps::idx++; + Maps::mapBuffer = Maps::mapSprite.createSprite(Maps::mapScrWidth, Maps::mapScrHeight); } /** - * @brief Parse vector file to coords + * @brief Generate map + * + * @details Generates the main map by compositing the center and surrounding tiles based on the current zoom level. + * Handles missing tiles, updates map bounds, overlays missing map notifications, and draws tracks if available. * - * @param file - * @param points + * @param zoom Zoom Level */ -void Maps::parseCoords(char *file, std::vector &points) +void Maps::generateMap(uint8_t zoom) { - char str[30]; - assert(points.size() == 0); - Point16 point; - while (true) - { - try - { - parseStrUntil(file, ',', str); - if (str[0] == '\0') - break; - point.x = (int16_t)std::stoi(str); - parseStrUntil(file, ';', str); - assert(str[0] != '\0'); - point.y = (int16_t)std::stoi(str); - } - catch (std::invalid_argument) - { - ESP_LOGE(TAG, "parseCoords invalid_argument: %s", str); - } - catch (std::out_of_range) + // Clear cache if zoom level changed (tiles are not compatible between zoom levels) + if (Maps::zoomLevel != zoom && Maps::zoomLevel != 0) { - ESP_LOGE(TAG, "parseCoords out_of_range: %s", str); - } - points.push_back(point); - } + ESP_LOGI(TAG, "Zoom level changed from %d to %d - clearing cache", Maps::zoomLevel, zoom); + clearTileCache(); + } + + Maps::zoomLevel = zoom; + + bool foundRoundMap = false; + bool missingMap = false; + + const float lat = Maps::followGps ? gps.gpsData.latitude : Maps::currentMapTile.lat; + const float lon = Maps::followGps ? gps.gpsData.longitude : Maps::currentMapTile.lon; + + Maps::currentMapTile = Maps::getMapTile(lon, lat, Maps::zoomLevel, 0, 0); + + // Detects if tile changes from actual GPS position + if (strcmp(Maps::currentMapTile.file, Maps::oldMapTile.file) != 0 || + Maps::currentMapTile.zoom != Maps::oldMapTile.zoom || + Maps::currentMapTile.tilex != Maps::oldMapTile.tilex || + Maps::currentMapTile.tiley != Maps::oldMapTile.tiley) + { + const int16_t size = Maps::mapTileSize; + + Maps::mapTempSprite.fillScreen(TFT_WHITE); + + if (mapSet.vectorMap) + Maps::isMapFound = renderTile(Maps::currentMapTile.file, size, size,Maps::mapTempSprite); + else + Maps::isMapFound = Maps::mapTempSprite.drawPngFile(Maps::currentMapTile.file, size, size); + + + Maps::oldMapTile = Maps::currentMapTile; + strcpy(Maps::oldMapTile.file, Maps::currentMapTile.file); + + if (!Maps::isMapFound) + { + ESP_LOGI(TAG, "No Map Found!"); + Maps::isMapFound = false; + Maps::mapTempSprite.fillScreen(TFT_BLACK); + Maps::showNoMap(Maps::mapTempSprite); + } + else + { + Maps::totalBounds = Maps::getTileBounds( + Maps::currentMapTile.tilex, Maps::currentMapTile.tiley, Maps::zoomLevel); + + const int8_t startX = -1; + const int8_t startY = -1; + + for (int8_t y = startY; y <= startY + 2; y++) + { + const int16_t offsetY = (y - startY) * size; + + for (int8_t x = startX; x <= startX + 2; x++) + { + if (x == 0 && y == 0) continue; // Skip center tile + + const int16_t offsetX = (x - startX) * size; + + Maps::roundMapTile = getMapTile( + Maps::currentMapTile.lon, Maps::currentMapTile.lat, + Maps::zoomLevel, x, y); + + if (mapSet.vectorMap) + foundRoundMap = renderTile(Maps::roundMapTile.file, offsetX, offsetY,Maps::mapTempSprite); + else + foundRoundMap = Maps::mapTempSprite.drawPngFile(Maps::roundMapTile.file, offsetX, offsetY); + + if (!foundRoundMap) + { + Maps::mapTempSprite.fillRect(offsetX, offsetY, size, size, TFT_BLACK); + Maps::mapTempSprite.drawPngFile(noMapFile, + offsetX + size / 2 - 50, + offsetY + size / 2 - 50); + missingMap = true; + } + else + { + const tileBounds currentBounds = Maps::getTileBounds( + Maps::roundMapTile.tilex, Maps::roundMapTile.tiley, Maps::zoomLevel); + + if (currentBounds.lat_min < Maps::totalBounds.lat_min) + Maps::totalBounds.lat_min = currentBounds.lat_min; + if (currentBounds.lat_max > Maps::totalBounds.lat_max) + Maps::totalBounds.lat_max = currentBounds.lat_max; + if (currentBounds.lon_min < Maps::totalBounds.lon_min) + Maps::totalBounds.lon_min = currentBounds.lon_min; + if (currentBounds.lon_max > Maps::totalBounds.lon_max) + Maps::totalBounds.lon_max = currentBounds.lon_max; + } + } + } + + if (!missingMap) + { + if (Maps::isCoordInBounds(Maps::destLat, Maps::destLon, Maps::totalBounds)) + { + Maps::coords2map(Maps::destLat, Maps::destLon, + Maps::totalBounds, &wptPosX, &wptPosY); + } + } + else + { + Maps::wptPosX = -1; + Maps::wptPosY = -1; + } + + Maps::redrawMap = true; + + + for (size_t i = 1; i < trackData.size(); ++i) + { + const auto &p1 = trackData[i - 1]; + const auto &p2 = trackData[i]; + + if (p1.lon > Maps::totalBounds.lon_min && p1.lon < Maps::totalBounds.lon_max && + p1.lat > Maps::totalBounds.lat_min && p1.lat < Maps::totalBounds.lat_max && + p2.lon > Maps::totalBounds.lon_min && p2.lon < Maps::totalBounds.lon_max && + p2.lat > Maps::totalBounds.lat_min && p2.lat < Maps::totalBounds.lat_max) + { + uint16_t x1, y1, x2, y2; + Maps::coords2map(p1.lat, p1.lon, Maps::totalBounds, &x1, &y1); + Maps::coords2map(p2.lat, p2.lon, Maps::totalBounds, &x2, &y2); + Maps::mapTempSprite.drawWideLine(x1, y1, x2, y2, 2, TFT_BLUE); + } + } + } + } } /** - * @brief Parse Mapbox + * @brief Display Map * - * @param str - * @return BBox + * @details Displays the map on the screen with proper rotation, waypoint overlay, and GPS navigation arrow positioning. + * Handles both GPS-following mode and manual panning mode with appropriate pivot points and rotations. */ -BBox Maps::parseBbox(String str) +void Maps::displayMap() { - char *next; - int32_t x1 = (int32_t)strtol(str.c_str(), &next, 10); - int32_t y1 = (int32_t)strtol(++next, &next, 10); - int32_t x2 = (int32_t)strtol(++next, &next, 10); - int32_t y2 = (int32_t)strtol(++next, NULL, 10); - return BBox(Point32(x1, y1), Point32(x2, y2)); + if (!Maps::isMapFound) + { + if (Maps::scrollUpdated && !Maps::followGps) + Maps::mapTempSprite.pushSprite(&mapSprite, 0, 0, TFT_TRANSPARENT); + else + Maps::mapTempSprite.pushSprite(&mapSprite, 0, 0, TFT_TRANSPARENT); + return; + } + + uint16_t mapHeading = 0; +#ifdef ENABLE_COMPASS + mapHeading = mapSet.mapRotationComp ? compass.getHeading() : gps.gpsData.heading; +#else + mapHeading = gps.gpsData.heading; +#endif + + Maps::mapTempSprite.pushImage(Maps::wptPosX - 8, Maps::wptPosY - 8, 16, 16, (uint16_t *)waypoint, TFT_BLACK); + + const uint16_t size = Maps::mapTileSize; + + if (Maps::followGps) + { + const float lat = gps.gpsData.latitude; + const float lon = gps.gpsData.longitude; + Maps::navArrowPosition = Maps::coord2ScreenPos(lon, lat, Maps::zoomLevel, Maps::mapTileSize); + Maps::mapTempSprite.setPivot(Maps::mapTileSize + Maps::navArrowPosition.posX, + Maps::mapTileSize + Maps::navArrowPosition.posY); + Maps::mapTempSprite.pushRotated(&mapSprite, 360 - mapHeading, TFT_TRANSPARENT); + } + else + { + const int16_t pivotX = Maps::tileWidth / 2 + Maps::offsetX; + const int16_t pivotY = Maps::tileHeight / 2 + Maps::offsetY; + Maps::mapTempSprite.setPivot(pivotX, pivotY); + Maps::mapTempSprite.pushRotated(&mapSprite, 0, TFT_TRANSPARENT); + } } /** - * @brief Read vector map file to memory block + * @brief Set Waypoint coords in Map * - * @param fileName - * @return MapBlock* + * @details Sets the latitude and longitude for the waypoint on the map. + * + * @param wptLat Waypoint Latitude + * @param wptLon Waypoint Longitude */ -Maps::MapBlock *Maps::readMapBlock(String fileName) +void Maps::setWaypoint(float wptLat, float wptLon) { - ESP_LOGI(TAG, "readMapBlock: %s", fileName.c_str()); - char str[30]; - MapBlock *mblock = new MapBlock(); - std::string filePath = fileName.c_str() + std::string(".fmp"); - ESP_LOGI(TAG, "File: %s", filePath.c_str()); - - FILE *file_ = storage.open(filePath.c_str(), "r"); - - if (!file_) - { - Maps::isMapFound = false; - mblock->inView = false; - return mblock; - } - else - { - size_t fileSize = storage.size(filePath.c_str()); + Maps::destLat = wptLat; + Maps::destLon = wptLon; +} -#ifdef BOARD_HAS_PSRAM - char *file = (char *)ps_malloc(fileSize + 1); -#else - char *file = (char *)malloc(fileSize + 1); -#endif +/** + * @brief Refresh current map + * + * @details Resets the old map tile and marks the position as moved, causing the map to be updated/redrawn. + */ + void Maps::updateMap() + { + Maps::oldMapTile = {}; + } - storage.read(file_, file, fileSize); - Maps::isMapFound = true; +/** + * @brief Pan current map + * + * @details Moves the map view by the given tile offsets in the X (longitude) and Y (latitude) directions. + * Updates the current map tile coordinates and recalculates the corresponding longitude and latitude. + * + * @param dx Tile offset in X direction (east-west) + * @param dy Tile offset in Y direction (north-south) + */ + void Maps::panMap(int8_t dx, int8_t dy) + { + Maps::currentMapTile.tilex += dx; + Maps::currentMapTile.tiley += dy; + Maps::currentMapTile.lon = Maps::tilex2lon(Maps::currentMapTile.tilex, Maps::currentMapTile.zoom); + Maps::currentMapTile.lat = Maps::tiley2lat(Maps::currentMapTile.tiley, Maps::currentMapTile.zoom); + } - uint32_t line = 0; - Maps::idx = 0; +/** + * @brief Center map on current GPS location + * + * @details Sets the map to follow the GPS and centers the map tile indices and coordinates on the current GPS location. + * + * @param lat GPS Latitude + * @param lon GPS Longitude + */ + void Maps::centerOnGps(float lat, float lon) + { + Maps::followGps = true; + Maps::currentMapTile.tilex = Maps::lon2tilex(lon, Maps::currentMapTile.zoom); + Maps::currentMapTile.tiley = Maps::lat2tiley(lat, Maps::currentMapTile.zoom); + Maps::currentMapTile.lat = lat; + Maps::currentMapTile.lon = lon; + } - // read polygons - Maps::parseStrUntil(file, ':', str); - if (strcmp(str, "Polygons") != 0) - { - ESP_LOGE(TAG, "Map error. Expected Polygons instead of: %s", str); - while (0); - } +/** + * @brief Smooth scroll current map + * + * @details Smoothly scrolls the map view with inertia and friction, updating tile indices and offsets as needed. + * Handles transitions when the scroll offset surpasses a threshold, triggering tile panning and preloading. + * + * @param dx Delta X input for scrolling + * @param dy Delta Y input for scrolling + */ +void Maps::scrollMap(int16_t dx, int16_t dy) +{ + const float inertia = 0.5f; + const float friction = 0.95f; + const float maxSpeed = 10.0f; + + static float speedX = 0.0f, speedY = 0.0f; + static unsigned long lastCacheStats = 0; + + speedX = (speedX + dx) * inertia * friction; + speedY = (speedY + dy) * inertia * friction; + + const float absSpeedX = fabsf(speedX); + const float absSpeedY = fabsf(speedY); + + if (absSpeedX > maxSpeed) speedX = (speedX > 0) ? maxSpeed : -maxSpeed; + if (absSpeedY > maxSpeed) speedY = (speedY > 0) ? maxSpeed : -maxSpeed; + + Maps::offsetX += (int16_t)speedX; + Maps::offsetY += (int16_t)speedY; + + Maps::scrollUpdated = false; + Maps::followGps = false; + + const int16_t threshold = Maps::scrollThreshold; + const int16_t tileSize = Maps::mapTileSize; + + if (Maps::offsetX <= -threshold) + { + Maps::tileX--; + Maps::offsetX += tileSize; + Maps::scrollUpdated = true; + } + else if (Maps::offsetX >= threshold) + { + Maps::tileX++; + Maps::offsetX -= tileSize; + Maps::scrollUpdated = true; + } + + if (Maps::offsetY <= -threshold) + { + Maps::tileY--; + Maps::offsetY += tileSize; + Maps::scrollUpdated = true; + } + else if (Maps::offsetY >= threshold) + { + Maps::tileY++; + Maps::offsetY -= tileSize; + Maps::scrollUpdated = true; + } + + if (Maps::scrollUpdated) + { + const int8_t deltaTileX = Maps::tileX - Maps::lastTileX; + const int8_t deltaTileY = Maps::tileY - Maps::lastTileY; + Maps::panMap(deltaTileX, deltaTileY); + Maps::preloadTiles(deltaTileX, deltaTileY); + Maps::lastTileX = Maps::tileX; + Maps::lastTileY = Maps::tileY; + } +} - int16_t count = Maps::parseInt16(file); - assert(count > 0); - line++; +/** + * @brief Preload Tiles for map scrolling + * + * @details Preloads map tiles in the direction of scrolling to enable smooth transitions. + * Loads one or two tiles into a temporary sprite and uses it to update the main map buffer. + * + * @param dirX Direction to preload tiles in X (-1 for left, 1 for right, 0 for no horizontal scroll) + * @param dirY Direction to preload tiles in Y (-1 for up, 1 for down, 0 for no vertical scroll) + */ +void Maps::preloadTiles(int8_t dirX, int8_t dirY) +{ + const int16_t tileSize = mapTileSize; + const int16_t preloadWidth = (dirX != 0) ? tileSize : tileSize * 2; + const int16_t preloadHeight = (dirY != 0) ? tileSize : tileSize * 2; + + TFT_eSprite preloadSprite = TFT_eSprite(&tft); + preloadSprite.createSprite(preloadWidth, preloadHeight); + + const int16_t startX = tileX + dirX; + const int16_t startY = tileY + dirY; + + for (int8_t i = 0; i < 2; ++i) + { + const int16_t tileToLoadX = startX + ((dirX == 0) ? i - 1 : 0); + const int16_t tileToLoadY = startY + ((dirY == 0) ? i - 1 : 0); + + Maps::roundMapTile = Maps::getMapTile( + Maps::currentMapTile.lon, + Maps::currentMapTile.lat, + Maps::zoomLevel, + tileToLoadX, + tileToLoadY + ); + + const int16_t offsetX = (dirX != 0) ? i * tileSize : 0; + const int16_t offsetY = (dirY != 0) ? i * tileSize : 0; + + bool foundTile = false; + + // Try cache first for vector maps + if (mapSet.vectorMap) + { + foundTile = getCachedTile(Maps::roundMapTile.file, preloadSprite, offsetX, offsetY); + if (foundTile) + ESP_LOGI(TAG, "Tile found in cache: %s", Maps::roundMapTile.file); + } + + // If not in cache, try to load from file + if (!foundTile) + { + if (mapSet.vectorMap) + { + // Create a temporary sprite for rendering to cache + TFT_eSprite tempSprite = TFT_eSprite(&tft); + tempSprite.createSprite(tileSize, tileSize); + + // Render tile to temporary sprite (this will cache it) + foundTile = renderTile(Maps::roundMapTile.file, 0, 0, tempSprite); + + if (foundTile) + { + // Copy from temporary sprite to preload sprite + preloadSprite.pushImage(offsetX, offsetY, tileSize, tileSize, tempSprite.frameBuffer(0)); + } + + tempSprite.deleteSprite(); + } + else + foundTile = preloadSprite.drawPngFile(Maps::roundMapTile.file, offsetX, offsetY); + } + + if (!foundTile) + preloadSprite.fillRect(offsetX, offsetY, tileSize, tileSize, TFT_LIGHTGREY); + } + + if (dirX != 0) + { + mapTempSprite.scroll(dirX * tileSize, 0); + const int16_t pushX = (dirX > 0) ? tileSize * 2 : 0; + mapTempSprite.pushImage(pushX, 0, preloadWidth, preloadHeight, preloadSprite.frameBuffer(0)); + } + else if (dirY != 0) + { + mapTempSprite.scroll(0, dirY * tileSize); + const int16_t pushY = (dirY > 0) ? tileSize * 2 : 0; + mapTempSprite.pushImage(0, pushY, preloadWidth, preloadHeight, preloadSprite.frameBuffer(0)); + } + + preloadSprite.deleteSprite(); +} - uint32_t totalPoints = 0; - Polygon polygon; - Point16 p; - while (count > 0) - { - Maps::parseStrUntil(file, '\n', str); // color - assert(str[0] == '0' && str[1] == 'x'); - polygon.color = (uint16_t)std::stoul(str, nullptr, 16); - line++; - - Maps::parseStrUntil(file, '\n', str); // maxZoom - polygon.maxZoom = str[0] ? (uint8_t)std::stoi(str) : MAX_ZOOM; - line++; - - Maps::parseStrUntil(file, ':', str); - if (strcmp(str, "bbox") != 0) - { - ESP_LOGE(TAG, "bbox error tag. Line %i : %s", line, str); - while (true); - } - polygon.bbox.min.x = Maps::parseInt16(file); - polygon.bbox.min.y = Maps::parseInt16(file); - polygon.bbox.max.x = Maps::parseInt16(file); - polygon.bbox.max.y = Maps::parseInt16(file); - - line++; - polygon.points.clear(); - Maps::parseStrUntil(file, ':', str); - if (strcmp(str, "coords") != 0) - { - ESP_LOGE(TAG, "coords error tag. Line %i : %s", line, str); - while (true); - } - - Maps::parseCoords(file, polygon.points); - line++; - mblock->polygons.push_back(polygon); - totalPoints += polygon.points.size(); - count--; +/** + * @brief Loads an RGB332 color palette from a binary file. + * + * @details This function reads up to 256 bytes from the specified binary file and stores them in the global PALETTE array, + * setting the PALETTE_SIZE accordingly. The palette is expected to be in RGB332 format. + * + * @param palettePath The path to the palette binary file. + * @return true if the palette was loaded successfully and contains at least one color, false otherwise. + */ +bool Maps::loadPalette(const char* palettePath) +{ + FILE* f = fopen(palettePath, "rb"); + if (!f) + return false; + + // Read 4-byte header for number of colors + uint32_t numColors; + if (fread(&numColors, 4, 1, f) != 1) { + fclose(f); + return false; } - assert(count == 0); - - // read lines - Maps::parseStrUntil(file, ':', str); - if (strcmp(str, "Polylines") != 0) - ESP_LOGE(TAG, "Map error. Expected Polylines instead of: %s", str); - count = Maps::parseInt16(file); - assert(count > 0); - line++; - - Polyline polyline; - while (count > 0) + + // Read RGB888 colors (3 bytes per color) and convert to RGB332 + uint8_t rgb888[3]; + PALETTE_SIZE = 0; + + for (uint32_t i = 0; i < numColors && i < 256; i++) { - Maps::parseStrUntil(file, '\n', str); // color - assert(str[0] == '0' && str[1] == 'x'); - polyline.color = (uint16_t)std::stoul(str, nullptr, 16); - line++; - Maps::parseStrUntil(file, '\n', str); // width - polyline.width = str[0] ? (uint8_t)std::stoi(str) : 1; - line++; - Maps::parseStrUntil(file, '\n', str); // maxZoom - polyline.maxZoom = str[0] ? (uint8_t)std::stoi(str) : MAX_ZOOM; - line++; - - Maps::parseStrUntil(file, ':', str); - if (strcmp(str, "bbox") != 0) - { - ESP_LOGE(TAG, "bbox error tag. Line %i : %s", line, str); - while (true); - } - - polyline.bbox.min.x = Maps::parseInt16(file); - polyline.bbox.min.y = Maps::parseInt16(file); - polyline.bbox.max.x = Maps::parseInt16(file); - polyline.bbox.max.y = Maps::parseInt16(file); - - line++; - - polyline.points.clear(); - Maps::parseStrUntil(file, ':', str); - if (strcmp(str, "coords") != 0) - { - ESP_LOGI(TAG, "coords tag. Line %i : %s", line, str); - while (true); - } - Maps::parseCoords(file, polyline.points); - line++; - mblock->polylines.push_back(polyline); - totalPoints += polyline.points.size(); - count--; + if (fread(rgb888, 3, 1, f) == 1) + { + // Convert RGB888 to RGB332 using same method as tile_generator.py hex_to_rgb332_direct + uint8_t r332 = rgb888[0] & 0xE0; // Keep top 3 bits + uint8_t g332 = (rgb888[1] & 0xE0) >> 3; // Keep top 3 bits, shift right + uint8_t b332 = rgb888[2] >> 6; // Keep top 2 bits + PALETTE[i] = r332 | g332 | b332; + PALETTE_SIZE++; + } } - assert(count == 0); - storage.close(file_); - delete[] file; - return mblock; - } + + fclose(f); + ESP_LOGI(TAG, "Loaded palette: %u colors", PALETTE_SIZE); + return PALETTE_SIZE > 0; } /** - * @brief Fill polygon routine + * @brief Converts a palette index to its corresponding RGB332 color value. + * + * @details descriptionLooks up the given index in the global PALETTE array and returns the corresponding RGB332 color. + * If the index is out of range (greater than or equal to PALETTE_SIZE), returns 0xFF (white) by default. * - * @param points - * @param color + * @param idx The palette index to look up. + * @return The RGB332 color value corresponding to the index, or 0xFF if the index is invalid. */ -void Maps::fillPolygon(Polygon p, TFT_eSprite &map) // scanline fill algorithm +uint8_t Maps::paletteToRGB332(const uint32_t idx) { - int16_t maxY = p.bbox.max.y; - int16_t minY = p.bbox.min.y; - - if (maxY >= Maps::tileHeight) - maxY = Maps::tileHeight - 1; - if (minY < 0) - minY = 0; - if (minY >= maxY) - return; - - int16_t nodeX[p.points.size()], pixelY; - - // Loop through the rows of the image. - int16_t nodes, i, swap; - for (pixelY = minY; pixelY <= maxY; pixelY++) - { // Build a list of nodes. - nodes = 0; - for (int i = 0; i < (p.points.size() - 1); i++) - { - if ((p.points[i].y < pixelY && p.points[i + 1].y >= pixelY) || - (p.points[i].y >= pixelY && p.points[i + 1].y < pixelY)) - { - nodeX[nodes++] = - p.points[i].x + double(pixelY - p.points[i].y) / double(p.points[i + 1].y - p.points[i].y) * - double(p.points[i + 1].x - p.points[i].x); - } - } - assert(nodes < p.points.size()); - - // Sort the nodes, via a simple β€œBubble” sort. - i = 0; - while (i < nodes - 1) - { // TODO: rework - if (nodeX[i] > nodeX[i + 1]) - { - swap = nodeX[i]; - nodeX[i] = nodeX[i + 1]; - nodeX[i + 1] = swap; - i = 0; - } - else - i++; - } - - // Fill the pixels between node pairs. - for (i = 0; i <= nodes - 2; i += 2) - { - if (nodeX[i] > Maps::tileWidth) - break; - if (nodeX[i + 1] < 0) - continue; - if (nodeX[i] < 0) - nodeX[i] = 0; - if (nodeX[i + 1] > Maps::tileWidth) - nodeX[i + 1] = Maps::tileWidth; - map.drawLine(nodeX[i], Maps::tileHeight - pixelY, nodeX[i + 1], Maps::tileHeight - pixelY, p.color); - } - } + if (idx < PALETTE_SIZE) + return PALETTE[idx]; + return 0xFF; } /** - * @brief Get bounding objects in memory block + * @brief Darkens an RGB332 color by a specified amount. + * + * @details This function extracts the red, green, and blue components from the input RGB332 color value, + * multiplies each component by (1.0 - amount), and recombines them into a new RGB332 value. The default darkening amount is 0.4. + * This can be used to generate a visually darker shade of the original color for effects such as shading or selection highlighting. * - * @param memBlocks - * @param bbox + * @param color The original RGB332 color value. + * @param amount The fraction to darken each color component (default is 0.4). + * @return The darkened RGB332 color value. */ -void Maps::getMapBlocks(BBox &bbox, Maps::MemCache &memCache) +uint8_t Maps::darkenRGB332(const uint8_t color, const float amount = 0.4f) { - ESP_LOGI(TAG, "getMapBlocks %i", millis()); - for (MapBlock *block : memCache.blocks) - { - block->inView = false; - } - // loop the 4 corners of the bbox and find the files that contain them - for (Point32 point : {bbox.min, bbox.max, Point32(bbox.min.x, bbox.max.y), Point32(bbox.max.x, bbox.min.y)}) - { - bool found = false; - int32_t blockMinX = point.x & (~MAPBLOCK_MASK); - int32_t blockMinY = point.y & (~MAPBLOCK_MASK); - - // check if the needed block is already in memory - for (MapBlock *memblock : memCache.blocks) - { - if (blockMinX == memblock->offset.x && blockMinY == memblock->offset.y) - { - memblock->inView = true; - found = true; - break; - } - } - if (found) - continue; - - ESP_LOGI(TAG, "load from disk (%i, %i) %i", blockMinX, blockMinY, millis()); - // block is not in memory => load from disk - int32_t blockX = (blockMinX >> MAPBLOCK_SIZE_BITS) & MAPFOLDER_MASK; - int32_t blockY = (blockMinY >> MAPBLOCK_SIZE_BITS) & MAPFOLDER_MASK; - int32_t folderNameX = blockMinX >> (MAPFOLDER_SIZE_BITS + MAPBLOCK_SIZE_BITS); - int32_t folderNameY = blockMinY >> (MAPFOLDER_SIZE_BITS + MAPBLOCK_SIZE_BITS); - char folderName[12]; - snprintf(folderName, 9, "%+04d%+04d", folderNameX, folderNameY); // force sign and 4 chars per number - String fileName = mapVectorFolder + folderName + "/" + blockX + "_" + blockY; // /maps/123_456/777_888 - - // check if cache is full - if (memCache.blocks.size() >= MAPBLOCKS_MAX) - { - // remove first one, the oldest - ESP_LOGV(TAG, "Deleting freeHeap: %i", esp_get_free_heap_size()); - MapBlock *firstBlock = memCache.blocks.front(); - delete firstBlock; // free memory - memCache.blocks.erase(memCache.blocks.begin()); // remove pointer from the vector - ESP_LOGV(TAG, "Deleted freeHeap: %i", esp_get_free_heap_size()); - } + uint8_t r = (color & 0xE0) >> 5; + uint8_t g = (color & 0x1C) >> 2; + uint8_t b = (color & 0x03); - MapBlock *newBlock = Maps::readMapBlock(fileName); - if (Maps::isMapFound) - { - newBlock->inView = true; - newBlock->offset = Point32(blockMinX, blockMinY); - memCache.blocks.push_back(newBlock); // add the block to the memory cache - assert(memCache.blocks.size() <= MAPBLOCKS_MAX); + r = static_cast(r * (1.0f - amount)); + g = static_cast(g * (1.0f - amount)); + b = static_cast(b * (1.0f - amount)); - ESP_LOGI(TAG, "Block readed from SD card: %p", newBlock); - ESP_LOGI(TAG, "FreeHeap: %i", esp_get_free_heap_size()); - } - } + return ((r << 5) | (g << 2) | b); +} - ESP_LOGI(TAG, "memCache size: %i %i", memCache.blocks.size(), millis()); +/** + * @brief Converts an RGB332 color value to RGB565 format. + * + * @details This function extracts the red, green, and blue components from the 8-bit RGB332 input value, scales each component to match the RGB565 format, and recombines them into a 16-bit RGB565 value. + * + * @param color The input color value in RGB332 format. + * @return The corresponding color value in RGB565 format. + */ +uint16_t Maps::RGB332ToRGB565(const uint8_t color) +{ + // Convert RGB332 to RGB565 + uint8_t r = (color & 0xE0); + uint8_t g = (color & 0x1C) << 3; + uint8_t b = (color & 0x03) << 6; + + // Convert to RGB565 format + uint16_t r565 = (r >> 3) & 0x1F; // 5 bits for red + uint16_t g565 = (g >> 2) & 0x3F; // 6 bits for green + uint16_t b565 = (b >> 3) & 0x1F; // 5 bits for blue + + return (r565 << 11) | (g565 << 5) | b565; } /** - * @brief Generate vectorized map + * @brief Reads a variable-length integer (varint) from a byte array. + * + * @details This function decodes a 32-bit unsigned integer from the provided byte array starting at the given offset, using a variable-length encoding (varint). + * It advances the offset as bytes are read, ensuring it does not exceed dataSize. + * The function returns the decoded value, or 0 if the offset moves past the end of the data buffer. * - * @param viewPort - * @param memblocks - * @param map -> Map Sprite - * @param zoom -> Zoom Level + * @param data Pointer to the input byte array. + * @param offset Reference to the current position in the byte array; will be updated to the new offset after reading. + * @param dataSize The total size of the data buffer. + * @return The decoded uint32_t varint value, or 0 if the offset exceeds dataSize. */ -void Maps::readVectorMap(Maps::ViewPort &viewPort, Maps::MemCache &memCache, TFT_eSprite &map, uint8_t zoom) +uint32_t Maps::readVarint(const uint8_t* data, size_t& offset, const size_t dataSize) { - Polygon newPolygon; - map.fillScreen(BACKGROUND_COLOR); - uint32_t totalTime = millis(); - ESP_LOGI(TAG, "Draw start %i", totalTime); - int16_t p1x, p1y, p2x, p2y; - if (Maps::isMapFound) - { - for (MapBlock *mblock : memCache.blocks) + uint32_t value = 0; + uint8_t shift = 0; + while (offset < dataSize && shift < 32) { - uint32_t blockTime = millis(); - if (!mblock->inView) - continue; - - // block to draw - Point16 screen_center_mc = viewPort.center.toPoint16() - mblock->offset.toPoint16(); // screen center with features coordinates - BBox screen_bbox_mc = viewPort.bbox - mblock->offset; // screen boundaries with features coordinates - - ////// Polygons - for (Polygon polygon : mblock->polygons) - { - if (zoom > polygon.maxZoom) - continue; - if (!polygon.bbox.intersects(screen_bbox_mc)) - continue; - newPolygon.color = polygon.color; - newPolygon.bbox.min.x = Maps::toScreenCoord(polygon.bbox.min.x, screen_center_mc.x); - newPolygon.bbox.min.y = Maps::toScreenCoord(polygon.bbox.min.y, screen_center_mc.y); - newPolygon.bbox.max.x = Maps::toScreenCoord(polygon.bbox.max.x, screen_center_mc.x); - newPolygon.bbox.max.y = Maps::toScreenCoord(polygon.bbox.max.y, screen_center_mc.y); - - newPolygon.points.clear(); - for (Point16 p : polygon.points) - { // TODO: move to fillPolygon - newPolygon.points.push_back(Point16( - Maps::toScreenCoord(p.x, screen_center_mc.x), - Maps::toScreenCoord(p.y, screen_center_mc.y))); - } - Maps::fillPolygon(newPolygon, map); - } - ESP_LOGI(TAG, "Block polygons done %i ms", millis() - blockTime); - blockTime = millis(); - - ////// Lines - for (Polyline line : mblock->polylines) - { - if (zoom > line.maxZoom) - continue; - if (!line.bbox.intersects(screen_bbox_mc)) - continue; - - p1x = -1; - for (int i = 0; i < line.points.size() - 1; i++) - { // TODO optimize - p1x = Maps::toScreenCoord(line.points[i].x, screen_center_mc.x); - p1y = Maps::toScreenCoord(line.points[i].y, screen_center_mc.y); - p2x = Maps::toScreenCoord(line.points[i + 1].x, screen_center_mc.x); - p2y = Maps::toScreenCoord(line.points[i + 1].y, screen_center_mc.y); - // map.drawWideLine( - // p1x, TILE_HEIGHT - p1y, - // p2x, TILE_HEIGHT - p2y, - // line.width / zoom ?: 1, line.color); - map.drawLine(p1x, Maps::tileHeight - p1y, p2x, Maps::tileHeight - p2y, line.color); - } - } - ESP_LOGI(TAG, "Block lines done %i ms", millis() - blockTime); + uint8_t byte = data[offset++]; + value |= ((uint32_t)(byte & 0x7F)) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; } - ESP_LOGI(TAG, "Total %i ms", millis() - totalTime); - - // TODO: paint only in NAV mode - // map.fillTriangle( - // TILE_WIDTH / 2 - 4, TILE_HEIGHT / 2 + 5, - // TILE_WIDTH / 2 + 4, TILE_HEIGHT / 2 + 5, - // TILE_WIDTH / 2, TILE_HEIGHT / 2 - 6, - // RED); - ESP_LOGI(TAG, "Draw done! %i", millis()); - - MapBlock *firstBlock = memCache.blocks.front(); - delete firstBlock; - memCache.blocks.erase(memCache.blocks.begin()); - - Maps::totalBounds.lat_min = Maps::mercatorY2lat(viewPort.bbox.min.y); - Maps::totalBounds.lat_max = Maps::mercatorY2lat(viewPort.bbox.max.y); - Maps::totalBounds.lon_min = Maps::mercatorX2lon(viewPort.bbox.min.x); - Maps::totalBounds.lon_max = Maps::mercatorX2lon(viewPort.bbox.max.x); - - ESP_LOGI(TAG, "Total Bounds: Lat Min: %f, Lat Max: %f, Lon Min: %f, Lon Max: %f", - Maps::totalBounds.lat_min, Maps::totalBounds.lat_max, Maps::totalBounds.lon_min, Maps::totalBounds.lon_max); - - if (Maps::isCoordInBounds(Maps::destLat, Maps::destLon, Maps::totalBounds)) - Maps::coords2map(Maps::destLat, Maps::destLon, Maps::totalBounds, &(Maps::wptPosX), &(Maps::wptPosY)); - else + if (offset > dataSize) { - Maps::wptPosX = -1; - Maps::wptPosY = -1; + offset = dataSize; + return 0; } - - for (size_t i = 1; i < trackData.size(); ++i) - { - if ( trackData[i-1].lon > Maps::totalBounds.lon_min && trackData[i-1].lon < Maps::totalBounds.lon_max && - trackData[i-1].lat > Maps::totalBounds.lat_min && trackData[i-1].lat < Maps::totalBounds.lat_max && - trackData[i].lon > Maps::totalBounds.lon_min && trackData[i].lon < Maps::totalBounds.lon_max && - trackData[i].lat > Maps::totalBounds.lat_min && trackData[i].lat < Maps::totalBounds.lat_max ) - { - uint16_t x, y, x2, y2; - Maps::coords2map(trackData[i-1].lat,trackData[i-1].lon,Maps::totalBounds, &x,&y); - Maps::coords2map(trackData[i].lat,trackData[i].lon,Maps::totalBounds, &x2,&y2); - - map.drawWideLine(x,y,x2,y2,2,TFT_BLUE); - } - } - } - else - { - Maps::isMapFound = false; - map.fillScreen(TFT_BLACK); - Maps::showNoMap(map); - ESP_LOGE(TAG, "Map doesn't exist"); - } + return value; } /** - * @brief Get vector map Position from GPS position and check if is moved + * @brief Reads a zigzag-encoded integer from a byte array. + * + * @details This function decodes a 32-bit signed integer from the provided byte array starting at the given offset, using zigzag encoding. + * It first reads a varint and then applies the zigzag decoding to convert it back to a signed integer. + * The function advances the offset as bytes are read, ensuring it does not exceed dataSize. + * If the offset exceeds dataSize, it returns 0. * - * @param lat - * @param lon + * @param data Pointer to the input byte array. + * @param offset Reference to the current position in the byte array; will be updated to the new offset after reading. + * @param dataSize The total size of the data buffer. + * @return The decoded int32_t zigzag value, or 0 if the offset exceeds dataSize. */ -void Maps::getPosition(double lat, double lon) +int32_t Maps::readZigzag(const uint8_t* data, size_t& offset, const size_t dataSize) { - Coord pos; - pos.lat = lat; - pos.lng = lon; - if (abs(pos.lat - Maps::prevLat) > 0.00005 && abs(pos.lng - Maps::prevLon) > 0.00005) - { - Maps::point.x = Maps::lon2x(pos.lng); - Maps::point.y = Maps::lat2y(pos.lat); - Maps::prevLat = pos.lat; - Maps::prevLon = pos.lng; - Maps::isPosMoved = true; - } + if (offset >= dataSize) + return 0; + const uint32_t encoded = Maps::readVarint(data, offset, dataSize); + return static_cast((encoded >> 1) ^ (-(int32_t)(encoded & 1))); } -// Common Private section - /** - * @brief Get min and max longitude and latitude from tile + * @brief Converts a 16-bit unsigned integer in the range [0, 65535] to a pixel coordinate in the range [0, TILE_SIZE]. + * + * @details This function scales the input value from the range of a 16-bit unsigned integer (0 to 65535) to a pixel coordinate within a tile of size TILE_SIZE (0 to 255). + * It ensures that the resulting pixel coordinate is clamped within the valid range of [0, TILE_SIZE]. * - * @param tileX -> tile X - * @param tileY -> tile Y - * @param zoom -> zoom - * @return tileBounds -> min and max longitude and latitude + * @param val The input value in the range [0, 65535]. + * @return The corresponding pixel coordinate in the range [0, TILE_SIZE]. */ -Maps::tileBounds Maps::getTileBounds(uint32_t tileX, uint32_t tileY, uint8_t zoom) +int Maps::uint16ToPixel(const int32_t val) { - tileBounds bounds; - bounds.lon_min = Maps::tilex2lon(tileX, zoom); - bounds.lat_min = Maps::tiley2lat(tileY + 1, zoom); - bounds.lon_max = Maps::tilex2lon(tileX + 1, zoom); - bounds.lat_max = Maps::tiley2lat(tileY, zoom); - return bounds; + int p = static_cast((val * TILE_SIZE_PLUS_ONE) / 65536); + if (p < 0) + p = 0; + if (p > TILE_SIZE) + p = TILE_SIZE; + return p; } /** - * @brief Check if coordinates are in map bounds + * @brief Checks if a point lies on the margin of a tile. + * + * @details This function determines whether the given point (px, py) is located within the margin area of a tile, as defined by the MARGIN_PIXELS constant. + * The margin is considered on any side of the tile (left, right, top, or bottom) if the point's coordinates are less than or equal to MARGIN_PIXELS, + * or greater than or equal to TILE_SIZE minus MARGIN_PIXELS. * - * @param lat -> latitude - * @param lon -> longitude - * @param bound -> map bounds - * @return true/false + * @param px The x-coordinate of the point. + * @param py The y-coordinate of the point. + * @return true if the point is on the margin, false otherwise. */ -bool Maps::isCoordInBounds(double lat, double lon, tileBounds bound) +bool Maps::isPointOnMargin(const int px, const int py) { - return (lat >= Maps::totalBounds.lat_min && lat <= Maps::totalBounds.lat_max && lon >= Maps::totalBounds.lon_min && lon <= Maps::totalBounds.lon_max); + return (px <= MARGIN_PIXELS || px >= TILE_SIZE - MARGIN_PIXELS || py <= MARGIN_PIXELS || py >= TILE_SIZE - MARGIN_PIXELS); } -/** - * @brief Convert GPS Coordinates to screen position (with offsets) - * - * @param lon -> Longitude - * @param lat -> Latitude - * @param zoomLevel -> Zoom level - * @param tileSize -> tile size - * @return ScreenCoord -> Screen position - */ -Maps::ScreenCoord Maps::coord2ScreenPos(double lon, double lat, uint8_t zoomLevel, uint16_t tileSize) +/** +* @brief Checks if a value is near a target within a specified tolerance. +* +* @details This function determines whether the given value (val) is within a certain tolerance (tol) of the target value. +* The default tolerance is set to 2. It returns true if the absolute difference between val and target is less than or equal to tol. +* +* @param val The value to check. +* @param target The target value to compare against. +* @param tol The tolerance range (default is 2). +* @return true if val is within tol of target, false otherwise. +*/ +bool Maps::isNear(int val, int target, int tol = 2) { - ScreenCoord data; - data.posX = Maps::lon2posx(lon, zoomLevel, tileSize); - data.posY = Maps::lat2posy(lat, zoomLevel, tileSize); - return data; + return abs(val - target) <= tol; } /** - * @brief Get position X,Y in render map for a coordinate - * - * @param lat -> latitude - * @param lon -> longitude - * @param bound -> map bounds - * @param pixelX -> X position on map - * @param pixelY -> Y position on map - */ -void Maps::coords2map(double lat, double lon, tileBounds bound, uint16_t *pixelX, uint16_t *pixelY) +* @brief Determines if a line between two points should be drawn based on specific criteria. +* +* @details This function evaluates whether a line defined by two endpoints (px1, py1) and (px2, py2) should be drawn. +* It filters out lines that cross the entire tile from edge to edge (horizontally, vertically, or diagonally), +* lines that are excessively long (more than 1.5 times the diagonal of the tile), and lines where both endpoints are on the margin of the tile. +* The function returns true if the line meets the criteria for drawing, and false otherwise. +* +* @param px1 The x-coordinate of the first endpoint. +* @param py1 The y-coordinate of the first endpoint. +* @param px2 The x-coordinate of the second endpoint. +* @param py2 The y-coordinate of the second endpoint. +* @return true if the line should be drawn, false otherwise. +*/ +bool Maps::shouldDrawLine(const int px1, const int py1, const int px2, const int py2) { - double lon_ratio = (lon - bound.lon_min) / (bound.lon_max - bound.lon_min); - double lat_ratio = (bound.lat_max - lat) / (bound.lat_max - bound.lat_min); + if ((isNear(px1, 0) && isNear(px2, TILE_SIZE)) || (isNear(px1, TILE_SIZE) && isNear(px2, 0))) + { + if ((isNear(py1, 0) && isNear(py2, TILE_SIZE)) || (isNear(py1, TILE_SIZE) && isNear(py2, 0))) + return false; + if (isNear(py1, py2)) + return false; + } + if ((isNear(py1, 0) && isNear(py2, TILE_SIZE)) || (isNear(py1, TILE_SIZE) && isNear(py2, 0))) + { + if (isNear(px1, px2)) + return false; + } - *pixelX = (int)(lon_ratio * Maps::tileWidth); - *pixelY = (int)(lat_ratio * Maps::tileHeight); + int dx = px2 - px1; + int dy = py2 - py1; + int len2 = dx*dx + dy*dy; + if (len2 > (TILE_SIZE * TILE_SIZE * 3)) + return false; + + if (isPointOnMargin(px1, py1) && isPointOnMargin(px2, py2)) + return false; + if ((px1 == px2) && (px1 <= MARGIN_PIXELS || px1 >= TILE_SIZE - MARGIN_PIXELS)) + return false; + if ((py1 == py2) && (py1 <= MARGIN_PIXELS || py1 >= TILE_SIZE - MARGIN_PIXELS)) + return false; + return true; } /** - * @brief Load No Map Image + * @brief Fills a polygon on a sprite using the scanline algorithm. + * + * @details This function fills a polygon defined by the given vertex arrays (px, py) on the provided TFT_eSprite object (map) using the specified color (c). + * It calculates the intersection points of the polygon with each scanline and draws horizontal lines between pairs of intersection points to fill the polygon. + * The function takes into account offsets for positioning within a larger context and ensures that drawing is constrained within the tile boundaries. * + * @param map The TFT_eSprite object where the polygon will be drawn. + * @param px Array of x-coordinates of the polygon vertices. + * @param py Array of y-coordinates of the polygon vertices. + * @param numPoints The number of vertices in the polygon. + * @param c The color to fill the polygon with (in RGB565 format). + * @param xOffset The x-offset to apply when drawing the polygon. + * @param yOffset The y-offset to apply when drawing the polygon. */ -void Maps::showNoMap(TFT_eSprite &map) +void Maps::fillPolygonGeneral(TFT_eSprite &map, const int *px, const int *py, const int numPoints, const uint16_t color, const int xOffset, const int yOffset) { - map.drawPngFile(noMapFile, (Maps::mapScrWidth / 2) - 50, (Maps::mapScrHeight / 2) - 50); - map.drawCenterString("NO MAP FOUND", (Maps::mapScrWidth / 2), (Maps::mapScrHeight >> 1) + 65, &fonts::DejaVu18); + static bool unifiedPoolLogged = false; + + int miny = py[0], maxy = py[0]; + for (int i = 1; i < numPoints; ++i) + { + if (py[i] < miny) + miny = py[i]; + if (py[i] > maxy) + maxy = py[i]; + } + + int *xints = nullptr; + + // Use RAII MemoryGuard for coordinate arrays + MemoryGuard xintsGuard(numPoints, 6); // Type 6 = coordArray + xints = xintsGuard.get(); + if (!xints) + { + ESP_LOGW(TAG, "fillPolygonGeneral: RAII MemoryGuard allocation failed"); + return; + } + if (!unifiedPoolLogged) + { + ESP_LOGI(TAG, "fillPolygonGeneral: Using RAII MemoryGuard for coordinate arrays"); + unifiedPoolLogged = true; + } + unifiedPoolHitCount++; + + if (!xints) + return; + + for (int y = miny; y <= maxy; ++y) + { + int nodes = 0; + for (int i = 0, j = numPoints - 1; i < numPoints; j = i++) + { + if ((py[i] < y && py[j] >= y) || (py[j] < y && py[i] >= y)) + xints[nodes++] = static_cast(px[i] + (y - py[i]) * (px[j] - px[i]) / (py[j] - py[i])); + } + if (nodes > 1) + { + std::sort(xints, xints + nodes); + for (int i = 0; i < nodes; i += 2) + { + if (i + 1 < nodes) + { + int x0 = xints[i] + xOffset; + int x1 = xints[i + 1] + xOffset; + int yy = y + yOffset; + if (yy >= 0 && yy <= TILE_SIZE + yOffset) + { + if (x0 < 0) + x0 = 0; + if (x1 > TILE_SIZE + xOffset) + x1 = TILE_SIZE + xOffset; + map.drawLine(x0, yy, x1, yy, color); + } + } + } + } + } } /** - * @brief Draw map widgets - * - */ -void Maps::drawMapWidgets(MAP mapSettings) +* @brief Draws the border of a polygon on a sprite, with special handling for margin points. +* +* @details This function draws the border of a polygon defined by the given vertex arrays (px, py) on the provided TFT_eSprite object (map). +* It uses different colors for lines connecting margin points and non-margin points, and can optionally skip drawing lines between two margin points if fillPolygons is false. +* The function takes into account offsets for positioning within a larger context and ensures that drawing is constrained within the tile boundaries. +* +* @param map The TFT_eSprite object where the polygon border will be drawn. +* @param px Array of x-coordinates of the polygon vertices. +* @param py Array of y-coordinates of the polygon vertices. +* @param numPoints The number of vertices in the polygon. +* @param borderColor The color to use for drawing the border lines (in RGB565 format). +* @param fillColor The color to use for lines between margin points (in RGB565 format). +* @param xOffset The x-offset to apply when drawing the polygon border. +* @param yOffset The y-offset to apply when drawing the polygon border. +*/ +void Maps::drawPolygonBorder(TFT_eSprite &map, const int *px, const int *py, const int numPoints, const uint16_t borderColor, const uint16_t fillColor, const int xOffset, const int yOffset) { - Maps::mapSprite.setTextColor(TFT_WHITE, TFT_WHITE); + if (numPoints < 2) + return; - uint16_t mapHeading = 0; -#ifdef ENABLE_COMPASS - if (mapSettings.mapRotationComp) - mapHeading = compass.getHeading(); - else - mapHeading = gps.gpsData.heading; -#else - mapHeading = gps.gpsData.heading; -#endif + for (uint32_t i = 0; i < numPoints - 1; ++i) + { + const bool marginA = isPointOnMargin(px[i], py[i]); + const bool marginB = isPointOnMargin(px[i+1], py[i+1]); + const uint16_t color = (marginA && marginB) ? fillColor : borderColor; - if (mapSettings.showMapCompass) - { - Maps::mapSprite.fillRectAlpha(Maps::mapScrWidth - 48, 0, 48, 48, 95, TFT_BLACK); - if (mapSettings.compassRotation) - Maps::mapSprite.pushImageRotateZoom(Maps::mapScrWidth - 24, 24, 24, 24, 360 - mapHeading, 1, 1, 48, 48, (uint16_t *)mini_compass, TFT_BLACK); - else - Maps::mapSprite.pushImage(Maps::mapScrWidth - 48, 0, 48, 48, (uint16_t *)mini_compass, TFT_BLACK); - } - - uint16_t mapHeight = 0; - if (mapSettings.mapFullScreen) - mapHeight = Maps::mapScrFull; - else - mapHeight = Maps::mapScrHeight; - - uint8_t toolBarOffset = 0; - uint8_t toolBarSpace = 0; -#ifdef LARGE_SCREEN - toolBarOffset = 100; - toolBarSpace = 60; -#endif -#ifndef LARGE_SCREEN - toolBarOffset = 80; - toolBarSpace = 50; -#endif + const int x0 = px[i] + xOffset; + const int y0 = py[i] + yOffset; + const int x1 = px[i+1] + xOffset; + const int y1 = py[i+1] + yOffset; - if (showMapToolBar) - { - if (mapSettings.mapFullScreen) - Maps::mapSprite.pushImage(10, mapHeight - toolBarOffset, 48, 48, (uint16_t *)collapse, TFT_BLACK); - else - Maps::mapSprite.pushImage(10, mapHeight - toolBarOffset, 48, 48, (uint16_t *)expand, TFT_BLACK); + if (x0 >= 0 && x0 <= TILE_SIZE + xOffset && y0 >= 0 && y0 <= TILE_SIZE + yOffset && + x1 >= 0 && x1 <= TILE_SIZE + xOffset && y1 >= 0 && y1 <= TILE_SIZE + yOffset) + { + if (!(marginA && marginB && !fillPolygons)) + map.drawLine(x0, y0, x1, y1, color); + } + } + const bool marginA = isPointOnMargin(px[numPoints-1], py[numPoints-1]); + const bool marginB = isPointOnMargin(px[0], py[0]); + const uint16_t color = (marginA && marginB) ? fillColor : borderColor; + const int x0 = px[numPoints-1] + xOffset; + const int y0 = py[numPoints-1] + yOffset; + const int x1 = px[0] + xOffset; + const int y1 = py[0] + yOffset; + + if (x0 >= 0 && x0 <= TILE_SIZE + xOffset && y0 >= 0 && y0 <= TILE_SIZE + yOffset && + x1 >= 0 && x1 <= TILE_SIZE + xOffset && y1 >= 0 && y1 <= TILE_SIZE + yOffset) + { + if (!(marginA && marginB && !fillPolygons)) + map.drawLine(x0, y0, x1, y1, color); + } +} + +/** +* @brief Draws a filled circle with a border on a sprite. +* +* @details This function draws a circle on the provided TFT_eSprite object (map) at the specified position (x, y) with the given radius (r). +* It fills the circle with the specified fill color and draws a border around it with the specified border color. +* If fillShape is false, it only draws the circle border using the fill color. +* +* @param map The TFT_eSprite object where the circle will be drawn. +* @param x The x-coordinate of the circle's center. +* @param y The y-coordinate of the circle's center. +* @param r The radius of the circle. +* @param fill The color to fill the circle with (in RGB565 format). +* @param border The color to use for the circle border (in RGB565 format). +* @param fillShape If true, the circle will be filled; if false, only the border will be drawn. +*/ - // Maps::mapSprite.fillRectAlpha(10, mapHeight - toolBarOffset, 48, 48, 50, TFT_BLACK); +/** +* @brief Draws a filled rectangle with a border on a sprite. +* +* @details This function draws a rectangle on the provided TFT_eSprite object (map) at the specified position (x, y) with the given width (w) and height (h). +* It fills the rectangle with the specified fill color and draws a border around it with the specified border color. +* If fillShape is false, it only draws the rectangle border using the fill color. +* +* @param map The TFT_eSprite object where the rectangle will be drawn. +* @param x The x-coordinate of the top-left corner of the rectangle. +* @param y The y-coordinate of the top-left corner of the rectangle. +* @param w The width of the rectangle. +* @param h The height of the rectangle. +* @param fill The color to fill the rectangle with (in RGB565 format). +* @param border The color to use for the rectangle border (in RGB565 format). +* @param fillShape If true, the rectangle will be filled; if false, only the border will be drawn. +*/ - Maps::mapSprite.pushImage(10, mapHeight - (toolBarOffset + toolBarSpace), 48, 48, (uint16_t *)zoomout, TFT_BLACK); - // Maps::mapSprite.fillRectAlpha(10, mapHeight - (toolBarOffset + toolBarSpace), 48, 48, 50, TFT_BLACK); +/** + * @brief Draw a dashed line between two points + * + * @details Draws a dashed line using Bresenham-like algorithm with configurable dash and gap lengths. + * + * @param map The TFT_eSprite object where the line will be drawn. + * @param x1, y1 Starting point coordinates. + * @param x2, y2 Ending point coordinates. + * @param dashLength Length of each dash segment. + * @param gapLength Length of each gap between dashes. + * @param color The color to draw the line with (in RGB565 format). + */ - Maps::mapSprite.pushImage(10, mapHeight - (toolBarOffset + (2 * toolBarSpace)), 48, 48, (uint16_t *)zoomin, TFT_BLACK); - // Maps::mapSprite.fillRectAlpha(10, mapHeight - (toolBarOffset + (2 * toolBarSpace)), 48, 48, 50, TFT_BLACK); - // if (!mapSettings.vectorMap) - // { - // Maps::mapSprite.pushImage(tft.width() - 58, mapHeight - toolBarOffset, 48, 48, (uint16_t *)move, TFT_BLACK); - // } - } - Maps::mapSprite.fillRectAlpha(0, 0, 50, 32, 95, TFT_BLACK); - Maps::mapSprite.pushImage(0, 4, 24, 24, (uint16_t *)zoom_ico, TFT_BLACK); - Maps::mapSprite.drawNumber(zoom, 26, 8, &fonts::FreeSansBold9pt7b); - if (mapSettings.showMapSpeed) - { - Maps::mapSprite.fillRectAlpha(0, mapHeight - 32, 70, 32, 95, TFT_BLACK); - Maps::mapSprite.pushImage(0, mapHeight - 28, 24, 24, (uint16_t *)speed_ico, TFT_BLACK); - Maps::mapSprite.drawNumber(gps.gpsData.speed, 26, mapHeight - 24, &fonts::FreeSansBold9pt7b); - } - if (!mapSettings.vectorMap) - if (mapSettings.showMapScale) - { - Maps::mapSprite.fillRectAlpha(Maps::mapScrWidth - 70, mapHeight - 32, 70, Maps::mapScrWidth - 75, 95, TFT_BLACK); - Maps::mapSprite.setTextSize(1); - Maps::mapSprite.drawFastHLine(Maps::mapScrWidth - 65, mapHeight - 14, 60); - Maps::mapSprite.drawFastVLine(Maps::mapScrWidth - 65, mapHeight - 19, 10); - Maps::mapSprite.drawFastVLine(Maps::mapScrWidth - 5, mapHeight - 19, 10); - Maps::mapSprite.drawCenterString(map_scale[zoom], Maps::mapScrWidth - 35, mapHeight - 24); - } -} /** - * @brief Set center coordinates of viewport + * @brief Initialize tile cache system * - * @param pcenter + * @details Initializes the tile cache system by clearing existing cache, reserving space for the maximum number of cached tiles, + * resetting the access counter, and logging the cache capacity for debugging purposes. */ -void Maps::ViewPort::setCenter(Point32 pcenter) +void Maps::initTileCache() { - center = pcenter; - bbox.min.x = pcenter.x - Maps::tileWidth * zoom / 2; - bbox.min.y = pcenter.y - Maps::tileHeight * zoom / 2; - bbox.max.x = pcenter.x + Maps::tileWidth * zoom / 2; - bbox.max.y = pcenter.y + Maps::tileHeight * zoom / 2; + tileCache.clear(); + tileCache.reserve(maxCachedTiles); + cacheAccessCounter = 0; + ESP_LOGI(TAG, "Tile cache initialized with %zu tiles capacity", maxCachedTiles); } -// Public section /** - * @brief Init map size + * @brief Calculate hash for tile identification + * + * @details Creates a simple hash from the file path for cache lookup. * - * @param mapHeight -> Screen map size height - * @param mapWidth -> Screen map size width - * @param mapFull -> Full Screen map size + * @param filePath The file path to hash. + * @return Hash value for the file path. */ -void Maps::initMap(uint16_t mapHeight, uint16_t mapWidth, uint16_t mapFull) +uint32_t Maps::calculateTileHash(const char* filePath) { - Maps::mapScrHeight = mapHeight; - Maps::mapScrWidth = mapWidth; - Maps::mapScrFull = mapFull; - - // Reserve PSRAM for buffer map - Maps::mapTempSprite.deleteSprite(); - Maps::mapTempSprite.createSprite(tileHeight, tileWidth); - - Maps::oldMapTile = {}; // Old Map tile coordinates and zoom - Maps::currentMapTile = {}; // Current Map tile coordinates and zoom - Maps::roundMapTile = {}; // Boundaries Map tiles - Maps::navArrowPosition = {0, 0}; // Map Arrow position - - Maps::totalBounds = {90.0, -90.0, 180.0, -180.0}; + uint32_t hash = 0; + const char* p = filePath; + while (*p) { + hash = hash * 31 + *p; + p++; + } + return hash; } /** - * @brief Delete map screen and release PSRAM + * @brief Get tile from cache * + * @details Attempts to retrieve a tile from the cache. If found, copies it to the target sprite. + * + * @param filePath The file path of the tile to retrieve. + * @param target The target sprite to copy the cached tile to. + * @param xOffset The x-offset for positioning. + * @param yOffset The y-offset for positioning. + * @return true if tile was found in cache, false otherwise. */ -void Maps::deleteMapScrSprites() +bool Maps::getCachedTile(const char* filePath, TFT_eSprite& target, int16_t xOffset, int16_t yOffset) { - Maps::arrowSprite.deleteSprite(); - Maps::mapSprite.deleteSprite(); + if (maxCachedTiles == 0) return false; // Cache disabled + + uint32_t tileHash = calculateTileHash(filePath); + + for (auto& cachedTile : tileCache) + { + if (cachedTile.isValid && cachedTile.tileHash == tileHash) + { + // Found in cache - update access time and copy sprite + cachedTile.lastAccess = ++cacheAccessCounter; + + // Copy the cached sprite to target position + target.pushImage(xOffset, yOffset, tileWidth, tileHeight, cachedTile.sprite->frameBuffer(0)); + return true; + } + } + + return false; // Not found in cache } /** - * @brief Create map screen + * @brief Add rendered tile to cache * + * @details Adds a newly rendered tile to the cache, evicting LRU entries if necessary. + * + * @param filePath The file path of the tile. + * @param source The source sprite containing the rendered tile. */ -void Maps::createMapScrSprites() +void Maps::addToCache(const char* filePath, TFT_eSprite& source) { - // Map Sprite - if (!mapSet.mapFullScreen) - Maps::mapSprite.createSprite(Maps::mapScrWidth, Maps::mapScrHeight); - else - Maps::mapSprite.createSprite(Maps::mapScrWidth, Maps::mapScrFull); - // Arrow Sprite - Maps::arrowSprite.createSprite(16, 16); - Maps::arrowSprite.setColorDepth(16); - Maps::arrowSprite.pushImage(0, 0, 16, 16, (uint16_t *)navigation); + if (maxCachedTiles == 0) return; // Cache disabled + + uint32_t tileHash = calculateTileHash(filePath); + + // Check if already in cache + for (auto& cachedTile : tileCache) + { + if (cachedTile.isValid && cachedTile.tileHash == tileHash) + { + cachedTile.lastAccess = ++cacheAccessCounter; + return; // Already cached + } + } + + // Need to add new entry + if (tileCache.size() >= maxCachedTiles) + evictLRUTile(); // Make room + + // Create new cache entry + CachedTile newEntry; + newEntry.sprite = new TFT_eSprite(&tft); + newEntry.sprite->createSprite(tileWidth, tileHeight); + + // Copy the rendered tile to cache + newEntry.sprite->pushImage(0, 0, tileWidth, tileHeight, source.frameBuffer(0)); + + newEntry.tileHash = tileHash; + newEntry.lastAccess = ++cacheAccessCounter; + newEntry.isValid = true; + strncpy(newEntry.filePath, filePath, sizeof(newEntry.filePath) - 1); + newEntry.filePath[sizeof(newEntry.filePath) - 1] = '\0'; + + tileCache.push_back(newEntry); } /** - * @brief Generate render map + * @brief Remove least recently used tile from cache * - * @param zoom -> Zoom Level + * @details Finds and removes the tile with the oldest access time. */ -void Maps::generateRenderMap(uint8_t zoom) +void Maps::evictLRUTile() { - Maps::mapTileSize = Maps::renderMapTileSize; - Maps::zoomLevel = zoom; - - bool foundRoundMap = false; - bool missingMap = false; - - - if (Maps::followGps) - Maps::currentMapTile = Maps::getMapTile(gps.gpsData.longitude, gps.gpsData.latitude, Maps::zoomLevel, 0, 0); - else - Maps::currentMapTile = Maps::getMapTile(Maps::currentMapTile.lon, Maps::currentMapTile.lat, Maps::zoomLevel, 0, 0); - - // Detects if tile changes from actual GPS position - if (strcmp(Maps::currentMapTile.file, Maps::oldMapTile.file) != 0 || Maps::currentMapTile.zoom != Maps::oldMapTile.zoom || - Maps::currentMapTile.tilex != Maps::oldMapTile.tilex || Maps::currentMapTile.tiley != Maps::oldMapTile.tiley) - { - Maps::isMapFound = Maps::mapTempSprite.drawPngFile(Maps::currentMapTile.file, Maps::mapTileSize, Maps::mapTileSize); + if (tileCache.empty()) return; + + auto lruIt = tileCache.begin(); + uint32_t oldestAccess = lruIt->lastAccess; - if (!Maps::isMapFound) + for (auto it = tileCache.begin(); it != tileCache.end(); ++it) { - ESP_LOGE(TAG, "No Map Found!"); - Maps::isMapFound = false; - Maps::oldMapTile = Maps::currentMapTile; - strcpy(Maps::oldMapTile.file,Maps::currentMapTile.file); - Maps::mapTempSprite.fillScreen(TFT_BLACK); - Maps::showNoMap(Maps::mapTempSprite); + if (it->lastAccess < oldestAccess) + { + oldestAccess = it->lastAccess; + lruIt = it; + } } - else + + // Free the sprite memory + if (lruIt->sprite) { - Maps::oldMapTile = Maps::currentMapTile; - strcpy(Maps::oldMapTile.file,Maps::currentMapTile.file); - - Maps::totalBounds = Maps::getTileBounds(Maps::currentMapTile.tilex, Maps::currentMapTile.tiley, Maps::zoomLevel); - - int8_t startX = -1; - int8_t startY = -1; - - for (int8_t y = startX; y <= startX + 2 ; y++) - { - for (int8_t x = startY; x <= startY + 2; x++) - { - - if (x == 0 && y == 0) - continue;// Skip Center Tile - - Maps::roundMapTile = getMapTile(Maps::currentMapTile.lon, Maps::currentMapTile.lat, Maps::zoomLevel, x, y); - - foundRoundMap = Maps::mapTempSprite.drawPngFile(Maps::roundMapTile.file, (x - startX) * Maps::mapTileSize, (y - startY) * Maps::mapTileSize); - if (!foundRoundMap) - { - Maps::mapTempSprite.fillRect((x - startX) * Maps::mapTileSize, (y - startY) * Maps::mapTileSize, Maps::mapTileSize, Maps::mapTileSize, TFT_BLACK); - Maps::mapTempSprite.drawPngFile(noMapFile, ((x - startX) * Maps::mapTileSize) + (Maps::mapTileSize / 2) - 50, ((y - startY) * Maps::mapTileSize) + (Maps::mapTileSize / 2) - 50); - missingMap = true; - } - else - { - tileBounds currentBounds = Maps::getTileBounds(Maps::roundMapTile.tilex, Maps::roundMapTile.tiley, Maps::zoomLevel); - - if (currentBounds.lat_min < Maps::totalBounds.lat_min) - Maps::totalBounds.lat_min = currentBounds.lat_min; - if (currentBounds.lat_max > Maps::totalBounds.lat_max) - Maps::totalBounds.lat_max = currentBounds.lat_max; - if (currentBounds.lon_min < Maps::totalBounds.lon_min) - Maps::totalBounds.lon_min = currentBounds.lon_min; - if (currentBounds.lon_max > Maps::totalBounds.lon_max) - Maps::totalBounds.lon_max = currentBounds.lon_max; - } - } - } - - if (!missingMap) - { - if (Maps::isCoordInBounds(Maps::destLat, Maps::destLon, Maps::totalBounds)) - Maps::coords2map(Maps::destLat, Maps::destLon, Maps::totalBounds, &(Maps::wptPosX), &(Maps::wptPosY)); - } - else - { - Maps::wptPosX = -1; - Maps::wptPosY = -1; - } - - Maps::redrawMap = true; - - for (size_t i = 1; i < trackData.size(); i++) - { - if ( trackData[i-1].lon > Maps::totalBounds.lon_min && trackData[i-1].lon < Maps::totalBounds.lon_max && - trackData[i-1].lat > Maps::totalBounds.lat_min && trackData[i-1].lat < Maps::totalBounds.lat_max && - trackData[i].lon > Maps::totalBounds.lon_min && trackData[i].lon < Maps::totalBounds.lon_max && - trackData[i].lat > Maps::totalBounds.lat_min && trackData[i].lat < Maps::totalBounds.lat_max ) - { - uint16_t x, y, x2, y2; - Maps::coords2map(trackData[i-1].lat,trackData[i-1].lon,Maps::totalBounds, &x,&y); - Maps::coords2map(trackData[i].lat,trackData[i].lon,Maps::totalBounds, &x2,&y2); + lruIt->sprite->deleteSprite(); + delete lruIt->sprite; + } + + // Remove from cache + tileCache.erase(lruIt); +} - Maps::mapTempSprite.drawWideLine(x,y,x2,y2,2,TFT_BLUE); - } - } +/** + * @brief Clear all cached tiles + * + * @details Frees all cached tiles and clears the cache. + */ +void Maps::clearTileCache() +{ + for (auto& cachedTile : tileCache) + { + if (cachedTile.sprite) + { + cachedTile.sprite->deleteSprite(); + delete cachedTile.sprite; + } } - } + tileCache.clear(); + cacheAccessCounter = 0; } /** - * @brief Generate vector map + * @brief Get current cache memory usage + * + * @details Calculates the current memory usage of the tile cache. * - * @param zoom -> Zoom Level + * @return Memory usage in bytes. */ -void Maps::generateVectorMap(uint8_t zoom) +size_t Maps::getCacheMemoryUsage() { - Maps::getPosition(gps.gpsData.latitude, gps.gpsData.longitude); - if (Maps::isPosMoved) - { - Maps::mapTileSize = Maps::vectorMapTileSize; - Maps::zoomLevel = zoom; - Maps::viewPort.setCenter(Maps::point); - Maps::getMapBlocks(Maps::viewPort.bbox, Maps::memCache); - Maps::readVectorMap(Maps::viewPort, Maps::memCache, Maps::mapTempSprite, zoom); - Maps::isPosMoved = false; - } + size_t memoryUsage = 0; + for (const auto& cachedTile : tileCache) + { + if (cachedTile.isValid && cachedTile.sprite) + memoryUsage += tileWidth * tileHeight * 2; // RGB565 = 2 bytes per pixel + } + return memoryUsage; } + + + /** - * @brief Display Map + * @brief Add tile to preload queue + * + * @details Adds a tile to the background preload queue for processing. * + * @param filePath The file path of the tile to preload. + * @param tileX The X coordinate of the tile. + * @param tileY The Y coordinate of the tile. + * @param zoom The zoom level of the tile. */ -void Maps::displayMap() + + + + + + + +/** +* @brief Renders a map tile from a binary file onto a sprite. +* +* @details This function reads a binary map tile file, decodes its drawing commands, and renders the resulting graphics onto the provided TFT_eSprite object (map). +* It supports line drawing and polygon filling based on the commands in the file, using a predefined color palette. The function handles batching of line drawing for performance optimization. +* The rendered tile is positioned on the sprite using the specified xOffset and yOffset. +* +* @param path The file path to the binary map tile. +* @param xOffset The x-offset to apply when rendering the tile on the sprite. +* @param yOffset The y-offset to apply when rendering the tile on the sprite. +* @param map The TFT_eSprite object where the tile will be rendered. +* @return true if the tile was rendered successfully, false otherwise. +*/ +bool Maps::renderTile(const char* path, const int16_t xOffset, const int16_t yOffset, TFT_eSprite &map) { - if (!mapSet.mapFullScreen) - Maps::mapSprite.pushSprite(0, 27); - else - Maps::mapSprite.pushSprite(0, 0); - - if (Maps::isMapFound) - { - uint16_t mapHeading = 0; -#ifdef ENABLE_COMPASS + static bool isPaletteLoaded = false; + static bool unifiedPoolLogged = false; + static bool unifiedPolylineLogged = false; + static bool unifiedLineBatchLogged = false; + static bool unifiedPolygonLogged = false; + static bool unifiedPolygonsLogged = false; + if (!isPaletteLoaded) + isPaletteLoaded = Maps::loadPalette("/sdcard/VECTMAP/palette.bin"); + + if (!path || path[0] == '\0') + return false; + + // Try to get tile from cache first + if (getCachedTile(path, map, xOffset, yOffset)) + return true; // Tile found in cache + + FILE* file = fopen(path, "rb"); + if (!file) + return false; + + fseek(file, 0, SEEK_END); + const long fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + + // Use RAII MemoryGuard for data allocation + MemoryGuard dataGuard(fileSize, 0); // Type 0 = general + uint8_t* data = dataGuard.get(); + if (!data) + { + fclose(file); + return false; + } + if (!unifiedPoolLogged) + { + unifiedPoolLogged = true; + } - if (mapSet.mapRotationComp) - mapHeading = compass.getHeading(); - else - mapHeading = gps.gpsData.heading; -#else - mapHeading = gps.gpsData.heading; -#endif + const size_t bytesRead = fread(data, 1, fileSize, file); + fclose(file); + if (bytesRead != fileSize) + return false; + + // Update memory statistics (simplified) + currentMemoryUsage = ESP.getFreeHeap(); + if (currentMemoryUsage > peakMemoryUsage) + peakMemoryUsage = currentMemoryUsage; - Maps::mapTempSprite.pushImage(Maps::wptPosX - 8, Maps::wptPosY - 8, 16, 16, (uint16_t *)waypoint, TFT_BLACK); + size_t offset = 0; + const size_t dataSize = fileSize; + uint8_t current_color = 0xFF; + uint16_t currentDrawColor = RGB332ToRGB565(current_color); - if (Maps::mapTileSize == Maps::renderMapTileSize && Maps::followGps) + const uint32_t num_cmds = readVarint(data, offset, dataSize); + if (num_cmds == 0) + return false; + + // Use optimal batch size for this hardware + const size_t optimalBatchSize = getOptimalBatchSize(); + + // Initialize efficient batch rendering + createRenderBatch(optimalBatchSize); + + int batchCount = 0; + + // Track memory allocations for statistics + totalMemoryAllocations++; + + int executed = 0; + int totalLines = 0; + int batchFlushes = 0; + int batchOptimizations = 0; // Track optimizations per tile + unsigned long renderStart = millis(); + + // Command type counters for debugging + int lineCommands = 0; + int polygonCommands = 0; + int rectangleCommands = 0; + int circleCommands = 0; + + // Optimized flushBatch with better memory access patterns + auto flushCurrentBatch = [&]() + { + if (batchCount == 0) return; + + totalLines += batchCount; + batchFlushes++; + + // Use efficient batch rendering + flushBatch(map, batchOptimizations); + batchCount = 0; + }; + + for (uint32_t cmd_idx = 0; cmd_idx < num_cmds; cmd_idx++) { - Maps::navArrowPosition = Maps::coord2ScreenPos(gps.gpsData.longitude, gps.gpsData.latitude, Maps::zoomLevel, Maps::renderMapTileSize); - Maps::mapTempSprite.setPivot(Maps::renderMapTileSize + Maps::navArrowPosition.posX, Maps::renderMapTileSize + Maps::navArrowPosition.posY); + if (offset >= dataSize) + break; + const size_t cmdStartOffset = offset; + const uint32_t cmdType = readVarint(data, offset, dataSize); + + // Since commands are pre-sorted by layer but we can't detect layer changes, + // we'll be more conservative and flush batches more frequently to ensure + // proper layer ordering. Flush every 50 commands to maintain layer integrity. + if (cmd_idx > 0 && cmd_idx % 50 == 0) { + flushCurrentBatch(); + } + + bool isLineCommand = false; + + switch (cmdType) + { + case SET_COLOR: + flushCurrentBatch(); + if (offset < dataSize) + { + current_color = data[offset++]; + currentDrawColor = RGB332ToRGB565(current_color); + executed++; + } + break; + case SET_COLOR_INDEX: + flushCurrentBatch(); + { + const uint32_t color_index = readVarint(data, offset, dataSize); + current_color = paletteToRGB332(color_index); + currentDrawColor = RGB332ToRGB565(current_color); + executed++; + } + break; + case DRAW_LINE: + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t dx = readZigzag(data, offset, dataSize); + const int32_t dy = readZigzag(data, offset, dataSize); + const int32_t x2 = x1 + dx; + const int32_t y2 = y1 + dy; + const int px1 = uint16ToPixel(x1) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + if (shouldDrawLine(px1 - xOffset, py1 - yOffset, px2 - xOffset, py2 - yOffset)) + { + // Use efficient batch rendering + addToBatch(px1, py1, px2, py2, currentDrawColor); + batchCount++; + executed++; + isLineCommand = true; + } + } + break; + case DRAW_POLYLINE: + { + const uint32_t numPoints = readVarint(data, offset, dataSize); + if (numPoints >= 2) + { + // Use RAII MemoryGuard for coordinate arrays + MemoryGuard pxGuard(numPoints, 6); // Type 6 = coordArray + MemoryGuard pyGuard(numPoints, 6); // Type 6 = coordArray + int* px = pxGuard.get(); + int* py = pyGuard.get(); + if (!px || !py) + { + continue; + } + if (!unifiedPolylineLogged) + { + unifiedPolylineLogged = true; + } + unifiedPoolHitCount += 2; + + if (!px || !py) + { + // Skip this command if allocation failed + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + readZigzag(data, offset, dataSize); + readZigzag(data, offset, dataSize); + } + continue; + } + int32_t prevX = readZigzag(data, offset, dataSize); + int32_t prevY = readZigzag(data, offset, dataSize); + px[0] = uint16ToPixel(prevX) + xOffset; + py[0] = uint16ToPixel(prevY) + yOffset; + for (uint32_t i = 1; i < numPoints; ++i) + { + const int32_t deltaX = readZigzag(data, offset, dataSize); + const int32_t deltaY = readZigzag(data, offset, dataSize); + prevX += deltaX; + prevY += deltaY; + px[i] = uint16ToPixel(prevX) + xOffset; + py[i] = uint16ToPixel(prevY) + yOffset; + } + for (uint32_t i = 1; i < numPoints; ++i) + { + if (shouldDrawLine(px[i-1] - xOffset, py[i-1] - yOffset, px[i] - xOffset, py[i] - yOffset)) + { + // Use efficient batch rendering + addToBatch(px[i-1], py[i-1], px[i], py[i], currentDrawColor); + batchCount++; + } + } + executed++; + isLineCommand = true; + } + else + { + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + readZigzag(data, offset, dataSize); + readZigzag(data, offset, dataSize); + } + } + } + break; + case DRAW_STROKE_POLYGON: + case OPTIMIZED_POLYGON: + case HOLLOW_POLYGON: + flushCurrentBatch(); + { + const uint32_t numPoints = readVarint(data, offset, dataSize); + if (numPoints >= 3) + { + // Use RAII MemoryGuard for coordinate arrays + MemoryGuard pxGuard(numPoints, 6); // Type 6 = coordArray + MemoryGuard pyGuard(numPoints, 6); // Type 6 = coordArray + int* px = pxGuard.get(); + int* py = pyGuard.get(); + if (!px || !py) + { + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + readZigzag(data, offset, dataSize); + readZigzag(data, offset, dataSize); + } + break; + } + if (!unifiedPolygonLogged) + { + unifiedPolygonLogged = true; + } + const int32_t firstX = readZigzag(data, offset, dataSize); + const int32_t firstY = readZigzag(data, offset, dataSize); + px[0] = uint16ToPixel(firstX); + py[0] = uint16ToPixel(firstY); + int prevX = firstX; + int prevY = firstY; + for (uint32_t i = 1; i < numPoints; ++i) + { + const int32_t deltaX = readZigzag(data, offset, dataSize); + const int32_t deltaY = readZigzag(data, offset, dataSize); + prevX += deltaX; + prevY += deltaY; + px[i] = uint16ToPixel(prevX); + py[i] = uint16ToPixel(prevY); + } + if (fillPolygons && numPoints >= 3 && cmdType != HOLLOW_POLYGON) + fillPolygonGeneral(map, px, py, numPoints, currentDrawColor, xOffset, yOffset); + + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + drawPolygonBorder(map, px, py, numPoints, borderColor, currentDrawColor, xOffset, yOffset); + executed++; + } + else + { + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + readZigzag(data, offset, dataSize); + readZigzag(data, offset, dataSize); + } + } + } + break; + case DRAW_STROKE_POLYGONS: + flushCurrentBatch(); + { + const uint32_t numPoints = readVarint(data, offset, dataSize); + int32_t accumX = 0, accumY = 0; + if (numPoints >= 3) + { + // Use RAII MemoryGuard for coordinate arrays + MemoryGuard pxGuard(numPoints, 6); // Type 6 = coordArray + MemoryGuard pyGuard(numPoints, 6); // Type 6 = coordArray + int* px = pxGuard.get(); + int* py = pyGuard.get(); + if (!px || !py) + { + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + readZigzag(data, offset, dataSize); + readZigzag(data, offset, dataSize); + } + break; + } + if (!unifiedPolygonsLogged) + { + unifiedPolygonsLogged = true; + } + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + if (i == 0) + { + accumX = readZigzag(data, offset, dataSize); + accumY = readZigzag(data, offset, dataSize); + } + else + { + const int32_t deltaX = readZigzag(data, offset, dataSize); + const int32_t deltaY = readZigzag(data, offset, dataSize); + accumX += deltaX; + accumY += deltaY; + } + px[i] = uint16ToPixel(accumX); + py[i] = uint16ToPixel(accumY); + } + if (fillPolygons && numPoints >= 3) + { + fillPolygonGeneral(map, px, py, numPoints, currentDrawColor, xOffset, yOffset); + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + drawPolygonBorder(map, px, py, numPoints, borderColor, currentDrawColor, xOffset, yOffset); + polygonCommands++; + executed++; + } + } + else + { + for (uint32_t i = 0; i < numPoints && offset < dataSize; ++i) + { + readZigzag(data, offset, dataSize); + readZigzag(data, offset, dataSize); + } + } + } + break; + case DRAW_HORIZONTAL_LINE: + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t dx = readZigzag(data, offset, dataSize); + const int32_t y = readZigzag(data, offset, dataSize); + const int32_t x2 = x1 + dx; + const int px1 = uint16ToPixel(x1) + xOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py = uint16ToPixel(y) + yOffset; + if (shouldDrawLine(px1 - xOffset, py - yOffset, px2 - xOffset, py - yOffset)) + { + // Use efficient batch rendering + addToBatch(px1, py, px2, py, currentDrawColor); + batchCount++; + executed++; + isLineCommand = true; + } + } + break; + case DRAW_VERTICAL_LINE: + { + const int32_t x = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t dy = readZigzag(data, offset, dataSize); + const int32_t y2 = y1 + dy; + const int px = uint16ToPixel(x) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + if (shouldDrawLine(px - xOffset, py1 - yOffset, px - xOffset, py2 - yOffset)) + { + // Use efficient batch rendering + addToBatch(px, py1, px, py2, currentDrawColor); + batchCount++; + executed++; + isLineCommand = true; + } + } + break; + case RECTANGLE: + flushCurrentBatch(); + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t dx = readZigzag(data, offset, dataSize); + const int32_t dy = readZigzag(data, offset, dataSize); + const int px1 = uint16ToPixel(x1); + const int py1 = uint16ToPixel(y1); + const int pwidth = uint16ToPixel(dx); + const int pheight = uint16ToPixel(dy); + if (px1 >= 0 && px1 <= TILE_SIZE && py1 >= 0 && py1 <= TILE_SIZE && + pwidth > 0 && pheight > 0 && + !isPointOnMargin(px1, py1) && + !isPointOnMargin(px1 + pwidth, py1 + pheight)) + { + if (fillPolygons) + { + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.fillRect(px1 + xOffset, py1 + yOffset, pwidth, pheight, currentDrawColor); + map.drawRect(px1 + xOffset, py1 + yOffset, pwidth, pheight, borderColor); + executed++; + } + else + map.drawRect(px1 + xOffset, py1 + yOffset, pwidth, pheight, currentDrawColor); + executed++; + } + } + break; + case SIMPLE_RECTANGLE: + flushCurrentBatch(); + { + const int32_t x = readZigzag(data, offset, dataSize); + const int32_t y = readZigzag(data, offset, dataSize); + const int32_t width = readZigzag(data, offset, dataSize); + const int32_t height = readZigzag(data, offset, dataSize); + const int px = uint16ToPixel(x); + const int py = uint16ToPixel(y); + const int pwidth = uint16ToPixel(width); + const int pheight = uint16ToPixel(height); + + // Validate rectangle bounds + if (px >= 0 && px <= TILE_SIZE && py >= 0 && py <= TILE_SIZE && + pwidth > 0 && pheight > 0 && + px + pwidth <= TILE_SIZE && py + pheight <= TILE_SIZE) + { + if (fillPolygons) + { + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.fillRect(px + xOffset, py + yOffset, pwidth, pheight, currentDrawColor); + map.drawRect(px + xOffset, py + yOffset, pwidth, pheight, borderColor); + } + else + map.drawRect(px + xOffset, py + yOffset, pwidth, pheight, currentDrawColor); + executed++; + } + } + break; + case OPTIMIZED_RECTANGLE: + flushCurrentBatch(); + { + const int32_t x = readZigzag(data, offset, dataSize); + const int32_t y = readZigzag(data, offset, dataSize); + const int32_t width = readZigzag(data, offset, dataSize); + const int32_t height = readZigzag(data, offset, dataSize); + const int px = uint16ToPixel(x); + const int py = uint16ToPixel(y); + const int pwidth = uint16ToPixel(width); + const int pheight = uint16ToPixel(height); + + // Validate rectangle bounds + if (px >= 0 && px <= TILE_SIZE && py >= 0 && py <= TILE_SIZE && + pwidth > 0 && pheight > 0 && + px + pwidth <= TILE_SIZE && py + pheight <= TILE_SIZE) + { + if (fillPolygons) + { + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.fillRect(px + xOffset, py + yOffset, pwidth, pheight, currentDrawColor); + map.drawRect(px + xOffset, py + yOffset, pwidth, pheight, borderColor); + } + else + map.drawRect(px + xOffset, py + yOffset, pwidth, pheight, currentDrawColor); + executed++; + } + } + break; + case STRAIGHT_LINE: + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t dx = readZigzag(data, offset, dataSize); + const int32_t dy = readZigzag(data, offset, dataSize); + const int32_t x2 = x1 + dx; + const int32_t y2 = y1 + dy; + const int px1 = uint16ToPixel(x1) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + if (shouldDrawLine(px1 - xOffset, py1 - yOffset, px2 - xOffset, py2 - yOffset)) + { + // Use efficient batch rendering + addToBatch(px1, py1, px2, py2, currentDrawColor); + batchCount++; + executed++; + isLineCommand = true; + } + } + break; + case CIRCLE: + flushCurrentBatch(); + { + const int32_t center_x = readZigzag(data, offset, dataSize); + const int32_t center_y = readZigzag(data, offset, dataSize); + const int32_t radius = readZigzag(data, offset, dataSize); + const int pcx = uint16ToPixel(center_x); + const int pcy = uint16ToPixel(center_y); + const int pradius = uint16ToPixel(radius); + if (pcx >= 0 && pcx <= TILE_SIZE && pcy >= 0 && pcy <= TILE_SIZE && pradius > 0 && + !isPointOnMargin(pcx, pcy)) + { + if (fillPolygons) + { + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.fillCircle(pcx + xOffset, pcy + yOffset, pradius, currentDrawColor); + map.drawCircle(pcx + xOffset, pcy + yOffset, pradius, borderColor); + executed++; + } + else + map.drawCircle(pcx + xOffset, pcy + yOffset, pradius, currentDrawColor); + + executed++; + } + } + break; + case SIMPLE_CIRCLE: + flushCurrentBatch(); + { + const int32_t center_x = readZigzag(data, offset, dataSize); + const int32_t center_y = readZigzag(data, offset, dataSize); + const int32_t radius = readZigzag(data, offset, dataSize); + const int pcx = uint16ToPixel(center_x); + const int pcy = uint16ToPixel(center_y); + const int pradius = uint16ToPixel(radius); + + if (pcx >= 0 && pcx <= TILE_SIZE && pcy >= 0 && pcy <= TILE_SIZE && pradius > 0 && + pcx + pradius <= TILE_SIZE && pcy + pradius <= TILE_SIZE && + pcx - pradius >= 0 && pcy - pradius >= 0) + { + if (fillPolygons) + { + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.fillCircle(pcx + xOffset, pcy + yOffset, pradius, currentDrawColor); + map.drawCircle(pcx + xOffset, pcy + yOffset, pradius, borderColor); + } + else + map.drawCircle(pcx + xOffset, pcy + yOffset, pradius, currentDrawColor); + executed++; + } + } + break; + case SIMPLE_TRIANGLE: + flushCurrentBatch(); + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t x2 = readZigzag(data, offset, dataSize); + const int32_t y2 = readZigzag(data, offset, dataSize); + const int32_t x3 = readZigzag(data, offset, dataSize); + const int32_t y3 = readZigzag(data, offset, dataSize); + const int px1 = uint16ToPixel(x1) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + const int px3 = uint16ToPixel(x3) + xOffset; + const int py3 = uint16ToPixel(y3) + yOffset; + + if (px1 >= 0 && px1 <= TILE_SIZE && py1 >= 0 && py1 <= TILE_SIZE && + px2 >= 0 && px2 <= TILE_SIZE && py2 >= 0 && py2 <= TILE_SIZE && + px3 >= 0 && px3 <= TILE_SIZE && py3 >= 0 && py3 <= TILE_SIZE) + { + if (fillPolygons) + { + // Fill triangle using simple algorithm + map.fillTriangle(px1, py1, px2, py2, px3, py3, currentDrawColor); + + // Draw border + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.drawLine(px1, py1, px2, py2, borderColor); + map.drawLine(px2, py2, px3, py3, borderColor); + map.drawLine(px3, py3, px1, py1, borderColor); + } + else + { + // Draw only outline + map.drawLine(px1, py1, px2, py2, currentDrawColor); + map.drawLine(px2, py2, px3, py3, currentDrawColor); + map.drawLine(px3, py3, px1, py1, currentDrawColor); + } + executed++; + } + } + break; + case OPTIMIZED_TRIANGLE: + flushCurrentBatch(); + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t x2 = readZigzag(data, offset, dataSize); + const int32_t y2 = readZigzag(data, offset, dataSize); + const int32_t x3 = readZigzag(data, offset, dataSize); + const int32_t y3 = readZigzag(data, offset, dataSize); + const int px1 = uint16ToPixel(x1) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + const int px3 = uint16ToPixel(x3) + xOffset; + const int py3 = uint16ToPixel(y3) + yOffset; + + if (px1 >= 0 && px1 <= TILE_SIZE && py1 >= 0 && py1 <= TILE_SIZE && + px2 >= 0 && px2 <= TILE_SIZE && py2 >= 0 && py2 <= TILE_SIZE && + px3 >= 0 && px3 <= TILE_SIZE && py3 >= 0 && py3 <= TILE_SIZE) + { + if (fillPolygons) + { + // Fill triangle using simple algorithm + map.fillTriangle(px1, py1, px2, py2, px3, py3, currentDrawColor); + + // Draw border + const uint16_t borderColor = RGB332ToRGB565(darkenRGB332(current_color)); + map.drawLine(px1, py1, px2, py2, borderColor); + map.drawLine(px2, py2, px3, py3, borderColor); + map.drawLine(px3, py3, px1, py1, borderColor); + } + else + { + // Draw only outline + map.drawLine(px1, py1, px2, py2, currentDrawColor); + map.drawLine(px2, py2, px3, py3, currentDrawColor); + map.drawLine(px3, py3, px1, py1, currentDrawColor); + } + executed++; + } + } + break; + case DASHED_LINE: + flushCurrentBatch(); + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t x2 = readZigzag(data, offset, dataSize); + const int32_t y2 = readZigzag(data, offset, dataSize); + const int32_t dashLength = readVarint(data, offset, dataSize); + const int32_t gapLength = readVarint(data, offset, dataSize); + const int px1 = uint16ToPixel(x1) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + + if (px1 >= 0 && px1 <= TILE_SIZE && py1 >= 0 && py1 <= TILE_SIZE && + px2 >= 0 && px2 <= TILE_SIZE && py2 >= 0 && py2 <= TILE_SIZE) + { + // Simple dashed line implementation + int dx = abs(px2 - px1); + int dy = abs(py2 - py1); + int sx = (px1 < px2) ? 1 : -1; + int sy = (py1 < py2) ? 1 : -1; + int err = dx - dy; + int currentX = px1, currentY = py1, dashCounter = 0; + + while (true) { + if (dashCounter % (dashLength + gapLength) < dashLength) + map.drawPixel(currentX, currentY, currentDrawColor); + dashCounter++; + if (currentX == px2 && currentY == py2) break; + + int e2 = 2 * err; + if (e2 > -dy) { err -= dy; currentX += sx; } + if (e2 < dx) { err += dx; currentY += sy; } + } + executed++; + } + } + break; + case DOTTED_LINE: + flushCurrentBatch(); + { + const int32_t x1 = readZigzag(data, offset, dataSize); + const int32_t y1 = readZigzag(data, offset, dataSize); + const int32_t x2 = readZigzag(data, offset, dataSize); + const int32_t y2 = readZigzag(data, offset, dataSize); + const int32_t dotSpacing = readVarint(data, offset, dataSize); + const int px1 = uint16ToPixel(x1) + xOffset; + const int py1 = uint16ToPixel(y1) + yOffset; + const int px2 = uint16ToPixel(x2) + xOffset; + const int py2 = uint16ToPixel(y2) + yOffset; + + if (px1 >= 0 && px1 <= TILE_SIZE && py1 >= 0 && py1 <= TILE_SIZE && + px2 >= 0 && px2 <= TILE_SIZE && py2 >= 0 && py2 <= TILE_SIZE) + { + // Simple dotted line implementation + int dx = abs(px2 - px1); + int dy = abs(py2 - py1); + int sx = (px1 < px2) ? 1 : -1; + int sy = (py1 < py2) ? 1 : -1; + int err = dx - dy; + int currentX = px1, currentY = py1, dotCounter = 0; + + while (true) { + if (dotCounter % dotSpacing == 0) + map.drawPixel(currentX, currentY, currentDrawColor); + dotCounter++; + if (currentX == px2 && currentY == py2) break; + + int e2 = 2 * err; + if (e2 > -dy) { err -= dy; currentX += sx; } + if (e2 < dx) { err += dx; currentY += sy; } + } + executed++; + } + } + break; + case GRID_PATTERN: + flushCurrentBatch(); + { + const int32_t x = readZigzag(data, offset, dataSize); + const int32_t y = readZigzag(data, offset, dataSize); + const int32_t width = readVarint(data, offset, dataSize); + const int32_t spacing = readVarint(data, offset, dataSize); + const int32_t count = readVarint(data, offset, dataSize); + const int32_t direction = readVarint(data, offset, dataSize); + const int px = uint16ToPixel(x) + xOffset; + const int py = uint16ToPixel(y) + yOffset; + + if (px >= 0 && px <= TILE_SIZE && py >= 0 && py <= TILE_SIZE) + { + // Simple grid pattern implementation + if (direction == 0) { + // Horizontal lines + for (int i = 0; i < count; i++) { + int lineY = py + (i * spacing); + map.drawLine(px, lineY, px + width, lineY, currentDrawColor); + } + } else { + // Vertical lines + for (int i = 0; i < count; i++) { + int lineX = px + (i * spacing); + map.drawLine(lineX, py, lineX, py + width, currentDrawColor); + } + } + executed++; + } + } + break; + default: + flushCurrentBatch(); + if (offset < dataSize - 4) + offset += 4; + + break; + } + if (!isLineCommand) + flushCurrentBatch(); + + if (offset <= cmdStartOffset) + break; } - else if (Maps::mapTileSize == Maps::renderMapTileSize && !Maps::followGps) + flushCurrentBatch(); + + // Clean up batch rendering + if (activeBatch) { - int16_t pivotX = Maps::tileWidth / 2 + Maps::offsetX; - int16_t pivotY = Maps::tileHeight / 2 + Maps::offsetY; - Maps::mapTempSprite.setPivot(pivotX, pivotY); + if (activeBatch->segments) + delete[] activeBatch->segments; + delete activeBatch; + activeBatch = nullptr; } - else if (Maps::mapTileSize == Maps::vectorMapTileSize) - Maps::mapTempSprite.setPivot(Maps::vectorMapTileSize, Maps::vectorMapTileSize); - if (Maps::followGps) + if (executed == 0) + return false; + + // Add successfully rendered tile to cache + addToCache(path, map); + + unsigned long renderTime = millis() - renderStart; + + return true; +} + +/** + * @brief Initialize the unified memory pool for efficient memory management + * + * @details Sets up a unified memory pool system with mutex protection, calculates optimal pool size based on available PSRAM or RAM, + * reserves space for pool entries, and initializes hit/miss counters for performance monitoring. + */ +void Maps::initUnifiedPool() +{ + ESP_LOGI(TAG, "Initializing unified memory pool..."); + + if (unifiedPoolMutex == nullptr) { - Maps::mapTempSprite.pushRotated(&(Maps::mapSprite), 360 - mapHeading, TFT_TRANSPARENT); - Maps::arrowSprite.pushRotated(&(Maps::mapSprite), 0, TFT_BLACK); + unifiedPoolMutex = xSemaphoreCreateMutex(); + if (unifiedPoolMutex == nullptr) + { + ESP_LOGE(TAG, "Failed to create unified pool mutex"); + return; + } } - else - Maps::mapTempSprite.pushRotated(&mapSprite, 0, TFT_TRANSPARENT); - - Maps::drawMapWidgets(mapSet); - } - else - { - if (Maps::scrollUpdated && !Maps::followGps) - Maps::mapTempSprite.pushSprite(&(Maps::mapSprite), 0, 0, TFT_TRANSPARENT); - else - Maps::mapTempSprite.pushSprite(&(Maps::mapSprite), 0, 0, TFT_TRANSPARENT); - } + +#ifdef BOARD_HAS_PSRAM + size_t psramFree = ESP.getFreePsram(); + maxUnifiedPoolEntries = std::min(static_cast(100), psramFree / (1024 * 32)); // 32KB per entry + ESP_LOGI(TAG, "PSRAM available: %zu bytes, setting unified pool size to %zu entries", psramFree, maxUnifiedPoolEntries); +#else + size_t ramFree = ESP.getFreeHeap(); + maxUnifiedPoolEntries = std::min(static_cast(25), ramFree / (1024 * 64)); // 64KB per entry + ESP_LOGI(TAG, "RAM available: %zu bytes, setting unified pool size to %zu entries", ramFree, maxUnifiedPoolEntries); +#endif + + unifiedPool.clear(); + unifiedPool.reserve(maxUnifiedPoolEntries); + unifiedPoolHitCount = 0; + unifiedPoolMissCount = 0; + + ESP_LOGI(TAG, "Unified memory pool initialized"); } /** - * @brief Set Waypoint coords in Map + * @brief Implement a unified memory allocation function that uses a memory pool + * + * @details Attempts to allocate memory from the unified pool first, falling back to direct heap allocation if pool is full. + * Uses mutex protection for thread safety and tracks allocation statistics for performance monitoring. * - * @param wptLat -> Waypoint Latitude - * @param wptLon -> Waypoint Longitude + * @param size Size of memory to allocate in bytes + * @param type Type of allocation for tracking purposes (0=general, 6=coordArray, etc.) + * @return void* Pointer to allocated memory, or nullptr if allocation failed */ -void Maps::setWaypoint(double wptLat, double wptLon) +void* Maps::unifiedAlloc(size_t size, uint8_t type) { - Maps::destLat = wptLat; - Maps::destLon = wptLon; + if (unifiedPoolMutex && xSemaphoreTake(unifiedPoolMutex, portMAX_DELAY) == pdTRUE) + { + for (auto& entry : unifiedPool) + { + if (!entry.isInUse && entry.size >= size) + { + entry.isInUse = true; + entry.allocationCount++; + entry.type = type; + unifiedPoolHitCount++; + xSemaphoreGive(unifiedPoolMutex); + return entry.ptr; + } + } + + if (unifiedPool.size() < maxUnifiedPoolEntries) + { + void* ptr = nullptr; +#ifdef BOARD_HAS_PSRAM + ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM); +#else + ptr = heap_caps_malloc(size, MALLOC_CAP_8BIT); +#endif + + if (ptr) + { + UnifiedPoolEntry entry; + entry.ptr = ptr; + entry.size = size; + entry.isInUse = true; + entry.allocationCount = 1; + entry.type = type; + unifiedPool.push_back(entry); + unifiedPoolHitCount++; + xSemaphoreGive(unifiedPoolMutex); + return ptr; + } + } + + unifiedPoolMissCount++; + xSemaphoreGive(unifiedPoolMutex); + } + + unifiedPoolMissCount++; +#ifdef BOARD_HAS_PSRAM + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); +#else + return heap_caps_malloc(size, MALLOC_CAP_8BIT); +#endif } /** - * @brief Refresh current map + * @brief Deallocate memory allocated from the unified memory pool * + * @details Marks memory as available in the unified pool if it was allocated from there, otherwise calls standard free. + * Uses mutex protection for thread safety and handles null pointer gracefully. + * + * @param ptr Pointer to memory to deallocate */ - void Maps::updateMap() - { - Maps::oldMapTile = {}; - Maps::isPosMoved = true; - } +void Maps::unifiedDealloc(void* ptr) +{ + if (!ptr) return; + + if (unifiedPoolMutex && xSemaphoreTake(unifiedPoolMutex, portMAX_DELAY) == pdTRUE) + { + for (auto& entry : unifiedPool) + { + if (entry.ptr == ptr && entry.isInUse) + { + entry.isInUse = false; + xSemaphoreGive(unifiedPoolMutex); + return; + } + } + xSemaphoreGive(unifiedPoolMutex); + } + + heap_caps_free(ptr); +} - /** - * @brief Pan current map - * - * @param dx - * @param dy +/** + * @brief Clear the unified memory pool, freeing all allocated memory + * + * @details Frees all memory entries in the unified pool, clears the pool vector, resets hit/miss counters, + * and logs the clearing operation. Uses mutex protection for thread safety. */ - void Maps::panMap(int8_t dx, int8_t dy) - { - Maps::currentMapTile.tilex += dx; - Maps::currentMapTile.tiley += dy; - Maps::currentMapTile.lon = Maps::tilex2lon(Maps::currentMapTile.tilex, Maps::currentMapTile.zoom); - Maps::currentMapTile.lat = Maps::tiley2lat(Maps::currentMapTile.tiley, Maps::currentMapTile.zoom); - } +void Maps::clearUnifiedPool() +{ + if (unifiedPoolMutex && xSemaphoreTake(unifiedPoolMutex, portMAX_DELAY) == pdTRUE) + { + for (auto& entry : unifiedPool) + { + if (entry.ptr) + heap_caps_free(entry.ptr); + } + unifiedPool.clear(); + unifiedPoolHitCount = 0; + unifiedPoolMissCount = 0; + xSemaphoreGive(unifiedPoolMutex); + ESP_LOGI(TAG, "Unified memory pool cleared"); + } +} + /** - * @brief Center map on current GPS location + * @brief Initialize batch rendering system, detecting optimal batch size based on hardware * - * @param lat -> GPS Latitude - * @param lon -> GPS Longitude + * @details Analyzes available PSRAM or RAM to determine optimal batch size for line rendering performance. + * Sets up batch rendering counters and initializes the active batch pointer to nullptr. */ - void Maps::centerOnGps(double lat, double lon) - { - Maps::followGps = true; - Maps::currentMapTile.tilex = Maps::lon2tilex(lon, Maps::currentMapTile.zoom); - Maps::currentMapTile.tiley = Maps::lat2tiley(lat, Maps::currentMapTile.zoom); - Maps::currentMapTile.lat = lat; - Maps::currentMapTile.lon = lon; - } +void Maps::initBatchRendering() +{ + // Detect optimal batch size based on hardware capabilities +#ifdef BOARD_HAS_PSRAM + size_t psramFree = ESP.getFreePsram(); + if (psramFree >= 4 * 1024 * 1024) + maxBatchSize = 512; // High-end ESP32-S3: 512 lines + else if (psramFree >= 2 * 1024 * 1024) + maxBatchSize = 256; // Mid-range ESP32-S3: 256 lines + else + maxBatchSize = 128; // Low-end ESP32-S3: 128 lines +#else + maxBatchSize = 64; // ESP32 without PSRAM: 64 lines +#endif + + activeBatch = nullptr; + batchRenderCount = 0; + batchOptimizationCount = 0; + batchFlushCount = 0; + + ESP_LOGI(TAG, "Batch rendering initialized"); +} /** - * @brief Smooth scroll current map + * @brief Create a new render batch with specified capacity * - * @param dx - * @param dy + * @details Allocates memory for a new render batch with the specified capacity for line segments. + * Cleans up any existing active batch before creating the new one. + * + * @param capacity Number of line segments the batch can hold */ - void Maps::scrollMap(int16_t dx, int16_t dy) - { - const float inertia = 0.5f; - const float friction = 0.95f; - const float maxSpeed = 10.0f; - - static float speedX = 0.0f, speedY = 0.0f; - - speedX = (speedX + dx) * inertia; - speedY = (speedY + dy) * inertia; - - speedX *= friction; - speedY *= friction; - - if (fabs(speedX) > maxSpeed) speedX = (speedX > 0) ? maxSpeed : -maxSpeed; - if (fabs(speedY) > maxSpeed) speedY = (speedY > 0) ? maxSpeed : -maxSpeed; - - Maps::offsetX += (int16_t)speedX; - Maps::offsetY += (int16_t)speedY; - - // Maps::offsetX += (int16_t)dx; - // Maps::offsetY += (int16_t)dy; - - Maps::scrollUpdated = false; - Maps::followGps = false; - - if (Maps::offsetX <= -Maps::scrollThreshold) - { - Maps::tileX--; - Maps::offsetX += Maps::renderMapTileSize; - Maps::scrollUpdated = true; - } - else if (offsetX >= Maps::scrollThreshold) - { - Maps::tileX++; - Maps::offsetX -= Maps::renderMapTileSize; - Maps::scrollUpdated = true; - } - - if (Maps::offsetY <= -Maps::scrollThreshold) - { - Maps::tileY--; - Maps::offsetY += Maps::renderMapTileSize; - Maps::scrollUpdated = true; - } - else if (Maps::offsetY >= Maps::scrollThreshold) - { - Maps::tileY++; - Maps::offsetY -= Maps::renderMapTileSize; - Maps::scrollUpdated = true; - } - - if (Maps::scrollUpdated) - { - int8_t deltaTileX = Maps::tileX - Maps::lastTileX; - int8_t deltaTileY = Maps::tileY - Maps::lastTileY; - Maps::panMap(deltaTileX, deltaTileY); - Maps::preloadTiles(deltaTileX, deltaTileY); - Maps::lastTileX = Maps::tileX; - Maps::lastTileY = Maps::tileY; - } - } +void Maps::createRenderBatch(size_t capacity) +{ + if (activeBatch) + { + if (activeBatch->segments) + delete[] activeBatch->segments; + delete activeBatch; + activeBatch = nullptr; + } + + activeBatch = new RenderBatch(); + activeBatch->segments = new LineSegment[capacity]; + activeBatch->count = 0; + activeBatch->capacity = capacity; + activeBatch->color = 0; +} - /** - * @brief Preload Tiles for map scrolling +/** + * @brief Add a line segment to the current batch if possible * - * @param dirX - * @param dirY + * @details Adds a line segment to the current batch if it matches the batch color and there's capacity. + * Creates a new batch if none exists. Only adds segments that can be batched together. + * + * @param x0 Starting X coordinate + * @param y0 Starting Y coordinate + * @param x1 Ending X coordinate + * @param y1 Ending Y coordinate + * @param color Color of the line segment */ -void Maps::preloadTiles(int8_t dirX, int8_t dirY) +void Maps::addToBatch(int x0, int y0, int x1, int y1, uint16_t color) { - int16_t preloadWidth = (dirX != 0) ? renderMapTileSize : renderMapTileSize * 2; - int16_t preloadHeight = (dirY != 0) ? renderMapTileSize : renderMapTileSize * 2; - - TFT_eSprite preloadSprite = TFT_eSprite(&tft); - preloadSprite.createSprite(preloadWidth, preloadHeight); - - int16_t startX = tileX + dirX; - int16_t startY = tileY + dirY; - - for (int8_t i = 0; i < 2; ++i) - { - int16_t tileToLoadX = startX + ((dirX == 0) ? i - 1 : 0); - int16_t tileToLoadY = startY + ((dirY == 0) ? i - 1 : 0); - - Maps::roundMapTile = Maps::getMapTile( - Maps::currentMapTile.lon, - Maps::currentMapTile.lat, - Maps::zoomLevel, - tileToLoadX, - tileToLoadY - ); - - bool foundTile = preloadSprite.drawPngFile( - Maps::roundMapTile.file, - (dirX != 0) ? i * renderMapTileSize : 0, - (dirY != 0) ? i * renderMapTileSize : 0 - ); - - if (!foundTile) + if (!activeBatch) + createRenderBatch(maxBatchSize); + + // Check if we can add to current batch (same color) + if (!canBatch(color) || activeBatch->count >= activeBatch->capacity) { - preloadSprite.fillRect( - (dirX != 0) ? i * renderMapTileSize : 0, - (dirY != 0) ? i * renderMapTileSize : 0, - renderMapTileSize, - renderMapTileSize, - TFT_LIGHTGREY - ); + return; } - } - - if (dirX != 0) - { - mapTempSprite.scroll(dirX * renderMapTileSize, 0); - mapTempSprite.pushImage( - (dirX > 0 ? renderMapTileSize * 2 : 0), - 0, - preloadWidth, - preloadHeight, - preloadSprite.frameBuffer(0) - ); + + // Add segment to batch + activeBatch->segments[activeBatch->count] = {x0, y0, x1, y1, color}; + activeBatch->count++; + + // Set batch color if first segment + if (activeBatch->count == 1) + activeBatch->color = color; +} + +/** + * @brief Flush the current batch, rendering all segments to the map + * + * @details Renders all line segments in the current batch to the map sprite, applies optimizations if beneficial, + * and resets the batch for reuse. Tracks batch flush and optimization statistics. + * + * @param map Reference to the TFT_eSprite map to render onto + * @param optimizations Reference to a counter for optimizations performed + */ +void Maps::flushBatch(TFT_eSprite& map, int& optimizations) +{ + if (!activeBatch || activeBatch->count == 0) + return; + + batchFlushCount++; + + // Optimize batch if beneficial (threshold based on hardware capabilities) + size_t optimizationThreshold = maxBatchSize / 16; // 6.25% of max batch size (32 lines for 512 batch) + if (activeBatch->count > optimizationThreshold) + { + // Simple batch optimization - just mark as optimized + batchOptimizationCount++; + optimizations++; // Increment local counter } - else if (dirY != 0) + + // Render all segments in batch + for (size_t i = 0; i < activeBatch->count; i++) { - mapTempSprite.scroll(0, dirY * renderMapTileSize); - mapTempSprite.pushImage( - 0, - (dirY > 0 ? renderMapTileSize * 2 : 0), - preloadWidth, - preloadHeight, - preloadSprite.frameBuffer(0) - ); + const LineSegment& segment = activeBatch->segments[i]; + map.drawLine(segment.x0, segment.y0, segment.x1, segment.y1, segment.color); } + + batchRenderCount++; + + // Clear batch for reuse + activeBatch->count = 0; + activeBatch->color = 0; +} - preloadSprite.deleteSprite(); -} \ No newline at end of file + +/** + * @brief Check if a line segment with the given color can be added to the current batch + * + * @details Determines if a line segment can be added to the current batch based on color compatibility. + * Returns true if the batch is empty or if the color matches the current batch color. + * + * @param color Color of the line segment to add + * @return true if it can be added + * @return false if it cannot be added + */ +bool Maps::canBatch(uint16_t color) +{ + if (!activeBatch) + return true; + + // Can batch if same color or batch is empty + return (activeBatch->count == 0) || (activeBatch->color == color); +} + +/** + * @brief Get the optimal batch size based on hardware capabilities + * + * @details Returns the maximum batch size determined during initialization based on available memory. + * This value is calculated based on PSRAM availability for ESP32-S3 or RAM for standard ESP32. + * + * @return size_t Optimal batch size for line rendering + */ +size_t Maps::getOptimalBatchSize() +{ + return maxBatchSize; +} diff --git a/lib/maps/src/maps.hpp b/lib/maps/src/maps.hpp index 7fca949b..c35c7a72 100644 --- a/lib/maps/src/maps.hpp +++ b/lib/maps/src/maps.hpp @@ -1,10 +1,9 @@ /** * @file maps.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) - Render Maps - * @author @aresta - https://github.com/aresta/ESP32_GPS - Vector Maps * @brief Maps draw class - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -12,6 +11,9 @@ #include #include #include +#include +#include +#include #include "tft.hpp" #include "gpsMath.hpp" #include "settings.hpp" @@ -19,159 +21,332 @@ #include "mapVars.h" #include "storage.hpp" +/** + * @brief Draw command types used for rendering vector graphics. + * + * @details These commands are synchronized with the corresponding Python script + * that generates the binary tile data. Each command corresponds to a specific + * geometric drawing operation. + */ +enum DrawCommand : uint8_t +{ + DRAW_LINE = 1, /**< Draw a simple line segment */ + DRAW_POLYLINE = 2, /**< Draw a series of connected line segments */ + DRAW_STROKE_POLYGON = 3, /**< Draw only the stroke (outline) of a single polygon */ + DRAW_STROKE_POLYGONS = 4, /**< Draw only the strokes (outlines) of multiple polygons */ + DRAW_HORIZONTAL_LINE = 5, /**< Draw a horizontal line */ + DRAW_VERTICAL_LINE = 6, /**< Draw a vertical line */ + SET_COLOR = 0x80, /**< Set current drawing color (RGB332) */ + SET_COLOR_INDEX = 0x81, /**< Set current color using palette index */ + RECTANGLE = 0x82, /**< Optimized rectangle command */ + STRAIGHT_LINE = 0x83, /**< Optimized straight line command */ + HIGHWAY_SEGMENT = 0x84, /**< Highway segment with continuity */ + GRID_PATTERN = 0x85, /**< Urban grid pattern */ + BLOCK_PATTERN = 0x86, /**< City block pattern */ + CIRCLE = 0x87, /**< Optimized circle command */ + SET_LAYER = 0x88, /**< Layer indicator command */ + RELATIVE_MOVE = 0x89, /**< Relative coordinate movement */ + PREDICTED_LINE = 0x8A, /**< Predictive line based on pattern */ + COMPRESSED_POLYLINE = 0x8B, /**< Huffman-compressed polyline */ + OPTIMIZED_POLYGON = 0x8C, /**< Optimized polygon (contour only, fill decided by viewer) */ + HOLLOW_POLYGON = 0x8D, /**< Polygon outline only (optimized for boundaries) */ + OPTIMIZED_TRIANGLE = 0x8E, /**< Optimized triangle (contour only, fill decided by viewer) */ + OPTIMIZED_RECTANGLE = 0x8F, /**< Optimized rectangle (contour only, fill decided by viewer) */ + OPTIMIZED_CIRCLE = 0x90, /**< Optimized circle (contour only, fill decided by viewer) */ + SIMPLE_RECTANGLE = 0x96, /**< Simple rectangle (x, y, width, height) */ + SIMPLE_CIRCLE = 0x97, /**< Simple circle (center_x, center_y, radius) */ + SIMPLE_TRIANGLE = 0x98, /**< Simple triangle (x1, y1, x2, y2, x3, y3) */ + DASHED_LINE = 0x99, /**< Dashed line with pattern */ + DOTTED_LINE = 0x9A, /**< Dotted line with pattern */ + }; + +/** + * @class Maps + * @brief Maps class for handling vector and raster map rendering, navigation, and viewport management. + * + */ class Maps { - private: - // Render Map - struct MapTile // Tile Map structure - { - char file[255]; - uint32_t tilex; - uint32_t tiley; - uint8_t zoom; - double lat; - double lon; - }; - uint16_t lon2posx(float f_lon, uint8_t zoom, uint16_t tileSize); - uint16_t lat2posy(float f_lat, uint8_t zoom, uint16_t tileSize); - uint32_t lon2tilex(double f_lon, uint8_t zoom); - uint32_t lat2tiley(double f_lat, uint8_t zoom); - double tilex2lon(uint32_t tileX, uint8_t zoom); - double tiley2lat(uint32_t tileY, uint8_t zoom); +private: + struct MapTile /**< Tile Map structure */ + { + char file[255]; /**< File path of the map tile */ + uint32_t tilex; /**< X index of the tile */ + uint32_t tiley; /**< Y index of the tile */ + uint8_t zoom; /**< Zoom level of the tile */ + float lat; /**< Latitude of the tile center */ + float lon; /**< Longitude of the tile center */ + }; + + struct CachedTile /**< Cached tile structure */ + { + TFT_eSprite* sprite; /**< Rendered tile sprite */ + uint32_t tileHash; /**< Hash for tile identification */ + uint32_t lastAccess; /**< Last access timestamp for LRU */ + bool isValid; /**< Cache entry validity */ + char filePath[255]; /**< Original file path */ + }; + + + struct LineSegment /**< Line segment structure */ + { + int x0, y0, x1, y1; + uint16_t color; + }; + + struct RenderBatch /**< Efficient rendering batch structure */ + { + LineSegment* segments; /**< Array of line segments */ + size_t count; /**< Number of segments in batch */ + size_t capacity; /**< Maximum capacity of batch */ + uint16_t color; /**< Common color for batch */ + }; + + struct PolygonBounds /**< Polygon bounding box structure */ + { + int minX, minY, maxX, maxY; /**< Bounding box coordinates */ + bool isValid; /**< Bounds are valid */ + }; + + // Layer rendering structures + struct RenderCommand /**< Command for layered rendering */ + { + uint8_t type; /**< Command type */ + uint8_t color; /**< RGB332 color */ + uint16_t colorRGB565; /**< RGB565 color */ + void* data; /**< Command-specific data */ + size_t dataSize; /**< Size of command data */ + }; + + struct tileBounds /**< Map boundaries structure */ + { + float lat_min; /**< Minimum latitude */ + float lat_max; /**< Maximum latitude */ + float lon_min; /**< Minimum longitude */ + float lon_max; /**< Maximum longitude */ + }; - // Vector Map - static const int32_t MAPBLOCK_MASK = pow(2, MAPBLOCK_SIZE_BITS) - 1; // ...00000000111111111111 - static const int32_t MAPFOLDER_MASK = pow(2, MAPFOLDER_SIZE_BITS) - 1;// ...00001111 - struct Coord // Point in geographic (lat,lon) coordinates - { - Point32 getPoint32(); - double lat = 0; - double lng = 0; - }; - struct Polyline // Polyline struct - { - std::vector points; - BBox bbox; - uint16_t color; - uint8_t width; - uint8_t maxZoom; - }; - struct Polygon // Polygon struct - { - std::vector points; - BBox bbox; - uint16_t color; - uint8_t maxZoom; - }; - struct MapBlock // Map square area of aprox 4096 meters side. Correspond to one single map file. - { - Point32 offset; - bool inView = false; - std::vector polylines; - std::vector polygons; - }; - struct ViewPort // Vector map viewport structure - { - void setCenter(Point32 pcenter); - Point32 center; - BBox bbox; - }; - ViewPort viewPort; // Vector map viewport - struct MemBlocks // MemBlocks stored in memory - { - std::map blocks_map; // block offset -> block index - std::array blocks; - }; - MemBlocks memBlocks; // Vector file map memory blocks - struct MemCache // MapBlocks memory store - { - std::vector blocks; - }; - MemCache memCache; // Memory Cache - Point32 point = viewPort.center; // Vector map GPS position point - double lat2y(double lat); - double lon2x(double lon); - double mercatorX2lon(double x); - double mercatorY2lat(double y); - int16_t toScreenCoord(const int32_t pxy, const int32_t screenCenterxy); - uint32_t idx; - int16_t parseInt16(char *file); - void parseStrUntil(char *file, char terminator, char *str); - void parseCoords(char *file, std::vector &points); - BBox parseBbox(String str); - MapBlock *readMapBlock(String fileName); - void fillPolygon(Polygon p, TFT_eSprite &map); - void getMapBlocks(BBox &bbox, MemCache &memCache); - void readVectorMap(ViewPort &viewPort, MemCache &memCache, TFT_eSprite &map, uint8_t zoom); - void getPosition(double lat, double lon); + struct ScreenCoord /**< Screen position from GPS coordinates */ + { + uint16_t posX; /**< X position on screen */ + uint16_t posY; /**< Y position on screen */ + }; + + static constexpr int TILE_SIZE = 255; + static constexpr int TILE_SIZE_PLUS_ONE = 256; + static constexpr int MARGIN_PIXELS = 1; + static const uint16_t tileHeight = 768; /**< Tile 9x9 Height Size */ + static const uint16_t tileWidth = 768; /**< Tile 9x9 Width Size */ + static const uint16_t mapTileSize = 256; /**< Map tile size */ + static const uint16_t scrollThreshold = mapTileSize / 2; /**< Smooth scroll threshold */ + + static uint16_t currentDrawColor; /**< Current drawing color state */ + static uint8_t PALETTE[256]; /**< Color palette for vector tiles */ + static uint32_t PALETTE_SIZE; /**< Number of entries in the palette */ + + // Tile cache system + static std::vector tileCache; /**< Tile cache storage */ + static size_t maxCachedTiles; /**< Maximum cached tiles based on hardware */ + static uint32_t cacheAccessCounter; /**< Counter for LRU algorithm */ + + // Background preload system + + // Unified memory pool system (experimental) + struct UnifiedPoolEntry + { + void* ptr; + size_t size; + bool isInUse; + uint32_t allocationCount; + uint8_t type; // 0=general, 1=point, 2=command, 3=coords, 4=feature, 5=lineSegment, 6=coordArray + }; + + static std::vector unifiedPool; /**< Unified memory pool */ + static SemaphoreHandle_t unifiedPoolMutex; /**< Mutex for unified pool */ + static size_t maxUnifiedPoolEntries; /**< Maximum unified pool entries */ + static uint32_t unifiedPoolHitCount; /**< Unified pool hits */ + static uint32_t unifiedPoolMissCount; /**< Unified pool misses */ + + // Memory monitoring and statistics + static uint32_t totalMemoryAllocations; /**< Total memory allocations */ + static uint32_t totalMemoryDeallocations; /**< Total memory deallocations */ + static uint32_t peakMemoryUsage; /**< Peak memory usage */ + static uint32_t currentMemoryUsage; /**< Current memory usage */ + static uint32_t poolEfficiencyScore; /**< Pool efficiency score (0-100) */ + static uint32_t lastStatsUpdate; /**< Last statistics update timestamp */ + + // Polygon optimization system + static bool polygonCullingEnabled; /**< Enable polygon culling */ + static bool optimizedScanlineEnabled; /**< Enable optimized scanline */ + static uint32_t polygonRenderCount; /**< Total polygons rendered */ + static uint32_t polygonCulledCount; /**< Polygons culled (not rendered) */ + static uint32_t polygonOptimizedCount; /**< Polygons using optimized algorithms */ + + + + // Efficient batch rendering system + static RenderBatch* activeBatch; /**< Currently active render batch */ + static size_t maxBatchSize; /**< Maximum batch size for hardware */ + static uint32_t batchRenderCount; /**< Total batches rendered */ + static uint32_t batchOptimizationCount; /**< Batches using optimization */ + static uint32_t batchFlushCount; /**< Total batch flushes */ + + tileBounds totalBounds; /**< Map boundaries */ + uint16_t wptPosX, wptPosY; /**< Waypoint position on screen map */ + TFT_eSprite mapTempSprite = TFT_eSprite(&tft); /**< Full map sprite (not showed) */ + TFT_eSprite mapSprite = TFT_eSprite(&tft); /**< Screen map sprite (showed) */ + float destLat, destLon; /**< Waypoint destination latitude and longitude */ + uint8_t zoomLevel; /**< Zoom level for map display */ + ScreenCoord navArrowPosition; /**< Navigation Arrow position on screen */ + + static uint16_t lon2posx(float f_lon, uint8_t zoom, uint16_t tileSize); + static uint16_t lat2posy(float f_lat, uint8_t zoom, uint16_t tileSize); + static uint32_t lon2tilex(float f_lon, uint8_t zoom); + static uint32_t lat2tiley(float f_lat, uint8_t zoom); + static float tilex2lon(uint32_t tileX, uint8_t zoom); + static float tiley2lat(uint32_t tileY, uint8_t zoom); - // Common - static const uint16_t tileHeight = 768; // Tile 9x9 Height Size - static const uint16_t tileWidth = 768; // Tile 9x9 Width Size - static const uint16_t renderMapTileSize = 256; // Render map tile size - static const uint16_t scrollThreshold = renderMapTileSize / 2; // Smooth scroll threshold - static const uint16_t vectorMapTileSize = tileHeight / 2; // Vector map tile size - uint16_t mapTileSize; // Actual map tile size (render or vector map) - uint16_t wptPosX, wptPosY; // Waypoint position on screen map - TFT_eSprite arrowSprite = TFT_eSprite(&tft); // Sprite for Navigation Arrow in map - TFT_eSprite mapTempSprite = TFT_eSprite(&tft); // Full map sprite (not showed) - TFT_eSprite mapSprite = TFT_eSprite(&tft); // Screen map sprite (showed) - double prevLat, prevLon; // Previous Latitude and Longitude - double destLat, destLon; // Waypoint destination latitude and longitude - uint8_t zoomLevel; // Zoom level for map display - bool isMapFound = false; // Flag to indicate when map is found on SD - struct tileBounds // Map boundaries structure - { - double lat_min; - double lat_max; - double lon_min; - double lon_max; - }; - tileBounds totalBounds; // Map boundaries - struct ScreenCoord // Screen postion from GPS coordinates - { - uint16_t posX; - uint16_t posY; - }; - ScreenCoord navArrowPosition; // Navigation Arrow position on screen tileBounds getTileBounds(uint32_t tileX, uint32_t tileY, uint8_t zoom); - bool isCoordInBounds(double lat, double lon, tileBounds bound); - ScreenCoord coord2ScreenPos(double lon, double lat, uint8_t zoomLevel, uint16_t tileSize); - void coords2map(double lat, double lon, tileBounds bound, uint16_t *pixelX, uint16_t *pixelY); + bool isCoordInBounds(float lat, float lon, tileBounds bound); + ScreenCoord coord2ScreenPos(float lon, float lat, uint8_t zoomLevel, uint16_t tileSize); + void coords2map(float lat, float lon, tileBounds bound, uint16_t *pixelX, uint16_t *pixelY); void showNoMap(TFT_eSprite &map); - void drawMapWidgets(MAP mapSet); + void panMap(int8_t dx, int8_t dy); - public: - uint16_t mapScrHeight; // Screen map size height - uint16_t mapScrWidth; // Screen map size width - uint16_t mapScrFull; // Screen map size in full screen - bool redrawMap = true; // Flag to indicate need redraw Map - bool isPosMoved = true; // Flag when current position changes (vector map) - bool followGps = true; // Flag to indicate if map follow GPS signal - MapTile oldMapTile; // Old Map tile coordinates and zoom - MapTile currentMapTile; // Current Map tile coordinates and zoom - MapTile roundMapTile; // Boundaries Map tiles - int8_t tileX = 0; // Map tile x counter - int8_t tileY = 0; // Map tile y counter - int16_t offsetX = 0; // Accumulative X scroll map offset - int16_t offsetY = 0; // Accumulative Y scroll map offset - bool scrollUpdated = false; // Flag to indicate when map was scrolled and needs to update - int8_t lastTileX = 0; - int8_t lastTileY = 0; + bool loadPalette(const char* palettePath); /**< Load color palette from binary file */ + uint8_t paletteToRGB332(const uint32_t idx); /**< Convert palette index to RGB332 color */ + uint8_t darkenRGB332(const uint8_t color, const float amount); /**< Darken RGB332 color by a given fraction */ + uint16_t RGB332ToRGB565(const uint8_t color); /**< Convert RGB332 color to RGB565 */ + uint32_t readVarint(const uint8_t* data, size_t& offset, size_t dataSize); /**< Read a varint-encoded uint32_t from data buffer */ + int32_t readZigzag(const uint8_t* data, size_t& offset, const size_t dataSize); /**< Read a zigzag-encoded int32_t from data buffer */ + int uint16ToPixel(const int32_t val); /**< Convert uint16_t tile coordinate to pixel coordinate */ + bool isPointOnMargin(const int px, const int py); /**< Check if a point is on the margin of the tile */ + bool isNear(int val, int target, int tol); /**< Check if a value is near a target within a tolerance */ + bool shouldDrawLine(const int px1, const int py1, const int px2, const int py2); /**< Determine if a line should be drawn based on its endpoints */ + void fillPolygonGeneral(TFT_eSprite &map, const int *px, const int *py, + const int numPoints, const uint16_t color, + const int xOffset, const int yOffset); /**< Fill a polygon using the scanline algorithm */ + void drawPolygonBorder(TFT_eSprite &map, const int *px, const int *py, + const int numPoints, const uint16_t borderColor, const uint16_t fillColor, + const int xOffset, const int yOffset); /**< Draw the border of a polygon with margin handling */ + + + // Tile cache methods + void initTileCache(); /**< Initialize tile cache system */ + uint32_t calculateTileHash(const char* filePath); /**< Calculate hash for tile identification */ + bool getCachedTile(const char* filePath, TFT_eSprite& target, int16_t xOffset, int16_t yOffset); /**< Get tile from cache */ + void addToCache(const char* filePath, TFT_eSprite& source); /**< Add rendered tile to cache */ + void evictLRUTile(); /**< Remove least recently used tile from cache */ + void clearTileCache(); /**< Clear all cached tiles */ + size_t getCacheMemoryUsage(); /**< Get current cache memory usage in bytes */ + + + // Unified memory pool methods (experimental) + void initUnifiedPool(); /**< Initialize unified memory pool */ + static void* unifiedAlloc(size_t size, uint8_t type = 0); /**< Allocate from unified pool */ + static void unifiedDealloc(void* ptr); /**< Deallocate from unified pool */ + void clearUnifiedPool(); /**< Clear unified pool */ + + // RAII Memory Guard for automatic memory management + template + class MemoryGuard + { + private: + T* ptr; + size_t size; + uint8_t type; + bool fromPool; + + public: + MemoryGuard(size_t numElements, uint8_t poolType = 0) + : ptr(nullptr), size(numElements * sizeof(T)), type(poolType), fromPool(false) + { + ptr = static_cast(Maps::unifiedAlloc(size, type)); + if (ptr) { + fromPool = true; + } else { + // Fallback to direct allocation +#ifdef BOARD_HAS_PSRAM + ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)); +#else + ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_8BIT)); +#endif + fromPool = false; + } + } + + ~MemoryGuard() + { + if (ptr) { + if (fromPool) { + Maps::unifiedDealloc(ptr); + } else { + heap_caps_free(ptr); + } + } + } + + T* get() const { return ptr; } + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + operator bool() const { return ptr != nullptr; } + + // Disable copy constructor and assignment + MemoryGuard(const MemoryGuard&) = delete; + MemoryGuard& operator=(const MemoryGuard&) = delete; + }; + + // Memory monitoring methods + + // Polygon optimization methods + + + // Efficient batch rendering methods + void initBatchRendering(); /**< Initialize batch rendering system */ + void createRenderBatch(size_t capacity); /**< Create new render batch */ + void addToBatch(int x0, int y0, int x1, int y1, uint16_t color); /**< Add line segment to current batch */ + void flushBatch(TFT_eSprite& map, int& optimizations); /**< Render and clear current batch */ + bool canBatch(uint16_t color); /**< Check if line can be added to current batch */ + size_t getOptimalBatchSize(); /**< Get optimal batch size for hardware */ + +public: + bool fillPolygons; /**< Flag for polygon filling */ + void* mapBuffer; /**< Pointer to map screen sprite */ + uint16_t mapScrHeight; /**< Screen map size height */ + uint16_t mapScrWidth; /**< Screen map size width */ + bool redrawMap = true; /**< Flag to indicate need redraw Map */ + bool followGps = true; /**< Flag to indicate if map follow GPS signal */ + bool isMapFound = false; /**< Flag to indicate when map is found on SD */ + MapTile oldMapTile; /**< Old Map tile coordinates and zoom */ + MapTile currentMapTile; /**< Current Map tile coordinates and zoom */ + MapTile roundMapTile; /**< Boundaries Map tiles */ + int8_t tileX = 0; /**< Map tile x counter */ + int8_t tileY = 0; /**< Map tile y counter */ + int16_t offsetX = 0; /**< Accumulative X scroll map offset */ + int16_t offsetY = 0; /**< Accumulative Y scroll map offset */ + bool scrollUpdated = false; /**< Flag to indicate when map was scrolled and needs to update */ + int8_t lastTileX = 0; /**< Last Map tile x counter */ + int8_t lastTileY = 0; /**< Last Map tile y counter */ Maps(); - MapTile getMapTile(double lon, double lat, uint8_t zoomLevel, int8_t offsetX, int8_t offsetY); - void initMap(uint16_t mapHeight, uint16_t mapWidth, uint16_t mapFull); + MapTile getMapTile(float lon, float lat, uint8_t zoomLevel, int8_t offsetX, int8_t offsetY); + void initMap(uint16_t mapHeight, uint16_t mapWidth); void deleteMapScrSprites(); void createMapScrSprites(); - void generateRenderMap(uint8_t zoom); - void generateVectorMap(uint8_t zoom); + void generateMap(uint8_t zoom); void displayMap(); - void setWaypoint(double wptLat, double wptLon); + void setWaypoint(float wptLat, float wptLon); void updateMap(); - void panMap(int8_t dx, int8_t dy); - void centerOnGps(double lat, double lon); + void centerOnGps(float lat, float lon); void scrollMap(int16_t dx, int16_t dy); void preloadTiles(int8_t dirX, int8_t dirY); -}; \ No newline at end of file + bool renderTile(const char* path, int16_t xOffset, int16_t yOffset, TFT_eSprite &map); + + // Background preload public methods + void disableBackgroundPreload(); /**< Disable background preload system */ + void triggerPreload(int16_t centerX, int16_t centerY, uint8_t zoom); /**< Trigger preload of adjacent tiles */ + + // Memory monitoring public methods +}; + diff --git a/lib/panel/ELECROW_ESP32.hpp b/lib/panel/ELECROW_ESP32.hpp index 7cac156b..4b49ff85 100644 --- a/lib/panel/ELECROW_ESP32.hpp +++ b/lib/panel/ELECROW_ESP32.hpp @@ -2,8 +2,8 @@ * @file ELECROW_ESP32.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver for ELECROW ESP32 3.5 Terminal - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/ICENAV_BOARD.hpp b/lib/panel/ICENAV_BOARD.hpp index b7937f9b..9819389e 100644 --- a/lib/panel/ICENAV_BOARD.hpp +++ b/lib/panel/ICENAV_BOARD.hpp @@ -2,8 +2,8 @@ * @file ICENAV_BOARD.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver for IceNaV board - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/ILI9341_NOTOUCH_SPI.hpp b/lib/panel/ILI9341_NOTOUCH_SPI.hpp index 2590b3ff..2004d9bd 100644 --- a/lib/panel/ILI9341_NOTOUCH_SPI.hpp +++ b/lib/panel/ILI9341_NOTOUCH_SPI.hpp @@ -2,8 +2,8 @@ // * @file ILI9341_NOTOUCH_SPI.hpp // * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) // * @brief LOVYANGFX TFT driver for ILI9341 SPI With no touch -// * @version 0.2.2 -// * @date 2025-05 +// * @version 0.2.3 +// * @date 2025-06 // */ #ifndef ILI9341_NOTOUCH_SPI_HPP diff --git a/lib/panel/ILI9341_XPT2046_SPI.hpp b/lib/panel/ILI9341_XPT2046_SPI.hpp index 10786c00..6dd51552 100644 --- a/lib/panel/ILI9341_XPT2046_SPI.hpp +++ b/lib/panel/ILI9341_XPT2046_SPI.hpp @@ -2,8 +2,8 @@ // * @file ILI9341_XPT2046_SPI.hpp // * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) // * @brief LOVYANGFX TFT driver for ILI9341 SPI With XPT2046 Touch controller -// * @version 0.2.2 -// * @date 2025-05 +// * @version 0.2.3 +// * @date 2025-06 // */ #pragma once diff --git a/lib/panel/ILI9488_FT5x06_16B.hpp b/lib/panel/ILI9488_FT5x06_16B.hpp index a6042e96..d939dc0f 100644 --- a/lib/panel/ILI9488_FT5x06_16B.hpp +++ b/lib/panel/ILI9488_FT5x06_16B.hpp @@ -2,8 +2,8 @@ * @file ILI9488_FT5x06_16B.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver for ILI9488 16 Bits parallel With FT5x06 Touch controller - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/ILI9488_FT5x06_SPI.hpp b/lib/panel/ILI9488_FT5x06_SPI.hpp index 5aff7468..8690d6ce 100644 --- a/lib/panel/ILI9488_FT5x06_SPI.hpp +++ b/lib/panel/ILI9488_FT5x06_SPI.hpp @@ -2,8 +2,8 @@ * @file ILI9488_XPT2046_SPI.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver for ILI9488 SPI With XPT2046 Touch controller - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/ILI9488_NOTOUCH_8B.hpp b/lib/panel/ILI9488_NOTOUCH_8B.hpp index 32e3ac91..5cc98e6f 100644 --- a/lib/panel/ILI9488_NOTOUCH_8B.hpp +++ b/lib/panel/ILI9488_NOTOUCH_8B.hpp @@ -2,8 +2,8 @@ * @file ILI9488_NOTOUCH_8B.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver for ILI9488 8 Bits parallel Without Touch controller - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/ILI9488_XPT2046_SPI.hpp b/lib/panel/ILI9488_XPT2046_SPI.hpp index 143470af..f038d7f7 100644 --- a/lib/panel/ILI9488_XPT2046_SPI.hpp +++ b/lib/panel/ILI9488_XPT2046_SPI.hpp @@ -2,8 +2,8 @@ * @file ILI9488_XPT2046_SPI.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver for ILI9488 SPI With XPT2046 Touch controller - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/LILYGO_TDECK.hpp b/lib/panel/LILYGO_TDECK.hpp index 5a0e0aea..6e22e9d1 100644 --- a/lib/panel/LILYGO_TDECK.hpp +++ b/lib/panel/LILYGO_TDECK.hpp @@ -2,8 +2,8 @@ * @file LILYGO_TDECK.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) and Antonio Vanegas @Hpsturn * @brief LOVYANGFX TFT driver for Lilygo T-DECK biard - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/MAKERF_ESP32S3.hpp b/lib/panel/MAKERF_ESP32S3.hpp index 2b864956..eb9840ae 100644 --- a/lib/panel/MAKERF_ESP32S3.hpp +++ b/lib/panel/MAKERF_ESP32S3.hpp @@ -2,8 +2,8 @@ * @file MAKERF_ESP32S3.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief LOVYANGFX TFT driver MakerFabs 16 Bits parallel With FT5x06 Touch controller - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once diff --git a/lib/panel/panelSelect.hpp b/lib/panel/panelSelect.hpp index d1bf6a94..267fbe12 100644 --- a/lib/panel/panelSelect.hpp +++ b/lib/panel/panelSelect.hpp @@ -2,12 +2,18 @@ * @file panelSelect.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Panel model select - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once +/** + * @brief Include the appropriate display panel driver based on the defined macro + * + * @details This section conditionally includes the correct header for the selected display/touch panel hardware. + * If no supported panel macro is defined, compilation will stop with an error. + */ #if defined(ILI9488_XPT2046_SPI) #include "ILI9488_XPT2046_SPI.hpp" #elif defined(ILI9488_FT5x06_16B) @@ -24,6 +30,10 @@ #include "ICENAV_BOARD.hpp" #elif defined(TDECK_ESP32S3) #include "LILYGO_TDECK.hpp" +#elif defined(MAKERF_ESP32S3) + #include "MAKERF_ESP32S3.hpp" +#elif defined(ELECROW_ESP32) + #include "ELECROW_ESP32.hpp" #else #error "No Panel defined!" #endif \ No newline at end of file diff --git a/lib/power/power.cpp b/lib/power/power.cpp index 6d542500..0f82a04d 100644 --- a/lib/power/power.cpp +++ b/lib/power/power.cpp @@ -2,109 +2,125 @@ * @file power.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief ESP32 Power Management functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "power.hpp" -extern const uint8_t BOARD_BOOT_PIN; +extern const uint8_t BOARD_BOOT_PIN; /**< External declaration for the board's boot pin number. */ /** * @brief Power Class constructor * + * @details Initializes the Power class and optionally disables radio interfaces (WiFi and Bluetooth) + * if the DISABLE_RADIO macro is defined, to reduce power consumption. */ Power::Power() { - #ifdef DISABLE_RADIO - WiFi.disconnect(true); - WiFi.mode(WIFI_OFF); - btStop(); - esp_wifi_stop(); - esp_bt_controller_disable(); - #endif +#ifdef DISABLE_RADIO + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + btStop(); + esp_wifi_stop(); + esp_bt_controller_disable(); +#endif } /** * @brief Deep Sleep Mode - * + * + * @details Puts the device into deep sleep mode to minimize power consumption. + * Disables Bluetooth and WiFi, configures wakeup sources, and starts deep sleep. */ void Power::powerDeepSleep() { - esp_bluedroid_disable(); - esp_bt_controller_disable(); - esp_wifi_stop(); - esp_deep_sleep_disable_rom_logging(); - delay(10); + esp_bluedroid_disable(); + esp_bt_controller_disable(); + esp_wifi_stop(); + esp_deep_sleep_disable_rom_logging(); + delay(10); - #ifdef ICENAV_BOARD - // If you need other peripherals to maintain power, please set the IO port to hold - gpio_hold_en(GPIO_NUM_46); - gpio_hold_en((gpio_num_t)BOARD_BOOT_PIN); - gpio_deep_sleep_hold_en(); - #endif + #ifdef ICENAV_BOARD + // If you need other peripherals to maintain power, please set the IO port to hold + gpio_hold_en(GPIO_NUM_46); + gpio_hold_en((gpio_num_t)BOARD_BOOT_PIN); + gpio_deep_sleep_hold_en(); + #endif - esp_sleep_enable_ext1_wakeup(1ull << BOARD_BOOT_PIN, ESP_EXT1_WAKEUP_ANY_LOW); - esp_deep_sleep_start(); + esp_sleep_enable_ext1_wakeup(1ull << BOARD_BOOT_PIN, ESP_EXT1_WAKEUP_ANY_LOW); + esp_deep_sleep_start(); } /** * @brief Sleep Mode Timer - * - * @param millis + * + * @details Puts the device into light sleep mode for a specified duration. + * Enables timer wakeup and starts light sleep. + * + * @param millis Duration of light sleep in milliseconds */ void Power::powerLightSleepTimer(int millis) { - esp_sleep_enable_timer_wakeup(millis * 1000); - esp_err_t rtc_gpio_hold_en(gpio_num_t GPIO_NUM_5); - esp_light_sleep_start(); + esp_sleep_enable_timer_wakeup(millis * 1000); + esp_err_t rtc_gpio_hold_en(gpio_num_t GPIO_NUM_5); + esp_light_sleep_start(); } /** * @brief Sleep Mode - * + * + * @details Puts the device into light sleep mode until an external event occurs. + * Configures the device to wake up on a low signal from the boot pin. */ void Power::powerLightSleep() { - esp_sleep_enable_ext1_wakeup(1ull << BOARD_BOOT_PIN, ESP_EXT1_WAKEUP_ANY_LOW); - esp_light_sleep_start(); + esp_sleep_enable_ext1_wakeup(1ull << BOARD_BOOT_PIN, ESP_EXT1_WAKEUP_ANY_LOW); + esp_light_sleep_start(); } /** - * @brief Power off peripherals devices + * @brief Powers off peripheral devices to reduce power consumption. + * + * @details Turns off the TFT display, clears the screen, and ends SPI and I2C communications. */ void Power::powerOffPeripherals() { - tftOff(); - tft.fillScreen(TFT_BLACK); - SPI.end(); - Wire.end(); + tftOff(); + tft.fillScreen(TFT_BLACK); + SPI.end(); + Wire.end(); } /** * @brief Core light suspend and TFT off + * + * @details Suspends the device by turning off the TFT display, entering light sleep mode, + * and restoring the display brightness upon wakeup. Ensures the device only resumes + * when the boot pin is released. */ void Power::deviceSuspend() { - int brightness = tft.getBrightness(); - lv_msgbox_close(powerMsg); - lv_refr_now(display); - tftOff(); - powerLightSleep(); - tftOn(brightness); - while (digitalRead(BOARD_BOOT_PIN) != 1) - { - delay(5); - }; - log_v("Exited sleep mode"); + int brightness = tft.getBrightness(); + closeMsg(); + lv_refr_now(display); + tftOff(); + powerLightSleep(); + tftOn(brightness); + while (digitalRead(BOARD_BOOT_PIN) != 1) + { + delay(5); + }; + log_v("Exited sleep mode"); } /** - * @brief Power off peripherals and deep sleep + * @brief Powers off peripherals and enters deep sleep mode. * + * @details Turns off all peripheral devices and puts the device into deep sleep to minimize power consumption. */ void Power::deviceShutdown() { - powerOffPeripherals(); - powerDeepSleep(); + powerOffPeripherals(); + powerDeepSleep(); } diff --git a/lib/power/power.hpp b/lib/power/power.hpp index e5b9dde1..37106a3d 100644 --- a/lib/power/power.hpp +++ b/lib/power/power.hpp @@ -2,8 +2,8 @@ * @file power.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief ESP32 Power Management functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -19,17 +19,24 @@ #include "lvgl.h" #include "globalGuiDef.h" +void closeMsg(); + +/** + * @class Power + * @brief Power management class for device sleep and shutdown operations + * + */ class Power { - private: - void powerDeepSleep(); - void powerLightSleepTimer(int millis); - void powerLightSleep(); - void powerOffPeripherals(); +private: + void powerDeepSleep(); + void powerLightSleepTimer(int millis); + void powerLightSleep(); + void powerOffPeripherals(); - public: - Power(); +public: + Power(); - void deviceSuspend(); - void deviceShutdown(); + void deviceSuspend(); + void deviceShutdown(); }; \ No newline at end of file diff --git a/lib/preferences/preferences-keys.h b/lib/preferences/preferences-keys.h index 04a2bcc7..269366bc 100644 --- a/lib/preferences/preferences-keys.h +++ b/lib/preferences/preferences-keys.h @@ -17,16 +17,17 @@ X(KWEB_FILE, "webServer", BOOL) \ X(KUSER, "-----", UNKNOWN) \ X(KDEF_ZOOM, "defZoom", UINT) \ - X(KMAP_MODE, "fullScrMap", BOOL) \ X(KMAP_VECTOR, "vectMap", BOOL) \ X(KMAP_SPEED, "mapSpeed", BOOL) \ X(KMAP_SCALE, "mapScale", BOOL) \ X(KMAP_COMPASS, "mapComp", BOOL) \ X(KMAP_COMP_ROT, "mapCompRot", BOOL) \ + X(KSIM_NAV, "simNav", BOOL) \ + X(KFILL_POL, "fillPoly", BOOL) \ X(KGPS_TX, "gpsTX", UINT) \ X(KGPS_RX, "gpsRX", UINT) \ - X(KLAT_DFL, "defLAT", DOUBLE) \ - X(KLON_DFL, "defLON", DOUBLE) \ + X(KLAT_DFL, "defLAT", FLOAT) \ + X(KLON_DFL, "defLON", FLOAT) \ X(KDEF_BRIGT, "defBright", UINT) \ X(KVMAX_BATT, "VmaxBatt", FLOAT) \ X(KVMIN_BATT, "VminBatt", FLOAT) \ diff --git a/lib/settings/settings.cpp b/lib/settings/settings.cpp index ab7fd3d8..7c39321d 100644 --- a/lib/settings/settings.cpp +++ b/lib/settings/settings.cpp @@ -2,229 +2,236 @@ * @file settings.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Settings functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "settings.hpp" static const char* TAG PROGMEM = "Settings"; -/** - * @brief Structure for map settings - * - */ MAP mapSet; +NAVIGATION navSet; /** * @brief Zoom Levels and Default zoom * */ -uint8_t minZoom = 0; // Min Zoom Level -uint8_t maxZoom = 0; // Max Zoom Level -uint8_t defZoomRender = 15; // Default Zoom Level for render map -uint8_t defZoomVector = 2; // Default Zoom Level for vector map -uint8_t zoom = 0; // Actual Zoom Level +uint8_t minZoom = 0; /**< Minimum Zoom Level */ +uint8_t maxZoom = 0; /**< Maximum Zoom Level */ +uint8_t defZoom = 15; /**< Default Zoom Level for rendered map */ +uint8_t zoom = 0; /**< Current Zoom Level */ #ifdef LARGE_SCREEN -static const float scale = 1.0f; + static const float scale = 1.0f; /**< Scale factor for large screens */ #else -static const float scale = 0.75f; + static const float scale = 0.75f; /**< Scale factor for small screens */ #endif /** - * @brief Global Variables definition for device preferences & config. + * @brief Global variables definition for device preferences & config. * */ -uint8_t defaultZoom = 0; // Default Zoom Value -uint8_t defBright = 255; // Default Brightness -uint16_t gpsBaud = 0; // GPS Speed -uint16_t gpsUpdate = 0; // GPS Update rate -uint16_t compassPosX = 0; // Compass widget position X -uint16_t compassPosY = 0; // Compass widget position Y -uint16_t coordPosX = 0; // Coordinates widget position X -uint16_t coordPosY = 0; // Coordinates widget position Y -uint16_t altitudePosX = 0; // Altitude widget position X -uint16_t altitudePosY = 0; // Altitude widget position Y -uint16_t speedPosX = 0; // Speed widget position X -uint16_t speedPosY = 0; // Speed widget position Y -uint16_t sunPosX = 0; // Sunrise/sunset position X -uint16_t sunPosY = 0; // Sunrise/sunset position Y -bool enableWeb = true; // Enable/disable web file server -bool showMapToolBar = false; // Show Map Toolbar -int8_t tempOffset = 0; // BME Temperature offset +uint8_t defaultZoom = 0; /**< Default Zoom Value */ +uint8_t defBright = 255; /**< Default Brightness */ +uint16_t gpsBaud = 0; /**< GPS Baud Rate */ +uint16_t gpsUpdate = 0; /**< GPS Update Rate */ +uint16_t compassPosX = 0; /**< Compass widget position X */ +uint16_t compassPosY = 0; /**< Compass widget position Y */ +uint16_t coordPosX = 0; /**< Coordinates widget position X */ +uint16_t coordPosY = 0; /**< Coordinates widget position Y */ +uint16_t altitudePosX = 0; /**< Altitude widget position X */ +uint16_t altitudePosY = 0; /**< Altitude widget position Y */ +uint16_t speedPosX = 0; /**< Speed widget position X */ +uint16_t speedPosY = 0; /**< Speed widget position Y */ +uint16_t sunPosX = 0; /**< Sunrise/sunset widget position X */ +uint16_t sunPosY = 0; /**< Sunrise/sunset widget position Y */ +bool enableWeb = true; /**< Enable or disable web file server */ +bool showMapToolBar = false;/**< Show Map Toolbar */ +int8_t tempOffset = 0; /**< BME Temperature offset */ +bool calculateDST = false;/**< Daylight Saving Time calculation flag */ + + extern Battery battery; extern Compass compass; extern Gps gps; -bool calculateDST = false; // Calculate DST flag /** * @brief Load stored preferences * + * @details Loads all device configuration parameters from persistent storage. + * Configures peripherals, map settings, widget positions, and feature flags + * based on user or default preferences. */ void loadPreferences() { - cfg.init("ICENAV"); -#ifdef ENABLE_COMPASS - compass.setOffsets(cfg.getFloat(PKEYS::KCOMP_OFFSET_X, 0.0), cfg.getFloat(PKEYS::KCOMP_OFFSET_Y, 0.0)); - compass.setDeclinationAngle(cfg.getFloat(PKEYS::KDECL_ANG, 0.22)); - compass.enableKalmanFilter(cfg.getBool(PKEYS::KKALM_FIL, false)); - compass.setKalmanFilterConst(cfg.getFloat(PKEYS::KKALM_Q, 0.01),cfg.getFloat(PKEYS::KKALM_R, 0.1)); -#endif - mapSet.mapRotationComp = cfg.getBool(PKEYS::KMAP_ROT_MODE, false); - mapSet.showMapCompass = cfg.getBool(PKEYS::KMAP_COMPASS, true); - mapSet.compassRotation = cfg.getBool(PKEYS::KMAP_COMP_ROT, true); - mapSet.mapFullScreen = cfg.getBool(PKEYS::KMAP_MODE, true); - mapSet.showMapSpeed = cfg.getBool(PKEYS::KMAP_SPEED, true); - mapSet.vectorMap = cfg.getBool(PKEYS::KMAP_VECTOR, false); - mapSet.showMapScale = cfg.getBool(PKEYS::KMAP_SCALE, true); - gpsBaud = cfg.getShort(PKEYS::KGPS_SPEED, 4); - gpsUpdate = cfg.getShort(PKEYS::KGPS_RATE, 3); - compassPosX = cfg.getInt(PKEYS::KCOMP_X, (TFT_WIDTH / 2) - (100 * scale)); - compassPosY = cfg.getInt(PKEYS::KCOMP_Y, 80); - coordPosX = cfg.getInt(PKEYS::KCOORD_X, (TFT_WIDTH / 2) - (90 * scale)); - coordPosY = cfg.getInt(PKEYS::KCOORD_Y, 30); - altitudePosX = cfg.getInt(PKEYS::KALTITUDE_X, 8); - altitudePosY = cfg.getInt(PKEYS::KALTITUDE_Y, TFT_HEIGHT - 170); - speedPosX = cfg.getInt(PKEYS::KSPEED_X, 1); - speedPosY = cfg.getInt(PKEYS::KSPEED_Y, TFT_HEIGHT - 130); - sunPosX = cfg.getInt(PKEYS::KSUN_X, 170); - sunPosY = cfg.getInt(PKEYS::KSUN_Y, TFT_HEIGHT - 170); - defBright = cfg.getUInt(PKEYS::KDEF_BRIGT, 254); - if (mapSet.vectorMap) - { - minZoom = 1; - maxZoom = 4; - defaultZoom = cfg.getUInt(PKEYS::KDEF_ZOOM, defZoomVector); - } - else - { - minZoom = 6; - maxZoom = 17; - defaultZoom = cfg.getUInt(PKEYS::KDEF_ZOOM, defZoomRender); - } - zoom = defaultZoom; - GPS_TX = cfg.getUInt(PKEYS::KGPS_TX, GPS_TX); - GPS_RX = cfg.getUInt(PKEYS::KGPS_RX, GPS_RX); - enableWeb = cfg.getBool(PKEYS::KWEB_FILE, enableWeb); - tempOffset = cfg.getInt(PKEYS::KTEMP_OFFS, 0); - -// Default Widgets positions -#ifdef TDECK_ESP32S3 - compassPosX = cfg.isKey(CONFKEYS::KCOMP_X) ? cfg.getInt(CONFKEYS::KCOMP_X, compassPosX) : 162; - compassPosY = cfg.isKey(CONFKEYS::KCOMP_Y) ? cfg.getInt(CONFKEYS::KCOMP_Y, compassPosY) : 6; - coordPosX = cfg.isKey(CONFKEYS::KCOORD_X) ? cfg.getInt(CONFKEYS::KCOORD_X, coordPosX) : 1; - coordPosY = cfg.isKey(CONFKEYS::KCOORD_Y) ? cfg.getInt(CONFKEYS::KCOORD_Y, coordPosY) : 10; - altitudePosX = cfg.isKey(CONFKEYS::KALTITUDE_X) ? cfg.getInt(CONFKEYS::KALTITUDE_X, altitudePosX) : 5; - altitudePosY = cfg.isKey(CONFKEYS::KALTITUDE_Y) ? cfg.getInt(CONFKEYS::KALTITUDE_Y, altitudePosY) : 57; - speedPosX = cfg.isKey(CONFKEYS::KSPEED_X) ? cfg.getInt(CONFKEYS::KSPEED_X, speedPosX) : 3; - speedPosY = cfg.isKey(CONFKEYS::KSPEED_Y) ? cfg.getInt(CONFKEYS::KSPEED_Y, speedPosY) : 94; - sunPosX = cfg.isKey(CONFKEYS::KSUN_X) ? cfg.getInt(CONFKEYS::KSPEED_X, speedPosX) : 3; - sunPosY = cfg.isKey(CONFKEYS::KSUN_Y) ? cfg.getInt(CONFKEYS::KSPEED_Y, speedPosY) : 110; -#endif - - battery.setBatteryLevels(cfg.getFloat(PKEYS::KVMAX_BATT, 4.2), cfg.getFloat(PKEYS::KVMIN_BATT, 3.6)); - printSettings(); + cfg.init("ICENAV"); + #ifdef ENABLE_COMPASS + compass.setOffsets(cfg.getFloat(PKEYS::KCOMP_OFFSET_X, 0.0), cfg.getFloat(PKEYS::KCOMP_OFFSET_Y, 0.0)); + compass.setDeclinationAngle(cfg.getFloat(PKEYS::KDECL_ANG, 0.22)); + compass.enableKalmanFilter(cfg.getBool(PKEYS::KKALM_FIL, false)); + compass.setKalmanFilterConst(cfg.getFloat(PKEYS::KKALM_Q, 0.01),cfg.getFloat(PKEYS::KKALM_R, 0.1)); + #endif + mapSet.mapRotationComp = cfg.getBool(PKEYS::KMAP_ROT_MODE, false); + mapSet.showMapCompass = cfg.getBool(PKEYS::KMAP_COMPASS, true); + mapSet.compassRotation = cfg.getBool(PKEYS::KMAP_COMP_ROT, true); + mapSet.showMapSpeed = cfg.getBool(PKEYS::KMAP_SPEED, true); + mapSet.vectorMap = cfg.getBool(PKEYS::KMAP_VECTOR, false); + mapSet.showMapScale = cfg.getBool(PKEYS::KMAP_SCALE, true); + mapSet.fillPolygons = cfg.getBool(PKEYS::KFILL_POL, false); + navSet.simNavigation = cfg.getBool(PKEYS::KSIM_NAV, false); + gpsBaud = cfg.getShort(PKEYS::KGPS_SPEED, 4); + gpsUpdate = cfg.getShort(PKEYS::KGPS_RATE, 3); + compassPosX = cfg.getInt(PKEYS::KCOMP_X, (TFT_WIDTH / 2) - (100 * scale)); + compassPosY = cfg.getInt(PKEYS::KCOMP_Y, 80); + coordPosX = cfg.getInt(PKEYS::KCOORD_X, (TFT_WIDTH / 2) - (90 * scale)); + coordPosY = cfg.getInt(PKEYS::KCOORD_Y, 30); + altitudePosX = cfg.getInt(PKEYS::KALTITUDE_X, 8); + altitudePosY = cfg.getInt(PKEYS::KALTITUDE_Y, TFT_HEIGHT - 170); + speedPosX = cfg.getInt(PKEYS::KSPEED_X, 1); + speedPosY = cfg.getInt(PKEYS::KSPEED_Y, TFT_HEIGHT - 130); + sunPosX = cfg.getInt(PKEYS::KSUN_X, 170); + sunPosY = cfg.getInt(PKEYS::KSUN_Y, TFT_HEIGHT - 170); + defBright = cfg.getUInt(PKEYS::KDEF_BRIGT, 254); + minZoom = 6; + maxZoom = 17; + defaultZoom = cfg.getUInt(PKEYS::KDEF_ZOOM, defZoom); + zoom = defaultZoom; + GPS_TX = cfg.getUInt(PKEYS::KGPS_TX, GPS_TX); + GPS_RX = cfg.getUInt(PKEYS::KGPS_RX, GPS_RX); + enableWeb = cfg.getBool(PKEYS::KWEB_FILE, enableWeb); + tempOffset = cfg.getInt(PKEYS::KTEMP_OFFS, 0); + + // Default Widgets positions + #ifdef TDECK_ESP32S3 + compassPosX = cfg.isKey(CONFKEYS::KCOMP_X) ? cfg.getInt(CONFKEYS::KCOMP_X, compassPosX) : 162; + compassPosY = cfg.isKey(CONFKEYS::KCOMP_Y) ? cfg.getInt(CONFKEYS::KCOMP_Y, compassPosY) : 6; + coordPosX = cfg.isKey(CONFKEYS::KCOORD_X) ? cfg.getInt(CONFKEYS::KCOORD_X, coordPosX) : 1; + coordPosY = cfg.isKey(CONFKEYS::KCOORD_Y) ? cfg.getInt(CONFKEYS::KCOORD_Y, coordPosY) : 10; + altitudePosX = cfg.isKey(CONFKEYS::KALTITUDE_X) ? cfg.getInt(CONFKEYS::KALTITUDE_X, altitudePosX) : 5; + altitudePosY = cfg.isKey(CONFKEYS::KALTITUDE_Y) ? cfg.getInt(CONFKEYS::KALTITUDE_Y, altitudePosY) : 57; + speedPosX = cfg.isKey(CONFKEYS::KSPEED_X) ? cfg.getInt(CONFKEYS::KSPEED_X, speedPosX) : 3; + speedPosY = cfg.isKey(CONFKEYS::KSPEED_Y) ? cfg.getInt(CONFKEYS::KSPEED_Y, speedPosY) : 94; + sunPosX = cfg.isKey(CONFKEYS::KSUN_X) ? cfg.getInt(CONFKEYS::KSPEED_X, speedPosX) : 3; + sunPosY = cfg.isKey(CONFKEYS::KSUN_Y) ? cfg.getInt(CONFKEYS::KSPEED_Y, speedPosY) : 110; + #endif + + battery.setBatteryLevels(cfg.getFloat(PKEYS::KVMAX_BATT, 4.2), cfg.getFloat(PKEYS::KVMIN_BATT, 3.6)); + printSettings(); } /** - * @brief Save GPS speed + * @brief Save GPS baud rate setting * - * @param gpsBaud + * @details Saves the GPS baud rate to persistent storage and configures the GPS port + * accordingly. If not using auto baud detection, sends configuration commands + * to the GPS module (AT6558D), resets the port, and sets the new baud rate. + * If using auto baud detection (gpsBaud == 3), attempts to detect baud rate + * automatically and reconfigures the port. + * + * @param gpsBaud Baud rate index to save and configure */ void saveGPSBaud(uint16_t gpsBaud) { - cfg.saveShort(PKEYS::KGPS_SPEED, gpsBaud); - if (gpsBaud != 4) - { -#ifdef AT6558D_GPS - gpsPort.flush(); - gpsPort.println(GPS_BAUD_PCAS[gpsBaud]); - gpsPort.flush(); - gpsPort.println("$PCAS00*01\r\n"); - gpsPort.flush(); - delay(500); -#endif - gpsPort.flush(); - gpsPort.end(); - delay(500); - gpsPort.setRxBufferSize(1024); - gpsPort.begin(GPS_BAUD[gpsBaud], SERIAL_8N1, GPS_RX, GPS_TX); - delay(500); - } - else - { - gpsBaudDetected = gps.autoBaud(); - - if (gpsBaudDetected != 0) - { - gpsPort.flush(); - gpsPort.end(); - delay(500); - gpsPort.setRxBufferSize(1024); - gpsPort.begin(gpsBaudDetected, SERIAL_8N1, GPS_RX, GPS_TX); - delay(500); - } + cfg.saveShort(PKEYS::KGPS_SPEED, gpsBaud); + if (gpsBaud != 3) + { + #ifdef AT6558D_GPS + gpsPort.flush(); + gpsPort.println(GPS_BAUD_PCAS[gpsBaud]); + gpsPort.flush(); + gpsPort.println("$PCAS00*01\r\n"); + gpsPort.flush(); + delay(500); + #endif + gpsPort.flush(); + gpsPort.end(); + delay(500); + gpsPort.setRxBufferSize(1024); + gpsPort.begin(GPS_BAUD[gpsBaud], SERIAL_8N1, GPS_RX, GPS_TX); + delay(500); + } + else + { + gpsBaudDetected = gps.autoBaud(); + + if (gpsBaudDetected != 0) + { + gpsPort.flush(); + gpsPort.end(); + delay(500); + gpsPort.setRxBufferSize(1024); + gpsPort.begin(gpsBaudDetected, SERIAL_8N1, GPS_RX, GPS_TX); + delay(500); + } } } /** - * @brief Save GPS Update rate + * @brief Save GPS update rate * - * @param gpsUpdateRate + * @details Saves the GPS update rate to persistent storage and, if using the AT6558D GPS module, + * sends the corresponding configuration commands to set the update rate. + * + * @param gpsUpdateRate Update rate index to save and configure */ void saveGPSUpdateRate(uint16_t gpsUpdateRate) { - cfg.saveShort(PKEYS::KGPS_RATE, gpsUpdateRate); + cfg.saveShort(PKEYS::KGPS_RATE, gpsUpdateRate); #ifdef AT6558D_GPS - gpsPort.flush(); - gpsPort.println(GPS_RATE_PCAS[gpsUpdateRate]); - gpsPort.flush(); - gpsPort.println("$PCAS00*01\r\n"); - gpsPort.flush(); - delay(500); + gpsPort.flush(); + gpsPort.println(GPS_RATE_PCAS[gpsUpdateRate]); + gpsPort.flush(); + gpsPort.println("$PCAS00*01\r\n"); + gpsPort.flush(); + delay(500); #endif } /** - * @brief Save Widget position + * @brief Save widget position + * + * @details Saves the X and Y coordinates of a given widget to persistent storage. * - * @param posX - * @param posY + * @param widget Name of the widget (used as key prefix) + * @param posX X position to save + * @param posY Y position to save */ void saveWidgetPos(char *widget, uint16_t posX, uint16_t posY) { - const char *strX = "X"; - const char *strY = "Y"; - char widgetX[30] = ""; - char widgetY[30] = ""; - strcat(widgetX, widget); - strcat(widgetX, strX); - strcat(widgetY, widget); - strcat(widgetY, strY); - - cfg.saveInt(widgetX, posX); - cfg.saveInt(widgetY, posY); + const char *strX = "X"; + const char *strY = "Y"; + char widgetX[30] = ""; + char widgetY[30] = ""; + strcat(widgetX, widget); + strcat(widgetX, strX); + strcat(widgetY, widget); + strcat(widgetY, strY); + + cfg.saveInt(widgetX, posX); + cfg.saveInt(widgetY, posY); } /** * @brief Utility to show all settings + * + * @details Prints all configuration keys, indicating if their values are custom or default, and displays their current values. */ void printSettings() { - ESP_LOGV(TAG, "%11s \t%s \t%s", "KEYNAME", "DEFINED", "VALUE"); - ESP_LOGV(TAG, "%11s \t%s \t%s", "=======", "=======", "====="); - - for (int i = 0; i < KCOUNT; i++) - { - if (i == PKEYS::KUSER) - continue; - String key = cfg.getKey((CONFKEYS)i); - bool isDefined = cfg.isKey(key); - String defined = isDefined ? "custom " : "default"; - String value = ""; - if (isDefined) - value = cfg.getValue(key); - ESP_LOGV(TAG, "%11s \t%s \t%s", key.c_str(), defined.c_str(), value.c_str()); - } + ESP_LOGV(TAG, "%11s \t%s \t%s", "KEYNAME", "DEFINED", "VALUE"); + ESP_LOGV(TAG, "%11s \t%s \t%s", "=======", "=======", "====="); + + for (int i = 0; i < KCOUNT; i++) + { + if (i == PKEYS::KUSER) + continue; + String key = cfg.getKey((CONFKEYS)i); + bool isDefined = cfg.isKey(key); + String defined = isDefined ? "custom " : "default"; + String value = ""; + if (isDefined) + value = cfg.getValue(key); + ESP_LOGV(TAG, "%11s \t%s \t%s", key.c_str(), defined.c_str(), value.c_str()); + } } diff --git a/lib/settings/settings.hpp b/lib/settings/settings.hpp index 2d35fda9..ff25ae02 100644 --- a/lib/settings/settings.hpp +++ b/lib/settings/settings.hpp @@ -2,61 +2,91 @@ * @file settings.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Settings functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once #include #include +#include "globalGpxDef.h" #include "tft.hpp" #include "gps.hpp" #include "battery.hpp" #include "compass.hpp" -extern uint8_t minZoom; // Min Zoom Level -extern uint8_t maxZoom; // Max Zoom Level -extern uint8_t defZoomRender; // Default Zoom Level for render map -extern uint8_t defZoomVector; // Default Zoom Level for vector map -extern uint8_t zoom; // Actual Zoom Level -extern uint8_t defBright; // Default brightness -extern uint8_t defaultZoom; // Default Zoom Value +/** + * @brief Map and Zoom configuration variables + * + */ +extern uint8_t minZoom; /**< Minimum Zoom Level */ +extern uint8_t maxZoom; /**< Maximum Zoom Level */ +extern uint8_t defZoomRender; /**< Default Zoom Level for rendering map */ +extern uint8_t defZoomVector; /**< Default Zoom Level for vector map */ +extern uint8_t zoom; /**< Current Zoom Level */ +extern uint8_t defBright; /**< Default screen brightness */ +extern uint8_t defaultZoom; /**< Default Zoom Value */ -extern bool showMapToolBar; // Show Map Toolbar -extern uint16_t gpsBaud; // GPS Speed -extern uint16_t gpsUpdate; // GPS Update rate -extern uint16_t compassPosX; // Compass widget position X -extern uint16_t compassPosY; // Compass widget position Y -extern uint16_t coordPosX; // Coordinates widget position X -extern uint16_t coordPosY; // Coordinates widget position Y -extern uint16_t altitudePosX; // Altitude widget position X -extern uint16_t altitudePosY; // Altitude widget position Y -extern uint16_t speedPosX; // Speed widget position X -extern uint16_t speedPosY; // Speed widget position Y -extern uint16_t sunPosX; // Sunrise/sunset position X -extern uint16_t sunPosY; // Sunrise/sunset position Y -extern bool enableWeb; // Enable/disable web file server -extern int8_t tempOffset; // BME Temperature offset -extern bool calculateDST; // Calculate DST flag +/** + * @brief UI and toolbar settings + * + */ +extern bool showMapToolBar; /**< Show Map Toolbar */ +/** + *@brief GPS and sensor settings + * + */ +extern uint16_t gpsBaud; /**< GPS Baud Rate */ +extern uint16_t gpsUpdate; /**< GPS Update rate (Hz or ms) */ +extern uint16_t compassPosX; /**< Compass widget position X */ +extern uint16_t compassPosY; /**< Compass widget position Y */ +extern uint16_t coordPosX; /**< Coordinates widget position X */ +extern uint16_t coordPosY; /**< Coordinates widget position Y */ +extern uint16_t altitudePosX; /**< Altitude widget position X */ +extern uint16_t altitudePosY; /**< Altitude widget position Y */ +extern uint16_t speedPosX; /**< Speed widget position X */ +extern uint16_t speedPosY; /**< Speed widget position Y */ +extern uint16_t sunPosX; /**< Sunrise/Sunset widget position X */ +extern uint16_t sunPosY; /**< Sunrise/Sunset widget position Y */ + +/** + * @brief System and environment settings + * + */ +extern bool enableWeb; /**< Enable or disable web file server */ +extern int8_t tempOffset; /**< BME temperature sensor offset */ +extern bool calculateDST; /**< Daylight Saving Time calculation flag */ /** * @brief Structure for map settings * + * @details Contains configuration flags for displaying and interacting with the map UI, + * such as compass, speed, map type, and scale settings. */ struct MAP { - bool showMapCompass; // Compass in map screen - bool compassRotation; // Compass rotation in map screen - bool mapRotationComp; // Rotate map with compass - bool mapFullScreen; // Full Screen map - bool showMapSpeed; // Speed in map screen - bool vectorMap; // Map type (vector/render) - bool showMapScale; // Scale in map screen + bool showMapCompass; /**< Show compass in map screen */ + bool compassRotation; /**< Enable compass rotation in map screen */ + bool mapRotationComp; /**< Rotate map with compass heading */ + bool showMapSpeed; /**< Show speed in map screen */ + bool vectorMap; /**< Map type: true for vector, false for rendered */ + bool showMapScale; /**< Show map scale on screen */ + bool fillPolygons; /**< Flag for polygon filling */ }; -extern MAP mapSet; +extern MAP mapSet; /**< Global instance for map settings */ +/** + * @brief Structure for navigation settings + * + * @details Contains navigation configuration + */ +struct NAVIGATION +{ + bool simNavigation; /**< Indicates whether navigation simulation mode is enabled or disabled. */ +}; +extern NAVIGATION navSet; /**< Global instance for navigation settings */ void loadPreferences(); diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 7f54453a..f5c4190b 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -2,8 +2,8 @@ * @file storage.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Storage definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "storage.hpp" @@ -15,31 +15,40 @@ #include #include -#define SD_OCR_SDHC_CAP (1<<30) +#define SD_OCR_SDHC_CAP (1 << 30) /**< SD card SDHC capacity flag */ -extern const uint8_t SD_CS; -extern const uint8_t SD_MISO; -extern const uint8_t SD_MOSI; -extern const uint8_t SD_CLK; +extern const uint8_t SD_CS; /**< Chip Select pin for SD card */ +extern const uint8_t SD_MISO; /**< MISO pin for SD card */ +extern const uint8_t SD_MOSI; /**< MOSI pin for SD card */ +extern const uint8_t SD_CLK; /**< Clock pin for SD card */ static const char *TAG = "Storage"; +/** + * @brief Formats a byte size as a human-readable string with appropriate units. + * + * @details Converts a size in bytes to a string with units (B, KB, MB, GB, TB), using + * two decimal places of precision. + * + * @param size Size in bytes + * @return std::string Human-readable string (e.g. "12.34 MB") + */ namespace { - std::string formatSize(uint64_t size) - { - static const char *suffixes[] = {"B","KB","MB","GB","TB"}; - int order = 0; - double formatted_size = static_cast(size); - while (formatted_size >= 1024 && order < sizeof(suffixes) / sizeof(suffixes[0]) - 1) - { - order++; - formatted_size /= 1024; - } - std::ostringstream oss; - oss << std::fixed << std::setprecision(2) << formatted_size << " " << suffixes[order]; - return oss.str(); - } + std::string formatSize(uint64_t size) + { + static const char *suffixes[] = {"B","KB","MB","GB","TB"}; + int order = 0; + double formatted_size = static_cast(size); + while (formatted_size >= 1024 && order < sizeof(suffixes) / sizeof(suffixes[0]) - 1) + { + order++; + formatted_size /= 1024; + } + std::ostringstream oss; + oss << std::fixed << std::setprecision(2) << formatted_size << " " << suffixes[order]; + return oss.str(); + } } /** @@ -48,242 +57,238 @@ namespace Storage::Storage() : isSdLoaded(false), card(nullptr) {} /** - * @brief SD Card init with DMA using ESP-IDF + * @brief Initialize the SD card + * @return esp_err_t result code */ esp_err_t Storage::initSD() { -#ifndef SPI_SHARED - - esp_err_t ret; - - sdmmc_host_t host = SDSPI_HOST_DEFAULT(); - #ifdef TDECK_ESP32S3 - host.slot = SPI2_HOST; - #endif - #ifdef ICENAV_BOARD - host.slot = SPI2_HOST; - #endif - #ifdef ESP32S3_N16R8 - host.slot = SPI2_HOST; - #endif - #ifdef ESP32_N16R4 - host.slot = HSPI_HOST; - host.command_timeout_ms = 1000; - #endif - - sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); - slot_config.gpio_cs = (gpio_num_t)SD_CS; - slot_config.host_id = (spi_host_device_t)host.slot; - - // SPI bus configuration - spi_bus_config_t bus_cfg = { - .mosi_io_num = (gpio_num_t)SD_MOSI, - .miso_io_num = (gpio_num_t)SD_MISO, - .sclk_io_num = (gpio_num_t)SD_CLK, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = 4096, // Set transfer size to 4096 bytes (multiple of 512) - .flags = 0, - .intr_flags = 0}; - - // Adjust the SPI speed (frequency) - host.max_freq_khz = 20000; - - // Initialize the SPI bus - ret = spi_bus_initialize((spi_host_device_t)host.slot, &bus_cfg, SPI_DMA_CH_AUTO); - if (ret != ESP_OK) - { - ESP_LOGE(TAG, "Failed to initialize SPI bus."); - return ret; - } - - - ESP_LOGI(TAG, "Initializing SD card"); - - esp_vfs_fat_mount_config_t mount_config = { - .format_if_mount_failed = false, - .max_files = 5, - .allocation_unit_size = 8 * 1024}; - - ret = esp_vfs_fat_sdspi_mount("/sdcard", &host, &slot_config, &mount_config, &card); - if (ret != ESP_OK) - { - if (ret == ESP_FAIL) - { - ESP_LOGE(TAG, "Failed to mount filesystem. " - "If you want the card to be formatted, set format_if_mount_failed = true."); - } - else - { - ESP_LOGE(TAG, "Failed to initialize the card (%s). " - "Make sure SD card lines have pull-up resistors in place.", - esp_err_to_name(ret)); - } - return ret; - } - else - { - ESP_LOGI(TAG, "SD card initialized successfully"); - sdmmc_card_print_info(stdout, card); - isSdLoaded = true; - - return ESP_OK; - } -#else - pinMode(SD_CS, OUTPUT); - digitalWrite(SD_CS, LOW); - - SPI.begin(SD_CLK, SD_MISO, SD_MOSI); - - if (!SD.begin(SD_CS, SPI, 20000000, "/sdcard")) - { - ESP_LOGE(TAG,"SD Card Mount Failed"); - isSdLoaded = false; - return ESP_FAIL; - } - else - { - ESP_LOGI(TAG,"SD Card Mounted"); - isSdLoaded = true; - return ESP_OK; - } -#endif + #ifndef SPI_SHARED + + esp_err_t ret; + + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + #ifdef TDECK_ESP32S3 + host.slot = SPI2_HOST; + #endif + #ifdef ICENAV_BOARD + host.slot = SPI2_HOST; + #endif + #ifdef ESP32S3_N16R8 + host.slot = SPI2_HOST; + #endif + #ifdef ESP32_N16R4 + host.slot = HSPI_HOST; + host.command_timeout_ms = 1000; + #endif + + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = (gpio_num_t)SD_CS; + slot_config.host_id = (spi_host_device_t)host.slot; + + // SPI bus configuration + spi_bus_config_t bus_cfg = { + .mosi_io_num = (gpio_num_t)SD_MOSI, + .miso_io_num = (gpio_num_t)SD_MISO, + .sclk_io_num = (gpio_num_t)SD_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 4096, // Set transfer size to 4096 bytes (multiple of 512) + .flags = 0, + .intr_flags = 0}; + + // Adjust the SPI speed (frequency) + host.max_freq_khz = 20000; + + // Initialize the SPI bus + ret = spi_bus_initialize((spi_host_device_t)host.slot, &bus_cfg, SPI_DMA_CH_AUTO); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "Failed to initialize SPI bus."); + return ret; + } + + + ESP_LOGI(TAG, "Initializing SD card"); + + esp_vfs_fat_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5, + .allocation_unit_size = 8 * 1024}; + + ret = esp_vfs_fat_sdspi_mount("/sdcard", &host, &slot_config, &mount_config, &card); + if (ret != ESP_OK) + { + if (ret == ESP_FAIL) + { + ESP_LOGE(TAG, "Failed to mount filesystem. " + "If you want the card to be formatted, set format_if_mount_failed = true."); + } + else + { + ESP_LOGE(TAG, "Failed to initialize the card (%s). " + "Make sure SD card lines have pull-up resistors in place.", + esp_err_to_name(ret)); + } + return ret; + } + else + { + ESP_LOGI(TAG, "SD card initialized successfully"); + sdmmc_card_print_info(stdout, card); + isSdLoaded = true; + + return ESP_OK; + } + #else + pinMode(SD_CS, OUTPUT); + digitalWrite(SD_CS, LOW); + + SPI.begin(SD_CLK, SD_MISO, SD_MOSI); + + if (!SD.begin(SD_CS, SPI, 20000000, "/sdcard")) + { + ESP_LOGE(TAG,"SD Card Mount Failed"); + isSdLoaded = false; + return ESP_FAIL; + } + else + { + ESP_LOGI(TAG,"SD Card Mounted"); + isSdLoaded = true; + return ESP_OK; + } + #endif } /** - * @brief SPIFFS initialization - * - * @return esp_err_t Error code for SPIFFS setup + * @brief Initialize SPIFFS filesystem + * @return esp_err_t result code */ esp_err_t Storage::initSPIFFS() { - ESP_LOGI(TAG, "Initializing SPIFFS"); - - esp_vfs_spiffs_conf_t conf = - { - .base_path = "/spiffs", - .partition_label = NULL, - .max_files = 5, - .format_if_mount_failed = false}; - - esp_err_t ret = esp_vfs_spiffs_register(&conf); - - if (ret != ESP_OK) - { - if (ret == ESP_FAIL) - ESP_LOGE(TAG, "Failed to mount or format filesystem"); - else if (ret == ESP_ERR_NOT_FOUND) - ESP_LOGE(TAG, "Failed to find SPIFFS partition"); - else - ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); - return ESP_FAIL; - } - - size_t total = 0, used = 0; - ret = esp_spiffs_info(NULL, &total, &used); - if (ret != ESP_OK) - ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); - else - ESP_LOGI(TAG, "Partition size: total: %d used: %d", total, used); - - return ESP_OK; + ESP_LOGI(TAG, "Initializing SPIFFS"); + + esp_vfs_spiffs_conf_t conf = + { + .base_path = "/spiffs", + .partition_label = NULL, + .max_files = 5, + .format_if_mount_failed = false}; + + esp_err_t ret = esp_vfs_spiffs_register(&conf); + + if (ret != ESP_OK) + { + if (ret == ESP_FAIL) + ESP_LOGE(TAG, "Failed to mount or format filesystem"); + else if (ret == ESP_ERR_NOT_FOUND) + ESP_LOGE(TAG, "Failed to find SPIFFS partition"); + else + ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + return ESP_FAIL; + } + + size_t total = 0, used = 0; + ret = esp_spiffs_info(NULL, &total, &used); + if (ret != ESP_OK) + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); + else + ESP_LOGI(TAG, "Partition size: total: %d used: %d", total, used); + + return ESP_OK; } /** * @brief Get SD card information - * - * @return SDCardInfo structure containing SD card information + * @return SDCardInfo structure containing SD card details */ SDCardInfo Storage::getSDCardInfo() { - SDCardInfo info; - - #ifndef SPI_SHARED - if (card != nullptr) - { - info.name = std::string(reinterpret_cast(card->cid.name)); - info.capacity = formatSize((uint64_t)(card->csd.capacity) * card->csd.sector_size); - info.sector_size = card->csd.sector_size; - info.read_block_len = card->csd.read_block_len; - info.card_type = (card->ocr && SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC"; - - FATFS *fs; - DWORD fre_clust, fre_sect, tot_sect; - - if (f_getfree("0:",&fre_clust, &fs) == FR_OK) - { - tot_sect = (fs->n_fatent - 2) * fs->csize; - fre_sect = fre_clust * fs->csize; - - uint64_t total_space_bytes = tot_sect / 2 ; - uint64_t free_space_bytes = fre_sect / 2 ; - uint64_t used_space_bytes = total_space_bytes - free_space_bytes; - - info.total_space = formatSize(total_space_bytes); - info.free_space = formatSize(free_space_bytes); - info.used_space = formatSize(used_space_bytes); - } - else - { - ESP_LOGE(TAG, "Failed to get filesystem info"); - info.total_space = "0 B"; - info.free_space = "0 B"; - info.used_space = "0 B"; - } - } - else - ESP_LOGE(TAG, "SD Card not initialized"); - #else - uint8_t cardType = SD.cardType(); - if (cardType == CARD_MMC) - info.card_type = "MMC"; - else if (cardType == CARD_SD) - info.card_type = "SDSC"; - else if (cardType == CARD_SDHC) - info.card_type = "SDHC"; - else - info.card_type = "UNKNOWN"; - - info.total_space = formatSize(SD.cardSize()); - info.free_space = formatSize((SD.totalBytes() - SD.usedBytes())); - info.used_space = formatSize(SD.usedBytes()); - #endif - - return info; + SDCardInfo info; + + #ifndef SPI_SHARED + if (card != nullptr) + { + info.name = std::string(reinterpret_cast(card->cid.name)); + info.capacity = formatSize((uint64_t)(card->csd.capacity) * card->csd.sector_size); + info.sector_size = card->csd.sector_size; + info.read_block_len = card->csd.read_block_len; + info.card_type = (card->ocr && SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC"; + + FATFS *fs; + DWORD fre_clust, fre_sect, tot_sect; + + if (f_getfree("0:",&fre_clust, &fs) == FR_OK) + { + tot_sect = (fs->n_fatent - 2) * fs->csize; + fre_sect = fre_clust * fs->csize; + + uint64_t total_space_bytes = tot_sect / 2 ; + uint64_t free_space_bytes = fre_sect / 2 ; + uint64_t used_space_bytes = total_space_bytes - free_space_bytes; + + info.total_space = formatSize(total_space_bytes); + info.free_space = formatSize(free_space_bytes); + info.used_space = formatSize(used_space_bytes); + } + else + { + ESP_LOGE(TAG, "Failed to get filesystem info"); + info.total_space = "0 B"; + info.free_space = "0 B"; + info.used_space = "0 B"; + } + } + else + ESP_LOGE(TAG, "SD Card not initialized"); + #else + uint8_t cardType = SD.cardType(); + if (cardType == CARD_MMC) + info.card_type = "MMC"; + else if (cardType == CARD_SD) + info.card_type = "SDSC"; + else if (cardType == CARD_SDHC) + info.card_type = "SDHC"; + else + info.card_type = "UNKNOWN"; + + info.total_space = formatSize(SD.cardSize()); + info.free_space = formatSize((SD.totalBytes() - SD.usedBytes())); + info.used_space = formatSize(SD.usedBytes()); + #endif + + return info; } /** - * @brief Get SD status - * + * @brief Check if SD card is loaded * @return true if SD card is loaded, false otherwise */ bool Storage::getSdLoaded() const { - return isSdLoaded; + return isSdLoaded; } /** - * @brief Open a file on the SD card - * - * @param path Path to the file - * @param mode Mode in which to open the file - * @return FILE* Pointer to the opened file + * @brief Open a file at the specified path + * @param path File path + * @param mode File open mode + * @return FILE* pointer to opened file, or nullptr on failure */ FILE *Storage::open(const char *path, const char *mode) { - return fopen(path, mode); + return fopen(path, mode); } /** - * @brief Close a file on the SD card - * - * @param file Pointer to the file - * @return int 0 on success, EOF on error + * @brief Close an open file + * @param file FILE* pointer to file + * @return Result of fclose (0 on success, EOF on error) */ int Storage::close(FILE *file) { - return fclose(file); + return fclose(file); } /** @@ -294,176 +299,163 @@ int Storage::close(FILE *file) */ size_t Storage::size(const char *path) { - struct stat st; - if (stat(path, &st) == 0) - return st.st_size; - return 0; + struct stat st; + if (stat(path, &st) == 0) + return st.st_size; + return 0; } /** - * @brief Read a specified number of bytes from a file into a buffer - * - * @param file Pointer to the file - * @param buffer Buffer to read the bytes into + * @brief Read from a file into a uint8_t buffer + * @param file FILE* pointer + * @param buffer Buffer to read into * @param size Number of bytes to read - * @return size_t Number of bytes actually read + * @return Number of bytes read */ size_t Storage::read(FILE *file, uint8_t *buffer, size_t size) { - if (!file) - return 0; - return fread(buffer, 1, size, file); + if (!file) + return 0; + return fread(buffer, 1, size, file); } /** - * @brief Read a specified number of chars from a file into a buffer - * - * @param file Pointer to the file - * @param buffer Buffer to read the chars into + * @brief Read from a file into a char buffer + * @param file FILE* pointer + * @param buffer Buffer to read into * @param size Number of bytes to read - * @return size_t Number of chars actually read + * @return Number of bytes read */ size_t Storage::read(FILE *file, char *buffer, size_t size) { - if (!file) - return 0; - return fread(buffer, 1, size, file); + if (!file) + return 0; + return fread(buffer, 1, size, file); } /** - * @brief Write a specified number of bytes from a buffer to a file - * - * @param file Pointer to the file - * @param buffer Buffer containing the bytes to write + * @brief Write from a uint8_t buffer to a file + * @param file FILE* pointer + * @param buffer Buffer to write from * @param size Number of bytes to write - * @return size_t Number of bytes actually written + * @return Number of bytes written */ size_t Storage::write(FILE *file, const uint8_t *buffer, size_t size) { - if (!file) - return 0; - return fwrite(buffer, 1, size, file); + if (!file) + return 0; + return fwrite(buffer, 1, size, file); } /** - * @brief Write a specified number of chars from a buffer to a file - * - * @param file Pointer to the file - * @param buffer Buffer containing the chars to write - * @param size Number of chars to write - * @return size_t Number of bytes actually written + * @brief Write from a char buffer to a file + * @param file FILE* pointer + * @param buffer Buffer to write from + * @param size Number of bytes to write + * @return Number of bytes written */ size_t Storage::write(FILE *file, const char *buffer, size_t size) { - if (!file) - return 0; - return fwrite(buffer, 1, size, file); + if (!file) + return 0; + return fwrite(buffer, 1, size, file); } /** - * @brief Check if a file exists on the SD card - * - * @param path Path to the file - * @return true if the file exists, false otherwise + * @brief Check if a file or directory exists + * @param path File or directory path + * @return true if it exists, false otherwise */ bool Storage::exists(const char *path) { - struct stat st; - return stat(path, &st) == 0; + struct stat st; + return stat(path, &st) == 0; } /** - * @brief Create a directory on the SD card - * - * @param path Path to the directory - * @return true if the directory was created successfully, false otherwise + * @brief Create a directory + * @param path Directory path + * @return true on success, false on failure */ bool Storage::mkdir(const char *path) { - return ::mkdir(path, 0777) == 0; + return ::mkdir(path, 0777) == 0; } /** - * @brief Remove a file from the SD card - * - * @param path Path to the file - * @return true if the file was removed successfully, false otherwise + * @brief Remove a file + * @param path File path + * @return true on success, false on failure */ bool Storage::remove(const char *path) { - return ::remove(path) == 0; + return ::remove(path) == 0; } /** - * @brief Remove a directory from the SD card - * - * @param path Path to the directory - * @return true if the directory was removed successfully, false otherwise + * @brief Remove a directory + * @param path Directory path + * @return true on success, false on failure */ bool Storage::rmdir(const char *path) { - return ::rmdir(path) == 0; + return ::rmdir(path) == 0; } /** - * @brief Seek to a specific position in a file - * - * @param file Pointer to the file - * @param offset Number of bytes to offset from whence - * @param whence Position from where offset is added - * (SEEK_SET, SEEK_CUR, SEEK_END) - * @return int 0 on success, non-zero on error + * @brief Set the file position indicator + * @param file FILE* pointer + * @param offset Offset in bytes + * @param whence Position from where offset is added (SEEK_SET, SEEK_CUR, SEEK_END) + * @return Result of fseek (0 on success, nonzero on error) */ int Storage::seek(FILE *file, long offset, int whence) { - if (!file) - return -1; - return fseek(file, offset, whence); + if (!file) + return -1; + return fseek(file, offset, whence); } /** - * @brief Write a string to a file without a newline - * - * @param file Pointer to the file - * @param str String to write - * @return int Number of characters written, negative on error + * @brief Print a string to a file + * @param file FILE* pointer + * @param str String to print + * @return Number of characters written, or a negative value if an error occurs */ int Storage::print(FILE *file, const char *str) { - if (!file) - return -1; - return fprintf(file, "%s", str); + if (!file) + return -1; + return fprintf(file, "%s", str); } /** - * @brief Write a string to a file with a newline - * - * @param file Pointer to the file - * @param str String to write - * @return int Number of characters written, negative on error + * @brief Print a string with a newline to a file + * @param file FILE* pointer + * @param str String to print + * @return Number of characters written, or a negative value if an error occurs */ int Storage::println(FILE *file, const char *str) { - if (!file) - return -1; - return fprintf(file, "%s\n", str); + if (!file) + return -1; + return fprintf(file, "%s\n", str); } /** - * @brief Get the number of bytes available to read from the file - * - * @param file Pointer to the file - * @return size_t Number of bytes available to read + * @brief Get number of available bytes left in the file + * @param file FILE* pointer + * @return Number of available bytes */ size_t Storage::fileAvailable(FILE *file) { - if (!file) - return 0; - long current_pos = ftell(file); - fseek(file, 0, SEEK_END); - long end_pos = ftell(file); - fseek(file, current_pos, SEEK_SET); - return end_pos - current_pos; + if (!file) + return 0; + long current_pos = ftell(file); + fseek(file, 0, SEEK_END); + long end_pos = ftell(file); + fseek(file, current_pos, SEEK_SET); + return end_pos - current_pos; } diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index 0fee2912..4f2c0f8e 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -2,8 +2,8 @@ * @file storage.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Storage definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -23,117 +23,170 @@ #include #ifdef SPI_SHARED - #include "Arduino.h" - #include "SD.h" + #include "Arduino.h" + #include "SD.h" #endif +/** + * @brief Structure for SD Card Information + * + * @details Contains descriptive and capacity-related information about an SD card, + * including its name, capacity, sector size, read block length, type, and space details. + */ struct SDCardInfo { - std::string name; - std::string capacity; - int sector_size; - int read_block_len; - std::string card_type; - std::string total_space; - std::string free_space; - std::string used_space; + std::string name; /**< Card name */ + std::string capacity; /**< Card capacity as a string */ + int sector_size; /**< Size of a sector in bytes */ + int read_block_len; /**< Read block length in bytes */ + std::string card_type; /**< Card type (e.g., SDHC, SDXC) */ + std::string total_space; /**< Total space as a string */ + std::string free_space; /**< Free space as a string */ + std::string used_space; /**< Used space as a string */ }; +/** + * @class FileStream + * @brief FileStream class to wrap FILE* operations as a Stream + * + * @details Provides a Stream-compatible interface for reading from and flushing a standard C FILE*. + * Write operations are not implemented. + */ class FileStream : public Stream { public: - FileStream(FILE *file) : file(file) {} - - virtual int available() override - { - if (!file) - return 0; - long current_pos = ftell(file); - fseek(file, 0, SEEK_END); - long end_pos = ftell(file); - fseek(file, current_pos, SEEK_SET); - return end_pos - current_pos; - } - - virtual int read() override - { - if (!file) - return -1; - return fgetc(file); - } - - virtual size_t read(uint8_t *buffer, size_t size) - { - if (!file) - return 0; - return fread(buffer, 1, size, file); - } - - virtual size_t readBytes(char *buffer, size_t length) override - { - if (!file) - return 0; - return fread(buffer, 1, length, file); - } - - virtual int peek() override - { - if (!file) - return -1; - int c = fgetc(file); - if (c != EOF) - ungetc(c, file); - return c; - } - - virtual void flush() override - { - if (file) - fflush(file); - } - - size_t write(uint8_t) override - { - // Not implemented - return 0; - } - - size_t write(const uint8_t *, size_t) override - { - // Not implemented - return 0; - } + FileStream(FILE *file) : file(file) {} + + /** + * @brief Returns the number of bytes available to read from the file. + * @return Number of available bytes, or 0 if file is nullptr. + */ + virtual int available() override + { + if (!file) + return 0; + long current_pos = ftell(file); + fseek(file, 0, SEEK_END); + long end_pos = ftell(file); + fseek(file, current_pos, SEEK_SET); + return end_pos - current_pos; + } + + /** + * @brief Reads a single byte from the file. + * @return The byte read, or -1 if file is nullptr or EOF. + */ + virtual int read() override + { + if (!file) + return -1; + return fgetc(file); + } + + /** + * @brief Reads up to size bytes into the buffer. + * @param buffer Buffer to store read bytes + * @param size Maximum number of bytes to read + * @return Number of bytes actually read + */ + virtual size_t read(uint8_t *buffer, size_t size) + { + if (!file) + return 0; + return fread(buffer, 1, size, file); + } + + /** + * @brief Reads up to length bytes into the buffer (char version). + * @param buffer Buffer to store read bytes + * @param length Maximum number of bytes to read + * @return Number of bytes actually read + */ + virtual size_t readBytes(char *buffer, size_t length) override + { + if (!file) + return 0; + return fread(buffer, 1, length, file); + } + + /** + * @brief Peeks at the next byte in the file without advancing the file pointer. + * @return The next byte, or -1 if file is nullptr or EOF. + */ + virtual int peek() override + { + if (!file) + return -1; + int c = fgetc(file); + if (c != EOF) + ungetc(c, file); + return c; + } + + /** + * @brief Flushes the file output buffer. + */ + virtual void flush() override + { + if (file) + fflush(file); + } + + /** + * @brief Not implemented: Write a single byte to the file. + */ + size_t write(uint8_t) override + { + // Not implemented + return 0; + } + + /** + * @brief Not implemented: Write multiple bytes to the file. + */ + size_t write(const uint8_t *, size_t) override + { + // Not implemented + return 0; + } private: - FILE *file; + FILE *file; /**< Pointer to the wrapped C FILE object */ }; +/** + * @class Storage + * @brief Storage class for SD and SPIFFS operations + * + * @details Provides an abstraction for file and directory operations on SD cards and SPIFFS, + * including initialization, basic file I/O, and SD card information retrieval. + */ class Storage { private: - bool isSdLoaded; - sdmmc_card_t *card; + bool isSdLoaded; /**< Indicates if the SD card is loaded */ + sdmmc_card_t *card; /**< Pointer to the SD card descriptor */ public: - Storage(); - - esp_err_t initSD(); - esp_err_t initSPIFFS(); - SDCardInfo getSDCardInfo(); - bool getSdLoaded() const; - FILE *open(const char *path, const char *mode); - int close(FILE *file); - bool exists(const char *path); - bool mkdir(const char *path); - bool remove(const char *path); - bool rmdir(const char *path); - size_t size(const char *path); - size_t read(FILE* file, uint8_t* buffer, size_t size); - size_t read(FILE* file, char* buffer, size_t size); - size_t write(FILE* file, const uint8_t* buffer, size_t size); - size_t write(FILE* file, const char* buffer, size_t size); - int seek(FILE* file, long offset, int whence); - int print(FILE* file, const char* str); - int println(FILE* file, const char* str); - size_t fileAvailable(FILE* file); + Storage(); + + esp_err_t initSD(); + esp_err_t initSPIFFS(); + SDCardInfo getSDCardInfo(); + bool getSdLoaded() const; + FILE *open(const char *path, const char *mode); + int close(FILE *file); + bool exists(const char *path); + bool mkdir(const char *path); + bool remove(const char *path); + bool rmdir(const char *path); + size_t size(const char *path); + size_t read(FILE* file, uint8_t* buffer, size_t size); + size_t read(FILE* file, char* buffer, size_t size); + size_t write(FILE* file, const uint8_t* buffer, size_t size); + size_t write(FILE* file, const char* buffer, size_t size); + int seek(FILE* file, long offset, int whence); + int print(FILE* file, const char* str); + int println(FILE* file, const char* str); + size_t fileAvailable(FILE* file); }; \ No newline at end of file diff --git a/lib/tasks/tasks.cpp b/lib/tasks/tasks.cpp index a696bb9c..b9944f0a 100644 --- a/lib/tasks/tasks.cpp +++ b/lib/tasks/tasks.cpp @@ -1,85 +1,103 @@ /** - * @file tasks.hpp + * @file tasks.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) - * @brief Core Tasks functions - * @version 0.2.2 - * @date 2025-05 + * @brief Core Tasks implementation for GPS and CLI management + * @version 0.2.3 + * @date 2025-06 + * @details This file contains the implementation of FreeRTOS tasks for GPS data processing + * and CLI interface management. It handles thread-safe GPS data reading and + * command-line interface operations with proper mutex protection. */ #include "tasks.hpp" -TaskHandle_t LVGLTaskHandler; -xSemaphoreHandle gpsMutex; -extern Gps gps; +TaskHandle_t LVGLTaskHandler; /**< Handle for the LVGL task */ +xSemaphoreHandle gpsMutex; /**< Mutex for GPS resource protection */ +extern Gps gps; /**< Global GPS instance for data processing */ -static const char* TAG PROGMEM = "Task"; +static const char* TAG PROGMEM = "Task"; /**< Logging tag for task operations */ /** - * @brief Read GPS data + * @brief GPS data processing task * - * @param pvParameters + * @details Continuously reads GPS data from the serial port, processes NMEA sentences, + * and updates the global GPS fix structure. Handles optional NMEA output to + * serial console and ensures thread-safe access using gpsMutex. The task runs + * on core 0 with high priority to ensure real-time GPS data processing. + * + * @param pvParameters Task parameters (unused in current implementation) */ void gpsTask(void *pvParameters) { - ESP_LOGV(TAG, "GPS Task - running on core %d", xPortGetCoreID()); - ESP_LOGV(TAG, "Stack size: %d", uxTaskGetStackHighWaterMark(NULL)); - while (1) - { - if ( xSemaphoreTake(gpsMutex, portMAX_DELAY) == pdTRUE ) - { - if (nmea_output_enable) - { - while (gpsPort.available()) - { - char c = gpsPort.read(); - Serial.print(c); - } - } + ESP_LOGV(TAG, "GPS Task - running on core %d", xPortGetCoreID()); + ESP_LOGV(TAG, "Stack size: %d", uxTaskGetStackHighWaterMark(NULL)); + while (1) + { + if ( xSemaphoreTake(gpsMutex, portMAX_DELAY) == pdTRUE ) + { + if (nmea_output_enable) + { + while (gpsPort.available()) + { + char c = gpsPort.read(); + Serial.print(c); + } + } - while (GPS.available( gpsPort )) - { - fix = GPS.read(); - gps.getGPSData(); - } + while (GPS.available( gpsPort )) + { + fix = GPS.read(); + gps.getGPSData(); + } - xSemaphoreGive(gpsMutex); + xSemaphoreGive(gpsMutex); - vTaskDelay(1); /// portTICK_PERIOD_MS); - } - } + vTaskDelay(1); /// portTICK_PERIOD_MS); + } + } } /** - * @brief Init GPS task + * @brief Initialize GPS processing task * + * @details Creates and starts the GPS task on core 0 with 8KB stack size and priority 1. + * Includes a 500ms delay after task creation to ensure proper initialization + * before other system components attempt to access GPS data. */ void initGpsTask() { - xTaskCreatePinnedToCore(gpsTask, PSTR("GPS Task"), 8192, NULL, 1, NULL, 0); - delay(500); + xTaskCreatePinnedToCore(gpsTask, PSTR("GPS Task"), 8192, NULL, 1, NULL, 0); + delay(500); } /** - * @brief CLI task + * @brief Command-line interface processing task + * + * @details Handles CLI operations including command parsing, execution, and response + * generation. Runs on core 1 with 20KB stack size to handle complex CLI + * operations and network communications. The task processes commands at + * 60ms intervals to maintain responsive user interaction. * - * @param param + * @param param Task parameters (unused in current implementation) */ #ifndef DISABLE_CLI void cliTask(void *param) { - ESP_LOGV(TAG, "CLI Task - running on core %d", xPortGetCoreID()); - ESP_LOGV(TAG, "Stack size: %d", uxTaskGetStackHighWaterMark(NULL)); - while(1) - { - wcli.loop(); - vTaskDelay(60 / portTICK_PERIOD_MS); - } - vTaskDelete(NULL); + ESP_LOGV(TAG, "CLI Task - running on core %d", xPortGetCoreID()); + ESP_LOGV(TAG, "Stack size: %d", uxTaskGetStackHighWaterMark(NULL)); + while(1) + { + wcli.loop(); + vTaskDelay(60 / portTICK_PERIOD_MS); + } + vTaskDelete(NULL); } /** - * @brief Init CLI task + * @brief Initialize CLI processing task * + * @details Creates and starts the CLI task on core 1 with 20KB stack size and priority 1. + * Only compiled when CLI functionality is enabled (not DISABLE_CLI). */ void initCLITask() { xTaskCreatePinnedToCore(cliTask, "cliTask ", 20000, NULL, 1, NULL, 1); } diff --git a/lib/tasks/tasks.hpp b/lib/tasks/tasks.hpp index 2bed1c48..ff24499f 100644 --- a/lib/tasks/tasks.hpp +++ b/lib/tasks/tasks.hpp @@ -1,9 +1,12 @@ /** * @file tasks.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) - * @brief Core Tasks functions - * @version 0.2.2 - * @date 2025-05 + * @brief Core Tasks header definitions for GPS and CLI management + * @version 0.2.3 + * @date 2025-06 + * @details This header defines the interface for FreeRTOS tasks used for GPS data processing + * and CLI interface management. It provides function declarations and configuration + * constants for task management. */ #pragma once @@ -14,16 +17,31 @@ #include "compass.hpp" #include "lvgl.h" #include "cli.hpp" -//#include "mainScr.hpp" #include "globalGpxDef.h" #include "lvglFuncs.hpp" -#define TASK_SLEEP_PERIOD_MS 5 +#define TASK_SLEEP_PERIOD_MS 5 /**< Sleep period for tasks in milliseconds */ +/** + * @brief GPS data processing task function + * @param pvParameters Task parameters (unused) + */ void gpsTask(void *pvParameters); + +/** + * @brief Initialize GPS processing task + */ void initGpsTask(); #ifndef DISABLE_CLI - void cliTask(void *param); - void initCLITask(); + /** + * @brief CLI processing task function + * @param param Task parameters (unused) + */ + void cliTask(void *param); + + /** + * @brief Initialize CLI processing task + */ + void initCLITask(); #endif diff --git a/lib/tft/tft.cpp b/lib/tft/tft.cpp index c1112b29..f7353d5d 100644 --- a/lib/tft/tft.cpp +++ b/lib/tft/tft.cpp @@ -2,8 +2,8 @@ * @file tft.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief TFT definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "tft.hpp" @@ -18,106 +18,113 @@ extern Storage storage; /** * @brief Turn on TFT Sleep Mode for ILI9488 * + * @details Wakes up the TFT display and sets the display brightness to the specified value. + * + * @param brightness Value to set the TFT backlight brightness */ void tftOn(uint8_t brightness) { - tft.writecommand(0x11); - delay(120); - tft.setBrightness(brightness); + tft.writecommand(0x11); + delay(120); + tft.setBrightness(brightness); } /** * @brief Turn off TFT Wake up Mode for ILI9488 * + * @details Puts the TFT display into sleep mode by setting brightness to 0 */ void tftOff() { - tft.setBrightness(0); - tft.writecommand(0x10); + tft.setBrightness(0); + tft.writecommand(0x10); } /** * @brief Touch calibrate * + * @details Calibrates the touch screen. If calibration data exists and recalibration is not requested, uses the saved data. + * Otherwise, performs on-screen calibration and saves the result. */ void touchCalibrate() { - uint16_t calData[8]; - uint8_t calDataOK = 0; + uint16_t calData[8]; + uint8_t calDataOK = 0; - FILE* f = storage.open(calibrationFile, "r"); + FILE* f = storage.open(calibrationFile, "r"); - if (f != NULL) - { - if (repeatCalib) - remove(calibrationFile); - else - { - if (fread((char *)calData, sizeof(char), 16, f)) - { - calDataOK = 1; - storage.close(f); - } - } - } - else - log_e("Touch calibration doesn't exists"); - - if (calDataOK && !repeatCalib) - tft.setTouchCalibrate(calData); - else - { - static const lgfx::v1::GFXfont* fontSmall; - static const lgfx::v1::GFXfont* fontLarge; - - #ifdef LARGE_SCREEN - fontSmall = &fonts::DejaVu18; - fontLarge = &fonts::DejaVu40; - #else - fontSmall = &fonts::DejaVu12; - fontLarge = &fonts::DejaVu24; - #endif - - tft.drawCenterString("TOUCH THE ARROW MARKER.", tft.width() >> 1, tft.height() >> 1, fontSmall); - tft.calibrateTouch(calData, TFT_WHITE, TFT_BLACK, std::max(tft.width(), tft.height()) >> 3); - tft.drawCenterString("DONE!", tft.width() >> 1, (tft.height() >> 1) + (tft.fontHeight(fontSmall) * 2), fontLarge); - delay(500); - tft.drawCenterString("TOUCH TO CONTINUE.", tft.width() >> 1, (tft.height() >> 1) + (tft.fontHeight(fontLarge) * 2), fontSmall); - - FILE* f = storage.open(calibrationFile, "w"); - if (f) + if (f != NULL) { - log_v("Calibration saved"); - fwrite((const unsigned char *)calData, sizeof(unsigned char), 16 ,f); - storage.close(f); + if (repeatCalib) + remove(calibrationFile); + else + { + if (fread((char *)calData, sizeof(char), 16, f)) + { + calDataOK = 1; + storage.close(f); + } + } } else - log_e("Calibration not saved!"); + log_e("Touch calibration doesn't exists"); - uint16_t touchX, touchY; - while (!tft.getTouch(&touchX, &touchY)); - } + if (calDataOK && !repeatCalib) + tft.setTouchCalibrate(calData); + else + { + static const lgfx::v1::GFXfont* fontSmall; + static const lgfx::v1::GFXfont* fontLarge; + + #ifdef LARGE_SCREEN + fontSmall = &fonts::DejaVu18; + fontLarge = &fonts::DejaVu40; + #else + fontSmall = &fonts::DejaVu12; + fontLarge = &fonts::DejaVu24; + #endif + + tft.drawCenterString("TOUCH THE ARROW MARKER.", tft.width() >> 1, tft.height() >> 1, fontSmall); + tft.calibrateTouch(calData, TFT_WHITE, TFT_BLACK, std::max(tft.width(), tft.height()) >> 3); + tft.drawCenterString("DONE!", tft.width() >> 1, (tft.height() >> 1) + (tft.fontHeight(fontSmall) * 2), fontLarge); + delay(500); + tft.drawCenterString("TOUCH TO CONTINUE.", tft.width() >> 1, (tft.height() >> 1) + (tft.fontHeight(fontLarge) * 2), fontSmall); + + FILE* f = storage.open(calibrationFile, "w"); + if (f) + { + log_v("Calibration saved"); + fwrite((const unsigned char *)calData, sizeof(unsigned char), 16 ,f); + storage.close(f); + } + else + log_e("Calibration not saved!"); + + uint16_t touchX, touchY; + while (!tft.getTouch(&touchX, &touchY)); + } } /** * @brief Init TFT display * + * @details Initializes the TFT display, */ void initTFT() { - tft.init(); - - #ifdef TDECK_ESP32S3 - tft.setRotation(1); - #endif + tft.init(); + + #ifdef TDECK_ESP32S3 + tft.setRotation(1); + #endif - TFT_HEIGHT = tft.height(); - TFT_WIDTH = tft.width(); + TFT_HEIGHT = tft.height(); + TFT_WIDTH = tft.width(); - tft.initDMA(); - tft.fillScreen(TFT_BLACK); + tft.initDMA(); + tft.fillScreen(TFT_BLACK); -#ifdef TOUCH_INPUT - touchCalibrate(); -#endif + #ifdef TOUCH_INPUT + touchCalibrate(); + #endif } diff --git a/lib/tft/tft.hpp b/lib/tft/tft.hpp index 7e463541..93e86829 100644 --- a/lib/tft/tft.hpp +++ b/lib/tft/tft.hpp @@ -2,8 +2,8 @@ * @file tft.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief TFT definition and functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -14,13 +14,13 @@ #include -extern TFT_eSPI tft; -static const char* calibrationFile PROGMEM = "/spiffs/TouchCal"; -extern bool repeatCalib; +extern TFT_eSPI tft; /**< TFT display object */ +static const char* calibrationFile PROGMEM = "/spiffs/TouchCal"; /**< Touch calibration file path */ +extern bool repeatCalib; /**< Flag to repeat touch calibration */ -extern uint16_t TFT_WIDTH; -extern uint16_t TFT_HEIGHT; -extern bool waitScreenRefresh; // Wait for refresh screen (screenshot issues) +extern uint16_t TFT_WIDTH; /**< TFT display width in pixels */ +extern uint16_t TFT_HEIGHT; /**< TFT display height in pixels */ +extern bool waitScreenRefresh; /**< Wait for refresh screen */ void tftOn(uint8_t brightness); void tftOff(); diff --git a/lib/upgrade/src/firmUpgrade.cpp b/lib/upgrade/src/firmUpgrade.cpp index 5a78d283..91de9339 100644 --- a/lib/upgrade/src/firmUpgrade.cpp +++ b/lib/upgrade/src/firmUpgrade.cpp @@ -2,8 +2,8 @@ * @file firmUpgrade.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Firmware upgrade from SD functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "firmUpgrade.hpp" @@ -13,94 +13,103 @@ static const char* TAG PROGMEM = "Firmware Update"; /** - * @brief Check if firmware file exist + * @brief Check if firmware file exists * - * @return true/false + * @details Checks if the firmware upgrade file is present in storage. + * + * @return true if the upgrade file exists, false otherwise */ bool checkFileUpgrade() { - if (storage.exists(upgrdFile)) - return true; - else - return false; + if (storage.exists(upgrdFile)) + return true; + else + return false; } /** * @brief Firmware upgrade start callback * + * @details Initiates the firmware upgrade by opening the firmware file, starting the update process, + * and handling upgrade progress and result. */ void onUpgrdStart() { - ESP_LOGV(TAG, "Try to upgrade firmware..."); - FILE *firmware = storage.open(upgrdFile, "r"); - if (firmware) - { - Update.onProgress(onUpgrdProcess); - Update.begin(storage.size(upgrdFile), U_FLASH); - FileStream firmwareStream(firmware); - Update.writeStream(firmwareStream); - if (Update.end()) - ESP_LOGV(TAG, "Upgrade finished!"); - else - { - ESP_LOGE(TAG, "Upgrade error!"); - lv_label_set_text_static(msgUprgdText, LV_SYMBOL_WARNING " Upgrade error!"); - lv_obj_clear_flag(btnMsgBack,LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(contMeter,LV_OBJ_FLAG_HIDDEN); - } - storage.close(firmware); - } - else - ESP_LOGE(TAG, "Upgrade firmware error , file open error"); + ESP_LOGV(TAG, "Try to upgrade firmware..."); + FILE *firmware = storage.open(upgrdFile, "r"); + if (firmware) + { + Update.onProgress(onUpgrdProcess); + Update.begin(storage.size(upgrdFile), U_FLASH); + FileStream firmwareStream(firmware); + Update.writeStream(firmwareStream); + if (Update.end()) + ESP_LOGV(TAG, "Upgrade finished!"); + else + { + ESP_LOGE(TAG, "Upgrade error!"); + lv_label_set_text_static(msgUprgdText, LV_SYMBOL_WARNING " Upgrade error!"); + lv_obj_clear_flag(btnMsgBack,LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(contMeter,LV_OBJ_FLAG_HIDDEN); + } + storage.close(firmware); + } + else + ESP_LOGE(TAG, "Upgrade firmware error , file open error"); } /** * @brief Draw progress bar * - * @param x -> X Position - * @param y -> Y Position - * @param w -> Bar width - * @param h -> Bar height - * @param percent -> Percent to show - * @param frameColor -> Bar frame color - * @param barColor -> Bar color + * @details Draws a rounded progress bar at the specified position, with given width, height, percentage, and colors. + * + * @param x X position of the progress bar + * @param y Y position of the progress bar + * @param w Bar width + * @param h Bar height + * @param percent Percentage to show (0-100) + * @param frameColor Frame color of the bar + * @param barColor Fill color of the bar */ void drawProgressBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percent, uint16_t frameColor, uint16_t barColor) { - if (percent == 0) - tft.fillRoundRect(x, y, w, h, 3, TFT_BLACK); - uint8_t margin = 2; - uint16_t barHeight = h - 2 * margin; - uint16_t barWidth = w - 2 * margin; - tft.drawRoundRect(x, y, w, h, 3, frameColor); - tft.fillRect(x + margin, y + margin, barWidth * percent / 100.0, barHeight, barColor); + if (percent == 0) + tft.fillRoundRect(x, y, w, h, 3, TFT_BLACK); + uint8_t margin = 2; + uint16_t barHeight = h - 2 * margin; + uint16_t barWidth = w - 2 * margin; + tft.drawRoundRect(x, y, w, h, 3, frameColor); + tft.fillRect(x + margin, y + margin, barWidth * percent / 100.0, barHeight, barColor); } /** * @brief Firmware upgrade process callback * - * @param currSize -> Current size from file - * @param totalSize -> Total size from file + * @details Handles the progress display during firmware upgrade by updating the progress bar and showing percentage. + * + * @param currSize Current written size from file + * @param totalSize Total size of the firmware file */ void onUpgrdProcess(size_t currSize, size_t totalSize) { - float progress = (currSize * 100) / totalSize; - ESP_LOGV(TAG, "Firmware Upgrade process %d ...", (int)progress); - char strProgress[30]; - sprintf(strProgress,"Upgrading... %d%%",(int)progress); - tft.drawCenterString(strProgress, tft.width() >> 1, (tft.height() >> 1)+25, &fonts::FreeSans9pt7b); - drawProgressBar(40,tft.height() >> 1,TFT_WIDTH - 80,20,(int)progress,TFT_WHITE,TFT_BLUE); - if ((int)progress == 100) - tft.drawCenterString("Upgrade complete", tft.width() >> 1, (tft.height() >> 1)+25, &fonts::FreeSans9pt7b); + float progress = (currSize * 100) / totalSize; + ESP_LOGV(TAG, "Firmware Upgrade process %d ...", (int)progress); + char strProgress[30]; + sprintf(strProgress,"Upgrading... %d%%",(int)progress); + tft.drawCenterString(strProgress, tft.width() >> 1, (tft.height() >> 1)+25, &fonts::FreeSans9pt7b); + drawProgressBar(40,tft.height() >> 1,TFT_WIDTH - 80,20,(int)progress,TFT_WHITE,TFT_BLUE); + if ((int)progress == 100) + tft.drawCenterString("Upgrade complete", tft.width() >> 1, (tft.height() >> 1)+25, &fonts::FreeSans9pt7b); } /** * @brief Firmware upgrade end callback * + * @details Called when the firmware upgrade process finishes. */ void onUpgrdEnd() { - delay(500); - ESP_LOGI(TAG, "Rebooting ESP32: "); - ESP.restart(); + delay(500); + ESP_LOGI(TAG, "Rebooting ESP32: "); + ESP.restart(); } \ No newline at end of file diff --git a/lib/upgrade/src/firmUpgrade.hpp b/lib/upgrade/src/firmUpgrade.hpp index 1cd56424..0bbeda9c 100644 --- a/lib/upgrade/src/firmUpgrade.hpp +++ b/lib/upgrade/src/firmUpgrade.hpp @@ -2,8 +2,8 @@ * @file firmUpgrade.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Firmware upgrade from SD functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -14,7 +14,7 @@ #include "upgradeScr.hpp" #include "storage.hpp" -static const char *upgrdFile PROGMEM = "/sdcard/firmware.bin"; // Firmware upgrade file +static const char *upgrdFile PROGMEM = "/sdcard/firmware.bin"; /**< Firmware upgrade file path */ bool checkFileUpgrade(); void onUpgrdStart(); diff --git a/lib/utils/src/gestures.cpp b/lib/utils/src/gestures.cpp index b9cf0d79..6cc1a83d 100644 --- a/lib/utils/src/gestures.cpp +++ b/lib/utils/src/gestures.cpp @@ -2,47 +2,51 @@ * @file gestures.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Touch gestures functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "gestures.hpp" /** - * @brief Detects movement for pinch-zoom with automatic dynamic threshold accordint to speed + * @brief Detects movement for pinch-zoom with automatic dynamic threshold according to speed + * + * @details Calculates the distance between two touch points in the previous and current states, + * dynamically adjusts the zoom thresholds based on gesture speed, and determines + * if a pinch-zoom (in or out) gesture has occurred. * * @param prev Array of two previous touch points (lgfx::touch_point_t). - * @param curr Array de two actual touch points (lgfx::touch_point_t). - * @param dt_ms time (in ms) between previous and actual touch points. - * @return zoom_dir: ZOOM_NONE, ZOOM_IN, o ZOOM_OUT. + * @param curr Array of two current touch points (lgfx::touch_point_t). + * @param dt_ms Time in milliseconds between previous and current touch points. + * @return zoom_dir: ZOOM_NONE, ZOOM_IN, or ZOOM_OUT. */ zoom_dir pinchZoom(const lgfx::touch_point_t prev[TOUCH_MAX_POINTS],const lgfx::touch_point_t curr[TOUCH_MAX_POINTS],float dt_ms) { - float distPrev = sqrtf((prev[0].x - prev[1].x) * (prev[0].x - prev[1].x) + - (prev[0].y - prev[1].y) * (prev[0].y - prev[1].y)); - float distCurr = sqrtf((curr[0].x - curr[1].x) * (curr[0].x - curr[1].x) + - (curr[0].y - curr[1].y) * (curr[0].y - curr[1].y)); - float delta = distCurr - distPrev; + float distPrev = hypotf(prev[0].x - prev[1].x, prev[0].y - prev[1].y); + float distCurr = hypotf(curr[0].x - curr[1].x, curr[0].y - curr[1].y); + float delta = distCurr - distPrev; + + float speed = (dt_ms > 0.0f) ? fabsf(delta) / dt_ms : 0.0f; - float speed = (dt_ms > 0) ? fabsf(delta) / dt_ms : 0.0f; - float thresholdIn = 0.03f * sqrtf(tft.width() * tft.width() + tft.height() * tft.height()); - float thresholdOut = 0.012f * sqrtf(tft.width() * tft.width() + tft.height() * tft.height()); + float diag = hypotf(tft.width(), tft.height()); // se calcula solo una vez + float thresholdIn = 0.03f * diag; + float thresholdOut = 0.012f * diag; - if (speed > SPEED_FAST) - { - thresholdIn *= 1.2f; - thresholdOut *= 0.7f; - } - else if (speed < SPEED_SLOW) - { - thresholdIn *= 0.7f; - thresholdOut *= 0.5f; - } + if (speed > SPEED_FAST) + { + thresholdIn *= 1.2f; + thresholdOut *= 0.7f; + } + else if (speed < SPEED_SLOW) + { + thresholdIn *= 0.7f; + thresholdOut *= 0.5f; + } - if (delta > thresholdOut) - return ZOOM_IN; - else if (delta < -thresholdIn) - return ZOOM_OUT; + if (delta > thresholdOut) + return ZOOM_IN; + else if (delta < -thresholdIn) + return ZOOM_OUT; - return ZOOM_NONE; + return ZOOM_NONE; } \ No newline at end of file diff --git a/lib/utils/src/gestures.hpp b/lib/utils/src/gestures.hpp index effc51a6..0b9429d1 100644 --- a/lib/utils/src/gestures.hpp +++ b/lib/utils/src/gestures.hpp @@ -2,8 +2,8 @@ * @file gestures.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Touch gestures functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,20 +11,24 @@ #include #include "tft.hpp" -#define TOUCH_MAX_POINTS 2 -#define TOUCH_DOUBLE_TOUCH_INTERVAL 150 -#define SPEED_FAST 1.0f // px/ms fast speed -#define SPEED_SLOW 0.3f // px/ms slow speed +#define TOUCH_MAX_POINTS 2 /**< Maximum number of supported touch points */ +#define TOUCH_DOUBLE_TOUCH_INTERVAL 150 /**< Interval (ms) to detect double touch */ +#define SPEED_FAST 1.0f /**< Fast gesture speed threshold (px/ms) */ +#define SPEED_SLOW 0.3f /**< Slow gesture speed threshold (px/ms) */ -static bool countTouchReleases = false; -static int numberTouchReleases = 0; -static uint32_t firstTouchReleaseTime = 0; +static bool countTouchReleases = false; /**< Indicates if touch release events are being counted */ +static int numberTouchReleases = 0; /**< Number of detected touch releases */ +static uint32_t firstTouchReleaseTime = 0; /**< Timestamp of the first touch release event */ +/** + * @brief Zoom gesture direction enumeration + * + */ typedef enum { - ZOOM_NONE = 0, - ZOOM_IN, - ZOOM_OUT + ZOOM_NONE = 0, /**< No zoom gesture detected */ + ZOOM_IN, /**< Pinch out gesture detected (zoom in) */ + ZOOM_OUT /**< Pinch in gesture detected (zoom out) */ } zoom_dir; zoom_dir pinchZoom(const lgfx::touch_point_t prev[TOUCH_MAX_POINTS],const lgfx::touch_point_t curr[TOUCH_MAX_POINTS],float dt_ms); \ No newline at end of file diff --git a/lib/utils/src/gpsMath.cpp b/lib/utils/src/gpsMath.cpp index 942c86c1..dc99505b 100644 --- a/lib/utils/src/gpsMath.cpp +++ b/lib/utils/src/gpsMath.cpp @@ -2,150 +2,293 @@ * @file gpsMath.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Math and various functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "gpsMath.hpp" -double midLat = 0; // Mid point between 2 Latitudes -double midLon = 0; // Mid point between 2 Longitudes +float midLat = 0; +float midLon = 0; +bool lutInit = false; /** - * @brief Function to calculate the distance in meters given 2 coordinates (latitude and longitude) haversine formula + * @brief Initialize lookup tables for sine and cosine * - * @param lat1 -> Latitude 1 - * @param lon1 -> Longitude 1 - * @param lat2 -> Latitude 2 - * @param lon2 -> Longitude 2 - * @return double -> Distance in meters + * @details Allocates memory for the tables in PSRAM if available, otherwise in RAM. + * Must be called once before using LUT-based trig functions. + * @return true if initialization was successful, false if memory allocation failed */ -double calcDist(double lat1, double lon1, double lat2, double lon2) +bool initTrigLUT() { - lat1 = lat1 * (M_PI / 180.0); - lon1 = lon1 * (M_PI / 180.0); - lat2 = lat2 * (M_PI / 180.0); - lon2 = lon2 * (M_PI / 180.0); - double dlat = lat2 - lat1; - double dlon = lon2 - lon1; - double a = sin(dlat / 2) * sin(dlat / 2) + - cos(lat1) * cos(lat2) * - sin(dlon / 2) * sin(dlon / 2); - double c = 2 * atan2(sqrt(a), sqrt(1 - a)); - return EARTH_RADIUS * c; +#ifdef BOARD_HAS_PSRAM + sinLut = (float*) heap_caps_malloc(sizeof(float) * LUT_SIZE, MALLOC_CAP_SPIRAM); + cosLut = (float*) heap_caps_malloc(sizeof(float) * LUT_SIZE, MALLOC_CAP_SPIRAM); + + if (!sinLut || !cosLut) + { + ESP_LOGE(TAGMATH, "Error: Failed to allocate memory for float LUTs"); + if (sinLut) free(sinLut); + if (cosLut) free(cosLut); + sinLut = cosLut = NULL; + return false; + } + ESP_LOGI(TAGMATH, "Allocated memory for float LUTs"); + + for (int i = 0; i < LUT_SIZE; ++i) + { + float angle = i * LUT_RES; + sinLut[i] = sinf(angle); + cosLut[i] = cosf(angle); + } + return true; +#else + return false; +#endif } /** - * @brief Function to calculate the heading given 2 coordinates (latitude and longitude) Orthodromic Course + * @brief Function to calculate the distance in meters between two coordinates using the Haversine formula + * + * @details Computes the great-circle distance between two points on the Earth's surface + * given their latitude and longitude values, using the Haversine formula. + * Uses LUTs for trig functions if initialized. * - * @param lat1 -> Latitude 1 - * @param lon1 -> Longitude 1 - * @param lat2 -> Latitude 2 - * @param lon2 -> Longitude 2 - * @return double -> heading + * @param lat1 Latitude of point 1 (in degrees) + * @param lon1 Longitude of point 1 (in degrees) + * @param lat2 Latitude of point 2 (in degrees) + * @param lon2 Longitude of point 2 (in degrees) + * @return Distance in meters between the two points */ -double calcCourse(double lat1, double lon1, double lat2, double lon2) +float calcDist(float lat1, float lon1, float lat2, float lon2) { - lat1 = lat1 * M_PI / 180.0; - lat2 = lat2 * M_PI / 180.0; - double dLon = (lon2 - lon1) * M_PI / 180.0; + lat1 = DEG2RAD(lat1); + lon1 = DEG2RAD(lon1); + lat2 = DEG2RAD(lat2); + lon2 = DEG2RAD(lon2); + float dlat = lat2 - lat1; + float dlon = lon2 - lon1; - double y = sin(dLon) * cos(lat2); - double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon); - double course = atan2(y, x); + float a, c; - course = course * 180.0 / M_PI; - course = fmod((course + 360.0), 360.0); + if (lutInit) + { + a = sinLUT(dlat * 0.5f) * sinLUT(dlat * 0.5f) + + cosLUT(lat1) * cosLUT(lat2) * + sinLUT(dlon * 0.5f) * sinLUT(dlon * 0.5f); + } + else + { + a = sinf(dlat * 0.5f) * sinf(dlat * 0.5f) + + cosf(lat1) * cosf(lat2) * + sinf(dlon * 0.5f) * sinf(dlon * 0.5f); + } - return course; + c = 2.0f * atan2f(sqrtf(a), sqrtf(1.0f - a)); + + return EARTH_RADIUS * c; } /** - * @brief Function to calculate the midpoint given 2 coordinates (latitude and longitude) + * @brief Function to calculate the heading (bearing) between two coordinates (latitude and longitude) using the Orthodromic (great-circle) course + * + * @details Calculates the initial bearing (forward azimuth) from the first point (lat1, lon1) + * to the second point (lat2, lon2) on the surface of a sphere (Earth). + * Uses LUTs for trig functions if initialized. * - * @param lat1 -> Latitude 1 - * @param lon1 -> Longitude 1 - * @param lat2 -> Latitude 2 - * @param lon2 -> Longitude 2 + * @param lat1 Latitude of point 1 (in degrees) + * @param lon1 Longitude of point 1 (in degrees) + * @param lat2 Latitude of point 2 (in degrees) + * @param lon2 Longitude of point 2 (in degrees) + * @return Initial heading (degrees from North, 0-360) + */ +float calcCourse(float lat1, float lon1, float lat2, float lon2) +{ + lat1 = DEG2RAD(lat1); + lat2 = DEG2RAD(lat2); + float dLon = DEG2RAD(lon2 - lon1); + + float sin_dLon, cos_dLon; + float sin_lat1, cos_lat1, sin_lat2, cos_lat2; + + if (lutInit) + { + sin_dLon = sinLUT(dLon); + cos_dLon = cosLUT(dLon); + sin_lat1 = sinLUT(lat1); + cos_lat1 = cosLUT(lat1); + sin_lat2 = sinLUT(lat2); + cos_lat2 = cosLUT(lat2); + } + else + { + sin_dLon = sinf(dLon); + cos_dLon = cosf(dLon); + sin_lat1 = sinf(lat1); + cos_lat1 = cosf(lat1); + sin_lat2 = sinf(lat2); + cos_lat2 = cosf(lat2); + } + + float y = sin_dLon * cos_lat2; + float x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dLon; + float course = atan2f(y, x) * (180.0f / M_PI); + + if (course < 0.0f) + course += 360.0f; + + return course; +} + +/** + * @brief Calculates the minimum signed angular difference between two angles. + * + * This function computes the smallest difference between two angles (in degrees), + * returning a value in the range [-180, 180]. Positive results indicate a + * counterclockwise difference, and negative results indicate a clockwise difference. + * + * @param a Angle 1 (in degrees) + * @param b Angle 2 (in degrees) + * @return Angular difference in degrees, normalized to [-180, 180] + */ +float calcAngleDiff(float a, float b) +{ + float diff = a - b; + while (diff > 180.0f) diff -= 360.0f; + while (diff < -180.0f) diff += 360.0f; + return diff; +} + +/** + * @brief Function to calculate the midpoint between two coordinates (latitude and longitude) + * + * @details Calculates the geographic midpoint (center point) between two coordinates on the Earth's surface. + * The result is stored in the global variables midLat and midLon. + * Uses LUTs for trig functions if initialized. + * + * @param lat1 Latitude of point 1 (in degrees) + * @param lon1 Longitude of point 1 (in degrees) + * @param lat2 Latitude of point 2 (in degrees) + * @param lon2 Longitude of point 2 (in degrees) */ void calcMidPoint(float lat1, float lon1, float lat2, float lon2) { + float rLat1 = DEG2RAD(lat1); + float rLon1 = DEG2RAD(lon1); + float rLat2 = DEG2RAD(lat2); + float rLon2 = DEG2RAD(lon2); + float dLon = rLon2 - rLon1; - float dLon = (radians(lon2) - radians(lon1)); - float cosLat1 = cos(radians(lat1)); - float cosLat2 = cos(radians(lat2)); - float sinLat1 = sin(radians(lat1)); - float sinLat2 = sin(radians(lat2)); - float Bx = cosLat2 * cos(dLon); - float By = cosLat2 * sin(dLon); + float sinLat1, sinLat2, cosLat1, cosLat2, cosDLon, sinDLon; - midLat = degrees(atan2(sinLat1 + sinLat2, sqrt((cosLat1 + Bx) * (cosLat1 + Bx) + By * By))); - midLon = degrees(radians(lon1) + atan2(By, cosLat1 + Bx)); + if (lutInit) + { + sinLat1 = sinLUT(rLat1); + sinLat2 = sinLUT(rLat2); + cosLat1 = cosLUT(rLat1); + cosLat2 = cosLUT(rLat2); + cosDLon = cosLUT(dLon); + sinDLon = sinLUT(dLon); + } + else + { + sinLat1 = sinf(rLat1); + sinLat2 = sinf(rLat2); + cosLat1 = cosf(rLat1); + cosLat2 = cosf(rLat2); + cosDLon = cosf(dLon); + sinDLon = sinf(dLon); + } + + float Bx = cosLat2 * cosDLon; + float By = cosLat2 * sinDLon; + float cosLat1_plus_Bx = cosLat1 + Bx; + + float midLatRad = atan2f(sinLat1 + sinLat2, sqrtf(cosLat1_plus_Bx * cosLat1_plus_Bx + By * By)); + float midLonRad = rLon1 + atan2f(By, cosLat1_plus_Bx); + + midLat = RAD2DEG(midLatRad); + midLon = RAD2DEG(midLonRad); } /** - * @brief float MAP + * @brief Map a float value from one range to another + * * - * @param x -> input value - * @param inMin -> min input value - * @param inMax -> max input value - * @param outMin -> min output value - * @param outMax -> max output value - * @return float -> output value + * @param x Input value + * @param inMin Minimum input value + * @param inMax Maximum input value + * @param outMin Minimum output value + * @param outMax Maximum output value + * @return float Mapped output value */ float mapFloat(float x, float inMin, float inMax, float outMin, float outMax) { - return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; + return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } /** - * @brief Latitude GGΒΊMM'SS" to string conversion + * @brief Format latitude in degreesΒ°minutes'seconds"N/S string. * - * @param lat -> Latitude - * @return char* -> String + * @details Converts a float latitude into a formatted string using degrees, minutes, + * and seconds notation (e.g., 41Β°23'45.12"N or S). + * Uses a static buffer and assumes `degreeFormat` is a valid format string. + * + * @param lat Latitude in decimal degrees. + * @return Pointer to static buffer containing formatted string. */ -char *latFormatString(double lat) +char *latFormatString(float lat) { - char N_S = PSTR('N'); - double absLatitude = lat; - uint16_t deg; - uint8_t min; - static char s_buf[64]; - if (lat < 0) - { - N_S = PSTR('S'); - absLatitude = fabs(lat); - } - deg = (uint16_t)absLatitude; - absLatitude = (absLatitude - deg) * 60; - min = (uint8_t)absLatitude; - absLatitude = (absLatitude - min) * 60; - sprintf(s_buf, degreeFormat, deg, min, absLatitude, N_S); - return s_buf; + char N_S = 'N'; + float absLatitude = lat; + uint16_t deg; + uint8_t min; + static char s_buf[64]; + + if (lat < 0.0f) + { + N_S = 'S'; + absLatitude = fabsf(lat); + } + + deg = static_cast(absLatitude); + absLatitude = (absLatitude - deg) * 60.0f; + min = static_cast(absLatitude); + absLatitude = (absLatitude - min) * 60.0f; + + sprintf(s_buf, degreeFormat, deg, min, absLatitude, N_S); + return s_buf; } /** - * @brief Longitude GGΒΊMM'SS" to string conversion + * @brief Format longitude in degreesΒ°minutes'seconds"E/W string. + * + * @details Converts a float longitude into a formatted string using degrees, minutes, + * and seconds notation (e.g., 2Β°10'30.45"E or W). + * Uses a static buffer and assumes `degreeFormat` is a valid format string. * - * @param lon -> Longitude - * @return char* -> String + * @param lon Longitude in decimal degrees. + * @return Pointer to static buffer containing formatted string. */ -char *lonFormatString(double lon) +char *lonFormatString(float lon) { - char E_W = PSTR('E'); - double absLongitude = lon; - uint16_t deg; - uint8_t min; - static char s_buf[64]; - if (lon < 0) - { - E_W = PSTR('W'); - absLongitude = fabs(lon); - } - deg = (uint16_t)absLongitude; - absLongitude = (absLongitude - deg) * 60; - min = (uint8_t)absLongitude; - absLongitude = (absLongitude - min) * 60; - sprintf(s_buf, degreeFormat, deg, min, absLongitude, E_W); - return s_buf; -} \ No newline at end of file + char E_W = 'E'; + float absLongitude = lon; + uint16_t deg; + uint8_t min; + static char s_buf[64]; + + if (lon < 0.0f) + { + E_W = 'W'; + absLongitude = fabsf(lon); + } + + deg = static_cast(absLongitude); + absLongitude = (absLongitude - deg) * 60.0f; + min = static_cast(absLongitude); + absLongitude = (absLongitude - min) * 60.0f; + + sprintf(s_buf, degreeFormat, deg, min, absLongitude, E_W); + return s_buf; +} diff --git a/lib/utils/src/gpsMath.hpp b/lib/utils/src/gpsMath.hpp index 4ffb8eeb..750799aa 100644 --- a/lib/utils/src/gpsMath.hpp +++ b/lib/utils/src/gpsMath.hpp @@ -2,8 +2,8 @@ * @file gpsMath.hpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Math and various functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #pragma once @@ -11,20 +11,102 @@ #include #include -#define EARTH_RADIUS 6378137 // Earth Radius -#define METER_PER_PIXELS 156543.03 // Meters per pixels +#define EARTH_RADIUS 6378137 /**< Earth radius in meters */ +#define METER_PER_PIXELS 156543.03 /**< Meters per pixel at zoom level 0 (latitude 0) */ +#define LUT_SIZE 65536 /**< Number of entries in the lookup tables */ +static const float LUT_RES = (TWO_PI / (float)LUT_SIZE); /**< Angular resolution (radians per LUT step) */ -#define DEG2RAD(a) ((a) / (180 / M_PI)) // Convert degrees to radians -#define RAD2DEG(a) ((a) * (180 / M_PI)) // Convert radians to degrees +// Lookup tables (allocated in PSRAM if available, else in normal RAM) +static float* sinLut = NULL; +static float* cosLut = NULL; +extern bool lutInit; -extern double midLat; // Mid point between 2 Latitudes -extern double midLon; // Mid point between 2 Longitudes +/** + * @brief Converts degrees to radians. + * + * @param deg Angle in degrees. + * @return Angle in radians. + */ +static inline __attribute__((always_inline)) float DEG2RAD(float deg) +{ + return deg * (M_PI / 180.0); +} + +/** + * @brief Converts radians to degrees. + * + * @param rad Angle in radians. + * @return Angle in degrees. + */ +static inline __attribute__((always_inline)) float RAD2DEG(float rad) +{ + return rad * (180.0 / M_PI); +} + +extern float midLat; /**< Midpoint between two latitudes */ +extern float midLon; /**< Midpoint between two longitudes */ + +static const char *degreeFormat PROGMEM = "%03d\xC2\xB0 %02d\' %.2f\" %c"; /**< Format string for degrees (DDDΒ°MM'SS" + hemisphere) */ +static const char* TAGMATH PROGMEM = "MATH"; + +bool initTrigLUT(); + +/** + * @brief Lookup sine value using precomputed LUT + * + * @param rad Angle in radians + * @return Sine of the angle + */ +static inline __attribute__((always_inline)) float sinLUT(float rad) +{ + if (!sinLut) + return sinf(rad); + + rad -= TWO_PI * floorf(rad / TWO_PI); + if (rad < 0.0f) rad += TWO_PI; + + float index = rad / LUT_RES; + int idx_low = (int)index; + int idx_high = (idx_low + 1) % LUT_SIZE; + + float frac = index - idx_low; + + float result = sinLut[idx_low] * (1.0f - frac) + sinLut[idx_high] * frac; + + return result; +} + +/** + * @brief Lookup cosine value using precomputed LUT + * + * @param rad Angle in radians + * @return Cosine of the angle + */ +static inline __attribute__((always_inline)) float cosLUT(float rad) +{ + if (!cosLut) + return cosf(rad); + + // Normalize angle to [0, TWO_PI) + rad -= TWO_PI * floorf(rad / TWO_PI); + if (rad < 0.0f) rad += TWO_PI; + + float index = rad / (float)LUT_RES; + int idx_low = (int)index; + int idx_high = (idx_low + 1) % LUT_SIZE; + + float frac = index - idx_low; + + float result = (float)cosLut[idx_low] * (1.0f - frac) + (float)cosLut[idx_high] * frac; + + return result; +} -static const char *degreeFormat PROGMEM = "%03d\xC2\xB0 %02d\' %.2f\" %c"; // GGΒΊMM'SS" to string format -double calcDist(double lat1, double lon1, double lat2, double lon2); +float calcDist(float lat1, float lon1, float lat2, float lon2); void calcMidPoint(float lat1, float lon1, float lat2, float lon2); float mapFloat(float x, float inMin, float inMax, float outMin, float outMax); -char *latFormatString(double lat); -char *lonFormatString(double lon); -double calcCourse(double lat1, double lon1, double lat2, double lon2); \ No newline at end of file +char *latFormatString(float lat); +char *lonFormatString(float lon); +float calcCourse(float lat1, float lon1, float lat2, float lon2); +float calcAngleDiff(float a, float b); diff --git a/lib/utils/src/navigation.cpp b/lib/utils/src/navigation.cpp new file mode 100644 index 00000000..a52ff3c5 --- /dev/null +++ b/lib/utils/src/navigation.cpp @@ -0,0 +1,289 @@ +/** + * @file navigation.cpp + * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) + * @brief Navigation functions + * @version 0.2.3 + * @date 2025-06 + */ + +#include "navigation.hpp" + +extern lv_obj_t *turnDistLabel; +extern lv_obj_t *turnImg; + +LV_IMG_DECLARE(straight); +LV_IMG_DECLARE(slleft); +LV_IMG_DECLARE(slright); +LV_IMG_DECLARE(tleft); +LV_IMG_DECLARE(tright); +LV_IMG_DECLARE(uleft); +LV_IMG_DECLARE(uright); +LV_IMG_DECLARE(finish); +LV_IMG_DECLARE(outtrack); + +/** + * @brief Finds the closest track point index to the user's current position using an adaptive local/global search. + * + * @details This function locates the closest waypoint in the GPX track to the current GPS coordinates. + * - Initially performs a localized search within a window around the last known closest index (`lastIdx`) to optimize performance. + * - If the distance to the closest point is too large (>30m) or the previous index is invalid, a full search of the track is triggered. + * - Prevents undesired small backward jumps due to GPS noise by ignoring points slightly behind the last index (<5 positions). + * - Returns the best matching index based on minimum haversine distance. + * + * @param userLat Current latitude of the user. + * @param userLon Current longitude of the user. + * @param track The vector of wayPoints representing the full track. + * @param lastIdx Index of the last known closest point. + * @return Index of the closest waypoint found in the track. + */ +int findClosestTrackPoint(float userLat, float userLon, const std::vector& track, int lastIdx, const NavConfig& config) +{ + int n = (int)track.size(); + int window = config.searchWindow; + float minDist = std::numeric_limits::max(); + int closestIdx = -1; + + // If last index is invalid or near track edges, force global search + bool forceGlobal = lastIdx < 0 || lastIdx >= n; + + int start = forceGlobal ? 0 : std::max(0, lastIdx - window); + int end = forceGlobal ? n - 1 : std::min(n - 1, lastIdx + window); + + for (int i = start; i <= end; ++i) + { + float d = calcDist(userLat, userLon, track[i].lat, track[i].lon); + if (d < minDist) { + minDist = d; + closestIdx = i; + } + } + + // If user is far from last known point, perform full search + if (!forceGlobal && minDist > config.offTrackThreshold) + { + for (int i = 0; i < n; ++i) { + float d = calcDist(userLat, userLon, track[i].lat, track[i].lon); + if (d < minDist) { + minDist = d; + closestIdx = i; + } + } + } + + // Avoid small backward jumps (likely due to GPS noise) + if (closestIdx < lastIdx && (lastIdx - closestIdx) < config.maxBackwardJump) + return lastIdx; + + return closestIdx; +} + +/** + * @brief Handle off-track condition and update navigation state + * + * @details Manages the navigation state when the user is off-track (>30m from track). + * Saves the current turn index and sets off-track flag. + * + * @param distToTrack Distance to the closest track point + * @param state Navigation state to update + * @param closestIdx Index of the closest track point + */ +void handleOffTrackCondition(float distToTrack, NavState& state, int closestIdx, const NavConfig& config) +{ + if (distToTrack > config.offTrackThreshold) + { + lv_img_set_src(turnImg, &outtrack); + + // Save current turn index if just detected off-track + if (!state.isOffTrack) + { + state.lastValidTurnIdx = state.nextTurnIdx; + state.isOffTrack = true; + } + + state.lastTrackIdx = closestIdx; + } +} + +/** + * @brief Advance turn index if turns have been passed + * + * @details Advances the nextTurnIdx if the corresponding turn has been passed already. + * + * @param turns Vector of detected turn points + * @param state Navigation state to update + * @param closestIdx Index of the closest track point + */ +void advanceTurnIndex(const std::vector& turns, NavState& state, int closestIdx) +{ + // Advance nextTurnIdx if it's already passed + while (state.nextTurnIdx < turns.size() && turns[state.nextTurnIdx].idx <= closestIdx) + state.nextTurnIdx++; +} + +/** + * @brief Find the next valid turn point + * + * @details Searches for the next valid turn point, skipping suspiciously distant ones. + * + * @param track Vector of track waypoints + * @param turns Vector of detected turn points + * @param userLat Current user latitude + * @param userLon Current user longitude + * @param state Navigation state + * @return Index of the next valid turn, or -1 if none found + */ +int findNextValidTurn(const std::vector& track, const std::vector& turns, + float userLat, float userLon, NavState& state, const NavConfig& config) +{ + for (int i = state.nextTurnIdx; i < turns.size(); ++i) + { + const float turnLat = track[turns[i].idx].lat; + const float turnLon = track[turns[i].idx].lon; + const float distanceToTurn = calcDist(userLat, userLon, turnLat, turnLon); + + if (distanceToTurn < config.minTurnDistance) + continue; + + if (distanceToTurn > config.maxTurnDistance) + { + ESP_LOGW("NAV", "Skipping suspicious turn at index %d (dist=%.1f)", i, distanceToTurn); + continue; + } + + return i; // Valid turn found + } + + return -1; // No valid turn found +} + +/** + * @brief Display appropriate turn icon based on distance and angle + * + * @details Shows the correct directional icon based on distance to turn and angle. + * + * @param distanceToNextEvent Distance to the next turn event + * @param abs_angle Absolute value of the turn angle + * @param derecha True if turn is to the right + * @param warnDist Warning distance threshold + * @param minAngleForCurve Minimum angle for curve classification + */ +void displayTurnIcon(float distanceToNextEvent, float abs_angle, bool derecha, float warnDist, float minAngleForCurve) +{ + // Display turn icon only within warning distance + if (distanceToNextEvent <= warnDist) + { + if (abs_angle >= minAngleForCurve && abs_angle < 60.0f) + { + lv_img_set_src(turnImg, derecha ? &slright : &slleft); + } + else if (abs_angle >= 60.0f) + { + lv_img_set_src(turnImg, derecha ? &tright : &tleft); + } + else + { + lv_img_set_src(turnImg, &straight); + } + } + else + { + lv_img_set_src(turnImg, &straight); + } +} + +/** + * @brief Updates turn-by-turn navigation status and on-screen instructions. + * + * @details Determines the user's current position relative to a GPX track and selects the next valid + * navigation event (turn) from a list of preprocessed `TurnPoint` entries. + * + * The function handles: + * - Real-time distance calculation to the track and the upcoming turn. + * - Resynchronization in case of GPS drift, off-track situations, or user jumps. + * - Prevention of regressions in track index to avoid flickering icons. + * - Filtering of suspicious or invalid turns based on distance thresholds. + * - Display of appropriate directional icons (left, right, straight, finish, out-of-track). + * + * Main logic steps: + * - Use `findClosestTrackPoint()` to locate the closest point in the track to the current position. + * - If off-track (>30m), mark the navigation state as off-route and restore `nextTurnIdx` using `lastValidTurnIdx`. + * - Advance `nextTurnIdx` if the corresponding turn has been passed already. + * - Skip suspiciously distant turn events (>2000m) to avoid invalid guidance. + * - Classify turns by angular threshold into straight, soft, or hard turns. + * - Show directional icon if within `warnDist`, or fallback to "straight ahead". + * - Round distance to next event to the nearest 5 meters and update the display label. + * + * @param userLat Current latitude. + * @param userLon Current longitude. + * @param userHeading Current heading (in degrees). + * @param speed_kmh Current speed in km/h. + * @param track Vector of GPX track waypoints. + * @param turns Vector of detected turn points (with indices and angles). + * @param state Persistent navigation state including current/last track and turn indices. + * @param minAngleForCurve Minimum angle (in degrees) to classify a soft curve (default: 15Β°). + * @param warnDist Distance threshold (in meters) to trigger the event warning (default: 100 m). + */ +void updateNavigation( + float userLat, float userLon, float userHeading, float speed_kmh, + const std::vector& track, + const std::vector& turns, + NavState& state, + float minAngleForCurve, + float warnDist, + const NavConfig& config +) +{ + int closestIdx = findClosestTrackPoint(userLat, userLon, track, state.lastTrackIdx, config); + float distToTrack = calcDist(userLat, userLon, track[closestIdx].lat, track[closestIdx].lon); + + // Handle off-track condition + if (distToTrack > config.offTrackThreshold) + { + handleOffTrackCondition(distToTrack, state, closestIdx, config); + return; + } + + // Restore turn index if user returns to track + if (state.isOffTrack) + { + state.nextTurnIdx = state.lastValidTurnIdx; + state.isOffTrack = false; + } + + // Advance turn index if turns have been passed + advanceTurnIndex(turns, state, closestIdx); + + // No more turns remaining + if (state.nextTurnIdx >= turns.size()) + { + lv_img_set_src(turnImg, &finish); + state.lastTrackIdx = closestIdx; + return; + } + + // Find next valid turn + int nextEventIdx = findNextValidTurn(track, turns, userLat, userLon, state, config); + + if (nextEventIdx == -1) + { + lv_img_set_src(turnImg, &finish); + state.lastTrackIdx = closestIdx; + return; + } + + // Calculate turn details + const float turnLat = track[turns[nextEventIdx].idx].lat; + const float turnLon = track[turns[nextEventIdx].idx].lon; + const float distanceToNextEvent = calcDist(userLat, userLon, turnLat, turnLon); + const float abs_angle = fabsf(turns[nextEventIdx].angle); + const bool derecha = (turns[nextEventIdx].angle > 0.0f); + + // Display appropriate turn icon + displayTurnIcon(distanceToNextEvent, abs_angle, derecha, warnDist, minAngleForCurve); + + // Update distance label + int roundedDist = ((int)distanceToNextEvent / 5) * 5; + lv_label_set_text_fmt(turnDistLabel, "%4d", roundedDist); + + state.lastTrackIdx = closestIdx; +} diff --git a/lib/utils/src/navigation.hpp b/lib/utils/src/navigation.hpp new file mode 100644 index 00000000..a15252d3 --- /dev/null +++ b/lib/utils/src/navigation.hpp @@ -0,0 +1,69 @@ +/** + * @file navigation.hpp + * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) + * @brief Navigation functions + * @version 0.2.3 + * @date 2025-06 + */ + +#pragma once + +#include +#include +#include "globalGpxDef.h" +#include "gpsMath.hpp" +#include "lvgl.h" + +/** + * @brief Persistent navigation state for turn-by-turn guidance. + * + * @details This structure maintains the state of the navigation system during GPX-based turn-by-turn routing. + * It tracks both current and upcoming turn indices and handles off-track situations gracefully. + * + * Fields: + * - lastTrackIdx: Index of the last closest track point matched to the user's current position. + * - nextTurnIdx: Index of the next turn point in the list of detected turns. + * - lastValidTurnIdx: Backup index of the last valid nextTurnIdx before going off-track, used to restore state. + * - isOffTrack: Flag indicating whether the user is currently considered off the track. + * + * This structure should persist across navigation updates to manage context and prevent redundant alerts. + */ +/** + * @brief Navigation configuration parameters + * + * @details Contains configurable parameters for navigation behavior. + * These values were previously hardcoded and are now configurable. + */ +struct NavConfig +{ + int searchWindow = 100; /**< Window size for local search in findClosestTrackPoint */ + float offTrackThreshold = 50.0f; /**< Distance threshold for off-track detection (meters) */ + float minTurnDistance = 5.0f; /**< Minimum distance for valid turn detection (meters) */ + float maxTurnDistance = 2000.0f; /**< Maximum distance for suspicious turn filtering (meters) */ + int maxBackwardJump = 8; /**< Maximum backward positions to prevent GPS noise jumps */ +}; + +struct NavState +{ + int lastTrackIdx = 0; + int nextTurnIdx = 0; + int lastValidTurnIdx = 0; + bool isOffTrack = false; +}; + +int findClosestTrackPoint(float userLat, float userLon, const std::vector& track, int lastIdx, const NavConfig& config = NavConfig{}); +void handleOffTrackCondition(float distToTrack, NavState& state, int closestIdx, const NavConfig& config = NavConfig{}); +void advanceTurnIndex(const std::vector& turns, NavState& state, int closestIdx); +int findNextValidTurn(const std::vector& track, const std::vector& turns, + float userLat, float userLon, NavState& state, const NavConfig& config = NavConfig{}); +void displayTurnIcon(float distanceToNextEvent, float abs_angle, bool derecha, float warnDist, float minAngleForCurve); +void updateNavigation +( + float userLat, float userLon, float userHeading, float speed_kmh, + const std::vector& track, + const std::vector& turns, + NavState& state, + float minAngleForCurve, + float warnDist, + const NavConfig& config = NavConfig{} +); \ No newline at end of file diff --git a/lib/utils/src/timezone.c b/lib/utils/src/timezone.c index ef102521..b8ad4aa8 100644 --- a/lib/utils/src/timezone.c +++ b/lib/utils/src/timezone.c @@ -2,463 +2,478 @@ * @file timezone.c * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief POSIX timezone (TZ) - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include #include #include - typedef struct - { - const char *name; - const char *posix_str; - } posix_tz; +/** + * @brief Structure to represent a POSIX time zone definition + * + * @details This struct stores a human-readable time zone name and its corresponding + * POSIX time zone string representation. + */ +typedef struct +{ + const char *name; /**< Human-readable time zone name */ + const char *posix_str; /**< POSIX time zone string representation */ +} posix_tz; - static const posix_tz posix_tz_db[] PROGMEM = { - {"Africa/Abidjan", "GMT0"}, - {"Africa/Accra", "GMT0"}, - {"Africa/Addis_Ababa", "EAT-3"}, - {"Africa/Algiers", "CET-1"}, - {"Africa/Asmara", "EAT-3"}, - {"Africa/Bamako", "GMT0"}, - {"Africa/Bangui", "WAT-1"}, - {"Africa/Banjul", "GMT0"}, - {"Africa/Bissau", "GMT0"}, - {"Africa/Blantyre", "CAT-2"}, - {"Africa/Brazzaville", "WAT-1"}, - {"Africa/Bujumbura", "CAT-2"}, - {"Africa/Cairo", "EET-2EEST,M4.5.5/0,M10.5.4/24"}, - {"Africa/Casablanca", "<+01>-1"}, - {"Africa/Ceuta", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Africa/Conakry", "GMT0"}, - {"Africa/Dakar", "GMT0"}, - {"Africa/Dar_es_Salaam", "EAT-3"}, - {"Africa/Djibouti", "EAT-3"}, - {"Africa/Douala", "WAT-1"}, - {"Africa/El_Aaiun", "<+01>-1"}, - {"Africa/Freetown", "GMT0"}, - {"Africa/Gaborone", "CAT-2"}, - {"Africa/Harare", "CAT-2"}, - {"Africa/Johannesburg", "SAST-2"}, - {"Africa/Juba", "CAT-2"}, - {"Africa/Kampala", "EAT-3"}, - {"Africa/Khartoum", "CAT-2"}, - {"Africa/Kigali", "CAT-2"}, - {"Africa/Kinshasa", "WAT-1"}, - {"Africa/Lagos", "WAT-1"}, - {"Africa/Libreville", "WAT-1"}, - {"Africa/Lome", "GMT0"}, - {"Africa/Luanda", "WAT-1"}, - {"Africa/Lubumbashi", "CAT-2"}, - {"Africa/Lusaka", "CAT-2"}, - {"Africa/Malabo", "WAT-1"}, - {"Africa/Maputo", "CAT-2"}, - {"Africa/Maseru", "SAST-2"}, - {"Africa/Mbabane", "SAST-2"}, - {"Africa/Mogadishu", "EAT-3"}, - {"Africa/Monrovia", "GMT0"}, - {"Africa/Nairobi", "EAT-3"}, - {"Africa/Ndjamena", "WAT-1"}, - {"Africa/Niamey", "WAT-1"}, - {"Africa/Nouakchott", "GMT0"}, - {"Africa/Ouagadougou", "GMT0"}, - {"Africa/Porto-Novo", "WAT-1"}, - {"Africa/Sao_Tome", "GMT0"}, - {"Africa/Tripoli", "EET-2"}, - {"Africa/Tunis", "CET-1"}, - {"Africa/Windhoek", "CAT-2"}, - {"America/Adak", "HST10HDT,M3.2.0,M11.1.0"}, - {"America/Anchorage", "AKST9AKDT,M3.2.0,M11.1.0"}, - {"America/Anguilla", "AST4"}, - {"America/Antigua", "AST4"}, - {"America/Araguaina", "<-03>3"}, - {"America/Argentina/Buenos_Aires", "<-03>3"}, - {"America/Argentina/Catamarca", "<-03>3"}, - {"America/Argentina/Cordoba", "<-03>3"}, - {"America/Argentina/Jujuy", "<-03>3"}, - {"America/Argentina/La_Rioja", "<-03>3"}, - {"America/Argentina/Mendoza", "<-03>3"}, - {"America/Argentina/Rio_Gallegos", "<-03>3"}, - {"America/Argentina/Salta", "<-03>3"}, - {"America/Argentina/San_Juan", "<-03>3"}, - {"America/Argentina/San_Luis", "<-03>3"}, - {"America/Argentina/Tucuman", "<-03>3"}, - {"America/Argentina/Ushuaia", "<-03>3"}, - {"America/Aruba", "AST4"}, - {"America/Asuncion", "<-04>4<-03>,M10.1.0/0,M3.4.0/0"}, - {"America/Atikokan", "EST5"}, - {"America/Bahia", "<-03>3"}, - {"America/Bahia_Banderas", "CST6"}, - {"America/Barbados", "AST4"}, - {"America/Belem", "<-03>3"}, - {"America/Belize", "CST6"}, - {"America/Blanc-Sablon", "AST4"}, - {"America/Boa_Vista", "<-04>4"}, - {"America/Bogota", "<-05>5"}, - {"America/Boise", "MST7MDT,M3.2.0,M11.1.0"}, - {"America/Cambridge_Bay", "MST7MDT,M3.2.0,M11.1.0"}, - {"America/Campo_Grande", "<-04>4"}, - {"America/Cancun", "EST5"}, - {"America/Caracas", "<-04>4"}, - {"America/Cayenne", "<-03>3"}, - {"America/Cayman", "EST5"}, - {"America/Chicago", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Chihuahua", "CST6"}, - {"America/Costa_Rica", "CST6"}, - {"America/Creston", "MST7"}, - {"America/Cuiaba", "<-04>4"}, - {"America/Curacao", "AST4"}, - {"America/Danmarkshavn", "GMT0"}, - {"America/Dawson", "MST7"}, - {"America/Dawson_Creek", "MST7"}, - {"America/Denver", "MST7MDT,M3.2.0,M11.1.0"}, - {"America/Detroit", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Dominica", "AST4"}, - {"America/Edmonton", "MST7MDT,M3.2.0,M11.1.0"}, - {"America/Eirunepe", "<-05>5"}, - {"America/El_Salvador", "CST6"}, - {"America/Fortaleza", "<-03>3"}, - {"America/Fort_Nelson", "MST7"}, - {"America/Glace_Bay", "AST4ADT,M3.2.0,M11.1.0"}, - {"America/Godthab", "<-02>2<-01>,M3.5.0/-1,M10.5.0/0"}, - {"America/Goose_Bay", "AST4ADT,M3.2.0,M11.1.0"}, - {"America/Grand_Turk", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Grenada", "AST4"}, - {"America/Guadeloupe", "AST4"}, - {"America/Guatemala", "CST6"}, - {"America/Guayaquil", "<-05>5"}, - {"America/Guyana", "<-04>4"}, - {"America/Halifax", "AST4ADT,M3.2.0,M11.1.0"}, - {"America/Havana", "CST5CDT,M3.2.0/0,M11.1.0/1"}, - {"America/Hermosillo", "MST7"}, - {"America/Indiana/Indianapolis", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Knox", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Marengo", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Petersburg", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Tell_City", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Vevay", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Vincennes", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Indiana/Winamac", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Inuvik", "MST7MDT,M3.2.0,M11.1.0"}, - {"America/Iqaluit", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Jamaica", "EST5"}, - {"America/Juneau", "AKST9AKDT,M3.2.0,M11.1.0"}, - {"America/Kentucky/Louisville", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Kentucky/Monticello", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Kralendijk", "AST4"}, - {"America/La_Paz", "<-04>4"}, - {"America/Lima", "<-05>5"}, - {"America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0"}, - {"America/Lower_Princes", "AST4"}, - {"America/Maceio", "<-03>3"}, - {"America/Managua", "CST6"}, - {"America/Manaus", "<-04>4"}, - {"America/Marigot", "AST4"}, - {"America/Martinique", "AST4"}, - {"America/Matamoros", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Mazatlan", "MST7"}, - {"America/Menominee", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Merida", "CST6"}, - {"America/Metlakatla", "AKST9AKDT,M3.2.0,M11.1.0"}, - {"America/Mexico_City", "CST6"}, - {"America/Miquelon", "<-03>3<-02>,M3.2.0,M11.1.0"}, - {"America/Moncton", "AST4ADT,M3.2.0,M11.1.0"}, - {"America/Monterrey", "CST6"}, - {"America/Montevideo", "<-03>3"}, - {"America/Montreal", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Montserrat", "AST4"}, - {"America/Nassau", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/New_York", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Nipigon", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Nome", "AKST9AKDT,M3.2.0,M11.1.0"}, - {"America/Noronha", "<-02>2"}, - {"America/North_Dakota/Beulah", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/North_Dakota/Center", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/North_Dakota/New_Salem", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Ojinaga", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Panama", "EST5"}, - {"America/Pangnirtung", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Paramaribo", "<-03>3"}, - {"America/Phoenix", "MST7"}, - {"America/Port-au-Prince", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Port_of_Spain", "AST4"}, - {"America/Porto_Velho", "<-04>4"}, - {"America/Puerto_Rico", "AST4"}, - {"America/Punta_Arenas", "<-03>3"}, - {"America/Rainy_River", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Rankin_Inlet", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Recife", "<-03>3"}, - {"America/Regina", "CST6"}, - {"America/Resolute", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Rio_Branco", "<-05>5"}, - {"America/Santarem", "<-03>3"}, - {"America/Santiago", "<-04>4<-03>,M9.1.6/24,M4.1.6/24"}, - {"America/Santo_Domingo", "AST4"}, - {"America/Sao_Paulo", "<-03>3"}, - {"America/Scoresbysund", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"}, - {"America/Sitka", "AKST9AKDT,M3.2.0,M11.1.0"}, - {"America/St_Barthelemy", "AST4"}, - {"America/St_Johns", "NST3:30NDT,M3.2.0,M11.1.0"}, - {"America/St_Kitts", "AST4"}, - {"America/St_Lucia", "AST4"}, - {"America/St_Thomas", "AST4"}, - {"America/St_Vincent", "AST4"}, - {"America/Swift_Current", "CST6"}, - {"America/Tegucigalpa", "CST6"}, - {"America/Thule", "AST4ADT,M3.2.0,M11.1.0"}, - {"America/Thunder_Bay", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Tijuana", "PST8PDT,M3.2.0,M11.1.0"}, - {"America/Toronto", "EST5EDT,M3.2.0,M11.1.0"}, - {"America/Tortola", "AST4"}, - {"America/Vancouver", "PST8PDT,M3.2.0,M11.1.0"}, - {"America/Whitehorse", "MST7"}, - {"America/Winnipeg", "CST6CDT,M3.2.0,M11.1.0"}, - {"America/Yakutat", "AKST9AKDT,M3.2.0,M11.1.0"}, - {"America/Yellowknife", "MST7MDT,M3.2.0,M11.1.0"}, - {"Antarctica/Casey", "<+11>-11"}, - {"Antarctica/Davis", "<+07>-7"}, - {"Antarctica/DumontDUrville", "<+10>-10"}, - {"Antarctica/Macquarie", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, - {"Antarctica/Mawson", "<+05>-5"}, - {"Antarctica/McMurdo", "NZST-12NZDT,M9.5.0,M4.1.0/3"}, - {"Antarctica/Palmer", "<-03>3"}, - {"Antarctica/Rothera", "<-03>3"}, - {"Antarctica/Syowa", "<+03>-3"}, - {"Antarctica/Troll", "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3"}, - {"Antarctica/Vostok", "<+06>-6"}, - {"Arctic/Longyearbyen", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Asia/Aden", "<+03>-3"}, - {"Asia/Almaty", "<+06>-6"}, - {"Asia/Amman", "<+03>-3"}, - {"Asia/Anadyr", "<+12>-12"}, - {"Asia/Aqtau", "<+05>-5"}, - {"Asia/Aqtobe", "<+05>-5"}, - {"Asia/Ashgabat", "<+05>-5"}, - {"Asia/Atyrau", "<+05>-5"}, - {"Asia/Baghdad", "<+03>-3"}, - {"Asia/Bahrain", "<+03>-3"}, - {"Asia/Baku", "<+04>-4"}, - {"Asia/Bangkok", "<+07>-7"}, - {"Asia/Barnaul", "<+07>-7"}, - {"Asia/Beirut", "EET-2EEST,M3.5.0/0,M10.5.0/0"}, - {"Asia/Bishkek", "<+06>-6"}, - {"Asia/Brunei", "<+08>-8"}, - {"Asia/Chita", "<+09>-9"}, - {"Asia/Choibalsan", "<+08>-8"}, - {"Asia/Colombo", "<+0530>-5:30"}, - {"Asia/Damascus", "<+03>-3"}, - {"Asia/Dhaka", "<+06>-6"}, - {"Asia/Dili", "<+09>-9"}, - {"Asia/Dubai", "<+04>-4"}, - {"Asia/Dushanbe", "<+05>-5"}, - {"Asia/Famagusta", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Asia/Gaza", "EET-2EEST,M3.4.4/50,M10.4.4/50"}, - {"Asia/Hebron", "EET-2EEST,M3.4.4/50,M10.4.4/50"}, - {"Asia/Ho_Chi_Minh", "<+07>-7"}, - {"Asia/Hong_Kong", "HKT-8"}, - {"Asia/Hovd", "<+07>-7"}, - {"Asia/Irkutsk", "<+08>-8"}, - {"Asia/Jakarta", "WIB-7"}, - {"Asia/Jayapura", "WIT-9"}, - {"Asia/Jerusalem", "IST-2IDT,M3.4.4/26,M10.5.0"}, - {"Asia/Kabul", "<+0430>-4:30"}, - {"Asia/Kamchatka", "<+12>-12"}, - {"Asia/Karachi", "PKT-5"}, - {"Asia/Kathmandu", "<+0545>-5:45"}, - {"Asia/Khandyga", "<+09>-9"}, - {"Asia/Kolkata", "IST-5:30"}, - {"Asia/Krasnoyarsk", "<+07>-7"}, - {"Asia/Kuala_Lumpur", "<+08>-8"}, - {"Asia/Kuching", "<+08>-8"}, - {"Asia/Kuwait", "<+03>-3"}, - {"Asia/Macau", "CST-8"}, - {"Asia/Magadan", "<+11>-11"}, - {"Asia/Makassar", "WITA-8"}, - {"Asia/Manila", "PST-8"}, - {"Asia/Muscat", "<+04>-4"}, - {"Asia/Nicosia", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Asia/Novokuznetsk", "<+07>-7"}, - {"Asia/Novosibirsk", "<+07>-7"}, - {"Asia/Omsk", "<+06>-6"}, - {"Asia/Oral", "<+05>-5"}, - {"Asia/Phnom_Penh", "<+07>-7"}, - {"Asia/Pontianak", "WIB-7"}, - {"Asia/Pyongyang", "KST-9"}, - {"Asia/Qatar", "<+03>-3"}, - {"Asia/Qyzylorda", "<+05>-5"}, - {"Asia/Riyadh", "<+03>-3"}, - {"Asia/Sakhalin", "<+11>-11"}, - {"Asia/Samarkand", "<+05>-5"}, - {"Asia/Seoul", "KST-9"}, - {"Asia/Shanghai", "CST-8"}, - {"Asia/Singapore", "<+08>-8"}, - {"Asia/Srednekolymsk", "<+11>-11"}, - {"Asia/Taipei", "CST-8"}, - {"Asia/Tashkent", "<+05>-5"}, - {"Asia/Tbilisi", "<+04>-4"}, - {"Asia/Tehran", "<+0330>-3:30"}, - {"Asia/Thimphu", "<+06>-6"}, - {"Asia/Tokyo", "JST-9"}, - {"Asia/Tomsk", "<+07>-7"}, - {"Asia/Ulaanbaatar", "<+08>-8"}, - {"Asia/Urumqi", "<+06>-6"}, - {"Asia/Ust-Nera", "<+10>-10"}, - {"Asia/Vientiane", "<+07>-7"}, - {"Asia/Vladivostok", "<+10>-10"}, - {"Asia/Yakutsk", "<+09>-9"}, - {"Asia/Yangon", "<+0630>-6:30"}, - {"Asia/Yekaterinburg", "<+05>-5"}, - {"Asia/Yerevan", "<+04>-4"}, - {"Atlantic/Azores", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"}, - {"Atlantic/Bermuda", "AST4ADT,M3.2.0,M11.1.0"}, - {"Atlantic/Canary", "WET0WEST,M3.5.0/1,M10.5.0"}, - {"Atlantic/Cape_Verde", "<-01>1"}, - {"Atlantic/Faroe", "WET0WEST,M3.5.0/1,M10.5.0"}, - {"Atlantic/Madeira", "WET0WEST,M3.5.0/1,M10.5.0"}, - {"Atlantic/Reykjavik", "GMT0"}, - {"Atlantic/South_Georgia", "<-02>2"}, - {"Atlantic/Stanley", "<-03>3"}, - {"Atlantic/St_Helena", "GMT0"}, - {"Australia/Adelaide", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"}, - {"Australia/Brisbane", "AEST-10"}, - {"Australia/Broken_Hill", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"}, - {"Australia/Currie", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, - {"Australia/Darwin", "ACST-9:30"}, - {"Australia/Eucla", "<+0845>-8:45"}, - {"Australia/Hobart", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, - {"Australia/Lindeman", "AEST-10"}, - {"Australia/Lord_Howe", "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0"}, - {"Australia/Melbourne", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, - {"Australia/Perth", "AWST-8"}, - {"Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, - {"Europe/Amsterdam", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Andorra", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Astrakhan", "<+04>-4"}, - {"Europe/Athens", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Belgrade", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Berlin", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Bratislava", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Brussels", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Bucharest", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Budapest", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Busingen", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Chisinau", "EET-2EEST,M3.5.0,M10.5.0/3"}, - {"Europe/Copenhagen", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Dublin", "IST-1GMT0,M10.5.0,M3.5.0/1"}, - {"Europe/Gibraltar", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Guernsey", "GMT0BST,M3.5.0/1,M10.5.0"}, - {"Europe/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Isle_of_Man", "GMT0BST,M3.5.0/1,M10.5.0"}, - {"Europe/Istanbul", "<+03>-3"}, - {"Europe/Jersey", "GMT0BST,M3.5.0/1,M10.5.0"}, - {"Europe/Kaliningrad", "EET-2"}, - {"Europe/Kiev", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Kirov", "MSK-3"}, - {"Europe/Lisbon", "WET0WEST,M3.5.0/1,M10.5.0"}, - {"Europe/Ljubljana", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/London", "GMT0BST,M3.5.0/1,M10.5.0"}, - {"Europe/Luxembourg", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Madrid", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Malta", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Mariehamn", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Minsk", "<+03>-3"}, - {"Europe/Monaco", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Moscow", "MSK-3"}, - {"Europe/Oslo", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Paris", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Podgorica", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Prague", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Riga", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Rome", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Samara", "<+04>-4"}, - {"Europe/San_Marino", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Sarajevo", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Saratov", "<+04>-4"}, - {"Europe/Simferopol", "MSK-3"}, - {"Europe/Skopje", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Sofia", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Stockholm", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Tallinn", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Tirane", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Ulyanovsk", "<+04>-4"}, - {"Europe/Uzhgorod", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Vaduz", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Vatican", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Vienna", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Vilnius", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Volgograd", "MSK-3"}, - {"Europe/Warsaw", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Zagreb", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Europe/Zaporozhye", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, - {"Europe/Zurich", "CET-1CEST,M3.5.0,M10.5.0/3"}, - {"Indian/Antananarivo", "EAT-3"}, - {"Indian/Chagos", "<+06>-6"}, - {"Indian/Christmas", "<+07>-7"}, - {"Indian/Cocos", "<+0630>-6:30"}, - {"Indian/Comoro", "EAT-3"}, - {"Indian/Kerguelen", "<+05>-5"}, - {"Indian/Mahe", "<+04>-4"}, - {"Indian/Maldives", "<+05>-5"}, - {"Indian/Mauritius", "<+04>-4"}, - {"Indian/Mayotte", "EAT-3"}, - {"Indian/Reunion", "<+04>-4"}, - {"Pacific/Apia", "<+13>-13"}, - {"Pacific/Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3"}, - {"Pacific/Bougainville", "<+11>-11"}, - {"Pacific/Chatham", "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45"}, - {"Pacific/Chuuk", "<+10>-10"}, - {"Pacific/Easter", "<-06>6<-05>,M9.1.6/22,M4.1.6/22"}, - {"Pacific/Efate", "<+11>-11"}, - {"Pacific/Enderbury", "<+13>-13"}, - {"Pacific/Fakaofo", "<+13>-13"}, - {"Pacific/Fiji", "<+12>-12"}, - {"Pacific/Funafuti", "<+12>-12"}, - {"Pacific/Galapagos", "<-06>6"}, - {"Pacific/Gambier", "<-09>9"}, - {"Pacific/Guadalcanal", "<+11>-11"}, - {"Pacific/Guam", "ChST-10"}, - {"Pacific/Honolulu", "HST10"}, - {"Pacific/Kiritimati", "<+14>-14"}, - {"Pacific/Kosrae", "<+11>-11"}, - {"Pacific/Kwajalein", "<+12>-12"}, - {"Pacific/Majuro", "<+12>-12"}, - {"Pacific/Marquesas", "<-0930>9:30"}, - {"Pacific/Midway", "SST11"}, - {"Pacific/Nauru", "<+12>-12"}, - {"Pacific/Niue", "<-11>11"}, - {"Pacific/Norfolk", "<+11>-11<+12>,M10.1.0,M4.1.0/3"}, - {"Pacific/Noumea", "<+11>-11"}, - {"Pacific/Pago_Pago", "SST11"}, - {"Pacific/Palau", "<+09>-9"}, - {"Pacific/Pitcairn", "<-08>8"}, - {"Pacific/Pohnpei", "<+11>-11"}, - {"Pacific/Port_Moresby", "<+10>-10"}, - {"Pacific/Rarotonga", "<-10>10"}, - {"Pacific/Saipan", "ChST-10"}, - {"Pacific/Tahiti", "<-10>10"}, - {"Pacific/Tarawa", "<+12>-12"}, - {"Pacific/Tongatapu", "<+13>-13"}, - {"Pacific/Wake", "<+12>-12"}, - {"Pacific/Wallis", "<+12>-12"} - }; +/** + * @brief POSIX time zone database array + * + * @details Array contains mappings of IANA time zone names to their corresponding POSIX time zone strings. + */ +static const posix_tz posix_tz_db[] PROGMEM = { + {"Africa/Abidjan", "GMT0"}, + {"Africa/Accra", "GMT0"}, + {"Africa/Addis_Ababa", "EAT-3"}, + {"Africa/Algiers", "CET-1"}, + {"Africa/Asmara", "EAT-3"}, + {"Africa/Bamako", "GMT0"}, + {"Africa/Bangui", "WAT-1"}, + {"Africa/Banjul", "GMT0"}, + {"Africa/Bissau", "GMT0"}, + {"Africa/Blantyre", "CAT-2"}, + {"Africa/Brazzaville", "WAT-1"}, + {"Africa/Bujumbura", "CAT-2"}, + {"Africa/Cairo", "EET-2EEST,M4.5.5/0,M10.5.4/24"}, + {"Africa/Casablanca", "<+01>-1"}, + {"Africa/Ceuta", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Africa/Conakry", "GMT0"}, + {"Africa/Dakar", "GMT0"}, + {"Africa/Dar_es_Salaam", "EAT-3"}, + {"Africa/Djibouti", "EAT-3"}, + {"Africa/Douala", "WAT-1"}, + {"Africa/El_Aaiun", "<+01>-1"}, + {"Africa/Freetown", "GMT0"}, + {"Africa/Gaborone", "CAT-2"}, + {"Africa/Harare", "CAT-2"}, + {"Africa/Johannesburg", "SAST-2"}, + {"Africa/Juba", "CAT-2"}, + {"Africa/Kampala", "EAT-3"}, + {"Africa/Khartoum", "CAT-2"}, + {"Africa/Kigali", "CAT-2"}, + {"Africa/Kinshasa", "WAT-1"}, + {"Africa/Lagos", "WAT-1"}, + {"Africa/Libreville", "WAT-1"}, + {"Africa/Lome", "GMT0"}, + {"Africa/Luanda", "WAT-1"}, + {"Africa/Lubumbashi", "CAT-2"}, + {"Africa/Lusaka", "CAT-2"}, + {"Africa/Malabo", "WAT-1"}, + {"Africa/Maputo", "CAT-2"}, + {"Africa/Maseru", "SAST-2"}, + {"Africa/Mbabane", "SAST-2"}, + {"Africa/Mogadishu", "EAT-3"}, + {"Africa/Monrovia", "GMT0"}, + {"Africa/Nairobi", "EAT-3"}, + {"Africa/Ndjamena", "WAT-1"}, + {"Africa/Niamey", "WAT-1"}, + {"Africa/Nouakchott", "GMT0"}, + {"Africa/Ouagadougou", "GMT0"}, + {"Africa/Porto-Novo", "WAT-1"}, + {"Africa/Sao_Tome", "GMT0"}, + {"Africa/Tripoli", "EET-2"}, + {"Africa/Tunis", "CET-1"}, + {"Africa/Windhoek", "CAT-2"}, + {"America/Adak", "HST10HDT,M3.2.0,M11.1.0"}, + {"America/Anchorage", "AKST9AKDT,M3.2.0,M11.1.0"}, + {"America/Anguilla", "AST4"}, + {"America/Antigua", "AST4"}, + {"America/Araguaina", "<-03>3"}, + {"America/Argentina/Buenos_Aires", "<-03>3"}, + {"America/Argentina/Catamarca", "<-03>3"}, + {"America/Argentina/Cordoba", "<-03>3"}, + {"America/Argentina/Jujuy", "<-03>3"}, + {"America/Argentina/La_Rioja", "<-03>3"}, + {"America/Argentina/Mendoza", "<-03>3"}, + {"America/Argentina/Rio_Gallegos", "<-03>3"}, + {"America/Argentina/Salta", "<-03>3"}, + {"America/Argentina/San_Juan", "<-03>3"}, + {"America/Argentina/San_Luis", "<-03>3"}, + {"America/Argentina/Tucuman", "<-03>3"}, + {"America/Argentina/Ushuaia", "<-03>3"}, + {"America/Aruba", "AST4"}, + {"America/Asuncion", "<-04>4<-03>,M10.1.0/0,M3.4.0/0"}, + {"America/Atikokan", "EST5"}, + {"America/Bahia", "<-03>3"}, + {"America/Bahia_Banderas", "CST6"}, + {"America/Barbados", "AST4"}, + {"America/Belem", "<-03>3"}, + {"America/Belize", "CST6"}, + {"America/Blanc-Sablon", "AST4"}, + {"America/Boa_Vista", "<-04>4"}, + {"America/Bogota", "<-05>5"}, + {"America/Boise", "MST7MDT,M3.2.0,M11.1.0"}, + {"America/Cambridge_Bay", "MST7MDT,M3.2.0,M11.1.0"}, + {"America/Campo_Grande", "<-04>4"}, + {"America/Cancun", "EST5"}, + {"America/Caracas", "<-04>4"}, + {"America/Cayenne", "<-03>3"}, + {"America/Cayman", "EST5"}, + {"America/Chicago", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Chihuahua", "CST6"}, + {"America/Costa_Rica", "CST6"}, + {"America/Creston", "MST7"}, + {"America/Cuiaba", "<-04>4"}, + {"America/Curacao", "AST4"}, + {"America/Danmarkshavn", "GMT0"}, + {"America/Dawson", "MST7"}, + {"America/Dawson_Creek", "MST7"}, + {"America/Denver", "MST7MDT,M3.2.0,M11.1.0"}, + {"America/Detroit", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Dominica", "AST4"}, + {"America/Edmonton", "MST7MDT,M3.2.0,M11.1.0"}, + {"America/Eirunepe", "<-05>5"}, + {"America/El_Salvador", "CST6"}, + {"America/Fortaleza", "<-03>3"}, + {"America/Fort_Nelson", "MST7"}, + {"America/Glace_Bay", "AST4ADT,M3.2.0,M11.1.0"}, + {"America/Godthab", "<-02>2<-01>,M3.5.0/-1,M10.5.0/0"}, + {"America/Goose_Bay", "AST4ADT,M3.2.0,M11.1.0"}, + {"America/Grand_Turk", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Grenada", "AST4"}, + {"America/Guadeloupe", "AST4"}, + {"America/Guatemala", "CST6"}, + {"America/Guayaquil", "<-05>5"}, + {"America/Guyana", "<-04>4"}, + {"America/Halifax", "AST4ADT,M3.2.0,M11.1.0"}, + {"America/Havana", "CST5CDT,M3.2.0/0,M11.1.0/1"}, + {"America/Hermosillo", "MST7"}, + {"America/Indiana/Indianapolis", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Knox", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Marengo", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Petersburg", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Tell_City", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Vevay", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Vincennes", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Indiana/Winamac", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Inuvik", "MST7MDT,M3.2.0,M11.1.0"}, + {"America/Iqaluit", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Jamaica", "EST5"}, + {"America/Juneau", "AKST9AKDT,M3.2.0,M11.1.0"}, + {"America/Kentucky/Louisville", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Kentucky/Monticello", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Kralendijk", "AST4"}, + {"America/La_Paz", "<-04>4"}, + {"America/Lima", "<-05>5"}, + {"America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0"}, + {"America/Lower_Princes", "AST4"}, + {"America/Maceio", "<-03>3"}, + {"America/Managua", "CST6"}, + {"America/Manaus", "<-04>4"}, + {"America/Marigot", "AST4"}, + {"America/Martinique", "AST4"}, + {"America/Matamoros", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Mazatlan", "MST7"}, + {"America/Menominee", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Merida", "CST6"}, + {"America/Metlakatla", "AKST9AKDT,M3.2.0,M11.1.0"}, + {"America/Mexico_City", "CST6"}, + {"America/Miquelon", "<-03>3<-02>,M3.2.0,M11.1.0"}, + {"America/Moncton", "AST4ADT,M3.2.0,M11.1.0"}, + {"America/Monterrey", "CST6"}, + {"America/Montevideo", "<-03>3"}, + {"America/Montreal", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Montserrat", "AST4"}, + {"America/Nassau", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/New_York", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Nipigon", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Nome", "AKST9AKDT,M3.2.0,M11.1.0"}, + {"America/Noronha", "<-02>2"}, + {"America/North_Dakota/Beulah", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/North_Dakota/Center", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/North_Dakota/New_Salem", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Ojinaga", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Panama", "EST5"}, + {"America/Pangnirtung", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Paramaribo", "<-03>3"}, + {"America/Phoenix", "MST7"}, + {"America/Port-au-Prince", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Port_of_Spain", "AST4"}, + {"America/Porto_Velho", "<-04>4"}, + {"America/Puerto_Rico", "AST4"}, + {"America/Punta_Arenas", "<-03>3"}, + {"America/Rainy_River", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Rankin_Inlet", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Recife", "<-03>3"}, + {"America/Regina", "CST6"}, + {"America/Resolute", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Rio_Branco", "<-05>5"}, + {"America/Santarem", "<-03>3"}, + {"America/Santiago", "<-04>4<-03>,M9.1.6/24,M4.1.6/24"}, + {"America/Santo_Domingo", "AST4"}, + {"America/Sao_Paulo", "<-03>3"}, + {"America/Scoresbysund", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"}, + {"America/Sitka", "AKST9AKDT,M3.2.0,M11.1.0"}, + {"America/St_Barthelemy", "AST4"}, + {"America/St_Johns", "NST3:30NDT,M3.2.0,M11.1.0"}, + {"America/St_Kitts", "AST4"}, + {"America/St_Lucia", "AST4"}, + {"America/St_Thomas", "AST4"}, + {"America/St_Vincent", "AST4"}, + {"America/Swift_Current", "CST6"}, + {"America/Tegucigalpa", "CST6"}, + {"America/Thule", "AST4ADT,M3.2.0,M11.1.0"}, + {"America/Thunder_Bay", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Tijuana", "PST8PDT,M3.2.0,M11.1.0"}, + {"America/Toronto", "EST5EDT,M3.2.0,M11.1.0"}, + {"America/Tortola", "AST4"}, + {"America/Vancouver", "PST8PDT,M3.2.0,M11.1.0"}, + {"America/Whitehorse", "MST7"}, + {"America/Winnipeg", "CST6CDT,M3.2.0,M11.1.0"}, + {"America/Yakutat", "AKST9AKDT,M3.2.0,M11.1.0"}, + {"America/Yellowknife", "MST7MDT,M3.2.0,M11.1.0"}, + {"Antarctica/Casey", "<+11>-11"}, + {"Antarctica/Davis", "<+07>-7"}, + {"Antarctica/DumontDUrville", "<+10>-10"}, + {"Antarctica/Macquarie", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, + {"Antarctica/Mawson", "<+05>-5"}, + {"Antarctica/McMurdo", "NZST-12NZDT,M9.5.0,M4.1.0/3"}, + {"Antarctica/Palmer", "<-03>3"}, + {"Antarctica/Rothera", "<-03>3"}, + {"Antarctica/Syowa", "<+03>-3"}, + {"Antarctica/Troll", "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3"}, + {"Antarctica/Vostok", "<+06>-6"}, + {"Arctic/Longyearbyen", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Asia/Aden", "<+03>-3"}, + {"Asia/Almaty", "<+06>-6"}, + {"Asia/Amman", "<+03>-3"}, + {"Asia/Anadyr", "<+12>-12"}, + {"Asia/Aqtau", "<+05>-5"}, + {"Asia/Aqtobe", "<+05>-5"}, + {"Asia/Ashgabat", "<+05>-5"}, + {"Asia/Atyrau", "<+05>-5"}, + {"Asia/Baghdad", "<+03>-3"}, + {"Asia/Bahrain", "<+03>-3"}, + {"Asia/Baku", "<+04>-4"}, + {"Asia/Bangkok", "<+07>-7"}, + {"Asia/Barnaul", "<+07>-7"}, + {"Asia/Beirut", "EET-2EEST,M3.5.0/0,M10.5.0/0"}, + {"Asia/Bishkek", "<+06>-6"}, + {"Asia/Brunei", "<+08>-8"}, + {"Asia/Chita", "<+09>-9"}, + {"Asia/Choibalsan", "<+08>-8"}, + {"Asia/Colombo", "<+0530>-5:30"}, + {"Asia/Damascus", "<+03>-3"}, + {"Asia/Dhaka", "<+06>-6"}, + {"Asia/Dili", "<+09>-9"}, + {"Asia/Dubai", "<+04>-4"}, + {"Asia/Dushanbe", "<+05>-5"}, + {"Asia/Famagusta", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Asia/Gaza", "EET-2EEST,M3.4.4/50,M10.4.4/50"}, + {"Asia/Hebron", "EET-2EEST,M3.4.4/50,M10.4.4/50"}, + {"Asia/Ho_Chi_Minh", "<+07>-7"}, + {"Asia/Hong_Kong", "HKT-8"}, + {"Asia/Hovd", "<+07>-7"}, + {"Asia/Irkutsk", "<+08>-8"}, + {"Asia/Jakarta", "WIB-7"}, + {"Asia/Jayapura", "WIT-9"}, + {"Asia/Jerusalem", "IST-2IDT,M3.4.4/26,M10.5.0"}, + {"Asia/Kabul", "<+0430>-4:30"}, + {"Asia/Kamchatka", "<+12>-12"}, + {"Asia/Karachi", "PKT-5"}, + {"Asia/Kathmandu", "<+0545>-5:45"}, + {"Asia/Khandyga", "<+09>-9"}, + {"Asia/Kolkata", "IST-5:30"}, + {"Asia/Krasnoyarsk", "<+07>-7"}, + {"Asia/Kuala_Lumpur", "<+08>-8"}, + {"Asia/Kuching", "<+08>-8"}, + {"Asia/Kuwait", "<+03>-3"}, + {"Asia/Macau", "CST-8"}, + {"Asia/Magadan", "<+11>-11"}, + {"Asia/Makassar", "WITA-8"}, + {"Asia/Manila", "PST-8"}, + {"Asia/Muscat", "<+04>-4"}, + {"Asia/Nicosia", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Asia/Novokuznetsk", "<+07>-7"}, + {"Asia/Novosibirsk", "<+07>-7"}, + {"Asia/Omsk", "<+06>-6"}, + {"Asia/Oral", "<+05>-5"}, + {"Asia/Phnom_Penh", "<+07>-7"}, + {"Asia/Pontianak", "WIB-7"}, + {"Asia/Pyongyang", "KST-9"}, + {"Asia/Qatar", "<+03>-3"}, + {"Asia/Qyzylorda", "<+05>-5"}, + {"Asia/Riyadh", "<+03>-3"}, + {"Asia/Sakhalin", "<+11>-11"}, + {"Asia/Samarkand", "<+05>-5"}, + {"Asia/Seoul", "KST-9"}, + {"Asia/Shanghai", "CST-8"}, + {"Asia/Singapore", "<+08>-8"}, + {"Asia/Srednekolymsk", "<+11>-11"}, + {"Asia/Taipei", "CST-8"}, + {"Asia/Tashkent", "<+05>-5"}, + {"Asia/Tbilisi", "<+04>-4"}, + {"Asia/Tehran", "<+0330>-3:30"}, + {"Asia/Thimphu", "<+06>-6"}, + {"Asia/Tokyo", "JST-9"}, + {"Asia/Tomsk", "<+07>-7"}, + {"Asia/Ulaanbaatar", "<+08>-8"}, + {"Asia/Urumqi", "<+06>-6"}, + {"Asia/Ust-Nera", "<+10>-10"}, + {"Asia/Vientiane", "<+07>-7"}, + {"Asia/Vladivostok", "<+10>-10"}, + {"Asia/Yakutsk", "<+09>-9"}, + {"Asia/Yangon", "<+0630>-6:30"}, + {"Asia/Yekaterinburg", "<+05>-5"}, + {"Asia/Yerevan", "<+04>-4"}, + {"Atlantic/Azores", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"}, + {"Atlantic/Bermuda", "AST4ADT,M3.2.0,M11.1.0"}, + {"Atlantic/Canary", "WET0WEST,M3.5.0/1,M10.5.0"}, + {"Atlantic/Cape_Verde", "<-01>1"}, + {"Atlantic/Faroe", "WET0WEST,M3.5.0/1,M10.5.0"}, + {"Atlantic/Madeira", "WET0WEST,M3.5.0/1,M10.5.0"}, + {"Atlantic/Reykjavik", "GMT0"}, + {"Atlantic/South_Georgia", "<-02>2"}, + {"Atlantic/Stanley", "<-03>3"}, + {"Atlantic/St_Helena", "GMT0"}, + {"Australia/Adelaide", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"}, + {"Australia/Brisbane", "AEST-10"}, + {"Australia/Broken_Hill", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"}, + {"Australia/Currie", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, + {"Australia/Darwin", "ACST-9:30"}, + {"Australia/Eucla", "<+0845>-8:45"}, + {"Australia/Hobart", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, + {"Australia/Lindeman", "AEST-10"}, + {"Australia/Lord_Howe", "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0"}, + {"Australia/Melbourne", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, + {"Australia/Perth", "AWST-8"}, + {"Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3"}, + {"Europe/Amsterdam", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Andorra", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Astrakhan", "<+04>-4"}, + {"Europe/Athens", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Belgrade", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Berlin", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Bratislava", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Brussels", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Bucharest", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Budapest", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Busingen", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Chisinau", "EET-2EEST,M3.5.0,M10.5.0/3"}, + {"Europe/Copenhagen", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Dublin", "IST-1GMT0,M10.5.0,M3.5.0/1"}, + {"Europe/Gibraltar", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Guernsey", "GMT0BST,M3.5.0/1,M10.5.0"}, + {"Europe/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Isle_of_Man", "GMT0BST,M3.5.0/1,M10.5.0"}, + {"Europe/Istanbul", "<+03>-3"}, + {"Europe/Jersey", "GMT0BST,M3.5.0/1,M10.5.0"}, + {"Europe/Kaliningrad", "EET-2"}, + {"Europe/Kiev", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Kirov", "MSK-3"}, + {"Europe/Lisbon", "WET0WEST,M3.5.0/1,M10.5.0"}, + {"Europe/Ljubljana", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/London", "GMT0BST,M3.5.0/1,M10.5.0"}, + {"Europe/Luxembourg", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Madrid", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Malta", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Mariehamn", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Minsk", "<+03>-3"}, + {"Europe/Monaco", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Moscow", "MSK-3"}, + {"Europe/Oslo", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Paris", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Podgorica", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Prague", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Riga", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Rome", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Samara", "<+04>-4"}, + {"Europe/San_Marino", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Sarajevo", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Saratov", "<+04>-4"}, + {"Europe/Simferopol", "MSK-3"}, + {"Europe/Skopje", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Sofia", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Stockholm", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Tallinn", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Tirane", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Ulyanovsk", "<+04>-4"}, + {"Europe/Uzhgorod", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Vaduz", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Vatican", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Vienna", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Vilnius", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Volgograd", "MSK-3"}, + {"Europe/Warsaw", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Zagreb", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Europe/Zaporozhye", "EET-2EEST,M3.5.0/3,M10.5.0/4"}, + {"Europe/Zurich", "CET-1CEST,M3.5.0,M10.5.0/3"}, + {"Indian/Antananarivo", "EAT-3"}, + {"Indian/Chagos", "<+06>-6"}, + {"Indian/Christmas", "<+07>-7"}, + {"Indian/Cocos", "<+0630>-6:30"}, + {"Indian/Comoro", "EAT-3"}, + {"Indian/Kerguelen", "<+05>-5"}, + {"Indian/Mahe", "<+04>-4"}, + {"Indian/Maldives", "<+05>-5"}, + {"Indian/Mauritius", "<+04>-4"}, + {"Indian/Mayotte", "EAT-3"}, + {"Indian/Reunion", "<+04>-4"}, + {"Pacific/Apia", "<+13>-13"}, + {"Pacific/Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3"}, + {"Pacific/Bougainville", "<+11>-11"}, + {"Pacific/Chatham", "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45"}, + {"Pacific/Chuuk", "<+10>-10"}, + {"Pacific/Easter", "<-06>6<-05>,M9.1.6/22,M4.1.6/22"}, + {"Pacific/Efate", "<+11>-11"}, + {"Pacific/Enderbury", "<+13>-13"}, + {"Pacific/Fakaofo", "<+13>-13"}, + {"Pacific/Fiji", "<+12>-12"}, + {"Pacific/Funafuti", "<+12>-12"}, + {"Pacific/Galapagos", "<-06>6"}, + {"Pacific/Gambier", "<-09>9"}, + {"Pacific/Guadalcanal", "<+11>-11"}, + {"Pacific/Guam", "ChST-10"}, + {"Pacific/Honolulu", "HST10"}, + {"Pacific/Kiritimati", "<+14>-14"}, + {"Pacific/Kosrae", "<+11>-11"}, + {"Pacific/Kwajalein", "<+12>-12"}, + {"Pacific/Majuro", "<+12>-12"}, + {"Pacific/Marquesas", "<-0930>9:30"}, + {"Pacific/Midway", "SST11"}, + {"Pacific/Nauru", "<+12>-12"}, + {"Pacific/Niue", "<-11>11"}, + {"Pacific/Norfolk", "<+11>-11<+12>,M10.1.0,M4.1.0/3"}, + {"Pacific/Noumea", "<+11>-11"}, + {"Pacific/Pago_Pago", "SST11"}, + {"Pacific/Palau", "<+09>-9"}, + {"Pacific/Pitcairn", "<-08>8"}, + {"Pacific/Pohnpei", "<+11>-11"}, + {"Pacific/Port_Moresby", "<+10>-10"}, + {"Pacific/Rarotonga", "<-10>10"}, + {"Pacific/Saipan", "ChST-10"}, + {"Pacific/Tahiti", "<-10>10"}, + {"Pacific/Tarawa", "<+12>-12"}, + {"Pacific/Tongatapu", "<+13>-13"}, + {"Pacific/Wake", "<+12>-12"}, + {"Pacific/Wallis", "<+12>-12"} + }; + /** - * @brief Retrieve TZ from POSIX TZ name + * @brief Retrieve POSIX TZ string from IANA timezone name * - * @param name timezone name - * @return timezone TZ format, NULL if don't found + * @details Searches the posix_tz_db array for a matching timezone name and returns the corresponding POSIX TZ string. + * If not found, returns "UTC". + * + * @param name Timezone name (IANA format, e.g., "Europe/Madrid") + * @return const char* POSIX TZ string, or "UTC" if not found */ const char* getPosixTZ(const char* name) { - int arraySize = sizeof(posix_tz_db) / sizeof(posix_tz_db[0]); + int arraySize = sizeof(posix_tz_db) / sizeof(posix_tz_db[0]); - for (int i = 0; i < arraySize; i++) - { - if (strcmp(posix_tz_db[i].name, name) == 0) - return posix_tz_db[i].posix_str; - } - return "UTC"; + for (int i = 0; i < arraySize; i++) + { + if (strcmp(posix_tz_db[i].name, name) == 0) + return posix_tz_db[i].posix_str; + } + return "UTC"; } \ No newline at end of file diff --git a/lib/webfile/src/webpage.h b/lib/webfile/src/webpage.h index 0eadd849..721b6e81 100644 --- a/lib/webfile/src/webpage.h +++ b/lib/webfile/src/webpage.h @@ -2,8 +2,8 @@ * @file webpage.h * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Web file server page - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ diff --git a/lib/webfile/src/webserver.h b/lib/webfile/src/webserver.h index 6b4eda14..4792de87 100644 --- a/lib/webfile/src/webserver.h +++ b/lib/webfile/src/webserver.h @@ -2,8 +2,8 @@ * @file webserver.h * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief Web file server functions - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include "storage.hpp" @@ -17,220 +17,242 @@ #include #include -/** - * @brief Current directory - * - */ -String oldDir; -String newDir; -String currentDir; +String oldDir; /**< Previous directory */ +String newDir; /**< Target/new directory */ +String currentDir; /**< Current directory */ -String createDir; -uint8_t nextSlash = 0; +String createDir; /**< Directory to create */ +uint8_t nextSlash = 0; /**< Index of next slash for parsing paths */ -bool updateList = true; -bool deleteDir = false; -String deletePath = ""; +bool updateList = true; /**< Flag to update file/directory list */ +bool deleteDir = false; /**< Flag to delete a directory */ +String deletePath = ""; /**< Path to directory/file to delete */ -const int FILES_PER_PAGE = 10; +const int FILES_PER_PAGE = 10; /**< Number of files per page (for pagination) */ extern Storage storage; static const char* TAG = "Webserver"; /** - * @brief File directory cache + * @brief File directory cache entry * + * @details This structure represents an entry in a file directory cache, + * storing the file or directory name, a flag indicating if it is a directory, + * and its size in bytes. */ struct FileEntry { - String name; - bool isDirectory; - size_t size; + String name; /**< Name of the file or directory */ + bool isDirectory; /**< True if entry is a directory, false if file */ + size_t size; /**< Size of the file in bytes (0 for directories) */ }; -std::vector fileCache; +std::vector fileCache; /**< Vector containing file/directory cache entries */ /** * @brief Web Server Declaration * + * @details Declares the main AsyncWebServer instance, a server-sent events (SSE) event source for "/eventRefresh", + * and sets the device hostname. */ -static AsyncWebServer server(80); -static AsyncEventSource eventRefresh("/eventRefresh"); -static const char* hostname = "icenav"; - +static AsyncWebServer server(80); /**< HTTP server running on port 80 */ +static AsyncEventSource eventRefresh("/eventRefresh"); /**< Server-Sent Events endpoint for refresh events */ +static const char* hostname = "icenav"; /**< Device hostname */ /** * @brief Convert bytes to Human Readable Size * - * @param bytes - * @return String + * @details Converts a byte count to a human-readable string representation (B, KB, MB, GB). + * + * @param bytes Number of bytes + * @return String Human-readable representation of the size */ String humanReadableSize(uint64_t bytes) { - if (bytes < 1024) - return String(bytes) + " B"; - else if (bytes < (1024 * 1024)) - return String(bytes / 1024.0) + " KB"; - else if (bytes < (1024 * 1024 * 1024)) - return String(bytes / (1024.0 * 1024.0)) + " MB"; - else - return String(bytes / (1024.0 * 1024.0 * 1024.0)) + " GB"; + if (bytes < 1024) + return String(bytes) + " B"; + else if (bytes < (1024 * 1024)) + return String(bytes / 1024.0) + " KB"; + else if (bytes < (1024 * 1024 * 1024)) + return String(bytes / (1024.0 * 1024.0)) + " MB"; + else + return String(bytes / (1024.0 * 1024.0 * 1024.0)) + " GB"; } /** - * @brief Extract position of numeric part of string + * @brief Extract numeric part from string starting at position * - * @param String - * @return pos + * @details Extracts an integer value from the given string starting at the + * specified position. Updates the position to the character after + * the numeric part. + * + * @param str Input string to parse + * @param pos Reference to the starting position; updated after extraction + * @return int Extracted integer value */ int extractNumber(const String& str, int& pos) { - int num = 0; - while (pos < str.length() && isdigit(str[pos])) - { - num = num * 10 + (str[pos] - '0'); - pos++; - } - return num; + int num = 0; + while (pos < str.length() && isdigit(str[pos])) + { + num = num * 10 + (str[pos] - '0'); + pos++; + } + return num; } /** - * @brief Compare alphanumerical two strings + * @brief Compare two strings using natural (alphanumeric) order + * + * @details Compares two strings by segments, so that numeric parts are compared as numbers, + * and character parts are compared lexicographically (case-insensitive). * - * @param a -> First string - * @param b -> Second string - * @return true/false + * @param a First string to compare + * @param b Second string to compare + * @return true if a < b in natural order, false otherwise */ bool naturalCompare(const String& a, const String& b) { - int i = 0, j = 0; - while (i < a.length() && j < b.length()) - { - if (isdigit(a[i]) && isdigit(b[j])) - { - int numA = extractNumber(a, i); - int numB = extractNumber(b, j); - if (numA != numB) - return numA < numB; - } - else - { - if (tolower(a[i]) != tolower(b[j])) - return tolower(a[i]) < tolower(b[j]); - i++; - j++; - } - } - - return a.length() < b.length(); + int i = 0, j = 0; + while (i < a.length() && j < b.length()) + { + if (isdigit(a[i]) && isdigit(b[j])) + { + int numA = extractNumber(a, i); + int numB = extractNumber(b, j); + if (numA != numB) + return numA < numB; + } + else + { + if (tolower(a[i]) != tolower(b[j])) + return tolower(a[i]) < tolower(b[j]); + i++; + j++; + } + } + + return a.length() < b.length(); } /** * @brief Compare File Cache entries * - * @param a -> File Cache entries - * @param b -> File Cache entries - * @return true/false + * @details Compares two FileEntry objects for sorting. Directories are ordered before files. + * If both are the same type, performs a natural alphanumeric comparison on the names. + * + * @param a First FileEntry + * @param b Second FileEntry + * @return true if a should appear before b, false otherwise */ bool compareFileEntries(const FileEntry& a, const FileEntry& b) { - if (a.isDirectory != b.isDirectory) - return a.isDirectory > b.isDirectory; + if (a.isDirectory != b.isDirectory) + return a.isDirectory > b.isDirectory; - return naturalCompare(a.name, b.name); + return naturalCompare(a.name, b.name); } /** * @brief Sort File Cache entries * + * @details Sorts the global fileCache vector, places directories before files and sorts names in natural alphanumeric order. */ void sortFileCache() { - std::sort(fileCache.begin(), fileCache.end(), compareFileEntries); + std::sort(fileCache.begin(), fileCache.end(), compareFileEntries); } /** * @brief Cache file entries in directory * - * @param dir + * @details Reads the contents of the specified directory, populates the global fileCache vector + * with FileEntry objects for each file and subdirectory. For files, the size is determined. + * For directories, size is set to 0. The fileCache is sorted after reading. + * + * @param dir Directory path (relative to /sdcard) */ void cacheDirectoryContent(const String& dir) { - fileCache.clear(); - - String fullDir = "/sdcard" + dir; - - DIR* dp = opendir(fullDir.c_str()); - if (dp != nullptr) - { - struct dirent* ep; - while ((ep = readdir(dp))) - { - FileEntry entry; - entry.name = String(ep->d_name); - entry.isDirectory = (ep->d_type == DT_DIR); - - if (!entry.isDirectory) - { - String filePath = fullDir + "/" + entry.name; - FILE* file = fopen(filePath.c_str(), "r"); - if (file) - { - fseek(file, 0, SEEK_END); - entry.size = ftell(file); - fclose(file); - } - } - else - entry.size = 0; - - fileCache.push_back(entry); - - esp_task_wdt_reset(); - } - closedir(dp); - } - - currentDir = dir; - - sortFileCache(); + fileCache.clear(); + + String fullDir = "/sdcard" + dir; + + DIR* dp = opendir(fullDir.c_str()); + if (dp != nullptr) + { + struct dirent* ep; + while ((ep = readdir(dp))) + { + FileEntry entry; + entry.name = String(ep->d_name); + entry.isDirectory = (ep->d_type == DT_DIR); + + if (!entry.isDirectory) + { + String filePath = fullDir + "/" + entry.name; + FILE* file = fopen(filePath.c_str(), "r"); + if (file) + { + fseek(file, 0, SEEK_END); + entry.size = ftell(file); + fclose(file); + } + } + else + entry.size = 0; + + fileCache.push_back(entry); + + esp_task_wdt_reset(); + } + closedir(dp); + } + + currentDir = dir; + + sortFileCache(); } /** * @brief Manage Web not found error * - * @param request + * @details Handles HTTP 404 (Not Found) errors for the web server. + * + * @param request Pointer to the AsyncWebServerRequest containing the HTTP request data */ void webNotFound(AsyncWebServerRequest *request) { - String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url(); - log_i("%s", logMessage.c_str()); - request->send(404, "text/plain", "Not found"); + String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url(); + log_i("%s", logMessage.c_str()); + request->send(404, "text/plain", "Not found"); } /** * @brief Replace HTML Vars with values * - * @param var - * @return String + * @details Substitutes specific variable names with their corresponding runtime values for HTML templates. + * + * @param var Variable name to substitute (e.g., "FIRMWARE", "FREEFS") + * @return String Value to replace the variable with */ String webParser(const String &var) { - SDCardInfo info = storage.getSDCardInfo(); - - if (var == "FIRMWARE") - return String(VERSION) + " - Rev: " + String(REVISION); - else if (var == "FREEFS") - return info.free_space.c_str(); - else if (var == "USEDFS") - return info.used_space.c_str(); - else if (var == "TOTALFS") - return info.total_space.c_str(); - else if (var == "TYPEFS") - return info.card_type.c_str(); - else - return ""; + SDCardInfo info = storage.getSDCardInfo(); + + if (var == "FIRMWARE") + return String(VERSION) + " - Rev: " + String(REVISION); + else if (var == "FREEFS") + return info.free_space.c_str(); + else if (var == "USEDFS") + return info.used_space.c_str(); + else if (var == "TOTALFS") + return info.total_space.c_str(); + else if (var == "TYPEFS") + return info.card_type.c_str(); + else + return ""; } /** @@ -239,495 +261,511 @@ String webParser(const String &var) */ void rebootESP() { - log_i("Rebooting ESP32: "); - ESP.restart(); + log_i("Rebooting ESP32: "); + ESP.restart(); } /** * @brief List Files in Web Page * - * @param ishtml - * @param page - * @return String + * @details Generates a paginated list of files and directories from the file cache, either as HTML or plain text. + * In HTML mode, displays a table with navigation buttons and options to download or delete files/directories. + * In plain text mode, lists file names and their sizes. + * + * @param ishtml If true, output is formatted as HTML; otherwise, as plain text. + * @param page Page number for pagination (default: 0). + * @return String The formatted file list. */ String listFiles(bool ishtml, int page = 0) { - String returnText = ""; - - int startIdx = page * FILES_PER_PAGE; - int endIdx = startIdx + FILES_PER_PAGE; - - if (ishtml) - { - returnText += "
"; - if (oldDir != "/") - { - returnText += ""; - returnText += ""; - } - } - - for (int i = startIdx; i < endIdx && i < fileCache.size(); ++i) - { - FileEntry& entry = fileCache[i]; - - if (ishtml) - { - returnText += ""; - returnText += ""; - returnText += ""; - } - else - { - returnText += " " + entry.name; - returnText += ""; - returnText += ""; - returnText += ""; - } - returnText += ""; - } - else - { - returnText += "File: " + entry.name + " Size: " + humanReadableSize(entry.size) + "\n"; - } - } - - if (ishtml) - { - returnText += "
NameSize
"; - returnText += " .."; - returnText += "dir
"; - if (entry.isDirectory) - { - returnText += " " + entry.name + ""; - returnText += "dir" + humanReadableSize(entry.size) + "

"; - - returnText += ""; - if (page > 0) - { - returnText += ""; - returnText += ""; - } - - returnText += " Page " + String(page + 1) + "/" + String((fileCache.size() / 10)+1) + " "; - - if (fileCache.size() > endIdx) - { - returnText += ""; - returnText += ""; - } - - returnText += "

"; - } - - return returnText; + String returnText = ""; + + int startIdx = page * FILES_PER_PAGE; + int endIdx = startIdx + FILES_PER_PAGE; + + if (ishtml) + { + returnText += "
"; + if (oldDir != "/") + { + returnText += ""; + returnText += ""; + } + } + + for (int i = startIdx; i < endIdx && i < fileCache.size(); ++i) + { + FileEntry& entry = fileCache[i]; + + if (ishtml) + { + returnText += ""; + returnText += ""; + returnText += ""; + } + else + { + returnText += " " + entry.name; + returnText += ""; + returnText += ""; + returnText += ""; + } + returnText += ""; + } + else + { + returnText += "File: " + entry.name + " Size: " + humanReadableSize(entry.size) + "\n"; + } + } + + if (ishtml) + { + returnText += "
NameSize
"; + returnText += " .."; + returnText += "dir
"; + if (entry.isDirectory) + { + returnText += " " + entry.name + ""; + returnText += "dir" + humanReadableSize(entry.size) + "

"; + + returnText += ""; + if (page > 0) + { + returnText += ""; + returnText += ""; + } + + returnText += " Page " + String(page + 1) + "/" + String((fileCache.size() / 10)+1) + " "; + + if (fileCache.size() > endIdx) + { + returnText += ""; + returnText += ""; + } + + returnText += "

"; + } + + return returnText; } /** * @brief Create directories if needed for upload * - * @param filepath -> Full file path - * @return true/false if successful + * @details Ensures that all directories in the provided file path exist by creating each + * missing directory in the path. Returns true if all directories are created/exist, + * false if any creation fails. + * + * @param filepath Full file path (relative to storage root) + * @return true if successful, false otherwise */ bool createDirectories(String filepath) { - uint8_t lastSlash = 0; - while (true) - { - nextSlash = filepath.indexOf('/', lastSlash + 1); - String dir = filepath.substring(0, nextSlash); - - String newDir = "/sdcard" + oldDir + "/" + dir; - if (!storage.exists(newDir.c_str())) - { - if (!storage.mkdir(newDir.c_str())) - { - ESP_LOGE(TAG, "Directory %s creation error", newDir.c_str()); - return false; - } - ESP_LOGI(TAG, "Directory %s created",newDir.c_str()); - } - if (nextSlash == 255) break; - lastSlash = nextSlash; - - esp_task_wdt_reset(); - } - return true; + uint8_t lastSlash = 0; + while (true) + { + nextSlash = filepath.indexOf('/', lastSlash + 1); + String dir = filepath.substring(0, nextSlash); + + String newDir = "/sdcard" + oldDir + "/" + dir; + if (!storage.exists(newDir.c_str())) + { + if (!storage.mkdir(newDir.c_str())) + { + ESP_LOGE(TAG, "Directory %s creation error", newDir.c_str()); + return false; + } + ESP_LOGI(TAG, "Directory %s created",newDir.c_str()); + } + if (nextSlash == 255) break; + lastSlash = nextSlash; + + esp_task_wdt_reset(); + } + return true; } /** * @brief Upload file handle * - * @param request - * @param filename - * @param index - * @param data - * @param len + * @details Handles file upload requests by writing the uploaded data in chunks to storage. + * Creates necessary directories if required, opens and writes to the file, and handles errors. + * + * @param request Pointer to the AsyncWebServerRequest representing the upload + * @param filename Name of the file being uploaded (may include path) + * @param index Current index of the upload data chunk + * @param data Pointer to the chunk data buffer + * @param len Length of the current chunk + * @param final True if this is the last chunk of the file */ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { - waitScreenRefresh = true; - - static FILE *file = nullptr; - - uint8_t lastSlashIndex = filename.lastIndexOf("/"); - - if (lastSlashIndex != 255) - { - String path = filename.substring(0, lastSlashIndex); - if (createDir != path) - { - - if (!createDirectories(path)) - ESP_LOGE(TAG, "Directory creation error"); - createDir = path; - } - } - - if (!index) - { - String fullPath = "/sdcard" + oldDir + "/" + filename; - file = storage.open(fullPath.c_str(), "w"); - if (!file) - { - ESP_LOGE(TAG, "Failed to open file for writing: %s", fullPath.c_str()); - request->send(500, "text/plain", "Failed to open file for writing"); - return; - } - ESP_LOGI(TAG, "Started writing file: %s", fullPath.c_str()); - } - - if (file) - { - if (fwrite(data, 1, len, file) != len) - { - ESP_LOGE(TAG, "Failed to write data to file"); - request->send(500, "text/plain", "Failed to write data to file"); - storage.close(file); - file = nullptr; - return; - } - } - - if (final) - { - ESP_LOGI(TAG, "Finished writing file"); - storage.close(file); - file = nullptr; - waitScreenRefresh = false; - } + waitScreenRefresh = true; + + static FILE *file = nullptr; + + uint8_t lastSlashIndex = filename.lastIndexOf("/"); + + if (lastSlashIndex != 255) + { + String path = filename.substring(0, lastSlashIndex); + if (createDir != path) + { + if (!createDirectories(path)) + ESP_LOGE(TAG, "Directory creation error"); + createDir = path; + } + } + + if (!index) + { + String fullPath = "/sdcard" + oldDir + "/" + filename; + file = storage.open(fullPath.c_str(), "w"); + if (!file) + { + ESP_LOGE(TAG, "Failed to open file for writing: %s", fullPath.c_str()); + request->send(500, "text/plain", "Failed to open file for writing"); + return; + } + ESP_LOGI(TAG, "Started writing file: %s", fullPath.c_str()); + } + + if (file) + { + if (fwrite(data, 1, len, file) != len) + { + ESP_LOGE(TAG, "Failed to write data to file"); + request->send(500, "text/plain", "Failed to write data to file"); + storage.close(file); + file = nullptr; + return; + } + } + + if (final) + { + ESP_LOGI(TAG, "Finished writing file"); + storage.close(file); + file = nullptr; + waitScreenRefresh = false; + } } /** * @brief Send PNG file from SPIFFS to webpage * - * @param imageFile - * @param request + * @details Reads a PNG image file from SPIFFS, sends it to the client as an HTTP response, + * + * @param imageFile Path to the image file in storage + * @param request Pointer to the AsyncWebServerRequest */ void sendSpiffsImage(const char *imageFile,AsyncWebServerRequest *request) { - FILE *file = storage.open(imageFile,"r"); - - if (file) - { - size_t size = storage.size(imageFile); - - #ifdef BOARD_HAS_PSRAM - uint8_t *buffer = (uint8_t*)ps_malloc(sizeof(uint8_t)*size); - #else - uint8_t *buffer = (uint8_t*)malloc(sizeof(uint8_t)*size); - #endif - - storage.read(file,buffer,size); - storage.close(file); - request->send_P(200,"image/png",buffer,size); - - free(buffer); - } + FILE *file = storage.open(imageFile,"r"); + + if (file) + { + size_t size = storage.size(imageFile); + + #ifdef BOARD_HAS_PSRAM + uint8_t *buffer = (uint8_t*)ps_malloc(sizeof(uint8_t)*size); + #else + uint8_t *buffer = (uint8_t*)malloc(sizeof(uint8_t)*size); + #endif + + storage.read(file,buffer,size); + storage.close(file); + request->send_P(200,"image/png",buffer,size); + + free(buffer); + } } /** - * @brief Delete directory recursive + * @brief Delete directory recursively * - * @param dirPath -> directory path - * @return true/false if successful + * @details Deletes a directory and all its contents (files and subdirectories) recursively. + * Uses a non-recursive approach with stacks to avoid stack overflow on deeply nested structures. + * + * @param dirPath Directory path to delete + * @return true if successful, false otherwise */ bool deleteDirRecursive(const char *dirPath) { - if (!dirPath || strlen(dirPath) == 0) - { - ESP_LOGE(TAG, "Error: Invalid directory path"); - return false; - } - - // Normalize the input path - std::string rootDir(dirPath); - - // Use two stacks: one for processing directories and another for tracking deletable directories - std::stack dirStack; // Stack for directories to process - std::stack deleteStack; // Stack for directories to delete - - dirStack.push(rootDir); - - while (!dirStack.empty()) - { - std::string currentDir = dirStack.top(); - dirStack.pop(); - - ESP_LOGI(TAG, "Processing directory: %s", currentDir.c_str()); - - DIR *dir = opendir(currentDir.c_str()); - if (!dir) - { - ESP_LOGE(TAG, "Error opening directory: %s", currentDir.c_str()); - return false; - } - - bool hasEntries = false; // Track if the directory has any entries - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) - { - // Skip the current and parent directory entries - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - - // Construct the full path for the current entry - char entryPath[PATH_MAX]; - snprintf(entryPath, sizeof(entryPath), "%s/%s", currentDir.c_str(), entry->d_name); - - struct stat entryStat; - if (stat(entryPath, &entryStat) == -1) - { - ESP_LOGE(TAG, "Error getting entry stats for: %s", entryPath); - closedir(dir); - return false; - } - - if (S_ISDIR(entryStat.st_mode)) - { - // Push the subdirectory onto the stack for later processing - ESP_LOGI(TAG, "Found subdirectory: %s", entryPath); - dirStack.push(std::string(entryPath)); - } - else - { - // Delete the file - ESP_LOGI(TAG, "Found file: %s", entryPath); - if (remove(entryPath) != 0) - { - ESP_LOGE(TAG, "Error deleting file: %s", entryPath); - closedir(dir); - return false; - } - ESP_LOGI(TAG, "Deleted file: %s", entryPath); - } - - hasEntries = true; // The directory is not empty - } - - closedir(dir); - - // If the directory is empty, add it to the delete stack - deleteStack.push(currentDir); - } - - // Now delete all directories in reverse order (from deepest to root) - while (!deleteStack.empty()) - { - std::string dirToDelete = deleteStack.top(); - deleteStack.pop(); - - ESP_LOGI(TAG, "Deleting directory: %s", dirToDelete.c_str()); - if (rmdir(dirToDelete.c_str()) != 0) - { - ESP_LOGE(TAG, "Error deleting directory: %s", dirToDelete.c_str()); - return false; - } - ESP_LOGI(TAG, "Deleted directory: %s", dirToDelete.c_str()); - } - - return true; + if (!dirPath || strlen(dirPath) == 0) + { + ESP_LOGE(TAG, "Error: Invalid directory path"); + return false; + } + + // Normalize the input path + std::string rootDir(dirPath); + + // Use two stacks: one for processing directories and another for tracking deletable directories + std::stack dirStack; // Stack for directories to process + std::stack deleteStack; // Stack for directories to delete + + dirStack.push(rootDir); + + while (!dirStack.empty()) + { + std::string currentDir = dirStack.top(); + dirStack.pop(); + + ESP_LOGI(TAG, "Processing directory: %s", currentDir.c_str()); + + DIR *dir = opendir(currentDir.c_str()); + if (!dir) + { + ESP_LOGE(TAG, "Error opening directory: %s", currentDir.c_str()); + return false; + } + + bool hasEntries = false; // Track if the directory has any entries + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + // Skip the current and parent directory entries + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Construct the full path for the current entry + char entryPath[PATH_MAX]; + snprintf(entryPath, sizeof(entryPath), "%s/%s", currentDir.c_str(), entry->d_name); + + struct stat entryStat; + if (stat(entryPath, &entryStat) == -1) + { + ESP_LOGE(TAG, "Error getting entry stats for: %s", entryPath); + closedir(dir); + return false; + } + + if (S_ISDIR(entryStat.st_mode)) + { + // Push the subdirectory onto the stack for later processing + ESP_LOGI(TAG, "Found subdirectory: %s", entryPath); + dirStack.push(std::string(entryPath)); + } + else + { + // Delete the file + ESP_LOGI(TAG, "Found file: %s", entryPath); + if (remove(entryPath) != 0) + { + ESP_LOGE(TAG, "Error deleting file: %s", entryPath); + closedir(dir); + return false; + } + ESP_LOGI(TAG, "Deleted file: %s", entryPath); + } + + hasEntries = true; // The directory is not empty + } + + closedir(dir); + + // If the directory is empty, add it to the delete stack + deleteStack.push(currentDir); + } + + // Now delete all directories in reverse order (from deepest to root) + while (!deleteStack.empty()) + { + std::string dirToDelete = deleteStack.top(); + deleteStack.pop(); + + ESP_LOGI(TAG, "Deleting directory: %s", dirToDelete.c_str()); + if (rmdir(dirToDelete.c_str()) != 0) + { + ESP_LOGE(TAG, "Error deleting directory: %s", dirToDelete.c_str()); + return false; + } + ESP_LOGI(TAG, "Deleted directory: %s", dirToDelete.c_str()); + } + + return true; } - /** * @brief Configure Web Server * + * @details Sets up all routes, event sources, handlers, and static file/image serving for the web server. + * Handles file uploads, downloads, directory navigation, image serving, and server reboot via HTTP endpoints. */ void configureWebServer() { - server.onNotFound(webNotFound); - server.onFileUpload(handleUpload); - server.addHandler(&eventRefresh); - oldDir = ""; - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) - { - String logMessage = "Client:" + request->client()->remoteIP().toString() + +" " + request->url(); - log_i("%s", logMessage.c_str()); - request->send_P(200, "text/html", index_html, webParser); - }); - - - server.on("/logo", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/LOGO_LARGE.png",request); }); - - server.on("/files", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/file.png",request); }); - - server.on("/folder", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/folder.png",request); }); - - server.on("/down", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/download.png", request); }); - - server.on("/up", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/upload.png", request); }); - - server.on("/del", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/delete.png", request); }); - - server.on("/reb", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/reboot.png", request); }); - - server.on("/list", HTTP_GET, [](AsyncWebServerRequest *request) - { sendSpiffsImage("/spiffs/list.png", request); }); - - server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request) - { - String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url(); - request->send(200, "text/html", reboot_html); - log_i("%s",logMessage.c_str()); - rebootESP(); }); - - server.on("/listfiles", HTTP_GET, [](AsyncWebServerRequest *request) - { - String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url(); - log_i("%s", logMessage.c_str()); - - int page = 0; - if (request->hasParam("page")) - { - page = request->getParam("page")->value().toInt(); - } - - if (updateList) - { - esp_task_wdt_reset(); - cacheDirectoryContent(oldDir); - } - - request->send(200, "text/html", listFiles(true, page)); }); - - server.on("/file", HTTP_GET, [](AsyncWebServerRequest *request) - { - if (request->hasParam("name") && request->hasParam("action")) - { - const char *fileName = request->getParam("name")->value().c_str(); - const char *fileAction = request->getParam("action")->value().c_str(); - - String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url() + "?name=" + String(fileName) + "&action=" + String(fileAction); - String path = "/sdcard" + oldDir + "/" + String(fileName); - - FILE* file = storage.open(path.c_str(), "r"); - - log_i("folder %s",path.c_str()); - if (!file) - { - if (strcmp(fileAction, "deldir") == 0) - { - logMessage += " deleted"; - deletePath = path; - deleteDir = true; - request->send(200, "text/plain", "Deleting Folder: " + String(fileName) + " please wait..."); - updateList = true; - } - else - request->send(400, "text/plain", "ERROR: file/ does not exist"); - } - else - { - if (strcmp(fileAction, "download") == 0) - { - logMessage += " downloaded"; - AsyncWebServerResponse *response = request->beginChunkedResponse("application/octet-stream", [file](uint8_t *buffer, size_t maxLen, size_t index) -> size_t - { - size_t bytesRead = storage.read(file, buffer, maxLen); - if (bytesRead == 0) - storage.close(file); - return bytesRead; - }); - response->addHeader("Content-Disposition", "attachment; filename=\"" + path.substring(path.lastIndexOf('/') + 1) + "\""); - request->send(response); - } - // else if (strcmp(fileAction, "deldir") == 0) - // { - // logMessage += " deleted"; - // deletePath = path; - // deleteDir = true; - // request->send(200, "text/plain", "Deleting Folder: " + String(fileName) + " please wait..."); - // updateList = true; - // } - else if (strcmp(fileAction, "delete") == 0) - { - logMessage += " deleted"; - storage.close(file); - storage.remove(path.c_str()); - request->send(200, "text/plain", "Deleted File: " + String(fileName)); - updateList = true; - } - else - { - logMessage += " ERROR: invalid action param supplied"; - request->send(400, "text/plain", "ERROR: invalid action param supplied"); - } - log_i("%s", logMessage.c_str()); - } - } - else - { - request->send(400, "text/plain", "ERROR: name and action params required"); - } }); - - server.on("/changedirectory", HTTP_GET, [](AsyncWebServerRequest *request) - { - if (request->hasParam("dir")) - { - updateList = false; - - newDir = request->getParam("dir")->value(); - log_i("new dir %s", newDir.c_str()); - log_i("old dir %s", oldDir.c_str()); - // UP Directory - if (newDir == "/..") - { - if (oldDir != "/..") - { - oldDir = oldDir.substring(0, oldDir.lastIndexOf("/")); - currentDir = ""; - request->send(200, "text/plain", "Path:" + oldDir ); - } - else - { - request->send(400, "text/plain", "Cannot go up from root directory"); - } - } - else - // DOWN Directory - { - if (oldDir != "/") - oldDir = oldDir + newDir; - else - oldDir = newDir; - currentDir = ""; - request->send(200, "text/plain", "Path:" + oldDir); - } - - cacheDirectoryContent(oldDir); - - } - else - { - request->send(400, "text/plain", "ERROR: dir parameter required"); - } - }); + server.onNotFound(webNotFound); + server.onFileUpload(handleUpload); + server.addHandler(&eventRefresh); + oldDir = ""; + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) + { + String logMessage = "Client:" + request->client()->remoteIP().toString() + +" " + request->url(); + log_i("%s", logMessage.c_str()); + request->send_P(200, "text/html", index_html, webParser); + }); + + + server.on("/logo", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/LOGO_LARGE.png",request); }); + + server.on("/files", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/file.png",request); }); + + server.on("/folder", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/folder.png",request); }); + + server.on("/down", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/download.png", request); }); + + server.on("/up", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/upload.png", request); }); + + server.on("/del", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/delete.png", request); }); + + server.on("/reb", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/reboot.png", request); }); + + server.on("/list", HTTP_GET, [](AsyncWebServerRequest *request) + { sendSpiffsImage("/spiffs/list.png", request); }); + + server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request) + { + String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url(); + request->send(200, "text/html", reboot_html); + log_i("%s",logMessage.c_str()); + rebootESP(); }); + + server.on("/listfiles", HTTP_GET, [](AsyncWebServerRequest *request) + { + String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url(); + log_i("%s", logMessage.c_str()); + + int page = 0; + if (request->hasParam("page")) + { + page = request->getParam("page")->value().toInt(); + } + + if (updateList) + { + esp_task_wdt_reset(); + cacheDirectoryContent(oldDir); + } + request->send(200, "text/html", listFiles(true, page)); }); + + server.on("/file", HTTP_GET, [](AsyncWebServerRequest *request) + { + if (request->hasParam("name") && request->hasParam("action")) + { + const char *fileName = request->getParam("name")->value().c_str(); + const char *fileAction = request->getParam("action")->value().c_str(); + + String logMessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url() + "?name=" + String(fileName) + "&action=" + String(fileAction); + String path = "/sdcard" + oldDir + "/" + String(fileName); + + FILE* file = storage.open(path.c_str(), "r"); + + log_i("folder %s",path.c_str()); + if (!file) + { + if (strcmp(fileAction, "deldir") == 0) + { + logMessage += " deleted"; + deletePath = path; + deleteDir = true; + request->send(200, "text/plain", "Deleting Folder: " + String(fileName) + " please wait..."); + updateList = true; + } + else + request->send(400, "text/plain", "ERROR: file/ does not exist"); + } + else + { + if (strcmp(fileAction, "download") == 0) + { + logMessage += " downloaded"; + AsyncWebServerResponse *response = request->beginChunkedResponse("application/octet-stream", [file](uint8_t *buffer, size_t maxLen, size_t index) -> size_t + { + size_t bytesRead = storage.read(file, buffer, maxLen); + if (bytesRead == 0) + storage.close(file); + return bytesRead; + }); + response->addHeader("Content-Disposition", "attachment; filename=\"" + path.substring(path.lastIndexOf('/') + 1) + "\""); + request->send(response); + } + // else if (strcmp(fileAction, "deldir") == 0) + // { + // logMessage += " deleted"; + // deletePath = path; + // deleteDir = true; + // request->send(200, "text/plain", "Deleting Folder: " + String(fileName) + " please wait..."); + // updateList = true; + // } + else if (strcmp(fileAction, "delete") == 0) + { + logMessage += " deleted"; + storage.close(file); + storage.remove(path.c_str()); + request->send(200, "text/plain", "Deleted File: " + String(fileName)); + updateList = true; + } + else + { + logMessage += " ERROR: invalid action param supplied"; + request->send(400, "text/plain", "ERROR: invalid action param supplied"); + } + log_i("%s", logMessage.c_str()); + } + } + else + { + request->send(400, "text/plain", "ERROR: name and action params required"); + } }); + + server.on("/changedirectory", HTTP_GET, [](AsyncWebServerRequest *request) + { + if (request->hasParam("dir")) + { + updateList = false; + + newDir = request->getParam("dir")->value(); + log_i("new dir %s", newDir.c_str()); + log_i("old dir %s", oldDir.c_str()); + // UP Directory + if (newDir == "/..") + { + if (oldDir != "/..") + { + oldDir = oldDir.substring(0, oldDir.lastIndexOf("/")); + currentDir = ""; + request->send(200, "text/plain", "Path:" + oldDir ); + } + else + { + request->send(400, "text/plain", "Cannot go up from root directory"); + } + } + else + // DOWN Directory + { + if (oldDir != "/") + oldDir = oldDir + newDir; + else + oldDir = newDir; + currentDir = ""; + request->send(200, "text/plain", "Path:" + oldDir); + } + + cacheDirectoryContent(oldDir); + + } + else + { + request->send(400, "text/plain", "ERROR: dir parameter required"); + } + }); } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 36fb48c3..5243e024 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,8 +15,8 @@ default_envs = ICENAV_BOARD [common] platform = espressif32@6.9.0 framework = arduino -version = 0.2.2 -revision = 86 +version = 0.2.3 +revision = 88 monitor_speed = 115200 ;monitor_rts = 0 ;monitor_dtr = 0 @@ -24,8 +24,9 @@ monitor_filters = esp32_exception_decoder extra_scripts = pre:prebuild.py build_flags = - -D LV_CONF_PATH="${PROJECT_DIR}/lib/lvgl/lv_conf.h" - -D CORE_DEBUG_LEVEL=0 + -O3 + -D LV_CONF_PATH="\"${PROJECT_DIR}/lib/lvgl/lv_conf.h\"" + -D CORE_DEBUG_LEVEL=5 -D DISABLE_RADIO=1 -D BAUDRATE=115200 -D DEBUG=1 @@ -40,13 +41,13 @@ build_flags = lib_deps = ; slashdevin/NeoGPS@4.2.9 https://github.com/jgauchia/NeoGPS.git#43c4766 - lvgl/lvgl@9.2.2 + lvgl/lvgl@9.3.0 lovyan03/LovyanGFX@1.2.7 - hpsaturn/EasyPreferences@0.1.3 + hpsaturn/EasyPreferences@0.1.4 hpsaturn/ESP32 Wifi CLI@0.3.4 kubafilinger/AsyncTCP@1.1.1 esphome/ESPAsyncWebServer-esphome@3.3.0 - jpb10/SolarCalculator@2.0.1 + jpb10/SolarCalculator@2.0.2 https://github.com/leethomason/tinyxml2.git [esp32_common] @@ -132,8 +133,8 @@ build_flags = ;-DAT6558D_GPS ;-DHMC5883L ;-DBME280 - ;-DILI9488_XPT2046_SPI - ;-DTFT_BL=33 + -DILI9488_XPT2046_SPI + -DTFT_BL=33 [env:ESP32S3_N16R8] extends = esp32_common diff --git a/src/main.cpp b/src/main.cpp index f94acc7a..67d0551d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,8 +2,8 @@ * @file main.cpp * @author Jordi GauchΓ­a (jgauchia@jgauchia.com) * @brief ICENAV - ESP32 GPS Navigator main code - * @version 0.2.2 - * @date 2025-05 + * @version 0.2.3 + * @date 2025-06 */ #include @@ -25,26 +25,26 @@ #include "tft.hpp" #ifdef HMC5883L -#include "compass.hpp" + #include "compass.hpp" #endif #ifdef QMC5883 -#include "compass.hpp" + #include "compass.hpp" #endif #ifdef IMU_MPU9250 -#include "compass.hpp" + #include "compass.hpp" #endif #ifdef BME280 -#include "bme.hpp" + #include "bme.hpp" #endif #ifdef MPU6050 -#include "imu.hpp" + #include "imu.hpp" #endif -extern xSemaphoreHandle gpsMutex; +extern xSemaphoreHandle gpsMutex; /**< Mutex used to synchronize access to the GPS resource */ #include "webpage.h" #include "webserver.h" @@ -60,163 +60,194 @@ extern Power power; extern Maps mapView; extern Gps gps; #ifdef ENABLE_COMPASS -Compass compass; + Compass compass; #endif +std::vector trackData; /**< Vector for storing track data */ +std::vector turnPoints; /**< Vector for storing turn points */ -std::vector trackData; +#include "navigation.hpp" +NavState navState; - -/** - * @brief Sunrise and Sunset - * - */ -static double transit, sunrise, sunset; +static double transit, sunrise, sunset; /**< Variables to store solar transit, sunrise, and sunset times (in hours or fractional day) */ #include "timezone.c" #include "settings.hpp" #include "lvglSetup.hpp" #include "tasks.hpp" + /** * @brief Calculate Sunrise and Sunset * Must be a global function * + * @details Calls calcSunriseSunset to compute the solar transit, sunrise, and sunset times for + * the current date and GPS location. Adjusts times for UTC offset */ void calculateSun() { - calcSunriseSunset(2000 + fix.dateTime.year, - fix.dateTime.month, - fix.dateTime.date, - gps.gpsData.latitude, - gps.gpsData.longitude, - transit, - sunrise, - sunset); - int hours = (int)sunrise + gps.gpsData.UTC; - int minutes = (int)round(((sunrise + gps.gpsData.UTC) - hours) * 60); - snprintf(gps.gpsData.sunriseHour, 6, "%02d:%02d", hours, minutes); - hours = (int)sunset + gps.gpsData.UTC; - minutes = (int)round(((sunset + gps.gpsData.UTC) - hours) * 60); - snprintf(gps.gpsData.sunsetHour, 6, "%02d:%02d", hours, minutes); - log_i("Sunrise: %s",gps.gpsData.sunriseHour); - log_i("Sunset: %s",gps.gpsData.sunsetHour); + calcSunriseSunset(2000 + fix.dateTime.year, + fix.dateTime.month, + fix.dateTime.date, + gps.gpsData.latitude, + gps.gpsData.longitude, + transit, + sunrise, + sunset); + int hours = (int)sunrise + gps.gpsData.UTC; + int minutes = (int)round(((sunrise + gps.gpsData.UTC) - hours) * 60); + snprintf(gps.gpsData.sunriseHour, 6, "%02d:%02d", hours, minutes); + hours = (int)sunset + gps.gpsData.UTC; + minutes = (int)round(((sunset + gps.gpsData.UTC) - hours) * 60); + snprintf(gps.gpsData.sunsetHour, 6, "%02d:%02d", hours, minutes); + log_i("Sunrise: %s",gps.gpsData.sunriseHour); + log_i("Sunset: %s",gps.gpsData.sunsetHour); } /** - * @brief Setup + * @brief Initialize the ESP32 GPS Navigator system * + * @details Performs complete system initialization including hardware setup, + * peripheral configuration, storage initialization, display setup, + * GPS initialization, LVGL GUI setup, and optional web server configuration. + * Sets up mutexes, GPIO pins, I2C communication, sensors, file systems, + * and launches background tasks for GPS processing and CLI interface. */ void setup() { - gpsMutex = xSemaphoreCreateMutex(); - esp_log_level_set("*", ESP_LOG_DEBUG); - esp_log_level_set("storage", ESP_LOG_DEBUG); - -// Force GPIO0 to internal PullUP during boot (avoid LVGL key read) -#ifdef POWER_SAVE - pinMode(BOARD_BOOT_PIN, INPUT_PULLUP); -#ifdef ICENAV_BOARD - gpio_hold_dis(GPIO_NUM_46); - gpio_hold_dis((gpio_num_t)BOARD_BOOT_PIN); - gpio_deep_sleep_hold_dis(); -#endif -#endif - -#ifdef TDECK_ESP32S3 - pinMode(BOARD_POWERON, OUTPUT); - digitalWrite(BOARD_POWERON, HIGH); - pinMode(GPIO_NUM_16, INPUT); - pinMode(SD_CS, OUTPUT); - pinMode(RADIO_CS_PIN, OUTPUT); - pinMode(TFT_SPI_CS, OUTPUT); - digitalWrite(SD_CS, HIGH); - digitalWrite(RADIO_CS_PIN, HIGH); - digitalWrite(TFT_SPI_CS, HIGH); - pinMode(SPI_MISO, INPUT_PULLUP); -#endif - - Wire.setPins(I2C_SDA_PIN, I2C_SCL_PIN); - Wire.begin(); - -#ifdef BME280 - initBME(); -#endif - -#ifdef ENABLE_COMPASS - compass.init(); -#endif - -#ifdef ENABLE_IMU - initIMU(); -#endif - - storage.initSD(); - storage.initSPIFFS(); - battery.initADC(); - - initTFT(); - createGpxFolders(); - - mapView.initMap(TFT_HEIGHT - 100, TFT_WIDTH, TFT_HEIGHT); - - loadPreferences(); - gps.init(); - initLVGL(); - - // Get init Latitude and Longitude - gps.gpsData.latitude = gps.getLat(); - gps.gpsData.longitude = gps.getLon(); - - initGpsTask(); - -#ifndef DISABLE_CLI - initCLI(); - initCLITask(); -#endif - - if (WiFi.status() == WL_CONNECTED) - { - if (!MDNS.begin(hostname)) - log_e("nDNS init error"); - - log_i("mDNS initialized"); - } - - if (WiFi.status() == WL_CONNECTED && enableWeb) - { - configureWebServer(); - server.begin(); - } - - if (WiFi.getMode() == WIFI_OFF) - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - splashScreen(); - lv_screen_load(searchSatScreen); + gpsMutex = xSemaphoreCreateMutex(); + esp_log_level_set("*", ESP_LOG_DEBUG); + esp_log_level_set("storage", ESP_LOG_DEBUG); + + lutInit = initTrigLUT(); + + // Force GPIO0 to internal PullUP during boot (avoid LVGL key read) + #ifdef POWER_SAVE + pinMode(BOARD_BOOT_PIN, INPUT_PULLUP); + #ifdef ICENAV_BOARD + gpio_hold_dis(GPIO_NUM_46); + gpio_hold_dis((gpio_num_t)BOARD_BOOT_PIN); + gpio_deep_sleep_hold_dis(); + #endif + #endif + + #ifdef TDECK_ESP32S3 + pinMode(BOARD_POWERON, OUTPUT); + digitalWrite(BOARD_POWERON, HIGH); + pinMode(GPIO_NUM_16, INPUT); + pinMode(SD_CS, OUTPUT); + pinMode(RADIO_CS_PIN, OUTPUT); + pinMode(TFT_SPI_CS, OUTPUT); + digitalWrite(SD_CS, HIGH); + digitalWrite(RADIO_CS_PIN, HIGH); + digitalWrite(TFT_SPI_CS, HIGH); + pinMode(SPI_MISO, INPUT_PULLUP); + #endif + + Wire.setPins(I2C_SDA_PIN, I2C_SCL_PIN); + Wire.begin(); + + #ifdef BME280 + initBME(); + #endif + + #ifdef ENABLE_COMPASS + compass.init(); + #endif + + #ifdef ENABLE_IMU + initIMU(); + #endif + + storage.initSD(); + storage.initSPIFFS(); + battery.initADC(); + + initTFT(); + createGpxFolders(); + + mapView.initMap(tft.height() - 27, tft.width()); + + loadPreferences(); + gps.init(); + initLVGL(); + mapView.fillPolygons = mapSet.fillPolygons; + + // Get init Latitude and Longitude + gps.gpsData.latitude = gps.getLat(); + gps.gpsData.longitude = gps.getLon(); + + initGpsTask(); + + #ifndef DISABLE_CLI + initCLI(); + initCLITask(); + #endif + + if (WiFi.status() == WL_CONNECTED) + { + if (!MDNS.begin(hostname)) + log_e("nDNS init error"); + + log_i("mDNS initialized"); + } + + if (WiFi.status() == WL_CONNECTED && enableWeb) + { + configureWebServer(); + server.begin(); + } + + if (WiFi.getMode() == WIFI_OFF) + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + splashScreen(); + lv_screen_load(searchSatScreen); } /** - * @brief Main Loop + * @brief Main application loop * + * @details Continuously processes the LVGL GUI timer handler, manages screen refresh timing, + * handles web server directory deletion operations, and processes GPS navigation + * when track data is loaded. Includes simulation mode for testing navigation + * without actual GPS movement and real-time navigation updates based on GPS speed. */ void loop() { - if (!waitScreenRefresh) - { - lv_timer_handler(); - vTaskDelay(pdMS_TO_TICKS(TASK_SLEEP_PERIOD_MS)); - } - - // Deleting recursive directories in webfile server - if (enableWeb && deleteDir) - { - deleteDir = false; - if (deleteDirRecursive(deletePath.c_str())) - { - updateList = true; - eventRefresh.send("refresh", nullptr, millis()); - eventRefresh.send("Folder deleted", "updateStatus", millis()); - } - } -} + if (!waitScreenRefresh) + { + lv_timer_handler(); + vTaskDelay(pdMS_TO_TICKS(TASK_SLEEP_PERIOD_MS)); + } + + // Deleting recursive directories in webfile server + if (enableWeb && deleteDir) + { + deleteDir = false; + if (deleteDirRecursive(deletePath.c_str())) + { + updateList = true; + eventRefresh.send("refresh", nullptr, millis()); + eventRefresh.send("Folder deleted", "updateStatus", millis()); + } + } + + if (isTrackLoaded) + { + if (navSet.simNavigation) + gps.simFakeGPS(trackData,120,1000); + + if (gps.gpsData.speed !=0) + { + // Use optimized NavConfig for simulation + NavConfig simConfig; + simConfig.searchWindow = 150; // Larger window for simulation + simConfig.offTrackThreshold = 75.0f; // More tolerant for simulation + simConfig.maxBackwardJump = 10; // Allow more backward movement + + updateNavigation(gps.gpsData.latitude, gps.gpsData.longitude, gps.gpsData.heading, gps.gpsData.speed, + trackData, turnPoints, navState, 20, 200, simConfig); + } + } +} \ No newline at end of file diff --git a/tools/mass_copy/README.md b/tools/mass_copy/README.md new file mode 100644 index 00000000..18421b5f --- /dev/null +++ b/tools/mass_copy/README.md @@ -0,0 +1,363 @@ +# Mass File Copy Script - User Manual + +A high-performance bash script optimized for copying millions of small files to external devices using rsync. + +## Overview + +This script solves the common problem of slow file transfers when dealing with millions of small files. Traditional copy methods can take hours, while this optimized approach reduces transfer time to minutes. + +## Features + +- βœ… **No compression issues** - Copies files exactly as they are +- βœ… **Real-time progress monitoring** - See exactly what's being copied +- βœ… **Resumable transfers** - Continue interrupted copies +- βœ… **Optimized for millions of small files** - Much faster than traditional copy methods +- βœ… **Built-in integrity verification** - Sample file verification +- βœ… **No temporary files** - Direct copy without intermediate TAR files +- βœ… **Performance metrics** - Speed and time statistics + +## Performance Comparison + +| Method | 1 Million Small Files | Notes | +|--------|----------------------|-------| +| **rsync script** | **15-30 minutes** | βœ… Recommended | +| TAR + copy | 25-45 minutes | ⚠️ Compression issues | +| cp file-by-file | 60-120 minutes | ❌ Very slow | + +## System Requirements + +### Operating System +- Debian/Ubuntu Linux (tested) +- Other Linux distributions (should work) +- bash shell version 4.0 or higher + +### Hardware Requirements +- Minimum 1GB RAM (more recommended for large datasets) +- Sufficient storage space on destination device +- USB 2.0 or higher port (USB 3.0+ recommended) + +### Permissions +- sudo privileges for device mounting/unmounting +- Read access to source directories +- User should be in `disk` group (recommended) + +## Dependencies + +### Required (Usually Pre-installed) +```bash +# Check if installed +which rsync +which bash +which mount +``` + +### Optional (Recommended) +```bash +# Install for enhanced file verification +sudo apt install coreutils +``` + +### Adding User to Disk Group (Recommended) +```bash +# Add current user to disk group +sudo usermod -a -G disk $USER + +# Verify group membership +groups $USER + +# Log out and back in for changes to take effect +``` + +## Installation + +### Quick Installation +1. Download the script file (`rsync_copy.sh`) +2. Make it executable: + ```bash + chmod +x rsync_copy.sh + ``` +3. Optionally move to system PATH: + ```bash + sudo cp rsync_copy.sh /usr/local/bin/ + ``` + +### Verification +```bash +# Test script help +./rsync_copy.sh --help + +# Check system dependencies +rsync --version +``` + +## Usage + +### Basic Syntax +```bash +./rsync_copy.sh [SOURCE] [DESTINATION_MOUNT] [DEVICE] +``` + +### Parameter Description +- **SOURCE**: Full path to directory containing files to copy +- **DESTINATION_MOUNT**: Mount point where device will be mounted (e.g., `/mnt/backup`) +- **DEVICE**: Device path (e.g., `/dev/sdb1`, `/dev/sdc1`) + +### Identifying Your Device +```bash +# List all storage devices +lsblk + +# Detailed device information +sudo fdisk -l + +# Monitor device connections +dmesg | tail -20 +``` + +Common device patterns: +- USB drives: `/dev/sdb1`, `/dev/sdc1` +- SD cards: `/dev/mmcblk0p1`, `/dev/mmcblk1p1` +- External drives: `/dev/sdd1`, `/dev/sde1` + +## Examples + +### Basic Examples +```bash +# Copy documents to USB drive +./rsync_copy.sh /home/user/documents /mnt/usb /dev/sdb1 + +# Copy photos to SD card +./rsync_copy.sh /home/user/photos /mnt/sd /dev/mmcblk0p1 + +# Copy project files to external drive +./rsync_copy.sh /var/www/html /mnt/backup /dev/sdc1 +``` + +### Advanced Examples +```bash +# Copy with logging +./rsync_copy.sh /source /dest /dev/sdb1 2>&1 | tee transfer.log + +# Copy specific subdirectory +./rsync_copy.sh /home/user/projects/important /mnt/backup /dev/sdb1 + +# Copy system files (as root) +sudo ./rsync_copy.sh /etc /mnt/backup /dev/sdb1 +``` + +## Step-by-Step Process + +### 1. Pre-Transfer Preparation +The script automatically performs: +- Device mounting with optimized settings +- Space verification (source vs. destination) +- File counting and time estimation +- Device write speed testing + +### 2. Transfer Process +- Real-time progress display +- File-by-file transfer status +- Speed monitoring +- Error detection and reporting + +### 3. Post-Transfer Verification +- File count comparison +- Sample integrity checking +- Performance summary +- Safe device unmounting + +## Workflow Overview + +``` +Start β†’ Mount Device β†’ Verify Space β†’ Count Files β†’ +Speed Test β†’ Rsync Transfer β†’ Verification β†’ Unmount β†’ Complete +``` + +## File Organization + +After successful transfer, files are organized as: +``` +/mount/point/backup/ +β”œβ”€β”€ [original directory structure preserved] +β”œβ”€β”€ file1.txt +β”œβ”€β”€ file2.jpg +└── subdirectory/ + β”œβ”€β”€ file3.doc + └── file4.pdf +``` + +## Performance Optimization + +### For Best Performance +- Use USB 3.0+ ports and devices +- Ensure source is on fast storage (SSD preferred) +- Close unnecessary applications during transfer +- Use wired connections for network storage + +### File System Recommendations +- **FAT32**: Good compatibility, 4GB file limit +- **exFAT**: Better for large files, good compatibility +- **ext4**: Best performance on Linux, limited Windows compatibility +- **NTFS**: Good for Windows compatibility + +### Batch Processing Large Datasets +For extremely large datasets (10M+ files): +```bash +# Split into batches +./rsync_copy.sh /source/batch1 /mnt/backup1 /dev/sdb1 +./rsync_copy.sh /source/batch2 /mnt/backup2 /dev/sdc1 +``` + +## Troubleshooting + +### Common Issues and Solutions + +#### Device Not Detected +```bash +# Check device connection +lsblk | grep -E "(sd|mmcblk)" + +# Check USB connections +lsusb + +# Reconnect device and check dmesg +dmesg | tail -10 +``` + +#### Permission Denied +```bash +# Check current user groups +groups + +# Add user to necessary groups +sudo usermod -a -G disk,plugdev $USER + +# Logout and login again +``` + +#### Mount Failures +```bash +# Manually unmount if stuck +sudo umount /dev/sdX1 + +# Check filesystem +sudo fsck.fat -v /dev/sdX1 # For FAT32 +sudo fsck.exfat /dev/sdX1 # For exFAT +``` + +#### Slow Performance +- Check for background processes: `top` or `htop` +- Verify USB port speed: `lsusb -t` +- Monitor I/O: `sudo iotop` +- Check device health: `sudo smartctl -a /dev/sdX` + +#### Transfer Interruption +Simply re-run the same command. rsync will automatically resume from where it left off. + +## Monitoring and Logging + +### Real-time Monitoring +```bash +# In another terminal, monitor progress +watch -n 5 'df -h /mnt/backup' + +# Monitor transfer speed +sudo iotop -p $(pgrep rsync) +``` + +### Logging Options +```bash +# Basic logging +./rsync_copy.sh /source /dest /dev/sdb1 > transfer.log 2>&1 + +# Timestamped logging +./rsync_copy.sh /source /dest /dev/sdb1 2>&1 | ts '[%Y-%m-%d %H:%M:%S]' | tee transfer.log +``` + +## Security Considerations + +### Data Safety +- Script performs read-only operations on source +- No source data is modified or deleted +- Destination device is safely unmounted +- Sample verification ensures data integrity + +### Permission Management +- Requires sudo only for mounting operations +- Preserves original file permissions +- Respects file ownership where possible + +## Integration and Automation + +### Scheduled Backups +```bash +# Edit crontab +crontab -e + +# Add weekly backup (Sundays at 2 AM) +0 2 * * 0 /path/to/rsync_copy.sh /home/user/data /mnt/backup /dev/sdb1 +``` + +### Script Integration +```bash +# Call from other scripts +if ./rsync_copy.sh "$SOURCE" "$DEST" "$DEVICE"; then + echo "Backup successful" + # Additional actions +else + echo "Backup failed" + # Error handling +fi +``` + +## Performance Metrics + +The script provides detailed statistics: +- **Transfer Time**: Total time for complete operation +- **Average Speed**: MB/s throughout transfer +- **Files per Second**: Processing rate +- **Device Speed**: Baseline write performance +- **Verification Results**: Integrity check status + +## Best Practices + +### Before Transfer +1. Test with small dataset first +2. Ensure adequate free space (20% margin recommended) +3. Close unnecessary applications +4. Use high-quality cables and ports + +### During Transfer +1. Avoid disconnecting devices +2. Don't run other intensive I/O operations +3. Monitor for error messages +4. Keep system powered (for laptops) + +### After Transfer +1. Review verification results +2. Test sample files on destination +3. Keep transfer logs for reference +4. Safely store backup devices + +## Support and Maintenance + +### Regular Updates +- Check for script updates periodically +- Update system packages: `sudo apt update && sudo apt upgrade` +- Verify dependency versions + +### Performance Monitoring +- Keep transfer logs for trend analysis +- Monitor device health regularly +- Replace aging storage devices proactively + +## Limitations + +- Requires sudo privileges for mounting +- Limited to devices with standard filesystems +- Not suitable for encrypted devices (without manual setup) +- Performance depends on source and destination device speeds + +--- + +**Note**: Always test with non-critical data first. Ensure proper backups exist before running large-scale transfers. \ No newline at end of file diff --git a/tools/mass_copy/rsync_copy.sh b/tools/mass_copy/rsync_copy.sh new file mode 100755 index 00000000..7998773f --- /dev/null +++ b/tools/mass_copy/rsync_copy.sh @@ -0,0 +1,309 @@ +#!/bin/bash +set -e + +# Fast mass copy using rsync with intelligent sync +# Usage: ./rsync_copy.sh [SOURCE] [DESTINATION] [DEVICE] + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to show help +show_help() { + echo -e "${BLUE}=== FAST MASS COPY WITH RSYNC ===${NC}" + echo "" + echo "Usage: $0 [SOURCE] [DESTINATION] [DEVICE]" + echo "" + echo "Features:" + echo " - Intelligent sync (only copies changed files)" + echo " - Built-in progress monitoring" + echo " - Resumable transfers" + echo " - Optimized for many small files" + echo " - Real-time speed display" + echo " - Preserves source folder name" + echo " - Automatic cleanup of obsolete files" + echo "" + echo "Examples:" + echo " $0 /home/user/files /mnt/sd /dev/sdc1" + echo " $0 /home/user/data /media/usb /dev/sdb1" + echo "" + exit 0 +} + +# Verify parameters +if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + show_help +fi + +if [ $# -lt 3 ]; then + echo -e "${RED}Error: 3 parameters required${NC}" + echo "Usage: $0 [SOURCE] [DESTINATION] [DEVICE]" + echo "Run '$0 --help' for more information" + exit 1 +fi + +SOURCE="$1" +DESTINATION="$2" +DEVICE="$3" + +# Extract source folder name +SOURCE_FOLDER_NAME=$(basename "$SOURCE") + +# Validate inputs +if [ ! -d "$SOURCE" ]; then + echo -e "${RED}Error: Source directory '$SOURCE' does not exist${NC}" + exit 1 +fi + +if [ ! -b "$DEVICE" ]; then + echo -e "${RED}Error: Device '$DEVICE' does not exist${NC}" + exit 1 +fi + +# Cleanup function +cleanup() { + echo -e "${YELLOW}Cleaning up...${NC}" + if mountpoint -q "$DESTINATION" 2>/dev/null; then + sudo umount "$DESTINATION" 2>/dev/null || true + fi +} + +trap cleanup EXIT INT TERM + +echo -e "${BLUE}=== FAST MASS COPY WITH RSYNC ===${NC}" +echo -e "${BLUE}Start: $(date)${NC}" +echo "" +echo "Configuration:" +echo " Source: $SOURCE" +echo " Source folder name: $SOURCE_FOLDER_NAME" +echo " Destination: $DESTINATION" +echo " Device: $DEVICE" +echo " Mode: Intelligent sync (only changed files)" +echo "" + +# STEP 1: Mount device +echo -e "${YELLOW}1. Mounting device...${NC}" +sudo mkdir -p "$DESTINATION" +sudo umount "$DEVICE" 2>/dev/null || true + +# Mount with optimizations for many small files +sudo mount -t vfat -o rw,uid=$(id -u),gid=$(id -g),umask=0022,async,noatime,nodiratime "$DEVICE" "$DESTINATION" + +if ! mountpoint -q "$DESTINATION"; then + echo -e "${RED}Error: Could not mount $DEVICE at $DESTINATION${NC}" + exit 1 +fi + +echo -e "${GREEN} βœ“ Device mounted successfully${NC}" + +# STEP 2: Space verification +echo -e "${YELLOW}2. Verifying available space...${NC}" + +# Get source size in bytes +SOURCE_SIZE=$(du -sb "$SOURCE" | cut -f1) +echo " Source size: $(du -sh "$SOURCE" | cut -f1)" + +# Initialize with default +DESTINATION_FREE="0" + +# Method 1: Try df -B1 (most compatible) +DF_OUTPUT=$(df -B1 "$DESTINATION" 2>/dev/null | tail -n 1) +if [ -n "$DF_OUTPUT" ]; then + # Extract available space (4th column) + DESTINATION_FREE=$(echo "$DF_OUTPUT" | awk '{print $4}' | grep -E '^[0-9]+$' || echo "0") +fi + +# Method 2: Fallback to df with KB +if [ "$DESTINATION_FREE" = "0" ]; then + DF_KB=$(df "$DESTINATION" 2>/dev/null | tail -1 | awk '{print $4}' | tr -d 'K' | grep -E '^[0-9]+$' || echo "0") + if [ "$DF_KB" != "0" ]; then + DESTINATION_FREE=$((DF_KB * 1024)) + fi +fi + +# Ensure DESTINATION_FREE is numeric +if ! [[ "$DESTINATION_FREE" =~ ^[0-9]+$ ]]; then + DESTINATION_FREE="0" +fi + +echo " Free space: $(echo "$DESTINATION_FREE" | awk '{printf "%.1fG", $1/1024/1024/1024}')" + +# Check space only if we have valid numbers +if [ "$DESTINATION_FREE" -gt 0 ] && [[ "$SOURCE_SIZE" =~ ^[0-9]+$ ]]; then + REQUIRED_SPACE=$((SOURCE_SIZE + SOURCE_SIZE / 10)) + echo " Required space (with 10% margin): $(echo "$REQUIRED_SPACE" | awk '{printf "%.1fG", $1/1024/1024/1024}')" + + if [ "$REQUIRED_SPACE" -gt "$DESTINATION_FREE" ]; then + echo -e "${RED}Error: Not enough space at destination${NC}" + echo " Required: $(echo "$REQUIRED_SPACE" | awk '{printf "%.1fG", $1/1024/1024/1024}')" + echo " Available: $(echo "$DESTINATION_FREE" | awk '{printf "%.1fG", $1/1024/1024/1024}')" + exit 1 + fi + echo -e "${GREEN} βœ“ Sufficient space available${NC}" +else + echo -e "${YELLOW} Warning: Could not verify space accurately, proceeding...${NC}" + echo " Debug: DESTINATION_FREE='$DESTINATION_FREE', SOURCE_SIZE='$SOURCE_SIZE'" +fi + +# STEP 3: Analyze sync requirements +echo -e "${YELLOW}3. Analyzing sync requirements...${NC}" + +TOTAL_FILES=$(find "$SOURCE" -type f 2>/dev/null | wc -l) +echo " Source files: $TOTAL_FILES" + +# Check if destination exists to determine sync type +if [ -d "$DESTINATION/$SOURCE_FOLDER_NAME" ]; then + DEST_FILES=$(find "$DESTINATION/$SOURCE_FOLDER_NAME" -type f 2>/dev/null | wc -l) + echo " Existing destination files: $DEST_FILES" + echo " Sync mode: Update existing destination" +else + echo " Sync mode: Initial copy (destination doesn't exist)" +fi + +# STEP 4: Device speed test +echo -e "${YELLOW}4. Testing device write speed...${NC}" +TEST_START=$(date +%s) +dd if=/dev/zero of="$DESTINATION/speedtest.tmp" bs=10M count=1 oflag=direct status=none 2>/dev/null || true +TEST_END=$(date +%s) +TEST_TIME=$((TEST_END - TEST_START)) +if [ $TEST_TIME -eq 0 ]; then TEST_TIME=1; fi +WRITE_SPEED=$((10 / TEST_TIME)) +rm -f "$DESTINATION/speedtest.tmp" + +echo " Write speed: ~${WRITE_SPEED}MB/s" + +# STEP 5: Rsync sync with intelligent behavior +echo -e "${YELLOW}5. Starting intelligent sync...${NC}" +echo " Destination folder: $DESTINATION/$SOURCE_FOLDER_NAME/" +echo " Rsync will:" +echo " - Only copy new/modified files" +echo " - Remove files from destination that no longer exist in source" +echo " - Resume interrupted transfers" +echo "" + +RSYNC_START=$(date +%s) + +# Rsync with intelligent sync - only copies what's needed +rsync -a \ + --info=progress2 \ + --partial \ + --inplace \ + --no-compress \ + --delete-during \ + --prune-empty-dirs \ + --human-readable \ + "$SOURCE/" "$DESTINATION/$SOURCE_FOLDER_NAME/" + +# Check rsync exit status +RSYNC_EXIT_CODE=$? +if [ $RSYNC_EXIT_CODE -ne 0 ]; then + echo -e "${RED}Error: rsync failed with exit code $RSYNC_EXIT_CODE${NC}" + exit 1 +fi + +RSYNC_END=$(date +%s) +RSYNC_TIME=$((RSYNC_END - RSYNC_START)) + +echo "" +echo -e "${GREEN} βœ“ Sync completed successfully${NC}" + +# STEP 6: Verification +echo -e "${YELLOW}6. Verifying sync result...${NC}" + +DEST_FILES_FINAL=$(find "$DESTINATION/$SOURCE_FOLDER_NAME" -type f 2>/dev/null | wc -l) +DEST_SIZE=$(du -sh "$DESTINATION/$SOURCE_FOLDER_NAME" 2>/dev/null | cut -f1) + +echo " Source files: $TOTAL_FILES" +echo " Destination files: $DEST_FILES_FINAL" +echo " Source size: $(du -sh "$SOURCE" | cut -f1)" +echo " Destination size: $DEST_SIZE" + +if [ "$TOTAL_FILES" -eq "$DEST_FILES_FINAL" ]; then + echo -e "${GREEN} βœ“ File count matches perfectly${NC}" +else + DIFF=$((TOTAL_FILES - DEST_FILES_FINAL)) + echo -e "${YELLOW} Warning: File count difference: $DIFF files${NC}" +fi + +# STEP 7: Sample integrity check +echo -e "${YELLOW}7. Sample integrity check...${NC}" +echo " Checking 10 random files..." + +SAMPLE_COUNT=0 +SAMPLE_ERRORS=0 + +for file in $(find "$SOURCE" -type f 2>/dev/null | shuf | head -10); do + SAMPLE_COUNT=$((SAMPLE_COUNT + 1)) + rel_path=${file#$SOURCE/} + dest_file="$DESTINATION/$SOURCE_FOLDER_NAME/$rel_path" + + if [ -f "$dest_file" ]; then + if cmp -s "$file" "$dest_file" 2>/dev/null; then + echo " βœ“ $rel_path" + else + echo " βœ— $rel_path (content differs)" + SAMPLE_ERRORS=$((SAMPLE_ERRORS + 1)) + fi + else + echo " βœ— $rel_path (missing)" + SAMPLE_ERRORS=$((SAMPLE_ERRORS + 1)) + fi +done + +if [ $SAMPLE_ERRORS -eq 0 ]; then + echo -e "${GREEN} βœ“ Sample verification passed (${SAMPLE_COUNT}/${SAMPLE_COUNT} files OK)${NC}" +else + echo -e "${YELLOW} Warning: $SAMPLE_ERRORS/$SAMPLE_COUNT sample files had issues${NC}" +fi + +# STEP 8: Final sync and unmount +echo -e "${YELLOW}8. Finalizing...${NC}" +echo " Syncing filesystem..." +sync +sleep 2 + +echo " Unmounting device..." +sudo umount "$DESTINATION" +sleep 1 + +echo -e "${GREEN} βœ“ Device unmounted safely${NC}" + +# Calculate performance stats +TOTAL_TIME=$RSYNC_TIME +if [ $TOTAL_TIME -gt 0 ]; then + AVG_SPEED=$((SOURCE_SIZE / 1024 / 1024 / TOTAL_TIME)) + FILES_PER_SEC=$((TOTAL_FILES / TOTAL_TIME)) +else + AVG_SPEED="N/A" + FILES_PER_SEC="N/A" +fi + +echo "" +echo -e "${GREEN}=== SYNC COMPLETED SUCCESSFULLY ===${NC}" +echo -e "${GREEN}End: $(date)${NC}" +echo "" +echo "Performance Summary:" +echo " Files processed: $TOTAL_FILES" +echo " Data size: $(du -sh "$SOURCE" | cut -f1)" +echo " Sync time: ${TOTAL_TIME}s ($((TOTAL_TIME / 60))m $((TOTAL_TIME % 60))s)" +echo " Average speed: ${AVG_SPEED}MB/s" +echo " Files/second: $FILES_PER_SEC" +echo " Device write speed: ~${WRITE_SPEED}MB/s" +echo " Sample verification: $((SAMPLE_COUNT - SAMPLE_ERRORS))/$SAMPLE_COUNT files OK" +echo "" +echo "Files synced to: $DESTINATION/$SOURCE_FOLDER_NAME/" +echo "" +echo -e "${BLUE}Rsync intelligently synced only new/modified files${NC}" +echo -e "${BLUE}Next sync will be faster as only changes will be copied${NC}" + +# Final status +if [ $SAMPLE_ERRORS -eq 0 ]; then + echo -e "${GREEN}βœ“ SYNC SUCCESSFUL - All verifications passed${NC}" + exit 0 +else + echo -e "${YELLOW}⚠ SYNC COMPLETED with verification warnings${NC}" + exit 1 +fi \ No newline at end of file