diff --git a/README.md b/README.md index 0261d38b..ad43804d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,47 @@ -# OpenTrickler RP2040 Controller +# OpenTrickler RP2040 Controller This repo is for the firmware that utilises the Raspberry Pi RP2040 micro controller OpenTrickler RP2040 Controller. +## What's New — Branch `feature/ai-tuning-port` + +This branch ports the AI auto-tuning system from [OpenTrickler-v2](https://github.com/Jump73/OpenTrickler-v2) and integrates it fully into the RP2040/RP2350 firmware. + +### AI Auto-Tuning (`src/ai_tuning.*`) +Automatic PID parameter optimisation using a binary-step search algorithm: +- **Phase 1 (COARSE_ONLY)** — tunes coarse trickler Kp/Kd across configurable search bounds +- **Phase 2 (FINE_ONLY)** — tunes fine trickler Kp/Kd, with a coarse pre-charge at the start of each drop +- Configurable time targets: `coarse_time_target_ms` (c14) and `total_time_target_ms` (c15) +- Configurable search bounds (Kp/Kd min/max), noise margin, and max overthrow threshold via `/rest/ai_tuning_config_set` +- Results persisted in flash; apply best parameters to a profile via `/rest/ai_tuning_apply` + +### ML Data Collection (`src/ml_data_collection.*`, `src/ai_drop_telemetry.*`) +- Records per-drop telemetry (weight, timing, motor params) to flash during normal charges when enabled +- Toggle via charge mode config field `c16` (`ml_data_collection_enabled`) + +### Charge Mode Integration (`src/charge_mode.cpp`) +- Charge loop queries `ai_tuning_get_next_params()` and overrides PID parameters when tuning is active +- Coarse pre-charge phase injected at start of each drop in Phase 2 +- `ai_tuning_record_drop()` called after every drop to feed results back to the tuner +- RST button cancels active tuning session +- Tuning completion detected after cup removal — charge mode exits automatically + +### Error System (`src/errors.*`) +- Centralised error reporting ported from v2; integrates with display and REST API + +### G&G Scale Fix +- Fixed serial parsing for G&G / JJB scale driver + +### Web UI (`src/html/web_portal.html`) +New **AI Tuning** section in the web portal Settings drawer: +- Select profile and target charge weight, then start/cancel/apply tuning +- Live progress panel (drops completed, progress %, current Kp/Kd) overlaid on the Trickler page during active tuning +- Recommended parameters displayed on completion with one-tap Apply +- Advanced collapsible panel for Kp/Kd search range and noise margin configuration +- ML Data Collection toggle +- REST endpoints used: `/rest/ai_tuning_start`, `/rest/ai_tuning_status`, `/rest/ai_tuning_apply`, `/rest/ai_tuning_cancel`, `/rest/ai_tuning_clear_history`, `/rest/ai_tuning_config`, `/rest/ai_tuning_config_set` + +### RP2350 Support +- All new modules compile cleanly for both `pico_w` (RP2040) and `pico2_w` (RP2350) targets + Join our [discord server](https://discord.gg/ZhdThA2vrW) for help and development information. ## Get Started diff --git a/build/.placeholder b/build/.placeholder deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ai_tuning.c b/src/ai_tuning.c new file mode 100644 index 00000000..743b5ffc --- /dev/null +++ b/src/ai_tuning.c @@ -0,0 +1,880 @@ +/** + * @file ai_tuning.c + * @brief AI Auto-Tuning Implementation for RP2040 + * + * Binary step algorithm for tuning Kp/Kd parameters. + */ + +#include "ai_tuning.h" +#include "charge_mode.h" +#include "flash_storage.h" +#include "eeprom.h" +#include "profile.h" +#include +#include +#include "FreeRTOS.h" +#include "semphr.h" +#include "mini_12864_module.h" +extern QueueHandle_t encoder_event_queue; + +// Access charge mode config for thresholds +extern charge_mode_config_t charge_mode_config; + +// Thread safety mutex +static SemaphoreHandle_t g_ai_tuning_mutex = NULL; + +// ============================================================================= +// PARAMETER RANGES - Coarse is gentle (0-1), Fine is aggressive (0-10) +// ============================================================================= +#define COARSE_KP_MIN 0.01f +#define COARSE_KP_MAX 1.0f +#define COARSE_KD_MIN 0.01f +#define COARSE_KD_MAX 2.0f + +#define FINE_KP_MIN 0.01f +#define FINE_KP_MAX 5.0f +#define FINE_KD_MIN 0.01f +#define FINE_KD_MAX 20.0f + +// Minimum step sizes based on range +#define COARSE_MIN_STEP 0.02f +#define FINE_MIN_STEP 0.1f + +// Global tuning session state +static ai_tuning_session_t g_session; +static ai_tuning_config_t g_config; +static ai_tuning_config_eeprom_t g_config_eeprom; +static ai_tuning_history_t g_history; +static bool g_initialized = false; +static bool g_history_loaded = false; +static bool g_config_loaded = false; + +// Original profile values saved at tuning start (for cancel/restore) +static float g_orig_coarse_kp = 0.0f; +static float g_orig_coarse_kd = 0.0f; +static float g_orig_fine_kp = 0.0f; +static float g_orig_fine_kd = 0.0f; + +// Tuning state for adaptive Kp/Kd adjustment +typedef enum { + TUNING_PHASE_ADAPTIVE_KP, + TUNING_PHASE_ADAPTIVE_KD +} tuning_phase_t; + +static tuning_phase_t g_coarse_tuning_phase; +static tuning_phase_t g_fine_tuning_phase; + +// Adaptive step sizes +static float g_coarse_kp_step; +static float g_coarse_kd_step; +static float g_fine_kp_step; +static float g_fine_kd_step; + +// Track for oscillation detection +static bool g_coarse_had_overthrow; +static bool g_fine_had_overthrow; + +// Forward declarations +static void calculate_next_params_phase1(const ai_drop_telemetry_t* drop); +static void calculate_next_params_phase2(const ai_drop_telemetry_t* drop); +static void load_history_from_flash(void); +static void save_history_to_flash(void); +static void load_config_from_eeprom(void); +static bool save_config_to_eeprom(void); + +// ============================================================================= +// Initialization +// ============================================================================= + +void ai_tuning_init(void) { + if (g_initialized) { + return; + } + + // Create mutex for thread safety + if (g_ai_tuning_mutex == NULL) { + g_ai_tuning_mutex = xSemaphoreCreateRecursiveMutex(); + } + + // Initialize default configuration + g_config.max_overthrow_percent = 6.67f; + + g_config.coarse_kp_min = COARSE_KP_MIN; + g_config.coarse_kp_max = COARSE_KP_MAX; + g_config.coarse_kd_min = COARSE_KD_MIN; + g_config.coarse_kd_max = COARSE_KD_MAX; + + g_config.fine_kp_min = FINE_KP_MIN; + g_config.fine_kp_max = FINE_KP_MAX; + g_config.fine_kd_min = FINE_KD_MIN; + g_config.fine_kd_max = FINE_KD_MAX; + + g_config.noise_margin = 0.05f; // ±0.05gn for fine Kd phase + + // Tuning acceptance factors + g_config.coarse_kp_max_factor = 1.02f; // Kp phase: threshold <= drop <= threshold * 1.02 + g_config.coarse_kd_max_factor = 1.01f; // Kd phase: drop <= threshold * 1.01 + g_config.fine_kp_max_factor = 1.003f; // Kp phase: target <= drop <= target * 1.003 + + // Clear session + memset(&g_session, 0, sizeof(ai_tuning_session_t)); + g_session.state = AI_TUNING_IDLE; + + // Load config from EEPROM (user settings) + load_config_from_eeprom(); + + // Load ML history from flash (runtime learning data) + load_history_from_flash(); + + // Register EEPROM save handler + eeprom_register_handler(save_config_to_eeprom); + + g_initialized = true; +} + +// ============================================================================= +// Config Accessor +// ============================================================================= + +ai_tuning_config_t* ai_tuning_get_config(void) { + return &g_config; +} + +// ============================================================================= +// Tuning Session Start +// ============================================================================= + +bool ai_tuning_start(profile_t* profile) { + if (!g_initialized) { + ai_tuning_init(); + } + + if (g_ai_tuning_mutex != NULL) { + if (xSemaphoreTakeRecursive(g_ai_tuning_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) { + return false; + } + } + + if (profile == NULL) { + if (g_ai_tuning_mutex != NULL) xSemaphoreGiveRecursive(g_ai_tuning_mutex); + return false; + } + + // Save original profile values for restore on cancel + g_orig_coarse_kp = profile->coarse_kp; + g_orig_coarse_kd = profile->coarse_kd; + g_orig_fine_kp = profile->fine_kp; + g_orig_fine_kd = profile->fine_kd; + + // Initialize session + memset(&g_session, 0, sizeof(ai_tuning_session_t)); + g_session.state = AI_TUNING_PHASE_1_COARSE; + g_session.target_profile = profile; + g_session.drops_completed = 0; + g_session.max_drops_allowed = 30; + g_session.phase2_start_idx = 0; + + // Get profile index for ML suggestions + uint8_t profile_idx = 0; + for (int i = 0; i < MAX_PROFILE_CNT; i++) { + if (profile_select(i) == profile) { + profile_idx = i; + break; + } + } + + // Start from suggestions or zero + float suggested_coarse_kp, suggested_coarse_kd; + float suggested_fine_kp, suggested_fine_kd; + bool have_suggestions = ai_tuning_get_suggestions(profile_idx, + &suggested_coarse_kp, &suggested_coarse_kd, + &suggested_fine_kp, &suggested_fine_kd); + + if (have_suggestions) { + g_session.coarse_kp_best = suggested_coarse_kp * 0.7f; + g_session.coarse_kd_best = suggested_coarse_kd * 0.7f; + g_session.fine_kp_best = suggested_fine_kp * 0.7f; + g_session.fine_kd_best = suggested_fine_kd * 0.7f; + } else { + g_session.coarse_kp_best = g_config.coarse_kp_min; + g_session.coarse_kd_best = g_config.coarse_kd_min; + g_session.fine_kp_best = g_config.fine_kp_min; + g_session.fine_kd_best = g_config.fine_kd_min; + } + + g_coarse_tuning_phase = TUNING_PHASE_ADAPTIVE_KP; + g_fine_tuning_phase = TUNING_PHASE_ADAPTIVE_KP; + + // Start from existing profile values if available, otherwise midpoint + bool profile_has_values = (profile->coarse_kp > 0.0f || profile->coarse_kd > 0.0f || + profile->fine_kp > 0.0f || profile->fine_kd > 0.0f); + if (profile_has_values) { + g_session.coarse_kp_best = fmaxf(g_config.coarse_kp_min, fminf(profile->coarse_kp, g_config.coarse_kp_max)); + g_session.coarse_kd_best = fmaxf(g_config.coarse_kd_min, fminf(profile->coarse_kd, g_config.coarse_kd_max)); + g_session.fine_kp_best = fmaxf(g_config.fine_kp_min, fminf(profile->fine_kp, g_config.fine_kp_max)); + g_session.fine_kd_best = fmaxf(g_config.fine_kd_min, fminf(profile->fine_kd, g_config.fine_kd_max)); + } else { + g_session.coarse_kp_best = (g_config.coarse_kp_min + g_config.coarse_kp_max) / 2.0f; + g_session.coarse_kd_best = (g_config.coarse_kd_min + g_config.coarse_kd_max) / 2.0f; + g_session.fine_kp_best = (g_config.fine_kp_min + g_config.fine_kp_max) / 2.0f; + g_session.fine_kd_best = (g_config.fine_kd_min + g_config.fine_kd_max) / 2.0f; + } + + g_coarse_kp_step = (g_config.coarse_kp_max - g_config.coarse_kp_min) / 2.0f; + g_coarse_kd_step = (g_config.coarse_kd_max - g_config.coarse_kd_min) / 2.0f; + g_fine_kp_step = (g_config.fine_kp_max - g_config.fine_kp_min) / 2.0f; + g_fine_kd_step = (g_config.fine_kd_max - g_config.fine_kd_min) / 2.0f; + + g_coarse_had_overthrow = false; + g_fine_had_overthrow = false; + + if (g_ai_tuning_mutex != NULL) xSemaphoreGiveRecursive(g_ai_tuning_mutex); + return true; +} + +// ============================================================================= +// Get Next Parameters +// ============================================================================= + +bool ai_tuning_get_next_params(float* coarse_kp, float* coarse_kd, + float* fine_kp, float* fine_kd) { + if (g_ai_tuning_mutex != NULL) { + if (xSemaphoreTakeRecursive(g_ai_tuning_mutex, pdMS_TO_TICKS(50)) != pdTRUE) { + return false; + } + } + + bool result = false; + + if (g_session.state == AI_TUNING_PHASE_1_COARSE) { + *coarse_kp = g_session.coarse_kp_best; + *coarse_kd = g_session.coarse_kd_best; + *fine_kp = 0.0f; + *fine_kd = 0.0f; + result = true; + } + else if (g_session.state == AI_TUNING_PHASE_2_FINE) { + *coarse_kp = g_session.recommended_coarse_kp; + *coarse_kd = g_session.recommended_coarse_kd; + *fine_kp = g_session.fine_kp_best; + *fine_kd = g_session.fine_kd_best; + result = true; + } + + if (g_ai_tuning_mutex != NULL) xSemaphoreGiveRecursive(g_ai_tuning_mutex); + return result; +} + +// ============================================================================= +// Record Drop Telemetry +// ============================================================================= + +bool ai_tuning_record_drop(const ai_drop_telemetry_t* telemetry) { + if (g_ai_tuning_mutex != NULL) { + if (xSemaphoreTakeRecursive(g_ai_tuning_mutex, pdMS_TO_TICKS(50)) != pdTRUE) { + return false; + } + } + + if (!ai_tuning_is_active() || telemetry == NULL) { + if (g_ai_tuning_mutex != NULL) xSemaphoreGiveRecursive(g_ai_tuning_mutex); + return false; + } + + if (g_session.drops_completed >= g_session.max_drops_allowed) { + g_session.state = AI_TUNING_ERROR; + snprintf(g_session.error_message, sizeof(g_session.error_message), + "Did not converge in %d drops", g_session.max_drops_allowed); + g_session.recommended_coarse_kp = g_session.coarse_kp_best; + g_session.recommended_coarse_kd = g_session.coarse_kd_best; + g_session.recommended_fine_kp = g_session.fine_kp_best; + g_session.recommended_fine_kd = g_session.fine_kd_best; + if (g_ai_tuning_mutex != NULL) xSemaphoreGiveRecursive(g_ai_tuning_mutex); + return false; + } + + // Store telemetry in circular buffer + uint8_t idx = g_session.drop_write_idx; + memcpy(&g_session.drops[idx], telemetry, sizeof(ai_drop_telemetry_t)); + g_session.drop_write_idx = (g_session.drop_write_idx + 1) % AI_TUNING_DROP_BUF_SIZE; + g_session.drops_completed++; + + // Dispatch to correct phase handler + if (g_session.state == AI_TUNING_PHASE_1_COARSE) { + calculate_next_params_phase1(telemetry); + } else if (g_session.state == AI_TUNING_PHASE_2_FINE) { + calculate_next_params_phase2(telemetry); + } + + if (g_ai_tuning_mutex != NULL) xSemaphoreGiveRecursive(g_ai_tuning_mutex); + return true; +} + +// ============================================================================= +// Phase 1: Tune Coarse Trickler (Binary Search) +// 1. Increase Kp until threshold <= drop <= threshold * 1.02 +// 2. Increase Kd until drop <= threshold * 1.02 +// If time > target, bump Kp by step and continue Kd +// ============================================================================= + +static void calculate_next_params_phase1(const ai_drop_telemetry_t* drop) { + float coarse_stop_threshold = charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold; + float target_time_ms = (float)charge_mode_config.eeprom_charge_mode_data.coarse_time_target_ms; + + // "threshold" = coarse stop point (where coarse should stop) + float threshold = drop->target_weight - coarse_stop_threshold; + float final_weight = drop->final_weight; + + // Kp acceptance bounds: threshold <= drop <= target / 1.015 + float accept_min = threshold; + float accept_max = drop->target_weight / 1.015f; + // Kd acceptance: drop <= target - (threshold * 0.02) + float kd_accept_max = drop->target_weight - (threshold * 0.02f); + + bool time_ok = (drop->coarse_time_ms <= target_time_ms); + + // Kp phase: change Kp until threshold <= drop <= target / 1.015 + if (g_coarse_tuning_phase == TUNING_PHASE_ADAPTIVE_KP) { + // Halve step first (binary search) + g_coarse_kp_step /= 2.0f; + + if (final_weight >= accept_min && final_weight <= accept_max) { + // In range - move to Kd phase + g_coarse_tuning_phase = TUNING_PHASE_ADAPTIVE_KD; + g_session.coarse_kd_best = (g_config.coarse_kd_min + g_config.coarse_kd_max) / 2.0f; + g_coarse_kd_step = (g_config.coarse_kd_max - g_config.coarse_kd_min) / 2.0f; + } else if (final_weight > accept_max) { + // Too high - decrease Kp + g_session.coarse_kp_best -= g_coarse_kp_step; + } else if (final_weight < accept_min) { + // Too low - increase Kp + g_session.coarse_kp_best += g_coarse_kp_step; + } + + // Step too small - move to Kd phase anyway + if (g_coarse_kp_step < COARSE_MIN_STEP) { + g_coarse_tuning_phase = TUNING_PHASE_ADAPTIVE_KD; + g_session.coarse_kd_best = (g_config.coarse_kd_min + g_config.coarse_kd_max) / 2.0f; + g_coarse_kd_step = (g_config.coarse_kd_max - g_config.coarse_kd_min) / 2.0f; + } + } + // Kd phase: change Kd until drop <= target - (threshold * 0.02) + // If time > target, increase Kp by step, then go to Fine + else if (g_coarse_tuning_phase == TUNING_PHASE_ADAPTIVE_KD) { + // Halve step first (binary search) + g_coarse_kd_step /= 2.0f; + + // If time bad, bump Kp + if (!time_ok) { + g_session.coarse_kp_best += g_coarse_kp_step; + } + + if (final_weight <= kd_accept_max) { + // In range - done with coarse, go directly to Fine + g_session.recommended_coarse_kp = g_session.coarse_kp_best; + g_session.recommended_coarse_kd = g_session.coarse_kd_best; + g_session.phase2_start_idx = g_session.drops_completed; + g_session.state = AI_TUNING_PHASE_2_FINE; + g_session.fine_kp_best = (g_config.fine_kp_min + g_config.fine_kp_max) / 2.0f; + g_session.fine_kd_best = (g_config.fine_kd_min + g_config.fine_kd_max) / 2.0f; + g_fine_kp_step = (g_config.fine_kp_max - g_config.fine_kp_min) / 2.0f; + g_fine_kd_step = (g_config.fine_kd_max - g_config.fine_kd_min) / 2.0f; + g_fine_tuning_phase = TUNING_PHASE_ADAPTIVE_KP; + } else { + // Too high - increase Kd + g_session.coarse_kd_best += g_coarse_kd_step; + } + + // Step too small - move to Fine + if (g_coarse_kd_step < COARSE_MIN_STEP) { + g_session.recommended_coarse_kp = g_session.coarse_kp_best; + g_session.recommended_coarse_kd = g_session.coarse_kd_best; + g_session.phase2_start_idx = g_session.drops_completed; + g_session.state = AI_TUNING_PHASE_2_FINE; + g_session.fine_kp_best = (g_config.fine_kp_min + g_config.fine_kp_max) / 2.0f; + g_session.fine_kd_best = (g_config.fine_kd_min + g_config.fine_kd_max) / 2.0f; + g_fine_kp_step = (g_config.fine_kp_max - g_config.fine_kp_min) / 2.0f; + g_fine_kd_step = (g_config.fine_kd_max - g_config.fine_kd_min) / 2.0f; + g_fine_tuning_phase = TUNING_PHASE_ADAPTIVE_KP; + } + } + + // Clamp to valid range + g_session.coarse_kp_best = fminf(fmaxf(g_session.coarse_kp_best, g_config.coarse_kp_min), g_config.coarse_kp_max); + g_session.coarse_kd_best = fminf(fmaxf(g_session.coarse_kd_best, g_config.coarse_kd_min), g_config.coarse_kd_max); +} + +// ============================================================================= +// Phase 2: Tune Fine Trickler (Binary Search) +// 1. Increase Kp until target <= drop <= target * 1.003 +// 2. Increase Kd until drop = target ± 0.05gn +// ============================================================================= + +static void calculate_next_params_phase2(const ai_drop_telemetry_t* drop) { + float target = drop->target_weight; + float final_weight = drop->final_weight; + float target_time_ms = (float)charge_mode_config.eeprom_charge_mode_data.total_time_target_ms; + float noise_margin = g_config.noise_margin; // 0.05gn + + // Acceptance bounds: + // Kp: target <= drop <= target * 1.003 + // Kd: drop = target ± 0.05gn + float kp_max = target * g_config.fine_kp_max_factor; // target * 1.003 + + bool time_ok = (drop->total_time_ms <= target_time_ms); + + // Kp phase: change Kp until target <= drop <= target * 1.003 + if (g_fine_tuning_phase == TUNING_PHASE_ADAPTIVE_KP) { + // Halve step first (binary search) + g_fine_kp_step /= 2.0f; + + if (final_weight >= target && final_weight <= kp_max) { + // In range - move to Kd phase + g_fine_tuning_phase = TUNING_PHASE_ADAPTIVE_KD; + g_session.fine_kd_best = (g_config.fine_kd_min + g_config.fine_kd_max) / 2.0f; + g_fine_kd_step = (g_config.fine_kd_max - g_config.fine_kd_min) / 2.0f; + } else if (final_weight > kp_max) { + // Too high - decrease Kp + g_session.fine_kp_best -= g_fine_kp_step; + } else if (final_weight < target) { + // Too low - increase Kp + g_session.fine_kp_best += g_fine_kp_step; + } + + // Step too small - move to Kd phase anyway + if (g_fine_kp_step < FINE_MIN_STEP) { + g_fine_tuning_phase = TUNING_PHASE_ADAPTIVE_KD; + g_session.fine_kd_best = (g_config.fine_kd_min + g_config.fine_kd_max) / 2.0f; + g_fine_kd_step = (g_config.fine_kd_max - g_config.fine_kd_min) / 2.0f; + } + } + // Kd phase: change Kd until drop = target ± 0.05gn + // If time > target, increase Kp by step + else if (g_fine_tuning_phase == TUNING_PHASE_ADAPTIVE_KD) { + // Halve step first (binary search) + g_fine_kd_step /= 2.0f; + + // If time bad, bump Kp + if (!time_ok) { + g_session.fine_kp_best += g_fine_kp_step; + } + + float error = fabsf(final_weight - target); + + if (error <= noise_margin) { + // Within ±0.05gn of target - done! + g_session.recommended_fine_kp = g_session.fine_kp_best; + g_session.recommended_fine_kd = g_session.fine_kd_best; + g_session.state = AI_TUNING_COMPLETE; + } else if (final_weight > target + noise_margin) { + // Too high - increase Kd + g_session.fine_kd_best += g_fine_kd_step; + } else if (final_weight < target - noise_margin) { + // Too low - decrease Kd + g_session.fine_kd_best -= g_fine_kd_step; + } + + // Step too small - done + if (g_fine_kd_step < FINE_MIN_STEP) { + g_session.recommended_fine_kp = g_session.fine_kp_best; + g_session.recommended_fine_kd = g_session.fine_kd_best; + g_session.state = AI_TUNING_COMPLETE; + } + } + + // Clamp to valid range + g_session.fine_kp_best = fminf(fmaxf(g_session.fine_kp_best, g_config.fine_kp_min), g_config.fine_kp_max); + g_session.fine_kd_best = fminf(fmaxf(g_session.fine_kd_best, g_config.fine_kd_min), g_config.fine_kd_max); +} + +// ============================================================================= +// Finalize Recommendations +// ============================================================================= + +static void finalize_recommendations(void) { + g_session.state = AI_TUNING_COMPLETE; + + float total_overthrow = 0.0f; + float total_time = 0.0f; + int count = g_session.drops_completed; + int buf_count = (count < AI_TUNING_DROP_BUF_SIZE) ? count : AI_TUNING_DROP_BUF_SIZE; + + for (int i = 0; i < buf_count; i++) { + total_overthrow += g_session.drops[i].overthrow; + total_time += g_session.drops[i].total_time_ms; + } + + if (buf_count > 0) { + g_session.avg_overthrow = total_overthrow / buf_count; + g_session.avg_total_time = total_time / buf_count; + } +} + +// ============================================================================= +// Session State Queries +// ============================================================================= + +bool ai_tuning_is_complete(void) { + return g_session.state == AI_TUNING_COMPLETE; +} + +ai_tuning_session_t* ai_tuning_get_session(void) { + return &g_session; +} + +void ai_tuning_get_session_copy(ai_tuning_session_t* out) { + if (g_ai_tuning_mutex != NULL) { + xSemaphoreTakeRecursive(g_ai_tuning_mutex, pdMS_TO_TICKS(50)); + } + memcpy(out, &g_session, sizeof(ai_tuning_session_t)); + if (g_ai_tuning_mutex != NULL) { + xSemaphoreGiveRecursive(g_ai_tuning_mutex); + } +} + +void ai_tuning_get_history_copy(ai_tuning_history_t* out) { + if (g_ai_tuning_mutex != NULL) { + xSemaphoreTakeRecursive(g_ai_tuning_mutex, pdMS_TO_TICKS(50)); + } + memcpy(out, &g_history, sizeof(ai_tuning_history_t)); + if (g_ai_tuning_mutex != NULL) { + xSemaphoreGiveRecursive(g_ai_tuning_mutex); + } +} + +bool ai_tuning_get_recommended_params(float* coarse_kp, float* coarse_kd, + float* fine_kp, float* fine_kd) { + if (g_session.state != AI_TUNING_COMPLETE && g_session.state != AI_TUNING_ERROR) { + return false; + } + + *coarse_kp = g_session.recommended_coarse_kp; + *coarse_kd = g_session.recommended_coarse_kd; + *fine_kp = g_session.recommended_fine_kp; + *fine_kd = g_session.recommended_fine_kd; + + return true; +} + +bool ai_tuning_apply_params(void) { + if ((g_session.state != AI_TUNING_COMPLETE && g_session.state != AI_TUNING_ERROR) || + g_session.target_profile == NULL) { + return false; + } + + g_session.target_profile->coarse_kp = g_session.recommended_coarse_kp; + g_session.target_profile->coarse_kd = g_session.recommended_coarse_kd; + g_session.target_profile->fine_kp = g_session.recommended_fine_kp; + g_session.target_profile->fine_kd = g_session.recommended_fine_kd; + + g_session.state = AI_TUNING_IDLE; + + return true; +} + +void ai_tuning_cancel(void) { + bool was_active = ai_tuning_is_active(); + + // Restore original profile values if we have a target profile + if (g_session.target_profile != NULL) { + g_session.target_profile->coarse_kp = g_orig_coarse_kp; + g_session.target_profile->coarse_kd = g_orig_coarse_kd; + g_session.target_profile->fine_kp = g_orig_fine_kp; + g_session.target_profile->fine_kd = g_orig_fine_kd; + } + + // Clear session state + g_session.state = AI_TUNING_IDLE; + memset(&g_session, 0, sizeof(ai_tuning_session_t)); + + // Exit charge mode so the trickler stops (only if was actually running) + if (was_active) { + charge_mode_config.charge_mode_state = CHARGE_MODE_EXIT; + ButtonEncoderEvent_t button_event = BUTTON_RST_PRESSED; + xQueueSend(encoder_event_queue, &button_event, 0); + } +} + +bool ai_tuning_is_active(void) { + return g_session.state == AI_TUNING_PHASE_1_COARSE || + g_session.state == AI_TUNING_PHASE_2_FINE; +} + +ai_motor_mode_t ai_tuning_get_motor_mode(void) { + switch (g_session.state) { + case AI_TUNING_PHASE_1_COARSE: + return AI_MOTOR_MODE_COARSE_ONLY; + case AI_TUNING_PHASE_2_FINE: + return AI_MOTOR_MODE_FINE_ONLY; + default: + return AI_MOTOR_MODE_NORMAL; + } +} + +uint8_t ai_tuning_get_progress_percent(void) { + if (g_session.max_drops_allowed == 0) { + return 0; + } + return (100 * g_session.drops_completed) / g_session.max_drops_allowed; +} + +// ============================================================================= +// Flash Persistence +// ============================================================================= + +static void load_history_from_flash(void) { + if (g_history_loaded) { + return; + } + + bool ok = flash_ml_history_read((uint8_t*)&g_history, sizeof(ai_tuning_history_t)); + + if (!ok || g_history.revision != AI_TUNING_HISTORY_REV) { + memset(&g_history, 0, sizeof(ai_tuning_history_t)); + g_history.revision = AI_TUNING_HISTORY_REV; + } + + g_history_loaded = true; +} + +static void save_history_to_flash(void) { + flash_ml_history_write((const uint8_t*)&g_history, sizeof(ai_tuning_history_t)); +} + +// ============================================================================= +// EEPROM Config Persistence +// ============================================================================= + +static void load_config_from_eeprom(void) { + if (g_config_loaded) { + return; + } + + bool ok = eeprom_read(EEPROM_AI_TUNING_CONFIG_BASE_ADDR, + (uint8_t*)&g_config_eeprom, + sizeof(ai_tuning_config_eeprom_t)); + + if (!ok || g_config_eeprom.revision != AI_TUNING_CONFIG_REV) { + // Keep defaults set in ai_tuning_init() + } else { + // Apply EEPROM config to runtime config + g_config.coarse_kp_min = g_config_eeprom.coarse_kp_min; + g_config.coarse_kp_max = g_config_eeprom.coarse_kp_max; + g_config.coarse_kd_min = g_config_eeprom.coarse_kd_min; + g_config.coarse_kd_max = g_config_eeprom.coarse_kd_max; + g_config.fine_kp_min = g_config_eeprom.fine_kp_min; + g_config.fine_kp_max = g_config_eeprom.fine_kp_max; + g_config.fine_kd_min = g_config_eeprom.fine_kd_min; + g_config.fine_kd_max = g_config_eeprom.fine_kd_max; + g_config.noise_margin = g_config_eeprom.noise_margin; + } + + g_config_loaded = true; +} + +static bool save_config_to_eeprom(void) { + // Copy runtime config to EEPROM structure + g_config_eeprom.revision = AI_TUNING_CONFIG_REV; + g_config_eeprom.coarse_kp_min = g_config.coarse_kp_min; + g_config_eeprom.coarse_kp_max = g_config.coarse_kp_max; + g_config_eeprom.coarse_kd_min = g_config.coarse_kd_min; + g_config_eeprom.coarse_kd_max = g_config.coarse_kd_max; + g_config_eeprom.fine_kp_min = g_config.fine_kp_min; + g_config_eeprom.fine_kp_max = g_config.fine_kp_max; + g_config_eeprom.fine_kd_min = g_config.fine_kd_min; + g_config_eeprom.fine_kd_max = g_config.fine_kd_max; + g_config_eeprom.noise_margin = g_config.noise_margin; + + bool ok = eeprom_write(EEPROM_AI_TUNING_CONFIG_BASE_ADDR, + (uint8_t*)&g_config_eeprom, + sizeof(ai_tuning_config_eeprom_t)); + + return ok; +} + +void ai_tuning_save_config(void) { + save_config_to_eeprom(); +} + +ai_tuning_history_t* ai_tuning_get_history(void) { + load_history_from_flash(); + return &g_history; +} + +void ai_tuning_record_charge(uint8_t profile_idx, + float coarse_kp, float coarse_kd, + float fine_kp, float fine_kd, + float overthrow, + float coarse_time_ms, float fine_time_ms) { + load_history_from_flash(); + + // Auto-clear history when profile changes so each profile gets a fresh session + if (g_history.count > 0 && profile_idx != g_history.current_profile_idx) { + memset(&g_history, 0, sizeof(ai_tuning_history_t)); + g_history.revision = AI_TUNING_HISTORY_REV; + } + g_history.current_profile_idx = profile_idx; + + ai_drop_record_t record = { + .coarse_kp = coarse_kp, + .coarse_kd = coarse_kd, + .fine_kp = fine_kp, + .fine_kd = fine_kd, + .overthrow = overthrow, + .coarse_time_ms = coarse_time_ms, + .fine_time_ms = fine_time_ms, + .total_time_ms = coarse_time_ms + fine_time_ms, + .profile_idx = profile_idx + }; + + g_history.drops[g_history.next_idx] = record; + g_history.next_idx = (g_history.next_idx + 1) % AI_TUNING_HISTORY_SIZE; + if (g_history.count < AI_TUNING_HISTORY_SIZE) { + g_history.count++; + } + + // Recalculate every 10 drops (at 10, 20, 30...) — first suggestion at drop 10 + if (g_history.count >= 10 && g_history.count % 10 == 0) { + ai_tuning_calculate_refinements(profile_idx); + } + + save_history_to_flash(); +} + +void ai_tuning_calculate_refinements(uint8_t profile_idx) { + load_history_from_flash(); + + if (g_history.count < 10) { + g_history.has_suggestions = false; + return; + } + + float avg_overthrow = 0.0f; + float avg_fine_kp = 0.0f, avg_fine_kd = 0.0f; + + for (int i = 0; i < g_history.count; i++) { + ai_drop_record_t* d = &g_history.drops[i]; + avg_overthrow += d->overthrow; + avg_fine_kp += d->fine_kp; + avg_fine_kd += d->fine_kd; + } + + float inv_count = 1.0f / g_history.count; + avg_overthrow *= inv_count; + avg_fine_kp *= inv_count; + avg_fine_kd *= inv_count; + + // ML only adjusts fine parameters - coarse stays unchanged + float fine_kp_adj = 0.0f, fine_kd_adj = 0.0f; + + if (avg_overthrow > g_config.noise_margin) { + // Overthrow: increase Kd to slow down + fine_kd_adj = 0.1f; + } else if (avg_overthrow < -g_config.noise_margin) { + // Underthrow: increase Kp to speed up + fine_kp_adj = 0.1f; + } + + // Coarse values unchanged (set to 0 to indicate no suggestion) + g_history.suggested_coarse_kp = 0.0f; + g_history.suggested_coarse_kd = 0.0f; + g_history.suggested_fine_kp = fminf(fmaxf(avg_fine_kp + fine_kp_adj, g_config.fine_kp_min), g_config.fine_kp_max); + g_history.suggested_fine_kd = fminf(fmaxf(avg_fine_kd + fine_kd_adj, g_config.fine_kd_min), g_config.fine_kd_max); + g_history.has_suggestions = true; +} + +bool ai_tuning_get_refined_params(float* coarse_kp, float* coarse_kd, + float* fine_kp, float* fine_kd) { + load_history_from_flash(); + + if (!g_history.has_suggestions) { + return false; + } + + *coarse_kp = g_history.suggested_coarse_kp; + *coarse_kd = g_history.suggested_coarse_kd; + *fine_kp = g_history.suggested_fine_kp; + *fine_kd = g_history.suggested_fine_kd; + + return true; +} + +bool ai_tuning_get_suggestions(uint8_t profile_idx, + float* coarse_kp, float* coarse_kd, + float* fine_kp, float* fine_kd) { + load_history_from_flash(); + + if (g_history.count < 3) { + return false; + } + + float sum_coarse_kp = 0, sum_coarse_kd = 0; + float sum_fine_kp = 0, sum_fine_kd = 0; + int count = 0; + + for (int i = 0; i < g_history.count; i++) { + ai_drop_record_t* d = &g_history.drops[i]; + if (profile_idx == 0xFF || d->profile_idx == profile_idx) { + sum_coarse_kp += d->coarse_kp; + sum_coarse_kd += d->coarse_kd; + sum_fine_kp += d->fine_kp; + sum_fine_kd += d->fine_kd; + count++; + } + } + + if (count < 3) { + if (g_history.has_suggestions) { + *coarse_kp = g_history.suggested_coarse_kp; + *coarse_kd = g_history.suggested_coarse_kd; + *fine_kp = g_history.suggested_fine_kp; + *fine_kd = g_history.suggested_fine_kd; + return true; + } + return false; + } + + float inv_count = 1.0f / count; + *coarse_kp = sum_coarse_kp * inv_count; + *coarse_kd = sum_coarse_kd * inv_count; + *fine_kp = sum_fine_kp * inv_count; + *fine_kd = sum_fine_kd * inv_count; + + return true; +} + +bool ai_tuning_apply_refined_params(uint8_t profile_idx) { + load_history_from_flash(); + + if (!g_history.has_suggestions) { + return false; + } + + profile_t* profile = profile_select(profile_idx); + if (!profile) { + return false; + } + + // ML only adjusts fine parameters - coarse stays unchanged + profile->fine_kp = g_history.suggested_fine_kp; + profile->fine_kd = g_history.suggested_fine_kd; + + profile_data_save(); + + g_history.count = 0; + g_history.next_idx = 0; + g_history.has_suggestions = false; + save_history_to_flash(); + + return true; +} + +void ai_tuning_clear_history(void) { + memset(&g_history, 0, sizeof(ai_tuning_history_t)); + g_history.revision = AI_TUNING_HISTORY_REV; + save_history_to_flash(); +} + +float ai_tuning_get_scale_compensation(void) { + return 0.0f; // Disabled - was over-engineering +} diff --git a/src/ai_tuning.h b/src/ai_tuning.h new file mode 100644 index 00000000..3dc96846 --- /dev/null +++ b/src/ai_tuning.h @@ -0,0 +1,177 @@ +#ifndef AI_TUNING_H_ +#define AI_TUNING_H_ + +#include +#include +#include "profile.h" + +#define AI_TUNING_HISTORY_SIZE 50 +#define AI_TUNING_HISTORY_REV 6 // Bumped: increased history buffer to 50 records +#define AI_TUNING_DROP_BUF_SIZE 10 +#define AI_TUNING_CONFIG_REV 2 // EEPROM config revision (v2: removed coarse_overthrow_max_percent) + +/** + * AI Tuning System + * + * Automatically tunes Kp and Kd parameters for both coarse and fine tricklers. + * Uses adaptive step-halving algorithm with GP refinement. + */ + +typedef enum { + AI_TUNING_IDLE = 0, + AI_TUNING_PHASE_1_COARSE, + AI_TUNING_PHASE_2_FINE, + AI_TUNING_COMPLETE, + AI_TUNING_ERROR +} ai_tuning_state_t; + +typedef enum { + AI_MOTOR_MODE_NORMAL = 0, + AI_MOTOR_MODE_COARSE_ONLY, + AI_MOTOR_MODE_FINE_ONLY +} ai_motor_mode_t; + +typedef struct { + uint8_t drop_number; + float coarse_time_ms; + float fine_time_ms; + float total_time_ms; + float final_weight; + float target_weight; + float overthrow; + float overthrow_percent; + float coarse_kp_used; + float coarse_kd_used; + float fine_kp_used; + float fine_kd_used; +} ai_drop_telemetry_t; + +typedef struct { + ai_tuning_state_t state; + profile_t* target_profile; + uint8_t drops_completed; + uint8_t max_drops_allowed; + uint8_t phase2_start_idx; + + // Circular buffer for last 10 drops + ai_drop_telemetry_t drops[AI_TUNING_DROP_BUF_SIZE]; + uint8_t drop_write_idx; + + float coarse_kp_best; + float coarse_kd_best; + float fine_kp_best; + float fine_kd_best; + + float recommended_coarse_kp; + float recommended_coarse_kd; + float recommended_fine_kp; + float recommended_fine_kd; + + float avg_overthrow; + float avg_total_time; + + char error_message[64]; +} ai_tuning_session_t; + +typedef struct { + float max_overthrow_percent; + + float coarse_kp_min; + float coarse_kp_max; + float coarse_kd_min; + float coarse_kd_max; + + float fine_kp_min; + float fine_kp_max; + float fine_kd_min; + float fine_kd_max; + + float noise_margin; + + // Tuning acceptance factors + float coarse_kp_max_factor; // Kp phase: threshold <= drop <= threshold * this (1.2) + float coarse_kd_max_factor; // Kd phase: drop <= threshold * this (1.1) + float fine_kp_max_factor; // Kp phase: target <= drop <= target * this (1.02) +} ai_tuning_config_t; + +typedef struct { + float coarse_kp; + float coarse_kd; + float fine_kp; + float fine_kd; + float overthrow; + float coarse_time_ms; + float fine_time_ms; + float total_time_ms; + uint8_t profile_idx; +} ai_drop_record_t; + +// AI tuning config stored in EEPROM (separate from ML history) +typedef struct { + uint16_t revision; + float coarse_kp_min; + float coarse_kp_max; + float coarse_kd_min; + float coarse_kd_max; + float fine_kp_min; + float fine_kp_max; + float fine_kd_min; + float fine_kd_max; + float noise_margin; +} __attribute__((packed)) ai_tuning_config_eeprom_t; + +// ML history stored in flash (runtime learning data) +typedef struct { + uint16_t revision; + uint8_t count; + uint8_t next_idx; + ai_drop_record_t drops[AI_TUNING_HISTORY_SIZE]; + float suggested_coarse_kp; + float suggested_coarse_kd; + float suggested_fine_kp; + float suggested_fine_kd; + bool has_suggestions; + float scale_latency_compensation; // Learned overthrow compensation (grains) + uint8_t current_profile_idx; // Profile for which data was collected (auto-clears on change) +} ai_tuning_history_t; + +#ifdef __cplusplus +extern "C" { +#endif + +void ai_tuning_init(void); +ai_tuning_config_t* ai_tuning_get_config(void); + +bool ai_tuning_start(profile_t* profile); +bool ai_tuning_record_drop(const ai_drop_telemetry_t* telemetry); +bool ai_tuning_get_next_params(float* coarse_kp, float* coarse_kd, float* fine_kp, float* fine_kd); +bool ai_tuning_is_complete(void); +ai_tuning_session_t* ai_tuning_get_session(void); +void ai_tuning_get_session_copy(ai_tuning_session_t* out); +void ai_tuning_get_history_copy(ai_tuning_history_t* out); +bool ai_tuning_get_recommended_params(float* coarse_kp, float* coarse_kd, float* fine_kp, float* fine_kd); +bool ai_tuning_apply_params(void); +void ai_tuning_cancel(void); +bool ai_tuning_is_active(void); +ai_motor_mode_t ai_tuning_get_motor_mode(void); +uint8_t ai_tuning_get_progress_percent(void); + +// ML Learning +ai_tuning_history_t* ai_tuning_get_history(void); +void ai_tuning_record_charge(uint8_t profile_idx, float coarse_kp, float coarse_kd, + float fine_kp, float fine_kd, float overthrow, + float coarse_time_ms, float fine_time_ms); +void ai_tuning_calculate_refinements(uint8_t profile_idx); +bool ai_tuning_get_refined_params(float* coarse_kp, float* coarse_kd, float* fine_kp, float* fine_kd); +bool ai_tuning_get_suggestions(uint8_t profile_idx, float* coarse_kp, float* coarse_kd, + float* fine_kp, float* fine_kd); +bool ai_tuning_apply_refined_params(uint8_t profile_idx); +void ai_tuning_clear_history(void); +void ai_tuning_save_config(void); +float ai_tuning_get_scale_compensation(void); // Get learned scale latency compensation + +#ifdef __cplusplus +} +#endif + +#endif // AI_TUNING_H_ diff --git a/src/app_state.c b/src/app_state.c new file mode 100644 index 00000000..f9da4159 --- /dev/null +++ b/src/app_state.c @@ -0,0 +1,9 @@ +/** + * @file app_state.c + * @brief Application state variable shared between display types + */ + +#include "app_state.h" + +// Global exit state - used for mode transitions +AppState_t exit_state = APP_STATE_DEFAULT; diff --git a/src/app_state.h b/src/app_state.h new file mode 100644 index 00000000..454fb83d --- /dev/null +++ b/src/app_state.h @@ -0,0 +1,23 @@ +/** + * @file app_state.h + * @brief Application state variable shared between display types + */ + +#ifndef APP_STATE_H +#define APP_STATE_H + +#include "app.h" // For AppState_t + +#ifdef __cplusplus +extern "C" { +#endif + +// Global exit state - used for mode transitions +// Defined in app_state.c +extern AppState_t exit_state; + +#ifdef __cplusplus +} +#endif + +#endif // APP_STATE_H diff --git a/src/charge_mode.cpp b/src/charge_mode.cpp index ad497e08..def538d5 100644 --- a/src/charge_mode.cpp +++ b/src/charge_mode.cpp @@ -21,6 +21,7 @@ #include "profile.h" #include "common.h" #include "servo_gate.h" +#include "ai_tuning.h" uint8_t charge_weight_digits[] = {0, 0, 0, 0, 0}; @@ -35,7 +36,7 @@ extern servo_gate_t servo_gate; const eeprom_charge_mode_data_t default_charge_mode_data = { .charge_mode_data_rev = 0, - .coarse_stop_threshold = 5, + .coarse_stop_threshold = 4, .fine_stop_threshold = 0.03, .set_point_sd_margin = 0.02, @@ -48,6 +49,26 @@ const eeprom_charge_mode_data_t default_charge_mode_data = { .precharge_time_ms = 1000, .precharge_speed_rps = 2, + // AI tuning time targets (defaults: 7000ms coarse pre-charge, 15000ms total) + .coarse_time_target_ms = 7000, + .total_time_target_ms = 15000, + + // ML data collection disabled by default + .ml_data_collection_enabled = false, + + // Auto zero disabled by default + .auto_zero_on_cup_return = false, + + // Pulse mode defaults (disabled by default) + .pulse_mode_enabled = false, + .pulse_threshold = 0.5f, // Start pulsing when within 0.5 grains (range: 0.3-1.0) + .pulse_duration_ms = 30, // 30ms motor burst + .pulse_wait_ms = 150, // 150ms wait for scale + + // Scale stabilization defaults + .stabilization_enabled = false, // adaptive by default + .stabilization_time_ms = 2000, // 2s fixed wait when enabled + // LED related .neopixel_normal_charge_colour = RGB_COLOUR_GREEN, // green .neopixel_under_charge_colour = RGB_COLOUR_YELLOW, // yellow @@ -62,6 +83,16 @@ static char title_string[30]; static TickType_t charge_start_tick = 0; static float last_charge_elapsed_seconds = 0.0f; +// Deferred ML recording (set in charge loop, written during cup removal to avoid flash blocking) +static bool ml_record_pending = false; +static float ml_coarse_time_ms = 0.0f; +static float ml_fine_time_ms = 0.0f; + +// Deferred AI tuning recording (same pattern - defer to cup removal for settled scale reading) +static bool ai_record_pending = false; +static ai_drop_telemetry_t ai_pending_telemetry; +static ai_motor_mode_t ai_pending_motor_mode; + // Menu system extern AppState_t exit_state; extern QueueHandle_t encoder_event_queue; @@ -104,7 +135,8 @@ void scale_measurement_render_task(void *p) { // Format the timer string based on current state if (charge_mode_config.charge_mode_state == CHARGE_MODE_WAIT_FOR_COMPLETE) { format_elapsed_time(time_buffer, sizeof(time_buffer), charge_start_tick); - } else if (charge_mode_config.charge_mode_state == CHARGE_MODE_WAIT_FOR_CUP_REMOVAL || + } else if (charge_mode_config.charge_mode_state == CHARGE_MODE_STABILIZING || + charge_mode_config.charge_mode_state == CHARGE_MODE_WAIT_FOR_CUP_REMOVAL || charge_mode_config.charge_mode_state == CHARGE_MODE_WAIT_FOR_CUP_RETURN || charge_mode_config.charge_mode_state == CHARGE_MODE_WAIT_FOR_ZERO) { snprintf(time_buffer, sizeof(time_buffer), "%.2f s", last_charge_elapsed_seconds); @@ -204,11 +236,20 @@ void charge_mode_wait_for_complete() { charge_start_tick = xTaskGetTickCount(); + // AI tuning: determine motor mode and get next params for this drop + ai_motor_mode_t ai_motor_mode = ai_tuning_get_motor_mode(); + bool ai_active = ai_tuning_is_active(); + float ai_coarse_kp = 0.0f, ai_coarse_kd = 0.0f; + float ai_fine_kp = 0.0f, ai_fine_kd = 0.0f; + if (ai_active) { + ai_tuning_get_next_params(&ai_coarse_kp, &ai_coarse_kd, &ai_fine_kp, &ai_fine_kd); + } + // Set colour to under charge neopixel_led_set_colour( neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.mini12864_backlight_colour, - charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, - charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, true ); @@ -216,17 +257,21 @@ void charge_mode_wait_for_complete() { if (servo_gate.eeprom_servo_gate_config.servo_gate_enable) { servo_gate_set_ratio(SERVO_GATE_RATIO_OPEN, false); } + // Update current status char target_weight_string[WEIGHT_STRING_LEN]; float_to_string(target_weight_string, charge_mode_config.target_charge_weight, charge_mode_config.eeprom_charge_mode_data.decimal_places); - - snprintf(title_string, sizeof(title_string), - "Target: %s", - target_weight_string); + snprintf(title_string, sizeof(title_string), "Target: %s", target_weight_string); // Read trickling parameter from the current profile profile_t * current_profile = profile_get_selected(); + // Use AI-tuned params if active, otherwise use profile params + float coarse_kp_used = ai_active ? ai_coarse_kp : current_profile->coarse_kp; + float coarse_kd_used = ai_active ? ai_coarse_kd : current_profile->coarse_kd; + float fine_kp_used = ai_active ? ai_fine_kp : current_profile->fine_kp; + float fine_kd_used = ai_active ? ai_fine_kd : current_profile->fine_kd; + // Find the minimum of max speed from the motor and the profile float coarse_trickler_max_speed = fmin(get_motor_max_speed(SELECT_COARSE_TRICKLER_MOTOR), current_profile->coarse_max_flow_speed_rps); @@ -237,18 +282,99 @@ void charge_mode_wait_for_complete() { float fine_trickler_min_speed = fmax(get_motor_min_speed(SELECT_FINE_TRICKLER_MOTOR), current_profile->fine_min_flow_speed_rps); + // Phase 2 (FINE_ONLY): run a coarse pre-charge using tuned coarse PID from Phase 1 + TickType_t coarse_stop_tick = 0; + if (ai_active && ai_motor_mode == AI_MOTOR_MODE_FINE_ONLY) { + ai_tuning_session_t *session = ai_tuning_get_session(); + float tuned_coarse_kp = session->recommended_coarse_kp; + float tuned_coarse_kd = session->recommended_coarse_kd; + + float precharge_target = charge_mode_config.target_charge_weight - + charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold; + + float precharge_integral = 0.0f; + float precharge_last_error = 0.0f; + TickType_t precharge_last_tick = xTaskGetTickCount(); + + int precharge_scale_fail_count = 0; + while (true) { + float current_weight; + if (!scale_block_wait_for_next_measurement(200, ¤t_weight)) { + precharge_scale_fail_count++; + if (precharge_scale_fail_count >= 10) { + // Scale disconnected for ~2 seconds - emergency stop + motor_set_speed(SELECT_BOTH_MOTOR, 0); + motor_enable(SELECT_COARSE_TRICKLER_MOTOR, false); + motor_enable(SELECT_FINE_TRICKLER_MOTOR, false); + if (servo_gate.eeprom_servo_gate_config.servo_gate_enable) { + servo_gate_set_ratio(SERVO_GATE_RATIO_CLOSED, false); + } + charge_mode_config.charge_mode_state = CHARGE_MODE_EXIT; + return; + } + continue; + } + precharge_scale_fail_count = 0; + + float precharge_error = precharge_target - current_weight; + + if (precharge_error < charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold) { + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); + // Settle before fine PID starts + vTaskDelay(pdMS_TO_TICKS(1000)); + coarse_stop_tick = xTaskGetTickCount(); + break; + } + + TickType_t now = xTaskGetTickCount(); + float dt_ms = (float)((now - precharge_last_tick) * portTICK_PERIOD_MS); + precharge_integral += precharge_error; + // Anti-windup clamp + precharge_integral = fmaxf(-50.0f, fminf(precharge_integral, 50.0f)); + float derivative = (dt_ms > 0.0f) ? (precharge_error - precharge_last_error) / dt_ms : 0.0f; + + float pid_output = tuned_coarse_kp * precharge_error + + current_profile->coarse_ki * precharge_integral + + tuned_coarse_kd * derivative; + if (pid_output <= 0.0f) { + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); + } else { + float spd = fmax(coarse_trickler_min_speed, fmin(pid_output, coarse_trickler_max_speed)); + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, spd); + } + + precharge_last_tick = now; + precharge_last_error = precharge_error; + + // User abort + ButtonEncoderEvent_t btn = button_wait_for_input(false); + if (btn == BUTTON_RST_PRESSED) { + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); + charge_mode_config.charge_mode_state = CHARGE_MODE_EXIT; + if (ai_active) ai_tuning_cancel(); + return; + } + } + } + float integral = 0.0f; float last_error = 0.0f; TickType_t last_sample_tick = xTaskGetTickCount(); TickType_t current_sample_tick = last_sample_tick; - bool should_coarse_trickler_move = true; + // In FINE_ONLY mode coarse trickler was already handled by pre-charge above + bool should_coarse_trickler_move = (ai_motor_mode != AI_MOTOR_MODE_FINE_ONLY); + + int scale_fail_count = 0; while (true) { // Non block waiting for the input ButtonEncoderEvent_t button_encoder_event = button_wait_for_input(false); if (button_encoder_event == BUTTON_RST_PRESSED) { charge_mode_config.charge_mode_state = CHARGE_MODE_EXIT; + if (ai_active) { + ai_tuning_cancel(); + } return; } @@ -257,14 +383,36 @@ void charge_mode_wait_for_complete() { float current_weight; if (!scale_block_wait_for_next_measurement(200, ¤t_weight)) { // If no measurement within 200ms then poll the button and retry + scale_fail_count++; + if (scale_fail_count >= 10) { + // Scale disconnected for ~2 seconds - emergency stop + motor_set_speed(SELECT_BOTH_MOTOR, 0); + motor_enable(SELECT_COARSE_TRICKLER_MOTOR, false); + motor_enable(SELECT_FINE_TRICKLER_MOTOR, false); + if (servo_gate.eeprom_servo_gate_config.servo_gate_enable) { + servo_gate_set_ratio(SERVO_GATE_RATIO_CLOSED, false); + } + if (ai_active) ai_tuning_cancel(); + charge_mode_config.charge_mode_state = CHARGE_MODE_EXIT; + return; + } continue; } + scale_fail_count = 0; current_sample_tick = xTaskGetTickCount(); float error = charge_mode_config.target_charge_weight - current_weight; - // Stop condition - if (error < charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold) { + // Stop condition - Phase 1 (COARSE_ONLY) exits at coarse threshold; others at fine threshold + if (ai_motor_mode == AI_MOTOR_MODE_COARSE_ONLY && + error <= charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold) { + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); + motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, 0); + coarse_stop_tick = xTaskGetTickCount(); + break; + } + + if (error <= charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold) { // Stop all motors motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, 0); motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); @@ -272,46 +420,70 @@ void charge_mode_wait_for_complete() { break; } - // Coarse trickler move condition + // Coarse trickler move condition else if (error < charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold && should_coarse_trickler_move) { should_coarse_trickler_move = false; motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); + coarse_stop_tick = xTaskGetTickCount(); - // NEW: When the coarse trickler stops, move the servo gate to a configured ratio + // When the coarse trickler stops, move the servo gate to a configured ratio // Ratio convention: 0.0 = open, 1.0 = close if (servo_gate.eeprom_servo_gate_config.servo_gate_enable) { float r = charge_mode_config.eeprom_charge_mode_data.coarse_stop_gate_ratio; - servo_gate_set_ratio(r, false); // don't block the charge loop } - // TODO: When turning off the coarse trickler, also move reverse to back off some powder } - // Update PID variables float elapse_time_ms = (current_sample_tick - last_sample_tick) / portTICK_RATE_MS; integral += error; float derivative = (error - last_error) / elapse_time_ms; - // Update fine trickler speed - float new_p = current_profile->fine_kp * error; - float new_i = current_profile->fine_ki * integral; - float new_d = current_profile->fine_kd * derivative; - float new_speed = fmax(fine_trickler_min_speed, fmin(new_p + new_i + new_d, fine_trickler_max_speed)); - - motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, new_speed); + float new_p, new_i, new_d, new_speed; + + // Update fine trickler speed (skip if COARSE_ONLY mode) + if (ai_motor_mode != AI_MOTOR_MODE_COARSE_ONLY) { + bool use_pulse = charge_mode_config.eeprom_charge_mode_data.pulse_mode_enabled && + error < charge_mode_config.eeprom_charge_mode_data.pulse_threshold && + error > charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold; + + if (use_pulse) { + // Pulse mode: short burst then wait for scale to settle + float pulse_speed = fmax(fine_trickler_min_speed, fine_trickler_max_speed * 0.3f); + motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, pulse_speed); + vTaskDelay(pdMS_TO_TICKS(charge_mode_config.eeprom_charge_mode_data.pulse_duration_ms)); + motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, 0); + vTaskDelay(pdMS_TO_TICKS(charge_mode_config.eeprom_charge_mode_data.pulse_wait_ms)); + } else { + new_p = fine_kp_used * error; + new_i = current_profile->fine_ki * integral; + new_d = fine_kd_used * derivative; + float fine_pid = new_p + new_i + new_d; + // Phase 2 FINE_ONLY: stop motor when PID <= 0 (don't force min speed past target) + if (ai_motor_mode == AI_MOTOR_MODE_FINE_ONLY && fine_pid <= 0.0f) { + motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, 0); + } else { + new_speed = fmax(fine_trickler_min_speed, fmin(fine_pid, fine_trickler_max_speed)); + motor_set_speed(SELECT_FINE_TRICKLER_MOTOR, new_speed); + } + } + } // Update coarse trickler speed if (should_coarse_trickler_move) { - new_p = current_profile->coarse_kp * error; + new_p = coarse_kp_used * error; new_i = current_profile->coarse_ki * integral; - new_d = current_profile->coarse_kd * derivative; - - new_speed = fmax(coarse_trickler_min_speed, fmin(new_p + new_i + new_d, coarse_trickler_max_speed)); - - motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, new_speed); + new_d = coarse_kd_used * derivative; + float coarse_pid = new_p + new_i + new_d; + // Phase 1 COARSE_ONLY: stop motor when PID <= 0 (don't force min speed past target) + if (ai_motor_mode == AI_MOTOR_MODE_COARSE_ONLY && coarse_pid <= 0.0f) { + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); + } else { + new_speed = fmax(coarse_trickler_min_speed, fmin(coarse_pid, coarse_trickler_max_speed)); + motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, new_speed); + } } // Record state @@ -319,19 +491,46 @@ void charge_mode_wait_for_complete() { last_error = error; } - // Stop the timer - TickType_t now = xTaskGetTickCount(); - TickType_t elapsed_ticks = now - charge_start_tick; + // Stop the timer + TickType_t end_tick = xTaskGetTickCount(); + TickType_t elapsed_ticks = end_tick - charge_start_tick; last_charge_elapsed_seconds = (float)(elapsed_ticks * portTICK_PERIOD_MS) / 1000.0f; + // Record AI telemetry or background ML data + float total_ms = (float)(elapsed_ticks * portTICK_PERIOD_MS); + float coarse_ms = coarse_stop_tick ? (float)((coarse_stop_tick - charge_start_tick) * portTICK_PERIOD_MS) : 0.0f; + float fine_ms = total_ms - coarse_ms; + + if (ai_active) { + // Defer AI telemetry to cup_removal (1s settle) for accurate weight reading, same as ML + ai_pending_telemetry.drop_number = ai_tuning_get_session()->drops_completed; + ai_pending_telemetry.coarse_time_ms = coarse_ms; + ai_pending_telemetry.fine_time_ms = fine_ms; + ai_pending_telemetry.total_time_ms = total_ms; + ai_pending_telemetry.coarse_kp_used = coarse_kp_used; + ai_pending_telemetry.coarse_kd_used = coarse_kd_used; + ai_pending_telemetry.fine_kp_used = fine_kp_used; + ai_pending_telemetry.fine_kd_used = fine_kd_used; + ai_pending_telemetry.target_weight = charge_mode_config.target_charge_weight; + ai_pending_motor_mode = ai_motor_mode; + ai_record_pending = true; + } + else if (charge_mode_config.eeprom_charge_mode_data.ml_data_collection_enabled) { + // Defer ML recording to cup removal phase — flash_safe_execute is too slow + // to call here without risking timeout while WiFi stack is active + ml_record_pending = true; + ml_coarse_time_ms = coarse_ms; + ml_fine_time_ms = fine_ms; + } + // Close the gate if the servo gate is present if (servo_gate.eeprom_servo_gate_config.servo_gate_enable) { - servo_gate_set_ratio(SERVO_GATE_RATIO_CLOSED, true); + servo_gate_set_ratio(SERVO_GATE_RATIO_CLOSED, true); } // Precharge if (charge_mode_config.eeprom_charge_mode_data.precharge_enable && - servo_gate.eeprom_servo_gate_config.servo_gate_enable) { + servo_gate.eeprom_servo_gate_config.servo_gate_enable) { // Set a fixed delay between closing the gate and precharge to allow the gate to fully close vTaskDelay(pdMS_TO_TICKS(500)); @@ -342,7 +541,34 @@ void charge_mode_wait_for_complete() { motor_set_speed(SELECT_COARSE_TRICKLER_MOTOR, 0); } else { - vTaskDelay(pdMS_TO_TICKS(20)); // Wait for other tasks to complete + vTaskDelay(pdMS_TO_TICKS(20)); // Wait for other tasks to complete + } + + charge_mode_config.charge_mode_state = CHARGE_MODE_STABILIZING; +} + +void charge_mode_stabilize() { + snprintf(title_string, sizeof(title_string), "Stabilizing..."); + + // Wait for scale to stabilize after motors stopped + if (charge_mode_config.eeprom_charge_mode_data.stabilization_enabled) { + // Fixed configured wait + vTaskDelay(pdMS_TO_TICKS(charge_mode_config.eeprom_charge_mode_data.stabilization_time_ms)); + } else { + // Adaptive: collect samples until SD < set_point_sd_margin or 3s timeout + FloatRingBuffer stab_buffer(5); + TickType_t stab_start = xTaskGetTickCount(); + const TickType_t stab_timeout = pdMS_TO_TICKS(3000); + while ((xTaskGetTickCount() - stab_start) < stab_timeout) { + float stab_weight; + if (scale_block_wait_for_next_measurement(200, &stab_weight)) { + stab_buffer.enqueue(stab_weight); + if (stab_buffer.getCounter() >= 5 && + stab_buffer.getSd() < charge_mode_config.eeprom_charge_mode_data.set_point_sd_margin) { + break; + } + } + } } charge_mode_config.charge_mode_state = CHARGE_MODE_WAIT_FOR_CUP_REMOVAL; @@ -354,13 +580,44 @@ void charge_mode_wait_for_cup_removal() { FloatRingBuffer data_buffer(5); - // Post charge analysis (while waiting for removal of the cup) - vTaskDelay(pdMS_TO_TICKS(1000)); // Wait for other tasks to complete - - // Take current measurement + // Take current measurement (settled reading used for ML overthrow too) float current_measurement = scale_get_current_measurement(); float error = charge_mode_config.target_charge_weight - current_measurement; + // Deferred AI tuning recording — use settled measurement for accurate weight + if (ai_record_pending) { + ai_record_pending = false; + // Phase 1: overthrow relative to coarse stop point; Phase 2+: relative to full target + float ai_effective_target; + if (ai_pending_motor_mode == AI_MOTOR_MODE_COARSE_ONLY) { + ai_effective_target = ai_pending_telemetry.target_weight - + charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold; + } else { + ai_effective_target = ai_pending_telemetry.target_weight; + } + float ai_overthrow = current_measurement - ai_effective_target; + ai_pending_telemetry.final_weight = current_measurement; + ai_pending_telemetry.overthrow = ai_overthrow; + ai_pending_telemetry.overthrow_percent = (ai_effective_target > 0.0f) + ? (ai_overthrow / ai_effective_target) * 100.0f + : 0.0f; + ai_tuning_record_drop(&ai_pending_telemetry); + } + + // Deferred ML recording — use settled measurement taken after 1s delay + if (ml_record_pending) { + ml_record_pending = false; + profile_t* ml_profile = profile_get_selected(); + float ml_overthrow = current_measurement - charge_mode_config.target_charge_weight; + ai_tuning_record_charge( + (uint8_t) profile_get_selected_idx(), + ml_profile->coarse_kp, ml_profile->coarse_kd, + ml_profile->fine_kp, ml_profile->fine_kd, + ml_overthrow, + ml_coarse_time_ms, ml_fine_time_ms + ); + } + // Update LED colour before moving to the next stage // Over charged if (error <= -charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold) { @@ -482,6 +739,11 @@ void charge_mode_wait_for_cup_return() { vTaskDelayUntil(&last_sample_tick, pdMS_TO_TICKS(20)); } + // Auto zero scale if enabled + if (charge_mode_config.eeprom_charge_mode_data.auto_zero_on_cup_return) { + scale_config.scale_handle->force_zero(); + } + charge_mode_config.charge_mode_state = CHARGE_MODE_WAIT_FOR_ZERO; } @@ -535,8 +797,15 @@ uint8_t charge_mode_menu(bool charge_mode_skip_user_input) { case CHARGE_MODE_WAIT_FOR_COMPLETE: charge_mode_wait_for_complete(); break; + case CHARGE_MODE_STABILIZING: + charge_mode_stabilize(); + break; case CHARGE_MODE_WAIT_FOR_CUP_REMOVAL: charge_mode_wait_for_cup_removal(); + // If AI tuning just completed, exit after the user removes the cup + if (ai_tuning_is_complete()) { + charge_mode_config.charge_mode_state = CHARGE_MODE_EXIT; + } break; case CHARGE_MODE_WAIT_FOR_CUP_RETURN: charge_mode_wait_for_cup_return(); @@ -605,9 +874,12 @@ bool http_rest_charge_mode_config(struct fs_file *file, int num_params, char *pa // c11 (int): precharge_time_ms // c12 (float): precharge_speed_rps // c13 (float): coarse_stop_gate_ratio + // c14 (uint32): coarse_time_target_ms (AI tuning coarse pre-charge duration) + // c15 (uint32): total_time_target_ms (AI tuning total time target) + // c16 (bool): ml_data_collection_enabled // ee (bool): save to eeprom - static char charge_mode_json_buffer[256]; + static char charge_mode_json_buffer[700]; bool save_to_eeprom = false; // Control @@ -642,6 +914,42 @@ bool http_rest_charge_mode_config(struct fs_file *file, int num_params, char *pa charge_mode_config.eeprom_charge_mode_data.coarse_stop_gate_ratio = strtof(values[idx], NULL); } + // AI tuning related settings + else if (strcmp(params[idx], "c14") == 0) { + charge_mode_config.eeprom_charge_mode_data.coarse_time_target_ms = (uint32_t) strtol(values[idx], NULL, 10); + } + else if (strcmp(params[idx], "c15") == 0) { + charge_mode_config.eeprom_charge_mode_data.total_time_target_ms = (uint32_t) strtol(values[idx], NULL, 10); + } + else if (strcmp(params[idx], "c16") == 0) { + charge_mode_config.eeprom_charge_mode_data.ml_data_collection_enabled = string_to_boolean(values[idx]); + } + else if (strcmp(params[idx], "c17") == 0) { + charge_mode_config.eeprom_charge_mode_data.auto_zero_on_cup_return = string_to_boolean(values[idx]); + } + + // Pulse mode settings + else if (strcmp(params[idx], "c18") == 0) { + charge_mode_config.eeprom_charge_mode_data.pulse_mode_enabled = string_to_boolean(values[idx]); + } + else if (strcmp(params[idx], "c19") == 0) { + float val = strtof(values[idx], NULL); + charge_mode_config.eeprom_charge_mode_data.pulse_threshold = fmaxf(0.3f, fminf(1.0f, val)); + } + else if (strcmp(params[idx], "c20") == 0) { + charge_mode_config.eeprom_charge_mode_data.pulse_duration_ms = (uint32_t) strtol(values[idx], NULL, 10); + } + else if (strcmp(params[idx], "c21") == 0) { + charge_mode_config.eeprom_charge_mode_data.pulse_wait_ms = (uint32_t) strtol(values[idx], NULL, 10); + } + + // Scale stabilization settings + else if (strcmp(params[idx], "c22") == 0) { + charge_mode_config.eeprom_charge_mode_data.stabilization_enabled = string_to_boolean(values[idx]); + } + else if (strcmp(params[idx], "c23") == 0) { + charge_mode_config.eeprom_charge_mode_data.stabilization_time_ms = (uint32_t) strtol(values[idx], NULL, 10); + } // LED related settings else if (strcmp(params[idx], "c1") == 0) { @@ -671,7 +979,10 @@ bool http_rest_charge_mode_config(struct fs_file *file, int num_params, char *pa sizeof(charge_mode_json_buffer), "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" "{\"c1\":\"#%06lx\",\"c2\":\"#%06lx\",\"c3\":\"#%06lx\",\"c4\":\"#%06lx\"," - "\"c5\":%.3f,\"c6\":%.3f,\"c7\":%.3f,\"c8\":%.3f,\"c9\":%d,\"c10\":%s,\"c11\":%ld,\"c12\":%0.3f,\"c13\":%0.3f}", + "\"c5\":%.3f,\"c6\":%.3f,\"c7\":%.3f,\"c8\":%.3f,\"c9\":%d,\"c10\":%s,\"c11\":%ld,\"c12\":%0.3f,\"c13\":%0.3f," + "\"c14\":%lu,\"c15\":%lu,\"c16\":%s,\"c17\":%s," + "\"c18\":%s,\"c19\":%.3f,\"c20\":%lu,\"c21\":%lu," + "\"c22\":%s,\"c23\":%lu}", charge_mode_config.eeprom_charge_mode_data.neopixel_normal_charge_colour._raw_colour, charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour._raw_colour, charge_mode_config.eeprom_charge_mode_data.neopixel_over_charge_colour._raw_colour, @@ -684,7 +995,17 @@ bool http_rest_charge_mode_config(struct fs_file *file, int num_params, char *pa boolean_to_string(charge_mode_config.eeprom_charge_mode_data.precharge_enable), charge_mode_config.eeprom_charge_mode_data.precharge_time_ms, charge_mode_config.eeprom_charge_mode_data.precharge_speed_rps, - charge_mode_config.eeprom_charge_mode_data.coarse_stop_gate_ratio); + charge_mode_config.eeprom_charge_mode_data.coarse_stop_gate_ratio, + (unsigned long) charge_mode_config.eeprom_charge_mode_data.coarse_time_target_ms, + (unsigned long) charge_mode_config.eeprom_charge_mode_data.total_time_target_ms, + boolean_to_string(charge_mode_config.eeprom_charge_mode_data.ml_data_collection_enabled), + boolean_to_string(charge_mode_config.eeprom_charge_mode_data.auto_zero_on_cup_return), + boolean_to_string(charge_mode_config.eeprom_charge_mode_data.pulse_mode_enabled), + charge_mode_config.eeprom_charge_mode_data.pulse_threshold, + (unsigned long) charge_mode_config.eeprom_charge_mode_data.pulse_duration_ms, + (unsigned long) charge_mode_config.eeprom_charge_mode_data.pulse_wait_ms, + boolean_to_string(charge_mode_config.eeprom_charge_mode_data.stabilization_enabled), + (unsigned long) charge_mode_config.eeprom_charge_mode_data.stabilization_time_ms); size_t data_length = strlen(charge_mode_json_buffer); file->data = charge_mode_json_buffer; diff --git a/src/charge_mode.h b/src/charge_mode.h index 78193ce8..84f9a8cf 100644 --- a/src/charge_mode.h +++ b/src/charge_mode.h @@ -7,7 +7,7 @@ #include "neopixel_led.h" -#define EEPROM_CHARGE_MODE_DATA_REV 8 // 16 byte +#define EEPROM_CHARGE_MODE_DATA_REV 13 // Added scale stabilization config #define WEIGHT_STRING_LEN 8 @@ -17,6 +17,7 @@ typedef enum { CHARGE_MODE_WAIT_FOR_COMPLETE = 2, CHARGE_MODE_WAIT_FOR_CUP_REMOVAL = 3, CHARGE_MODE_WAIT_FOR_CUP_RETURN = 4, + CHARGE_MODE_STABILIZING = 5, } charge_mode_state_t; typedef struct { @@ -36,6 +37,26 @@ typedef struct { uint32_t precharge_time_ms; float precharge_speed_rps; + // AI tuning time targets + uint32_t coarse_time_target_ms; + uint32_t total_time_target_ms; + + // ML data collection during normal (non-tuning) charges + bool ml_data_collection_enabled; + + // Auto zero scale when cup is returned + bool auto_zero_on_cup_return; + + // Pulse mode - helps with slow scales near target + bool pulse_mode_enabled; + float pulse_threshold; // Start pulsing when error < this (grains) + uint32_t pulse_duration_ms; // Motor on time per pulse + uint32_t pulse_wait_ms; // Wait time between pulses for scale to update + + // Scale stabilization after motors stop (before overthrow/underthrow decision) + bool stabilization_enabled; // true = fixed wait, false = adaptive SD-based + uint32_t stabilization_time_ms; // Fixed wait time when enabled (default 2000ms) + // LED related settings rgbw_u32_t neopixel_normal_charge_colour; rgbw_u32_t neopixel_under_charge_colour; diff --git a/src/display.h b/src/display.h index 5487e86e..c4ac7de6 100644 --- a/src/display.h +++ b/src/display.h @@ -9,6 +9,8 @@ extern "C" { #endif u8g2_t *get_display_handler(void); +void acquire_display_buffer_access(void); +void release_display_buffer_access(void); // REST bool http_get_display_buffer(struct fs_file *file, int num_params, char *params[], char *values[]); diff --git a/src/display_config.c b/src/display_config.c new file mode 100644 index 00000000..775b31b9 --- /dev/null +++ b/src/display_config.c @@ -0,0 +1,132 @@ +#include +#include +#include + +#include "display_config.h" +#include "eeprom.h" +#include "common.h" + +// Global config +static display_config_t display_config; + +bool display_config_save(void) { + bool is_ok = eeprom_write(EEPROM_DISPLAY_CONFIG_BASE_ADDR, (uint8_t *)&display_config, sizeof(display_config)); + return is_ok; +} + +// Get the compiled display type (cannot be changed at runtime) +static display_type_t get_compiled_display_type(void) { +#if defined(USE_TFT35) + return DISPLAY_TYPE_TFT35; +#elif defined(USE_TFT43) + return DISPLAY_TYPE_TFT43; +#else + return DISPLAY_TYPE_MINI_12864; +#endif +} + +bool display_config_init(void) { + bool is_ok; + + // Read configuration from EEPROM + memset(&display_config, 0x0, sizeof(display_config)); + is_ok = eeprom_read(EEPROM_DISPLAY_CONFIG_BASE_ADDR, (uint8_t *)&display_config, sizeof(display_config)); + + bool need_defaults = false; + if (!is_ok) { + printf("Unable to read display config from EEPROM at address %x, using defaults\n", EEPROM_DISPLAY_CONFIG_BASE_ADDR); + need_defaults = true; + } else if (display_config.data_rev != EEPROM_DISPLAY_CONFIG_DATA_REV) { + need_defaults = true; + } + + if (need_defaults) { + display_config.data_rev = EEPROM_DISPLAY_CONFIG_DATA_REV; + + // Set defaults + display_config.rotation = DISPLAY_ROTATION_0; + display_config.brightness = 255; // Max brightness + display_config.inverted_encoder = false; + + // Write back (may fail if no EEPROM) + if (is_ok && !display_config_save()) { + printf("Unable to write display config to %x\n", EEPROM_DISPLAY_CONFIG_BASE_ADDR); + } + } + + // Always use compiled display type (firmware-specific) + display_config.display_type = get_compiled_display_type(); + + printf("Display config loaded: type=%d, rotation=%d, brightness=%d\n", + display_config.display_type, display_config.rotation, display_config.brightness); + return true; +} + +display_type_t display_config_get_type(void) { + return display_config.display_type; +} + +void display_config_set_type(display_type_t type) { + display_config.display_type = type; +} + +display_config_t* display_config_get(void) { + return &display_config; +} + +bool http_rest_display_config(struct fs_file *file, int num_params, char *params[], char *values[]) { + // Mappings: + // d0 (int): display_type (0=Mini12864, 1=TFT35, 2=TFT43) - READ ONLY, set by firmware + // d1 (int): rotation (0=0°, 1=90°, 2=180°, 3=270°) + // d2 (int): brightness (0-255) + // d3 (bool): inverted_encoder + // ee (bool): save to eeprom + static char buf[256]; + bool save_to_eeprom = false; + + // Process parameters + for (int idx = 0; idx < num_params; idx++) { + // d0 (display_type) is ignored - it's firmware-specific and cannot be changed + if (strcmp(params[idx], "d1") == 0) { + int rot = atoi(values[idx]); + if (rot >= DISPLAY_ROTATION_0 && rot <= DISPLAY_ROTATION_270) { + display_config.rotation = (display_rotation_t)rot; + } + } + else if (strcmp(params[idx], "d2") == 0) { + int bright = atoi(values[idx]); + if (bright >= 0 && bright <= 255) { + display_config.brightness = (uint8_t)bright; + } + } + else if (strcmp(params[idx], "d3") == 0) { + display_config.inverted_encoder = string_to_boolean(values[idx]); + } + else if (strcmp(params[idx], "ee") == 0) { + save_to_eeprom = string_to_boolean(values[idx]); + } + } + + // Save to EEPROM if requested + if (save_to_eeprom) { + display_config_save(); + } + + // Response + snprintf(buf, sizeof(buf), + "%s" + "{\"d0\":%d,\"d1\":%d,\"d2\":%d,\"d3\":%s}", + http_json_header, + display_config.display_type, + display_config.rotation, + display_config.brightness, + boolean_to_string(display_config.inverted_encoder)); + + size_t response_len = strlen(buf); + file->data = buf; + file->len = response_len; + file->index = response_len; + file->flags = FS_FILE_FLAGS_HEADER_INCLUDED; + + return true; +} diff --git a/src/display_config.h b/src/display_config.h new file mode 100644 index 00000000..2acdcce9 --- /dev/null +++ b/src/display_config.h @@ -0,0 +1,41 @@ +#ifndef DISPLAY_CONFIG_H_ +#define DISPLAY_CONFIG_H_ + +#include +#include +#include "http_rest.h" +#include "mini_12864_module.h" + +#define EEPROM_DISPLAY_CONFIG_BASE_ADDR (13 * 1024) // 13K - Display type selection +#define EEPROM_DISPLAY_CONFIG_DATA_REV 2 + +typedef enum { + DISPLAY_TYPE_MINI_12864 = 0, // Default: Mini 12864 monochrome + DISPLAY_TYPE_TFT35 = 1, // TFT35 V3.0.1: 3.5 inch, 480x320 + DISPLAY_TYPE_TFT43 = 2, // TFT43 V3.0: 4.3 inch, 480x272 +} display_type_t; + +typedef struct { + uint32_t data_rev; + display_type_t display_type; + display_rotation_t rotation; + uint8_t brightness; // 0-255, for TFT displays + bool inverted_encoder; // For Mini 12864 rotary encoder +} display_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +bool display_config_init(void); +bool display_config_save(void); +display_type_t display_config_get_type(void); +void display_config_set_type(display_type_t type); +display_config_t* display_config_get(void); +bool http_rest_display_config(struct fs_file *file, int num_params, char *params[], char *values[]); + +#ifdef __cplusplus +} +#endif + +#endif // DISPLAY_CONFIG_H_ diff --git a/src/eeprom.h b/src/eeprom.h index 773e9fc5..a12330a7 100644 --- a/src/eeprom.h +++ b/src/eeprom.h @@ -16,6 +16,7 @@ #define EEPROM_MINI_12864_CONFIG_BASE_ADDR 8 * 1024 // 8k #define EEPROM_PROFILE_DATA_BASE_ADDR 9 * 1024 // 9k #define EEPROM_SERVO_GATE_CONFIG_BASE_ADDR 10 * 1024 // 10k +#define EEPROM_AI_TUNING_CONFIG_BASE_ADDR 14 * 1024 // 14k #define EEPROM_METADATA_REV 2 // 16 byte diff --git a/src/error.c b/src/error.c new file mode 100644 index 00000000..5ab05de4 --- /dev/null +++ b/src/error.c @@ -0,0 +1,322 @@ +#include +#include +#include +#include + +#include "error.h" +#include "neopixel_led.h" + +// Only include u8g2 display code for Mini 12864 builds +#ifndef USE_COLOR_TFT +#include "display.h" +#endif + +// Error system state +static error_system_state_t error_state = { + .eeprom_ready = false, + .neopixel_ready = false, + .display_ready = false, + .wifi_ready = false, +}; + +// Error log circular buffer +static error_code_t error_log[ERROR_LOG_SIZE]; +static uint8_t error_log_count = 0; +static uint8_t error_log_write_idx = 0; + +// Last error that occurred +static error_code_t last_error = ERR_NONE; +static bool error_occurred = false; + +// Error LED color (red) +#define ERROR_LED_COLOR 0xFF0000 + +#ifndef USE_COLOR_TFT +// Display layout constants (Mini 12864 only) +#define ERROR_TITLE_HEIGHT 13 +#define ERROR_LINE_HEIGHT 8 +#define ERROR_MAX_DISPLAY_LINES 5 +#endif + +void error_system_init(void) { + error_state.eeprom_ready = false; + error_state.neopixel_ready = false; + error_state.display_ready = false; + error_state.wifi_ready = false; + last_error = ERR_NONE; + error_occurred = false; + error_log_count = 0; + error_log_write_idx = 0; + memset(error_log, 0, sizeof(error_log)); +} + +void error_set_eeprom_ready(bool ready) { + error_state.eeprom_ready = ready; +} + +void error_set_neopixel_ready(bool ready) { + error_state.neopixel_ready = ready; +} + +void error_set_display_ready(bool ready) { + error_state.display_ready = ready; +} + +void error_set_wifi_ready(bool ready) { + error_state.wifi_ready = ready; +} + +// Get short category string (max 3 chars) +static const char* error_get_short_category(error_code_t code) { + if (code >= 100 && code < 200) return "EEP"; + if (code >= 200 && code < 300) return "DSP"; + if (code >= 300 && code < 400) return "LED"; + if (code >= 400 && code < 500) return "WIF"; + if (code >= 500 && code < 600) return "MOT"; + if (code >= 600 && code < 700) return "SCL"; + if (code >= 700 && code < 800) return "SRV"; + if (code >= 800 && code < 900) return "PRF"; + if (code >= 900 && code < 1000) return "CHG"; + if (code >= 1000 && code < 1100) return "RST"; + if (code >= 1100 && code < 1200) return "MEM"; + if (code >= 1200 && code < 1300) return "CAL"; + return "UNK"; +} + +const char* error_code_to_string(error_code_t code) { + switch (code) { + case ERR_NONE: return "No error"; + + // EEPROM + case ERR_EEPROM_I2C_INIT: return "EEPROM I2C init"; + case ERR_EEPROM_READ_FAIL: return "EEPROM read"; + case ERR_EEPROM_WRITE_FAIL: return "EEPROM write"; + case ERR_EEPROM_MUTEX_CREATE: return "EEPROM mutex"; + case ERR_EEPROM_HANDLER_ALLOC: return "EEPROM alloc"; + case ERR_EEPROM_INVALID_SIZE: return "EEPROM size"; + + // Display + case ERR_DISPLAY_MUTEX_CREATE: return "Display mutex"; + case ERR_DISPLAY_INIT_FAIL: return "Display init"; + case ERR_DISPLAY_TASK_CREATE: return "Display task"; + case ERR_DISPLAY_ENCODER_QUEUE_CREATE: return "Encoder queue"; + + // Neopixel + case ERR_NEOPIXEL_MUTEX_CREATE: return "Neopixel mutex"; + case ERR_NEOPIXEL_PIO_INIT: return "Neopixel PIO"; + + // WiFi + case ERR_WIFI_INIT_FAIL: return "WiFi init"; + case ERR_WIFI_CONNECT_FAIL: return "WiFi connect"; + case ERR_WIFI_QUEUE_CREATE: return "WiFi queue"; + case ERR_WIFI_TASK_CREATE: return "WiFi task"; + case ERR_WIFI_DNS_BIND_FAIL: return "DNS bind fail"; + + // Motors + case ERR_MOTOR_UART_INIT: return "Motor UART"; + case ERR_MOTOR_DRIVER_ALLOC: return "Motor alloc"; + case ERR_MOTOR_COARSE_INIT: return "Coarse motor"; + case ERR_MOTOR_FINE_INIT: return "Fine motor"; + case ERR_MOTOR_QUEUE_CREATE: return "Motor queue"; + case ERR_MOTOR_TASK_CREATE: return "Motor task"; + case ERR_MOTOR_INVALID_SELECT: return "Invalid motor"; + + // Scale + case ERR_SCALE_UART_INIT: return "Scale UART"; + case ERR_SCALE_SEMAPHORE_CREATE: return "Scale sema"; + case ERR_SCALE_MUTEX_CREATE: return "Scale mutex"; + case ERR_SCALE_TASK_CREATE: return "Scale task"; + case ERR_SCALE_DRIVER_SELECT: return "Scale driver"; + + // Servo + case ERR_SERVO_QUEUE_CREATE: return "Servo queue"; + case ERR_SERVO_SEMAPHORE_CREATE: return "Servo sema"; + case ERR_SERVO_TASK_CREATE: return "Servo task"; + + // Profile + case ERR_PROFILE_EEPROM_READ: return "Profile read"; + case ERR_PROFILE_EEPROM_WRITE: return "Profile write"; + + // Charge mode + case ERR_CHARGE_EEPROM_READ: return "Charge read"; + case ERR_CHARGE_EEPROM_WRITE: return "Charge write"; + + // REST + case ERR_REST_QUEUE_CREATE: return "REST queue"; + case ERR_REST_ALLOC_FAIL: return "REST alloc"; + + // Memory + case ERR_MEMORY_ALLOC: return "Memory alloc"; + case ERR_MEMORY_BUFFER_OVERFLOW: return "Buffer overflow"; + case ERR_MEMORY_STACK_OVERFLOW: return "Stack overflow"; + case ERR_MEMORY_MALLOC_FAILED: return "Malloc failed"; + + // Calibration + case ERR_CALIBRATE_TASK_CREATE: return "Calibrate task"; + + default: return "Unknown"; + } +} + +// Get error category from code (for serial output) +static const char* error_get_category(error_code_t code) { + if (code >= 100 && code < 200) return "EEPROM"; + if (code >= 200 && code < 300) return "DISPLAY"; + if (code >= 300 && code < 400) return "LED"; + if (code >= 400 && code < 500) return "WIFI"; + if (code >= 500 && code < 600) return "MOTOR"; + if (code >= 600 && code < 700) return "SCALE"; + if (code >= 700 && code < 800) return "SERVO"; + if (code >= 800 && code < 900) return "PROFILE"; + if (code >= 900 && code < 1000) return "CHARGE"; + if (code >= 1000 && code < 1100) return "REST"; + if (code >= 1100 && code < 1200) return "MEMORY"; + if (code >= 1200 && code < 1300) return "CALIBRATE"; + return "UNKNOWN"; +} + +#ifndef USE_COLOR_TFT +// Update display with scrolling error list (Mini 12864 only) +static void error_update_display(void) { + if (!error_state.display_ready) { + return; + } + + u8g2_t *display = get_display_handler(); + if (display == NULL) { + return; + } + + acquire_display_buffer_access(); + + u8g2_ClearBuffer(display); + + // Title with error count + u8g2_SetFont(display, u8g2_font_helvB08_tr); + char title[20]; + snprintf(title, sizeof(title), "ERRORS (%d)", error_log_count); + u8g2_DrawStr(display, 5, 10, title); + + // Line under title + u8g2_DrawHLine(display, 0, ERROR_TITLE_HEIGHT, u8g2_GetDisplayWidth(display)); + + // Show most recent errors (newest at bottom, scrolls up) + u8g2_SetFont(display, u8g2_font_4x6_tf); // Tiny fixed-width font, ~25 chars/row + + uint8_t lines_to_show = (error_log_count < ERROR_MAX_DISPLAY_LINES) + ? error_log_count + : ERROR_MAX_DISPLAY_LINES; + + // Calculate starting index for oldest error to display + int start_idx; + if (error_log_count <= ERROR_MAX_DISPLAY_LINES) { + // Show all errors from beginning + start_idx = 0; + } else { + // Show most recent ERROR_MAX_DISPLAY_LINES errors + start_idx = error_log_count - ERROR_MAX_DISPLAY_LINES; + } + + for (uint8_t i = 0; i < lines_to_show; i++) { + // Get error from log (accounting for circular buffer) + int log_idx; + if (error_log_count < ERROR_LOG_SIZE) { + log_idx = start_idx + i; + } else { + log_idx = (error_log_write_idx + start_idx + i) % ERROR_LOG_SIZE; + } + + error_code_t code = error_log[log_idx]; + + // Format: "EEP:105 EEPROM alloc" + char line[32]; + snprintf(line, sizeof(line), "%s:%d %s", + error_get_short_category(code), + (int)code, + error_code_to_string(code)); + + int y_pos = ERROR_TITLE_HEIGHT + 10 + (i * ERROR_LINE_HEIGHT); + u8g2_DrawStr(display, 2, y_pos, line); + } + + u8g2_SendBuffer(display); + + release_display_buffer_access(); +} +#endif + +void report_error(error_code_t code) { + if (code == ERR_NONE) { + return; + } + + // Store error + last_error = code; + error_occurred = true; + + // Add to error log + error_log[error_log_write_idx] = code; + error_log_write_idx = (error_log_write_idx + 1) % ERROR_LOG_SIZE; + if (error_log_count < ERROR_LOG_SIZE) { + error_log_count++; + } + + // 1. Always log to printf (USB serial) - available from boot + printf("ERROR [%s] %d: %s\n", + error_get_category(code), + (int)code, + error_code_to_string(code)); + + // 2. Set LED to red if neopixel is ready + if (error_state.neopixel_ready) { + // Set all LEDs to red to indicate error + // Use mutex-protected public API instead of raw _neopixel_led_set_colour + rgbw_u32_t error_colour = { ._raw_colour = ERROR_LED_COLOR }; + neopixel_led_set_colour(error_colour, error_colour, error_colour, false); + } + + // 3. Update display with scrolling error list (Mini 12864 only) +#ifndef USE_COLOR_TFT + error_update_display(); +#endif + // For TFT displays, errors are logged to serial only for now + // LVGL-based error display can be added later if needed + + // 4. WiFi/REST notification could be added here + // if (error_state.wifi_ready) { ... } +} + +error_code_t error_get_last(void) { + return last_error; +} + +void error_clear_last(void) { + last_error = ERR_NONE; + error_occurred = false; + error_log_count = 0; + error_log_write_idx = 0; +} + +bool error_has_occurred(void) { + return error_occurred; +} + +uint8_t error_get_count(void) { + return error_log_count; +} + +error_code_t error_get_at(uint8_t index) { + if (index >= error_log_count) { + return ERR_NONE; + } + + // Calculate actual index in circular buffer + int log_idx; + if (error_log_count < ERROR_LOG_SIZE) { + log_idx = index; + } else { + log_idx = (error_log_write_idx + index) % ERROR_LOG_SIZE; + } + + return error_log[log_idx]; +} diff --git a/src/error.h b/src/error.h new file mode 100644 index 00000000..0466e854 --- /dev/null +++ b/src/error.h @@ -0,0 +1,133 @@ +#ifndef ERROR_H_ +#define ERROR_H_ + +#include +#include + +// Error codes for all modules +// Format: ERR__ +typedef enum { + ERR_NONE = 0, + + // EEPROM errors (1xx) + ERR_EEPROM_I2C_INIT = 100, + ERR_EEPROM_READ_FAIL, + ERR_EEPROM_WRITE_FAIL, + ERR_EEPROM_MUTEX_CREATE, + ERR_EEPROM_HANDLER_ALLOC, + ERR_EEPROM_INVALID_SIZE, + + // Display errors (2xx) + ERR_DISPLAY_MUTEX_CREATE = 200, + ERR_DISPLAY_INIT_FAIL, + ERR_DISPLAY_TASK_CREATE, + ERR_DISPLAY_ENCODER_QUEUE_CREATE, + + // Neopixel LED errors (3xx) + ERR_NEOPIXEL_MUTEX_CREATE = 300, + ERR_NEOPIXEL_PIO_INIT, + + // Wireless errors (4xx) + ERR_WIFI_INIT_FAIL = 400, + ERR_WIFI_CONNECT_FAIL, + ERR_WIFI_QUEUE_CREATE, + ERR_WIFI_TASK_CREATE, + ERR_WIFI_DNS_BIND_FAIL, + + // Motor errors (5xx) + ERR_MOTOR_UART_INIT = 500, + ERR_MOTOR_DRIVER_ALLOC, + ERR_MOTOR_COARSE_INIT, + ERR_MOTOR_FINE_INIT, + ERR_MOTOR_QUEUE_CREATE, + ERR_MOTOR_TASK_CREATE, + ERR_MOTOR_INVALID_SELECT, + + // Scale errors (6xx) + ERR_SCALE_UART_INIT = 600, + ERR_SCALE_SEMAPHORE_CREATE, + ERR_SCALE_MUTEX_CREATE, + ERR_SCALE_TASK_CREATE, + ERR_SCALE_DRIVER_SELECT, + + // Servo gate errors (7xx) + ERR_SERVO_QUEUE_CREATE = 700, + ERR_SERVO_SEMAPHORE_CREATE, + ERR_SERVO_TASK_CREATE, + + // Profile errors (8xx) + ERR_PROFILE_EEPROM_READ = 800, + ERR_PROFILE_EEPROM_WRITE, + + // Charge mode errors (9xx) + ERR_CHARGE_EEPROM_READ = 900, + ERR_CHARGE_EEPROM_WRITE, + + // REST API errors (10xx) + ERR_REST_QUEUE_CREATE = 1000, + ERR_REST_ALLOC_FAIL, + + // Memory errors (11xx) + ERR_MEMORY_ALLOC = 1100, + ERR_MEMORY_BUFFER_OVERFLOW, + ERR_MEMORY_STACK_OVERFLOW, + ERR_MEMORY_MALLOC_FAILED, + + // Calibration errors (12xx) + ERR_CALIBRATE_TASK_CREATE = 1200, + + // Generic/unknown + ERR_UNKNOWN = 9999, +} error_code_t; + +// Initialization state flags (what's available for error reporting) +typedef struct { + bool eeprom_ready; + bool neopixel_ready; + bool display_ready; + bool wifi_ready; +} error_system_state_t; + +#ifdef __cplusplus +extern "C" { +#endif + +// Initialize error system (call early in main) +void error_system_init(void); + +// Mark subsystems as ready for error reporting +void error_set_eeprom_ready(bool ready); +void error_set_neopixel_ready(bool ready); +void error_set_display_ready(bool ready); +void error_set_wifi_ready(bool ready); + +// Main error reporting function +// Reports via all available channels (printf, LED, display, etc.) +void report_error(error_code_t code); + +// Get human-readable error string +const char* error_code_to_string(error_code_t code); + +// Get last error (for REST API queries) +error_code_t error_get_last(void); + +// Clear last error +void error_clear_last(void); + +// Check if system has any errors +bool error_has_occurred(void); + +// Get error count +uint8_t error_get_count(void); + +// Get error at index (0 = oldest in buffer) +error_code_t error_get_at(uint8_t index); + +// Maximum errors stored in log +#define ERROR_LOG_SIZE 8 + +#ifdef __cplusplus +} +#endif + +#endif // ERROR_H_ diff --git a/src/favicon.ico.h b/src/favicon.ico.h new file mode 100644 index 00000000..b3a7b56c --- /dev/null +++ b/src/favicon.ico.h @@ -0,0 +1,1307 @@ +// ---------------------------------------------------------- // +// This file is autogenerated by bin2header.py; do not edit! // +// ---------------------------------------------------------- // + +#ifndef FAVICON_ICO_H_ +#define FAVICON_ICO_H_ + +#include + +// HTTP header + binary data combined +static const unsigned char favicon_ico[] = { + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x30, + 0x20, 0x4f, 0x4b, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2d, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x2f, 0x78, 0x2d, 0x69, 0x63, 0x6f, 0x6e, 0x0d, 0x0a, 0x43, 0x61, 0x63, + 0x68, 0x65, 0x2d, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x3a, 0x20, + 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x38, 0x36, 0x34, 0x30, + 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x10, + 0x10, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x68, 0x04, 0x00, 0x00, 0x36, + 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x28, + 0x11, 0x00, 0x00, 0x9e, 0x04, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x01, + 0x00, 0x20, 0x00, 0x68, 0x26, 0x00, 0x00, 0xc6, 0x15, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, + 0x00, 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, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x80, 0x02, 0x00, + 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc0, 0xfc, 0x4d, 0x00, + 0xbf, 0xf7, 0x20, 0x00, 0xc1, 0xfd, 0x7c, 0x00, 0xc3, 0xfd, 0x7f, 0x00, + 0xc1, 0xfd, 0x7b, 0x00, 0xc1, 0xfd, 0x7b, 0x00, 0xc1, 0xfd, 0x7f, 0x00, + 0xc1, 0xfd, 0x77, 0x00, 0xb5, 0xea, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xc1, 0xfe, 0xba, 0x00, 0xbf, 0xfb, 0x74, 0x00, 0xaa, 0xd4, 0x0c, 0x00, + 0xb9, 0xf7, 0x21, 0x00, 0xc1, 0xfc, 0x99, 0x00, 0x9f, 0xdf, 0x08, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xb4, 0xe1, 0x11, 0x00, 0xbf, 0xfb, 0x7c, 0x00, + 0xc1, 0xf9, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xb9, 0xf7, 0x21, 0x00, 0x00, 0x00, 0x01, 0xc6, + 0x55, 0x00, 0x09, 0xc7, 0x80, 0x25, 0xa0, 0x14, 0xbb, 0xe6, 0xe4, 0x00, + 0xc2, 0xfc, 0xb4, 0x00, 0x9f, 0xdf, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xc1, 0xfc, 0x5b, 0x00, 0xc0, 0xfc, 0x51, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xb1, 0xeb, 0x0d, 0x00, 0xaa, 0xea, 0x0c, 0x00, 0x00, 0x00, 0x01, 0xc6, + 0x71, 0x00, 0x09, 0xe4, 0x76, 0x07, 0xb5, 0xe4, 0x76, 0x07, 0xff, 0xd0, + 0x7d, 0x1c, 0xe3, 0x11, 0xbc, 0xec, 0xc7, 0x00, 0xc1, 0xfd, 0x95, 0x00, + 0x9f, 0xdf, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xc1, 0xfb, 0x80, 0x00, 0xb9, 0xe8, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xc0, 0xfd, 0x72, 0x00, 0xb9, 0xf3, 0x16, 0x00, + 0x55, 0xaa, 0x03, 0xdf, 0x76, 0x0b, 0xb5, 0xe3, 0x76, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xb3, 0xba, 0x7d, 0x32, 0x76, 0x12, + 0xb0, 0xe9, 0xff, 0x00, 0xc2, 0xfe, 0xb3, 0x00, 0x9f, 0xdf, 0x08, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xc2, 0xf3, 0x15, 0x00, 0xc2, 0xfd, 0x75, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc2, 0xfd, 0x7e, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x78, 0xfd, 0x73, 0x5b, 0xa1, 0x9a, 0xff, 0xe2, + 0x76, 0x08, 0xff, 0xe3, 0x76, 0x07, 0xb4, 0xe1, 0x76, 0x06, 0x79, 0xe4, + 0x76, 0x07, 0xfe, 0xb9, 0x78, 0x35, 0xff, 0x12, 0xb1, 0xe9, 0xfe, 0x00, + 0xc1, 0xfd, 0x91, 0x00, 0x9f, 0xdf, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xc1, 0xfd, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xc0, 0xfb, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x52, 0xfd, 0xcd, 0x00, + 0x77, 0xfd, 0xfe, 0x6e, 0x9a, 0x87, 0x95, 0xe1, 0x76, 0x06, 0x79, 0xe4, + 0x76, 0x07, 0xfe, 0xe4, 0x76, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xfb, 0xc6, + 0x7e, 0x29, 0x63, 0x13, 0xbb, 0xe8, 0xd2, 0x00, 0xc2, 0xfc, 0xb4, 0x00, + 0x9f, 0xdf, 0x08, 0x00, 0xc2, 0xfd, 0x81, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xc0, 0xfb, 0x76, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x52, 0xfb, 0x41, 0x00, 0x52, 0xfb, 0x3e, 0x59, 0xa0, 0x97, 0x53, 0xe0, + 0x77, 0x0b, 0xfe, 0xe3, 0x76, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xfb, 0xe4, + 0x77, 0x07, 0x67, 0xe4, 0x76, 0x08, 0xc9, 0xcf, 0x7d, 0x1c, 0xe4, 0x14, + 0xbb, 0xe6, 0xe4, 0x00, 0xc2, 0xfb, 0x8d, 0x00, 0xc0, 0xfd, 0x82, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc1, 0xfd, 0x77, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x4f, 0xf6, 0x1d, 0x00, + 0x70, 0xfd, 0xef, 0x4b, 0xa4, 0xab, 0xff, 0xde, 0x77, 0x0b, 0xfb, 0xdf, + 0x76, 0x07, 0x68, 0xe3, 0x75, 0x08, 0xca, 0xe4, 0x76, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xc3, 0x80, 0x27, 0x89, 0x00, 0xb6, 0xf3, 0x15, 0x00, + 0xc2, 0xfd, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xc2, 0xfd, 0x6c, 0x00, 0xaf, 0xdf, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x53, 0xf5, 0x31, 0x00, 0x53, 0xfd, 0xff, 0x00, 0x72, 0xfd, 0xe2, 0x52, + 0xa0, 0xa0, 0x3e, 0x69, 0x9a, 0x8a, 0xb1, 0xe2, 0x76, 0x08, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0x99, 0x55, 0x55, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xbd, 0xf6, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xbc, 0xf2, 0x13, 0x00, 0xc0, 0xfd, 0x72, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x2a, 0xd4, 0x06, 0x00, 0x4f, 0xf9, 0x2a, 0x00, + 0x4b, 0xf0, 0x11, 0x00, 0x51, 0xfc, 0x52, 0x00, 0x78, 0xfd, 0xff, 0x5d, + 0xa1, 0x98, 0xff, 0xdf, 0x76, 0x0a, 0x97, 0x55, 0x55, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xb3, 0xe6, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xbe, 0xfc, 0x4a, 0x00, 0xbf, 0xfc, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x52, 0xfb, 0x44, 0x00, + 0x53, 0xfc, 0xc2, 0x00, 0x72, 0xfc, 0x57, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xbe, 0xfb, 0x47, 0x00, 0xc2, 0xfd, 0xa7, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc1, 0xfc, 0x4a, 0x00, + 0xc2, 0xfd, 0x6d, 0x00, 0xa2, 0xe8, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xaa, 0xe3, 0x09, 0x00, 0xc0, 0xfa, 0x6e, 0x00, 0xc1, 0xfa, 0x5e, 0x00, + 0xc0, 0xfb, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xb9, 0xf3, 0x16, 0x00, 0xc2, 0xfd, 0x6d, 0x00, + 0xc0, 0xfd, 0x72, 0x00, 0xbf, 0xfa, 0x70, 0x00, 0xc0, 0xfa, 0x71, 0x00, + 0xc0, 0xfd, 0x76, 0x00, 0xc0, 0xfd, 0x76, 0x00, 0xbd, 0xf7, 0x1f, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x02, 0x00, + 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 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, 0x28, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, + 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x04, 0x00, + 0xbf, 0xff, 0x04, 0x00, 0x99, 0x99, 0x05, 0x00, 0x80, 0x80, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xc5, 0xff, 0x30, 0x00, + 0x92, 0xb6, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8e, 0xc6, 0x09, 0x00, 0xc4, 0xff, 0x4a, 0x00, 0xc1, 0xfc, 0x95, 0x00, + 0xc2, 0xfc, 0xc9, 0x00, 0xc3, 0xff, 0xe8, 0x00, 0xc0, 0xfc, 0xf0, 0x00, + 0xc3, 0xfe, 0xef, 0x00, 0xc2, 0xfe, 0xe3, 0x00, 0xc1, 0xfb, 0xc0, 0x00, + 0xc2, 0xff, 0x86, 0x00, 0xbe, 0xf6, 0x3b, 0x00, 0x80, 0xff, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfb, 0xcb, 0x00, 0xc2, 0xff, 0x32, 0x00, + 0xa2, 0xd1, 0x0b, 0x00, 0xc1, 0xfd, 0x74, 0x00, 0xc3, 0xff, 0xe1, 0x00, + 0xc0, 0xfb, 0xbe, 0x00, 0xc2, 0xfd, 0x6d, 0x00, 0xbe, 0xfa, 0x33, 0x00, + 0xaf, 0xef, 0x10, 0x00, 0xaa, 0xff, 0x03, 0x00, 0x6d, 0x92, 0x07, 0x00, + 0xbf, 0xf2, 0x14, 0x00, 0xbf, 0xf6, 0x3c, 0x00, 0xc1, 0xfd, 0x7b, 0x00, + 0xc3, 0xff, 0xcd, 0x00, 0xc0, 0xfb, 0xd2, 0x00, 0xc0, 0xfc, 0x59, 0x00, + 0x80, 0x80, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xc2, 0xfe, 0xc8, 0x00, 0xbe, 0xfb, 0x72, 0x00, 0xc3, 0xfe, 0xd8, 0x00, + 0xc1, 0xfc, 0xa9, 0x00, 0xbe, 0xf3, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x80, 0x80, 0x02, 0x00, 0xc0, 0xfa, 0x6d, 0x00, + 0xb9, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x80, 0x02, 0x00, + 0xc0, 0xfb, 0x3d, 0x00, 0xc0, 0xfb, 0xc2, 0x00, 0xc2, 0xfe, 0xc0, 0x00, + 0xb6, 0xf0, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xc1, 0xfc, 0xc5, 0x00, + 0xc2, 0xfe, 0xea, 0x00, 0xba, 0xf5, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbf, 0xf9, 0x80, 0x00, 0xc2, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xb9, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x80, 0xff, 0x02, 0x00, 0xbd, 0xf8, 0x6d, 0x00, 0xc3, 0xfe, 0xe4, 0x00, + 0xbf, 0xf7, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbf, 0xf9, 0x5c, 0x00, 0xc1, 0xf8, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0xdd, 0x77, 0x08, 0x1e, 0x67, 0xa1, 0x8d, 0x8d, 0x00, + 0xc0, 0xfc, 0xf8, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xc1, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0xf7, 0x42, 0x00, 0xc3, 0xff, 0xe5, 0x00, + 0xc1, 0xfb, 0x3e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xde, 0x73, 0x08, 0x1f, 0xe3, + 0x76, 0x07, 0xdc, 0xe3, 0x76, 0x07, 0xf9, 0x74, 0x9c, 0x80, 0xa2, 0x00, + 0xc0, 0xfc, 0xf8, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xb9, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0xf4, 0x45, 0x00, 0xc3, 0xfe, 0xe4, 0x00, + 0xb7, 0xef, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0xde, + 0x73, 0x08, 0x1f, 0xe2, 0x75, 0x07, 0xdc, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xf8, 0x72, 0x9c, 0x82, 0x9f, 0x00, + 0xc0, 0xfc, 0xf8, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfb, 0x85, 0x00, + 0xb9, 0xf3, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf9, 0x76, 0x00, 0xc3, 0xfe, 0xba, 0x00, + 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xbe, 0xff, 0x2f, 0x00, 0xbc, 0xf4, 0x2e, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0xdf, 0x78, 0x08, 0x20, 0xe3, 0x76, 0x07, 0xdc, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xf8, 0x62, 0xa2, 0x93, 0x87, 0x00, + 0xc1, 0xfc, 0xa0, 0x00, 0xc2, 0xff, 0xdf, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xb9, 0xed, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xbf, 0xff, 0x04, 0x00, 0xc1, 0xfb, 0xcd, 0x00, 0xc2, 0xff, 0x50, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xfb, 0xc3, 0x00, 0xc2, 0xff, 0x4f, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x6c, 0x08, 0x21, 0xe4, + 0x76, 0x07, 0xdc, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xd9, 0xdb, 0x76, 0x09, 0x1c, 0x28, 0xa6, 0xd1, 0x59, 0x00, + 0xbd, 0xfb, 0xff, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xd9, 0x00, + 0xb9, 0xed, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xc3, 0xff, 0x4d, 0x00, 0xc1, 0xfc, 0xc8, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xbf, 0xf9, 0x28, 0x00, 0xc2, 0xfe, 0xde, 0x00, + 0x80, 0xaa, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xaa, 0xc6, 0x09, 0xd6, 0x7b, 0x15, 0xd9, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xd9, 0xe4, 0x76, 0x09, 0x1c, 0xe1, + 0x75, 0x08, 0x66, 0xdd, 0x74, 0x0e, 0xfc, 0x48, 0x81, 0xb0, 0xff, 0x00, + 0xbe, 0xfb, 0xff, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xd9, 0x00, + 0xb9, 0xed, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x99, 0x99, 0x05, 0x00, + 0xc3, 0xff, 0xde, 0x00, 0xb8, 0xf4, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xfb, 0x72, 0x00, 0xc0, 0xfb, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xfd, 0x76, 0x47, + 0xaa, 0xb0, 0xff, 0xde, 0x79, 0x0d, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xd9, 0xe4, + 0x76, 0x09, 0x1c, 0xde, 0x75, 0x08, 0x66, 0xe5, 0x76, 0x07, 0xfc, 0xe3, + 0x76, 0x07, 0xff, 0xdd, 0x75, 0x0e, 0xff, 0x48, 0x81, 0xb0, 0xff, 0x00, + 0xbe, 0xfb, 0xff, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfe, 0xd8, 0x00, + 0xb6, 0xed, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xc2, 0xfd, 0x92, 0x00, + 0xc1, 0xff, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xc3, 0xff, 0xa9, 0x00, + 0xbf, 0xf9, 0x50, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xf9, 0x5a, 0x00, 0x6a, 0xff, 0xfb, 0x00, 0xba, 0xfd, 0xff, 0x48, + 0xa9, 0xae, 0xff, 0xdf, 0x78, 0x0d, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x77, 0x07, 0xd9, 0xd4, 0x6e, 0x08, 0x1e, 0xe1, 0x75, 0x08, 0x66, 0xe5, + 0x76, 0x07, 0xfc, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xdd, 0x75, 0x0e, 0xff, 0x48, 0x81, 0xb0, 0xff, 0x00, + 0xbe, 0xfb, 0xfd, 0x00, 0xc2, 0xff, 0xc5, 0x00, 0xc0, 0xfc, 0x9b, 0x00, + 0xb9, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfc, 0x55, 0x00, 0xc1, 0xfc, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfb, 0xce, 0x00, 0xc1, 0xf8, 0x25, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x53, 0xff, 0x9d, 0x00, + 0x52, 0xfb, 0xff, 0x00, 0x6a, 0xfe, 0xff, 0x00, 0xba, 0xfd, 0xff, 0x4a, + 0xa9, 0xad, 0xff, 0xd8, 0x7b, 0x15, 0xd6, 0xce, 0x73, 0x08, 0x1f, 0xe6, + 0x77, 0x08, 0x65, 0xe4, 0x76, 0x07, 0xfc, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xdf, 0x76, 0x0c, 0xe8, 0x0a, 0xbd, 0xf5, 0x32, 0x00, + 0xc0, 0xfb, 0xba, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xb9, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xc2, 0xff, 0x2e, 0x00, 0xc2, 0xfe, 0xd5, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xc3, 0xff, 0xdd, 0x00, 0xa6, 0xd9, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x52, 0xfc, 0x99, 0x00, 0x53, 0xfe, 0xff, 0x00, + 0x52, 0xfb, 0xff, 0x00, 0x69, 0xfe, 0xfc, 0x00, 0xb2, 0xfd, 0x77, 0x00, + 0x8e, 0xc6, 0x09, 0xe3, 0x77, 0x08, 0x65, 0xe3, 0x75, 0x07, 0xfc, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x06, 0xee, 0xdf, + 0x72, 0x09, 0x38, 0xe1, 0x73, 0x08, 0x3c, 0x6b, 0x9e, 0x8a, 0x96, 0x00, + 0xc0, 0xfc, 0xf8, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xb9, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xe6, 0x1e, 0x00, + 0xc3, 0xff, 0xe5, 0x00, 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xc2, 0xfd, 0xdd, 0x00, + 0xbb, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xfc, 0x57, 0x00, 0x52, 0xfc, 0xab, 0x00, 0x52, 0xff, 0xa1, 0x00, + 0x52, 0xf6, 0x57, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x7a, 0x19, 0x5c, 0xe4, + 0x76, 0x07, 0xfc, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe4, + 0x77, 0x06, 0xee, 0xdb, 0x72, 0x09, 0x38, 0xe4, 0x76, 0x08, 0x41, 0xe3, + 0x76, 0x07, 0xf3, 0xe3, 0x76, 0x07, 0xf8, 0x73, 0x9c, 0x81, 0xa0, 0x00, + 0xc0, 0xfc, 0xf8, 0x00, 0xc3, 0xfe, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xb6, 0xed, 0x1c, 0x00, 0xbf, 0xf6, 0x1c, 0x00, 0xc1, 0xfd, 0xe5, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xfc, 0xcc, 0x00, 0xb7, 0xef, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0xcc, 0x0f, 0x31, 0xb2, 0xc9, 0xe1, 0xd2, 0x7b, 0x19, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x08, 0xee, 0xdf, 0x74, 0x09, 0x37, 0xe4, + 0x76, 0x08, 0x41, 0xe3, 0x76, 0x07, 0xf3, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xf8, 0x72, 0x9b, 0x82, 0xa1, 0x00, + 0xc0, 0xfc, 0xf8, 0x00, 0xc3, 0xff, 0xfa, 0x00, 0xbe, 0xf7, 0x43, 0x00, + 0xbb, 0xfa, 0x31, 0x00, 0xc3, 0xff, 0xd5, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xc2, 0xff, 0xa8, 0x00, 0xbd, 0xf8, 0x46, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x44, 0xdd, 0x0f, 0x00, 0x59, 0xfe, 0xbf, 0x00, + 0xb0, 0xfb, 0xff, 0x2d, 0xb3, 0xcd, 0xff, 0xd2, 0x7b, 0x19, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x76, 0x08, 0xee, 0xe3, + 0x74, 0x05, 0x37, 0xe0, 0x74, 0x08, 0x42, 0xe3, 0x76, 0x07, 0xf3, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xf8, 0x55, 0xa6, 0xa1, 0x75, 0x00, + 0xbd, 0xf9, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xfc, 0x58, 0x00, + 0xc1, 0xfc, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf8, 0x71, 0x00, + 0xc2, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0xfc, 0x65, 0x00, 0x53, 0xfd, 0xff, 0x00, 0x5f, 0xfe, 0xff, 0x00, + 0xb1, 0xfb, 0xff, 0x2d, 0xb3, 0xcd, 0xff, 0xd2, 0x7b, 0x19, 0xff, 0xe3, + 0x76, 0x08, 0xee, 0xe3, 0x74, 0x05, 0x37, 0xd9, 0x72, 0x08, 0x43, 0xe5, + 0x77, 0x07, 0xf3, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xaf, 0xbf, 0x60, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xc3, 0xff, 0x95, 0x00, 0xc2, 0xfd, 0x79, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xc1, 0xf8, 0x25, 0x00, 0xc0, 0xfb, 0xcc, 0x00, + 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x52, 0xfc, 0x63, 0x00, + 0x53, 0xfd, 0xff, 0x00, 0x53, 0xfd, 0xff, 0x00, 0x60, 0xfe, 0xff, 0x00, + 0xb1, 0xfb, 0xff, 0x34, 0xb0, 0xc6, 0xbf, 0xcd, 0x78, 0x19, 0x33, 0x23, + 0xb1, 0xcd, 0x24, 0xdd, 0x79, 0x10, 0xf4, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe1, 0x75, 0x07, 0xb0, 0xdb, 0x6d, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbf, 0xfb, 0xb8, 0x00, 0xbe, 0xf9, 0x2b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xc3, 0xff, 0xbf, 0x00, 0xba, 0xf2, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0xf7, 0x5e, 0x00, 0x53, 0xfe, 0xff, 0x00, + 0x53, 0xfd, 0xff, 0x00, 0x52, 0xfc, 0xff, 0x00, 0x57, 0xfd, 0x8d, 0x00, + 0x40, 0x80, 0x04, 0x00, 0x55, 0xaa, 0x03, 0x00, 0xb2, 0xfc, 0xad, 0x4c, + 0xa8, 0xab, 0xff, 0xdf, 0x78, 0x0c, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xaf, 0xdb, + 0x6d, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0xff, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xc0, 0xfc, 0x49, 0x00, 0xc2, 0xff, 0xb8, 0x00, 0x40, 0x40, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x51, 0xff, 0x13, 0x00, 0x51, 0xf6, 0x58, 0x00, 0x51, 0xfc, 0x4f, 0x00, + 0x51, 0xf7, 0x3f, 0x00, 0x00, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xfb, 0x8c, 0x00, 0x6a, 0xfe, 0xff, 0x00, 0xba, 0xfd, 0xff, 0x4b, + 0xa9, 0xac, 0xff, 0xdf, 0x78, 0x0d, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x76, 0x07, 0xaf, 0xbf, 0x60, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x02, 0x00, + 0xc1, 0xfc, 0xb2, 0x00, 0xc1, 0xfc, 0x5b, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x53, 0xff, 0xbc, 0x00, + 0x52, 0xfb, 0xff, 0x00, 0x6a, 0xfe, 0xff, 0x00, 0xba, 0xfd, 0xff, 0x4a, + 0xa9, 0xad, 0xfe, 0xd7, 0x7b, 0x14, 0xa6, 0xaa, 0x71, 0x1c, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0xf4, 0x30, 0x00, 0xc1, 0xf6, 0x1d, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xf5, 0x1a, 0x00, + 0xc1, 0xfd, 0xd7, 0x00, 0xc1, 0xff, 0x2d, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x52, 0xfc, 0xb8, 0x00, 0x53, 0xfe, 0xff, 0x00, + 0x52, 0xfb, 0xff, 0x00, 0x69, 0xff, 0xec, 0x00, 0xb0, 0xf8, 0x44, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xbd, 0xf8, 0x23, 0x00, + 0xc3, 0xff, 0xe8, 0x00, 0xbf, 0xfa, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xfa, 0x36, 0x00, + 0xc1, 0xfd, 0xd6, 0x00, 0xc4, 0xff, 0x2b, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xf9, 0x57, 0x00, 0x53, 0xfd, 0x8a, 0x00, 0x52, 0xfd, 0x80, 0x00, + 0x4e, 0xf4, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xbf, 0xff, 0x20, 0x00, 0xc2, 0xfd, 0xd8, 0x00, 0xc2, 0xfe, 0xdd, 0x00, + 0xc1, 0xff, 0x6b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xfa, 0x37, 0x00, + 0xc2, 0xfd, 0xd6, 0x00, 0xc2, 0xff, 0x50, 0x00, 0x55, 0x55, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xc2, 0xfb, 0x43, 0x00, 0xc1, 0xfc, 0xdf, 0x00, + 0xc3, 0xff, 0x5e, 0x00, 0xc0, 0xfb, 0x85, 0x00, 0xc0, 0xfc, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0xf6, 0x1d, 0x00, + 0xc1, 0xfe, 0xb9, 0x00, 0xc2, 0xfe, 0xaa, 0x00, 0xb6, 0xed, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xbf, 0xf7, 0x20, 0x00, 0xc0, 0xfc, 0x9e, 0x00, + 0xc2, 0xfd, 0xd4, 0x00, 0xc3, 0xff, 0x37, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x03, 0x00, + 0xbf, 0xfc, 0x54, 0x00, 0xc3, 0xff, 0xc9, 0x00, 0xc0, 0xfc, 0xb6, 0x00, + 0xc1, 0xfc, 0x63, 0x00, 0xbe, 0xf8, 0x27, 0x00, 0x92, 0xdb, 0x07, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x40, 0x40, 0x04, 0x00, 0x99, 0xff, 0x05, 0x00, + 0xbf, 0xf8, 0x24, 0x00, 0xc1, 0xfc, 0x5e, 0x00, 0xc3, 0xff, 0xaf, 0x00, + 0xc0, 0xfc, 0xdc, 0x00, 0xc2, 0xff, 0x70, 0x00, 0xaa, 0xe3, 0x09, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x55, 0x55, 0x03, 0x00, 0xbf, 0xff, 0x34, 0x00, 0xbf, 0xf9, 0x84, 0x00, + 0xc2, 0xff, 0xbc, 0x00, 0xc1, 0xfd, 0xda, 0x00, 0xc2, 0xfd, 0xdc, 0x00, + 0xc3, 0xff, 0xdc, 0x00, 0xc1, 0xfc, 0xde, 0x00, 0xc3, 0xff, 0xc5, 0x00, + 0xc1, 0xfd, 0x90, 0x00, 0xbd, 0xf8, 0x49, 0x00, 0xcc, 0xff, 0x05, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0xff, 0x01, 0x00, 0x80, 0xbf, 0x04, 0x00, 0x99, 0x99, 0x05, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, + 0x00, 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, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x04, 0x00, 0x92, 0xb6, 0x07, 0x00, + 0xd4, 0xff, 0x06, 0x00, 0xaa, 0xff, 0x06, 0x00, 0x92, 0xb6, 0x07, 0x00, + 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xb9, 0xff, 0x0b, 0x00, 0xc8, 0xff, 0x0e, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0xaa, 0xea, 0x0c, 0x00, + 0xc3, 0xff, 0x1e, 0x00, 0xc1, 0xfb, 0x42, 0x00, 0xc1, 0xfb, 0x7f, 0x00, + 0xc3, 0xfe, 0xb2, 0x00, 0xc3, 0xff, 0xd6, 0x00, 0xc1, 0xfd, 0xea, 0x00, + 0xc2, 0xfe, 0xf2, 0x00, 0xc2, 0xff, 0xf0, 0x00, 0xc2, 0xfe, 0xe5, 0x01, + 0xc0, 0xfb, 0xcd, 0x00, 0xc2, 0xfc, 0xa3, 0x00, 0xc1, 0xfd, 0x6b, 0x00, + 0xc3, 0xff, 0x33, 0x09, 0xad, 0xe4, 0x1c, 0x00, 0xb6, 0xff, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xc0, 0xfb, 0x75, 0x00, 0xc2, 0xfd, 0x81, 0x00, + 0xcc, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xbf, 0xff, 0x14, 0x00, 0xc1, 0xfc, 0x5a, 0x00, 0xc0, 0xfa, 0x9e, 0x00, + 0xc3, 0xfe, 0xd7, 0x00, 0xc4, 0xff, 0xed, 0x00, 0xc0, 0xfc, 0xcc, 0x00, + 0xc0, 0xfb, 0xab, 0x00, 0xc3, 0xff, 0x91, 0x00, 0xc3, 0xff, 0x80, 0x00, + 0xbe, 0xfb, 0x79, 0x00, 0xc1, 0xfb, 0x7c, 0x00, 0xc3, 0xff, 0x84, 0x00, + 0xc2, 0xff, 0x96, 0x00, 0xc0, 0xfb, 0xb2, 0x00, 0xc1, 0xfd, 0xd7, 0x00, + 0xc4, 0xff, 0xec, 0x00, 0xc3, 0xfe, 0xc7, 0x00, 0xc0, 0xf9, 0x8a, 0x00, + 0xc1, 0xff, 0x42, 0x00, 0xb6, 0xff, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfa, 0xa9, 0x00, 0xbf, 0xfb, 0xb3, 0x00, + 0xc3, 0xff, 0x11, 0x00, 0xcc, 0xff, 0x05, 0x00, 0xbd, 0xf6, 0x55, 0x00, + 0xc2, 0xfe, 0xca, 0x00, 0xc2, 0xff, 0xf0, 0x00, 0xc1, 0xfe, 0xd6, 0x02, + 0xbf, 0xf9, 0x7b, 0x00, 0xc1, 0xfa, 0x31, 0x00, 0xba, 0xff, 0x1a, 0x00, + 0xbb, 0xee, 0x0f, 0x1a, 0x99, 0xb3, 0x0a, 0x00, 0x80, 0xff, 0x02, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x55, 0x55, 0x03, 0x00, 0x99, 0x99, 0x05, 0x00, + 0xbf, 0xff, 0x08, 0x00, 0xc3, 0xff, 0x11, 0x00, 0xb9, 0xf0, 0x21, 0x00, + 0xbf, 0xfb, 0x40, 0x00, 0xc4, 0xff, 0x93, 0x00, 0xc2, 0xfe, 0xe5, 0x01, + 0xc0, 0xfb, 0xea, 0x00, 0xc1, 0xfc, 0xb1, 0x00, 0xc2, 0xff, 0x32, 0x00, + 0x55, 0x55, 0x03, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc1, 0xfd, 0xa5, 0x00, 0xc0, 0xfb, 0xba, 0x00, + 0xba, 0xf5, 0x34, 0x00, 0xc3, 0xff, 0x9e, 0x00, 0xc2, 0xff, 0xf0, 0x01, + 0xc0, 0xfa, 0xd1, 0x00, 0xbf, 0xfc, 0x63, 0x00, 0xbf, 0xff, 0x0c, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xff, 0xff, 0x01, 0x00, 0xbd, 0xf8, 0x23, 0x00, 0xb8, 0xed, 0x2b, 0x00, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x80, 0x02, 0x00, 0xbd, 0xff, 0x1b, 0x00, + 0xc2, 0xfd, 0x85, 0x01, 0xc0, 0xfa, 0xe1, 0x00, 0xc2, 0xfe, 0xe6, 0x00, + 0xc2, 0xff, 0x70, 0x00, 0xbc, 0xf2, 0x13, 0x00, 0x55, 0x55, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xc1, 0xfd, 0xa1, 0x00, 0xc2, 0xff, 0xe3, 0x00, + 0xc2, 0xfc, 0xdc, 0x00, 0xc0, 0xfd, 0xe0, 0x00, 0xc2, 0xff, 0x76, 0x00, + 0xbf, 0xff, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x80, 0x04, 0x00, + 0xbf, 0xff, 0x44, 0x00, 0xc2, 0xff, 0xd5, 0x00, 0xc2, 0xfd, 0xd9, 0x00, + 0xbe, 0xf9, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xff, 0x01, 0x00, 0xc1, 0xf9, 0x2d, 0x00, 0xbf, 0xfa, 0x94, 0x00, + 0xc2, 0xfe, 0xed, 0x00, 0xc3, 0xff, 0xad, 0x00, 0xba, 0xf5, 0x34, 0x00, + 0x80, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0xc0, 0xfa, 0x9d, 0x00, 0xc3, 0xff, 0xfe, 0x00, + 0xc2, 0xff, 0xc6, 0x00, 0xba, 0xf4, 0x30, 0x00, 0x9f, 0xbf, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xf2, 0x27, 0x01, + 0xbf, 0xfa, 0xd3, 0x00, 0xc2, 0xfd, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, + 0xc3, 0xfe, 0xe8, 0x03, 0xbb, 0xf5, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xea, 0x0c, 0x03, + 0xbe, 0xf6, 0x52, 0x00, 0xc3, 0xfe, 0xe8, 0x00, 0xc3, 0xff, 0xcf, 0x00, + 0xc1, 0xfa, 0x31, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0xfc, 0x66, 0x00, 0xc0, 0xfc, 0xa2, 0x00, + 0xc8, 0xff, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x99, 0x66, 0x00, 0x05, 0x94, 0x8e, 0x5f, 0x2b, 0x11, + 0xbd, 0xeb, 0xb6, 0x01, 0xc0, 0xfb, 0xfb, 0x00, 0xc2, 0xfe, 0xff, 0x00, + 0xc3, 0xff, 0xff, 0x00, 0xc3, 0xfe, 0xe8, 0x00, 0xbe, 0xf9, 0x4f, 0x00, + 0xbf, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x05, 0xbd, 0xf1, 0x36, 0x00, 0xc2, 0xfe, 0xd2, 0x00, + 0xc3, 0xff, 0xde, 0x00, 0xbf, 0xfa, 0x30, 0x00, 0x40, 0x80, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x02, 0x00, 0xb6, 0xdb, 0x07, 0x00, + 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xde, 0x73, 0x09, 0x54, 0xe3, 0x76, 0x07, 0xce, 0xcc, + 0x7f, 0x22, 0xaf, 0x1c, 0xb8, 0xdf, 0xaf, 0x00, 0xc0, 0xfc, 0xf8, 0x00, + 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xbe, 0xf9, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x03, 0x00, 0xba, 0xf7, 0x3f, 0x00, + 0xc3, 0xfe, 0xd3, 0x00, 0xc3, 0xff, 0xcd, 0x00, 0xbd, 0xf5, 0x32, 0x00, + 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x55, 0x55, 0x00, 0x03, 0xe3, + 0x78, 0x06, 0x53, 0xe4, 0x77, 0x07, 0xea, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x75, 0x07, 0xfc, 0xce, 0x7e, 0x1f, 0xc0, 0x1a, 0xb9, 0xe0, 0xae, 0x01, + 0xc0, 0xfb, 0xfb, 0x00, 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, + 0xc3, 0xfe, 0xe8, 0x03, 0xbb, 0xf5, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x03, 0x04, + 0xb9, 0xf2, 0x3a, 0x00, 0xc2, 0xff, 0xeb, 0x00, 0xc3, 0xff, 0xa9, 0x00, + 0xb8, 0xf1, 0x12, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0xbf, 0x80, 0x00, 0x04, 0xdd, 0x75, 0x06, 0x53, 0xe3, + 0x76, 0x08, 0xea, 0xe5, 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xfb, 0xcc, 0x80, 0x22, 0xae, 0x17, + 0xba, 0xe4, 0xbd, 0x01, 0xc0, 0xfb, 0xfb, 0x00, 0xc2, 0xfe, 0xff, 0x00, + 0xc3, 0xff, 0xff, 0x00, 0xc3, 0xfe, 0xc7, 0x00, 0xb3, 0xec, 0x1b, 0x00, + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x03, 0xbc, 0xf7, 0x5c, 0x00, 0xc2, 0xfd, 0xef, 0x00, + 0xc3, 0xff, 0x66, 0x00, 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, 0x00, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x55, + 0x55, 0x00, 0x03, 0xe3, 0x76, 0x06, 0x52, 0xe4, 0x76, 0x07, 0xdc, 0xe2, + 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xf8, 0xcb, + 0x7f, 0x22, 0xab, 0x18, 0xba, 0xe4, 0xab, 0x00, 0xc0, 0xfc, 0xf8, 0x00, + 0xc3, 0xfe, 0xd9, 0x00, 0xc2, 0xff, 0x97, 0x00, 0xc2, 0xfe, 0xab, 0x00, + 0xbd, 0xf5, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbb, 0xee, 0x0f, 0x00, 0xc0, 0xfa, 0x9f, 0x00, + 0xc2, 0xfe, 0xe3, 0x00, 0xc7, 0xff, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x66, 0x99, 0x05, 0x00, + 0xc0, 0xff, 0x7e, 0x00, 0xc3, 0xfd, 0x78, 0x00, 0x40, 0x40, 0x04, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xe1, + 0x75, 0x06, 0x55, 0xe2, 0x76, 0x08, 0xea, 0xe4, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xfb, 0xca, 0x80, 0x23, 0x9a, 0x00, 0xc1, 0xfc, 0x5f, 0x02, + 0xbf, 0xf9, 0x84, 0x00, 0xc2, 0xfe, 0xec, 0x00, 0xc4, 0xff, 0xfe, 0x00, + 0xc3, 0xfe, 0xe8, 0x03, 0xbe, 0xf5, 0x4e, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xfb, 0x3a, 0x01, + 0xc0, 0xfb, 0xe6, 0x00, 0xc2, 0xfd, 0xa4, 0x00, 0xbf, 0xff, 0x04, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0xf5, 0x33, 0x00, + 0xc1, 0xfd, 0xe4, 0x00, 0xc3, 0xfd, 0xa1, 0x00, 0x80, 0x80, 0x02, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x92, 0x49, 0x24, 0x07, 0xe3, 0x76, 0x06, 0x52, 0xe5, + 0x77, 0x07, 0xea, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xe8, 0xe0, 0x74, 0x07, 0x4b, 0xbf, 0x80, 0x40, 0x04, 0x07, + 0xbc, 0xf7, 0x9c, 0x01, 0xbe, 0xfb, 0xff, 0x00, 0xc2, 0xfe, 0xff, 0x00, + 0xc3, 0xff, 0xff, 0x00, 0xc1, 0xfe, 0xe8, 0x00, 0xc0, 0xf8, 0x4d, 0x00, + 0xbf, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0xaa, 0xaa, 0x03, 0x00, + 0xc2, 0xfd, 0x9e, 0x01, 0xc0, 0xfc, 0xe6, 0x00, 0xbf, 0xfa, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xaa, 0xff, 0x03, 0x00, 0xc2, 0xff, 0x76, 0x00, + 0xc0, 0xfc, 0xf0, 0x00, 0xbd, 0xf5, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0xe0, 0x78, 0x0d, 0x51, 0xe2, 0x75, 0x07, 0xdc, 0xe4, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, + 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, + 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, 0x77, 0x07, 0xd9, 0xe5, + 0x74, 0x07, 0x4d, 0xb3, 0x66, 0x1a, 0x0a, 0xe1, 0x74, 0x08, 0x5c, 0xa7, + 0x79, 0x4b, 0xb1, 0x11, 0x99, 0xeb, 0xff, 0x00, 0xbe, 0xfb, 0xff, 0x00, + 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, 0xc2, 0xfd, 0xd9, 0x00, + 0xc0, 0xf8, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xc2, 0xff, 0x2e, 0x00, 0xc3, 0xff, 0xef, 0x00, 0xc0, 0xfb, 0x7e, 0x00, + 0x99, 0xcc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x0c, 0xaa, 0xdb, 0x15, 0x00, 0xc3, 0xff, 0xb3, 0x00, + 0xc2, 0xff, 0xb3, 0x00, 0x80, 0x80, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, + 0xae, 0xdc, 0x16, 0xc2, 0x82, 0x2c, 0xe3, 0xe4, 0x76, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe1, 0x75, 0x08, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x76, 0x08, 0xe8, 0xde, 0x76, 0x07, 0x4e, 0xff, + 0x55, 0x00, 0x03, 0xe3, 0x74, 0x06, 0x5c, 0xe2, 0x75, 0x08, 0xeb, 0xe1, + 0x75, 0x0b, 0xfe, 0x99, 0x73, 0x59, 0xff, 0x11, 0x99, 0xeb, 0xff, 0x01, + 0xbe, 0xfb, 0xff, 0x00, 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, + 0xc1, 0xfe, 0xe8, 0x03, 0xbd, 0xf5, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x55, 0xaa, 0x03, 0x00, 0xc4, 0xff, 0xb4, 0x00, 0xc3, 0xfe, 0xba, 0x0b, + 0xaa, 0xd4, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0xf2, 0x26, 0x00, 0xc0, 0xfb, 0xe3, 0x00, + 0xc3, 0xff, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x06, 0x02, + 0xbd, 0xfa, 0x8e, 0x39, 0xaf, 0xbf, 0xff, 0xc8, 0x80, 0x26, 0xff, 0xe4, + 0x76, 0x08, 0xff, 0xe1, 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xe8, 0xe5, 0x74, 0x07, 0x4d, 0xc6, 0x71, 0x1c, 0x09, 0xde, + 0x73, 0x08, 0x5d, 0xe5, 0x77, 0x07, 0xf7, 0xe5, 0x77, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe0, 0x75, 0x0b, 0xff, 0x8a, 0x71, 0x69, 0xff, 0x11, + 0xa1, 0xeb, 0xff, 0x01, 0xbe, 0xfb, 0xff, 0x00, 0xc2, 0xfe, 0xff, 0x00, + 0xc3, 0xff, 0xff, 0x00, 0xc1, 0xfe, 0xe8, 0x00, 0xbf, 0xf8, 0x48, 0x00, + 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0xbf, 0xfc, 0x5f, 0x00, 0xc3, 0xff, 0xe8, 0x00, + 0xc1, 0xff, 0x29, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xc1, 0xff, 0x4a, 0x00, 0xc2, 0xfd, 0xe5, 0x00, + 0xbc, 0xf3, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x39, 0xaa, 0x09, 0x00, 0x5a, 0xff, 0x60, 0x00, + 0xa9, 0xfe, 0xec, 0x04, 0xbf, 0xf8, 0xff, 0x48, 0xa9, 0xaf, 0xff, 0xc9, + 0x80, 0x25, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xd9, 0xdc, + 0x73, 0x0a, 0x50, 0xdb, 0x6d, 0x00, 0x07, 0xe3, 0x77, 0x08, 0x65, 0xe3, + 0x75, 0x08, 0xeb, 0xe2, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xdd, 0x75, 0x0e, 0xff, 0x8b, + 0x71, 0x69, 0xff, 0x12, 0x99, 0xeb, 0xff, 0x00, 0xbe, 0xfb, 0xff, 0x00, + 0xc2, 0xfe, 0xff, 0x00, 0xc2, 0xff, 0xfc, 0x00, 0xc2, 0xfd, 0x93, 0x00, + 0xb7, 0xeb, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbb, 0xf4, 0x2d, 0x00, 0xc2, 0xfd, 0xea, 0x00, + 0xc2, 0xff, 0x53, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0xc2, 0xff, 0x7d, 0x00, 0xc2, 0xff, 0xc5, 0x00, + 0xb8, 0xf5, 0x19, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0xf7, 0x40, 0x00, 0x53, 0xfc, 0xea, 0x00, + 0x62, 0xfe, 0xff, 0x00, 0xad, 0xfe, 0xff, 0x04, 0xbf, 0xf8, 0xff, 0x3c, + 0xae, 0xbc, 0xff, 0xd5, 0x7c, 0x18, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x77, 0x07, 0xe8, 0xe5, 0x74, 0x07, 0x4d, 0x99, + 0x66, 0x00, 0x05, 0xde, 0x72, 0x08, 0x5e, 0xe5, 0x76, 0x08, 0xeb, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe0, + 0x75, 0x0b, 0xff, 0x9a, 0x73, 0x58, 0xff, 0x12, 0x9a, 0xeb, 0xfb, 0x01, + 0xbf, 0xfb, 0xfe, 0x00, 0xc1, 0xfe, 0xcf, 0x00, 0xc4, 0xff, 0x9f, 0x00, + 0xc2, 0xfe, 0xda, 0x03, 0xbb, 0xf5, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0xff, 0x1a, 0x00, 0xbf, 0xfb, 0xcc, 0x00, + 0xc1, 0xfd, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0xbf, 0xfa, 0xa7, 0x00, 0xc3, 0xfd, 0xa9, 0x00, + 0xc8, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0xfc, 0x5f, 0x00, 0x52, 0xfc, 0xff, 0x00, + 0x53, 0xfc, 0xff, 0x00, 0x63, 0xff, 0xff, 0x00, 0xaa, 0xfe, 0xff, 0x01, + 0xbf, 0xfb, 0xff, 0x3d, 0xae, 0xbb, 0xff, 0xc9, 0x80, 0x25, 0xfe, 0xe2, + 0x77, 0x09, 0xe7, 0xd9, 0x73, 0x0a, 0x50, 0xbf, 0x60, 0x00, 0x08, 0xe1, + 0x76, 0x05, 0x5d, 0xe4, 0x77, 0x07, 0xf7, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe1, 0x76, 0x09, 0xfb, 0xb5, 0x7e, 0x39, 0x8a, 0x02, + 0xc1, 0xfd, 0x6b, 0x02, 0xbe, 0xf9, 0x7e, 0x00, 0xc3, 0xfe, 0xf6, 0x00, + 0xc3, 0xff, 0xff, 0x00, 0xc3, 0xfe, 0xe8, 0x00, 0xbe, 0xf5, 0x4f, 0x00, + 0xbf, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xc3, 0xff, 0x11, 0x00, 0xc3, 0xfe, 0xb2, 0x01, + 0xbf, 0xfb, 0xb3, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc3, 0xff, 0xbf, 0x00, 0xc1, 0xfc, 0x97, 0x00, + 0xaa, 0xe3, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x53, 0xff, 0x59, 0x00, 0x52, 0xff, 0xfe, 0x00, + 0x53, 0xfc, 0xff, 0x00, 0x53, 0xfc, 0xff, 0x00, 0x6a, 0xfe, 0xff, 0x00, + 0xaa, 0xfe, 0xff, 0x04, 0xbe, 0xf7, 0xf4, 0x55, 0xa5, 0xa2, 0xa8, 0xcb, + 0x80, 0x22, 0x44, 0xbf, 0x60, 0x00, 0x08, 0xe1, 0x75, 0x08, 0x66, 0xe4, + 0x76, 0x08, 0xeb, 0xe5, 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, + 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, + 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x76, 0x08, 0xc9, 0xdc, 0x73, 0x0a, 0x33, 0xff, + 0x55, 0x00, 0x03, 0x08, 0xbf, 0xf5, 0x83, 0x00, 0xc0, 0xfc, 0xf8, 0x00, + 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, 0xc2, 0xfd, 0xda, 0x00, + 0xbe, 0xf5, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xa4, 0xdb, 0x0e, 0x00, 0xc3, 0xff, 0xa1, 0x00, + 0xc3, 0xff, 0xcb, 0x00, 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x80, 0x80, 0x02, 0x00, 0xc3, 0xff, 0xcc, 0x00, 0xc2, 0xfd, 0x8d, 0x1c, + 0x8e, 0xaa, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x03, 0x50, 0xf9, 0x56, 0x00, 0x52, 0xfe, 0xfe, 0x00, + 0x53, 0xff, 0xff, 0x00, 0x53, 0xfc, 0xff, 0x00, 0x53, 0xfc, 0xff, 0x00, + 0x60, 0xfe, 0xf7, 0x00, 0x91, 0xff, 0x5d, 0x0f, 0xa5, 0xe1, 0x11, 0x00, + 0x80, 0x80, 0x02, 0xe3, 0x77, 0x06, 0x5c, 0xe4, 0x76, 0x08, 0xeb, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe1, 0x75, 0x08, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xd1, 0xe3, 0x75, 0x07, 0x25, 0xc8, 0x6d, 0x12, 0x0e, 0xe3, + 0x75, 0x07, 0x92, 0xcd, 0x7e, 0x20, 0xb6, 0x1b, 0xba, 0xe2, 0xad, 0x01, + 0xc0, 0xfb, 0xfb, 0x00, 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, + 0xc3, 0xfe, 0xe8, 0x03, 0xbb, 0xf5, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa2, 0xd1, 0x0b, 0x00, 0xc2, 0xfc, 0x9a, 0x00, + 0xc3, 0xff, 0xd8, 0x00, 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0xc2, 0xfd, 0xcd, 0x00, 0xc2, 0xff, 0x8b, 0x00, + 0x99, 0xff, 0x05, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0xfb, 0x3c, 0x00, 0x52, 0xfc, 0xdf, 0x00, + 0x52, 0xff, 0xe2, 0x00, 0x53, 0xff, 0xe0, 0x00, 0x52, 0xfb, 0xd6, 0x00, + 0x51, 0xf9, 0x55, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0xaa, 0x03, 0xd7, + 0x76, 0x10, 0x5f, 0xe4, 0x76, 0x07, 0xf7, 0xe5, 0x77, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe1, 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xfd, 0xe2, 0x75, 0x08, 0xca, 0xd8, + 0x6f, 0x07, 0x27, 0xeb, 0x76, 0x00, 0x0d, 0xe5, 0x76, 0x08, 0x88, 0xe1, + 0x75, 0x08, 0xf8, 0xe4, 0x76, 0x07, 0xfb, 0xcc, 0x80, 0x22, 0xae, 0x17, + 0xba, 0xe5, 0xbd, 0x01, 0xc0, 0xfb, 0xfb, 0x00, 0xc2, 0xfe, 0xff, 0x00, + 0xc3, 0xff, 0xff, 0x00, 0xc3, 0xfe, 0xe8, 0x00, 0xbb, 0xf5, 0x4f, 0x00, + 0xaa, 0xff, 0x03, 0x00, 0xc6, 0xff, 0x09, 0x00, 0xc0, 0xfc, 0x99, 0x00, + 0xc1, 0xfe, 0xd7, 0x00, 0x80, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfb, 0xbf, 0x00, 0xc1, 0xfc, 0x93, 0x00, + 0xb6, 0xff, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x55, 0xff, 0x06, 0x00, 0x4d, 0xf2, 0x28, 0x00, + 0x51, 0xee, 0x2c, 0x00, 0x4e, 0xf8, 0x24, 0x00, 0x55, 0xff, 0x18, 0x00, + 0x33, 0x99, 0x05, 0x00, 0x00, 0x00, 0x03, 0x1c, 0xb7, 0xe3, 0x2e, 0xb3, + 0x86, 0x3c, 0xe0, 0xe2, 0x75, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, + 0x75, 0x07, 0xff, 0xe4, 0x77, 0x06, 0xc9, 0xe3, 0x76, 0x05, 0x36, 0xbb, + 0x66, 0x11, 0x0f, 0xe1, 0x75, 0x07, 0x89, 0xe5, 0x77, 0x07, 0xf3, 0xe5, + 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xf8, 0xcc, + 0x80, 0x22, 0xae, 0x1a, 0xb9, 0xe2, 0xae, 0x00, 0xc0, 0xfc, 0xf8, 0x00, + 0xc2, 0xfe, 0xff, 0x00, 0xc3, 0xff, 0xff, 0x00, 0xc2, 0xfc, 0xcc, 0x00, + 0xb2, 0xe6, 0x1e, 0x00, 0xbf, 0xff, 0x0c, 0x00, 0xc3, 0xff, 0xa2, 0x00, + 0xc0, 0xfc, 0xcc, 0x00, 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xfd, 0xa4, 0x00, 0xbf, 0xfa, 0xa4, 0x00, + 0xb1, 0xeb, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x71, 0xc6, 0x09, 0x02, 0xb9, 0xfb, 0x87, 0x1e, + 0xb9, 0xdd, 0xfa, 0xb6, 0x86, 0x39, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xd1, 0xd2, 0x73, 0x0d, 0x28, 0xea, 0x6a, 0x00, 0x0c, 0xe4, + 0x76, 0x07, 0x97, 0xe2, 0x76, 0x07, 0xf9, 0xe2, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xfb, 0xce, 0x7f, 0x20, 0xbf, 0x1a, 0xb9, 0xe0, 0xae, 0x01, + 0xc0, 0xfb, 0xfb, 0x00, 0xc2, 0xfe, 0xfc, 0x00, 0xc3, 0xff, 0xa1, 0x00, + 0xbb, 0xee, 0x0f, 0x0c, 0xaa, 0xdb, 0x15, 0x00, 0xc4, 0xff, 0xb4, 0x00, + 0xc4, 0xff, 0xb1, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0xc3, 0xff, 0x7b, 0x00, 0xc2, 0xfe, 0xbc, 0x0b, + 0xaa, 0xdf, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x40, 0xdf, 0x08, 0x00, 0x5a, 0xff, 0x69, 0x00, 0x8f, 0xfc, 0xf4, 0x00, + 0xbf, 0xfc, 0xff, 0x1e, 0xb8, 0xdd, 0xff, 0xb0, 0x88, 0x3f, 0xff, 0xdf, + 0x76, 0x0a, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe1, 0x75, 0x08, 0xfd, 0xe3, 0x75, 0x06, 0xca, 0xe3, + 0x75, 0x07, 0x25, 0xdb, 0x6d, 0x12, 0x0e, 0xe0, 0x74, 0x07, 0x8b, 0xe5, + 0x77, 0x07, 0xf8, 0xe5, 0x77, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xfb, 0xc9, 0x7f, 0x24, 0xa5, 0x0d, + 0xbf, 0xf0, 0xaf, 0x01, 0xc0, 0xfb, 0xae, 0x00, 0xc2, 0xf5, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb6, 0xed, 0x1c, 0x00, 0xc1, 0xfd, 0xcf, 0x00, + 0xc2, 0xfd, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xbe, 0xfc, 0x4a, 0x00, 0xc3, 0xff, 0xd9, 0x00, + 0xc1, 0xff, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x53, 0xfa, 0x69, 0x00, 0x53, 0xfe, 0xf5, 0x00, 0x5f, 0xfe, 0xff, 0x00, + 0x97, 0xfc, 0xff, 0x01, 0xc0, 0xfb, 0xff, 0x2d, 0xb3, 0xcd, 0xff, 0xb0, + 0x88, 0x3f, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x75, 0x06, 0xca, 0xdf, 0x72, 0x09, 0x38, 0xd8, + 0x76, 0x00, 0x0d, 0xe5, 0x78, 0x08, 0x88, 0xe3, 0x76, 0x07, 0xf3, 0xe2, + 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe2, + 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xfb, 0xe3, 0x76, 0x07, 0x9c, 0x8b, + 0x97, 0x68, 0x16, 0x00, 0xb9, 0xe8, 0x0b, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xc6, 0xff, 0x2d, 0x00, 0xc1, 0xfd, 0xec, 0x00, + 0xbe, 0xf6, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0xf1, 0x25, 0x00, 0xc2, 0xfd, 0xdd, 0x00, + 0xc2, 0xff, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x53, 0xfd, 0xa9, 0x01, 0x52, 0xfb, 0xff, 0x00, 0x53, 0xfe, 0xff, 0x00, + 0x5a, 0xff, 0xff, 0x00, 0x97, 0xfc, 0xff, 0x01, 0xc0, 0xfb, 0xff, 0x1e, + 0xb8, 0xdd, 0xff, 0xb6, 0x86, 0x39, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe3, + 0x76, 0x07, 0xd1, 0xe3, 0x75, 0x07, 0x25, 0xd8, 0x76, 0x00, 0x0d, 0xdf, + 0x75, 0x07, 0x98, 0xe4, 0x76, 0x07, 0xf8, 0xe5, 0x77, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe1, 0x75, 0x08, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe6, 0x76, 0x07, 0xbe, 0xd8, 0x74, 0x08, 0x21, 0x80, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0xc2, 0xfc, 0x65, 0x00, 0xc4, 0xff, 0xe8, 0x00, + 0xbc, 0xf9, 0x2a, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc3, 0xff, 0x11, 0x00, 0xc0, 0xfb, 0xb1, 0x00, + 0xc0, 0xfc, 0x95, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x53, 0xff, 0xa5, 0x00, 0x53, 0xfd, 0xff, 0x00, 0x52, 0xfb, 0xff, 0x00, + 0x53, 0xfe, 0xff, 0x00, 0x5a, 0xff, 0xff, 0x00, 0x99, 0xfc, 0xff, 0x00, + 0xbf, 0xfc, 0xff, 0x1d, 0xb8, 0xde, 0xf1, 0xa9, 0x8a, 0x47, 0xba, 0xc6, + 0x73, 0x1a, 0x28, 0x49, 0x92, 0x92, 0x07, 0xc5, 0x81, 0x2a, 0x7f, 0xe2, + 0x77, 0x09, 0xf9, 0xe1, 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe5, + 0x77, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xfb, 0xe3, + 0x76, 0x08, 0xbe, 0xe4, 0x6b, 0x0d, 0x13, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0xbf, 0xfa, 0xa7, 0x00, 0xc1, 0xfe, 0xb6, 0x00, + 0xc9, 0xff, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0xaa, 0xff, 0x03, 0x00, 0xc4, 0xff, 0x74, 0x00, + 0xc1, 0xfd, 0xe1, 0x00, 0xba, 0xf5, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x53, 0xfd, 0xa0, 0x00, 0x53, 0xff, 0xff, 0x00, 0x53, 0xfd, 0xff, 0x00, + 0x52, 0xfb, 0xff, 0x00, 0x53, 0xfe, 0xff, 0x00, 0x60, 0xfe, 0xff, 0x00, + 0x91, 0xfd, 0xd5, 0x00, 0xb8, 0xf9, 0x5a, 0x00, 0xb4, 0xf0, 0x11, 0x00, + 0x00, 0x00, 0x01, 0x0c, 0xb6, 0xe7, 0x2a, 0x4f, 0xa7, 0xa8, 0xd9, 0xcb, + 0x7f, 0x22, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe2, 0x75, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe3, 0x76, 0x07, 0xff, 0xe2, + 0x75, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xfb, 0xe5, 0x76, 0x07, 0xaf, 0xd8, + 0x74, 0x08, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbf, 0xfb, 0x40, 0x00, 0xbc, 0xf7, 0x41, 0x00, + 0x80, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0xbf, 0xfa, 0x30, 0x00, + 0xc3, 0xff, 0xe2, 0x00, 0xc0, 0xfb, 0x7e, 0x00, 0x55, 0x55, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x52, 0xf8, 0x99, 0x00, 0x53, 0xfe, 0xff, 0x00, 0x53, 0xff, 0xff, 0x00, + 0x53, 0xfd, 0xff, 0x01, 0x52, 0xfb, 0xff, 0x00, 0x53, 0xfe, 0xd2, 0x00, + 0x57, 0xff, 0x35, 0x00, 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x49, 0xff, 0x07, 0x00, 0x99, 0xff, 0xa7, 0x05, 0xc0, 0xf7, 0xfd, 0x3f, + 0xad, 0xb9, 0xff, 0xd6, 0x7c, 0x16, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe1, + 0x75, 0x08, 0xff, 0xe4, 0x76, 0x07, 0xff, 0xe5, 0x77, 0x07, 0xff, 0xe4, + 0x76, 0x07, 0xff, 0xe0, 0x74, 0x07, 0xbf, 0xe6, 0x7b, 0x08, 0x1f, 0xff, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x66, 0x99, 0x05, 0x00, + 0xc3, 0xff, 0x99, 0x00, 0xc3, 0xff, 0xdc, 0x00, 0xb7, 0xf2, 0x27, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0xfa, 0x35, 0x02, 0x50, 0xfa, 0x92, 0x00, 0x53, 0xfd, 0x8a, 0x00, + 0x54, 0xff, 0x80, 0x00, 0x52, 0xfd, 0x73, 0x00, 0x4e, 0xf4, 0x2e, 0x00, + 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xef, 0x10, 0x00, + 0x52, 0xfc, 0xa4, 0x00, 0x62, 0xff, 0xff, 0x00, 0xaa, 0xfe, 0xff, 0x01, + 0xbf, 0xfb, 0xff, 0x3e, 0xad, 0xba, 0xff, 0xcb, 0x80, 0x23, 0xff, 0xe4, + 0x76, 0x08, 0xff, 0xe1, 0x75, 0x08, 0xff, 0xe5, 0x77, 0x07, 0xfb, 0xe6, + 0x76, 0x07, 0xbe, 0xd9, 0x73, 0x0d, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0xb8, 0xf1, 0x24, 0x00, 0xc2, 0xfe, 0xd9, 0x00, 0xc2, 0xff, 0x86, 0x00, + 0xa2, 0xd1, 0x0b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xfb, 0x3c, 0x00, + 0x52, 0xfc, 0xe8, 0x00, 0x53, 0xfc, 0xff, 0x00, 0x6a, 0xfe, 0xff, 0x00, + 0xaa, 0xfe, 0xff, 0x04, 0xbf, 0xf7, 0xff, 0x4b, 0xa9, 0xac, 0xff, 0xcb, + 0x80, 0x23, 0xff, 0xe5, 0x77, 0x07, 0xfb, 0xe1, 0x75, 0x07, 0xb0, 0xd7, + 0x70, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x02, 0x03, 0xbe, 0xf7, 0x5e, 0x00, 0xc3, 0xff, 0xe1, 0x00, + 0xc2, 0xff, 0x3b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x51, 0xff, 0x3c, 0x00, + 0x52, 0xff, 0xe8, 0x00, 0x53, 0xfc, 0xff, 0x00, 0x53, 0xfc, 0xff, 0x00, + 0x63, 0xff, 0xff, 0x00, 0xad, 0xfe, 0xff, 0x04, 0xbf, 0xf7, 0xff, 0x3d, + 0xad, 0xbb, 0xfc, 0xc2, 0x83, 0x2c, 0xa4, 0xd7, 0x78, 0x08, 0x20, 0x33, + 0x33, 0x33, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf8, 0x24, 0x00, 0xbf, 0xfb, 0x77, 0x00, + 0xbf, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbb, 0xee, 0x0f, 0x00, 0xc0, 0xfc, 0xa1, 0x00, + 0xc2, 0xfe, 0xd4, 0x00, 0xbd, 0xff, 0x1f, 0x00, 0x55, 0x55, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x52, 0xf6, 0x3b, 0x00, + 0x53, 0xff, 0xe7, 0x00, 0x53, 0xff, 0xff, 0x00, 0x53, 0xfc, 0xff, 0x00, + 0x53, 0xfc, 0xff, 0x00, 0x62, 0xff, 0xfd, 0x00, 0xa2, 0xfe, 0xcd, 0x04, + 0xb5, 0xf4, 0x45, 0x00, 0x80, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x55, 0x55, 0x03, 0x00, + 0xc2, 0xff, 0x15, 0x00, 0xc2, 0xfe, 0xc1, 0x00, 0xc1, 0xfc, 0xe9, 0x00, + 0xbc, 0xf8, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xf9, 0x2a, 0x00, + 0xc0, 0xfb, 0xc6, 0x00, 0xc3, 0xfe, 0xbb, 0x00, 0xbf, 0xff, 0x28, 0x00, + 0x55, 0x55, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xf4, 0x2e, 0x00, + 0x53, 0xfa, 0xe1, 0x00, 0x53, 0xff, 0xfc, 0x00, 0x53, 0xfe, 0xfc, 0x00, + 0x53, 0xfc, 0xfa, 0x00, 0x51, 0xfb, 0xc0, 0x00, 0x50, 0xff, 0x33, 0x00, + 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xef, 0x1f, 0x00, + 0xc0, 0xfa, 0xa9, 0x00, 0xc2, 0xfe, 0xfe, 0x00, 0xc3, 0xff, 0xe9, 0x00, + 0xb9, 0xf2, 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xbf, 0xf9, 0x28, 0x01, 0xc0, 0xfb, 0xd4, 0x00, 0xc1, 0xfe, 0xb9, 0x00, + 0xc1, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x49, 0xdb, 0x07, 0x00, + 0x52, 0xf6, 0x38, 0x04, 0x50, 0xf7, 0x43, 0x00, 0x52, 0xff, 0x38, 0x00, + 0x50, 0xfa, 0x30, 0x00, 0x4c, 0xec, 0x1b, 0x00, 0x55, 0x55, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0xbf, 0xf2, 0x14, 0x00, 0xc3, 0xff, 0xa6, 0x00, + 0xc2, 0xfe, 0xf3, 0x02, 0xc0, 0xfc, 0xa5, 0x00, 0xc3, 0xfe, 0xe8, 0x00, + 0xc8, 0xff, 0x25, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbf, 0xf9, 0x28, 0x00, 0xc1, 0xfb, 0xc8, 0x00, + 0xc3, 0xff, 0xcc, 0x00, 0xc3, 0xff, 0x33, 0x00, 0x8e, 0xc6, 0x09, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0xbf, 0xff, 0x04, 0x00, + 0xc4, 0xf8, 0x27, 0x01, 0xbf, 0xfa, 0xbc, 0x00, 0xc2, 0xfe, 0xe6, 0x00, + 0xc6, 0xff, 0x59, 0x00, 0xc1, 0xfc, 0x4a, 0x00, 0xc0, 0xfc, 0xe0, 0x00, + 0xbf, 0xf8, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0xbb, 0xf4, 0x2d, 0x00, + 0xc0, 0xfa, 0xa6, 0x00, 0xc3, 0xff, 0xdd, 0x00, 0xc2, 0xff, 0x79, 0x00, + 0xb6, 0xed, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb3, 0xe6, 0x14, 0x00, 0xbf, 0xfa, 0x6b, 0x00, + 0xc3, 0xff, 0xd8, 0x00, 0xc2, 0xfe, 0xcd, 0x00, 0xbc, 0xf9, 0x50, 0x00, + 0xaa, 0xd4, 0x06, 0x00, 0xc4, 0xff, 0x1a, 0x00, 0xbf, 0xfa, 0x60, 0x00, + 0xaf, 0xdf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbf, 0xef, 0x10, 0x02, 0xbd, 0xf8, 0x68, 0x00, 0xc3, 0xff, 0xdd, 0x00, + 0xc3, 0xff, 0xd0, 0x00, 0xbf, 0xfa, 0x60, 0x00, 0x9d, 0xd8, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x8e, 0xc6, 0x09, 0x00, + 0xc4, 0xff, 0x4e, 0x00, 0xc2, 0xff, 0xc6, 0x00, 0xc1, 0xfc, 0xed, 0x00, + 0xc1, 0xfd, 0x99, 0x00, 0xc3, 0xff, 0x1e, 0x00, 0xaa, 0xaa, 0x03, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x02, 0x00, 0xb7, 0xf4, 0x2e, 0x00, + 0xc1, 0xfd, 0xa9, 0x00, 0xc4, 0xff, 0xe5, 0x00, 0xc1, 0xfb, 0xcd, 0x00, + 0xc0, 0xfa, 0x6d, 0x00, 0xc1, 0xff, 0x29, 0x00, 0xc5, 0xff, 0x16, 0x12, + 0x92, 0xc8, 0x0e, 0x00, 0x80, 0xbf, 0x04, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0xff, 0xff, 0x01, 0x00, + 0x80, 0xff, 0x02, 0x00, 0xb3, 0xe6, 0x0a, 0x00, 0xb1, 0xe9, 0x17, 0x00, + 0xc3, 0xf8, 0x26, 0x00, 0xc3, 0xff, 0x62, 0x00, 0xc0, 0xfb, 0xc6, 0x00, + 0xc2, 0xfd, 0xed, 0x00, 0xc2, 0xff, 0xc8, 0x00, 0xc2, 0xfc, 0x4f, 0x20, + 0x80, 0x9f, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x80, 0x9f, 0x08, 0x00, 0xc0, 0xff, 0x3d, 0x00, 0xc1, 0xfd, 0x84, 0x00, + 0xc0, 0xfb, 0xc1, 0x00, 0xc1, 0xfd, 0xdd, 0x00, 0xc3, 0xfe, 0xc0, 0x00, + 0xc1, 0xfd, 0x9d, 0x00, 0xc1, 0xfb, 0x83, 0x00, 0xbf, 0xfa, 0x70, 0x00, + 0xc3, 0xff, 0x66, 0x00, 0xc3, 0xfc, 0x66, 0x00, 0xbf, 0xfa, 0x70, 0x00, + 0xbf, 0xfb, 0x80, 0x00, 0xc3, 0xff, 0x99, 0x00, 0xc1, 0xfe, 0xbd, 0x00, + 0xc1, 0xfd, 0xe1, 0x00, 0xc3, 0xff, 0xd3, 0x00, 0xc3, 0xff, 0x99, 0x00, + 0xbe, 0xf9, 0x56, 0x00, 0xb3, 0xe6, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0xaa, 0xff, 0x06, 0x00, + 0xc5, 0xff, 0x16, 0x00, 0xbb, 0xf5, 0x31, 0x00, 0xc1, 0xfa, 0x67, 0x00, + 0xc3, 0xff, 0x9d, 0x00, 0xc2, 0xfe, 0xc6, 0x00, 0xc0, 0xfc, 0xdc, 0x00, + 0xc2, 0xfe, 0xe7, 0x00, 0xc4, 0xff, 0xe8, 0x00, 0xc3, 0xfe, 0xe1, 0x01, + 0xc0, 0xfa, 0xd1, 0x00, 0xc3, 0xff, 0xad, 0x00, 0xc3, 0xff, 0x78, 0x00, + 0xbd, 0xfb, 0x3e, 0x08, 0xb2, 0xe8, 0x21, 0x00, 0xcc, 0xff, 0x0a, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0xbf, 0x04, 0x1c, + 0x8e, 0xaa, 0x09, 0x00, 0x92, 0xdb, 0x07, 0x00, 0xbf, 0xff, 0x04, 0x00, + 0x80, 0x80, 0x02, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x40, 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, + 0x40, 0x40, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 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 +}; + +static const size_t favicon_ico_len = 15483; + +#endif // FAVICON_ICO_H_ diff --git a/src/flash_storage.c b/src/flash_storage.c new file mode 100644 index 00000000..52f0106b --- /dev/null +++ b/src/flash_storage.c @@ -0,0 +1,83 @@ +#include "flash_storage.h" +#include "pico/flash.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include +#include + +// Flash storage is memory-mapped at XIP_BASE + offset +static const uint8_t* ml_history_flash_ptr = (const uint8_t*)(XIP_BASE + FLASH_STORAGE_ML_HISTORY_OFFSET); + +// Static buffer for write operations (must persist during flash_safe_execute) +static uint8_t g_write_buffer[FLASH_STORAGE_ML_HISTORY_SIZE] __attribute__((aligned(4))); +static size_t g_write_len = 0; + +void flash_storage_init(void) { + printf("Flash storage: ML history at offset 0x%X (XIP 0x%08X)\n", + FLASH_STORAGE_ML_HISTORY_OFFSET, + (unsigned int)(XIP_BASE + FLASH_STORAGE_ML_HISTORY_OFFSET)); +} + +bool flash_ml_history_read(uint8_t* data, size_t len) { + if (data == NULL || len == 0 || len > FLASH_STORAGE_ML_HISTORY_SIZE) { + return false; + } + + // Direct read from XIP-mapped flash - no special handling needed + memcpy(data, ml_history_flash_ptr, len); + return true; +} + +// Callback for flash_safe_execute - performs the actual erase + program +static void flash_write_callback(void* param) { + (void)param; + + // Erase the sector first (4KB minimum) + flash_range_erase(FLASH_STORAGE_ML_HISTORY_OFFSET, FLASH_SECTOR_SIZE); + + // Program in page-sized chunks (256 bytes) + size_t bytes_written = 0; + while (bytes_written < g_write_len) { + size_t chunk_size = g_write_len - bytes_written; + if (chunk_size > FLASH_PAGE_SIZE) { + chunk_size = FLASH_PAGE_SIZE; + } + + // Pad to page size if needed (flash_range_program requires page-aligned writes) + uint8_t page_buffer[FLASH_PAGE_SIZE]; + memset(page_buffer, 0xFF, FLASH_PAGE_SIZE); // 0xFF is erased state + memcpy(page_buffer, g_write_buffer + bytes_written, chunk_size); + + flash_range_program(FLASH_STORAGE_ML_HISTORY_OFFSET + bytes_written, + page_buffer, FLASH_PAGE_SIZE); + bytes_written += FLASH_PAGE_SIZE; + } +} + +bool flash_ml_history_write(const uint8_t* data, size_t len) { + if (data == NULL || len == 0 || len > FLASH_STORAGE_ML_HISTORY_SIZE) { + printf("Flash storage: Invalid write parameters (len=%u)\n", (unsigned int)len); + return false; + } + + // Copy data to static buffer (must persist during flash_safe_execute) + memcpy(g_write_buffer, data, len); + g_write_len = len; + + // Use flash_safe_execute for FreeRTOS-safe flash programming + // This handles multi-core coordination and disables interrupts during flash ops + int rc = flash_safe_execute(flash_write_callback, NULL, 1000); // 1 second timeout + + if (rc != PICO_OK) { + printf("Flash storage: flash_safe_execute failed with %d\n", rc); + return false; + } + + // Verify write + if (memcmp(ml_history_flash_ptr, data, len) != 0) { + printf("Flash storage: Write verification failed\n"); + return false; + } + + return true; +} diff --git a/src/flash_storage.h b/src/flash_storage.h new file mode 100644 index 00000000..cfe6dd55 --- /dev/null +++ b/src/flash_storage.h @@ -0,0 +1,50 @@ +#ifndef FLASH_STORAGE_H_ +#define FLASH_STORAGE_H_ + +#include +#include +#include + +// Flash Storage Configuration +// Pico W has 2MB flash. Reserve last 8KB for persistent storage: +// - 0x1FE000 (2MB - 8KB): ML history sector (4KB) +// - 0x1FF000 (2MB - 4KB): Reserved for future use +// +// Note: FLASH_SECTOR_SIZE is 4KB, FLASH_PAGE_SIZE is 256 bytes +// flash_range_erase() erases in sector units, flash_range_program() writes in page units + +#define FLASH_STORAGE_ML_HISTORY_OFFSET (2 * 1024 * 1024 - 8 * 1024) // 0x1FE000 +#define FLASH_STORAGE_ML_HISTORY_SIZE (4 * 1024) // 4KB sector + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize flash storage subsystem + * Must be called after FreeRTOS scheduler starts + */ +void flash_storage_init(void); + +/** + * Read ML history from flash + * @param data Buffer to read into + * @param len Number of bytes to read (must not exceed sector size) + * @return true on success + */ +bool flash_ml_history_read(uint8_t* data, size_t len); + +/** + * Write ML history to flash + * Erases the sector and writes new data + * @param data Data to write + * @param len Number of bytes to write (must not exceed sector size) + * @return true on success + */ +bool flash_ml_history_write(const uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif // FLASH_STORAGE_H_ diff --git a/src/gng_scale.c b/src/gng_scale.c index c2290f03..d25bcfcd 100644 --- a/src/gng_scale.c +++ b/src/gng_scale.c @@ -76,34 +76,59 @@ void _gng_scale_listener_task(void *p) { gngscale_standard_data_format_t frame; while (true) { - // Request for a data transfer (ESC p) - uart_puts(SCALE_UART, CMD_REQUEST_DATA_TRANSFER); - - // Read all data - while (uart_is_readable(SCALE_UART)) { - char ch = uart_getc(SCALE_UART); - frame.bytes[string_buf_idx++] = ch; - - // If we have received 14 bytes then we can decode the message - if (string_buf_idx == sizeof(gngscale_standard_data_format_t)) { - // Data is ready, send to decode - scale_config.current_scale_measurement = _decode_measurement_msg(&frame); - // Signal the data is ready - if (scale_config.scale_measurement_ready) { - xSemaphoreGive(scale_config.scale_measurement_ready); + // Reset frame buffer index at the start of each poll cycle (fixes partial-frame carryover on timeout) + string_buf_idx = 0; + + // Clear any stale data in RX buffer before sending request + while (uart_is_readable(SCALE_UART)) { + uart_getc(SCALE_UART); + } + + // Request for a data transfer using mutex-protected write + scale_write(CMD_REQUEST_DATA_TRANSFER, strlen(CMD_REQUEST_DATA_TRANSFER)); + + // Wait for scale to respond (up to 200ms) + TickType_t start_tick = xTaskGetTickCount(); + bool got_response = false; + + while ((xTaskGetTickCount() - start_tick) < pdMS_TO_TICKS(200)) { + while (uart_is_readable(SCALE_UART)) { + char ch = uart_getc(SCALE_UART); + frame.bytes[string_buf_idx++] = ch; + + // If we have received 14 bytes then we can decode the message + if (string_buf_idx == sizeof(gngscale_standard_data_format_t)) { + // Data is ready, send to decode + scale_config.current_scale_measurement = _decode_measurement_msg(&frame); + // Signal the data is ready + if (scale_config.scale_measurement_ready) { + xSemaphoreGive(scale_config.scale_measurement_ready); + } + + // Reset + string_buf_idx = 0; + got_response = true; } - // Reset - string_buf_idx = 0; + // \n is the terminator. Reset on receiving it. + if (ch == '\n') { + string_buf_idx = 0; + if (got_response) { + break; + } + } } - // \n is the terminator. We shall reset the receive of message on receiving any of those character. - if (ch =='\n') { - string_buf_idx = 0; + if (got_response) { + break; } + + // Small delay before checking again + vTaskDelay(pdMS_TO_TICKS(10)); } - vTaskDelay(pdMS_TO_TICKS(250)); + // Wait before next request + vTaskDelay(pdMS_TO_TICKS(50)); } } diff --git a/src/html/web_portal.html b/src/html/web_portal.html index 5178950b..a8193297 100644 --- a/src/html/web_portal.html +++ b/src/html/web_portal.html @@ -54,7 +54,14 @@
Charge Control
- + + +
+ Active Profile + +
+
  • Wait
  • @@ -74,6 +81,21 @@
+ + + + @@ -688,6 +768,244 @@ + + +