From 01e089fd07f766aec13705b7a1c3d2805f1f9d5e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 24 Jul 2024 08:23:04 -0500 Subject: [PATCH 001/305] Ignore invalid service envelopes (#4326) --- src/mqtt/MQTT.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 5f7d6d902..a7085dffe 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -135,6 +135,10 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); return; } else { + if (e.channel_id == NULL || e.gateway_id == NULL) { + LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); + return; + } meshtastic_Channel ch = channels.getByName(e.channel_id); if (strcmp(e.gateway_id, owner.id) == 0) { // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message. From a000a8d347d1040ecde038a0435e19c23c8c58b7 Mon Sep 17 00:00:00 2001 From: dylanli <167049793+Dylanliacc@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:10:38 +0800 Subject: [PATCH 002/305] Support Seeed Tracker-T1000-E (#4303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature-T1000-E: add Added the board definition for T1000-E - integrate a script for rapid dependency download that is compatible with both Linux and Windows platforms. - add the pin definitions for UART, SPI, GPIO, and other peripherals have been ensured to be correct. - add the env configuration for PlatformIO. * refact-T1000-E: redefine T1000-E board * feature-T1000-E: add basic sensors * feature-T1000-E: add button init * feat: add DRADIOLIB_GODMODE defination for use function setDioAsRfSwitch to DIO LORA RF * feat : add gps(GNSS_Airoha) sleep mode * feat: add behavier when rec or send message * chore: hang IIC bus usage to avoid sensor address conflict * feat: add sensor data acquisition * feat : support Airoha GPS - add disable it in FSM - update lookForTime and lookForLocation function * fix: fix a bug * version: change version to 0.9.0 * Update tracker-t1000-e.json Remove a space * Delete variants/tracker-t1000-e/run_once.sh Delete not need as we will change platformio.ini * Update platformio.ini Update SoftDevice 7.3.0 usage in line with other lr1110 targets Do we need to keep GODMODE ? * fix: Button behavier incorrect bug * fix:remove some invaild code of TextMessageModule * fix: remove invaild comment * version: change version to 0.9.1 - update mark's patch - remove some invaild code and comments - fix button behavier * trunk format * fix: HELTEC_CAPSULE_SENSOR_V3 block got accidentally deleted * fix: EnvironmentTelemetry upstream merge went awry. * fix: Added macro definitions to ensure correct operation of LORA section * fix :GNSS_AIROHA macro defination in line with others * fix: upstream backmerge accidentally. * fix: wrap macro PIN_3V3_EN BUZZER_EN_PIN GNSS_AIROHA in the TRACKER_T1000_E macro guard --------- Co-authored-by: Mark Trevor Birss Co-authored-by: Ben Meadors Co-authored-by: Thomas Göttgens --- boards/tracker-t1000-e.json | 58 +++++++ src/ButtonThread.cpp | 5 +- src/gps/GPS.cpp | 55 ++++++- src/gps/GPS.h | 2 +- src/mesh/LR11x0Interface.cpp | 8 +- .../Telemetry/EnvironmentTelemetry.cpp | 8 +- src/modules/Telemetry/Sensor/T1000xSensor.h | 1 - src/modules/TextMessageModule.cpp | 3 +- src/sleep.cpp | 19 +++ variants/tracker-t1000-e/platformio.ini | 16 ++ variants/tracker-t1000-e/variant.cpp | 64 ++++++++ variants/tracker-t1000-e/variant.h | 150 ++++++++++++++++++ variants/wio-t1000-s/variant.h | 2 +- 13 files changed, 373 insertions(+), 18 deletions(-) create mode 100644 boards/tracker-t1000-e.json create mode 100644 variants/tracker-t1000-e/platformio.ini create mode 100644 variants/tracker-t1000-e/variant.cpp create mode 100644 variants/tracker-t1000-e/variant.h diff --git a/boards/tracker-t1000-e.json b/boards/tracker-t1000-e.json new file mode 100644 index 000000000..2be716e22 --- /dev/null +++ b/boards/tracker-t1000-e.json @@ -0,0 +1,58 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v7.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "T1000-E-BOOT", + "mcu": "nrf52840", + "variant": "Seeed_T1000-E", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "7.3.0", + "sd_fwid": "0x0123" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": ["arduino"], + "name": "Seeed T1000-E", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink", + "cmsis-dap", + "blackmagic" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html", + "vendor": "Seeed Studio" +} diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index a81518f31..d479de6ed 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -29,7 +29,6 @@ volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BU #if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) OneButton ButtonThread::userButton; // Get reference to static member #endif - ButtonThread::ButtonThread() : OSThread("Button") { #if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) @@ -43,7 +42,7 @@ ButtonThread::ButtonThread() : OSThread("Button") int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin #if defined(HELTEC_CAPSULE_SENSOR_V3) this->userButton = OneButton(pin, false, false); -#elif defined(BUTTON_ACTIVE_LOW) // change by WayenWeng +#elif defined(BUTTON_ACTIVE_LOW) this->userButton = OneButton(pin, BUTTON_ACTIVE_LOW, BUTTON_ACTIVE_PULLUP); #else this->userButton = OneButton(pin, true, true); @@ -53,7 +52,7 @@ ButtonThread::ButtonThread() : OSThread("Button") #ifdef INPUT_PULLUP_SENSE // Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did -#ifdef BUTTON_SENSE_TYPE // change by WayenWeng +#ifdef BUTTON_SENSE_TYPE pinMode(pin, BUTTON_SENSE_TYPE); #else pinMode(pin, INPUT_PULLUP_SENSE); diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index f04b45622..494622dc6 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -787,7 +787,6 @@ GPS::~GPS() // we really should unregister our sleep observer notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); } - // Put the GPS hardware into a specified state void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) { @@ -824,6 +823,11 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) setPowerPMU(false); // Power (PMU): off writePinStandby(true); // Standby (pin): asleep (not awake) setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed +#ifdef GNSS_AIROHA + if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) { + digitalWrite(PIN_GPS_EN, LOW); + } +#endif break; case GPS_OFF: @@ -833,6 +837,11 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) setPowerPMU(false); // Power (PMU): off writePinStandby(true); // Standby (pin): asleep setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely +#ifdef GNSS_AIROHA + if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) { + digitalWrite(PIN_GPS_EN, LOW); + } +#endif break; } } @@ -1171,7 +1180,8 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->updateBaudRate(serialSpeed); } #endif -#ifdef GNSS_Airoha // add by WayenWeng +#ifdef GNSS_AIROHA + return GNSS_MODEL_UNKNOWN; #else #ifdef GPS_DEBUG @@ -1484,11 +1494,25 @@ bool GPS::factoryReset() */ bool GPS::lookForTime() { -#ifdef GNSS_Airoha // add by WayenWeng + +#ifdef GNSS_AIROHA uint8_t fix = reader.fixQuality(); uint32_t now = millis(); + if (fix > 0) { + if (lastFixStartMsec > 0) { + if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { + return false; + } else { + clearBuffer(); + } + } else { + lastFixStartMsec = now; + return false; + } + } else { + return false; + } #endif - auto ti = reader.time; auto d = reader.date; if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed @@ -1523,13 +1547,26 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s */ bool GPS::lookForLocation() { -#ifdef GNSS_Airoha // add by WayenWeng +#ifdef GNSS_AIROHA if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) { uint8_t fix = reader.fixQuality(); uint32_t now = millis(); + if (fix > 0) { + if (lastFixStartMsec > 0) { + if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { + return false; + } else { + clearBuffer(); + } + } else { + lastFixStartMsec = now; + return false; + } + } else { + return false; + } } #endif - // By default, TinyGPS++ does not parse GPGSA lines, which give us // the 2D/3D fixType (see NMEAGPS.h) // At a minimum, use the fixQuality indicator in GPGGA (FIXME?) @@ -1739,6 +1776,12 @@ void GPS::toggleGpsMode() if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; LOG_INFO("User toggled GpsMode. Now DISABLED.\n"); +#ifdef GNSS_AIROHA + if (powerState == GPS_ACTIVE) { + LOG_DEBUG("User power Off GPS\n"); + digitalWrite(PIN_GPS_EN, LOW); + } +#endif disable(); } else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 7cbf771bc..87d03c592 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -69,7 +69,7 @@ class GPS : private concurrency::OSThread #endif private: const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600}; - + uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; uint32_t rx_gpio = 0; uint32_t tx_gpio = 0; uint32_t en_gpio = 0; diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index fc059ec16..1965eef89 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -100,7 +100,13 @@ template bool LR11x0Interface::init() // FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option if (res == RADIOLIB_ERR_NONE) res = lora.setRegulatorDCDC(); - +#ifdef TRACKER_T1000_E +#ifdef LR11X0_DIO_RF_SWITCH_CONFIG + res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG); +#else + res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0); +#endif +#endif if (res == RADIOLIB_ERR_NONE) { if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate res = lora.setRxBoostedGainMode(true); diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 92f90cfdd..fec1ee461 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -33,9 +33,7 @@ #include "Sensor/SHT31Sensor.h" #include "Sensor/SHT4XSensor.h" #include "Sensor/SHTC3Sensor.h" -#ifdef T1000X_SENSOR_EN #include "Sensor/T1000xSensor.h" -#endif #include "Sensor/TSL2591Sensor.h" #include "Sensor/VEML7700Sensor.h" @@ -98,7 +96,7 @@ int32_t EnvironmentTelemetryModule::runOnce() LOG_INFO("Environment Telemetry: Initializing\n"); // it's possible to have this module enabled, only for displaying values on the screen. // therefore, we should only enable the sensor loop if measurement is also enabled -#ifdef T1000X_SENSOR_EN // add by WayenWeng +#ifdef T1000X_SENSOR_EN result = t1000xSensor.runOnce(); #else if (dfRobotLarkSensor.hasSensor()) @@ -420,7 +418,11 @@ meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply() bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; +#ifdef T1000X_SENSOR_EN + if (t1000xSensor.getMetrics(&m)) { +#else if (getEnvironmentTelemetry(&m)) { +#endif LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n", m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current, m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity, diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.h b/src/modules/Telemetry/Sensor/T1000xSensor.h index 127d2630c..a1c771cfa 100644 --- a/src/modules/Telemetry/Sensor/T1000xSensor.h +++ b/src/modules/Telemetry/Sensor/T1000xSensor.h @@ -7,7 +7,6 @@ class T1000xSensor : public TelemetrySensor { - private: protected: virtual void setup() override; diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp index 0f86a6470..2933718af 100644 --- a/src/modules/TextMessageModule.cpp +++ b/src/modules/TextMessageModule.cpp @@ -2,8 +2,8 @@ #include "MeshService.h" #include "NodeDB.h" #include "PowerFSM.h" +#include "buzz.h" #include "configuration.h" - TextMessageModule *textMessageModule; ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp) @@ -12,7 +12,6 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp auto &p = mp.decoded; LOG_INFO("Received text msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); #endif - // We only store/display messages destined for us. // Keep a copy of the most recent text message. devicestate.rx_text_message = mp; diff --git a/src/sleep.cpp b/src/sleep.cpp index 3793ee0cf..4e685563a 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -237,6 +237,25 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) #ifdef PIN_POWER_EN pinMode(PIN_POWER_EN, INPUT); // power off peripherals // pinMode(PIN_POWER_EN1, INPUT_PULLDOWN); +#endif + +#ifdef TRACKER_T1000_E +#ifdef GNSS_AIROHA + digitalWrite(GPS_VRTC_EN, LOW); + digitalWrite(PIN_GPS_RESET, LOW); + digitalWrite(GPS_SLEEP_INT, LOW); + digitalWrite(GPS_RTC_INT, LOW); + pinMode(GPS_RESETB_OUT, OUTPUT); + digitalWrite(GPS_RESETB_OUT, LOW); +#endif + +#ifdef BUZZER_EN_PIN + digitalWrite(BUZZER_EN_PIN, LOW); +#endif + +#ifdef PIN_3V3_EN + digitalWrite(PIN_3V3_EN, LOW); +#endif #endif setLed(false); diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini new file mode 100644 index 000000000..1db57ca29 --- /dev/null +++ b/variants/tracker-t1000-e/platformio.ini @@ -0,0 +1,16 @@ +; tracker-t1000-e v0.9.1 +[env:tracker-t1000-e] +extends = nrf52840_base +board = tracker-t1000-e +; board_level = extra +; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e +build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -DRADIOLIB_GODMODE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. +board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/tracker-t1000-e> +lib_deps = + ${nrf52840_base.lib_deps} +debug_tool = jlink +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +upload_protocol = nrfutil diff --git a/variants/tracker-t1000-e/variant.cpp b/variants/tracker-t1000-e/variant.cpp new file mode 100644 index 000000000..85e0c44f3 --- /dev/null +++ b/variants/tracker-t1000-e/variant.cpp @@ -0,0 +1,64 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + // LED1 & LED2 + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); + + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); + + pinMode(PIN_3V3_ACC_EN, OUTPUT); + digitalWrite(PIN_3V3_ACC_EN, LOW); + + pinMode(BUZZER_EN_PIN, OUTPUT); + digitalWrite(BUZZER_EN_PIN, HIGH); + + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, LOW); + + pinMode(GPS_VRTC_EN, OUTPUT); + digitalWrite(GPS_VRTC_EN, HIGH); + + pinMode(PIN_GPS_RESET, OUTPUT); + digitalWrite(PIN_GPS_RESET, LOW); + + pinMode(GPS_SLEEP_INT, OUTPUT); + digitalWrite(GPS_SLEEP_INT, HIGH); + + pinMode(GPS_RTC_INT, OUTPUT); + digitalWrite(GPS_RTC_INT, LOW); + + pinMode(GPS_RESETB_OUT, INPUT); +} \ No newline at end of file diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h new file mode 100644 index 000000000..75d8ddffc --- /dev/null +++ b/variants/tracker-t1000-e/variant.h @@ -0,0 +1,150 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_TRACKER_T1000_E_ +#define _VARIANT_TRACKER_T1000_E_ + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +#define PIN_3V3_EN (32 + 6) // P1.6, Power to Sensors +#define PIN_3V3_ACC_EN (32 + 7) // P1.7, Power to Acc + +#define PIN_LED1 (0 + 24) // P0.24 +#define LED_PIN PIN_LED1 +#define LED_BUILTIN -1 +#define LED_BLUE -1 // Actually green +#define LED_STATE_ON 1 // State when LED is lit + +#define BUTTON_PIN (0 + 6) // P0.6 +#define BUTTON_ACTIVE_LOW false +#define BUTTON_ACTIVE_PULLUP false +#define BUTTON_SENSE_TYPE 0x6 + +#define HAS_WIRE 1 + +#define WIRE_INTERFACES_COUNT 1 + +// unused pins +#define PIN_WIRE_SDA (0 + 9) // P0.26 +#define PIN_WIRE_SCL (0 + 10) // P0.27 + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (0 + 14) // P0.14 +#define PIN_SERIAL1_TX (0 + 13) // P0.13 + +#define PIN_SERIAL2_RX (0 + 17) // P0.17 +#define PIN_SERIAL2_TX (0 + 16) // P0.16 + +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (32 + 8) // P1.08 +#define PIN_SPI_MOSI (32 + 9) // P1.09 +#define PIN_SPI_SCK (0 + 11) // P0.11 +#define PIN_SPI_NSS (0 + 12) // P0.12 + +#define LORA_RESET (32 + 10) // P1.10 // RST +#define LORA_DIO1 (32 + 1) // P1.01 // IRQ +#define LORA_DIO2 (0 + 7) // P0.07 // BUSY +#define LORA_SCK PIN_SPI_SCK +#define LORA_MISO PIN_SPI_MISO +#define LORA_MOSI PIN_SPI_MOSI +#define LORA_CS PIN_SPI_NSS + +// supported modules list +#define USE_LR1110 + +#define LR1110_IRQ_PIN LORA_DIO1 +#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_BUSY_PIN LORA_DIO2 +#define LR1110_SPI_NSS_PIN LORA_CS +#define LR1110_SPI_SCK_PIN LORA_SCK +#define LR1110_SPI_MOSI_PIN LORA_MOSI +#define LR1110_SPI_MISO_PIN LORA_MISO + +#define LR11X0_DIO3_TCXO_VOLTAGE 1.6 +#define LR11X0_DIO_AS_RF_SWITCH +#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 + +#define HAS_GPS 1 +#define GNSS_AIROHA +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX + +#define GPS_BAUDRATE 115200 + +#define PIN_GPS_EN (32 + 11) // P1.11 +#define GPS_EN_ACTIVE HIGH + +#define PIN_GPS_RESET (32 + 15) // P1.15 +#define GPS_RESET_MODE HIGH + +#define GPS_VRTC_EN (0 + 8) // P0.8, awlays high +#define GPS_SLEEP_INT (32 + 12) // P1.12, awlays high +#define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH +#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up + +#define GPS_FIX_HOLD_TIME 15000 // ms +#define BATTERY_PIN 2 +#define ADC_MULTIPLIER (2.0F) + +#define ADC_RESOLUTION 14 +#define BATTERY_SENSE_RESOLUTION_BITS 12 + +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 + +// Buzzer +#define BUZZER_EN_PIN (32 + 5) // P1.05, awlays high +#define PIN_BUZZER (0 + 25) // P0.25, pwm output + +#define T1000X_SENSOR_EN +#define T1000X_VCC_PIN (0 + 4) // P0.4 +#define T1000X_NTC_PIN (0 + 31) // P0.31 +#define T1000X_LUX_PIN (0 + 29) // P0.29 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif // _VARIANT_TRACKER_T1000_E_ diff --git a/variants/wio-t1000-s/variant.h b/variants/wio-t1000-s/variant.h index 86bd34f62..fa6ea4abc 100644 --- a/variants/wio-t1000-s/variant.h +++ b/variants/wio-t1000-s/variant.h @@ -106,7 +106,7 @@ extern "C" { #define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 #define HAS_GPS 1 -#define GNSS_Airoha +#define GNSS_AIROHA #define GPS_RX_PIN PIN_SERIAL1_RX #define GPS_TX_PIN PIN_SERIAL1_TX From c5f2d2736d77f80652963621b621a0c85cdab810 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 24 Jul 2024 21:14:58 -0500 Subject: [PATCH 003/305] Whitespace trunk grousing --- src/ButtonThread.cpp | 2 +- src/gps/GPS.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index d479de6ed..914ff8e06 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -42,7 +42,7 @@ ButtonThread::ButtonThread() : OSThread("Button") int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin #if defined(HELTEC_CAPSULE_SENSOR_V3) this->userButton = OneButton(pin, false, false); -#elif defined(BUTTON_ACTIVE_LOW) +#elif defined(BUTTON_ACTIVE_LOW) this->userButton = OneButton(pin, BUTTON_ACTIVE_LOW, BUTTON_ACTIVE_PULLUP); #else this->userButton = OneButton(pin, true, true); diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 494622dc6..aec3d595d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1180,7 +1180,7 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->updateBaudRate(serialSpeed); } #endif -#ifdef GNSS_AIROHA +#ifdef GNSS_AIROHA return GNSS_MODEL_UNKNOWN; #else From 1481ce987e405c8856f0b7aa39f98ecd2c48dd1c Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Thu, 25 Jul 2024 20:05:03 +0200 Subject: [PATCH 004/305] Fix T1000-E GPS - some changes went missing from #4303? (#4328) * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp * Update GPS.cpp --- src/gps/GPS.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index aec3d595d..815eb9f0f 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -400,9 +400,16 @@ bool GPS::setup() int msglen = 0; if (!didSerialInit) { +#ifdef GNSS_AIROHA + if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { + probe(GPS_BAUDRATE); + LOG_INFO("GPS setting to %d.\n", GPS_BAUDRATE); + } +#else +#if !defined(GPS_UC6580) if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { - + // if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate. if (speedSelect == 0 && GPS_BAUDRATE != serialSpeeds[speedSelect]) { speedSelect = std::find(serialSpeeds, std::end(serialSpeeds), GPS_BAUDRATE) - serialSpeeds; @@ -423,6 +430,9 @@ bool GPS::setup() } else { gnssModel = GNSS_MODEL_UNKNOWN; } +#else + gnssModel = GNSS_MODEL_UC6580; +#endif if (gnssModel == GNSS_MODEL_MTK) { /* @@ -774,6 +784,7 @@ bool GPS::setup() LOG_INFO("GNSS module configuration saved!\n"); } } +#endif didSerialInit = true; } @@ -1789,4 +1800,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS \ No newline at end of file +#endif // Exclude GPS From 7ac64bd7626c389f405ae1c45599f71a6fdf77df Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 25 Jul 2024 13:09:28 -0500 Subject: [PATCH 005/305] Trunk --- src/gps/GPS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 815eb9f0f..c50bc7b41 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -409,7 +409,7 @@ bool GPS::setup() #if !defined(GPS_UC6580) if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { - + // if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate. if (speedSelect == 0 && GPS_BAUDRATE != serialSpeeds[speedSelect]) { speedSelect = std::find(serialSpeeds, std::end(serialSpeeds), GPS_BAUDRATE) - serialSpeeds; From 4b0bbb8af13a67f92e6e1a353b0b8d871b279cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 26 Jul 2024 03:16:21 +0200 Subject: [PATCH 006/305] Make STM compile again and update toolchain. (#2960) * Make STM compile again and update toolchain. The binary is too big for the flash. WIP * Making progress with OSFS, still WIP * more progress, still too big. Adding RAK3172 to the equasion * Make STM compile again and update toolchain. The binary is too big for the flash. WIP * Making progress with OSFS, still WIP * more progress, still too big. Adding RAK3172 to the equasion * still too big * minimize build * trunk fmt * fix a couple of symbol clashes * trunk fmt * down to 101% with a release vs. debug build and omitting the flash strings * fix compilation * fix compilation once more * update protobufs linkage * - Toolchain updated - Fixed macro error * silence compiler warning note: do something about this assert... * new toolkit and fix Power.cpp * STM32WL make it fit (#4330) * Add option to exclude I2C parts The I2C hals and related code uses a significant amount of flash space and aren't required for a basic node. * Add option to disable Admin and NodeInfo modules Disabled by default in minimal build. This saves a significant amount of flash * Disable unused hals These use up significant flash * Add float support for printf for debugging Makes serial look nice for debugging * This breaks my build for some reason * These build flags can save a bit of flash * Don't disable NodeInfo and Admin modules in minimal build They fit in flash * Don't include printf float support by default Only useful for debugging --------- Co-authored-by: Adam Lawson --------- Co-authored-by: Ben Meadors Co-authored-by: Adam Lawson --- arch/stm32/stm32.ini | 36 ++ arch/stm32/stm32wl5e.ini | 28 -- ...generic_wl5e.json => wiscore_rak3172.json} | 3 + src/FSCommon.cpp | 60 ++- src/FSCommon.h | 11 +- src/Power.cpp | 5 +- src/configuration.h | 1 + src/detect/ScanI2CTwoWire.cpp | 4 +- src/detect/ScanI2CTwoWire.h | 6 +- src/freertosinc.h | 2 +- src/gps/GPS.cpp | 4 +- src/gps/GeoCoord.cpp | 4 +- src/gps/GeoCoord.h | 4 +- src/main.cpp | 15 +- src/mesh/NodeDB.cpp | 16 +- src/mesh/NodeDB.h | 2 +- src/mesh/PhoneAPI.cpp | 8 + src/mesh/STM32WLE5JCInterface.h | 2 +- src/mesh/mesh-pb-constants.cpp | 6 +- src/modules/AdminModule.cpp | 2 + src/modules/CannedMessageModule.cpp | 2 +- src/modules/ExternalNotificationModule.cpp | 2 +- src/modules/Modules.cpp | 10 + src/platform/stm32wl/InternalFileSystem.cpp | 141 ------- src/platform/stm32wl/InternalFileSystem.h | 54 --- src/platform/stm32wl/LittleFS.cpp | 258 ------------- src/platform/stm32wl/LittleFS.h | 85 ---- src/platform/stm32wl/LittleFS_File.cpp | 362 ------------------ src/platform/stm32wl/LittleFS_File.h | 82 ---- src/platform/stm32wl/STM32WLCryptoEngine.cpp | 47 ++- src/platform/stm32wl/main-stm32wl.cpp | 8 - src/xmodem.cpp | 5 +- src/xmodem.h | 3 + variants/rak3172/platformio.ini | 12 + variants/rak3172/variant.h | 12 + variants/wio-e5/platformio.ini | 29 +- variants/wio-e5/variant.h | 1 + 37 files changed, 271 insertions(+), 1061 deletions(-) create mode 100644 arch/stm32/stm32.ini delete mode 100644 arch/stm32/stm32wl5e.ini rename boards/{generic_wl5e.json => wiscore_rak3172.json} (91%) delete mode 100644 src/platform/stm32wl/InternalFileSystem.cpp delete mode 100644 src/platform/stm32wl/InternalFileSystem.h delete mode 100644 src/platform/stm32wl/LittleFS.cpp delete mode 100644 src/platform/stm32wl/LittleFS.h delete mode 100644 src/platform/stm32wl/LittleFS_File.cpp delete mode 100644 src/platform/stm32wl/LittleFS_File.h create mode 100644 variants/rak3172/platformio.ini create mode 100644 variants/rak3172/variant.h diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini new file mode 100644 index 000000000..2cea4bbc5 --- /dev/null +++ b/arch/stm32/stm32.ini @@ -0,0 +1,36 @@ +[stm32_base] +extends = arduino_base +platform = ststm32 +platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#361a7fdb67e2a7104e99b4f42a802469eef8b129 + +build_type = release + +;board_build.flash_offset = 0x08000000 + +build_flags = + ${arduino_base.build_flags} + -flto + -Isrc/platform/stm32wl -g + -DMESHTASTIC_MINIMIZE_BUILD + -DDEBUG_MUTE +; -DVECT_TAB_OFFSET=0x08000000 + -DconfigUSE_CMSIS_RTOS_V2=1 +; -DSPI_MODE_0=SPI_MODE0 + -fmerge-all-constants + -ffunction-sections + -fdata-sections + +build_src_filter = + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - + +board_upload.offset_address = 0x08000000 +upload_protocol = stlink + +lib_deps = + ${env.lib_deps} + charlesbaynham/OSFS@^1.2.3 + https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e + +lib_ignore = + mathertel/OneButton + Wire \ No newline at end of file diff --git a/arch/stm32/stm32wl5e.ini b/arch/stm32/stm32wl5e.ini deleted file mode 100644 index 4d74ade8f..000000000 --- a/arch/stm32/stm32wl5e.ini +++ /dev/null @@ -1,28 +0,0 @@ -[stm32wl5e_base] -platform_packages = platformio/framework-arduinoststm32 @ https://github.com/stm32duino/Arduino_Core_STM32.git#6e3f9910d0122e82a6c3438507dfac3d2fd80a39 -platform = ststm32 -board = generic_wl5e -framework = arduino - -build_type = debug - -build_flags = - ${arduino_base.build_flags} - -Isrc/platform/stm32wl -g - -DconfigUSE_CMSIS_RTOS_V2=1 - -DVECT_TAB_OFFSET=0x08000000 - -build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - - - -board_upload.offset_address = 0x08000000 -upload_protocol = stlink - -lib_deps = - ${env.lib_deps} - https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b - https://github.com/littlefs-project/littlefs.git#v2.5.1 - https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1 - -lib_ignore = - mathertel/OneButton \ No newline at end of file diff --git a/boards/generic_wl5e.json b/boards/wiscore_rak3172.json similarity index 91% rename from boards/generic_wl5e.json rename to boards/wiscore_rak3172.json index 5c4bc24a7..714e09115 100644 --- a/boards/generic_wl5e.json +++ b/boards/wiscore_rak3172.json @@ -1,5 +1,8 @@ { "build": { + "arduino": { + "variant_h": "variant_RAK3172_MODULE.h" + }, "core": "stm32", "cpu": "cortex-m4", "extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX", diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 7d3788c4d..3017ec085 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -24,6 +24,30 @@ SPIClass SPI1(HSPI); #endif // HAS_SDCARD +#if defined(ARCH_STM32WL) + +uint16_t OSFS::startOfEEPROM = 1; +uint16_t OSFS::endOfEEPROM = 2048; + +// 3) How do I read from the medium? +void OSFS::readNBytes(uint16_t address, unsigned int num, byte *output) +{ + for (uint16_t i = address; i < address + num; i++) { + *output = EEPROM.read(i); + output++; + } +} + +// 4) How to I write to the medium? +void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input) +{ + for (uint16_t i = address; i < address + num; i++) { + EEPROM.update(i, *input); + input++; + } +} +#endif + /** * @brief Copies a file from one location to another. * @@ -33,7 +57,33 @@ SPIClass SPI1(HSPI); */ bool copyFile(const char *from, const char *to) { -#ifdef FSCom +#ifdef ARCH_STM32WL + unsigned char cbuffer[2048]; + + // Var to hold the result of actions + OSFS::result r; + + r = OSFS::getFile(from, cbuffer); + + if (r == notfound) { + LOG_ERROR("Failed to open source file %s\n", from); + return false; + } else if (r == noerr) { + r = OSFS::newFile(to, cbuffer, true); + if (r == noerr) { + return true; + } else { + LOG_ERROR("OSFS Error %d\n", r); + return false; + } + + } else { + LOG_ERROR("OSFS Error %d\n", r); + return false; + } + return true; + +#elif defined(FSCom) unsigned char cbuffer[16]; File f1 = FSCom.open(from, FILE_O_READ); @@ -70,7 +120,13 @@ bool copyFile(const char *from, const char *to) */ bool renameFile(const char *pathFrom, const char *pathTo) { -#ifdef FSCom +#ifdef ARCH_STM32WL + if (copyFile(pathFrom, pathTo) && (OSFS::deleteFile(pathFrom) == OSFS::result::NO_ERROR)) { + return true; + } else { + return false; + } +#elif defined(FSCom) #ifdef ARCH_ESP32 // rename was fixed for ESP32 IDF LittleFS in April return FSCom.rename(pathFrom, pathTo); diff --git a/src/FSCommon.h b/src/FSCommon.h index 8fbabd952..3d485d1b1 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -15,10 +15,13 @@ #endif #if defined(ARCH_STM32WL) -#include "platform/stm32wl/InternalFileSystem.h" // STM32WL version -#define FSCom InternalFS -#define FSBegin() FSCom.begin() -using namespace LittleFS_Namespace; +// STM32WL series 2 Kbytes (8 rows of 256 bytes) +#include +#include + +// Useful consts +const OSFS::result noerr = OSFS::result::NO_ERROR; +const OSFS::result notfound = OSFS::result::FILE_NOT_FOUND; #endif #if defined(ARCH_RP2040) diff --git a/src/Power.cpp b/src/Power.cpp index 19c5c9937..138b06e71 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -200,7 +200,8 @@ class AnalogBatteryLevel : public HasBatteryLevel } #endif -#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR +#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(HAS_PMU) && \ + !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (hasINA()) { LOG_DEBUG("Using INA on I2C addr 0x%x for device battery voltage\n", config.power.device_battery_ina_address); return getINAVoltage(); @@ -420,7 +421,7 @@ class AnalogBatteryLevel : public HasBatteryLevel } #endif -#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) uint16_t getINAVoltage() { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { diff --git a/src/configuration.h b/src/configuration.h index 6351c35b1..b298d5424 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -259,6 +259,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_SCREEN 1 #define MESHTASTIC_EXCLUDE_MQTT 1 #define MESHTASTIC_EXCLUDE_POWERMON 1 +#define MESHTASTIC_EXCLUDE_I2C 1 #endif // Turn off all optional modules diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 8738e2722..b831b0e71 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -1,7 +1,8 @@ #include "ScanI2CTwoWire.h" +#if !MESHTASTIC_EXCLUDE_I2C + #include "concurrency/LockGuard.h" -#include "configuration.h" #if defined(ARCH_PORTDUINO) #include "linux/LinuxHardwareI2C.h" #endif @@ -403,3 +404,4 @@ size_t ScanI2CTwoWire::countDevices() const { return foundDevices.size(); } +#endif \ No newline at end of file diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h index 82b48f6b4..c8dd96469 100644 --- a/src/detect/ScanI2CTwoWire.h +++ b/src/detect/ScanI2CTwoWire.h @@ -1,5 +1,8 @@ #pragma once +#include "configuration.h" +#if !MESHTASTIC_EXCLUDE_I2C + #include #include #include @@ -55,4 +58,5 @@ class ScanI2CTwoWire : public ScanI2C uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const; DeviceType probeOLED(ScanI2C::DeviceAddress) const; -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/src/freertosinc.h b/src/freertosinc.h index 166054241..e9e6cd53a 100644 --- a/src/freertosinc.h +++ b/src/freertosinc.h @@ -12,7 +12,7 @@ #include #endif -#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_RP2040) +#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_RP2040) #define HAS_FREE_RTOS #include diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index c50bc7b41..67f6adb98 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1182,7 +1182,7 @@ int GPS::prepareDeepSleep(void *unused) GnssModel_t GPS::probe(int serialSpeed) { -#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) +#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) _serial_gps->end(); _serial_gps->begin(serialSpeed); #else @@ -1270,7 +1270,7 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->write(_message_prt, sizeof(_message_prt)); delay(500); serialSpeed = 9600; -#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) +#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) _serial_gps->end(); _serial_gps->begin(serialSpeed); #else diff --git a/src/gps/GeoCoord.cpp b/src/gps/GeoCoord.cpp index 2224bd281..5abb25a06 100644 --- a/src/gps/GeoCoord.cpp +++ b/src/gps/GeoCoord.cpp @@ -493,7 +493,7 @@ std::shared_ptr GeoCoord::pointAtDistance(double bearing, double range * The bearing in string format * @return Bearing in degrees */ -uint GeoCoord::bearingToDegrees(const char *bearing) +unsigned int GeoCoord::bearingToDegrees(const char *bearing) { if (strcmp(bearing, "N") == 0) return 0; @@ -537,7 +537,7 @@ uint GeoCoord::bearingToDegrees(const char *bearing) * The bearing in degrees * @return Bearing in string format */ -const char *GeoCoord::degreesToBearing(uint degrees) +const char *GeoCoord::degreesToBearing(unsigned int degrees) { if (degrees >= 348 || degrees < 11) return "N"; diff --git a/src/gps/GeoCoord.h b/src/gps/GeoCoord.h index b02d12afb..ecdaf0ec7 100644 --- a/src/gps/GeoCoord.h +++ b/src/gps/GeoCoord.h @@ -117,8 +117,8 @@ class GeoCoord static float bearing(double lat1, double lon1, double lat2, double lon2); static float rangeRadiansToMeters(double range_radians); static float rangeMetersToRadians(double range_meters); - static uint bearingToDegrees(const char *bearing); - static const char *degreesToBearing(uint degrees); + static unsigned int bearingToDegrees(const char *bearing); + static const char *degreesToBearing(unsigned int degrees); // Point to point conversions int32_t distanceTo(const GeoCoord &pointB); diff --git a/src/main.cpp b/src/main.cpp index 187942344..dc5863bbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,11 @@ #include "concurrency/OSThread.h" #include "concurrency/Periodic.h" #include "detect/ScanI2C.h" + +#if !MESHTASTIC_EXCLUDE_I2C #include "detect/ScanI2CTwoWire.h" +#include +#endif #include "detect/axpDebug.h" #include "detect/einkScan.h" #include "graphics/RAKled.h" @@ -31,7 +35,6 @@ #include "shutdown.h" #include "sleep.h" #include "target_specific.h" -#include #include #include // #include @@ -159,8 +162,10 @@ bool pauseBluetoothLogging = false; bool pmu_found; +#if !MESHTASTIC_EXCLUDE_I2C // Array map of sensor types with i2c address and wire as we'll find in the i2c scan std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = {}; +#endif Router *router = NULL; // Users of router don't care what sort of subclass implements that API @@ -349,6 +354,7 @@ void setup() #endif +#if !MESHTASTIC_EXCLUDE_I2C #if defined(I2C_SDA1) && defined(ARCH_RP2040) Wire1.setSDA(I2C_SDA1); Wire1.setSCL(I2C_SCL1); @@ -373,6 +379,7 @@ void setup() #elif HAS_WIRE Wire.begin(); #endif +#endif #ifdef PIN_LCD_RESET // FIXME - move this someplace better, LCD is at address 0x3F @@ -405,6 +412,7 @@ void setup() powerStatus->observe(&power->newStatus); power->setup(); // Must be after status handler is installed, so that handler gets notified of the initial configuration +#if !MESHTASTIC_EXCLUDE_I2C // We need to scan here to decide if we have a screen for nodeDB.init() and because power has been applied to // accessories auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); @@ -560,6 +568,7 @@ void setup() SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK) i2cScanner.reset(); +#endif #ifdef HAS_SDCARD setupSDCard(); @@ -620,6 +629,7 @@ void setup() screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // keep dimension of 128x64 #endif +#if !MESHTASTIC_EXCLUDE_I2C #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (acc_info.type != ScanI2C::DeviceType::NONE) { config.display.wake_on_tap_or_motion = true; @@ -635,6 +645,7 @@ void setup() ambientLightingThread = new AmbientLightingThread(rgb_found.type); } #endif +#endif #ifdef T_WATCH_S3 drv.begin(); @@ -721,6 +732,7 @@ void setup() RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_NO_AXP192); // Record a hardware fault for missing hardware #endif +#if !MESHTASTIC_EXCLUDE_I2C // Don't call screen setup until after nodedb is setup (because we need // the current region name) #if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \ @@ -733,6 +745,7 @@ void setup() #else if (screen_found.port != ScanI2C::I2CPort::NO_I2C) screen->setup(); +#endif #endif screen->print("Started...\n"); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index c0bed3437..4257837b3 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -192,9 +192,11 @@ bool NodeDB::factoryReset() LOG_INFO("Performing factory reset!\n"); // first, remove the "/prefs" (this removes most prefs) rmDir("/prefs"); +#ifdef FSCom if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) { LOG_ERROR("Could not remove rangetest.csv file\n"); } +#endif // second, install default state (this will deal with the duplicate mac address issue) installDefaultDeviceState(); installDefaultConfig(); @@ -574,7 +576,7 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t state = LoadFileResult::DECODE_FAILED; } else { LOG_INFO("Loaded %s successfully\n", filename); - state = LoadFileResult::SUCCESS; + state = LoadFileResult::LOAD_SUCCESS; } f.close(); } else { @@ -582,7 +584,7 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t } #else LOG_ERROR("ERROR: Filesystem not implemented\n"); - state = LoadFileState::NO_FILESYSTEM; + state = LoadFileResult::NO_FILESYSTEM; #endif return state; } @@ -593,7 +595,7 @@ void NodeDB::loadFromDisk() auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES * sizeof(meshtastic_NodeInfo), sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate); - if (state != LoadFileResult::SUCCESS) { + if (state != LoadFileResult::LOAD_SUCCESS) { installDefaultDeviceState(); // Our in RAM copy might now be corrupt } else { if (devicestate.version < DEVICESTATE_MIN_VER) { @@ -610,7 +612,7 @@ void NodeDB::loadFromDisk() state = loadProto(configFileName, meshtastic_LocalConfig_size, sizeof(meshtastic_LocalConfig), &meshtastic_LocalConfig_msg, &config); - if (state != LoadFileResult::SUCCESS) { + if (state != LoadFileResult::LOAD_SUCCESS) { installDefaultConfig(); // Our in RAM copy might now be corrupt } else { if (config.version < DEVICESTATE_MIN_VER) { @@ -623,7 +625,7 @@ void NodeDB::loadFromDisk() state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig), &meshtastic_LocalModuleConfig_msg, &moduleConfig); - if (state != LoadFileResult::SUCCESS) { + if (state != LoadFileResult::LOAD_SUCCESS) { installDefaultModuleConfig(); // Our in RAM copy might now be corrupt } else { if (moduleConfig.version < DEVICESTATE_MIN_VER) { @@ -636,7 +638,7 @@ void NodeDB::loadFromDisk() state = loadProto(channelFileName, meshtastic_ChannelFile_size, sizeof(meshtastic_ChannelFile), &meshtastic_ChannelFile_msg, &channelFile); - if (state != LoadFileResult::SUCCESS) { + if (state != LoadFileResult::LOAD_SUCCESS) { installDefaultChannels(); // Our in RAM copy might now be corrupt } else { if (channelFile.version < DEVICESTATE_MIN_VER) { @@ -648,7 +650,7 @@ void NodeDB::loadFromDisk() } state = loadProto(oemConfigFile, meshtastic_OEMStore_size, sizeof(meshtastic_OEMStore), &meshtastic_OEMStore_msg, &oemStore); - if (state == LoadFileResult::SUCCESS) { + if (state == LoadFileResult::LOAD_SUCCESS) { LOG_INFO("Loaded OEMStore\n"); } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 5207d8629..258b7276d 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -40,7 +40,7 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p); enum LoadFileResult { // Successfully opened the file - SUCCESS = 1, + LOAD_SUCCESS = 1, // File does not exist NOT_FOUND = 2, // Device does not have a filesystem diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 0b63b4a58..f0775741a 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -42,7 +42,9 @@ void PhoneAPI::handleStartConfig() if (!isConnected()) { onConnectionChanged(true); observe(&service.fromNumChanged); +#ifdef FSCom observe(&xModem.packetReady); +#endif } // even if we were already connected - restart our state machine @@ -62,7 +64,9 @@ void PhoneAPI::close() state = STATE_SEND_NOTHING; unobserve(&service.fromNumChanged); +#ifdef FSCom unobserve(&xModem.packetReady); +#endif releasePhonePacket(); // Don't leak phone packets on shutdown releaseQueueStatusPhonePacket(); releaseMqttClientProxyPhonePacket(); @@ -110,7 +114,9 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) break; case meshtastic_ToRadio_xmodemPacket_tag: LOG_INFO("Got xmodem packet\n"); +#ifdef FSCom xModem.handlePacket(toRadioScratch.xmodemPacket); +#endif break; #if !MESHTASTIC_EXCLUDE_MQTT case meshtastic_ToRadio_mqttClientProxyMessage_tag: @@ -496,12 +502,14 @@ bool PhoneAPI::available() if (hasPacket) return true; +#ifdef FSCom if (xmodemPacketForPhone.control == meshtastic_XModem_Control_NUL) xmodemPacketForPhone = xModem.getForPhone(); if (xmodemPacketForPhone.control != meshtastic_XModem_Control_NUL) { xModem.resetForPhone(); return true; } +#endif #ifdef ARCH_ESP32 #if !MESHTASTIC_EXCLUDE_STOREFORWARD diff --git a/src/mesh/STM32WLE5JCInterface.h b/src/mesh/STM32WLE5JCInterface.h index 73d53d92f..fad793332 100644 --- a/src/mesh/STM32WLE5JCInterface.h +++ b/src/mesh/STM32WLE5JCInterface.h @@ -23,7 +23,7 @@ static const float tcxoVoltage = 1.7; * Wio-E5 module ONLY transmits through RFO_HP * Receive: PA4=1, PA5=0 * Transmit(high output power, SMPS mode): PA4=0, PA5=1 */ -static const RADIOLIB_PIN_TYPE rfswitch_pins[3] = {PA4, PA5, RADIOLIB_NC}; +static const RADIOLIB_PIN_TYPE rfswitch_pins[5] = {PA4, PA5, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; static const Module::RfSwitchMode_t rfswitch_table[4] = { {STM32WLx::MODE_IDLE, {LOW, LOW}}, {STM32WLx::MODE_RX, {HIGH, LOW}}, {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, END_OF_MODE_TABLE}; diff --git a/src/mesh/mesh-pb-constants.cpp b/src/mesh/mesh-pb-constants.cpp index 93dbf0178..676208e25 100644 --- a/src/mesh/mesh-pb-constants.cpp +++ b/src/mesh/mesh-pb-constants.cpp @@ -1,6 +1,7 @@ -#include "mesh-pb-constants.h" -#include "FSCommon.h" #include "configuration.h" + +#include "FSCommon.h" +#include "mesh-pb-constants.h" #include #include #include @@ -15,6 +16,7 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc LOG_ERROR("Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream)); assert( 0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options + return 0; } else { return stream.bytes_written; } diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 98b789f41..8939b972b 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -259,11 +259,13 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } case meshtastic_AdminMessage_delete_file_request_tag: { LOG_DEBUG("Client is requesting to delete file: %s\n", r->delete_file_request); +#ifdef FSCom if (FSCom.remove(r->delete_file_request)) { LOG_DEBUG("Successfully deleted file\n"); } else { LOG_DEBUG("Failed to delete file\n"); } +#endif break; } #ifdef ARCH_PORTDUINO diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 84b5a3260..524d37a3d 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -1066,7 +1066,7 @@ void CannedMessageModule::loadProtoForModule() { if (nodeDB->loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg, - &cannedMessageModuleConfig) != LoadFileResult::SUCCESS) { + &cannedMessageModuleConfig) != LoadFileResult::LOAD_SUCCESS) { installDefaultCannedMessageModuleConfig(); } } diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index c02559240..652db04d3 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -355,7 +355,7 @@ ExternalNotificationModule::ExternalNotificationModule() if (moduleConfig.external_notification.enabled) { if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), - &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::SUCCESS) { + &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) { memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); strncpy(rtttlConfig.ringtone, "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p", diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 300afc246..5a0e36fea 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -7,7 +7,9 @@ #include "input/cardKbI2cImpl.h" #include "input/kbMatrixImpl.h" #endif +#if !MESHTASTIC_EXCLUDE_ADMIN #include "modules/AdminModule.h" +#endif #if !MESHTASTIC_EXCLUDE_ATAK #include "modules/AtakPluginModule.h" #endif @@ -20,7 +22,9 @@ #if !MESHTASTIC_EXCLUDE_NEIGHBORINFO #include "modules/NeighborInfoModule.h" #endif +#if !MESHTASTIC_EXCLUDE_NODEINFO #include "modules/NodeInfoModule.h" +#endif #if !MESHTASTIC_EXCLUDE_GPS #include "modules/PositionModule.h" #endif @@ -88,8 +92,12 @@ void setupModules() #if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER inputBroker = new InputBroker(); #endif +#if !MESHTASTIC_EXCLUDE_ADMIN adminModule = new AdminModule(); +#endif +#if !MESHTASTIC_EXCLUDE_NODEINFO nodeInfoModule = new NodeInfoModule(); +#endif #if !MESHTASTIC_EXCLUDE_GPS positionModule = new PositionModule(); #endif @@ -192,7 +200,9 @@ void setupModules() #endif #endif } else { +#if !MESHTASTIC_EXCLUDE_ADMIN adminModule = new AdminModule(); +#endif #if HAS_TELEMETRY new DeviceTelemetryModule(); #endif diff --git a/src/platform/stm32wl/InternalFileSystem.cpp b/src/platform/stm32wl/InternalFileSystem.cpp deleted file mode 100644 index d42a646a5..000000000 --- a/src/platform/stm32wl/InternalFileSystem.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 hathach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "InternalFileSystem.h" -#include - -//--------------------------------------------------------------------+ -// LFS Disk IO -//--------------------------------------------------------------------+ - -static inline uint32_t lba2addr(uint32_t block) -{ - return ((uint32_t)LFS_FLASH_ADDR) + block * LFS_BLOCK_SIZE; -} - -static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) -{ - (void)c; - - uint32_t addr = lba2addr(block) + off; - uint8_t prom; - - for (int i = 0; i < size; i++) { - prom = EEPROM.read(addr + i); - memcpy((char *)buffer + i, &prom, 1); - } - return 0; -} - -// Program a region in a block. The block must have previously -// been erased. Negative error codes are propagated to the user. -// May return LFS_ERR_CORRUPT if the block should be considered bad. -static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) -{ - (void)c; - - uint32_t addr = lba2addr(block) + off; - uint8_t prom; - - for (int i = 0; i < size; i++) { - memcpy(&prom, (char *)buffer + i, 1); - EEPROM.update(addr + i, prom); - } - return 0; -} - -// Erase a block. A block must be erased before being programmed. -// The state of an erased block is undefined. Negative error codes -// are propagated to the user. -// May return LFS_ERR_CORRUPT if the block should be considered bad. -static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block) -{ - (void)c; - - uint32_t addr = lba2addr(block); - - // implement as write 0xff to whole block address - for (int i = 0; i < LFS_BLOCK_SIZE; i++) { - EEPROM.update(addr, 0xff); - } - - return 0; -} - -// Sync the state of the underlying block device. Negative error codes -// are propagated to the user. -static int _internal_flash_sync(const struct lfs_config *c) -{ - // we don't use a ram cache, this is a noop - return 0; -} - -static struct lfs_config _InternalFSConfig = {.context = NULL, - - .read = _internal_flash_read, - .prog = _internal_flash_prog, - .erase = _internal_flash_erase, - .sync = _internal_flash_sync, - - .read_size = LFS_CACHE_SIZE, - .prog_size = LFS_CACHE_SIZE, - .block_size = LFS_BLOCK_SIZE, - .block_count = LFS_FLASH_TOTAL_SIZE / LFS_BLOCK_SIZE, - .block_cycles = - 500, // protection against wear leveling (suggested values between 100-1000) - .cache_size = LFS_CACHE_SIZE, - .lookahead_size = LFS_CACHE_SIZE, - - .read_buffer = lfs_read_buffer, - .prog_buffer = lfs_prog_buffer, - .lookahead_buffer = lfs_lookahead_buffer}; - -InternalFileSystem InternalFS; - -//--------------------------------------------------------------------+ -// -//--------------------------------------------------------------------+ - -InternalFileSystem::InternalFileSystem(void) : LittleFS(&_InternalFSConfig) {} - -bool InternalFileSystem::begin(void) -{ - // failed to mount, erase all sector then format and mount again - if (!LittleFS::begin()) { - // Erase all sectors of internal flash region for Filesystem. - // implement as write 0xff to whole block address - for (uint32_t addr = LFS_FLASH_ADDR; addr < (LFS_FLASH_ADDR + LFS_FLASH_TOTAL_SIZE); addr++) { - EEPROM.update(addr, 0xff); - } - - // lfs format - this->format(); - - // mount again if still failed, give up - if (!LittleFS::begin()) - return false; - } - - return true; -} diff --git a/src/platform/stm32wl/InternalFileSystem.h b/src/platform/stm32wl/InternalFileSystem.h deleted file mode 100644 index 66344194e..000000000 --- a/src/platform/stm32wl/InternalFileSystem.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 hathach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef INTERNALFILESYSTEM_H_ -#define INTERNALFILESYSTEM_H_ - -#include "LittleFS.h" - -// The EEPROM Library assumes our usable flash area starts at logical 0 -#define LFS_FLASH_ADDR 0 - -// use the built in EEPROM emulation. Total Size is 2Kbyte -#define LFS_BLOCK_SIZE 128 // min. block size is 128 to fit CTZ pointers -#define LFS_CACHE_SIZE 16 - -#define LFS_FLASH_TOTAL_SIZE FLASH_PAGE_SIZE - -static uint8_t lfs_read_buffer[LFS_CACHE_SIZE] = {0}; -static uint8_t lfs_prog_buffer[LFS_CACHE_SIZE] = {0}; -static uint8_t lfs_lookahead_buffer[LFS_CACHE_SIZE] = {0}; - -class InternalFileSystem : public LittleFS -{ - public: - InternalFileSystem(void); - - // overwrite to also perform low level format (sector erase of whole flash region) - bool begin(void); -}; - -extern InternalFileSystem InternalFS; - -#endif /* INTERNALFILESYSTEM_H_ */ diff --git a/src/platform/stm32wl/LittleFS.cpp b/src/platform/stm32wl/LittleFS.cpp deleted file mode 100644 index b1267d88a..000000000 --- a/src/platform/stm32wl/LittleFS.cpp +++ /dev/null @@ -1,258 +0,0 @@ -#include -#include - -#include "LittleFS.h" - -using namespace LittleFS_Namespace; - -//--------------------------------------------------------------------+ -// Implementation -//--------------------------------------------------------------------+ - -LittleFS::LittleFS(void) : LittleFS(NULL) {} - -LittleFS::LittleFS(struct lfs_config *cfg) -{ - memset(&_lfs, 0, sizeof(_lfs)); - _lfs_cfg = cfg; - _mounted = false; - _mutex = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace); -} - -LittleFS::~LittleFS() {} - -// Initialize and mount the file system -// Return true if mounted successfully else probably corrupted. -// User should format the disk and try again -bool LittleFS::begin(struct lfs_config *cfg) -{ - _lockFS(); - - bool ret; - // not a loop, just an quick way to short-circuit on error - do { - if (_mounted) { - ret = true; - break; - } - if (cfg) { - _lfs_cfg = cfg; - } - if (nullptr == _lfs_cfg) { - ret = false; - break; - } - // actually attempt to mount, and log error if one occurs - int err = lfs_mount(&_lfs, _lfs_cfg); - PRINT_LFS_ERR(err); - _mounted = (err == LFS_ERR_OK); - ret = _mounted; - } while (0); - - _unlockFS(); - return ret; -} - -// Tear down and unmount file system -void LittleFS::end(void) -{ - _lockFS(); - - if (_mounted) { - _mounted = false; - int err = lfs_unmount(&_lfs); - PRINT_LFS_ERR(err); - (void)err; - } - - _unlockFS(); -} - -bool LittleFS::format(void) -{ - _lockFS(); - - int err = LFS_ERR_OK; - bool attemptMount = _mounted; - // not a loop, just an quick way to short-circuit on error - do { - // if already mounted: umount first -> format -> remount - if (_mounted) { - _mounted = false; - err = lfs_unmount(&_lfs); - if (LFS_ERR_OK != err) { - PRINT_LFS_ERR(err); - break; - } - } - err = lfs_format(&_lfs, _lfs_cfg); - if (LFS_ERR_OK != err) { - PRINT_LFS_ERR(err); - break; - } - - if (attemptMount) { - err = lfs_mount(&_lfs, _lfs_cfg); - if (LFS_ERR_OK != err) { - PRINT_LFS_ERR(err); - break; - } - _mounted = true; - } - // success! - } while (0); - - _unlockFS(); - return LFS_ERR_OK == err; -} - -// Open a file or folder -LittleFS_Namespace::File LittleFS::open(char const *filepath, uint8_t mode) -{ - // No lock is required here ... the File() object will synchronize with the mutex provided - return LittleFS_Namespace::File(filepath, mode, *this); -} - -// Check if file or folder exists -bool LittleFS::exists(char const *filepath) -{ - struct lfs_info info; - _lockFS(); - - bool ret = (0 == lfs_stat(&_lfs, filepath, &info)); - - _unlockFS(); - return ret; -} - -// Create a directory, create intermediate parent if needed -bool LittleFS::mkdir(char const *filepath) -{ - bool ret = true; - const char *slash = filepath; - if (slash[0] == '/') - slash++; // skip root '/' - - _lockFS(); - - // make intermediate parent directory(ies) - while (NULL != (slash = strchr(slash, '/'))) { - char parent[slash - filepath + 1] = {0}; - memcpy(parent, filepath, slash - filepath); - - int rc = lfs_mkdir(&_lfs, parent); - if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) { - PRINT_LFS_ERR(rc); - ret = false; - break; - } - slash++; - } - // make the final requested directory - if (ret) { - int rc = lfs_mkdir(&_lfs, filepath); - if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) { - PRINT_LFS_ERR(rc); - ret = false; - } - } - - _unlockFS(); - return ret; -} - -// Remove a file -bool LittleFS::remove(char const *filepath) -{ - _lockFS(); - - int err = lfs_remove(&_lfs, filepath); - PRINT_LFS_ERR(err); - - _unlockFS(); - return LFS_ERR_OK == err; -} - -// Rename a file -bool LittleFS::rename(char const *oldfilepath, char const *newfilepath) -{ - _lockFS(); - - int err = lfs_rename(&_lfs, oldfilepath, newfilepath); - PRINT_LFS_ERR(err); - - _unlockFS(); - return LFS_ERR_OK == err; -} - -// Remove a folder -bool LittleFS::rmdir(char const *filepath) -{ - _lockFS(); - - int err = lfs_remove(&_lfs, filepath); - PRINT_LFS_ERR(err); - - _unlockFS(); - return LFS_ERR_OK == err; -} - -// Remove a folder recursively -bool LittleFS::rmdir_r(char const *filepath) -{ - /* adafruit: lfs is modified to remove non-empty folder, - According to below issue, comment these 2 line won't corrupt filesystem - at least when using LFS v1. If moving to LFS v2, see tracked issue - to see if issues (such as the orphans in threaded linked list) are resolved. - https://github.com/ARMmbed/littlefs/issues/43 - */ - _lockFS(); - - int err = lfs_remove(&_lfs, filepath); - PRINT_LFS_ERR(err); - - _unlockFS(); - return LFS_ERR_OK == err; -} - -//------------- Debug -------------// -#if CFG_DEBUG - -const char *dbg_strerr_lfs(int32_t err) -{ - switch (err) { - case LFS_ERR_OK: - return "LFS_ERR_OK"; - case LFS_ERR_IO: - return "LFS_ERR_IO"; - case LFS_ERR_CORRUPT: - return "LFS_ERR_CORRUPT"; - case LFS_ERR_NOENT: - return "LFS_ERR_NOENT"; - case LFS_ERR_EXIST: - return "LFS_ERR_EXIST"; - case LFS_ERR_NOTDIR: - return "LFS_ERR_NOTDIR"; - case LFS_ERR_ISDIR: - return "LFS_ERR_ISDIR"; - case LFS_ERR_NOTEMPTY: - return "LFS_ERR_NOTEMPTY"; - case LFS_ERR_BADF: - return "LFS_ERR_BADF"; - case LFS_ERR_INVAL: - return "LFS_ERR_INVAL"; - case LFS_ERR_NOSPC: - return "LFS_ERR_NOSPC"; - case LFS_ERR_NOMEM: - return "LFS_ERR_NOMEM"; - - default: - static char errcode[10]; - sprintf(errcode, "%ld", err); - return errcode; - } - - return NULL; -} - -#endif diff --git a/src/platform/stm32wl/LittleFS.h b/src/platform/stm32wl/LittleFS.h deleted file mode 100644 index 4a0b01af2..000000000 --- a/src/platform/stm32wl/LittleFS.h +++ /dev/null @@ -1,85 +0,0 @@ - -#ifndef LITTLEFS_H_ -#define LITTLEFS_H_ - -#include - -#include "lfs.h" - -#include "LittleFS_File.h" - -#include "FreeRTOS.h" // tied to FreeRTOS for serialization -#include "semphr.h" - -class LittleFS -{ - public: - LittleFS(void); - explicit LittleFS(struct lfs_config *cfg); - virtual ~LittleFS(); - - bool begin(struct lfs_config *cfg = NULL); - void end(void); - - // Open the specified file/directory with the supplied mode (e.g. read or - // write, etc). Returns a File object for interacting with the file. - // Note that currently only one file can be open at a time. - LittleFS_Namespace::File open(char const *filename, uint8_t mode = LittleFS_Namespace::FILE_O_READ); - - // Methods to determine if the requested file path exists. - bool exists(char const *filepath); - - // Create the requested directory hierarchy--if intermediate directories - // do not exist they will be created. - bool mkdir(char const *filepath); - - // Delete the file. - bool remove(char const *filepath); - - // Rename the file. - bool rename(char const *oldfilepath, char const *newfilepath); - - // Delete a folder (must be empty) - bool rmdir(char const *filepath); - - // Delete a folder (recursively) - bool rmdir_r(char const *filepath); - - // format file system - bool format(void); - - /*------------------------------------------------------------------*/ - /* INTERNAL USAGE ONLY - * Although declare as public, it is meant to be invoked by internal - * code. User should not call these directly - *------------------------------------------------------------------*/ - lfs_t *_getFS(void) { return &_lfs; } - void _lockFS(void) { xSemaphoreTake(_mutex, portMAX_DELAY); } - void _unlockFS(void) { xSemaphoreGive(_mutex); } - - protected: - bool _mounted; - struct lfs_config *_lfs_cfg; - lfs_t _lfs; - SemaphoreHandle_t _mutex; - - private: - StaticSemaphore_t _MutexStorageSpace; -}; - -#if !CFG_DEBUG -#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, NULL) -#define PRINT_LFS_ERR(_err) -#else -#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, dbg_strerr_lfs) -#define PRINT_LFS_ERR(_err) \ - do { \ - if (_err) { \ - VERIFY_MESS((long int)_err, dbg_strerr_lfs); \ - } \ - } while (0) // LFS_ERR are of type int, VERIFY_MESS expects long_int - -const char *dbg_strerr_lfs(int32_t err); -#endif - -#endif /* LITTLEFS_H_ */ diff --git a/src/platform/stm32wl/LittleFS_File.cpp b/src/platform/stm32wl/LittleFS_File.cpp deleted file mode 100644 index 548a3d300..000000000 --- a/src/platform/stm32wl/LittleFS_File.cpp +++ /dev/null @@ -1,362 +0,0 @@ -#include - -#include "LittleFS.h" - -#include - -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM DECLARATION -//--------------------------------------------------------------------+ - -using namespace LittleFS_Namespace; - -File::File(LittleFS &fs) -{ - _fs = &fs; - _is_dir = false; - _name[0] = 0; - _dir_path = NULL; - - _dir = NULL; - _file = NULL; -} - -File::File(char const *filename, uint8_t mode, LittleFS &fs) : File(fs) -{ - // public constructor calls public API open(), which will obtain the mutex - this->open(filename, mode); -} - -bool File::_open_file(char const *filepath, uint8_t mode) -{ - int flags = (mode == FILE_O_READ) ? LFS_O_RDONLY : (mode == FILE_O_WRITE) ? (LFS_O_RDWR | LFS_O_CREAT) : 0; - - if (flags) { - _file = (lfs_file_t *)malloc(sizeof(lfs_file_t)); - if (!_file) - return false; - - int rc = lfs_file_open(_fs->_getFS(), _file, filepath, flags); - - if (rc) { - // failed to open - PRINT_LFS_ERR(rc); - return false; - } - - // move to end of file - if (mode == FILE_O_WRITE) - lfs_file_seek(_fs->_getFS(), _file, 0, LFS_SEEK_END); - - _is_dir = false; - } - - return true; -} - -bool File::_open_dir(char const *filepath) -{ - _dir = (lfs_dir_t *)malloc(sizeof(lfs_dir_t)); - if (!_dir) - return false; - - int rc = lfs_dir_open(_fs->_getFS(), _dir, filepath); - - if (rc) { - // failed to open - PRINT_LFS_ERR(rc); - return false; - } - - _is_dir = true; - - _dir_path = (char *)malloc(strlen(filepath) + 1); - strcpy(_dir_path, filepath); - - return true; -} - -bool File::open(char const *filepath, uint8_t mode) -{ - bool ret = false; - _fs->_lockFS(); - - ret = this->_open(filepath, mode); - - _fs->_unlockFS(); - return ret; -} - -bool File::_open(char const *filepath, uint8_t mode) -{ - bool ret = false; - - // close if currently opened - if (this->isOpen()) - _close(); - - struct lfs_info info; - int rc = lfs_stat(_fs->_getFS(), filepath, &info); - - if (LFS_ERR_OK == rc) { - // file existed, open file or directory accordingly - ret = (info.type == LFS_TYPE_REG) ? _open_file(filepath, mode) : _open_dir(filepath); - } else if (LFS_ERR_NOENT == rc) { - // file not existed, only proceed with FILE_O_WRITE mode - if (mode == FILE_O_WRITE) - ret = _open_file(filepath, mode); - } else { - PRINT_LFS_ERR(rc); - } - - // save bare file name - if (ret) { - char const *splash = strrchr(filepath, '/'); - strncpy(_name, splash ? (splash + 1) : filepath, LFS_NAME_MAX); - } - return ret; -} - -size_t File::write(uint8_t ch) -{ - return write(&ch, 1); -} - -size_t File::write(uint8_t const *buf, size_t size) -{ - lfs_ssize_t wrcount = 0; - _fs->_lockFS(); - - if (!this->_is_dir) { - wrcount = lfs_file_write(_fs->_getFS(), _file, buf, size); - if (wrcount < 0) { - wrcount = 0; - } - } - - _fs->_unlockFS(); - return wrcount; -} - -int File::read(void) -{ - // this thin wrapper relies on called function to synchronize - int ret = -1; - uint8_t ch; - if (read(&ch, 1) > 0) { - ret = static_cast(ch); - } - return ret; -} - -int File::read(void *buf, uint16_t nbyte) -{ - int ret = 0; - _fs->_lockFS(); - - if (!this->_is_dir) { - ret = lfs_file_read(_fs->_getFS(), _file, buf, nbyte); - } - - _fs->_unlockFS(); - return ret; -} - -int File::peek(void) -{ - int ret = -1; - _fs->_lockFS(); - - if (!this->_is_dir) { - uint32_t pos = lfs_file_tell(_fs->_getFS(), _file); - uint8_t ch = 0; - if (lfs_file_read(_fs->_getFS(), _file, &ch, 1) > 0) { - ret = static_cast(ch); - } - (void)lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET); - } - - _fs->_unlockFS(); - return ret; -} - -int File::available(void) -{ - int ret = 0; - _fs->_lockFS(); - - if (!this->_is_dir) { - uint32_t fsize = lfs_file_size(_fs->_getFS(), _file); - uint32_t pos = lfs_file_tell(_fs->_getFS(), _file); - ret = fsize - pos; - } - - _fs->_unlockFS(); - return ret; -} - -bool File::seek(uint32_t pos) -{ - bool ret = false; - _fs->_lockFS(); - - if (!this->_is_dir) { - ret = lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET) >= 0; - } - - _fs->_unlockFS(); - return ret; -} - -uint32_t File::position(void) -{ - uint32_t ret = 0; - _fs->_lockFS(); - - if (!this->_is_dir) { - ret = lfs_file_tell(_fs->_getFS(), _file); - } - - _fs->_unlockFS(); - return ret; -} - -uint32_t File::size(void) -{ - uint32_t ret = 0; - _fs->_lockFS(); - - if (!this->_is_dir) { - ret = lfs_file_size(_fs->_getFS(), _file); - } - - _fs->_unlockFS(); - return ret; -} - -bool File::truncate(uint32_t pos) -{ - int32_t ret = LFS_ERR_ISDIR; - _fs->_lockFS(); - if (!this->_is_dir) { - ret = lfs_file_truncate(_fs->_getFS(), _file, pos); - } - _fs->_unlockFS(); - return (ret == 0); -} - -bool File::truncate(void) -{ - int32_t ret = LFS_ERR_ISDIR; - _fs->_lockFS(); - if (!this->_is_dir) { - uint32_t pos = lfs_file_tell(_fs->_getFS(), _file); - ret = lfs_file_truncate(_fs->_getFS(), _file, pos); - } - _fs->_unlockFS(); - return (ret == 0); -} - -void File::flush(void) -{ - _fs->_lockFS(); - - if (!this->_is_dir) { - lfs_file_sync(_fs->_getFS(), _file); - } - - _fs->_unlockFS(); - return; -} - -void File::close(void) -{ - _fs->_lockFS(); - this->_close(); - _fs->_unlockFS(); -} - -void File::_close(void) -{ - if (this->isOpen()) { - if (this->_is_dir) { - lfs_dir_close(_fs->_getFS(), _dir); - free(_dir); - _dir = NULL; - - if (this->_dir_path) - free(_dir_path); - _dir_path = NULL; - } else { - lfs_file_close(this->_fs->_getFS(), _file); - free(_file); - _file = NULL; - } - } -} - -File::operator bool(void) -{ - return isOpen(); -} - -bool File::isOpen(void) -{ - return (_file != NULL) || (_dir != NULL); -} - -// WARNING -- although marked as `const`, the values pointed -// to may change. For example, if the same File -// object has `open()` called with a different -// file or directory name, this same pointer will -// suddenly (unexpectedly?) have different values. -char const *File::name(void) -{ - return this->_name; -} - -bool File::isDirectory(void) -{ - return this->_is_dir; -} - -File File::openNextFile(uint8_t mode) -{ - _fs->_lockFS(); - - File ret(*_fs); - if (this->_is_dir) { - struct lfs_info info; - int rc; - - // lfs_dir_read returns 0 when reaching end of directory, 1 if found an entry - // Skip the "." and ".." entries ... - do { - rc = lfs_dir_read(_fs->_getFS(), _dir, &info); - } while (rc == 1 && (!strcmp(".", info.name) || !strcmp("..", info.name))); - - if (rc == 1) { - // string cat name with current folder - char filepath[strlen(_dir_path) + 1 + strlen(info.name) + 1]; // potential for significant stack usage - strcpy(filepath, _dir_path); - if (!(_dir_path[0] == '/' && _dir_path[1] == 0)) - strcat(filepath, "/"); // only add '/' if cwd is not root - strcat(filepath, info.name); - - (void)ret._open(filepath, mode); // return value is ignored ... caller is expected to check isOpened() - } else if (rc < 0) { - PRINT_LFS_ERR(rc); - } - } - _fs->_unlockFS(); - return ret; -} - -void File::rewindDirectory(void) -{ - _fs->_lockFS(); - if (this->_is_dir) { - lfs_dir_rewind(_fs->_getFS(), _dir); - } - _fs->_unlockFS(); -} diff --git a/src/platform/stm32wl/LittleFS_File.h b/src/platform/stm32wl/LittleFS_File.h deleted file mode 100644 index e88a2790d..000000000 --- a/src/platform/stm32wl/LittleFS_File.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef LITTLEFS_FILE_H_ -#define LITTLEFS_FILE_H_ - -// Forward declaration -class LittleFS; - -namespace LittleFS_Namespace -{ - -// avoid conflict with other FileSystem FILE_READ/FILE_WRITE -enum { - FILE_O_READ = 0, - FILE_O_WRITE = 1, -}; - -class File : public Stream -{ - public: - explicit File(LittleFS &fs); - File(char const *filename, uint8_t mode, LittleFS &fs); - - public: - bool open(char const *filename, uint8_t mode); - - //------------- Stream API -------------// - virtual size_t write(uint8_t ch); - virtual size_t write(uint8_t const *buf, size_t size); - size_t write(const char *str) - { - if (str == NULL) - return 0; - return write((const uint8_t *)str, strlen(str)); - } - size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); } - - virtual int read(void); - int read(void *buf, uint16_t nbyte); - - virtual int peek(void); - virtual int available(void); - virtual void flush(void); - - bool seek(uint32_t pos); - uint32_t position(void); - uint32_t size(void); - - bool truncate(uint32_t pos); - bool truncate(void); - - void close(void); - - operator bool(void); - - bool isOpen(void); - char const *name(void); - - bool isDirectory(void); - File openNextFile(uint8_t mode = FILE_O_READ); - void rewindDirectory(void); - - private: - LittleFS *_fs; - - bool _is_dir; - - union { - lfs_file_t *_file; - lfs_dir_t *_dir; - }; - - char *_dir_path; - char _name[LFS_NAME_MAX + 1]; - - bool _open(char const *filepath, uint8_t mode); - bool _open_file(char const *filepath, uint8_t mode); - bool _open_dir(char const *filepath); - void _close(void); -}; - -} // namespace LittleFS_Namespace - -#endif /* LITTLEFS_FILE_H_ */ diff --git a/src/platform/stm32wl/STM32WLCryptoEngine.cpp b/src/platform/stm32wl/STM32WLCryptoEngine.cpp index 7367a2bc0..4debdf78e 100644 --- a/src/platform/stm32wl/STM32WLCryptoEngine.cpp +++ b/src/platform/stm32wl/STM32WLCryptoEngine.cpp @@ -1,33 +1,64 @@ +#undef RNG +#include "AES.h" +#include "CTR.h" #include "CryptoEngine.h" -#include "aes.hpp" #include "configuration.h" class STM32WLCryptoEngine : public CryptoEngine { + + CTRCommon *ctr = NULL; + public: STM32WLCryptoEngine() {} ~STM32WLCryptoEngine() {} + virtual void setKey(const CryptoKey &k) override + { + CryptoEngine::setKey(k); + LOG_DEBUG("Installing AES%d key!\n", key.length * 8); + if (ctr) { + delete ctr; + ctr = NULL; + } + if (key.length != 0) { + if (key.length == 16) + ctr = new CTR(); + else + ctr = new CTR(); + + ctr->setKey(key.bytes, key.length); + } + } /** * Encrypt a packet * * @param bytes is updated in place */ - virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override + virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override { if (key.length > 0) { - AES_ctx ctx; - initNonce(fromNode, packetNum); - AES_init_ctx_iv(&ctx, key.bytes, nonce); - AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes); + initNonce(fromNode, packetId); + if (numBytes <= MAX_BLOCKSIZE) { + static uint8_t scratch[MAX_BLOCKSIZE]; + memcpy(scratch, bytes, numBytes); + memset(scratch + numBytes, 0, + sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) + + ctr->setIV(nonce, sizeof(nonce)); + ctr->setCounterSize(4); + ctr->encrypt(bytes, scratch, numBytes); + } else { + LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); + } } } - virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override + virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override { // For CTR, the implementation is the same - encrypt(fromNode, packetNum, numBytes, bytes); + encrypt(fromNode, packetId, numBytes, bytes); } private: diff --git a/src/platform/stm32wl/main-stm32wl.cpp b/src/platform/stm32wl/main-stm32wl.cpp index 60c3cce10..3eddbb3cf 100644 --- a/src/platform/stm32wl/main-stm32wl.cpp +++ b/src/platform/stm32wl/main-stm32wl.cpp @@ -26,11 +26,3 @@ void getMacAddr(uint8_t *dmac) } void cpuDeepSleep(uint32_t msecToWake) {} - -/* pacify libc_nano */ -extern "C" { -int _gettimeofday(struct timeval *tv, void *tzvp) -{ - return -1; -} -} \ No newline at end of file diff --git a/src/xmodem.cpp b/src/xmodem.cpp index 852ff3453..de73e8668 100644 --- a/src/xmodem.cpp +++ b/src/xmodem.cpp @@ -50,6 +50,8 @@ #include "xmodem.h" +#ifdef FSCom + XModemAdapter xModem; XModemAdapter::XModemAdapter() {} @@ -248,4 +250,5 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) // Unknown control character break; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/xmodem.h b/src/xmodem.h index 2ba0bb39f..4cfcb43e1 100644 --- a/src/xmodem.h +++ b/src/xmodem.h @@ -38,6 +38,8 @@ #define MAXRETRANS 25 +#ifdef FSCom + class XModemAdapter { public: @@ -75,3 +77,4 @@ class XModemAdapter }; extern XModemAdapter xModem; +#endif // FSCom \ No newline at end of file diff --git a/variants/rak3172/platformio.ini b/variants/rak3172/platformio.ini new file mode 100644 index 000000000..63766c988 --- /dev/null +++ b/variants/rak3172/platformio.ini @@ -0,0 +1,12 @@ +[env:rak3172] +extends = stm32_base +board_level = extra +board = wiscore_rak3172 +build_flags = + ${stm32_base.build_flags} + -Ivariants/rak3172 + -DHAL_DAC_MODULE_ONLY + -DSERIAL_UART_INSTANCE=1 + -DPIN_SERIAL_RX=PB7 + -DPIN_SERIAL_TX=PB6 +upload_port = stlink \ No newline at end of file diff --git a/variants/rak3172/variant.h b/variants/rak3172/variant.h new file mode 100644 index 000000000..21de65b2c --- /dev/null +++ b/variants/rak3172/variant.h @@ -0,0 +1,12 @@ +/* +This variant is a work in progress. +Do not expect a working Meshtastic device with this target. +*/ + +#ifndef _VARIANT_RAK3172_ +#define _VARIANT_RAK3172_ + +#define USE_STM32WLx +#define MAX_NUM_NODES 10 + +#endif \ No newline at end of file diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini index 07f6efa6d..12cd6190d 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5/platformio.ini @@ -1,11 +1,34 @@ [env:wio-e5] -extends = stm32wl5e_base +extends = stm32_base board_level = extra +board = lora_e5_dev_board build_flags = - ${stm32wl5e_base.build_flags} + ${stm32_base.build_flags} -Ivariants/wio-e5 - -DHAL_DAC_MODULE_ONLY -DSERIAL_UART_INSTANCE=1 -DPIN_SERIAL_RX=PB7 -DPIN_SERIAL_TX=PB6 + -DHAL_DAC_MODULE_ONLY + -DHAL_ADC_MODULE_DISABLED + -DHAL_COMP_MODULE_DISABLED + -DHAL_CRC_MODULE_DISABLED + -DHAL_CRYP_MODULE_DISABLED + -DHAL_GTZC_MODULE_DISABLED + -DHAL_HSEM_MODULE_DISABLED + -DHAL_I2C_MODULE_DISABLED + -DHAL_I2S_MODULE_DISABLED + -DHAL_IPCC_MODULE_DISABLED + -DHAL_IRDA_MODULE_DISABLED + -DHAL_IWDG_MODULE_DISABLED + -DHAL_LPTIM_MODULE_DISABLED + -DHAL_PKA_MODULE_DISABLED + -DHAL_RNG_MODULE_DISABLED + -DHAL_RTC_MODULE_DISABLED + -DHAL_SMARTCARD_MODULE_DISABLED + -DHAL_SMBUS_MODULE_DISABLED + -DHAL_TIM_MODULE_DISABLED + -DHAL_WWDG_MODULE_DISABLED + -DHAL_EXTI_MODULE_DISABLED +; -D PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF + upload_port = stlink \ No newline at end of file diff --git a/variants/wio-e5/variant.h b/variants/wio-e5/variant.h index 86e58bcb2..b4345a530 100644 --- a/variants/wio-e5/variant.h +++ b/variants/wio-e5/variant.h @@ -13,5 +13,6 @@ Do not expect a working Meshtastic device with this target. #define _VARIANT_WIOE5_ #define USE_STM32WLx +#define MAX_NUM_NODES 10 #endif \ No newline at end of file From f645ae943dd1411fefe8c48ae83d01ad6a4072c5 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 25 Jul 2024 20:50:42 -0500 Subject: [PATCH 007/305] JSON serialization refactor (#4331) --- src/mesh/http/ContentHandler.cpp | 2 +- src/mqtt/MQTT.cpp | 304 +------------------- src/mqtt/MQTT.h | 5 +- src/{mqtt => serialization}/JSON.cpp | 0 src/{mqtt => serialization}/JSON.h | 0 src/{mqtt => serialization}/JSONValue.cpp | 0 src/{mqtt => serialization}/JSONValue.h | 0 src/serialization/MeshPacketSerializer.cpp | 317 +++++++++++++++++++++ src/serialization/MeshPacketSerializer.h | 8 + 9 files changed, 331 insertions(+), 305 deletions(-) rename src/{mqtt => serialization}/JSON.cpp (100%) rename src/{mqtt => serialization}/JSON.h (100%) rename src/{mqtt => serialization}/JSONValue.cpp (100%) rename src/{mqtt => serialization}/JSONValue.h (100%) create mode 100644 src/serialization/MeshPacketSerializer.cpp create mode 100644 src/serialization/MeshPacketSerializer.h diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index b309484e2..ca2c5d4be 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -9,8 +9,8 @@ #if HAS_WIFI #include "mesh/wifi/WiFiAPClient.h" #endif -#include "mqtt/JSON.h" #include "power.h" +#include "serialization/JSON.h" #include "sleep.h" #include #include diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index a7085dffe..2fce526a0 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -19,6 +19,8 @@ #include #endif #include "Default.h" +#include "serialization/JSON.h" +#include "serialization/MeshPacketSerializer.h" #include const int reconnectMax = 5; @@ -459,7 +461,7 @@ void MQTT::publishQueuedMessages() #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 if (moduleConfig.mqtt.json_enabled) { // handle json topic - auto jsonString = this->meshPacketToJson(env->packet); + auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); if (jsonString.length() != 0) { std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id; LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), @@ -520,7 +522,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 if (moduleConfig.mqtt.json_enabled) { // handle json topic - auto jsonString = this->meshPacketToJson((meshtastic_MeshPacket *)&mp_decoded); + auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); if (jsonString.length() != 0) { std::string topicJson = jsonTopic + channelId + "/" + owner.id; LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), @@ -621,304 +623,6 @@ void MQTT::perhapsReportToMap() } } -// converts a downstream packet into a json message -std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp) -{ - // the created jsonObj is immutable after creation, so - // we need to do the heavy lifting before assembling it. - std::string msgType; - JSONObject jsonObj; - - if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { - JSONObject msgPayload; - switch (mp->decoded.portnum) { - case meshtastic_PortNum_TEXT_MESSAGE_APP: { - msgType = "text"; - // convert bytes to string - LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); - char payloadStr[(mp->decoded.payload.size) + 1]; - memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); - payloadStr[mp->decoded.payload.size] = 0; // null terminated string - // check if this is a JSON payload - JSONValue *json_value = JSON::Parse(payloadStr); - if (json_value != NULL) { - LOG_INFO("text message payload is of type json\n"); - // if it is, then we can just use the json object - jsonObj["payload"] = json_value; - } else { - // if it isn't, then we need to create a json object - // with the string as the value - LOG_INFO("text message payload is of type plaintext\n"); - msgPayload["text"] = new JSONValue(payloadStr); - jsonObj["payload"] = new JSONValue(msgPayload); - } - break; - } - case meshtastic_PortNum_TELEMETRY_APP: { - msgType = "telemetry"; - meshtastic_Telemetry scratch; - meshtastic_Telemetry *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) { - decoded = &scratch; - if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) { - msgPayload["battery_level"] = new JSONValue((unsigned int)decoded->variant.device_metrics.battery_level); - msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage); - msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization); - msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx); - msgPayload["uptime_seconds"] = new JSONValue((unsigned int)decoded->variant.device_metrics.uptime_seconds); - } else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) { - msgPayload["temperature"] = new JSONValue(decoded->variant.environment_metrics.temperature); - msgPayload["relative_humidity"] = new JSONValue(decoded->variant.environment_metrics.relative_humidity); - msgPayload["barometric_pressure"] = new JSONValue(decoded->variant.environment_metrics.barometric_pressure); - msgPayload["gas_resistance"] = new JSONValue(decoded->variant.environment_metrics.gas_resistance); - msgPayload["voltage"] = new JSONValue(decoded->variant.environment_metrics.voltage); - msgPayload["current"] = new JSONValue(decoded->variant.environment_metrics.current); - msgPayload["lux"] = new JSONValue(decoded->variant.environment_metrics.lux); - msgPayload["white_lux"] = new JSONValue(decoded->variant.environment_metrics.white_lux); - msgPayload["iaq"] = new JSONValue((uint)decoded->variant.environment_metrics.iaq); - msgPayload["wind_speed"] = new JSONValue(decoded->variant.environment_metrics.wind_speed); - msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction); - msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust); - msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull); - } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { - msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage); - msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current); - msgPayload["voltage_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_voltage); - msgPayload["current_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_current); - msgPayload["voltage_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_voltage); - msgPayload["current_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_current); - } - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for telemetry message!\n"); - } - break; - } - case meshtastic_PortNum_NODEINFO_APP: { - msgType = "nodeinfo"; - meshtastic_User scratch; - meshtastic_User *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) { - decoded = &scratch; - msgPayload["id"] = new JSONValue(decoded->id); - msgPayload["longname"] = new JSONValue(decoded->long_name); - msgPayload["shortname"] = new JSONValue(decoded->short_name); - msgPayload["hardware"] = new JSONValue(decoded->hw_model); - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); - } - break; - } - case meshtastic_PortNum_POSITION_APP: { - msgType = "position"; - meshtastic_Position scratch; - meshtastic_Position *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) { - decoded = &scratch; - if ((int)decoded->time) { - msgPayload["time"] = new JSONValue((unsigned int)decoded->time); - } - if ((int)decoded->timestamp) { - msgPayload["timestamp"] = new JSONValue((unsigned int)decoded->timestamp); - } - msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i); - msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i); - if ((int)decoded->altitude) { - msgPayload["altitude"] = new JSONValue((int)decoded->altitude); - } - if ((int)decoded->ground_speed) { - msgPayload["ground_speed"] = new JSONValue((unsigned int)decoded->ground_speed); - } - if (int(decoded->ground_track)) { - msgPayload["ground_track"] = new JSONValue((unsigned int)decoded->ground_track); - } - if (int(decoded->sats_in_view)) { - msgPayload["sats_in_view"] = new JSONValue((unsigned int)decoded->sats_in_view); - } - if ((int)decoded->PDOP) { - msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP); - } - if ((int)decoded->HDOP) { - msgPayload["HDOP"] = new JSONValue((int)decoded->HDOP); - } - if ((int)decoded->VDOP) { - msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP); - } - if ((int)decoded->precision_bits) { - msgPayload["precision_bits"] = new JSONValue((int)decoded->precision_bits); - } - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for position message!\n"); - } - break; - } - case meshtastic_PortNum_WAYPOINT_APP: { - msgType = "position"; - meshtastic_Waypoint scratch; - meshtastic_Waypoint *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) { - decoded = &scratch; - msgPayload["id"] = new JSONValue((unsigned int)decoded->id); - msgPayload["name"] = new JSONValue(decoded->name); - msgPayload["description"] = new JSONValue(decoded->description); - msgPayload["expire"] = new JSONValue((unsigned int)decoded->expire); - msgPayload["locked_to"] = new JSONValue((unsigned int)decoded->locked_to); - msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i); - msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i); - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for position message!\n"); - } - break; - } - case meshtastic_PortNum_NEIGHBORINFO_APP: { - msgType = "neighborinfo"; - meshtastic_NeighborInfo scratch; - meshtastic_NeighborInfo *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg, - &scratch)) { - decoded = &scratch; - msgPayload["node_id"] = new JSONValue((unsigned int)decoded->node_id); - msgPayload["node_broadcast_interval_secs"] = new JSONValue((unsigned int)decoded->node_broadcast_interval_secs); - msgPayload["last_sent_by_id"] = new JSONValue((unsigned int)decoded->last_sent_by_id); - msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count); - JSONArray neighbors; - for (uint8_t i = 0; i < decoded->neighbors_count; i++) { - JSONObject neighborObj; - neighborObj["node_id"] = new JSONValue((unsigned int)decoded->neighbors[i].node_id); - neighborObj["snr"] = new JSONValue((int)decoded->neighbors[i].snr); - neighbors.push_back(new JSONValue(neighborObj)); - } - msgPayload["neighbors"] = new JSONValue(neighbors); - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); - } - break; - } - case meshtastic_PortNum_TRACEROUTE_APP: { - if (mp->decoded.request_id) { // Only report the traceroute response - msgType = "traceroute"; - meshtastic_RouteDiscovery scratch; - meshtastic_RouteDiscovery *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg, - &scratch)) { - decoded = &scratch; - JSONArray route; // Route this message took - // Lambda function for adding a long name to the route - auto addToRoute = [](JSONArray *route, NodeNum num) { - char long_name[40] = "Unknown"; - meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num); - bool name_known = node ? node->has_user : false; - if (name_known) - memcpy(long_name, node->user.long_name, sizeof(long_name)); - route->push_back(new JSONValue(long_name)); - }; - addToRoute(&route, mp->to); // Started at the original transmitter (destination of response) - for (uint8_t i = 0; i < decoded->route_count; i++) { - addToRoute(&route, decoded->route[i]); - } - addToRoute(&route, mp->from); // Ended at the original destination (source of response) - - msgPayload["route"] = new JSONValue(route); - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for traceroute message!\n"); - } - } - break; - } - case meshtastic_PortNum_DETECTION_SENSOR_APP: { - msgType = "detection"; - char payloadStr[(mp->decoded.payload.size) + 1]; - memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); - payloadStr[mp->decoded.payload.size] = 0; // null terminated string - msgPayload["text"] = new JSONValue(payloadStr); - jsonObj["payload"] = new JSONValue(msgPayload); - break; - } -#ifdef ARCH_ESP32 - case meshtastic_PortNum_PAXCOUNTER_APP: { - msgType = "paxcounter"; - meshtastic_Paxcount scratch; - meshtastic_Paxcount *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Paxcount_msg, &scratch)) { - decoded = &scratch; - msgPayload["wifi_count"] = new JSONValue((unsigned int)decoded->wifi); - msgPayload["ble_count"] = new JSONValue((unsigned int)decoded->ble); - msgPayload["uptime"] = new JSONValue((unsigned int)decoded->uptime); - jsonObj["payload"] = new JSONValue(msgPayload); - } else { - LOG_ERROR("Error decoding protobuf for Paxcount message!\n"); - } - break; - } -#endif - case meshtastic_PortNum_REMOTE_HARDWARE_APP: { - meshtastic_HardwareMessage scratch; - meshtastic_HardwareMessage *decoded = NULL; - memset(&scratch, 0, sizeof(scratch)); - if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg, - &scratch)) { - decoded = &scratch; - if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) { - msgType = "gpios_changed"; - msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value); - jsonObj["payload"] = new JSONValue(msgPayload); - } else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) { - msgType = "gpios_read_reply"; - msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value); - msgPayload["gpio_mask"] = new JSONValue((unsigned int)decoded->gpio_mask); - jsonObj["payload"] = new JSONValue(msgPayload); - } - } else { - LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n"); - } - break; - } - // add more packet types here if needed - default: - break; - } - } else { - LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n"); - } - - jsonObj["id"] = new JSONValue((unsigned int)mp->id); - jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time); - jsonObj["to"] = new JSONValue((unsigned int)mp->to); - jsonObj["from"] = new JSONValue((unsigned int)mp->from); - jsonObj["channel"] = new JSONValue((unsigned int)mp->channel); - jsonObj["type"] = new JSONValue(msgType.c_str()); - jsonObj["sender"] = new JSONValue(owner.id); - if (mp->rx_rssi != 0) - jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi); - if (mp->rx_snr != 0) - jsonObj["snr"] = new JSONValue((float)mp->rx_snr); - if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { - jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit)); - jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start)); - } - - // serialize and write it to the stream - JSONValue *value = new JSONValue(jsonObj); - std::string jsonStr = value->Stringify(); - - LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); - - delete value; - return jsonStr; -} - bool MQTT::isValidJsonEnvelope(JSONObject &json) { // if "sender" is provided, avoid processing packets we uplinked diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index d68f1b88d..ba0987783 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -5,7 +5,7 @@ #include "concurrency/OSThread.h" #include "mesh/Channels.h" #include "mesh/generated/meshtastic/mqtt.pb.h" -#include "mqtt/JSON.h" +#include "serialization/JSON.h" #if HAS_WIFI #include #if !defined(ARCH_PORTDUINO) @@ -106,9 +106,6 @@ class MQTT : private concurrency::OSThread /// Called when a new publish arrives from the MQTT server void onReceive(char *topic, byte *payload, size_t length); - /// Called when a new publish arrives from the MQTT server - std::string meshPacketToJson(meshtastic_MeshPacket *mp); - void publishQueuedMessages(); void publishNodeInfo(); diff --git a/src/mqtt/JSON.cpp b/src/serialization/JSON.cpp similarity index 100% rename from src/mqtt/JSON.cpp rename to src/serialization/JSON.cpp diff --git a/src/mqtt/JSON.h b/src/serialization/JSON.h similarity index 100% rename from src/mqtt/JSON.h rename to src/serialization/JSON.h diff --git a/src/mqtt/JSONValue.cpp b/src/serialization/JSONValue.cpp similarity index 100% rename from src/mqtt/JSONValue.cpp rename to src/serialization/JSONValue.cpp diff --git a/src/mqtt/JSONValue.h b/src/serialization/JSONValue.h similarity index 100% rename from src/mqtt/JSONValue.h rename to src/serialization/JSONValue.h diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp new file mode 100644 index 000000000..dc2db1e6f --- /dev/null +++ b/src/serialization/MeshPacketSerializer.cpp @@ -0,0 +1,317 @@ +#include "MeshPacketSerializer.h" +#include "JSON.h" +#include "NodeDB.h" +#include "mesh/generated/meshtastic/mqtt.pb.h" +#include "mesh/generated/meshtastic/telemetry.pb.h" +#include "modules/RoutingModule.h" +#include +#include +#if defined(ARCH_ESP32) +#include "../mesh/generated/meshtastic/paxcount.pb.h" +#endif +#include "mesh/generated/meshtastic/remote_hardware.pb.h" + +std::string MeshPacketSerializer::JsonSerialize(meshtastic_MeshPacket *mp, bool shouldLog) +{ + // the created jsonObj is immutable after creation, so + // we need to do the heavy lifting before assembling it. + std::string msgType; + JSONObject jsonObj; + + if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + JSONObject msgPayload; + switch (mp->decoded.portnum) { + case meshtastic_PortNum_TEXT_MESSAGE_APP: { + msgType = "text"; + // convert bytes to string + if (shouldLog) + LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); + + char payloadStr[(mp->decoded.payload.size) + 1]; + memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); + payloadStr[mp->decoded.payload.size] = 0; // null terminated string + // check if this is a JSON payload + JSONValue *json_value = JSON::Parse(payloadStr); + if (json_value != NULL) { + if (shouldLog) + LOG_INFO("text message payload is of type json\n"); + + // if it is, then we can just use the json object + jsonObj["payload"] = json_value; + } else { + // if it isn't, then we need to create a json object + // with the string as the value + if (shouldLog) + LOG_INFO("text message payload is of type plaintext\n"); + + msgPayload["text"] = new JSONValue(payloadStr); + jsonObj["payload"] = new JSONValue(msgPayload); + } + break; + } + case meshtastic_PortNum_TELEMETRY_APP: { + msgType = "telemetry"; + meshtastic_Telemetry scratch; + meshtastic_Telemetry *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) { + decoded = &scratch; + if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) { + msgPayload["battery_level"] = new JSONValue((unsigned int)decoded->variant.device_metrics.battery_level); + msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage); + msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization); + msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx); + msgPayload["uptime_seconds"] = new JSONValue((unsigned int)decoded->variant.device_metrics.uptime_seconds); + } else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) { + msgPayload["temperature"] = new JSONValue(decoded->variant.environment_metrics.temperature); + msgPayload["relative_humidity"] = new JSONValue(decoded->variant.environment_metrics.relative_humidity); + msgPayload["barometric_pressure"] = new JSONValue(decoded->variant.environment_metrics.barometric_pressure); + msgPayload["gas_resistance"] = new JSONValue(decoded->variant.environment_metrics.gas_resistance); + msgPayload["voltage"] = new JSONValue(decoded->variant.environment_metrics.voltage); + msgPayload["current"] = new JSONValue(decoded->variant.environment_metrics.current); + msgPayload["lux"] = new JSONValue(decoded->variant.environment_metrics.lux); + msgPayload["white_lux"] = new JSONValue(decoded->variant.environment_metrics.white_lux); + msgPayload["iaq"] = new JSONValue((uint)decoded->variant.environment_metrics.iaq); + msgPayload["wind_speed"] = new JSONValue(decoded->variant.environment_metrics.wind_speed); + msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction); + msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust); + msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull); + } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { + msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage); + msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current); + msgPayload["voltage_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_voltage); + msgPayload["current_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_current); + msgPayload["voltage_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_voltage); + msgPayload["current_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_current); + } + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for telemetry message!\n"); + } + break; + } + case meshtastic_PortNum_NODEINFO_APP: { + msgType = "nodeinfo"; + meshtastic_User scratch; + meshtastic_User *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) { + decoded = &scratch; + msgPayload["id"] = new JSONValue(decoded->id); + msgPayload["longname"] = new JSONValue(decoded->long_name); + msgPayload["shortname"] = new JSONValue(decoded->short_name); + msgPayload["hardware"] = new JSONValue(decoded->hw_model); + msgPayload["role"] = new JSONValue((int)decoded->role); + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); + } + break; + } + case meshtastic_PortNum_POSITION_APP: { + msgType = "position"; + meshtastic_Position scratch; + meshtastic_Position *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) { + decoded = &scratch; + if ((int)decoded->time) { + msgPayload["time"] = new JSONValue((unsigned int)decoded->time); + } + if ((int)decoded->timestamp) { + msgPayload["timestamp"] = new JSONValue((unsigned int)decoded->timestamp); + } + msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i); + msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i); + if ((int)decoded->altitude) { + msgPayload["altitude"] = new JSONValue((int)decoded->altitude); + } + if ((int)decoded->ground_speed) { + msgPayload["ground_speed"] = new JSONValue((unsigned int)decoded->ground_speed); + } + if (int(decoded->ground_track)) { + msgPayload["ground_track"] = new JSONValue((unsigned int)decoded->ground_track); + } + if (int(decoded->sats_in_view)) { + msgPayload["sats_in_view"] = new JSONValue((unsigned int)decoded->sats_in_view); + } + if ((int)decoded->PDOP) { + msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP); + } + if ((int)decoded->HDOP) { + msgPayload["HDOP"] = new JSONValue((int)decoded->HDOP); + } + if ((int)decoded->VDOP) { + msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP); + } + if ((int)decoded->precision_bits) { + msgPayload["precision_bits"] = new JSONValue((int)decoded->precision_bits); + } + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for position message!\n"); + } + break; + } + case meshtastic_PortNum_WAYPOINT_APP: { + msgType = "position"; + meshtastic_Waypoint scratch; + meshtastic_Waypoint *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) { + decoded = &scratch; + msgPayload["id"] = new JSONValue((unsigned int)decoded->id); + msgPayload["name"] = new JSONValue(decoded->name); + msgPayload["description"] = new JSONValue(decoded->description); + msgPayload["expire"] = new JSONValue((unsigned int)decoded->expire); + msgPayload["locked_to"] = new JSONValue((unsigned int)decoded->locked_to); + msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i); + msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i); + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for position message!\n"); + } + break; + } + case meshtastic_PortNum_NEIGHBORINFO_APP: { + msgType = "neighborinfo"; + meshtastic_NeighborInfo scratch; + meshtastic_NeighborInfo *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg, + &scratch)) { + decoded = &scratch; + msgPayload["node_id"] = new JSONValue((unsigned int)decoded->node_id); + msgPayload["node_broadcast_interval_secs"] = new JSONValue((unsigned int)decoded->node_broadcast_interval_secs); + msgPayload["last_sent_by_id"] = new JSONValue((unsigned int)decoded->last_sent_by_id); + msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count); + JSONArray neighbors; + for (uint8_t i = 0; i < decoded->neighbors_count; i++) { + JSONObject neighborObj; + neighborObj["node_id"] = new JSONValue((unsigned int)decoded->neighbors[i].node_id); + neighborObj["snr"] = new JSONValue((int)decoded->neighbors[i].snr); + neighbors.push_back(new JSONValue(neighborObj)); + } + msgPayload["neighbors"] = new JSONValue(neighbors); + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); + } + break; + } + case meshtastic_PortNum_TRACEROUTE_APP: { + if (mp->decoded.request_id) { // Only report the traceroute response + msgType = "traceroute"; + meshtastic_RouteDiscovery scratch; + meshtastic_RouteDiscovery *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg, + &scratch)) { + decoded = &scratch; + JSONArray route; // Route this message took + // Lambda function for adding a long name to the route + auto addToRoute = [](JSONArray *route, NodeNum num) { + char long_name[40] = "Unknown"; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num); + bool name_known = node ? node->has_user : false; + if (name_known) + memcpy(long_name, node->user.long_name, sizeof(long_name)); + route->push_back(new JSONValue(long_name)); + }; + addToRoute(&route, mp->to); // Started at the original transmitter (destination of response) + for (uint8_t i = 0; i < decoded->route_count; i++) { + addToRoute(&route, decoded->route[i]); + } + addToRoute(&route, mp->from); // Ended at the original destination (source of response) + + msgPayload["route"] = new JSONValue(route); + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for traceroute message!\n"); + } + } + break; + } + case meshtastic_PortNum_DETECTION_SENSOR_APP: { + msgType = "detection"; + char payloadStr[(mp->decoded.payload.size) + 1]; + memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); + payloadStr[mp->decoded.payload.size] = 0; // null terminated string + msgPayload["text"] = new JSONValue(payloadStr); + jsonObj["payload"] = new JSONValue(msgPayload); + break; + } +#ifdef ARCH_ESP32 + case meshtastic_PortNum_PAXCOUNTER_APP: { + msgType = "paxcounter"; + meshtastic_Paxcount scratch; + meshtastic_Paxcount *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Paxcount_msg, &scratch)) { + decoded = &scratch; + msgPayload["wifi_count"] = new JSONValue((unsigned int)decoded->wifi); + msgPayload["ble_count"] = new JSONValue((unsigned int)decoded->ble); + msgPayload["uptime"] = new JSONValue((unsigned int)decoded->uptime); + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for Paxcount message!\n"); + } + break; + } +#endif + case meshtastic_PortNum_REMOTE_HARDWARE_APP: { + meshtastic_HardwareMessage scratch; + meshtastic_HardwareMessage *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg, + &scratch)) { + decoded = &scratch; + if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) { + msgType = "gpios_changed"; + msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value); + jsonObj["payload"] = new JSONValue(msgPayload); + } else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) { + msgType = "gpios_read_reply"; + msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value); + msgPayload["gpio_mask"] = new JSONValue((unsigned int)decoded->gpio_mask); + jsonObj["payload"] = new JSONValue(msgPayload); + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n"); + } + break; + } + // add more packet types here if needed + default: + break; + } + } else if (shouldLog) { + LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n"); + } + + jsonObj["id"] = new JSONValue((unsigned int)mp->id); + jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time); + jsonObj["to"] = new JSONValue((unsigned int)mp->to); + jsonObj["from"] = new JSONValue((unsigned int)mp->from); + jsonObj["channel"] = new JSONValue((unsigned int)mp->channel); + jsonObj["type"] = new JSONValue(msgType.c_str()); + jsonObj["sender"] = new JSONValue(owner.id); + if (mp->rx_rssi != 0) + jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi); + if (mp->rx_snr != 0) + jsonObj["snr"] = new JSONValue((float)mp->rx_snr); + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { + jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit)); + jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start)); + } + + // serialize and write it to the stream + JSONValue *value = new JSONValue(jsonObj); + std::string jsonStr = value->Stringify(); + + if (shouldLog) + LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); + + delete value; + return jsonStr; +} \ No newline at end of file diff --git a/src/serialization/MeshPacketSerializer.h b/src/serialization/MeshPacketSerializer.h new file mode 100644 index 000000000..579ee2fd6 --- /dev/null +++ b/src/serialization/MeshPacketSerializer.h @@ -0,0 +1,8 @@ +#include +#include + +class MeshPacketSerializer +{ + public: + static std::string JsonSerialize(meshtastic_MeshPacket *mp, bool shouldLog = true); +}; \ No newline at end of file From 755952c261322a97c521c81fc2966f5b7fe0acfa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 06:29:05 -0500 Subject: [PATCH 008/305] [create-pull-request] automated change (#4333) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 7f90178f1..b1a79d5db 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 7f90178f183820e288aec41133144f30723228fe +Subproject commit b1a79d5db00f6aeeb0bbae156e8939e146c95299 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 841ca7aa4..ca860aed5 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -178,6 +178,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 = 69, /* Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor */ meshtastic_HardwareModel_SENSECAP_INDICATOR = 70, + /* Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. */ + meshtastic_HardwareModel_TRACKER_T1000_E = 71, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From 394e0e1b3e33cbf64d6518e4b7911a402eae5284 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 26 Jul 2024 06:30:28 -0500 Subject: [PATCH 009/305] T1000_E hw model --- src/platform/nrf52/architecture.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index b66552a28..99879f0f6 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -60,6 +60,8 @@ #define HW_VENDOR meshtastic_HardwareModel_NRF52_PROMICRO_DIY #elif defined(WIO_WM1110) #define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110 +#elif defined(TRACKER_T1000_E) +#define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E #elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #else From 4ee15d81282e7f861d55fc79a141292699eae790 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 26 Jul 2024 06:38:54 -0500 Subject: [PATCH 010/305] Trunk --- src/platform/nrf52/architecture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 99879f0f6..a276a6081 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -60,7 +60,7 @@ #define HW_VENDOR meshtastic_HardwareModel_NRF52_PROMICRO_DIY #elif defined(WIO_WM1110) #define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110 -#elif defined(TRACKER_T1000_E) +#elif defined(TRACKER_T1000_E) #define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E #elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW From 8641777bac4a81a8dbe5c31d1261bba53ca4a4f6 Mon Sep 17 00:00:00 2001 From: Nestpebble <116762865+Nestpebble@users.noreply.github.com> Date: Sat, 27 Jul 2024 02:14:31 +0100 Subject: [PATCH 011/305] Add the UF2 conversion script to the p.io task menu (#4337) * Add the UF2 conversion script to the p.io task menu Update platformio-custom.py to include the UF2 conversion script as a project task. Saves you dropping into the command line every time. Tested on Windows only... * Forgot the build target... --- bin/platformio-custom.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 3202a1e7a..065f1267b 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -91,3 +91,12 @@ projenv.Append( "-DAPP_VERSION_SHORT=" + verObj["short"], ] ) + +# Add a custom p.io project task to run the UF2 conversion script. +env.AddCustomTarget( + name="Convert Hex to UF2", + dependencies=None, + actions=["PYTHON .\\bin\\uf2conv.py $BUILD_DIR\$env\\firmware.hex -c -f 0xADA52840 -o $BUILD_DIR\$env\\firmware.uf2"], + title="Convert hex to uf2", + description="Runs the python script to convert an already-built .hex file into .uf2 for copying to a device" +) From 6f235232f04523c17d091e57ccc19014e40944b1 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 27 Jul 2024 13:58:55 +0300 Subject: [PATCH 012/305] Added RF95 SX1268 support (#4338) --- variants/diy/nrf52_promicro_diy_tcxo/variant.h | 2 ++ variants/diy/nrf52_promicro_diy_xtal/variant.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index 8b957fe12..bacc0796d 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -115,6 +115,8 @@ NRF52 PRO MICRO PIN ASSIGNMENT // LORA MODULES #define USE_LLCC68 #define USE_SX1262 +#define USE_RF95 +#define USE_SX1268 // LORA CONFIG #define SX126X_CS (32 + 13) // P1.13 FIXME - we really should define LORA_CS instead diff --git a/variants/diy/nrf52_promicro_diy_xtal/variant.h b/variants/diy/nrf52_promicro_diy_xtal/variant.h index fd0b21681..c00c424cc 100644 --- a/variants/diy/nrf52_promicro_diy_xtal/variant.h +++ b/variants/diy/nrf52_promicro_diy_xtal/variant.h @@ -114,6 +114,8 @@ NRF52 PRO MICRO PIN ASSIGNMENT // LORA MODULES #define USE_LLCC68 #define USE_SX1262 +#define USE_RF95 +#define USE_SX1268 // LORA CONFIG #define SX126X_CS (32 + 13) // P1.13 FIXME - we really should define LORA_CS instead From f583837b4edfc1c75baa24534e90afb4f45b3a2c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Jul 2024 06:39:16 -0500 Subject: [PATCH 013/305] Enable STM32 build (#4339) * Enable stm32 builds and wio-e5 board * Chmod --- .github/workflows/build_stm32.yml | 33 +++++++++++++++++++++++++++++++ .github/workflows/main_matrix.yml | 10 ++++++++++ bin/build-stm32.sh | 29 +++++++++++++++++++++++++++ variants/wio-e5/platformio.ini | 1 - 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build_stm32.yml create mode 100755 bin/build-stm32.sh diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml new file mode 100644 index 000000000..d13c52c8a --- /dev/null +++ b/.github/workflows/build_stm32.yml @@ -0,0 +1,33 @@ +name: Build STM32 + +on: + workflow_call: + inputs: + board: + required: true + type: string + +jobs: + build-stm32: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build base + id: base + uses: ./.github/actions/setup-base + + - name: Build STM32 + run: bin/build-stm32.sh ${{ inputs.board }} + + - name: Get release version string + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip + overwrite: true + path: | + release/*.hex + release/*.bin diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 14c8a9d10..b1d1d307a 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -41,6 +41,7 @@ jobs: esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }} nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }} rp2040: ${{ steps.jsonStep.outputs.rp2040 }} + stm32: ${{ steps.jsonStep.outputs.stm32 }} check: ${{ steps.jsonStep.outputs.check }} check: @@ -103,6 +104,15 @@ jobs: with: board: ${{ matrix.board }} + build-stm32: + needs: setup + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup.outputs.stm32) }} + uses: ./.github/workflows/build_stm32.yml + with: + board: ${{ matrix.board }} + package-raspbian: uses: ./.github/workflows/package_raspbian.yml diff --git a/bin/build-stm32.sh b/bin/build-stm32.sh new file mode 100755 index 000000000..9e813a8c0 --- /dev/null +++ b/bin/build-stm32.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -e + +VERSION=$(bin/buildinfo.py long) +SHORT_VERSION=$(bin/buildinfo.py short) + +OUTDIR=release/ + +rm -f $OUTDIR/firmware* +rm -r $OUTDIR/* || true + +# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale +platformio pkg update -e $1 + +echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" +rm -f .pio/build/$1/firmware.* + +# The shell vars the build tool expects to find +export APP_VERSION=$VERSION + +basename=firmware-$1-$VERSION + +pio run --environment $1 # -v +SRCELF=.pio/build/$1/firmware.elf +cp $SRCELF $OUTDIR/$basename.elf + +SRCELF=.pio/build/$1/firmware.bin +cp $SRCHEX $OUTDIR/$basename.bin diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini index 12cd6190d..51591d569 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5/platformio.ini @@ -1,6 +1,5 @@ [env:wio-e5] extends = stm32_base -board_level = extra board = lora_e5_dev_board build_flags = ${stm32_base.build_flags} From e70435ebd7b2635fbc67aa0371b7c2697d15a0a0 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Jul 2024 06:49:11 -0500 Subject: [PATCH 014/305] All builds need to only pkg update for their target environment --- .github/workflows/main_matrix.yml | 2 +- bin/build-esp32.sh | 2 +- bin/build-nrf52.sh | 2 +- bin/build-rpi2040.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index b1d1d307a..ae50b94ec 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, check] + arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, stm32, check] runs-on: ubuntu-latest steps: - id: checkout diff --git a/bin/build-esp32.sh b/bin/build-esp32.sh index f60331ed5..adb6ab12a 100755 --- a/bin/build-esp32.sh +++ b/bin/build-esp32.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update +platformio pkg update -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index c0658dad9..060d06cfd 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update +platformio pkg update -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* diff --git a/bin/build-rpi2040.sh b/bin/build-rpi2040.sh index fe0725085..dad6a7e67 100755 --- a/bin/build-rpi2040.sh +++ b/bin/build-rpi2040.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update +platformio pkg update -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* From bca9fbe7e4f91daf809eb8131b8a25385df7d35f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Jul 2024 06:49:50 -0500 Subject: [PATCH 015/305] Missed a needs --- .github/workflows/main_matrix.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index ae50b94ec..36125f72f 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -144,6 +144,7 @@ jobs: build-esp32-c3, build-nrf52, build-rpi2040, + build-stm32, package-raspbian, package-raspbian-armv7l, package-native, From 1b249c32bfb10d249f069b17cbe74ddb95db5bd1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Jul 2024 07:28:11 -0500 Subject: [PATCH 016/305] Copy the actual bin --- bin/build-stm32.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/build-stm32.sh b/bin/build-stm32.sh index 9e813a8c0..76c5a75fb 100755 --- a/bin/build-stm32.sh +++ b/bin/build-stm32.sh @@ -25,5 +25,5 @@ pio run --environment $1 # -v SRCELF=.pio/build/$1/firmware.elf cp $SRCELF $OUTDIR/$basename.elf -SRCELF=.pio/build/$1/firmware.bin -cp $SRCHEX $OUTDIR/$basename.bin +SRCBIN=.pio/build/$1/firmware.bin +cp $SRCBIN $OUTDIR/$basename.bin From 32bc2f1137ab04440569fae9394a6acba1495110 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 27 Jul 2024 09:38:28 -0500 Subject: [PATCH 017/305] Add RAK3172 to the STM32WL canon --- variants/rak3172/platformio.ini | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/variants/rak3172/platformio.ini b/variants/rak3172/platformio.ini index 63766c988..d1bd55e83 100644 --- a/variants/rak3172/platformio.ini +++ b/variants/rak3172/platformio.ini @@ -1,12 +1,31 @@ [env:rak3172] extends = stm32_base -board_level = extra board = wiscore_rak3172 build_flags = ${stm32_base.build_flags} -Ivariants/rak3172 - -DHAL_DAC_MODULE_ONLY -DSERIAL_UART_INSTANCE=1 -DPIN_SERIAL_RX=PB7 -DPIN_SERIAL_TX=PB6 + -DHAL_DAC_MODULE_ONLY + -DHAL_ADC_MODULE_DISABLED + -DHAL_COMP_MODULE_DISABLED + -DHAL_CRC_MODULE_DISABLED + -DHAL_CRYP_MODULE_DISABLED + -DHAL_GTZC_MODULE_DISABLED + -DHAL_HSEM_MODULE_DISABLED + -DHAL_I2C_MODULE_DISABLED + -DHAL_I2S_MODULE_DISABLED + -DHAL_IPCC_MODULE_DISABLED + -DHAL_IRDA_MODULE_DISABLED + -DHAL_IWDG_MODULE_DISABLED + -DHAL_LPTIM_MODULE_DISABLED + -DHAL_PKA_MODULE_DISABLED + -DHAL_RNG_MODULE_DISABLED + -DHAL_RTC_MODULE_DISABLED + -DHAL_SMARTCARD_MODULE_DISABLED + -DHAL_SMBUS_MODULE_DISABLED + -DHAL_TIM_MODULE_DISABLED + -DHAL_WWDG_MODULE_DISABLED + -DHAL_EXTI_MODULE_DISABLED upload_port = stlink \ No newline at end of file From 1a1d545c38bfb3daa298d4aee03ec97f51d1b3ee Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 28 Jul 2024 14:12:30 -0500 Subject: [PATCH 018/305] Adds a userPrefs.h file, default blank, used for default settings for custom builds (#4325) * add a userPrefs.h file, default blank, which can be used to easily set defaults on custom builds. * Add Splash Screen to userPrefs * Add channel 0 defaults to userPrefs.h * CONFIG_LORA_IGNORE_MQTT_DEFAULT * Unify naming for USERPREFS defines --------- Co-authored-by: Ben Meadors --- src/graphics/Screen.cpp | 5 +++++ src/graphics/img/icon.xbm | 4 +++- src/mesh/Channels.cpp | 27 ++++++++++++++++++++++++++- src/mesh/NodeDB.cpp | 13 +++++++++++++ userPrefs.h | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 userPrefs.h diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index b2059b71c..54fd1ea4d 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . */ #include "Screen.h" +#include "../userPrefs.h" #include "configuration.h" #if HAS_SCREEN #include @@ -156,7 +157,11 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl display->setFont(FONT_MEDIUM); display->setTextAlignment(TEXT_ALIGN_LEFT); +#ifdef SPLASH_TITLE_USERPREFS + const char *title = SPLASH_TITLE_USERPREFS; +#else const char *title = "meshtastic.org"; +#endif display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); display->setFont(FONT_SMALL); diff --git a/src/graphics/img/icon.xbm b/src/graphics/img/icon.xbm index 297f31ed6..f90cf4946 100644 --- a/src/graphics/img/icon.xbm +++ b/src/graphics/img/icon.xbm @@ -1,3 +1,4 @@ +#ifndef HAS_USERPREFS_SPLASH #define icon_width 50 #define icon_height 28 static uint8_t icon_bits[] = { @@ -17,4 +18,5 @@ static uint8_t icon_bits[] = { 0xFE, 0x00, 0x00, 0xFC, 0x01, 0x7E, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x01, 0x7E, 0x00, 0x3E, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3C, 0x00, 0x00, 0x70, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, }; \ No newline at end of file + 0x00, 0x00, 0x00, 0x00, }; +#endif \ No newline at end of file diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index bb4d629e7..1a23c7861 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -1,4 +1,5 @@ #include "Channels.h" +#include "../userPrefs.h" #include "CryptoEngine.h" #include "DisplayFormatters.h" #include "NodeDB.h" @@ -90,6 +91,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast loraConfig.use_preset = true; loraConfig.tx_power = 0; // default + loraConfig.channel_num = 0; uint8_t defaultpskIndex = 1; channelSettings.psk.bytes[0] = defaultpskIndex; channelSettings.psk.size = 1; @@ -99,6 +101,29 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) ch.has_settings = true; ch.role = meshtastic_Channel_Role_PRIMARY; + +#ifdef LORACONFIG_MODEM_PRESET_USERPREFS + loraConfig.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS; +#endif +#ifdef LORACONFIG_CHANNEL_NUM_USERPREFS + loraConfig.channel_num = LORACONFIG_CHANNEL_NUM_USERPREFS; +#endif + + // Install custom defaults. Will eventually support setting multiple default channels + if (chIndex == 0) { +#ifdef CHANNEL_0_PSK_USERPREFS + static const uint8_t defaultpsk[] = CHANNEL_0_PSK_USERPREFS; + memcpy(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)); + channelSettings.psk.size = sizeof(defaultpsk); + +#endif +#ifdef CHANNEL_0_NAME_USERPREFS + strcpy(channelSettings.name, CHANNEL_0_NAME_USERPREFS); +#endif +#ifdef CHANNEL_0_PRECISION_USERPREFS + channelSettings.module_settings.position_precision = CHANNEL_0_PRECISION_USERPREFS; +#endif + } } CryptoKey Channels::getKey(ChannelIndex chIndex) @@ -330,4 +355,4 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) int16_t Channels::setActiveByIndex(ChannelIndex channelIndex) { return setCrypto(channelIndex); -} +} \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 4257837b3..b4b5ec286 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1,3 +1,4 @@ +#include "../userPrefs.h" #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" @@ -237,10 +238,22 @@ void NodeDB::installDefaultConfig() config.lora.tx_enabled = true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off) config.lora.override_duty_cycle = false; +#ifdef CONFIG_LORA_REGION_USERPREFS + config.lora.region = CONFIG_LORA_REGION_USERPREFS; +#else config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET; +#endif +#ifdef LORACONFIG_MODEM_PRESET_USERPREFS + config.lora.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS; +#else config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; +#endif config.lora.hop_limit = HOP_RELIABLE; +#ifdef CONFIG_LORA_IGNORE_MQTT_USERPREFS + config.lora.ignore_mqtt = CONFIG_LORA_IGNORE_MQTT_USERPREFS; +#else config.lora.ignore_mqtt = false; +#endif #ifdef PIN_GPS_EN config.position.gps_en_gpio = PIN_GPS_EN; #endif diff --git a/userPrefs.h b/userPrefs.h new file mode 100644 index 000000000..e03b70023 --- /dev/null +++ b/userPrefs.h @@ -0,0 +1,33 @@ +#ifndef _USERPREFS_ +#define _USERPREFS_ +// Uncomment and modify to set device defaults + +// #define CONFIG_LORA_REGION_USERPREFS meshtastic_Config_LoRaConfig_RegionCode_US +// #define LORACONFIG_MODEM_PRESET_USERPREFS meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST +// #define LORACONFIG_CHANNEL_NUM_USERPREFS 31 +// #define CONFIG_LORA_IGNORE_MQTT_USERPREFS true +/* +#define CHANNEL_0_PSK_USERPREFS \ + { \ + 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, \ + 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 \ + } +*/ +// #define CHANNEL_0_NAME_USERPREFS "DEFCONnect" +// #define CHANNEL_0_PRECISION_USERPREFS 13 + +// #define SPLASH_TITLE_USERPREFS "DEFCONtastic" +// #define icon_width 34 +// #define icon_height 29 +// #define HAS_USERPREFS_SPLASH +/* +static unsigned char icon_bits[] = { + 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, + 0x9E, 0xE7, 0x00, 0x00, 0x00, 0x0E, 0xC7, 0x01, 0x00, 0x1C, 0x0F, 0xC7, 0x01, 0x00, 0x1C, 0xDF, 0xE7, 0x63, 0x00, 0x1C, 0xFF, + 0xBF, 0xE1, 0x00, 0x3C, 0xF3, 0xBF, 0xE3, 0x00, 0x7F, 0xF7, 0xBF, 0xF1, 0x00, 0xFF, 0xF7, 0xBF, 0xF9, 0x03, 0xFF, 0xE7, 0x9F, + 0xFF, 0x03, 0xC0, 0xCF, 0xEF, 0xDF, 0x03, 0x00, 0xDF, 0xE3, 0x8F, 0x00, 0x00, 0x7C, 0xFB, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0x00, + 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x78, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x00, 0x00, + 0x98, 0x3F, 0xF0, 0x23, 0x00, 0xFC, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0x80, 0xFF, 0x01, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x70, + 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; +*/ +#endif \ No newline at end of file From 8b0208d1c6ec750ad343ec71fc0ccf742646df77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Jul 2024 14:53:25 -0500 Subject: [PATCH 019/305] [create-pull-request] automated change (#4335) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 4c87608d0..b8bb5e67d 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 4 -build = 1 +build = 2 From 811a9ae2615e8586d18bf31725370e103be87d5d Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 28 Jul 2024 19:49:10 -0500 Subject: [PATCH 020/305] Macro to trace log all MeshPackets as JSON (#4336) * Macro to trace log all MeshPackets as JSON * Comment * Add trace logging to file for native target * bytes to hex * Add time_ms --------- Co-authored-by: Jonathan Bennett --- bin/config-dist.yaml | 3 +- src/RedirectablePrint.cpp | 22 +++++++++++++- src/mesh/Router.cpp | 24 +++++++++++++++ src/platform/portduino/PortduinoGlue.cpp | 14 ++++++++- src/platform/portduino/PortduinoGlue.h | 5 +++- src/serialization/MeshPacketSerializer.cpp | 34 +++++++++++++++++++++- src/serialization/MeshPacketSerializer.h | 17 ++++++++++- 7 files changed, 113 insertions(+), 6 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 333d6eadc..0ec5a440b 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -135,10 +135,11 @@ Input: Logging: LogLevel: info # debug, info, warn, error +# TraceFile: /var/log/meshtasticd.json Webserver: # Port: 443 # Port for Webserver & Webservices # RootPath: /usr/share/doc/meshtasticd/web # Root Dir of WebServer General: - MaxNodes: 200 + MaxNodes: 200 \ No newline at end of file diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 9c3dcdc98..05d349de9 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -49,7 +49,11 @@ size_t RedirectablePrint::write(uint8_t c) size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg) { va_list copy; +#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO + static char printBuf[512]; +#else static char printBuf[160]; +#endif va_copy(copy, arg); size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy); @@ -98,6 +102,8 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, Print::write("\u001b[33m", 6); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) Print::write("\u001b[31m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) + Print::write("\u001b[35m", 6); uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile if (rtc_sec > 0) { long hms = rtc_sec % SEC_PER_DAY; @@ -244,7 +250,21 @@ meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel) void RedirectablePrint::log(const char *logLevel, const char *format, ...) { -#ifdef ARCH_PORTDUINO +#if ARCH_PORTDUINO + // level trace is special, two possible ways to handle it. + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) { + if (settingsStrings[traceFilename] != "") { + va_list arg; + va_start(arg, format); + try { + traceFile << va_arg(arg, char *) << std::endl; + } catch (const std::ios_base::failure &e) { + } + va_end(arg); + } + if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) + return; + } if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) return; else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 35536e714..c1801d419 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -11,6 +11,12 @@ #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif +#if ARCH_PORTDUINO +#include "platform/portduino/PortduinoGlue.h" +#endif +#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO +#include "serialization/MeshPacketSerializer.h" +#endif /** * Router todo * @@ -356,6 +362,13 @@ bool perhapsDecode(meshtastic_MeshPacket *p) } */ printPacket("decoded message", p); +#if ENABLE_JSON_LOGGING + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); +#elif ARCH_PORTDUINO + if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); + } +#endif return true; } } @@ -491,6 +504,17 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) { +#if ENABLE_JSON_LOGGING + // Even ignored packets get logged in the trace + p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); +#elif ARCH_PORTDUINO + // Even ignored packets get logged in the trace + if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { + p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); + } +#endif // assert(radioConfig.has_preferences); bool ignore = is_in_repeated(config.lora.ignore_incoming, p->from) || (config.lora.ignore_mqtt && p->via_mqtt); diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 89ac806dd..b910206ec 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -17,6 +17,7 @@ std::map settingsMap; std::map settingsStrings; +std::ofstream traceFile; char *configPath = nullptr; // FIXME - move setBluetoothEnable into a HALPlatform class @@ -134,7 +135,9 @@ void portduinoSetup() try { if (yamlConfig["Logging"]) { - if (yamlConfig["Logging"]["LogLevel"].as("info") == "debug") { + if (yamlConfig["Logging"]["LogLevel"].as("info") == "trace") { + settingsMap[logoutputlevel] = level_trace; + } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "debug") { settingsMap[logoutputlevel] = level_debug; } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "info") { settingsMap[logoutputlevel] = level_info; @@ -143,6 +146,7 @@ void portduinoSetup() } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "error") { settingsMap[logoutputlevel] = level_error; } + settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as(""); } if (yamlConfig["Lora"]) { settingsMap[use_sx1262] = false; @@ -346,6 +350,14 @@ void portduinoSetup() if (settingsStrings[spidev] != "") { SPI.begin(settingsStrings[spidev].c_str()); } + if (settingsStrings[traceFilename] != "") { + try { + traceFile.open(settingsStrings[traceFilename], std::ios::out | std::ios::app); + } catch (std::ofstream::failure &e) { + std::cout << "*** traceFile Exception " << e.what() << std::endl; + exit(EXIT_FAILURE); + } + } return; } diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index ca935ea3b..6b9a8eb8e 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -1,4 +1,5 @@ #pragma once +#include #include enum configNames { @@ -46,6 +47,7 @@ enum configNames { displayInvert, keyboardDevice, logoutputlevel, + traceFilename, webserver, webserverport, webserverrootpath, @@ -53,8 +55,9 @@ enum configNames { }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; -enum { level_error, level_warn, level_info, level_debug }; +enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; +extern std::ofstream traceFile; int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index dc2db1e6f..a42bb867a 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -11,7 +11,7 @@ #endif #include "mesh/generated/meshtastic/remote_hardware.pb.h" -std::string MeshPacketSerializer::JsonSerialize(meshtastic_MeshPacket *mp, bool shouldLog) +std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog) { // the created jsonObj is immutable after creation, so // we need to do the heavy lifting before assembling it. @@ -312,6 +312,38 @@ std::string MeshPacketSerializer::JsonSerialize(meshtastic_MeshPacket *mp, bool if (shouldLog) LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); + delete value; + return jsonStr; +} + +std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp) +{ + JSONObject jsonObj; + + jsonObj["id"] = new JSONValue((unsigned int)mp->id); + jsonObj["time_ms"] = new JSONValue((double)millis()); + jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time); + jsonObj["to"] = new JSONValue((unsigned int)mp->to); + jsonObj["from"] = new JSONValue((unsigned int)mp->from); + jsonObj["channel"] = new JSONValue((unsigned int)mp->channel); + jsonObj["want_ack"] = new JSONValue(mp->want_ack); + + if (mp->rx_rssi != 0) + jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi); + if (mp->rx_snr != 0) + jsonObj["snr"] = new JSONValue((float)mp->rx_snr); + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { + jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit)); + jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start)); + } + jsonObj["size"] = new JSONValue((unsigned int)mp->encrypted.size); + auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size); + jsonObj["bytes"] = new JSONValue(encryptedStr.c_str()); + + // serialize and write it to the stream + JSONValue *value = new JSONValue(jsonObj); + std::string jsonStr = value->Stringify(); + delete value; return jsonStr; } \ No newline at end of file diff --git a/src/serialization/MeshPacketSerializer.h b/src/serialization/MeshPacketSerializer.h index 579ee2fd6..03860ab35 100644 --- a/src/serialization/MeshPacketSerializer.h +++ b/src/serialization/MeshPacketSerializer.h @@ -1,8 +1,23 @@ #include #include +static const char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + class MeshPacketSerializer { public: - static std::string JsonSerialize(meshtastic_MeshPacket *mp, bool shouldLog = true); + static std::string JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog = true); + static std::string JsonSerializeEncrypted(const meshtastic_MeshPacket *mp); + + private: + static std::string bytesToHex(const uint8_t *bytes, int len) + { + std::string result = ""; + for (int i = 0; i < len; ++i) { + char const byte = bytes[i]; + result += hexChars[(byte & 0xF0) >> 4]; + result += hexChars[(byte & 0x0F) >> 0]; + } + return result; + } }; \ No newline at end of file From cf22b7ff04e9f51114762a4c7443d544bd7e0e8d Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 06:11:08 -0500 Subject: [PATCH 021/305] Latest pip version of setuptools is broken. Install specific version --- .github/actions/setup-base/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 7f8659523..cbb1e12f7 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -33,6 +33,7 @@ runs: shell: bash run: | python -m pip install --upgrade pip + pip install -U setuptools<72.0.0 pip install -U platformio adafruit-nrfutil pip install -U meshtastic --pre From 8c0ff89972f017b9f478a53ed8777a6c37d8314c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 06:14:32 -0500 Subject: [PATCH 022/305] Trunk --- .github/actions/setup-base/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index cbb1e12f7..f74f702a7 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -11,7 +11,7 @@ runs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} - - name: Install dependencies + - name: Install dependencies shell: bash run: | sudo apt-get -y update --fix-missing From c501cc501dc646607cbf7e5799daeaeadd599671 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 06:37:19 -0500 Subject: [PATCH 023/305] Pip pip cheerios plz --- .github/actions/setup-base/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index f74f702a7..80fa8db9d 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -32,8 +32,8 @@ runs: - name: Upgrade python tools shell: bash run: | + pip install --no-build-isolation --no-cache-dir "setuptools<72" python -m pip install --upgrade pip - pip install -U setuptools<72.0.0 pip install -U platformio adafruit-nrfutil pip install -U meshtastic --pre From 2ffc93324db330d17b68f5ed6d6db5bc83bb22e6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 06:38:34 -0500 Subject: [PATCH 024/305] After --- .github/actions/setup-base/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 80fa8db9d..1fb3d6c96 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -32,8 +32,8 @@ runs: - name: Upgrade python tools shell: bash run: | - pip install --no-build-isolation --no-cache-dir "setuptools<72" python -m pip install --upgrade pip + pip install --no-build-isolation --no-cache-dir "setuptools<72" pip install -U platformio adafruit-nrfutil pip install -U meshtastic --pre From 4aa6f60e95a6d3fb88f08110e65ff925f56d32c6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 06:41:26 -0500 Subject: [PATCH 025/305] Maybe remove pip cache --- .github/actions/setup-base/action.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 1fb3d6c96..e19eff786 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -22,12 +22,12 @@ runs: with: python-version: 3.x - - name: Cache python libs - uses: actions/cache@v4 - id: cache-pip # needed in if test - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip + # - name: Cache python libs + # uses: actions/cache@v4 + # id: cache-pip # needed in if test + # with: + # path: ~/.cache/pip + # key: ${{ runner.os }}-pip - name: Upgrade python tools shell: bash From 6813b8e4e9bf5e4f424070a9b92392f7daa6c366 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 06:52:14 -0500 Subject: [PATCH 026/305] Just literally trying stuff at this point --- .github/actions/setup-base/action.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index e19eff786..61466655d 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -33,9 +33,9 @@ runs: shell: bash run: | python -m pip install --upgrade pip - pip install --no-build-isolation --no-cache-dir "setuptools<72" - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre + pip install -U --no-build-isolation --no-cache-dir "setuptools<72" + pip install -U platformio adafruit-nrfutil --no-build-isolation + pip install -U meshtastic --pre --no-build-isolation - name: Upgrade platformio shell: bash From 59cc57fc29b7f7f1e0285c5ab10bed549d284d4f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 29 Jul 2024 20:16:47 -0500 Subject: [PATCH 027/305] Event mode: Enforce reliable hop limit and disallow default public MQTT (#4343) * Event mode: Enforce reliable hop limit * Event mode: Short circuit wantsLink on MQTT for default broker address * Just enforce at channels level since everything uses this * For events never forward packets with excessive hop_limit * In EVENT_MODE, don't respond with hop_limit set more then the configured max. * Correct hop_start when correcting hop_limit in event mode. * Make EVENT_MODE work from userPrefs.h * Event mode: Disallow Router or Repeater roles --------- Co-authored-by: Jonathan Bennett --- src/mesh/Channels.cpp | 7 +++++++ src/mesh/Default.cpp | 10 ++++++++++ src/mesh/Default.h | 1 + src/mesh/FloodingRouter.cpp | 8 ++++++++ src/mesh/ReliableRouter.cpp | 3 ++- src/mesh/Router.cpp | 3 ++- src/modules/AdminModule.cpp | 7 +++++++ src/modules/RoutingModule.cpp | 6 +++++- userPrefs.h | 3 +++ 9 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 1a23c7861..8d5b4353b 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -1,6 +1,7 @@ #include "Channels.h" #include "../userPrefs.h" #include "CryptoEngine.h" +#include "Default.h" #include "DisplayFormatters.h" #include "NodeDB.h" #include "RadioInterface.h" @@ -276,6 +277,12 @@ void Channels::setChannel(const meshtastic_Channel &c) bool Channels::anyMqttEnabled() { +#if EVENT_MODE + // Don't publish messages on the public MQTT broker if we are in event mode + if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0) { + return false; + } +#endif for (int i = 0; i < getNumChannels(); i++) if (channelFile.channels[i].role != meshtastic_Channel_Role_DISABLED && channelFile.channels[i].has_settings && (channelFile.channels[i].settings.downlink_enabled || channelFile.channels[i].settings.uplink_enabled)) diff --git a/src/mesh/Default.cpp b/src/mesh/Default.cpp index d4e9b3d79..ac7441394 100644 --- a/src/mesh/Default.cpp +++ b/src/mesh/Default.cpp @@ -1,4 +1,5 @@ #include "Default.h" +#include "../userPrefs.h" uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval) { @@ -40,4 +41,13 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d return getConfiguredOrDefaultMs(configured, defaultValue); return getConfiguredOrDefaultMs(configured, defaultValue) * congestionScalingCoefficient(numOnlineNodes); +} + +uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured) +{ +#if EVENT_MODE + return (configured > HOP_RELIABLE) ? HOP_RELIABLE : config.lora.hop_limit; +#else + return (configured >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit; +#endif } \ No newline at end of file diff --git a/src/mesh/Default.h b/src/mesh/Default.h index 7d79d696e..3c95544da 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -30,6 +30,7 @@ class Default static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval); static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue); static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes); + static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured); private: static float congestionScalingCoefficient(int numOnlineNodes) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 0fdde5277..fbe56159c 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -1,4 +1,5 @@ #include "FloodingRouter.h" +#include "../userPrefs.h" #include "configuration.h" #include "mesh-pb-constants.h" @@ -46,6 +47,13 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it tosend->hop_limit--; // bump down the hop count +#if EVENT_MODE + if (tosend->hop_limit > 2) { + // if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away. + tosend->hop_start -= (tosend->hop_limit - 2); + tosend->hop_limit = 2; + } +#endif LOG_INFO("Rebroadcasting received floodmsg to neighbors\n"); // Note: we are careful to resend using the original senders node id diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index d3246b48d..c91ce50c5 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -1,4 +1,5 @@ #include "ReliableRouter.h" +#include "Default.h" #include "MeshModule.h" #include "MeshTypes.h" #include "configuration.h" @@ -17,7 +18,7 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p) // message will rebroadcast. But asking for hop_limit 0 in that context means the client app has no preference on hop // counts and we want this message to get through the whole mesh, so use the default. if (p->hop_limit == 0) { - p->hop_limit = (config.lora.hop_limit >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit; + p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); } auto copy = packetPool.allocCopy(*p); diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index c1801d419..9bffd7a5d 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -11,6 +11,7 @@ #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif +#include "Default.h" #if ARCH_PORTDUINO #include "platform/portduino/PortduinoGlue.h" #endif @@ -125,7 +126,7 @@ meshtastic_MeshPacket *Router::allocForSending() p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // Assume payload is decoded at start. p->from = nodeDB->getNodeNum(); p->to = NODENUM_BROADCAST; - p->hop_limit = (config.lora.hop_limit >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit; + p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); p->id = generatePacketId(); p->rx_time = getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 8939b972b..c1e9b388b 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -400,6 +400,13 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) requiresReboot = true; } } +#if EVENT_MODE + // If we're in event mode, nobody is a Router or Repeater + if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || + config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { + config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT; + } +#endif break; case meshtastic_Config_position_tag: LOG_INFO("Setting config: Position\n"); diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index fe1abab05..80ac92fff 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -1,4 +1,5 @@ #include "RoutingModule.h" +#include "Default.h" #include "MeshService.h" #include "NodeDB.h" #include "Router.h" @@ -50,12 +51,15 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit // Hops used by the request. If somebody in between running modified firmware modified it, ignore it uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit; if (hopsUsed > config.lora.hop_limit) { +// In event mode, we never want to send packets with more than our default 3 hops. +#if !(EVENTMODE) // This falls through to the default. return hopsUsed; // If the request used more hops than the limit, use the same amount of hops +#endif } else if ((uint8_t)(hopsUsed + 2) < config.lora.hop_limit) { return hopsUsed + 2; // Use only the amount of hops needed with some margin as the way back may be different } } - return config.lora.hop_limit; // Use the default hop limit + return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit } RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg) diff --git a/userPrefs.h b/userPrefs.h index e03b70023..b365e8c6f 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -1,7 +1,10 @@ #ifndef _USERPREFS_ #define _USERPREFS_ + // Uncomment and modify to set device defaults +// #define EVENT_MODE 1 + // #define CONFIG_LORA_REGION_USERPREFS meshtastic_Config_LoRaConfig_RegionCode_US // #define LORACONFIG_MODEM_PRESET_USERPREFS meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST // #define LORACONFIG_CHANNEL_NUM_USERPREFS 31 From 302caa854a9799250b6f3ac05660aae2d86b9891 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:13:25 -0500 Subject: [PATCH 028/305] [create-pull-request] automated change (#4354) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/config.pb.h | 12 ++++++++---- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/protobufs b/protobufs index b1a79d5db..abd5eaa87 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit b1a79d5db00f6aeeb0bbae156e8939e146c95299 +Subproject commit abd5eaa875233d9bcee7e4cef3d5edcbc5b66307 diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 44a86f4d6..27a7c1383 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -303,6 +303,8 @@ typedef struct _meshtastic_Config_DeviceConfig { char tzdef[65]; /* If true, disable the default blinking LED (LED_PIN) behavior on the device */ bool led_heartbeat_disabled; + /* Enable LogRecord messages over Serial for API connections */ + bool logrecord_serial_enabled; } meshtastic_Config_DeviceConfig; /* Position Config */ @@ -614,7 +616,7 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} -#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} +#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0, 0} #define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} @@ -623,7 +625,7 @@ extern "C" { #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} -#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} +#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0, 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} @@ -645,6 +647,7 @@ extern "C" { #define meshtastic_Config_DeviceConfig_disable_triple_click_tag 10 #define meshtastic_Config_DeviceConfig_tzdef_tag 11 #define meshtastic_Config_DeviceConfig_led_heartbeat_disabled_tag 12 +#define meshtastic_Config_DeviceConfig_logrecord_serial_enabled_tag 13 #define meshtastic_Config_PositionConfig_position_broadcast_secs_tag 1 #define meshtastic_Config_PositionConfig_position_broadcast_smart_enabled_tag 2 #define meshtastic_Config_PositionConfig_fixed_position_tag 3 @@ -750,7 +753,8 @@ X(a, STATIC, SINGULAR, BOOL, double_tap_as_button_press, 8) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 9) \ X(a, STATIC, SINGULAR, BOOL, disable_triple_click, 10) \ X(a, STATIC, SINGULAR, STRING, tzdef, 11) \ -X(a, STATIC, SINGULAR, BOOL, led_heartbeat_disabled, 12) +X(a, STATIC, SINGULAR, BOOL, led_heartbeat_disabled, 12) \ +X(a, STATIC, SINGULAR, BOOL, logrecord_serial_enabled, 13) #define meshtastic_Config_DeviceConfig_CALLBACK NULL #define meshtastic_Config_DeviceConfig_DEFAULT NULL @@ -873,7 +877,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size #define meshtastic_Config_BluetoothConfig_size 12 -#define meshtastic_Config_DeviceConfig_size 100 +#define meshtastic_Config_DeviceConfig_size 102 #define meshtastic_Config_DisplayConfig_size 30 #define meshtastic_Config_LoRaConfig_size 82 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index eb37f4f95..fb6ab1bf2 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -307,7 +307,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 166 -#define meshtastic_OEMStore_size 3388 +#define meshtastic_OEMStore_size 3390 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 983f48ad3..9eff14754 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 555 +#define meshtastic_LocalConfig_size 557 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus From a1c998e7e06b3e4c89f25fb5c5e68b9e7e32bd9f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:57:24 -0500 Subject: [PATCH 029/305] [create-pull-request] automated change (#4361) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/config.pb.h | 12 ++++-------- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/protobufs b/protobufs index abd5eaa87..976748839 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit abd5eaa875233d9bcee7e4cef3d5edcbc5b66307 +Subproject commit 976748839fafcf0049bb364fe2c7226a194d18a9 diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 27a7c1383..44a86f4d6 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -303,8 +303,6 @@ typedef struct _meshtastic_Config_DeviceConfig { char tzdef[65]; /* If true, disable the default blinking LED (LED_PIN) behavior on the device */ bool led_heartbeat_disabled; - /* Enable LogRecord messages over Serial for API connections */ - bool logrecord_serial_enabled; } meshtastic_Config_DeviceConfig; /* Position Config */ @@ -616,7 +614,7 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} -#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0, 0} +#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} @@ -625,7 +623,7 @@ extern "C" { #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} -#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0, 0} +#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} @@ -647,7 +645,6 @@ extern "C" { #define meshtastic_Config_DeviceConfig_disable_triple_click_tag 10 #define meshtastic_Config_DeviceConfig_tzdef_tag 11 #define meshtastic_Config_DeviceConfig_led_heartbeat_disabled_tag 12 -#define meshtastic_Config_DeviceConfig_logrecord_serial_enabled_tag 13 #define meshtastic_Config_PositionConfig_position_broadcast_secs_tag 1 #define meshtastic_Config_PositionConfig_position_broadcast_smart_enabled_tag 2 #define meshtastic_Config_PositionConfig_fixed_position_tag 3 @@ -753,8 +750,7 @@ X(a, STATIC, SINGULAR, BOOL, double_tap_as_button_press, 8) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 9) \ X(a, STATIC, SINGULAR, BOOL, disable_triple_click, 10) \ X(a, STATIC, SINGULAR, STRING, tzdef, 11) \ -X(a, STATIC, SINGULAR, BOOL, led_heartbeat_disabled, 12) \ -X(a, STATIC, SINGULAR, BOOL, logrecord_serial_enabled, 13) +X(a, STATIC, SINGULAR, BOOL, led_heartbeat_disabled, 12) #define meshtastic_Config_DeviceConfig_CALLBACK NULL #define meshtastic_Config_DeviceConfig_DEFAULT NULL @@ -877,7 +873,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size #define meshtastic_Config_BluetoothConfig_size 12 -#define meshtastic_Config_DeviceConfig_size 102 +#define meshtastic_Config_DeviceConfig_size 100 #define meshtastic_Config_DisplayConfig_size 30 #define meshtastic_Config_LoRaConfig_size 82 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index fb6ab1bf2..eb37f4f95 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -307,7 +307,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 166 -#define meshtastic_OEMStore_size 3390 +#define meshtastic_OEMStore_size 3388 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 9eff14754..983f48ad3 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 557 +#define meshtastic_LocalConfig_size 555 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus From 93ba19d1e16f72c167c3edaae4aa0eae0576cede Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 30 Jul 2024 15:05:33 -0500 Subject: [PATCH 030/305] Make LogRecord protobuf serial logging over Phone API opt-in instead (#4358) * Make LogRecord protobuf serial logging over Phone API opt-in instead of enabled by default * debug_log_enabled --- src/SerialConsole.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index 9a9331e47..d25b81da7 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) { - if (usingProtobufs) { + if (usingProtobufs && config.device.debug_log_enabled) { meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset switch (logLevel[0]) { case 'D': @@ -120,4 +120,4 @@ void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_l emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg); } else RedirectablePrint::log_to_serial(logLevel, format, arg); -} \ No newline at end of file +} From 1951569b1a5986aea7a48729ba51c7214776ad5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Gjels=C3=B8?= <36234524+gjelsoe@users.noreply.github.com> Date: Tue, 30 Jul 2024 22:05:51 +0200 Subject: [PATCH 031/305] PA FAN Disable (#4355) * PA FAN Disable * PA FAN Disable * Trunk * Trunk * Trunk * Thunk ..... --- src/main.cpp | 9 ++++++++- src/modules/AdminModule.cpp | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index dc5863bbc..2e0115139 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -973,6 +973,13 @@ void setup() mqttInit(); #endif +#ifdef RF95_FAN_EN + // Ability to disable FAN if PIN has been set with RF95_FAN_EN. + // Make sure LoRa has been started before disabling FAN. + if (config.lora.pa_fan_disabled) + digitalWrite(RF95_FAN_EN, LOW ^ 0); +#endif + #ifndef ARCH_PORTDUINO // Initialize Wifi @@ -1087,4 +1094,4 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} \ No newline at end of file +} diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index c1e9b388b..c0e2a1ab2 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -466,6 +466,15 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.lora.sx126x_rx_boosted_gain == c.payload_variant.lora.sx126x_rx_boosted_gain) { requiresReboot = false; } + +#ifdef RF95_FAN_EN + // Turn PA off if disabled by config + if (c.payload_variant.lora.pa_fan_disabled) { + digitalWrite(RF95_FAN_EN, LOW ^ 0); + } else { + digitalWrite(RF95_FAN_EN, HIGH ^ 0); + } +#endif config.lora = c.payload_variant.lora; // If we're setting region for the first time, init the region if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) { @@ -877,4 +886,4 @@ AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_AP { // restrict to the admin channel for rx boundChannel = Channels::adminChannel; -} \ No newline at end of file +} From 5dde738a31df27086778f4af155ec23649a672e1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 30 Jul 2024 15:08:38 -0500 Subject: [PATCH 032/305] Trunk --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2e0115139..39212a0a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -982,7 +982,7 @@ void setup() #ifndef ARCH_PORTDUINO - // Initialize Wifi + // Initialize Wifi #if HAS_WIFI initWifi(); #endif From 1f9dacf486e2d22505ff0a92ad75d89ec6a31ea1 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Wed, 31 Jul 2024 04:57:13 +0800 Subject: [PATCH 033/305] Add support for ATGM332D series (#4351) Adds detection code for the ATGM332D series of chips (11, 21, 31, 51, 71), and sets meshtastic to use GNSS_MODEL_ATGM336H for them. --- src/gps/GPS.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 67f6adb98..091b4e6bc 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1217,7 +1217,7 @@ GnssModel_t GPS::probe(int serialSpeed) return GNSS_MODEL_UC6580; } - // Get version information + // Get version information for ATGM336H clearBuffer(); _serial_gps->write("$PCAS06,1*1A\r\n"); if (getACK("$GPTXT,01,01,02,HW=ATGM336H", 500) == GNSS_RESPONSE_OK) { @@ -1225,6 +1225,15 @@ GnssModel_t GPS::probe(int serialSpeed) return GNSS_MODEL_ATGM336H; } + /* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) + based on AT6558 */ + clearBuffer(); + _serial_gps->write("$PCAS06,1*1A\r\n"); + if (getACK("$GPTXT,01,01,02,HW=ATGM332D", 500) == GNSS_RESPONSE_OK) { + LOG_INFO("ATGM332D detected, using ATGM336H Module\n"); + return GNSS_MODEL_ATGM336H; + } + // Get version information clearBuffer(); _serial_gps->write("$PCAS06,0*1B\r\n"); From 9f5f630dca02f4cecf04a6e2599cc9735f649e4b Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Wed, 31 Jul 2024 04:57:31 +0800 Subject: [PATCH 034/305] Remove unused define in NRF52 architecture (#4350) The NRF52 architecture defaulted to setting GPS_UBLOX. However, GPS_UBLOX is not used anywhere in the code. Remove these lines. --- src/platform/nrf52/architecture.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index a276a6081..2b285ec2e 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -88,10 +88,6 @@ #endif -#ifndef TTGO_T_ECHO -#define GPS_UBLOX -#endif - #define LED_PIN PIN_LED1 // LED1 on nrf52840-DK #ifdef PIN_BUTTON1 @@ -120,4 +116,4 @@ #if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA) // No serial ports on this board - ONLY use segger in memory console #define USE_SEGGER -#endif \ No newline at end of file +#endif From a111f54b6189093123b6ee0fd846a708dfd91b35 Mon Sep 17 00:00:00 2001 From: Oliver0804 Date: Wed, 31 Jul 2024 06:15:50 +0800 Subject: [PATCH 035/305] Add #define USE_SSD1306 to avoid automatic detection causing pixel shift. (#4356) Co-authored-by: Ben Meadors --- variants/heltec_v3/variant.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/heltec_v3/variant.h b/variants/heltec_v3/variant.h index 2417b873d..4f1d91db8 100644 --- a/variants/heltec_v3/variant.h +++ b/variants/heltec_v3/variant.h @@ -1,5 +1,7 @@ #define LED_PIN LED +#define USE_SSD1306 // Heltec_v3 has a SSD1306 display + #define RESET_OLED RST_OLED #define I2C_SDA SDA_OLED // I2C pins for this board #define I2C_SCL SCL_OLED From 29fe6e7448d1566675b1ec388df0892752ed36ff Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 31 Jul 2024 05:52:17 -0500 Subject: [PATCH 036/305] Event mode: Block problematic portnums of traffic (#4362) --- src/mesh/Router.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 9bffd7a5d..bac25171b 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -485,6 +485,20 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) cancelSending(p->from, p->id); skipHandle = true; } + +#if EVENT_MODE + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && + (p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN || + p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP || + p->decoded.portnum == meshtastic_PortNum_AUDIO_APP || p->decoded.portnum == meshtastic_PortNum_PRIVATE_APP || + p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP || + p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || + p->decoded.portnum == meshtastic_PortNum_REMOTE_HARDWARE_APP)) { + LOG_DEBUG("Ignoring packet on blacklisted portnum during event\n"); + cancelSending(p->from, p->id); + skipHandle = true; + } +#endif } else { printPacket("packet decoding failed or skipped (no PSK?)", p); } From ce1eb149ac0b2e326010a850e7a4d3488d7390d9 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Wed, 31 Jul 2024 18:58:19 +0800 Subject: [PATCH 037/305] Cleanup for Air530z GPS (#4344) It turns out that the Air530z is a GNSS_MODEL_MTK. Our existing code detects and configures it properly. This patch removes a couple of AIROHA ifdefs since they are not required for a working GPS. Co-authored-by: Ben Meadors --- src/gps/GPS.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 091b4e6bc..d6ea2cb08 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -400,12 +400,6 @@ bool GPS::setup() int msglen = 0; if (!didSerialInit) { -#ifdef GNSS_AIROHA - if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { - probe(GPS_BAUDRATE); - LOG_INFO("GPS setting to %d.\n", GPS_BAUDRATE); - } -#else #if !defined(GPS_UC6580) if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { @@ -784,7 +778,6 @@ bool GPS::setup() LOG_INFO("GNSS module configuration saved!\n"); } } -#endif didSerialInit = true; } @@ -1191,10 +1184,6 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->updateBaudRate(serialSpeed); } #endif -#ifdef GNSS_AIROHA - - return GNSS_MODEL_UNKNOWN; -#else #ifdef GPS_DEBUG for (int i = 0; i < 20; i++) { getACK("$GP", 200); @@ -1359,7 +1348,6 @@ GnssModel_t GPS::probe(int serialSpeed) } return GNSS_MODEL_UBLOX; -#endif // !GNSS_Airoha } GPS *GPS::createGps() @@ -1809,4 +1797,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file From 848b9773b9b33af1036ebdecb0193c33c27d476e Mon Sep 17 00:00:00 2001 From: Alexander Smyslov <37107500+alexander-smyslov@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:58:41 +0200 Subject: [PATCH 038/305] Add trackerd to build. (#4347) Co-authored-by: Ben Meadors --- variants/trackerd/platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/trackerd/platformio.ini b/variants/trackerd/platformio.ini index 23868ffb4..6fba190f3 100644 --- a/variants/trackerd/platformio.ini +++ b/variants/trackerd/platformio.ini @@ -4,10 +4,10 @@ extends = esp32_base platform = espressif32 board = pico32 board_build.f_flash = 80000000L -board_level = extra + build_flags = ${esp32_base.build_flags} -D PRIVATE_HW -I variants/trackerd -D BSFILE=\"boards/dragino_lbt2.h\" ;board_build.partitions = no_ota.csv ;platform_packages = ; platformio/framework-arduinoespressif32@3 -;platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.1-RC1 \ No newline at end of file +;platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.1-RC1 From 103ab0c242cdc9b08ce2d1b97c165444db5de51c Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Wed, 31 Jul 2024 19:56:06 +0800 Subject: [PATCH 039/305] Cleanup - remove unused defines. (#4353) * Cleanup - remove unused defines. There were a number of defined variables that were carried over from old code. - Removed. Also a typo. - Fixed fix. Also duplicate definitions of the number of seconds in a day. -deduplicated. * Cleanup - remove unused defines. There were a number of defined variables that were carried over from old code. - Removed. Also a typo. - Fixed fix. Also duplicate definitions of the number of seconds in a day. -deduplicated. --------- Co-authored-by: Ben Meadors --- src/ButtonThread.h | 2 +- src/configuration.h | 4 ---- src/gps/RTC.h | 2 +- src/mesh/NodeDB.h | 5 +---- src/mesh/RF95Interface.cpp | 1 - src/mesh/RadioInterface.cpp | 3 --- src/modules/RangeTestModule.cpp | 6 +----- 7 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/ButtonThread.h b/src/ButtonThread.h index d7a9201a3..9cd7b3dac 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -13,7 +13,7 @@ #endif #ifndef BUTTON_TOUCH_MS -#define BUTTON_TOCH_MS 400 +#define BUTTON_TOUCH_MS 400 #endif class ButtonThread : public concurrency::OSThread diff --git a/src/configuration.h b/src/configuration.h index b298d5424..4ebc59ffc 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -52,10 +52,6 @@ along with this program. If not, see . // Configuration // ----------------------------------------------------------------------------- -// If we are using the JTAG port for debugging, some pins must be left free for that (and things like GPS have to be disabled) -// we don't support jtag on the ttgo - access to gpio 12 is a PITA -#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found - /// Convert a preprocessor name into a quoted string #define xstr(s) ystr(s) #define ystr(s) #s diff --git a/src/gps/RTC.h b/src/gps/RTC.h index 4b065b376..d31e85433 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -43,4 +43,4 @@ time_t gm_mktime(struct tm *tm); #define SEC_PER_DAY 86400 #define SEC_PER_HOUR 3600 -#define SEC_PER_MIN 60 \ No newline at end of file +#define SEC_PER_MIN 60 diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 258b7276d..e2c2471d4 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -204,9 +204,6 @@ extern NodeDB *nodeDB; prefs.is_power_saving = True */ -// Our delay functions check for this for times that should never expire -#define NODE_DELAY_FOREVER 0xffffffff - /// Sometimes we will have Position objects that only have a time, so check for /// valid lat/lon static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n) @@ -231,4 +228,4 @@ extern uint32_t error_address; ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \ ModuleConfig_TelemetryConfig_size + ModuleConfig_size) -// Please do not remove this comment, it makes trunk and compiler happy at the same time. \ No newline at end of file +// Please do not remove this comment, it makes trunk and compiler happy at the same time. diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index bd1ebdb0e..b6083e7f9 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -16,7 +16,6 @@ // In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING // if you set power to something higher than 17 or 20 you might fry your board. -#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level #ifdef RADIOMASTER_900_BANDIT_NANO // Structure to hold DAC and DB values typedef struct { diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 343b7f200..e9445ccac 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -177,9 +177,6 @@ The band is from 902 to 928 MHz. It mentions channel number and its respective c separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts at 903.08 MHz center frequency. */ -// 1kb was too small -#define RADIO_STACK_SIZE 4096 - /** * Calculate airtime per * https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index a66a0513e..9bdb36b0e 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -27,10 +27,6 @@ RangeTestModule::RangeTestModule() : concurrency::OSThread("RangeTestModule") {} uint32_t packetSequence = 0; -#define SEC_PER_DAY 86400 -#define SEC_PER_HOUR 3600 -#define SEC_PER_MIN 60 - int32_t RangeTestModule::runOnce() { #if defined(ARCH_ESP32) || defined(ARCH_NRF52) @@ -295,4 +291,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) #endif return 1; -} \ No newline at end of file +} From 106a50bce245ed674b026570621d69859e0907dc Mon Sep 17 00:00:00 2001 From: Sylvain Migaud Date: Wed, 31 Jul 2024 14:38:21 +0200 Subject: [PATCH 040/305] Adding support for Chatter keypad (#4022) * Adding support for Chatter keypad * Remove user button mapping since full keypad is now useable * Adding TAB key and RIGHT to allow selecting a destination * Fix shift bug (there's only three levels, not four) * reformat file * Fix bug with fast repeated keypresses --------- Co-authored-by: Ben Meadors --- src/input/SerialKeyboard.cpp | 187 +++++++++++++++++++++++++++++++ src/input/SerialKeyboard.h | 25 +++++ src/input/SerialKeyboardImpl.cpp | 21 ++++ src/input/SerialKeyboardImpl.h | 19 ++++ src/modules/Modules.cpp | 5 + variants/chatter2/variant.h | 9 +- 6 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/input/SerialKeyboard.cpp create mode 100644 src/input/SerialKeyboard.h create mode 100644 src/input/SerialKeyboardImpl.cpp create mode 100644 src/input/SerialKeyboardImpl.h diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp new file mode 100644 index 000000000..b3d11b0fc --- /dev/null +++ b/src/input/SerialKeyboard.cpp @@ -0,0 +1,187 @@ +#include "SerialKeyboard.h" +#include "configuration.h" + +#ifdef INPUTBROKER_SERIAL_TYPE +#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file + +#if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter +// 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number +unsigned char KeyMap[3][4][10]= {{{'.','a','d','g','j','m','p','t','w',' '}, + {',','b','e','h','k','n','q','u','x',' '}, + {'?','c','f','i','l','o','r','v','y',' '}, + {'1','2','3','4','5','6','s','8','z',' '}}, // low case + {{'!','A','D','G','J','M','P','T','W',' '}, + {'+','B','E','H','K','N','Q','U','X',' '}, + {'-','C','F','I','L','O','R','V','Y',' '}, + {'1','2','3','4','5','6','S','8','Z',' '}}, // upper case + {{'1','2','3','4','5','6','7','8','9','0'}, + {'1','2','3','4','5','6','7','8','9','0'}, + {'1','2','3','4','5','6','7','8','9','0'}, + {'1','2','3','4','5','6','7','8','9','0'}}}; // numbers + +#endif + + +SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name) +{ + this->_originName = name; +} + + +void SerialKeyboard::erase(){ + InputEvent e; + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = 0x08; + e.source = this->_originName; + this->notifyObservers(&e); +} + + +int32_t SerialKeyboard::runOnce() +{ + if (!INPUTBROKER_SERIAL_TYPE) { + // Input device is not requested. + return disable(); + } + + if (firstTime) { + // This is the first time the OSThread library has called this function, so do port setup + firstTime = 0; + pinMode(KB_LOAD, OUTPUT); + pinMode(KB_CLK, OUTPUT); + pinMode(KB_DATA, INPUT); + digitalWrite(KB_LOAD, HIGH); + digitalWrite(KB_CLK, LOW); + prevKeys = 0b1111111111111111; + LOG_DEBUG("Serial Keyboard setup\n"); + } + + if (INPUTBROKER_SERIAL_TYPE == 1) { //Chatter V1.0 & V2.0 keypads + // scan for keypresses + // Write pulse to load pin + digitalWrite(KB_LOAD, LOW); + delayMicroseconds(5); + digitalWrite(KB_LOAD, HIGH); + delayMicroseconds(5); + + // Get data from 74HC165 + byte shiftRegister1 = shiftIn(KB_DATA, KB_CLK, LSBFIRST); + byte shiftRegister2 = shiftIn(KB_DATA, KB_CLK, LSBFIRST); + + keys = (shiftRegister1 << 8) + shiftRegister2; + + // Print to serial monitor + //Serial.print (shiftRegister1, BIN); + //Serial.print ("X"); + //Serial.println (shiftRegister2, BIN); + + if (millis()-lastPressTime > 500){ + quickPress = 0; + } + + if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but shouldn't be a limitation + InputEvent e; + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.source = this->_originName; + // SELECT OR SEND OR CANCEL EVENT + if (!(shiftRegister2 & (1 << 3))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; + } + else if (!(shiftRegister2 & (1 << 2))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; + e.kbchar = 0xb7; + } + else if (!(shiftRegister2 & (1 << 1))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; + } + else if (!(shiftRegister2 & (1 << 0))) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; + } + + // TEXT INPUT EVENT + else if (!(shiftRegister1 & (1 << 4))) { + keyPressed = 0; + } + else if (!(shiftRegister1 & (1 << 3))) { + keyPressed = 1; + } + else if (!(shiftRegister2 & (1 << 4))) { + keyPressed = 2; + } + else if (!(shiftRegister1 & (1 << 5))) { + keyPressed = 3; + } + else if (!(shiftRegister1 & (1 << 2))) { + keyPressed = 4; + } + else if (!(shiftRegister2 & (1 << 5))) { + keyPressed = 5; + } + else if (!(shiftRegister1 & (1 << 6))) { + keyPressed = 6; + } + else if (!(shiftRegister1 & (1 << 1))) { + keyPressed = 7; + } + else if (!(shiftRegister2 & (1 << 6))) { + keyPressed = 8; + } + else if (!(shiftRegister1 & (1 << 0))) { + keyPressed = 9; + } + // BACKSPACE or TAB + else if (!(shiftRegister1 & (1 << 7))) { + if (shift == 0 || shift ==2){ // BACKSPACE + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = 0x08; + } else { // shift = 1 => TAB + e.inputEvent = ANYKEY; + e.kbchar = 0x09; + } + } + // SHIFT + else if (!(shiftRegister2 & (1 << 7))) { + keyPressed = 10; + } + + + if (keyPressed < 11){ + if (keyPressed == lastKeyPressed && millis()-lastPressTime < 500){ + quickPress += 1; + if (quickPress > 3){ + quickPress = 0; + } + } + if (keyPressed != lastKeyPressed){ + quickPress = 0; + } + if (keyPressed < 10){ // if it's a letter + if (keyPressed == lastKeyPressed && millis()-lastPressTime < 500){ + erase(); + } + e.inputEvent = ANYKEY; + e.kbchar = char(KeyMap[shift][quickPress][keyPressed]); + } + else { //then it's shift + shift += 1; + if (shift > 2){ + shift = 0; + } + } + lastPressTime = millis(); + lastKeyPressed = keyPressed; + keyPressed = 13; + } + + if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { + this->notifyObservers(&e); + } + } + prevKeys = keys; + + } + return 50; +} + + +#endif // INPUTBROKER_SERIAL_TYPE \ No newline at end of file diff --git a/src/input/SerialKeyboard.h b/src/input/SerialKeyboard.h new file mode 100644 index 000000000..1480c4d58 --- /dev/null +++ b/src/input/SerialKeyboard.h @@ -0,0 +1,25 @@ +#pragma once + +#include "InputBroker.h" +#include "concurrency/OSThread.h" + +class SerialKeyboard : public Observable, public concurrency::OSThread +{ + public: + explicit SerialKeyboard(const char *name); + + protected: + virtual int32_t runOnce() override; + void erase(); + + private: + const char *_originName; + bool firstTime = 1; + int prevKeys = 0; + int keys = 0; + int shift = 0; + int keyPressed = 13; + int lastKeyPressed = 13; + int quickPress = 0; + unsigned long lastPressTime = 0; +}; \ No newline at end of file diff --git a/src/input/SerialKeyboardImpl.cpp b/src/input/SerialKeyboardImpl.cpp new file mode 100644 index 000000000..579356f47 --- /dev/null +++ b/src/input/SerialKeyboardImpl.cpp @@ -0,0 +1,21 @@ +#include "configuration.h" +#include "InputBroker.h" +#include "SerialKeyboardImpl.h" + +#ifdef INPUTBROKER_SERIAL_TYPE + +SerialKeyboardImpl *aSerialKeyboardImpl; + +SerialKeyboardImpl::SerialKeyboardImpl() : SerialKeyboard("serialKB") {} + +void SerialKeyboardImpl::init() +{ + if (!INPUTBROKER_SERIAL_TYPE) { + disable(); + return; + } + + inputBroker->registerSource(this); +} + +#endif // INPUTBROKER_SERIAL_TYPE \ No newline at end of file diff --git a/src/input/SerialKeyboardImpl.h b/src/input/SerialKeyboardImpl.h new file mode 100644 index 000000000..7f62aa420 --- /dev/null +++ b/src/input/SerialKeyboardImpl.h @@ -0,0 +1,19 @@ +#pragma once +#include "SerialKeyboard.h" +#include "main.h" + +/** + * @brief The idea behind this class to have static methods for the event handlers. + * Check attachInterrupt() at RotaryEncoderInteruptBase.cpp + * Technically you can have as many rotary encoders hardver attached + * to your device as you wish, but you always need to have separate event + * handlers, thus you need to have a RotaryEncoderInterrupt implementation. + */ +class SerialKeyboardImpl : public SerialKeyboard +{ + public: + SerialKeyboardImpl(); + void init(); +}; + +extern SerialKeyboardImpl *aSerialKeyboardImpl; \ No newline at end of file diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 5a0e36fea..40352a56e 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -6,6 +6,7 @@ #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" #include "input/kbMatrixImpl.h" +#include "input/SerialKeyboardImpl.h" #endif #if !MESHTASTIC_EXCLUDE_ADMIN #include "modules/AdminModule.h" @@ -149,6 +150,10 @@ void setupModules() kbMatrixImpl = new KbMatrixImpl(); kbMatrixImpl->init(); #endif // INPUTBROKER_MATRIX_TYPE +#ifdef INPUTBROKER_SERIAL_TYPE + aSerialKeyboardImpl = new SerialKeyboardImpl(); + aSerialKeyboardImpl->init(); +#endif // INPUTBROKER_MATRIX_TYPE #endif // HAS_BUTTON #if ARCH_PORTDUINO aLinuxInputImpl = new LinuxInputImpl(); diff --git a/variants/chatter2/variant.h b/variants/chatter2/variant.h index 90faa1d7b..52aceafcd 100644 --- a/variants/chatter2/variant.h +++ b/variants/chatter2/variant.h @@ -34,7 +34,7 @@ // Buzzer #define PIN_BUZZER 19 // Buttons -#define BUTTON_PIN 36 // Use the WAKE button as the user button +//#define BUTTON_PIN 36 // Use the WAKE button as the user button // I2C // #define I2C_SCL 27 // #define I2C_SDA 26 @@ -91,6 +91,13 @@ #define GPS_TX_PIN 13 #define GPS_RX_PIN 2 +// keyboard +#define INPUTBROKER_SERIAL_TYPE 1 +#define KB_LOAD 21 // load values from the switch and store in shift register +#define KB_CLK 22 // clock pin for serial data out +#define KB_DATA 23 // data pin +#define CANNED_MESSAGE_MODULE_ENABLE 1 + ///////////////////////////////////////////////////////////////////////////////// // // // You should have no need to modify the code below, nor in pins_arduino.h // From 24ecfa1a45372e5cb3ac4c9e5d1346d30a6e8c26 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 31 Jul 2024 07:42:23 -0500 Subject: [PATCH 041/305] Trunk fmt --- src/input/SerialKeyboard.cpp | 109 +++++++++++++------------------ src/input/SerialKeyboardImpl.cpp | 4 +- src/modules/Modules.cpp | 2 +- variants/chatter2/variant.h | 8 +-- 4 files changed, 53 insertions(+), 70 deletions(-) diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp index b3d11b0fc..fa3eb2528 100644 --- a/src/input/SerialKeyboard.cpp +++ b/src/input/SerialKeyboard.cpp @@ -6,29 +6,28 @@ #if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter // 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number -unsigned char KeyMap[3][4][10]= {{{'.','a','d','g','j','m','p','t','w',' '}, - {',','b','e','h','k','n','q','u','x',' '}, - {'?','c','f','i','l','o','r','v','y',' '}, - {'1','2','3','4','5','6','s','8','z',' '}}, // low case - {{'!','A','D','G','J','M','P','T','W',' '}, - {'+','B','E','H','K','N','Q','U','X',' '}, - {'-','C','F','I','L','O','R','V','Y',' '}, - {'1','2','3','4','5','6','S','8','Z',' '}}, // upper case - {{'1','2','3','4','5','6','7','8','9','0'}, - {'1','2','3','4','5','6','7','8','9','0'}, - {'1','2','3','4','5','6','7','8','9','0'}, - {'1','2','3','4','5','6','7','8','9','0'}}}; // numbers +unsigned char KeyMap[3][4][10] = {{{'.', 'a', 'd', 'g', 'j', 'm', 'p', 't', 'w', ' '}, + {',', 'b', 'e', 'h', 'k', 'n', 'q', 'u', 'x', ' '}, + {'?', 'c', 'f', 'i', 'l', 'o', 'r', 'v', 'y', ' '}, + {'1', '2', '3', '4', '5', '6', 's', '8', 'z', ' '}}, // low case + {{'!', 'A', 'D', 'G', 'J', 'M', 'P', 'T', 'W', ' '}, + {'+', 'B', 'E', 'H', 'K', 'N', 'Q', 'U', 'X', ' '}, + {'-', 'C', 'F', 'I', 'L', 'O', 'R', 'V', 'Y', ' '}, + {'1', '2', '3', '4', '5', '6', 'S', '8', 'Z', ' '}}, // upper case + {{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}, + {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}, + {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}, + {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}}}; // numbers #endif - SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name) { this->_originName = name; } - -void SerialKeyboard::erase(){ +void SerialKeyboard::erase() +{ InputEvent e; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; e.kbchar = 0x08; @@ -36,7 +35,6 @@ void SerialKeyboard::erase(){ this->notifyObservers(&e); } - int32_t SerialKeyboard::runOnce() { if (!INPUTBROKER_SERIAL_TYPE) { @@ -56,82 +54,71 @@ int32_t SerialKeyboard::runOnce() LOG_DEBUG("Serial Keyboard setup\n"); } - if (INPUTBROKER_SERIAL_TYPE == 1) { //Chatter V1.0 & V2.0 keypads + if (INPUTBROKER_SERIAL_TYPE == 1) { // Chatter V1.0 & V2.0 keypads // scan for keypresses // Write pulse to load pin digitalWrite(KB_LOAD, LOW); delayMicroseconds(5); digitalWrite(KB_LOAD, HIGH); delayMicroseconds(5); - + // Get data from 74HC165 byte shiftRegister1 = shiftIn(KB_DATA, KB_CLK, LSBFIRST); byte shiftRegister2 = shiftIn(KB_DATA, KB_CLK, LSBFIRST); - + keys = (shiftRegister1 << 8) + shiftRegister2; // Print to serial monitor - //Serial.print (shiftRegister1, BIN); - //Serial.print ("X"); - //Serial.println (shiftRegister2, BIN); + // Serial.print (shiftRegister1, BIN); + // Serial.print ("X"); + // Serial.println (shiftRegister2, BIN); - if (millis()-lastPressTime > 500){ + if (millis() - lastPressTime > 500) { quickPress = 0; } - if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but shouldn't be a limitation + if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but + // shouldn't be a limitation InputEvent e; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; e.source = this->_originName; // SELECT OR SEND OR CANCEL EVENT if (!(shiftRegister2 & (1 << 3))) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; - } - else if (!(shiftRegister2 & (1 << 2))) { + } else if (!(shiftRegister2 & (1 << 2))) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; e.kbchar = 0xb7; - } - else if (!(shiftRegister2 & (1 << 1))) { + } else if (!(shiftRegister2 & (1 << 1))) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; - } - else if (!(shiftRegister2 & (1 << 0))) { + } else if (!(shiftRegister2 & (1 << 0))) { e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; } - + // TEXT INPUT EVENT else if (!(shiftRegister1 & (1 << 4))) { keyPressed = 0; - } - else if (!(shiftRegister1 & (1 << 3))) { + } else if (!(shiftRegister1 & (1 << 3))) { keyPressed = 1; - } - else if (!(shiftRegister2 & (1 << 4))) { + } else if (!(shiftRegister2 & (1 << 4))) { keyPressed = 2; - } - else if (!(shiftRegister1 & (1 << 5))) { + } else if (!(shiftRegister1 & (1 << 5))) { keyPressed = 3; - } - else if (!(shiftRegister1 & (1 << 2))) { + } else if (!(shiftRegister1 & (1 << 2))) { keyPressed = 4; - } - else if (!(shiftRegister2 & (1 << 5))) { + } else if (!(shiftRegister2 & (1 << 5))) { keyPressed = 5; - } - else if (!(shiftRegister1 & (1 << 6))) { + } else if (!(shiftRegister1 & (1 << 6))) { keyPressed = 6; - } - else if (!(shiftRegister1 & (1 << 1))) { + } else if (!(shiftRegister1 & (1 << 1))) { keyPressed = 7; - } - else if (!(shiftRegister2 & (1 << 6))) { + } else if (!(shiftRegister2 & (1 << 6))) { keyPressed = 8; - } - else if (!(shiftRegister1 & (1 << 0))) { + } else if (!(shiftRegister1 & (1 << 0))) { keyPressed = 9; } // BACKSPACE or TAB else if (!(shiftRegister1 & (1 << 7))) { - if (shift == 0 || shift ==2){ // BACKSPACE + if (shift == 0 || shift == 2) { // BACKSPACE e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; e.kbchar = 0x08; } else { // shift = 1 => TAB @@ -144,27 +131,25 @@ int32_t SerialKeyboard::runOnce() keyPressed = 10; } - - if (keyPressed < 11){ - if (keyPressed == lastKeyPressed && millis()-lastPressTime < 500){ + if (keyPressed < 11) { + if (keyPressed == lastKeyPressed && millis() - lastPressTime < 500) { quickPress += 1; - if (quickPress > 3){ + if (quickPress > 3) { quickPress = 0; } } - if (keyPressed != lastKeyPressed){ + if (keyPressed != lastKeyPressed) { quickPress = 0; } - if (keyPressed < 10){ // if it's a letter - if (keyPressed == lastKeyPressed && millis()-lastPressTime < 500){ + if (keyPressed < 10) { // if it's a letter + if (keyPressed == lastKeyPressed && millis() - lastPressTime < 500) { erase(); - } + } e.inputEvent = ANYKEY; e.kbchar = char(KeyMap[shift][quickPress][keyPressed]); - } - else { //then it's shift + } else { // then it's shift shift += 1; - if (shift > 2){ + if (shift > 2) { shift = 0; } } @@ -178,10 +163,8 @@ int32_t SerialKeyboard::runOnce() } } prevKeys = keys; - } return 50; } - #endif // INPUTBROKER_SERIAL_TYPE \ No newline at end of file diff --git a/src/input/SerialKeyboardImpl.cpp b/src/input/SerialKeyboardImpl.cpp index 579356f47..249b76fe3 100644 --- a/src/input/SerialKeyboardImpl.cpp +++ b/src/input/SerialKeyboardImpl.cpp @@ -1,6 +1,6 @@ -#include "configuration.h" -#include "InputBroker.h" #include "SerialKeyboardImpl.h" +#include "InputBroker.h" +#include "configuration.h" #ifdef INPUTBROKER_SERIAL_TYPE diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 40352a56e..08e681ff8 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -2,11 +2,11 @@ #if !MESHTASTIC_EXCLUDE_INPUTBROKER #include "input/InputBroker.h" #include "input/RotaryEncoderInterruptImpl1.h" +#include "input/SerialKeyboardImpl.h" #include "input/TrackballInterruptImpl1.h" #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" #include "input/kbMatrixImpl.h" -#include "input/SerialKeyboardImpl.h" #endif #if !MESHTASTIC_EXCLUDE_ADMIN #include "modules/AdminModule.h" diff --git a/variants/chatter2/variant.h b/variants/chatter2/variant.h index 52aceafcd..70438e52a 100644 --- a/variants/chatter2/variant.h +++ b/variants/chatter2/variant.h @@ -34,7 +34,7 @@ // Buzzer #define PIN_BUZZER 19 // Buttons -//#define BUTTON_PIN 36 // Use the WAKE button as the user button +// #define BUTTON_PIN 36 // Use the WAKE button as the user button // I2C // #define I2C_SCL 27 // #define I2C_SDA 26 @@ -93,9 +93,9 @@ // keyboard #define INPUTBROKER_SERIAL_TYPE 1 -#define KB_LOAD 21 // load values from the switch and store in shift register -#define KB_CLK 22 // clock pin for serial data out -#define KB_DATA 23 // data pin +#define KB_LOAD 21 // load values from the switch and store in shift register +#define KB_CLK 22 // clock pin for serial data out +#define KB_DATA 23 // data pin #define CANNED_MESSAGE_MODULE_ENABLE 1 ///////////////////////////////////////////////////////////////////////////////// From bcdda4de8ab81fec6a35be52cc2a3a0fa3fa5332 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 31 Jul 2024 08:53:59 -0500 Subject: [PATCH 042/305] Missed some includes of userPrefs that would allow behavior we don't want --- src/mesh/Router.cpp | 1 + src/modules/AdminModule.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index bac25171b..1260b7c04 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -18,6 +18,7 @@ #if ENABLE_JSON_LOGGING || ARCH_PORTDUINO #include "serialization/MeshPacketSerializer.h" #endif +#include "../userPrefs.h" /** * Router todo * diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index c0e2a1ab2..747f8a8a5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -16,6 +16,7 @@ #ifdef ARCH_PORTDUINO #include "unistd.h" #endif +#include "../userPrefs.h" #include "Default.h" #include "TypeConversions.h" @@ -886,4 +887,4 @@ AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_AP { // restrict to the admin channel for rx boundChannel = Channels::adminChannel; -} +} \ No newline at end of file From 4c1c5b070eef35ee8320df608781c1866477d865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Gjels=C3=B8?= <36234524+gjelsoe@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:53:38 +0200 Subject: [PATCH 043/305] Changed a RADIOMASTER_900_BANDIT_NANO to DISPLAY_FLIP_SCREEN (#4366) * More compatible Changed a RADIOMASTER_900_BANDIT_NANO to DISPLAY_FLIP_SCREEN that is responsible for flipping the OLED screen for better compatible with other devices. * Update variant.h Radiomaster Remove a un-used SCREEN_ROTATE and added DISPLAY_FLIP_SCREEN --- src/mesh/NodeDB.cpp | 4 ++-- variants/radiomaster_900_bandit_nano/variant.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index b4b5ec286..6fbbbf546 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -303,7 +303,7 @@ void NodeDB::installDefaultConfig() meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW); -#ifdef RADIOMASTER_900_BANDIT_NANO +#ifdef DISPLAY_FLIP_SCREEN config.display.flip_screen = true; #endif #ifdef T_WATCH_S3 @@ -1054,4 +1054,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} \ No newline at end of file +} diff --git a/variants/radiomaster_900_bandit_nano/variant.h b/variants/radiomaster_900_bandit_nano/variant.h index bd6687733..39c1bc06f 100644 --- a/variants/radiomaster_900_bandit_nano/variant.h +++ b/variants/radiomaster_900_bandit_nano/variant.h @@ -50,7 +50,7 @@ #define BUTTON_PIN 39 #define BUTTON_NEED_PULLUP -#define SCREEN_ROTATE +#define DISPLAY_FLIP_SCREEN /* No External notification. @@ -81,4 +81,4 @@ */ #define RF95_PA_EN 26 #define RF95_PA_DAC_EN -#define RF95_PA_LEVEL 90 \ No newline at end of file +#define RF95_PA_LEVEL 90 From d2ea430a3ee40647e1a414d5bc95c0bfdb4a37b7 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 1 Aug 2024 19:29:49 -0500 Subject: [PATCH 044/305] Make SPI frequency and TOPHONE queue size configurable on Native (#4369) * Make SPI frequency configurable on Native * Make the tophone queue size configurable for Portduino * The modified SPISettings must be configured in setup(), after config.yaml is processed * make MeshService a pointer, so we can configure MAX_RX_TOPHONE at run time * Got a little over excited with refactoring * Silence a warning --- bin/config-dist.yaml | 14 +++++++++++-- src/ButtonThread.cpp | 4 ++-- src/main.cpp | 15 +++++++++----- src/mesh/MeshModule.cpp | 2 +- src/mesh/MeshService.cpp | 6 +++++- src/mesh/MeshService.h | 2 +- src/mesh/PhoneAPI.cpp | 20 +++++++++---------- src/mesh/ProtobufModule.h | 2 +- src/mesh/RadioInterface.cpp | 4 ++-- src/mesh/SinglePortModule.h | 4 ++-- src/mesh/mesh-pb-constants.h | 2 ++ src/modules/AdminModule.cpp | 6 +++--- src/modules/AtakPluginModule.cpp | 4 ++-- src/modules/CannedMessageModule.cpp | 8 ++++---- src/modules/DetectionSensorModule.cpp | 4 ++-- src/modules/DropzoneModule.cpp | 4 ++-- src/modules/NeighborInfoModule.cpp | 2 +- src/modules/NodeInfoModule.cpp | 8 ++++---- src/modules/PositionModule.cpp | 14 ++++++------- src/modules/RangeTestModule.cpp | 4 ++-- src/modules/RemoteHardwareModule.cpp | 2 +- src/modules/RoutingModule.cpp | 2 +- src/modules/SerialModule.cpp | 4 ++-- src/modules/Telemetry/AirQualityTelemetry.cpp | 6 +++--- src/modules/Telemetry/DeviceTelemetry.cpp | 6 +++--- .../Telemetry/EnvironmentTelemetry.cpp | 6 +++--- src/modules/Telemetry/PowerTelemetry.cpp | 6 +++--- src/modules/esp32/AudioModule.cpp | 2 +- src/modules/esp32/PaxcounterModule.cpp | 2 +- src/modules/esp32/StoreForwardModule.cpp | 6 +++--- src/mqtt/MQTT.cpp | 8 ++++---- src/platform/portduino/PortduinoGlue.cpp | 3 +++ src/platform/portduino/PortduinoGlue.h | 2 ++ src/platform/portduino/SimRadio.cpp | 4 ++-- variants/heltec_mesh_node_t114/variant.h | 4 ++-- variants/portduino/variant.h | 1 + 36 files changed, 110 insertions(+), 83 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 0ec5a440b..1cd219c4c 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -54,6 +54,8 @@ Lora: # ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341 +# spiSpeed: 2000000 + ### Set gpio chip to use in /dev/. Defaults to 0. ### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4 # gpiochip: 4 @@ -111,6 +113,9 @@ Display: # Height: 320 # Rotate: true +### You can also specify the spi device for the display to use +# spidev: spidev0.0 + Touchscreen: ### Note, at least for now, the touchscreen must have a CS pin defined, even if you let Linux manage the CS switching. @@ -126,9 +131,13 @@ Touchscreen: # CS: 7 # IRQ: 17 -### Configure device for direct keyboard input +### You can also specify the spi device for the touchscreen to use +# spidev: spidev0.0 + Input: +### Configure device for direct keyboard input + # KeyboardDevice: /dev/input/by-id/usb-_Raspberry_Pi_Internal_Keyboard-event-kbd ### @@ -142,4 +151,5 @@ Webserver: # RootPath: /usr/share/doc/meshtasticd/web # Root Dir of WebServer General: - MaxNodes: 200 \ No newline at end of file + MaxNodes: 200 + MaxMessageQueue: 100 \ No newline at end of file diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 914ff8e06..1b101044f 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -144,8 +144,8 @@ int32_t ButtonThread::runOnce() case BUTTON_EVENT_DOUBLE_PRESSED: { LOG_BUTTON("Double press!\n"); - service.refreshLocalMeshNode(); - auto sentPosition = service.trySendPosition(NODENUM_BROADCAST, true); + service->refreshLocalMeshNode(); + auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true); if (screen) { if (sentPosition) screen->print("Sent ad-hoc position\n"); diff --git a/src/main.cpp b/src/main.cpp index 39212a0a9..561b24a34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -208,7 +208,6 @@ uint32_t timeLastPowered = 0; static Periodic *ledPeriodic; static OSThread *powerFSMthread; static OSThread *ambientLightingThread; -SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0); RadioInterface *rIf = NULL; @@ -231,6 +230,12 @@ void printInfo() void setup() { concurrency::hasBeenSetup = true; +#if ARCH_PORTDUINO + SPISettings spiSettings(settingsMap[spiSpeed], MSBFIRST, SPI_MODE0); +#else + SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0); +#endif + meshtastic_Config_DisplayConfig_OledType screen_model = meshtastic_Config_DisplayConfig_OledType::meshtastic_Config_DisplayConfig_OledType_OLED_AUTO; OLEDDISPLAY_GEOMETRY screen_geometry = GEOMETRY_128_64; @@ -714,8 +719,8 @@ void setup() LOG_DEBUG("Starting audio thread\n"); audioThread = new AudioThread(); #endif - - service.init(); + service = new MeshService(); + service->init(); // Now that the mesh service is created, create any modules setupModules(); @@ -1080,7 +1085,7 @@ void loop() // TODO: This should go into a thread handled by FreeRTOS. // handleWebResponse(); - service.loop(); + service->loop(); long delayMsec = mainController.runOrDelay(); @@ -1094,4 +1099,4 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} +} \ No newline at end of file diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 1ef4f60d8..604ac9dc4 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -170,7 +170,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) if (isDecoded && mp.decoded.want_response && toUs) { if (currentReply) { printPacket("Sending response", currentReply); - service.sendToMesh(currentReply); + service->sendToMesh(currentReply); currentReply = NULL; } else if (mp.from != ourNodeNum && !ignoreRequest) { // Note: if the message started with the local node or a module asked to ignore the request, we don't want to send a diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 1181ffb9a..fd07c3801 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -23,6 +23,10 @@ #include "nimble/NimbleBluetooth.h" #endif +#if ARCH_PORTDUINO +#include "PortduinoGlue.h" +#endif + /* receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone. It is implemented with a FreeRTos queue (wrapped with a little RTQueue class) of pointers to MeshPacket protobufs (which were @@ -53,7 +57,7 @@ nodenum (filtering against whatever it knows about the nodedb) and rebroadcast t FIXME in the initial proof of concept we just skip the entire want/deny flow and just hand pick node numbers at first. */ -MeshService service; +MeshService *service; static MemoryDynamic staticMqttClientProxyMessagePool; diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index ef92ba7d4..528adb137 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -150,4 +150,4 @@ class MeshService friend class RoutingModule; }; -extern MeshService service; \ No newline at end of file +extern MeshService *service; \ No newline at end of file diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index f0775741a..fc0099e87 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -41,7 +41,7 @@ void PhoneAPI::handleStartConfig() // Must be before setting state (because state is how we know !connected) if (!isConnected()) { onConnectionChanged(true); - observe(&service.fromNumChanged); + observe(&service->fromNumChanged); #ifdef FSCom observe(&xModem.packetReady); #endif @@ -63,7 +63,7 @@ void PhoneAPI::close() if (state != STATE_SEND_NOTHING) { state = STATE_SEND_NOTHING; - unobserve(&service.fromNumChanged); + unobserve(&service->fromNumChanged); #ifdef FSCom unobserve(&xModem.packetReady); #endif @@ -186,7 +186,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.my_info = myNodeInfo; state = STATE_SEND_OWN_NODEINFO; - service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon. + service->refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon. break; case STATE_SEND_OWN_NODEINFO: { @@ -443,7 +443,7 @@ void PhoneAPI::handleDisconnect() void PhoneAPI::releasePhonePacket() { if (packetForPhone) { - service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore + service->releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore packetForPhone = NULL; } } @@ -451,7 +451,7 @@ void PhoneAPI::releasePhonePacket() void PhoneAPI::releaseQueueStatusPhonePacket() { if (queueStatusPacketForPhone) { - service.releaseQueueStatusToPool(queueStatusPacketForPhone); + service->releaseQueueStatusToPool(queueStatusPacketForPhone); queueStatusPacketForPhone = NULL; } } @@ -459,7 +459,7 @@ void PhoneAPI::releaseQueueStatusPhonePacket() void PhoneAPI::releaseMqttClientProxyPhonePacket() { if (mqttClientProxyMessageForPhone) { - service.releaseMqttClientProxyMessageToPool(mqttClientProxyMessageForPhone); + service->releaseMqttClientProxyMessageToPool(mqttClientProxyMessageForPhone); mqttClientProxyMessageForPhone = NULL; } } @@ -495,9 +495,9 @@ bool PhoneAPI::available() return true; // Always say we have something, because we might need to advance our state machine case STATE_SEND_PACKETS: { if (!queueStatusPacketForPhone) - queueStatusPacketForPhone = service.getQueueStatusForPhone(); + queueStatusPacketForPhone = service->getQueueStatusForPhone(); if (!mqttClientProxyMessageForPhone) - mqttClientProxyMessageForPhone = service.getMqttClientProxyMessageForPhone(); + mqttClientProxyMessageForPhone = service->getMqttClientProxyMessageForPhone(); bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone; if (hasPacket) return true; @@ -520,7 +520,7 @@ bool PhoneAPI::available() #endif if (!packetForPhone) - packetForPhone = service.getForPhone(); + packetForPhone = service->getForPhone(); hasPacket = !!packetForPhone; // LOG_DEBUG("available hasPacket=%d\n", hasPacket); return hasPacket; @@ -538,7 +538,7 @@ bool PhoneAPI::available() bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); - service.handleToRadio(p); + service->handleToRadio(p); return true; } diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h index ed76b877f..3b438ebeb 100644 --- a/src/mesh/ProtobufModule.h +++ b/src/mesh/ProtobufModule.h @@ -38,7 +38,7 @@ template class ProtobufModule : protected SinglePortModule /** * Return a mesh packet which has been preinited with a particular protobuf data payload and port number. * You can then send this packet (after customizing any of the payload fields you might need) with - * service.sendToMesh() + * service->sendToMesh() */ meshtastic_MeshPacket *allocDataProtobuf(const T &payload) { diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index e9445ccac..262d2d6a9 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -334,7 +334,7 @@ bool RadioInterface::init() { LOG_INFO("Starting meshradio init...\n"); - configChangedObserver.observe(&service.configChanged); + configChangedObserver.observe(&service->configChanged); preflightSleepObserver.observe(&preflightSleep); notifyDeepSleepObserver.observe(¬ifyDeepSleep); @@ -585,4 +585,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} +} \ No newline at end of file diff --git a/src/mesh/SinglePortModule.h b/src/mesh/SinglePortModule.h index a5aaa2582..e43de09d1 100644 --- a/src/mesh/SinglePortModule.h +++ b/src/mesh/SinglePortModule.h @@ -26,7 +26,7 @@ class SinglePortModule : public MeshModule /** * Return a mesh packet which has been preinited as a data packet with a particular port number. * You can then send this packet (after customizing any of the payload fields you might need) with - * service.sendToMesh() + * service->sendToMesh() */ meshtastic_MeshPacket *allocDataPacket() { @@ -36,4 +36,4 @@ class SinglePortModule : public MeshModule return p; } -}; +}; \ No newline at end of file diff --git a/src/mesh/mesh-pb-constants.h b/src/mesh/mesh-pb-constants.h index b8ef236c9..f91c48560 100644 --- a/src/mesh/mesh-pb-constants.h +++ b/src/mesh/mesh-pb-constants.h @@ -14,7 +14,9 @@ /// max number of packets which can be waiting for delivery to android - note, this value comes from mesh.options protobuf // FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in // RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0])) +#ifndef MAX_RX_TOPHONE #define MAX_RX_TOPHONE 32 +#endif /// max number of nodes allowed in the mesh #ifndef MAX_NUM_NODES diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 747f8a8a5..26ddab0db 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -348,7 +348,7 @@ void AdminModule::handleSetOwner(const meshtastic_User &o) } if (changed) { // If nothing really changed, don't broadcast on the network or write to flash - service.reloadOwner(!hasOpenEditTransaction); + service->reloadOwner(!hasOpenEditTransaction); saveChanges(SEGMENT_DEVICESTATE); } } @@ -848,7 +848,7 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot) { if (!hasOpenEditTransaction) { LOG_INFO("Saving changes to disk\n"); - service.reloadConfig(saveWhat); // Calls saveToDisk among other things + service->reloadConfig(saveWhat); // Calls saveToDisk among other things } else { LOG_INFO("Delaying save of changes to disk until the open transaction is committed\n"); } @@ -879,7 +879,7 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) channels.setChannel(primaryChannel); channels.onConfigChanged(); - service.reloadOwner(false); + service->reloadOwner(false); saveChanges(SEGMENT_CONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); } diff --git a/src/modules/AtakPluginModule.cpp b/src/modules/AtakPluginModule.cpp index c05f055d4..437a341db 100644 --- a/src/modules/AtakPluginModule.cpp +++ b/src/modules/AtakPluginModule.cpp @@ -188,7 +188,7 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast pb_encode_to_bytes(decompressedCopy->decoded.payload.bytes, sizeof(decompressedCopy->decoded.payload), meshtastic_TAKPacket_fields, &uncompressed); - service.sendToPhone(decompressedCopy); + service->sendToPhone(decompressedCopy); } return; -} +} \ No newline at end of file diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 524d37a3d..8df5d2d9e 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -264,8 +264,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) #endif break; case 0xaf: // fn+space send network ping like double press does - service.refreshLocalMeshNode(); - if (service.trySendPosition(NODENUM_BROADCAST, true)) { + service->refreshLocalMeshNode(); + if (service->trySendPosition(NODENUM_BROADCAST, true)) { showTemporaryMessage("Position \nUpdate Sent"); } else { showTemporaryMessage("Node Info \nUpdate Sent"); @@ -388,7 +388,7 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); - service.sendToMesh( + service->sendToMesh( p, RX_SRC_LOCAL, true); // send to mesh, cc to phone. Even if there's no phone connected, this stores the message to match ACKs } @@ -1048,7 +1048,7 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket & e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen requestFocus(); // Tell Screen::setFrames that our module's frame should be shown, even if not "first" in the frameset this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED; - this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id); + this->incoming = service->getNodenumFromRequestId(mp.decoded.request_id); meshtastic_Routing decoded = meshtastic_Routing_init_default; pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; diff --git a/src/modules/DetectionSensorModule.cpp b/src/modules/DetectionSensorModule.cpp index b6e5f1e29..20d91a381 100644 --- a/src/modules/DetectionSensorModule.cpp +++ b/src/modules/DetectionSensorModule.cpp @@ -82,7 +82,7 @@ void DetectionSensorModule::sendDetectionMessage() } LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); lastSentToMesh = millis(); - service.sendToMesh(p); + service->sendToMesh(p); delete[] message; } @@ -97,7 +97,7 @@ void DetectionSensorModule::sendCurrentStateMessage() memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size); LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); lastSentToMesh = millis(); - service.sendToMesh(p); + service->sendToMesh(p); delete[] message; } diff --git a/src/modules/DropzoneModule.cpp b/src/modules/DropzoneModule.cpp index 8c5b5dcdd..b1ca2af81 100644 --- a/src/modules/DropzoneModule.cpp +++ b/src/modules/DropzoneModule.cpp @@ -1,7 +1,7 @@ #if !MESHTASTIC_EXCLUDE_DROPZONE #include "DropzoneModule.h" -#include "MeshService.h" +#include "Meshservice->h" #include "configuration.h" #include "gps/GeoCoord.h" #include "gps/RTC.h" @@ -20,7 +20,7 @@ int32_t DropzoneModule::runOnce() { // Send on a 5 second delay from receiving the matching request if (startSendConditions != 0 && (startSendConditions + 5000U) < millis()) { - service.sendToMesh(sendConditions(), RX_SRC_LOCAL); + service->sendToMesh(sendConditions(), RX_SRC_LOCAL); startSendConditions = 0; } // Run every second to check if we need to send conditions diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 774b42d7b..fb1249029 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -108,7 +108,7 @@ void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies) p->to = dest; p->decoded.want_response = wantReplies; printNeighborInfo("SENDING", &neighborInfo); - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); } /* diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 78af7099a..62cf9d2a1 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -26,7 +26,7 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes // if user has changed while packet was not for us, inform phone if (hasChanged && !wasBroadcast && mp.to != nodeDB->getNodeNum()) - service.sendToPhone(packetPool.allocCopy(mp)); + service->sendToPhone(packetPool.allocCopy(mp)); // LOG_DEBUG("did handleReceived\n"); return false; // Let others look at this message also if they want @@ -36,7 +36,7 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha { // cancel any not yet sent (now stale) position packets if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal) - service.cancelSending(prevPacketId); + service->cancelSending(prevPacketId); meshtastic_MeshPacket *p = allocReply(); if (p) { // Check whether we didn't ignore it @@ -52,7 +52,7 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha prevPacketId = p->id; - service.sendToMesh(p); + service->sendToMesh(p); } } @@ -98,4 +98,4 @@ int32_t NodeInfoModule::runOnce() sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) } return Default::getConfiguredOrDefaultMs(config.device.node_info_broadcast_secs, default_node_info_broadcast_secs); -} +} \ No newline at end of file diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 228929e96..5da918049 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -141,7 +141,7 @@ meshtastic_MeshPacket *PositionModule::allocReply() return nullptr; } - meshtastic_NodeInfoLite *node = service.refreshLocalMeshNode(); // should guarantee there is now a position + meshtastic_NodeInfoLite *node = service->refreshLocalMeshNode(); // should guarantee there is now a position assert(node->has_position); // configuration of POSITION packet @@ -280,7 +280,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha { // cancel any not yet sent (now stale) position packets if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal) - service.cancelSending(prevPacketId); + service->cancelSending(prevPacketId); // Set's the class precision value for this particular packet if (channels.getByIndex(channel).settings.has_module_settings) { @@ -309,7 +309,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha if (channel > 0) p->channel = channel; - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) && @@ -359,7 +359,7 @@ int32_t PositionModule::runOnce() } } } else if (config.position.position_broadcast_smart_enabled) { - const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position + const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position if (hasValidPosition(node2)) { // The minimum time (in seconds) that would pass before we are able to send a new position packet. @@ -398,7 +398,7 @@ void PositionModule::sendLostAndFoundText() p->decoded.payload.size = strlen(message); memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size); - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); delete[] message; } @@ -437,7 +437,7 @@ struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic void PositionModule::handleNewPosition() { meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); - const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position + const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position // We limit our GPS broadcasts to a max rate if (hasValidPosition(node2)) { auto smartPosition = getDistanceTraveledSinceLastSend(node->position); @@ -458,4 +458,4 @@ void PositionModule::handleNewPosition() } } -#endif +#endif \ No newline at end of file diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index 9bdb36b0e..8154a661e 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -120,7 +120,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies) p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply memcpy(p->decoded.payload.bytes, heartbeatString, p->decoded.payload.size); - service.sendToMesh(p); + service->sendToMesh(p); // TODO: Handle this better. We want to keep the phone awake otherwise it stops sending. powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); @@ -291,4 +291,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) #endif return 1; -} +} \ No newline at end of file diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index 8e64b9a9c..d999e75cc 100644 --- a/src/modules/RemoteHardwareModule.cpp +++ b/src/modules/RemoteHardwareModule.cpp @@ -131,7 +131,7 @@ int32_t RemoteHardwareModule::runOnce() r.type = meshtastic_HardwareMessage_Type_GPIOS_CHANGED; r.gpio_value = curVal; meshtastic_MeshPacket *p = allocDataProtobuf(r); - service.sendToMesh(p); + service->sendToMesh(p); } } } else { diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 80ac92fff..87015032d 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -17,7 +17,7 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh // Note: we are careful not to send back packets that started with the phone back to the phone if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB->getNodeNum()) && (mp.from != 0)) { printPacket("Delivering rx packet", &mp); - service.handleFromRadio(&mp); + service->handleFromRadio(&mp); } return false; // Let others look at this message also if they want diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 4b8a4d228..f0ba64f65 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -236,7 +236,7 @@ void SerialModule::sendTelemetry(meshtastic_Telemetry m) p->to = NODENUM_BROADCAST; p->decoded.want_response = false; p->priority = meshtastic_MeshPacket_Priority_RELIABLE; - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); } /** @@ -272,7 +272,7 @@ void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies) p->decoded.payload.size = serialPayloadSize; // You must specify how many bytes are in the reply memcpy(p->decoded.payload.bytes, serialBytes, p->decoded.payload.size); - service.sendToMesh(p); + service->sendToMesh(p); } /** diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 6d2bf5e01..d07296710 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -54,7 +54,7 @@ int32_t AirQualityTelemetryModule::runOnce() airTime->isTxAllowedAirUtil()) { sendTelemetry(); lastSentToMesh = now; - } else if (service.isToPhoneQueueEmpty()) { + } else if (service->isToPhoneQueueEmpty()) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); @@ -162,10 +162,10 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { LOG_INFO("Sending packet to phone\n"); - service.sendToPhone(p); + service->sendToPhone(p); } else { LOG_INFO("Sending packet to mesh\n"); - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); } return true; } diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 9c1ac289c..4bde73f41 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -25,7 +25,7 @@ int32_t DeviceTelemetryModule::runOnce() config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { sendTelemetry(); lastSentToMesh = uptimeLastMs; - } else if (service.isToPhoneQueueEmpty()) { + } else if (service->isToPhoneQueueEmpty()) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); @@ -113,10 +113,10 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) nodeDB->updateTelemetry(nodeDB->getNodeNum(), telemetry, RX_SRC_LOCAL); if (phoneOnly) { LOG_INFO("Sending packet to phone\n"); - service.sendToPhone(p); + service->sendToPhone(p); } else { LOG_INFO("Sending packet to mesh\n"); - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); } return true; } \ No newline at end of file diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index fec1ee461..a100d1ef5 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -161,7 +161,7 @@ int32_t EnvironmentTelemetryModule::runOnce() sendTelemetry(); lastSentToMesh = now; } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && - (service.isToPhoneQueueEmpty())) { + (service->isToPhoneQueueEmpty())) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); @@ -449,10 +449,10 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { LOG_INFO("Sending packet to phone\n"); - service.sendToPhone(p); + service->sendToPhone(p); } else { LOG_INFO("Sending packet to mesh\n"); - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index a6f922e56..90371780f 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -78,7 +78,7 @@ int32_t PowerTelemetryModule::runOnce() sendTelemetry(); lastSentToMesh = now; } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && - (service.isToPhoneQueueEmpty())) { + (service->isToPhoneQueueEmpty())) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); @@ -244,10 +244,10 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { LOG_INFO("Sending packet to phone\n"); - service.sendToPhone(p); + service->sendToPhone(p); } else { LOG_INFO("Sending packet to mesh\n"); - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 2e2e4f528..8a29f9a2e 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -266,7 +266,7 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) p->decoded.payload.size = tx_encode_frame_index; memcpy(p->decoded.payload.bytes, tx_encode_frame, p->decoded.payload.size); - service.sendToMesh(p); + service->sendToMesh(p); } ProcessMessage AudioModule::handleReceived(const meshtastic_MeshPacket &mp) diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index 895328234..71810df07 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -52,7 +52,7 @@ bool PaxcounterModule::sendInfo(NodeNum dest) p->decoded.want_response = false; p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; - service.sendToMesh(p, RX_SRC_LOCAL, true); + service->sendToMesh(p, RX_SRC_LOCAL, true); paxcounterModule->reportedDataSent = true; diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index ff0f796a1..7581bbc38 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -211,7 +211,7 @@ bool StoreForwardModule::sendPayload(NodeNum dest, uint32_t last_time) meshtastic_MeshPacket *p = preparePayload(dest, last_time); if (p) { LOG_INFO("*** Sending S&F Payload\n"); - service.sendToMesh(p); + service->sendToMesh(p); this->requestCount++; return true; } @@ -293,7 +293,7 @@ void StoreForwardModule::sendMessage(NodeNum dest, const meshtastic_StoreAndForw p->want_ack = false; p->decoded.want_response = false; - service.sendToMesh(p); + service->sendToMesh(p); } /** @@ -336,7 +336,7 @@ void StoreForwardModule::sendErrorTextMessage(NodeNum dest, bool want_response) if (want_response) { ignoreRequest = true; // This text message counts as response. } - service.sendToMesh(pr); + service->sendToMesh(pr); } /** diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 2fce526a0..4bb9cd5eb 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -83,7 +83,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) { memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length()); p->decoded.payload.size = jsonPayloadStr.length(); - service.sendToMesh(p, RX_SRC_LOCAL); + service->sendToMesh(p, RX_SRC_LOCAL); } else { LOG_WARN("Received MQTT json payload too long, dropping\n"); } @@ -114,7 +114,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Position_msg, &pos); // make the Data protobuf from position - service.sendToMesh(p, RX_SRC_LOCAL); + service->sendToMesh(p, RX_SRC_LOCAL); } else { LOG_DEBUG("JSON Ignoring downlink message with unsupported type.\n"); } @@ -245,7 +245,7 @@ bool MQTT::publish(const char *topic, const char *payload, bool retained) strcpy(msg->topic, topic); strcpy(msg->payload_variant.text, payload); msg->retained = retained; - service.sendMqttMessageToClientProxy(msg); + service->sendMqttMessageToClientProxy(msg); return true; } #if HAS_NETWORKING @@ -265,7 +265,7 @@ bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, boo msg->payload_variant.data.size = length; memcpy(msg->payload_variant.data.bytes, payload, length); msg->retained = retained; - service.sendMqttMessageToClientProxy(msg); + service->sendMqttMessageToClientProxy(msg); return true; } #if HAS_NETWORKING diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index b910206ec..dc7a9ed61 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -97,6 +97,7 @@ void portduinoSetup() settingsStrings[webserverrootpath] = ""; settingsStrings[spidev] = ""; settingsStrings[displayspidev] = ""; + settingsMap[spiSpeed] = 2000000; YAML::Node yamlConfig; @@ -173,6 +174,7 @@ void portduinoSetup() settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC); settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false); + settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); gpioChipName += std::to_string(settingsMap[gpiochip]); settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0"); @@ -280,6 +282,7 @@ void portduinoSetup() } settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); + settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); } catch (YAML::Exception &e) { std::cout << "*** Exception " << e.what() << std::endl; diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 6b9a8eb8e..0c81b8686 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -19,6 +19,7 @@ enum configNames { user, gpiochip, spidev, + spiSpeed, i2cdev, has_gps, touchscreenModule, @@ -51,6 +52,7 @@ enum configNames { webserver, webserverport, webserverrootpath, + maxtophone, maxnodes }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9488, hx8357d }; diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index d0072cf35..12757fe6b 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -199,8 +199,8 @@ void SimRadio::startSend(meshtastic_MeshPacket *txp) pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Compressed_msg, &c); p->decoded.portnum = meshtastic_PortNum_SIMULATOR_APP; - service.sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id); - service.sendToPhone(p); // Sending back to simulator + service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id); + service->sendToPhone(p); // Sending back to simulator } void SimRadio::startReceive(meshtastic_MeshPacket *p) diff --git a/variants/heltec_mesh_node_t114/variant.h b/variants/heltec_mesh_node_t114/variant.h index b233069c6..615526b22 100644 --- a/variants/heltec_mesh_node_t114/variant.h +++ b/variants/heltec_mesh_node_t114/variant.h @@ -126,8 +126,8 @@ No longer populated on PCB #define LORA_CS (0 + 24) #define SX126X_DIO1 (0 + 20) // Note DIO2 is attached internally to the module to an analog switch for TX/RX switching -// #define SX1262_DIO3 \ -// (0 + 21) // This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the +// #define SX1262_DIO3 (0 + 21) +// This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the // main // CPU? #define SX126X_BUSY (0 + 17) diff --git a/variants/portduino/variant.h b/variants/portduino/variant.h index 414a3fa56..70d7cbd52 100644 --- a/variants/portduino/variant.h +++ b/variants/portduino/variant.h @@ -1,5 +1,6 @@ #define HAS_SCREEN 1 #define CANNED_MESSAGE_MODULE_ENABLE 1 #define HAS_GPS 1 +#define MAX_RX_TOPHONE settingsMap[maxtophone] #define MAX_NUM_NODES settingsMap[maxnodes] #define RADIOLIB_GODMODE 1 From 4b4c1669a964c7e3f543952a2633db09afbbf2a1 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Fri, 2 Aug 2024 14:03:59 +1200 Subject: [PATCH 045/305] Initial support for HT-VME290, sleep fixes for HT-VME213 (#4334) * Fix I2C pindefs * Initial driver testing for HT-VME290 * E-Ink full refresh after canned message pop up * Tidy variant folders * Clean ESP32 cpuDeepSleep method Merge sections, and remove the random assortment of gpio holds left behind. * Enable 32kHz in variant.h * Orient E290 with LoRa antenna facing up * Revert "Clean ESP32 cpuDeepSleep method" This reverts commit cb8ee508ec2d6bc27a8e228021fd1efbd034c4a0. * Reduce deep-sleep current for VME213 (non-intrusive) Originally I made an attempt at tidying up the cpuDeepSleep method, but have reverted that. New commit makes only the changes needed to support VME213. Don't really want the headache of breaking sleep for other variants, especially when this PR is just about implementing new boards. * Update lib_deps; remove board_level extra --- boards/heltec_vision_master_e290.json | 42 +++++++++++++++++++ src/graphics/Screen.cpp | 7 ++++ src/mesh/NodeDB.cpp | 4 ++ src/modules/CannedMessageModule.cpp | 9 ++++ src/platform/esp32/main-esp32.cpp | 18 ++++---- variants/heltec_vision_master_e213/variant.h | 9 ++-- .../heltec_vision_master_e290/pins_arduino.h | 8 ++-- .../heltec_vision_master_e290/platformio.ini | 32 +++++++------- variants/heltec_vision_master_e290/variant.h | 36 +++++++--------- variants/heltec_wireless_paper/variant.h | 9 ++-- variants/heltec_wireless_paper_v1/variant.h | 9 ++-- 11 files changed, 121 insertions(+), 62 deletions(-) create mode 100644 boards/heltec_vision_master_e290.json diff --git a/boards/heltec_vision_master_e290.json b/boards/heltec_vision_master_e290.json new file mode 100644 index 000000000..70f7d5f02 --- /dev/null +++ b/boards/heltec_vision_master_e290.json @@ -0,0 +1,42 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + ["0x303A", "0x1001"], + ["0x303A", "0x0002"] + ], + "mcu": "esp32s3", + "variant": "heltec_vision_master_e290" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "Heltec Vision Master E290", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://heltec.org/project/vision-master-e290/", + "vendor": "Heltec" +} diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 54fd1ea4d..633fb4c67 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1902,6 +1902,13 @@ int32_t Screen::runOnce() // standard screen loop handling here if (config.display.auto_screen_carousel_secs > 0 && (millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) { + +// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead +// Carousel is potentially a major source of E-Ink display wear +#if !defined(EINK_BACKGROUND_USES_FAST) + EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); +#endif + LOG_DEBUG("LastScreenTransition exceeded %ums transitioning to next frame\n", (millis() - lastScreenTransition)); handleOnPress(); } diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 6fbbbf546..1f66857f8 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -310,6 +310,10 @@ void NodeDB::installDefaultConfig() config.display.screen_on_secs = 30; config.display.wake_on_tap_or_motion = true; #endif +#ifdef HELTEC_VISION_MASTER_E290 + // Orient so that LoRa antenna faces up + config.display.flip_screen = true; +#endif initConfigIntervals(); } diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 8df5d2d9e..f4ee3abd2 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -17,6 +17,9 @@ #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" #endif +#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) +#include "graphics/EInkDynamicDisplay.h" // To select between full and fast refresh on E-Ink displays +#endif #ifndef INPUTBROKER_MATRIX_TYPE #define INPUTBROKER_MATRIX_TYPE 0 @@ -928,6 +931,9 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->setFont(FONT_MEDIUM); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage); } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) { + // E-Ink: clean the screen *after* this pop-up + EINK_ADD_FRAMEFLAG(display, COSMETIC); + requestFocus(); // Tell Screen::setFrames to move to our module's frame display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); @@ -950,6 +956,9 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi); } } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { + // E-Ink: clean the screen *after* this pop-up + EINK_ADD_FRAMEFLAG(display, COSMETIC); + requestFocus(); // Tell Screen::setFrames to move to our module's frame display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 3910f718f..6bce498ab 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -195,16 +195,16 @@ void cpuDeepSleep(uint32_t msecToWake) button(s), maybe we should not include any other GPIOs... */ #if SOC_RTCIO_HOLD_SUPPORTED - static const uint8_t rtcGpios[] = {/* 0, */ 2, - /* 4, */ -#ifndef USE_JTAG - 13, - /* 14, */ /* 15, */ + static const uint8_t rtcGpios[] = { +#ifndef HELTEC_VISION_MASTER_E213 + // For this variant, >20mA leaks through the display if pin 2 held + // Todo: check if it's safe to remove this pin for all variants + 2, #endif - /* 25, */ /* 26, */ /* 27, */ - /* 32, */ /* 33, */ 34, 35, - /* 36, */ 37 - /* 38, 39 */}; +#ifndef USE_JTAG + 13, +#endif + 34, 35, 37}; for (int i = 0; i < sizeof(rtcGpios); i++) rtc_gpio_isolate((gpio_num_t)rtcGpios[i]); diff --git a/variants/heltec_vision_master_e213/variant.h b/variants/heltec_vision_master_e213/variant.h index 99bc1d138..bbc697f09 100644 --- a/variants/heltec_vision_master_e213/variant.h +++ b/variants/heltec_vision_master_e213/variant.h @@ -15,9 +15,9 @@ // SPI #define SPI_INTERFACES_COUNT 2 -#define PIN_SPI_MISO 10 // MISO -#define PIN_SPI_MOSI 11 // MOSI -#define PIN_SPI_SCK 9 // SCK +#define PIN_SPI_MISO 11 +#define PIN_SPI_MOSI 10 +#define PIN_SPI_SCK 9 // Power #define VEXT_ENABLE 18 // Powers the E-Ink display, and the 3.3V supply to the I2C QuickLink connector @@ -29,11 +29,12 @@ #define ADC_CHANNEL ADC1_GPIO7_CHANNEL #define ADC_MULTIPLIER 4.9 * 1.03 #define ADC_ATTENUATION ADC_ATTEN_DB_2_5 +#define HAS_32768HZ // LoRa #define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_DIO0 RADIOLIB_NC // a No connect on the SX1262 module #define LORA_RESET 12 #define LORA_DIO1 14 // SX1262 IRQ #define LORA_DIO2 13 // SX1262 BUSY diff --git a/variants/heltec_vision_master_e290/pins_arduino.h b/variants/heltec_vision_master_e290/pins_arduino.h index e5d507846..77cf3176a 100644 --- a/variants/heltec_vision_master_e290/pins_arduino.h +++ b/variants/heltec_vision_master_e290/pins_arduino.h @@ -3,15 +3,15 @@ #include -static const uint8_t LED_BUILTIN = 35; +static const uint8_t LED_BUILTIN = -1; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN static const uint8_t TX = 43; static const uint8_t RX = 44; -static const uint8_t SDA = 41; -static const uint8_t SCL = 42; +static const uint8_t SDA = 39; +static const uint8_t SCL = 38; static const uint8_t SS = 8; static const uint8_t MOSI = 10; @@ -56,6 +56,6 @@ static const uint8_t T14 = 14; static const uint8_t RST_LoRa = 12; static const uint8_t BUSY_LoRa = 13; -static const uint8_t DIO0 = 14; +static const uint8_t DIO1 = 14; #endif /* Pins_Arduino_h */ diff --git a/variants/heltec_vision_master_e290/platformio.ini b/variants/heltec_vision_master_e290/platformio.ini index 60ff60036..e1ba100ae 100644 --- a/variants/heltec_vision_master_e290/platformio.ini +++ b/variants/heltec_vision_master_e290/platformio.ini @@ -1,25 +1,25 @@ [env:heltec-vision-master-e290] -board_level = extra extends = esp32s3_base -board = heltec_wifi_lora_32_V3 +board = heltec_vision_master_e290 build_flags = ${esp32s3_base.build_flags} - -Ivariants/heltec_vision_master_e290 - -DHELTEC_VISION_MASTER_E290 - -DEINK_DISPLAY_MODEL=GxEPD2_290_BS - -DEINK_WIDTH=296 - -DEINK_HEIGHT=128 -; -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -; -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted -; -D EINK_LIMIT_RATE_BACKGROUND_SEC=1 ; Minimum interval between BACKGROUND updates -; -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated -; -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -; -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" -; -D EINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight + -I variants/heltec_vision_master_e290 + -D HELTEC_VISION_MASTER_E290 + -D BUTTON_CLICK_MS=200 + -D EINK_DISPLAY_MODEL=GxEPD2_290_BN8 + -D EINK_WIDTH=296 + -D EINK_HEIGHT=128 + -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted + -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates + -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates + -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" + -D EINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight +; -D EINK_LIMIT_GHOSTING_PX=2000 ; How much image ghosting is tolerated +; -D EINK_BACKGROUND_USES_FAST ; (If enabled) don't redraw RESPONSIVE frames at next BACKGROUND update lib_deps = ${esp32s3_base.lib_deps} - https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d + https://github.com/meshtastic/GxEPD2#448c8538129fde3d02a7cb5e6fc81971ad92547f lewisxhe/PCF8563_Library@^1.0.1 upload_speed = 115200 \ No newline at end of file diff --git a/variants/heltec_vision_master_e290/variant.h b/variants/heltec_vision_master_e290/variant.h index a8ec5485b..6af4b06a5 100644 --- a/variants/heltec_vision_master_e290/variant.h +++ b/variants/heltec_vision_master_e290/variant.h @@ -1,48 +1,42 @@ -// #define LED_PIN 18 +#define BUTTON_PIN 0 -// Enable bus for external periherals +// I2C #define I2C_SDA SDA #define I2C_SCL SCL +// Display (E-Ink) #define USE_EINK - -/* - * eink display pins - */ #define PIN_EINK_CS 3 -#define PIN_EINK_BUSY 5 +#define PIN_EINK_BUSY 6 #define PIN_EINK_DC 4 #define PIN_EINK_RES 5 #define PIN_EINK_SCLK 2 #define PIN_EINK_MOSI 1 -/* - * SPI interfaces - */ +// SPI #define SPI_INTERFACES_COUNT 2 +#define PIN_SPI_MISO 11 +#define PIN_SPI_MOSI 10 +#define PIN_SPI_SCK 9 -#define PIN_SPI_MISO 10 // MISO -#define PIN_SPI_MOSI 11 // MOSI -#define PIN_SPI_SCK 9 // SCK - -#define VEXT_ENABLE 18 // powers the e-ink display -#define VEXT_ON_VALUE 1 -#define BUTTON_PIN 0 - +// Power +#define VEXT_ENABLE 18 // Powers the E-Ink display only +#define VEXT_ON_VALUE HIGH #define ADC_CTRL 46 #define ADC_CTRL_ENABLED HIGH #define BATTERY_PIN 7 #define ADC_CHANNEL ADC1_GPIO7_CHANNEL #define ADC_MULTIPLIER 4.9 * 1.03 -#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // Voltage divider output is quite high +#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 +#define HAS_32768HZ +// LoRa #define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_DIO0 RADIOLIB_NC // a No connect on the SX1262 module #define LORA_RESET 12 #define LORA_DIO1 14 // SX1262 IRQ #define LORA_DIO2 13 // SX1262 BUSY -#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled #define LORA_SCK 9 #define LORA_MISO 11 diff --git a/variants/heltec_wireless_paper/variant.h b/variants/heltec_wireless_paper/variant.h index c41d6d9df..a7bd460f7 100644 --- a/variants/heltec_wireless_paper/variant.h +++ b/variants/heltec_wireless_paper/variant.h @@ -16,9 +16,9 @@ // SPI #define SPI_INTERFACES_COUNT 2 -#define PIN_SPI_MISO 10 // MISO -#define PIN_SPI_MOSI 11 // MOSI -#define PIN_SPI_SCK 9 // SCK +#define PIN_SPI_MISO 11 +#define PIN_SPI_MOSI 10 +#define PIN_SPI_SCK 9 // Power #define VEXT_ENABLE 45 // Active low, powers the E-Ink display @@ -28,11 +28,12 @@ #define ADC_MULTIPLIER 2 // Voltage divider is roughly 1:1 #define BAT_MEASURE_ADC_UNIT 2 // Use ADC2 #define ADC_ATTENUATION ADC_ATTEN_DB_12 // Voltage divider output is quite high +#define HAS_32768HZ // LoRa #define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_DIO0 RADIOLIB_NC // a No connect on the SX1262 module #define LORA_RESET 12 #define LORA_DIO1 14 // SX1262 IRQ #define LORA_DIO2 13 // SX1262 BUSY diff --git a/variants/heltec_wireless_paper_v1/variant.h b/variants/heltec_wireless_paper_v1/variant.h index c41d6d9df..a7bd460f7 100644 --- a/variants/heltec_wireless_paper_v1/variant.h +++ b/variants/heltec_wireless_paper_v1/variant.h @@ -16,9 +16,9 @@ // SPI #define SPI_INTERFACES_COUNT 2 -#define PIN_SPI_MISO 10 // MISO -#define PIN_SPI_MOSI 11 // MOSI -#define PIN_SPI_SCK 9 // SCK +#define PIN_SPI_MISO 11 +#define PIN_SPI_MOSI 10 +#define PIN_SPI_SCK 9 // Power #define VEXT_ENABLE 45 // Active low, powers the E-Ink display @@ -28,11 +28,12 @@ #define ADC_MULTIPLIER 2 // Voltage divider is roughly 1:1 #define BAT_MEASURE_ADC_UNIT 2 // Use ADC2 #define ADC_ATTENUATION ADC_ATTEN_DB_12 // Voltage divider output is quite high +#define HAS_32768HZ // LoRa #define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_DIO0 RADIOLIB_NC // a No connect on the SX1262 module #define LORA_RESET 12 #define LORA_DIO1 14 // SX1262 IRQ #define LORA_DIO2 13 // SX1262 BUSY From 8db6039264003aa2a803a3690c83ef07cd0da3ef Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 2 Aug 2024 19:32:28 +0800 Subject: [PATCH 046/305] Remove empty file, StatusHandler.h (#4372) Was added 4 years ago, never used/modified. --- src/StatusHandler.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/StatusHandler.h diff --git a/src/StatusHandler.h b/src/StatusHandler.h deleted file mode 100644 index e69de29bb..000000000 From 48c063518887616f208629f7b4f9573487c7d9b2 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 2 Aug 2024 23:10:55 +0800 Subject: [PATCH 047/305] Remove OSTimer (#4373) This code is not included anywhere, and none of the functions are called by code elsewhere in meshtastic. --- src/OSTimer.cpp | 60 ------------------------------------------------- src/OSTimer.h | 8 ------- 2 files changed, 68 deletions(-) delete mode 100644 src/OSTimer.cpp delete mode 100644 src/OSTimer.h diff --git a/src/OSTimer.cpp b/src/OSTimer.cpp deleted file mode 100644 index f6739d75e..000000000 --- a/src/OSTimer.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "OSTimer.h" -#include "configuration.h" - -/** - * Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR) - * - * NOTE! xTimerPend... seems to ignore the time passed in on ESP32 and on NRF52 - * The reason this didn't work is because xTimerPednFunctCall really isn't a timer function at all - it just means run the -callback - * from the timer thread the next time you have spare cycles. - * - * @return true if successful, false if the timer fifo is too full. - -bool scheduleOSCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec) -{ - return xTimerPendFunctionCall(callback, param1, param2, pdMS_TO_TICKS(delayMsec)); -} */ - -#ifdef ARCH_ESP32 - -// Super skanky quick hack to use hardware timers of the ESP32 -static hw_timer_t *timer; -static PendableFunction tCallback; -static void *tParam1; -static uint32_t tParam2; - -static void IRAM_ATTR onTimer() -{ - (*tCallback)(tParam1, tParam2); -} - -/** - * Schedules a hardware callback function to be executed after a specified delay. - * - * @param callback The function to be executed. - * @param param1 The first parameter to be passed to the function. - * @param param2 The second parameter to be passed to the function. - * @param delayMsec The delay time in milliseconds before the function is executed. - * - * @return True if the function was successfully scheduled, false otherwise. - */ -bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec) -{ - if (!timer) { - timer = timerBegin(0, 80, true); // one usec per tick (main clock is 80MhZ on ESP32) - assert(timer); - timerAttachInterrupt(timer, &onTimer, true); - } - - tCallback = callback; - tParam1 = param1; - tParam2 = param2; - - timerAlarmWrite(timer, delayMsec * 1000UL, false); // Do not reload, we want it to be a single shot timer - timerRestart(timer); - timerAlarmEnable(timer); - return true; -} - -#endif \ No newline at end of file diff --git a/src/OSTimer.h b/src/OSTimer.h deleted file mode 100644 index 37415f3a6..000000000 --- a/src/OSTimer.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -typedef void (*PendableFunction)(void *pvParameter1, uint32_t ulParameter2); - -/// Uses a hardware timer, but calls the handler in _interrupt_ context -bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec); \ No newline at end of file From 703da1d8c7956490d569812ff6fb80a0e8e7aa2d Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 2 Aug 2024 16:28:04 -0700 Subject: [PATCH 048/305] Fix build to not use incorrect OneButton version (#4374) * Fix build to not use incorrect OneButton version OneButton pushed out a new update today that has a different API rather than just use whichever new version they push, stay on 2.5.x until someone sees a need to update. Fixes build for wm1100 tracker. * Update stm32.ini * 2.6.1 * Try github tag instead? * Update stm32.ini --------- Co-authored-by: Ben Meadors --- arch/stm32/stm32.ini | 4 ++-- platformio.ini | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 2cea4bbc5..d6d14e2c9 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -32,5 +32,5 @@ lib_deps = https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e lib_ignore = - mathertel/OneButton - Wire \ No newline at end of file + https://github.com/mathertel/OneButton@~2.6.1 + Wire diff --git a/platformio.ini b/platformio.ini index b3f677247..e60f0d7b9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -86,7 +86,7 @@ monitor_filters = direct lib_deps = jgromes/RadioLib@~6.6.0 https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 - mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce + https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0 @@ -155,4 +155,4 @@ lib_deps = mprograms/QMC5883LCompass@^1.2.0 - https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee \ No newline at end of file + https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee From 09ea198205c1a9a543154fea9b2e41891360bab8 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 2 Aug 2024 16:55:04 -0700 Subject: [PATCH 049/305] Automatically generate .uf2 files anytime we generate a .hex file for nrf52 (#4370) * Automatically generate .uf2 files (which are often used by nrf52 bootloaders for installing app loads) anytime we generate a new hex file. This tool takes very little time to run and it is handy for development * Remove an old custom target I had tried to add to autogen uf2 files (that never worked) Build output now looks like: $ pio run --environment tracker-t1000-e Processing tracker-t1000-e (board: tracker-t1000-e; platform: platformio/nordicnrf52@^10.5.0; framework: arduino) ... Generating UF2 file Converting to uf2, output size: 1395200, start address: 0x27000 Wrote 1395200 bytes to /home/kevinh/development/meshtastic/firmware/.pio/build/tracker-t1000-e/firmware.uf2 Building .pio/build/tracker-t1000-e/firmware.zip Zip created at .pio/build/tracker-t1000-e/firmware.zip =================================================================================== [SUCCESS] Took 9.33 seconds =================================================================================== Environment Status Duration --------------- -------- ------------ tracker-t1000-e SUCCESS 00:00:09.327 =================================================================================== 1 succeeded in 00:00:09.327 =================================================================================== Co-authored-by: Ben Meadors --- bin/platformio-custom.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 065f1267b..4f7ce55d0 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -78,6 +78,11 @@ if platform.name == "espressif32": # For newer ESP32 targets, using newlib nano works better. env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"]) +if platform.name == "nordicnrf52": + env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", + env.VerboseAction(f"python ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", + "Generating UF2 file")) + Import("projenv") prefsLoc = projenv["PROJECT_DIR"] + "/version.properties" @@ -90,13 +95,4 @@ projenv.Append( "-DAPP_VERSION=" + verObj["long"], "-DAPP_VERSION_SHORT=" + verObj["short"], ] -) - -# Add a custom p.io project task to run the UF2 conversion script. -env.AddCustomTarget( - name="Convert Hex to UF2", - dependencies=None, - actions=["PYTHON .\\bin\\uf2conv.py $BUILD_DIR\$env\\firmware.hex -c -f 0xADA52840 -o $BUILD_DIR\$env\\firmware.uf2"], - title="Convert hex to uf2", - description="Runs the python script to convert an already-built .hex file into .uf2 for copying to a device" -) +) \ No newline at end of file From dd552a99e18ec0c67710211ae357f88e43e32607 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 2 Aug 2024 18:20:44 -0700 Subject: [PATCH 050/305] fix #4367 make USB power detection work correctly on seeed trackers (#4376) for wio tracker 1110 and 1000-E and possibly other nrf52 boards. The problem was that nrf52 power stuff wasn't generating regular powerstatus notifications (because that code was guarded by a batteryLevel check which was null for those boards). So I've cleaned up the battery status stuff a bit and we now have fewer special cases. Tested on a 1000-E, tracker 1110 and a rak4631 board. Co-authored-by: Ben Meadors --- src/Power.cpp | 158 +++++++++++++------------- src/PowerFSM.cpp | 2 +- variants/tracker-t1000-e/variant.h | 5 +- variants/wio-tracker-wm1110/variant.h | 5 +- 4 files changed, 87 insertions(+), 83 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 138b06e71..e8e406a94 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -461,7 +461,7 @@ class AnalogBatteryLevel : public HasBatteryLevel #endif }; -AnalogBatteryLevel analogLevel; +static AnalogBatteryLevel analogLevel; Power::Power() : OSThread("Power") { @@ -560,6 +560,10 @@ bool Power::setup() { bool found = axpChipInit() || analogInit(); +#ifdef NRF_APM + found = true; +#endif + enabled = found; low_voltage_counter = 0; @@ -589,10 +593,16 @@ void Power::shutdown() // TODO(girts): move this and other axp stuff to power.h/power.cpp. void Power::readPowerStatus() { + int32_t batteryVoltageMv = -1; // Assume unknown + int8_t batteryChargePercent = -1; + OptionalBool usbPowered = OptUnknown; + OptionalBool hasBattery = OptUnknown; // These must be static because NRF_APM code doesn't run every time + OptionalBool isCharging = OptUnknown; + if (batteryLevel) { - bool hasBattery = batteryLevel->isBatteryConnect(); - uint32_t batteryVoltageMv = 0; - int8_t batteryChargePercent = 0; + hasBattery = batteryLevel->isBatteryConnect() ? OptTrue : OptFalse; + usbPowered = batteryLevel->isVbusIn() ? OptTrue : OptFalse; + isCharging = batteryLevel->isCharging() ? OptTrue : OptFalse; if (hasBattery) { batteryVoltageMv = batteryLevel->getBattVoltage(); // If the AXP192 returns a valid battery percentage, use it @@ -607,102 +617,90 @@ void Power::readPowerStatus() 0, 100); } } + } - OptionalBool NRF_USB = OptFalse; - +// FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way better instead to make a Nrf52IsUsbPowered subclass +// (which shares a superclass with the BatteryLevel stuff) +// that just provides a few methods. But in the interest of fixing this bug I'm going to follow current +// practice. #ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect // changes. - static nrfx_power_usb_state_t prev_nrf_usb_state = (nrfx_power_usb_state_t)-1; // -1 so that state detected at boot - nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get(); + nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get(); + // LOG_DEBUG("NRF Power %d\n", nrf_usb_state); - // If state changed - if (nrf_usb_state != prev_nrf_usb_state) { - // If changed to DISCONNECTED - if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) { - powerFSM.trigger(EVENT_POWER_DISCONNECTED); - NRF_USB = OptFalse; - } - // If changed to CONNECTED / READY - else { - powerFSM.trigger(EVENT_POWER_CONNECTED); - NRF_USB = OptTrue; - } + // If changed to DISCONNECTED + if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) + isCharging = usbPowered = OptFalse; + // If changed to CONNECTED / READY + else + isCharging = usbPowered = OptTrue; - // Cache the current state - prev_nrf_usb_state = nrf_usb_state; - } #endif - // Notify any status instances that are observing us - const PowerStatus powerStatus2 = PowerStatus( - hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() || NRF_USB == OptTrue ? OptTrue : OptFalse, - batteryLevel->isCharging() || NRF_USB == OptTrue ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent); - LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(), - powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); - newStatus.notifyObservers(&powerStatus2); + + // Notify any status instances that are observing us + const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isCharging, batteryVoltageMv, batteryChargePercent); + LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(), + powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); + newStatus.notifyObservers(&powerStatus2); #ifdef DEBUG_HEAP - if (lastheap != memGet.getFreeHeap()) { - LOG_DEBUG("Threads running:"); - int running = 0; - for (int i = 0; i < MAX_THREADS; i++) { - auto thread = concurrency::mainController.get(i); - if ((thread != nullptr) && (thread->enabled)) { - LOG_DEBUG(" %s", thread->ThreadName.c_str()); - running++; - } + if (lastheap != memGet.getFreeHeap()) { + LOG_DEBUG("Threads running:"); + int running = 0; + for (int i = 0; i < MAX_THREADS; i++) { + auto thread = concurrency::mainController.get(i); + if ((thread != nullptr) && (thread->enabled)) { + LOG_DEBUG(" %s", thread->ThreadName.c_str()); + running++; } - LOG_DEBUG("\n"); - LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(), - memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); - lastheap = memGet.getFreeHeap(); } + LOG_DEBUG("\n"); + LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(), + memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); + lastheap = memGet.getFreeHeap(); + } #ifdef DEBUG_HEAP_MQTT - if (mqtt) { - // send MQTT-Packet with Heap-Size - uint8_t dmac[6]; - getMacAddr(dmac); // Get our hardware ID - char mac[18]; - sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]); + if (mqtt) { + // send MQTT-Packet with Heap-Size + uint8_t dmac[6]; + getMacAddr(dmac); // Get our hardware ID + char mac[18]; + sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]); - auto newHeap = memGet.getFreeHeap(); - std::string heapTopic = - (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac); - std::string heapString = std::to_string(newHeap); - mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false); - auto wifiRSSI = WiFi.RSSI(); - std::string wifiTopic = - (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac); - std::string wifiString = std::to_string(wifiRSSI); - mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false); - } + auto newHeap = memGet.getFreeHeap(); + std::string heapTopic = + (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac); + std::string heapString = std::to_string(newHeap); + mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false); + auto wifiRSSI = WiFi.RSSI(); + std::string wifiTopic = + (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac); + std::string wifiString = std::to_string(wifiRSSI); + mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false); + } #endif #endif - // If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in - // a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough. - // - if (powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) { - if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) { - low_voltage_counter++; - LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter); - if (low_voltage_counter > 10) { + // If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in + // a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough. + // + if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) { + if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) { + low_voltage_counter++; + LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter); + if (low_voltage_counter > 10) { #ifdef ARCH_NRF52 - // We can't trigger deep sleep on NRF52, it's freezing the board - LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n"); + // We can't trigger deep sleep on NRF52, it's freezing the board + LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n"); #else - LOG_INFO("Low voltage detected, triggering deep sleep\n"); - powerFSM.trigger(EVENT_LOW_BATTERY); + LOG_INFO("Low voltage detected, triggering deep sleep\n"); + powerFSM.trigger(EVENT_LOW_BATTERY); #endif - } - } else { - low_voltage_counter = 0; } + } else { + low_voltage_counter = 0; } - } else { - // No power sensing on this board - tell everyone else we have no idea what is happening - const PowerStatus powerStatus3 = PowerStatus(OptUnknown, OptUnknown, OptUnknown, -1, -1); - newStatus.notifyObservers(&powerStatus3); } } @@ -1051,4 +1049,4 @@ bool Power::axpChipInit() #else return false; #endif -} +} \ No newline at end of file diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 72e00810b..699a6bca6 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -26,7 +26,7 @@ static bool isPowered() { // Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC -#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) +#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) && !defined(NRF_APM) return true; #endif diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index 75d8ddffc..63c2a76dc 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -40,6 +40,9 @@ extern "C" { #define NUM_ANALOG_INPUTS (6) #define NUM_ANALOG_OUTPUTS (0) +// Use the native nrf52 usb power detection +#define NRF_APM + #define PIN_3V3_EN (32 + 6) // P1.6, Power to Sensors #define PIN_3V3_ACC_EN (32 + 7) // P1.7, Power to Acc @@ -147,4 +150,4 @@ extern "C" { * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_TRACKER_T1000_E_ +#endif // _VARIANT_TRACKER_T1000_E_ \ No newline at end of file diff --git a/variants/wio-tracker-wm1110/variant.h b/variants/wio-tracker-wm1110/variant.h index e929332e6..2bb2f1a59 100644 --- a/variants/wio-tracker-wm1110/variant.h +++ b/variants/wio-tracker-wm1110/variant.h @@ -43,6 +43,9 @@ extern "C" { #define WIRE_INTERFACES_COUNT 1 +// We rely on the nrf52840 USB controller to tell us if we are hooked to a power supply +#define NRF_APM + #define PIN_3V3_EN (32 + 1) // P1.01, Power to Sensors #define PIN_WIRE_SDA (0 + 5) // P0.05 @@ -108,4 +111,4 @@ extern "C" { * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_WIO_TRACKER_WM1110_ +#endif // _VARIANT_WIO_TRACKER_WM1110_ \ No newline at end of file From d1ff160256c802a5903b4dd392dd8149cc5e6158 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 3 Aug 2024 05:41:35 -0700 Subject: [PATCH 051/305] Generalize SWD debugging stuff so it works on all nrf52 targets. (#4377) * add bootloader install script for wio tracker 1110 board Mostly for documentation purposes for future devs. * Generalize nrf52 hw debugging support so it works on all nrf52 targets --- arch/nrf52/nrf52840.ini | 69 +++++++++++++++++++++ bin/wio_tracker_bootloader_update.bin | 30 ++++++++++ variants/rak4631/platformio.ini | 70 +--------------------- variants/wio-tracker-wm1110/platformio.ini | 3 +- 4 files changed, 101 insertions(+), 71 deletions(-) create mode 100644 bin/wio_tracker_bootloader_update.bin diff --git a/arch/nrf52/nrf52840.ini b/arch/nrf52/nrf52840.ini index cf08fd02e..a13a600f3 100644 --- a/arch/nrf52/nrf52840.ini +++ b/arch/nrf52/nrf52840.ini @@ -7,3 +7,72 @@ lib_deps = ${nrf52_base.lib_deps} ${environmental_base.lib_deps} https://github.com/Kongduino/Adafruit_nRFCrypto.git#e31a8825ea3300b163a0a3c1ddd5de34e10e1371 + +; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board. + +; We want the initial breakpoint at setup() instead of main(). Also we want to enable semihosting at that point so instead of +debug_init_break = tbreak setup +; we just turn off the platformio tbreak and do it in .gdbinit (where we have more flexibility for scripting) +; also we use a permanent breakpoint so it gets reused each time we restart the debugging session? +; debug_init_break = tbreak main + +; Note: add "monitor arm semihosting_redirect tcp 4444 all" if you want the stdout from the device to go to that port number instead +; (for use by meshtastic command line) +; monitor arm semihosting disable +; monitor debug_level 3 +; +; IMPORTANT: fileio must be disabled before using port 5555 - openocd ver 0.12 has a bug where if enabled it never properly parses the special :tt name +; for stdio access. +; monitor arm semihosting_redirect tcp 5555 stdio + +; Also note: it is _impossible_ to do non blocking reads on the semihost console port (an oversight when ARM specified the semihost API). +; So we'll neve be able to general purpose bi-directional communication with the device over semihosting. +debug_extra_cmds = + echo Running .gdbinit script + ;monitor arm semihosting enable + ;monitor arm semihosting_fileio enable + ;monitor arm semihosting_redirect disable + commands 1 + ; echo Breakpoint at setup() has semihosting console, connect to it with "telnet localhost 5555" + ; set wantSemihost = 1 + set useSoftDevice = 0 + end + + ; Only reprogram the board if the code has changed +debug_load_mode = modified +;debug_load_mode = manual +; We default to the stlink adapter because it is very cheap and works well, though others (such as jlink) are also supported. +;debug_tool = jlink +debug_tool = stlink +debug_speed = 4000 +;debug_tool = custom +; debug_server = +; openocd +; -f +; /usr/local/share/openocd/scripts/interface/stlink.cfg +; -f +; /usr/local/share/openocd/scripts/target/nrf52.cfg +; $PLATFORMIO_CORE_DIR/packages/tool-openocd/openocd/scripts/interface/cmsis-dap.cfg + +; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!) +; programming time is about the same as the bootloader version. +; For information on this see the meshtastic developers documentation for "Development on the NRF52" +; We manually pass in the elf file so that pyocd can reverse engineer FreeRTOS data (running threads, etc...) +;debug_server = +; pyocd +; gdbserver +; -j +; ${platformio.workspace_dir}/.. +; -t +; nrf52840 +; --semihosting +; --elf +; ${platformio.build_dir}/${this.__env__}/firmware.elf + +; If you want to debug the semihosting support you can turn on extra logging in pyocd with +; -L +; pyocd.debug.semihost.trace=debug + +; The following is not needed because it automatically tries do this +;debug_server_ready_pattern = -.*GDB server started on port \d+.* +;debug_port = localhost:3333 \ No newline at end of file diff --git a/bin/wio_tracker_bootloader_update.bin b/bin/wio_tracker_bootloader_update.bin new file mode 100644 index 000000000..57cd0ad17 --- /dev/null +++ b/bin/wio_tracker_bootloader_update.bin @@ -0,0 +1,30 @@ +# tips from mark on how to replace the (broken) bootloader on the red wio_tracker_1110 boards. + +~/.platformio/penv/bin/adafruit-nrfutil --verbose dfu serial --package wio_tracker_1110_bootloader-0.9.1_s140_7.3.0.zip -p /dev/ttyACM1 -b 115200 --singlebank --touch 1200 + +exit + +# Output should look like + +Upgrading target on /dev/ttyACM1 with DFU package /home/kevinh/development/meshtastic/WioWM1110/wio_tracker_1110_bootloader-0.9.1_s140_7.3.0.zip. Flow control is disabled, Single bank, Touch 1200 +Touched serial port /dev/ttyACM1 +Opened serial port /dev/ttyACM1 +Starting DFU upgrade of type 3, SoftDevice size: 152728, bootloader size: 39000, application size: 0 +Sending DFU start packet +Sending DFU init packet +Sending firmware file +######################################## +######################################## +######################################## +######################################## +######################################## +######################################## +######################################## +######################################## +######################################## +############### +Activating new firmware + +DFU upgrade took 20.242434978485107s +Device programmed. + diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 6a67b0083..8f1006eca 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -17,9 +17,6 @@ lib_deps = https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 https://github.com/meshtastic/RAK12034-BMX160.git#4821355fb10390ba8557dc43ca29a023bcfbb9d9 -debug_tool = jlink - - ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) ; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds @@ -52,69 +49,4 @@ lib_deps = upload_protocol = stlink ; eventually use platformio/tool-pyocd@^2.3600.0 instad ;upload_protocol = custom -;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE - -; We want the initial breakpoint at setup() instead of main(). Also we want to enable semihosting at that point so instead of -; debug_init_break = tbreak setup -; we just turn off the platformio tbreak and do it in .gdbinit (where we have more flexibility for scripting) -; also we use a permanent breakpoint so it gets reused each time we restart the debugging session? -debug_init_break = tbreak setup - -; Note: add "monitor arm semihosting_redirect tcp 4444 all" if you want the stdout from the device to go to that port number instead -; (for use by meshtastic command line) -; monitor arm semihosting disable -; monitor debug_level 3 -; -; IMPORTANT: fileio must be disabled before using port 5555 - openocd ver 0.12 has a bug where if enabled it never properly parses the special :tt name -; for stdio access. -; monitor arm semihosting_redirect tcp 5555 stdio - -; Also note: it is _impossible_ to do non blocking reads on the semihost console port (an oversight when ARM specified the semihost API). -; So we'll neve be able to general purpose bi-directional communication with the device over semihosting. -debug_extra_cmds = - echo Running .gdbinit script - monitor arm semihosting enable - monitor arm semihosting_fileio enable - monitor arm semihosting_redirect disable - commands 1 - echo Breakpoint at setup() has semihosting console, connect to it with "telnet localhost 5555" - set wantSemihost = true - set useSoftDevice = false - end - - -; Only reprogram the board if the code has changed -debug_load_mode = modified -;debug_load_mode = manual -debug_tool = stlink -;debug_tool = custom -; debug_server = -; openocd -; -f -; /usr/local/share/openocd/scripts/interface/stlink.cfg -; -f -; /usr/local/share/openocd/scripts/target/nrf52.cfg -; $PLATFORMIO_CORE_DIR/packages/tool-openocd/openocd/scripts/interface/cmsis-dap.cfg - -; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!) -; programming time is about the same as the bootloader version. -; For information on this see the meshtastic developers documentation for "Development on the NRF52" -; We manually pass in the elf file so that pyocd can reverse engineer FreeRTOS data (running threads, etc...) -;debug_server = -; pyocd -; gdbserver -; -j -; ${platformio.workspace_dir}/.. -; -t -; nrf52840 -; --semihosting -; --elf -; ${platformio.build_dir}/${this.__env__}/firmware.elf - -; If you want to debug the semihosting support you can turn on extra logging in pyocd with -; -L -; pyocd.debug.semihost.trace=debug - -; The following is not needed because it automatically tries do this -;debug_server_ready_pattern = -.*GDB server started on port \d+.* -;debug_port = localhost:3333 \ No newline at end of file +;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE \ No newline at end of file diff --git a/variants/wio-tracker-wm1110/platformio.ini b/variants/wio-tracker-wm1110/platformio.ini index b58b6291f..614fea588 100644 --- a/variants/wio-tracker-wm1110/platformio.ini +++ b/variants/wio-tracker-wm1110/platformio.ini @@ -9,6 +9,5 @@ board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/wio-tracker-wm1110> lib_deps = ${nrf52840_base.lib_deps} -debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -;upload_protocol = jlink +;upload_protocol = jlink \ No newline at end of file From 5453c495e2732442c5e34a9c9a0809971aaa338c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 3 Aug 2024 13:12:22 -0500 Subject: [PATCH 052/305] Actually set the rand() seed for Portduino (#4380) --- src/platform/portduino/PortduinoGlue.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index dc7a9ed61..f8dd79b20 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "PortduinoGlue.h" #include "linux/gpio/LinuxGPIOPin.h" @@ -134,6 +135,9 @@ void portduinoSetup() return; } + // Rather important to set this, if not running simulated. + randomSeed(time(NULL)); + try { if (yamlConfig["Logging"]) { if (yamlConfig["Logging"]["LogLevel"].as("info") == "trace") { @@ -382,4 +386,4 @@ int initGPIOPin(int pinNum, const std::string gpioChipName) #else return ERRNO_OK; #endif -} \ No newline at end of file +} From 5bbafdfd311f63d560d82a73bc1ff32b688bf37c Mon Sep 17 00:00:00 2001 From: Ken Piper Date: Sun, 4 Aug 2024 06:06:36 -0500 Subject: [PATCH 053/305] Configure pin modes of selected pins before attempting to write to them (#4385) --- src/modules/RemoteHardwareModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index d999e75cc..6910005a8 100644 --- a/src/modules/RemoteHardwareModule.cpp +++ b/src/modules/RemoteHardwareModule.cpp @@ -60,13 +60,13 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r // Print notification to LCD screen screen->print("Write GPIOs\n"); + pinModes(p.gpio_mask, OUTPUT); for (uint8_t i = 0; i < NUM_GPIOS; i++) { uint64_t mask = 1ULL << i; if (p.gpio_mask & mask) { digitalWrite(i, (p.gpio_value & mask) ? 1 : 0); } } - pinModes(p.gpio_mask, OUTPUT); break; } From 7d00e1cef9408eb0470b589b84554c611a583f8e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 4 Aug 2024 18:52:10 -0500 Subject: [PATCH 054/305] Output more useful log message when the NodeDB is full (#4389) --- src/mesh/NodeDB.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 1f66857f8..de63af08e 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1010,7 +1010,8 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) { if (screen) screen->print("Warn: node database full!\nErasing oldest entry\n"); - LOG_WARN("Node database full! Erasing oldest entry\n"); + LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes, + memGet.getFreeHeap()); // look for oldest node and erase it uint32_t oldest = UINT32_MAX; int oldestIndex = -1; From 40d6b99911e85eeebe3f64e43b1e81294688b521 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Mon, 5 Aug 2024 12:59:57 +0200 Subject: [PATCH 055/305] Add Minewsemi LR1110+nRF52840-ME25LS01 [both 4.2inch e-ink and non e-ink varaint] (#4387) * Add files via upload * Update EInkDisplay2.cpp * Add files via upload * Update platformio.ini * Update platformio.ini * Update platformio.ini * Update platformio.ini --- boards/me25ls01-4y10td.json | 58 +++++++ src/graphics/EInkDisplay2.cpp | 8 +- variants/ME25LS01-4Y10TD/platformio.ini | 15 ++ variants/ME25LS01-4Y10TD/variant.cpp | 40 +++++ variants/ME25LS01-4Y10TD/variant.h | 139 +++++++++++++++ variants/ME25LS01-4Y10TD_e-ink/platformio.ini | 19 ++ variants/ME25LS01-4Y10TD_e-ink/variant.cpp | 40 +++++ variants/ME25LS01-4Y10TD_e-ink/variant.h | 162 ++++++++++++++++++ 8 files changed, 477 insertions(+), 4 deletions(-) create mode 100644 boards/me25ls01-4y10td.json create mode 100644 variants/ME25LS01-4Y10TD/platformio.ini create mode 100644 variants/ME25LS01-4Y10TD/variant.cpp create mode 100644 variants/ME25LS01-4Y10TD/variant.h create mode 100644 variants/ME25LS01-4Y10TD_e-ink/platformio.ini create mode 100644 variants/ME25LS01-4Y10TD_e-ink/variant.cpp create mode 100644 variants/ME25LS01-4Y10TD_e-ink/variant.h diff --git a/boards/me25ls01-4y10td.json b/boards/me25ls01-4y10td.json new file mode 100644 index 000000000..46c526a7c --- /dev/null +++ b/boards/me25ls01-4y10td.json @@ -0,0 +1,58 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v7.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "ME25LS01-BOOT", + "mcu": "nrf52840", + "variant": "MINEWSEMI_ME25LS01", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "7.3.0", + "sd_fwid": "0x0123" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": ["arduino"], + "name": "Minesemi ME25LS01", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink", + "cmsis-dap", + "blackmagic" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://en.minewsemi.com/lora-module/lr1110-nrf52840-me25LS01l", + "vendor": "Minesemi" +} diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index d81ab6ff4..4b845bd51 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -174,13 +174,13 @@ bool EInkDisplay::connect() adafruitDisplay->init(); adafruitDisplay->setRotation(3); } -#elif defined(PCA10059) +#elif defined(PCA10059) || defined(ME25LS01) { auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); adafruitDisplay = new GxEPD2_BW(*lowLevel); - adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); - adafruitDisplay->setRotation(3); - adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); + adafruitDisplay->init(115200, true, 40, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); + adafruitDisplay->setRotation(0); + adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); } #elif defined(M5_COREINK) auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); diff --git a/variants/ME25LS01-4Y10TD/platformio.ini b/variants/ME25LS01-4Y10TD/platformio.ini new file mode 100644 index 000000000..985919d2d --- /dev/null +++ b/variants/ME25LS01-4Y10TD/platformio.ini @@ -0,0 +1,15 @@ +[env:ME25LS01-4Y10TD] +extends = nrf52840_base +board = me25ls01-4y10td +board_level = extra +; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e +build_flags = ${nrf52840_base.build_flags} -Ivariants/ME25LS01-4Y10TD -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD ;-DRADIOLIB_GODMODE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. +board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/ME25LS01-4Y10TD> +lib_deps = + ${nrf52840_base.lib_deps} +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +upload_protocol = nrfutil +upload_port = /dev/ttyACM1 diff --git a/variants/ME25LS01-4Y10TD/variant.cpp b/variants/ME25LS01-4Y10TD/variant.cpp new file mode 100644 index 000000000..35dc1d39b --- /dev/null +++ b/variants/ME25LS01-4Y10TD/variant.cpp @@ -0,0 +1,40 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); + + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); +} \ No newline at end of file diff --git a/variants/ME25LS01-4Y10TD/variant.h b/variants/ME25LS01-4Y10TD/variant.h new file mode 100644 index 000000000..24ed65b33 --- /dev/null +++ b/variants/ME25LS01-4Y10TD/variant.h @@ -0,0 +1,139 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_ME25LS01_4Y10TD_ +#define _VARIANT_ME25LS01_4Y10TD_ + +#define ME25LS01 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// Use the native nrf52 usb power detection +#define NRF_APM + +#define PIN_3V3_EN (32 + 5) //-1 +#define PIN_3V3_ACC_EN -1 + +#define PIN_LED1 (32 + 7) // P1.07 Blue D2 + +#define LED_PIN PIN_LED1 +#define LED_BUILTIN -1 + +#define LED_BLUE -1 +#define LED_STATE_ON 1 // State when LED is lit + +#define BUTTON_PIN (0 + 27) // P0.27 K3 +#define BUTTON_NEED_PULLUP + +#define HAS_WIRE 1 + +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (0 + 15) // P0.15 +#define PIN_WIRE_SCL (0 + 17) // P0.17 + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (0 + 14) // P0.14 +#define PIN_SERIAL1_TX (0 + 13) // P0.13 + +#define PIN_SERIAL2_RX (0 + 17) // P0.17 +#define PIN_SERIAL2_TX (0 + 16) // P0.16 + +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (0 + 29) // P0.20 // MISO +#define PIN_SPI_MOSI (0 + 2) // P0.02 // MOSI +#define PIN_SPI_SCK (32 + 15) // P1.15 // SCK +#define PIN_SPI_NSS (32 + 13) // P1.13 // NSS + +#define LORA_RESET (32 + 11) // P1.11 // RST +#define LORA_DIO1 (32 + 12) // P1.12 // IRQ +#define LORA_DIO2 (32 + 10) // P1.10 // BUSY +#define LORA_SCK PIN_SPI_SCK +#define LORA_MISO PIN_SPI_MISO +#define LORA_MOSI PIN_SPI_MOSI +#define LORA_CS PIN_SPI_NSS + +// supported modules list +#define USE_LR1110 + +#define LR1110_IRQ_PIN LORA_DIO1 +#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_BUSY_PIN LORA_DIO2 +#define LR1110_SPI_NSS_PIN LORA_CS +#define LR1110_SPI_SCK_PIN LORA_SCK +#define LR1110_SPI_MOSI_PIN LORA_MOSI +#define LR1110_SPI_MISO_PIN LORA_MISO + +#define LR11X0_DIO3_TCXO_VOLTAGE 1.6 +#define LR11X0_DIO_AS_RF_SWITCH +#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 + +#define HAS_GPS 0 + +#define PIN_GPS_EN -1 +#define GPS_EN_ACTIVE HIGH +#define PIN_GPS_RESET -1 +#define GPS_VRTC_EN -1 +#define GPS_SLEEP_INT -1 +#define GPS_RTC_INT -1 +#define GPS_RESETB_OUT -1 + +#define BATTERY_PIN -1 +#define ADC_MULTIPLIER (2.0F) + +#define ADC_RESOLUTION 14 +#define BATTERY_SENSE_RESOLUTION_BITS 12 + +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 + +// Buzzer +#define BUZZER_EN_PIN -1 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif // _VARIANT_ME25LS01_4Y10TD_ \ No newline at end of file diff --git a/variants/ME25LS01-4Y10TD_e-ink/platformio.ini b/variants/ME25LS01-4Y10TD_e-ink/platformio.ini new file mode 100644 index 000000000..4e837abd8 --- /dev/null +++ b/variants/ME25LS01-4Y10TD_e-ink/platformio.ini @@ -0,0 +1,19 @@ +[env:ME25LS01-4Y10TD_e-ink] +extends = nrf52840_base +board = me25ls01-4y10td +board_level = extra +; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e +build_flags = ${nrf52840_base.build_flags} -Ivariants/ME25LS01-4Y10TD_e-ink -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DME25LS01_4Y10TD ;-DRADIOLIB_GODMODE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DEINK_DISPLAY_MODEL=GxEPD2_420_GDEY042T81 + -DEINK_WIDTH=400 + -DEINK_HEIGHT=300 +board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/ME25LS01-4Y10TD_e-ink> +lib_deps = + ${nrf52840_base.lib_deps} + zinggjm/GxEPD2@^1.5.8 +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +upload_protocol = nrfutil +upload_port = /dev/ttyACM1 diff --git a/variants/ME25LS01-4Y10TD_e-ink/variant.cpp b/variants/ME25LS01-4Y10TD_e-ink/variant.cpp new file mode 100644 index 000000000..35dc1d39b --- /dev/null +++ b/variants/ME25LS01-4Y10TD_e-ink/variant.cpp @@ -0,0 +1,40 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); + + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); +} \ No newline at end of file diff --git a/variants/ME25LS01-4Y10TD_e-ink/variant.h b/variants/ME25LS01-4Y10TD_e-ink/variant.h new file mode 100644 index 000000000..7c7740505 --- /dev/null +++ b/variants/ME25LS01-4Y10TD_e-ink/variant.h @@ -0,0 +1,162 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_ME25LS01_4Y10TD_ +#define _VARIANT_ME25LS01_4Y10TD_ + +#define ME25LS01 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// Use the native nrf52 usb power detection +#define NRF_APM + +#define PIN_3V3_EN (32 + 5) //-1 +#define PIN_3V3_ACC_EN -1 + +#define PIN_LED1 (32 + 7) // P1.07 Blue D2 + +#define LED_PIN PIN_LED1 +#define LED_BUILTIN -1 + +#define LED_BLUE -1 +#define LED_STATE_ON 1 // State when LED is lit + +#define BUTTON_PIN (0 + 27) // P0.27 K3 +#define BUTTON_NEED_PULLUP + +#define HAS_WIRE 1 + +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (0 + 15) // P0.15 +#define PIN_WIRE_SCL (0 + 17) // P0.17 + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (0 + 14) // P0.14 +#define PIN_SERIAL1_TX (0 + 13) // P0.13 + +#define PIN_SERIAL2_RX (0 + 17) // P0.17 +#define PIN_SERIAL2_TX (0 + 16) // P0.16 + +#define SPI_INTERFACES_COUNT 2 + +// LoRa SPI +#define PIN_SPI_MISO (0 + 29) // P0.20 // MISO +#define PIN_SPI_MOSI (0 + 2) // P0.02 // MOSI +#define PIN_SPI_SCK (32 + 15) // P1.15 // SCK +#define PIN_SPI_NSS (32 + 13) // P1.13 // NSS + +#define LORA_RESET (32 + 11) // P1.11 // RST +#define LORA_DIO1 (32 + 12) // P1.12 // IRQ +#define LORA_DIO2 (32 + 10) // P1.10 // BUSY +#define LORA_SCK PIN_SPI_SCK +#define LORA_MISO PIN_SPI_MISO +#define LORA_MOSI PIN_SPI_MOSI +#define LORA_CS PIN_SPI_NSS + +static const uint8_t SS = (32 + 13); // P1.13 // NSS +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// EPD SPI +#define PIN_SPI1_MISO (-1) // Not Used for EPD +#define PIN_SPI1_MOSI (0 + 10) // EPD_MOSI P0.10 +#define PIN_SPI1_SCK (0 + 9) // EPD_SCLK P0.09 + +/* + * eink display pins + */ + +#define USE_EINK +#define PIN_EINK_CS (32 + 0) // EPD_CS +#define PIN_EINK_BUSY (0 + 19) // EPD_BUSY +#define PIN_EINK_DC (0 + 24) // EPD_D/C +#define PIN_EINK_RES (0 + 23) // EPD_RESET +#define PIN_EINK_SCLK (0 + 9) // EPD_SCLK +#define PIN_EINK_MOSI (0 + 10) // EPD_MOSI + +// supported modules list +#define USE_LR1110 + +#define LR1110_IRQ_PIN LORA_DIO1 +#define LR1110_NRESER_PIN LORA_RESET +#define LR1110_BUSY_PIN LORA_DIO2 +#define LR1110_SPI_NSS_PIN LORA_CS +#define LR1110_SPI_SCK_PIN LORA_SCK +#define LR1110_SPI_MOSI_PIN LORA_MOSI +#define LR1110_SPI_MISO_PIN LORA_MISO + +#define LR11X0_DIO3_TCXO_VOLTAGE 1.6 +#define LR11X0_DIO_AS_RF_SWITCH +#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 + +#define HAS_GPS 0 + +#define PIN_GPS_EN -1 +#define GPS_EN_ACTIVE HIGH +#define PIN_GPS_RESET -1 +#define GPS_VRTC_EN -1 +#define GPS_SLEEP_INT -1 +#define GPS_RTC_INT -1 +#define GPS_RESETB_OUT -1 + +#define BATTERY_PIN -1 +#define ADC_MULTIPLIER (2.0F) + +#define ADC_RESOLUTION 14 +#define BATTERY_SENSE_RESOLUTION_BITS 12 + +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 + +// Buzzer +#define BUZZER_EN_PIN -1 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif // _VARIANT_ME25LS01_4Y10TD__ \ No newline at end of file From 1a38c4e51d2d0d8799130c199f94c667f7132d94 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 5 Aug 2024 04:02:32 -0700 Subject: [PATCH 056/305] Remove LED_INVERTED, see below for why ;-) (#4382) While working on #4378 I noticed a funny problem: the blinking system LED was on during deep-sleep. Initially I thought it was some weird sleep hw config thing but it turns out it was easier but more pervasive. We had two different preprocessor symbols which both meant approximately the same thing LED_INVERTED and LED_STATE_ON (though their polarity was opposite). Some variant files were setting one, others were setting the other, and others were setting both. heh. In the case of the board I was testing (seeed tracker wio 1100) it was only setting one and the default behavior for the other (for all boards) was incorrect. So I did a grep and it seems like LED_STATE_ON was used more often, so I kept that one and removed LED_INVERTED everywhere. --- src/DebugConfiguration.h | 4 ++-- src/main.cpp | 4 ++-- src/modules/AdminModule.cpp | 2 +- src/platform/nrf52/architecture.h | 10 +++++----- src/sleep.cpp | 4 ++-- variants/EBYTE_ESP32-S3/variant.h | 2 +- variants/canaryone/variant.h | 1 - variants/heltec_esp32c3/variant.h | 6 +++--- variants/heltec_mesh_node_t114/variant.h | 1 - variants/m5stack_coreink/variant.h | 4 ++-- variants/nano-g2-ultra/variant.h | 1 - variants/t-echo/variant.h | 1 - variants/tbeam-s3-core/variant.h | 4 ++-- variants/tbeam/variant.h | 4 ++-- variants/unphone/variant.h | 4 ++-- 15 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index ebe9da8d4..f5b3fa25a 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -3,8 +3,8 @@ #include "configuration.h" // DEBUG LED -#ifndef LED_INVERTED -#define LED_INVERTED 0 // define as 1 if LED is active low (on) +#ifndef LED_STATE_ON +#define LED_STATE_ON 1 #endif // ----------------------------------------------------------------------------- diff --git a/src/main.cpp b/src/main.cpp index 561b24a34..64fe04f00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -583,7 +583,7 @@ void setup() #ifdef LED_PIN pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now + digitalWrite(LED_PIN, LED_STATE_ON); // turn on for now #endif // Hello @@ -728,7 +728,7 @@ void setup() #ifdef LED_PIN // Turn LED off after boot, if heartbeat by config if (config.device.led_heartbeat_disabled) - digitalWrite(LED_PIN, LOW ^ LED_INVERTED); + digitalWrite(LED_PIN, HIGH ^ LED_STATE_ON); #endif // Do this after service.init (because that clears error_code) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 26ddab0db..8287f8a00 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -372,7 +372,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) #ifdef LED_PIN // Turn LED off if heartbeat by config if (c.payload_variant.device.led_heartbeat_disabled) { - digitalWrite(LED_PIN, LOW ^ LED_INVERTED); + digitalWrite(LED_PIN, HIGH ^ LED_STATE_ON); } #endif if (config.device.button_gpio == c.payload_variant.device.button_gpio && diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 2b285ec2e..d5685d611 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -75,16 +75,16 @@ #ifdef ARDUINO_NRF52840_PCA10056 // This board uses 0 to be mean LED on -#undef LED_INVERTED -#define LED_INVERTED 1 +#undef LED_STATE_ON +#define LED_STATE_ON 0 // State when LED is lit #endif #ifdef _SEEED_XIAO_NRF52840_SENSE_H_ // This board uses 0 to be mean LED on -#undef LED_INVERTED -#define LED_INVERTED 1 +#undef LED_STATE_ON +#define LED_STATE_ON 0 // State when LED is lit #endif @@ -116,4 +116,4 @@ #if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA) // No serial ports on this board - ONLY use segger in memory console #define USE_SEGGER -#endif +#endif \ No newline at end of file diff --git a/src/sleep.cpp b/src/sleep.cpp index 4e685563a..486d22dfe 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -90,7 +90,7 @@ void setLed(bool ledOn) #ifdef LED_PIN // toggle the led so we can get some rough sense of how often loop is pausing - digitalWrite(LED_PIN, ledOn ^ LED_INVERTED); + digitalWrite(LED_PIN, ledOn ^ LED_STATE_ON ^ HIGH); #endif #ifdef HAS_PMU @@ -519,4 +519,4 @@ void enableLoraInterrupt() } #endif } -#endif +#endif \ No newline at end of file diff --git a/variants/EBYTE_ESP32-S3/variant.h b/variants/EBYTE_ESP32-S3/variant.h index 10b39617b..80fb26434 100644 --- a/variants/EBYTE_ESP32-S3/variant.h +++ b/variants/EBYTE_ESP32-S3/variant.h @@ -101,7 +101,7 @@ // Status #define LED_PIN 1 -#define LED_INVERTED 0 +#define LED_STATE_ON 1 // State when LED is lit // External notification // FIXME: Check if EXT_NOTIFY_OUT actualy has any effect and removes the need for setting the external notication pin in the // app/preferences diff --git a/variants/canaryone/variant.h b/variants/canaryone/variant.h index d283f08f6..140b605ad 100644 --- a/variants/canaryone/variant.h +++ b/variants/canaryone/variant.h @@ -56,7 +56,6 @@ extern "C" { #define LED_CONN PIN_LED3 #define LED_STATE_ON 0 // State when LED is lit -#define LED_INVERTED 1 /* * Buttons diff --git a/variants/heltec_esp32c3/variant.h b/variants/heltec_esp32c3/variant.h index 360d9bf1f..ca00c43fa 100644 --- a/variants/heltec_esp32c3/variant.h +++ b/variants/heltec_esp32c3/variant.h @@ -3,8 +3,8 @@ // LED pin on HT-DEV-ESP_V2 and HT-DEV-ESP_V3 // https://resource.heltec.cn/download/HT-CT62/HT-CT62_Reference_Design.pdf // https://resource.heltec.cn/download/HT-DEV-ESP/HT-DEV-ESP_V3_Sch.pdf -#define LED_PIN 2 // LED -#define LED_INVERTED 0 +#define LED_PIN 2 // LED +#define LED_STATE_ON 1 // State when LED is lit #define HAS_SCREEN 0 #define HAS_GPS 0 @@ -26,4 +26,4 @@ #define SX126X_BUSY LORA_BUSY #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 \ No newline at end of file diff --git a/variants/heltec_mesh_node_t114/variant.h b/variants/heltec_mesh_node_t114/variant.h index 615526b22..8df59e394 100644 --- a/variants/heltec_mesh_node_t114/variant.h +++ b/variants/heltec_mesh_node_t114/variant.h @@ -80,7 +80,6 @@ extern "C" { #define LED_CONN PIN_GREEN #define LED_STATE_ON 0 // State when LED is lit -#define LED_INVERTED 1 /* * Buttons diff --git a/variants/m5stack_coreink/variant.h b/variants/m5stack_coreink/variant.h index f19da2696..ecd93b7be 100644 --- a/variants/m5stack_coreink/variant.h +++ b/variants/m5stack_coreink/variant.h @@ -10,7 +10,7 @@ // #define GPS_TX_PIN 32 (now used by SX1262 BUSY as GPS works with just RX) // Green LED -#define LED_INVERTED 0 +#define LED_STATE_ON 1 // State when LED is lit #define LED_PIN 10 #include "pcf8563.h" @@ -106,4 +106,4 @@ // GND // https://github.com/m5stack/M5Core-Ink/blob/master/examples/Basics/FactoryTest/FactoryTest.ino#L58 #define ADC_MULTIPLIER 5 -// https://embeddedexplorer.com/esp32-adc-esp-idf-tutorial/ +// https://embeddedexplorer.com/esp32-adc-esp-idf-tutorial/ \ No newline at end of file diff --git a/variants/nano-g2-ultra/variant.h b/variants/nano-g2-ultra/variant.h index 4d8aa5784..fd51cf9a1 100644 --- a/variants/nano-g2-ultra/variant.h +++ b/variants/nano-g2-ultra/variant.h @@ -54,7 +54,6 @@ extern "C" { #define LED_CONN PIN_GREEN #define LED_STATE_ON 0 // State when LED is lit -// #define LED_INVERTED 1 /* * Buttons diff --git a/variants/t-echo/variant.h b/variants/t-echo/variant.h index 1c263a61a..9abb4ea69 100644 --- a/variants/t-echo/variant.h +++ b/variants/t-echo/variant.h @@ -56,7 +56,6 @@ extern "C" { #define LED_CONN PIN_GREEN #define LED_STATE_ON 0 // State when LED is lit -#define LED_INVERTED 1 /* * Buttons diff --git a/variants/tbeam-s3-core/variant.h b/variants/tbeam-s3-core/variant.h index 0fa9f8fe8..cc706459f 100644 --- a/variants/tbeam-s3-core/variant.h +++ b/variants/tbeam-s3-core/variant.h @@ -11,7 +11,7 @@ // anywhere. // #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module. -#define LED_INVERTED 1 +#define LED_STATE_ON 0 // State when LED is lit // TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if // not found then probe for SX1262 @@ -66,4 +66,4 @@ // has 32768 Hz crystal #define HAS_32768HZ -#define USE_SH1106 +#define USE_SH1106 \ No newline at end of file diff --git a/variants/tbeam/variant.h b/variants/tbeam/variant.h index d237f542b..8771c20d2 100644 --- a/variants/tbeam/variant.h +++ b/variants/tbeam/variant.h @@ -8,8 +8,8 @@ // anywhere. #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module. -#define LED_INVERTED 1 -#define LED_PIN 4 // Newer tbeams (1.1) have an extra led on GPIO4 +#define LED_STATE_ON 0 // State when LED is lit +#define LED_PIN 4 // Newer tbeams (1.1) have an extra led on GPIO4 // TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if // not found then probe for SX1262 diff --git a/variants/unphone/variant.h b/variants/unphone/variant.h index 7d5c30f79..0a94c5987 100644 --- a/variants/unphone/variant.h +++ b/variants/unphone/variant.h @@ -51,8 +51,8 @@ // #define HAS_SDCARD 1 // causes hang if defined #define SDCARD_CS 43 -#define LED_PIN 13 // the red part of the RGB LED -#define LED_INVERTED 1 +#define LED_PIN 13 // the red part of the RGB LED +#define LED_STATE_ON 0 // State when LED is lit #define BUTTON_PIN 21 // Button 3 - square - top button in landscape mode #define BUTTON_NEED_PULLUP // we do need a helping hand up From 9ddfc6de4c45918bd07a52885c631d93276b347d Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 5 Aug 2024 14:02:54 +0300 Subject: [PATCH 057/305] Commented RF95(1276) as no needed right now (#4386) --- variants/diy/nrf52_promicro_diy_tcxo/variant.h | 2 +- variants/diy/nrf52_promicro_diy_xtal/variant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index bacc0796d..b09d3bdb4 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -115,7 +115,7 @@ NRF52 PRO MICRO PIN ASSIGNMENT // LORA MODULES #define USE_LLCC68 #define USE_SX1262 -#define USE_RF95 +// #define USE_RF95 #define USE_SX1268 // LORA CONFIG diff --git a/variants/diy/nrf52_promicro_diy_xtal/variant.h b/variants/diy/nrf52_promicro_diy_xtal/variant.h index c00c424cc..7aafab7da 100644 --- a/variants/diy/nrf52_promicro_diy_xtal/variant.h +++ b/variants/diy/nrf52_promicro_diy_xtal/variant.h @@ -114,7 +114,7 @@ NRF52 PRO MICRO PIN ASSIGNMENT // LORA MODULES #define USE_LLCC68 #define USE_SX1262 -#define USE_RF95 +// #define USE_RF95 #define USE_SX1268 // LORA CONFIG From d8f3c3324c95b370db3054b874e6550f7d0431f0 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 5 Aug 2024 04:47:04 -0700 Subject: [PATCH 058/305] Make lora radio reset reliable on wio-tracker-1100 and lower lr11x0 power consumption in sleep (#4383) * Fix wio-tracker-1110 lora radio reset GPIO assignment This fixes flaky lora radio init on this board. * No need to keep lr11x0 radio config during sleep anymore, also stop TCXO I think the problem (at least on the board I'm using for power testing a wio tracker 1110) was that actually the RESET GPIO was not correct for the radio. This led to the radio not being properly reinited after exiting sleep mode. Now that the GPIO is fixed I can enter deep sleep (fully shutting down radio) and then later when the CPU resets, it can successfully init the radio and send packets. After this seeming success, I also turned off the TCXO during sleep and that worked as well. --- src/mesh/LR11x0Interface.cpp | 8 +++----- variants/wio-tracker-wm1110/variant.h | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 1965eef89..13b32533e 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -285,17 +285,15 @@ template bool LR11x0Interface::isActivelyReceiving() template bool LR11x0Interface::sleep() { - // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet // \todo Display actual typename of the adapter, not just `LR11x0` - LOG_DEBUG("LR11x0 entering sleep mode (FIXME, don't keep config)\n"); + LOG_DEBUG("LR11x0 entering sleep mode\n"); setStandby(); // Stop any pending operations // turn off TCXO if it was powered - // FIXME - this isn't correct - // lora.setTCXO(0); + lora.setTCXO(0); // put chipset into sleep mode (we've already disabled interrupts by now) - bool keepConfig = true; + bool keepConfig = false; lora.sleep(keepConfig, 0); // Note: we do not keep the config, full reinit will be needed #ifdef LR11X0_POWER_EN diff --git a/variants/wio-tracker-wm1110/variant.h b/variants/wio-tracker-wm1110/variant.h index 2bb2f1a59..de7da9911 100644 --- a/variants/wio-tracker-wm1110/variant.h +++ b/variants/wio-tracker-wm1110/variant.h @@ -79,9 +79,9 @@ extern "C" { #define PIN_SPI_SCK (32 + 13) // P1.13 45 #define PIN_SPI_NSS (32 + 12) // P1.12 44 -#define LORA_RESET (0 + 18) // P0.18 18 // RST -#define LORA_DIO1 (0 + 2) // P0.02 2 // IRQ -#define LORA_DIO2 (32 + 11) // P1.11 43 // BUSY +#define LORA_RESET (32 + 10) // P1.10 10 // RST +#define LORA_DIO1 (0 + 2) // P0.02 2 // IRQ +#define LORA_DIO2 (32 + 11) // P1.11 43 // BUSY #define LORA_SCK PIN_SPI_SCK #define LORA_MISO PIN_SPI_MISO #define LORA_MOSI PIN_SPI_MOSI From e509a9101996d737f8db0cccf2f6aaa7d1b168a9 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Mon, 5 Aug 2024 23:00:32 +0800 Subject: [PATCH 059/305] Fix UC6580 ifdefs (#4393) meshtastic/firmware#4319 added autodetect code for UC6580, and removed these ifdefs. However, they were inadvertantly re-added by #4328 . --- src/gps/GPS.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index d6ea2cb08..0d937ae63 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -400,8 +400,6 @@ bool GPS::setup() int msglen = 0; if (!didSerialInit) { -#if !defined(GPS_UC6580) - if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { // if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate. @@ -424,9 +422,6 @@ bool GPS::setup() } else { gnssModel = GNSS_MODEL_UNKNOWN; } -#else - gnssModel = GNSS_MODEL_UC6580; -#endif if (gnssModel == GNSS_MODEL_MTK) { /* @@ -1797,4 +1792,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS \ No newline at end of file +#endif // Exclude GPS From 66a4632f34028208921c48408cc089faebdf7671 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Mon, 5 Aug 2024 23:00:52 +0800 Subject: [PATCH 060/305] Fix python call in UF2 generation. (#4392) The call to generate UF2 files in the platformio custom script was a bare call to python. In some environments, this command won't exist in this way. Instead, use the standard env approach to find the right python. Additionally, add the shebang line on line 1 so this script can be executed standalone if needed. --- bin/platformio-custom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 4f7ce55d0..b22d76098 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # trunk-ignore-all(ruff/F821) # trunk-ignore-all(flake8/F821): For SConstruct imports import sys @@ -80,7 +81,7 @@ if platform.name == "espressif32": if platform.name == "nordicnrf52": env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", - env.VerboseAction(f"python ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", + env.VerboseAction(f"/usr/bin/env python3 ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", "Generating UF2 file")) Import("projenv") @@ -95,4 +96,4 @@ projenv.Append( "-DAPP_VERSION=" + verObj["long"], "-DAPP_VERSION_SHORT=" + verObj["short"], ] -) \ No newline at end of file +) From 02231fd487c588683629cb0ac158550f8c4149b8 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 5 Aug 2024 15:07:43 -0700 Subject: [PATCH 061/305] fix minor type comparison warning that I saw in the build (#4398) --- src/mesh/compression/unishox2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/compression/unishox2.cpp b/src/mesh/compression/unishox2.cpp index fcb12a222..4e239d489 100644 --- a/src/mesh/compression/unishox2.cpp +++ b/src/mesh/compression/unishox2.cpp @@ -1096,7 +1096,7 @@ int decodeRepeat(const char *in, int len, char *out, int olen, int ol, int *bit_ return -1; if (left <= 0) return olen + 1; - if (dist >= strlen(cur_line->data)) + if ((size_t)dist >= strlen(cur_line->data)) return -1; memmove(out + ol, cur_line->data + dist, min_of(left, dict_len)); if (left < dict_len) @@ -1289,7 +1289,7 @@ int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(c if (usx_templates[idx] == NULL) break; size_t tlen = strlen(usx_templates[idx]); - if (rem > tlen) + if ((size_t)rem > tlen) break; rem = tlen - rem; int eof = 0; From 1f458d6397f259b8c305d6254a31e162da96761b Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 6 Aug 2024 08:25:47 +0800 Subject: [PATCH 062/305] Make UF2 build command windows-friendly (#4399) As reported by @mrekin, the previous changes to the platformio custom build script may not work on windows. Change to use python3 instead of a call to /usr/bin/env python3. --- bin/platformio-custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index b22d76098..bfcee897a 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -81,7 +81,7 @@ if platform.name == "espressif32": if platform.name == "nordicnrf52": env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", - env.VerboseAction(f"/usr/bin/env python3 ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", + env.VerboseAction(f"python3 ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", "Generating UF2 file")) Import("projenv") From 4a79a690dbb6414333d6947af0b8d82ac5a854d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 20:14:07 -0500 Subject: [PATCH 063/305] [create-pull-request] automated change (#4400) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/protobufs b/protobufs index 976748839..d0fe91ab9 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 976748839fafcf0049bb364fe2c7226a194d18a9 +Subproject commit d0fe91ab99734cacdc188403f73fe30f766917cf diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index ca860aed5..59664b792 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -228,7 +228,14 @@ typedef enum _meshtastic_CriticalErrorCode { meshtastic_CriticalErrorCode_SX1262_FAILURE = 10, /* A (likely software but possibly hardware) failure was detected while trying to send packets. If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug */ - meshtastic_CriticalErrorCode_RADIO_SPI_BUG = 11 + meshtastic_CriticalErrorCode_RADIO_SPI_BUG = 11, + /* Corruption was detected on the flash filesystem but we were able to repair things. + If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. */ + meshtastic_CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE = 12, + /* Corruption was detected on the flash filesystem but we were unable to repair things. + NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...) + If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. */ + meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE = 13 } meshtastic_CriticalErrorCode; /* How the location was acquired: manual, onboard GPS, external (EUD) GPS */ @@ -931,8 +938,8 @@ extern "C" { #define _meshtastic_Constants_ARRAYSIZE ((meshtastic_Constants)(meshtastic_Constants_DATA_PAYLOAD_LEN+1)) #define _meshtastic_CriticalErrorCode_MIN meshtastic_CriticalErrorCode_NONE -#define _meshtastic_CriticalErrorCode_MAX meshtastic_CriticalErrorCode_RADIO_SPI_BUG -#define _meshtastic_CriticalErrorCode_ARRAYSIZE ((meshtastic_CriticalErrorCode)(meshtastic_CriticalErrorCode_RADIO_SPI_BUG+1)) +#define _meshtastic_CriticalErrorCode_MAX meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE +#define _meshtastic_CriticalErrorCode_ARRAYSIZE ((meshtastic_CriticalErrorCode)(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE+1)) #define _meshtastic_Position_LocSource_MIN meshtastic_Position_LocSource_LOC_UNSET #define _meshtastic_Position_LocSource_MAX meshtastic_Position_LocSource_LOC_EXTERNAL From 06eaf2ba5d78a14e9a101db578999e6f01f76278 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 6 Aug 2024 19:48:05 +0800 Subject: [PATCH 064/305] Use sys.executable to refer to python. (#4402) Thanks to @mrekin for testing the build on Windows. The previous fix for this UF2 call did not work. sys.executable should fix it. --- bin/platformio-custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index bfcee897a..0f099c09a 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -81,7 +81,7 @@ if platform.name == "espressif32": if platform.name == "nordicnrf52": env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", - env.VerboseAction(f"python3 ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", + env.VerboseAction(f"{sys.executable} ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2", "Generating UF2 file")) Import("projenv") From c1870f91fcdb718a79ea5c3e46fdc76d68503c86 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 6 Aug 2024 10:35:54 -0700 Subject: [PATCH 065/305] Finish powermon/powerstress (#4230) * Turn off vscode cmake prompt - we don't use cmake on meshtastic * Add rak4631_dap variant for debugging with NanoDAP debug probe device. * The rak device can also run freertos (which is underneath nrf52 arduino) * Add semihosting support for nrf52840 devices Initial platformio.ini file only supports rak4630 Default to non TCP for the semihosting log output for now... Fixes https://github.com/meshtastic/firmware/issues/4135 * powermon WIP (for https://github.com/meshtastic/firmware/issues/4136 ) * oops - mean't to mark the _dbg variant as an 'extra' board. * powermon wip * Make serial port on wio-sdk-wm1110 board work By disabling the (inaccessible) adafruit USB * Instrument (radiolib only for now) lora for powermon per https://github.com/meshtastic/firmware/issues/4136 * powermon gps support https://github.com/meshtastic/firmware/issues/4136 * Add CPU deep and light sleep powermon states https://github.com/meshtastic/firmware/issues/4136 * Change the board/swversion bootstring so it is a new "structured" log msg. * powermon wip * add example script for getting esp S3 debugging working Not yet used but I didn't want these nasty tricks to get lost yet. * Add PowerMon reporting for screen and bluetooth pwr. * make power.powermon_enables config setting work. * update to latest protobufs * fix bogus shellcheck warning * make powermon optional (but default enabled because tiny and no runtime impact) * tell vscode, if formatting, use whatever our trunk formatter wants without this flag if the user has set some other formatter (clang) in their user level settings, it will be looking in the wrong directory for the clang options (we want the options in .trunk/clang) Note: formatOnSave is true in master, which means a bunch of our older files are non compliant and if you edit them it will generate lots of formatting related diffs. I guess I'll start letting that happen with my future commits ;-). * add PowerStress module * nrf52 arduino is built upon freertos, so let platformio debug it * don't accidentally try to Segger ICE if we are using another ICE * clean up RedirectablePrint::log so it doesn't have three very different implementations inline. * remove NoopPrint - it is no longer needed * when talking to API clients via serial, don't turn off log msgs instead encapsuate them * fix the build - would loop forever if there were no files to send * don't use Segger code if not talking to a Segger debugger * when encapsulating logs, make sure the strings always has nul terminators * nrf52 soft device will watchdog if you use ICE while BT on... so have debugger disable bluetooth. * Important to not print debug messages while writing to the toPhone scratch buffer * don't include newlines if encapsulating log records as protobufs * update to latest protobufs (needed for powermon goo) * PowerStress WIP * for #4154 and #4136 add concept of dependent gpios... Which is currently only tested with the LED but eventually will be used for shared GPIO/screen power rail enable and LED forcing (which is a sanity check in the power stress testing) * fix linter warning * Transformer is a better name for the LED input > operation > output classes * PMW led changes to work on esp32-s3 * power stress improvements * allow ble logrecords to be fetched either by NOTIFY or INDICATE ble types This allows 'lossless' log reading. If client has requested INDICATE (rather than NOTIFY) each log record emitted via log() will have to fetched by the client device before the meshtastic node can continue. * Fix serious problem with nrf52 BLE logging. When doing notifies of LogRecords it is important to use the binary write routines - writing using the 'string' write won't work. Because protobufs can contain \0 nuls inside of them which if being parsed as a string will cause only a portion of the protobuf to be sent. I noticed this because some log messages were not getting through. * fix gpio transformer stuff to work correctly with LED_INVERTED Thanks @todd-herbert for noticing this and the great stack trace. The root cause was that I had accidentially shadowed outPin in a subclass with an unneeded override. It would break on any board that had inverted LED power. fixes https://github.com/meshtastic/firmware/pull/4230#pullrequestreview-2217389099 * Support driving multiple output gpios from one input. While investigating https://github.com/meshtastic/firmware/pull/4230#pullrequestreview-2217389099 I noticed in variant.h that there are now apparently newer TBEAMs than mine that have _both_ a GPIO based power LED and the PMU based LED. Add a splitter so that we can drive two output GPIOs from one logical signal. --------- Co-authored-by: Ben Meadors --- src/GpioLogic.cpp | 84 +++++++++++++++ src/GpioLogic.h | 144 ++++++++++++++++++++++++++ src/Led.cpp | 66 ++++++++++++ src/Led.h | 7 ++ src/Power.cpp | 3 - src/PowerFSM.cpp | 21 +--- src/PowerMon.cpp | 6 +- src/PowerMon.h | 10 ++ src/graphics/Screen.cpp | 3 + src/main.cpp | 3 +- src/mesh/http/ContentHandler.cpp | 6 +- src/modules/PowerStressModule.cpp | 80 +++++++++++--- src/platform/esp32/main-esp32.cpp | 2 + src/platform/nrf52/main-nrf52.cpp | 7 +- src/power.h | 7 ++ src/sleep.cpp | 25 +---- src/sleep.h | 1 - variants/tbeam-s3-core/pins_arduino.h | 16 +-- 18 files changed, 422 insertions(+), 69 deletions(-) create mode 100644 src/GpioLogic.cpp create mode 100644 src/GpioLogic.h create mode 100644 src/Led.cpp create mode 100644 src/Led.h diff --git a/src/GpioLogic.cpp b/src/GpioLogic.cpp new file mode 100644 index 000000000..d164615a7 --- /dev/null +++ b/src/GpioLogic.cpp @@ -0,0 +1,84 @@ +#include "GpioLogic.h" +#include + +void GpioVirtPin::set(bool value) +{ + if (value != this->value) { + this->value = value ? PinState::On : PinState::Off; + if (dependentPin) + dependentPin->update(); + } +} + +GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {} + +void GpioTransformer::set(bool value) +{ + outPin->set(value); +} + +GpioNotTransformer::GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin) +{ + assert(!inPin->dependentPin); // We only allow one dependent pin + inPin->dependentPin = this; + + // Don't update at construction time, because various GpioPins might be global constructor based not yet initied because + // order of operations for global constructors is not defined. + // update(); +} + +/** + * Update the output pin based on the current state of the input pin. + */ +void GpioNotTransformer::update() +{ + auto p = inPin->get(); + if (p == GpioVirtPin::PinState::Unset) + return; // Not yet fully initialized + + set(!p); +} + +GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation) + : GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation) +{ + assert(!inPin1->dependentPin); // We only allow one dependent pin + inPin1->dependentPin = this; + assert(!inPin2->dependentPin); // We only allow one dependent pin + inPin2->dependentPin = this; + + // Don't update at construction time, because various GpioPins might be global constructor based not yet initied because + // order of operations for global constructors is not defined. + // update(); +} + +void GpioBinaryTransformer::update() +{ + auto p1 = inPin1->get(), p2 = inPin2->get(); + GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset; + + if (p1 == GpioVirtPin::PinState::Unset) + newValue = p2; // Not yet fully initialized + else if (p2 == GpioVirtPin::PinState::Unset) + newValue = p1; // Not yet fully initialized + + // If we've already found our value just use it, otherwise need to do the operation + if (newValue == GpioVirtPin::PinState::Unset) { + switch (operation) { + case And: + newValue = (GpioVirtPin::PinState)(p1 && p2); + break; + case Or: + newValue = (GpioVirtPin::PinState)(p1 || p2); + break; + case Xor: + newValue = (GpioVirtPin::PinState)(p1 != p2); + break; + default: + assert(false); + } + } + set(newValue); +} + +GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {} \ No newline at end of file diff --git a/src/GpioLogic.h b/src/GpioLogic.h new file mode 100644 index 000000000..27e85d55b --- /dev/null +++ b/src/GpioLogic.h @@ -0,0 +1,144 @@ +#pragma once + +#include "configuration.h" + +/**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not + require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared power enable) + then using these classes might be able to let you cleanly turn on that enable when either dependent device is needed. + + Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM + requirements. +*/ + +/** + * A logical GPIO pin (not necessary raw hardware). + */ +class GpioPin +{ + public: + virtual void set(bool value) = 0; +}; + +/** + * A physical GPIO hw pin. + */ +class GpioHwPin : public GpioPin +{ + uint32_t num; + + public: + explicit GpioHwPin(uint32_t num) : num(num) {} + + void set(bool value) { digitalWrite(num, value); } +}; + +class GpioTransformer; +class GpioNotTransformer; +class GpioBinaryTransformer; + +/** + * A virtual GPIO pin. + */ +class GpioVirtPin : public GpioPin +{ + friend class GpioBinaryTransformer; + friend class GpioNotTransformer; + + public: + enum PinState { On = true, Off = false, Unset = 2 }; + + void set(bool value); + PinState get() const { return value; } + + private: + PinState value = PinState::Unset; + GpioTransformer *dependentPin = NULL; +}; + +#include + +/** + * A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to change. + * notably: the set method is not public (because it always is calculated by a subclass) + */ +class GpioTransformer +{ + public: + /** + * Update the output pin based on the current state of the input pin. + */ + virtual void update() = 0; + + protected: + GpioTransformer(GpioPin *outPin); + + void set(bool value); + + private: + GpioPin *outPin; +}; + +/** + * A transformer that performs a unary NOT operation from an input. + */ +class GpioNotTransformer : public GpioTransformer +{ + public: + GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin); + + protected: + friend class GpioVirtPin; + + /** + * Update the output pin based on the current state of the input pin. + */ + void update(); + + private: + GpioVirtPin *inPin; +}; + +/** + * A transformer that combines multiple virtual pins to drive an output pin + */ +class GpioBinaryTransformer : public GpioTransformer +{ + + public: + enum Operation { And, Or, Xor }; + + GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation); + + protected: + friend class GpioVirtPin; + + /** + * Update the output pin based on the current state of the input pins. + */ + void update(); + + private: + GpioVirtPin *inPin1; + GpioVirtPin *inPin2; + Operation operation; +}; + +/** + * Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that. + */ +class GpioSplitter : public GpioPin +{ + + public: + GpioSplitter(GpioPin *outPin1, GpioPin *outPin2); + + void set(bool value) + { + outPin1->set(value); + outPin2->set(value); + } + + private: + GpioPin *outPin1; + GpioPin *outPin2; +}; \ No newline at end of file diff --git a/src/Led.cpp b/src/Led.cpp new file mode 100644 index 000000000..6406cd2f7 --- /dev/null +++ b/src/Led.cpp @@ -0,0 +1,66 @@ +#include "Led.h" +#include "PowerMon.h" +#include "main.h" +#include "power.h" + +GpioVirtPin ledForceOn, ledBlink; + +#if defined(LED_PIN) +// Most boards have a GPIO for LED control +static GpioHwPin ledRawHwPin(LED_PIN); +#else +static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware +#endif + +#if LED_STATE_ON == 0 +static GpioVirtPin ledHwPin; +static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin); +#else +static GpioPin &ledHwPin = ledRawHwPin; +#endif + +#if defined(HAS_PMU) +/** + * A GPIO controlled by the PMU + */ +class GpioPmuPin : public GpioPin +{ + public: + void set(bool value) + { + if (pmu_found && PMU) { + // blink the axp led + PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF); + } + } +} ledPmuHwPin; + +// In some cases we need to drive a PMU LED and a normal LED +static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin); +#else +static GpioPin &ledFinalPin = ledHwPin; +#endif + +#ifdef USE_POWERMON +/** + * We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff. + */ +class MonitoredLedPin : public GpioPin +{ + public: + void set(bool value) + { + if (powerMon) { + if (value) + powerMon->setState(meshtastic_PowerMon_State_LED_On); + else + powerMon->clearState(meshtastic_PowerMon_State_LED_On); + } + ledFinalPin.set(value); + } +} monitoredLedPin; +#else +static GpioPin &monitoredLedPin = ledFinalPin; +#endif + +static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or); \ No newline at end of file diff --git a/src/Led.h b/src/Led.h new file mode 100644 index 000000000..68833e041 --- /dev/null +++ b/src/Led.h @@ -0,0 +1,7 @@ +#include "GpioLogic.h" +#include "configuration.h" + +/** + * ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main) + */ +extern GpioVirtPin ledForceOn, ledBlink; \ No newline at end of file diff --git a/src/Power.cpp b/src/Power.cpp index e8e406a94..d63c43137 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor; #endif #ifdef HAS_PMU -#include "XPowersAXP192.tpp" -#include "XPowersAXP2101.tpp" -#include "XPowersLibInterface.hpp" XPowersLibInterface *PMU = NULL; #else diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 699a6bca6..0a954c1b8 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -9,6 +9,7 @@ */ #include "PowerFSM.h" #include "Default.h" +#include "Led.h" #include "MeshService.h" #include "NodeDB.h" #include "PowerMon.h" @@ -50,7 +51,6 @@ static bool isPowered() static void sdsEnter() { LOG_DEBUG("Enter state: SDS\n"); - powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep); // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false); } @@ -70,7 +70,6 @@ static uint32_t secsSlept; static void lsEnter() { LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs); - powerMon->clearState(meshtastic_PowerMon_State_Screen_On); screen->setOn(false); secsSlept = 0; // How long have we been sleeping this time @@ -91,7 +90,7 @@ static void lsIdle() uint32_t sleepTime = SLEEP_TIME; powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep); - setLed(false); // Never leave led on while in light sleep + ledBlink.set(false); // Never leave led on while in light sleep esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL); powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep); @@ -99,7 +98,7 @@ static void lsIdle() case ESP_SLEEP_WAKEUP_TIMER: // Normal case: timer expired, we should just go back to sleep ASAP - setLed(true); // briefly turn on led + ledBlink.set(true); // briefly turn on led wakeCause2 = doLightSleep(100); // leave led on for 1ms secsSlept += sleepTime; @@ -134,7 +133,7 @@ static void lsIdle() } } else { // Time to stop sleeping! - setLed(false); + ledBlink.set(false); LOG_INFO("Reached ls_secs, servicing loop()\n"); powerFSM.trigger(EVENT_WAKE_TIMER); } @@ -149,7 +148,6 @@ static void lsExit() static void nbEnter() { LOG_DEBUG("Enter state: NB\n"); - powerMon->clearState(meshtastic_PowerMon_State_BT_On); screen->setOn(false); #ifdef ARCH_ESP32 // Only ESP32 should turn off bluetooth @@ -161,8 +159,6 @@ static void nbEnter() static void darkEnter() { - powerMon->clearState(meshtastic_PowerMon_State_BT_On); - powerMon->clearState(meshtastic_PowerMon_State_Screen_On); setBluetoothEnable(true); screen->setOn(false); } @@ -170,8 +166,6 @@ static void darkEnter() static void serialEnter() { LOG_DEBUG("Enter state: SERIAL\n"); - powerMon->clearState(meshtastic_PowerMon_State_BT_On); - powerMon->setState(meshtastic_PowerMon_State_Screen_On); setBluetoothEnable(false); screen->setOn(true); screen->print("Serial connected\n"); @@ -180,7 +174,6 @@ static void serialEnter() static void serialExit() { // Turn bluetooth back on when we leave serial stream API - powerMon->setState(meshtastic_PowerMon_State_BT_On); setBluetoothEnable(true); screen->print("Serial disconnected\n"); } @@ -193,8 +186,6 @@ static void powerEnter() LOG_INFO("Loss of power in Powered\n"); powerFSM.trigger(EVENT_POWER_DISCONNECTED); } else { - powerMon->setState(meshtastic_PowerMon_State_BT_On); - powerMon->setState(meshtastic_PowerMon_State_Screen_On); screen->setOn(true); setBluetoothEnable(true); // within enter() the function getState() returns the state we came from @@ -218,8 +209,6 @@ static void powerIdle() static void powerExit() { - powerMon->setState(meshtastic_PowerMon_State_BT_On); - powerMon->setState(meshtastic_PowerMon_State_Screen_On); screen->setOn(true); setBluetoothEnable(true); @@ -231,8 +220,6 @@ static void powerExit() static void onEnter() { LOG_DEBUG("Enter state: ON\n"); - powerMon->setState(meshtastic_PowerMon_State_BT_On); - powerMon->setState(meshtastic_PowerMon_State_Screen_On); screen->setOn(true); setBluetoothEnable(true); } diff --git a/src/PowerMon.cpp b/src/PowerMon.cpp index 3d28715e0..16909262d 100644 --- a/src/PowerMon.cpp +++ b/src/PowerMon.cpp @@ -2,9 +2,11 @@ #include "NodeDB.h" // Use the 'live' config flag to figure out if we should be showing this message -static bool is_power_enabled(uint64_t m) +bool PowerMon::is_power_enabled(uint64_t m) { - return (m & config.power.powermon_enables) ? true : false; + // FIXME: VERY STRANGE BUG: if I or in "force_enabled || " the flashed image on a rak4631 is not accepted by the bootloader as + // valid!!! Possibly a linker/gcc/bootloader bug somewhere? + return ((m & config.power.powermon_enables) ? true : false); } void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason) diff --git a/src/PowerMon.h b/src/PowerMon.h index e9f5dbd59..a19a6d010 100644 --- a/src/PowerMon.h +++ b/src/PowerMon.h @@ -17,6 +17,13 @@ class PowerMon { uint64_t states = 0UL; + friend class PowerStressModule; + + /** + * If stress testing we always want all events logged + */ + bool force_enabled = false; + public: PowerMon() {} @@ -27,6 +34,9 @@ class PowerMon private: // Emit the coded log message void emitLog(const char *reason); + + // Use the 'live' config flag to figure out if we should be showing this message + bool is_power_enabled(uint64_t m); }; extern PowerMon *powerMon; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 633fb4c67..fe6fd3f06 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -21,6 +21,7 @@ along with this program. If not, see . */ #include "Screen.h" #include "../userPrefs.h" +#include "PowerMon.h" #include "configuration.h" #if HAS_SCREEN #include @@ -1574,6 +1575,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) if (on != screenOn) { if (on) { LOG_INFO("Turning on screen\n"); + powerMon->setState(meshtastic_PowerMon_State_Screen_On); #ifdef T_WATCH_S3 PMU->enablePowerOutput(XPOWERS_ALDO2); #endif @@ -1599,6 +1601,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) setInterval(0); // Draw ASAP runASAP = true; } else { + powerMon->clearState(meshtastic_PowerMon_State_Screen_On); #ifdef USE_EINK // eInkScreensaver parameter is usually NULL (default argument), default frame used instead setScreensaverFrames(einkScreensaver); diff --git a/src/main.cpp b/src/main.cpp index 64fe04f00..f4f7ad537 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ #include "power.h" // #include "debug.h" #include "FSCommon.h" +#include "Led.h" #include "RTC.h" #include "SPILock.h" #include "concurrency/OSThread.h" @@ -197,7 +198,7 @@ static int32_t ledBlinker() static bool ledOn; ledOn ^= 1; - setLed(ledOn); + ledBlink.set(ledOn); // have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000); diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index ca2c5d4be..9a981ee54 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -9,9 +9,9 @@ #if HAS_WIFI #include "mesh/wifi/WiFiAPClient.h" #endif +#include "Led.h" #include "power.h" #include "serialization/JSON.h" -#include "sleep.h" #include #include #include @@ -798,9 +798,9 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) if (blink_target == "LED") { uint8_t count = 10; while (count > 0) { - setLed(true); + ledBlink.set(true); delay(50); - setLed(false); + ledBlink.set(false); delay(50); count = count - 1; } diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp index c86017ae2..4c9f0df88 100644 --- a/src/modules/PowerStressModule.cpp +++ b/src/modules/PowerStressModule.cpp @@ -1,10 +1,14 @@ #include "PowerStressModule.h" +#include "Led.h" #include "MeshService.h" #include "NodeDB.h" +#include "PowerMon.h" #include "RTC.h" #include "Router.h" #include "configuration.h" #include "main.h" +#include "sleep.h" +#include "target_specific.h" extern void printInfo(); @@ -29,6 +33,10 @@ bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, case meshtastic_PowerStressMessage_Opcode_PRINT_INFO: printInfo(); + + // Now that we know we are actually doing power stress testing, go ahead and turn on all enables (so the log is fully + // detailed) + powerMon->force_enabled = true; break; default: @@ -44,7 +52,6 @@ bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, int32_t PowerStressModule::runOnce() { - if (!config.power.powermon_enables) { // Powermon not enabled - stop using CPU/stop this thread return disable(); @@ -58,19 +65,68 @@ int32_t PowerStressModule::runOnce() // Done with the previous command - our sleep must have finished p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET; p.num_seconds = 0; + isRunningCommand = false; + LOG_INFO("S:PS:%u\n", p.cmd); } else { - sleep_msec = (int32_t)(p.num_seconds * 1000); - isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running + if (p.cmd != meshtastic_PowerStressMessage_Opcode_UNSET) { + sleep_msec = (int32_t)(p.num_seconds * 1000); + isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running + LOG_INFO( + "S:PS:%u\n", + p.cmd); // Emit a structured log saying we are starting a powerstress state (to make it easier to parse later) - switch (p.cmd) { - case meshtastic_PowerStressMessage_Opcode_UNSET: // No need to start a new command - break; - case meshtastic_PowerStressMessage_Opcode_LED_ON: - break; - default: - LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd); - sleep_msec = 0; // Don't do whatever sleep was requested... - break; + switch (p.cmd) { + case meshtastic_PowerStressMessage_Opcode_LED_ON: + ledForceOn.set(true); + break; + case meshtastic_PowerStressMessage_Opcode_LED_OFF: + ledForceOn.set(false); + break; + case meshtastic_PowerStressMessage_Opcode_GPS_ON: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_GPS_OFF: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_LORA_OFF: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_LORA_RX: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_LORA_TX: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_SCREEN_OFF: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_SCREEN_ON: + // FIXME - implement + break; + case meshtastic_PowerStressMessage_Opcode_BT_OFF: + setBluetoothEnable(false); + break; + case meshtastic_PowerStressMessage_Opcode_BT_ON: + setBluetoothEnable(true); + break; + case meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP: + doDeepSleep(sleep_msec, true); + break; + case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: { + uint32_t start_msec = millis(); + while ((millis() - start_msec) < (uint32_t)sleep_msec) + ; // Don't let CPU idle at all + sleep_msec = 0; // we already slept + break; + } + case meshtastic_PowerStressMessage_Opcode_CPU_IDLE: + // FIXME - implement + break; + default: + LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd); + sleep_msec = 0; // Don't do whatever sleep was requested... + break; + } } } return sleep_msec; diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 6bce498ab..19f435908 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -1,4 +1,5 @@ #include "PowerFSM.h" +#include "PowerMon.h" #include "configuration.h" #include "esp_task_wdt.h" #include "main.h" @@ -34,6 +35,7 @@ void setBluetoothEnable(bool enable) nimbleBluetooth = new NimbleBluetooth(); } if (enable && !nimbleBluetooth->isActive()) { + powerMon->setState(meshtastic_PowerMon_State_BT_On); nimbleBluetooth->setup(); } // For ESP32, no way to recover from bluetooth shutdown without reboot diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 7334f3a04..a7d5d8c3d 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -9,6 +9,7 @@ #include // #include #include "NodeDB.h" +#include "PowerMon.h" #include "error.h" #include "main.h" @@ -91,6 +92,8 @@ void setBluetoothEnable(bool enable) } if (enable) { + powerMon->setState(meshtastic_PowerMon_State_BT_On); + // If not yet set-up if (!nrf52Bluetooth) { LOG_DEBUG("Initializing NRF52 Bluetooth\n"); @@ -105,8 +108,10 @@ void setBluetoothEnable(bool enable) nrf52Bluetooth->resumeAdvertising(); } // Disable (if previously set-up) - else if (nrf52Bluetooth) + else if (nrf52Bluetooth) { + powerMon->clearState(meshtastic_PowerMon_State_BT_On); nrf52Bluetooth->shutdown(); + } } #else #warning NRF52 "Bluetooth disable" workaround does not apply to builds with MESHTASTIC_EXCLUDE_BLUETOOTH diff --git a/src/power.h b/src/power.h index b970dfeaf..a4307ee07 100644 --- a/src/power.h +++ b/src/power.h @@ -53,6 +53,13 @@ extern INA3221Sensor ina3221Sensor; extern RAK9154Sensor rak9154Sensor; #endif +#ifdef HAS_PMU +#include "XPowersAXP192.tpp" +#include "XPowersAXP2101.tpp" +#include "XPowersLibInterface.hpp" +extern XPowersLibInterface *PMU; +#endif + class Power : private concurrency::OSThread { diff --git a/src/sleep.cpp b/src/sleep.cpp index 486d22dfe..b4171002a 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -5,6 +5,7 @@ #endif #include "ButtonThread.h" +#include "Led.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" @@ -81,26 +82,6 @@ void setCPUFast(bool on) #endif } -void setLed(bool ledOn) -{ - if (ledOn) - powerMon->setState(meshtastic_PowerMon_State_LED_On); - else - powerMon->clearState(meshtastic_PowerMon_State_LED_On); - -#ifdef LED_PIN - // toggle the led so we can get some rough sense of how often loop is pausing - digitalWrite(LED_PIN, ledOn ^ LED_STATE_ON ^ HIGH); -#endif - -#ifdef HAS_PMU - if (pmu_found && PMU) { - // blink the axp led - PMU->setChargingLedMode(ledOn ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF); - } -#endif -} - // Perform power on init that we do on each wake from deep sleep void initDeepSleep() { @@ -230,6 +211,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) notifyDeepSleep.notifyObservers(NULL); #endif + powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep); + screen->doDeepSleep(); // datasheet says this will draw only 10ua nodeDB->saveToDisk(); @@ -257,7 +240,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) digitalWrite(PIN_3V3_EN, LOW); #endif #endif - setLed(false); + ledBlink.set(false); #ifdef RESET_OLED digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power diff --git a/src/sleep.h b/src/sleep.h index f154b8d44..6ac420769 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -22,7 +22,6 @@ extern XPowersLibInterface *PMU; void initDeepSleep(); void setCPUFast(bool on); -void setLed(bool ledOn); /** return true if sleep is allowed right now */ bool doPreflightSleep(); diff --git a/variants/tbeam-s3-core/pins_arduino.h b/variants/tbeam-s3-core/pins_arduino.h index 24edb7d9f..e66b69e02 100644 --- a/variants/tbeam-s3-core/pins_arduino.h +++ b/variants/tbeam-s3-core/pins_arduino.h @@ -6,13 +6,13 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) +// Now declared in .platformio/packages/framework-arduinoespressif32/cores/esp32/Arduino.h +// #define NUM_ANALOG_INPUTS 20 +// #define EXTERNAL_NUM_INTERRUPTS 46 +// #define NUM_DIGITAL_PINS 48 +// #define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) +// #define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) +// #define digitalPinHasPWM(p) (p < 46) static const uint8_t TX = 43; static const uint8_t RX = 44; @@ -39,4 +39,4 @@ static const uint8_t SCK = 12; // #define PMU_IRQ (40) #define RTC_INT (14) -#endif /* Pins_Arduino_h */ +#endif /* Pins_Arduino_h */ \ No newline at end of file From 66c41e683d7401d02c158f6ceaaba4e00e270bc1 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 6 Aug 2024 11:59:06 -0700 Subject: [PATCH 066/305] bug #4184: fix config file loss due to filesystem write errors (#4397) * Use SafeFile for atomic file writing (with xor checksum readback) * Write db.proto last because it could be the largest file on the FS (and less critical) * Don't keep a tmp file around while writing db.proto (because too big to fit two files in the filesystem) * generate a new critial fault if we encounter errors writing to flash either CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE or CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE (depending on if the second write attempt worked) * reformat the filesystem if we detect it is corrupted (then rewrite our config files) (only on nrf52 - not sure yet if we should bother on ESP32) * If we have to format the FS, make sure to preserve the oem.proto if it exists Co-authored-by: Ben Meadors --- src/SafeFile.cpp | 99 +++++++++++ src/SafeFile.h | 49 ++++++ src/gps/GPS.cpp | 2 +- src/mesh/NodeDB.cpp | 165 +++++++++++------- src/mesh/NodeDB.h | 17 +- src/mesh/mesh-pb-constants.cpp | 4 +- .../Telemetry/Sensor/NAU7802Sensor.cpp | 25 ++- 7 files changed, 278 insertions(+), 83 deletions(-) create mode 100644 src/SafeFile.cpp create mode 100644 src/SafeFile.h diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp new file mode 100644 index 000000000..161a80870 --- /dev/null +++ b/src/SafeFile.cpp @@ -0,0 +1,99 @@ +#include "SafeFile.h" + +#ifdef FSCom + +// Only way to work on both esp32 and nrf52 +static File openFile(const char *filename, bool fullAtomic) +{ + if (!fullAtomic) + FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) + + String filenameTmp = filename; + filenameTmp += ".tmp"; + + return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); +} + +SafeFile::SafeFile(const char *_filename, bool fullAtomic) + : filename(_filename), f(openFile(_filename, fullAtomic)), fullAtomic(fullAtomic) +{ +} + +size_t SafeFile::write(uint8_t ch) +{ + if (!f) + return 0; + + hash ^= ch; + return f.write(ch); +} + +size_t SafeFile::write(const uint8_t *buffer, size_t size) +{ + if (!f) + return 0; + + for (size_t i = 0; i < size; i++) { + hash ^= buffer[i]; + } + return f.write((uint8_t const *)buffer, size); // This nasty cast is _IMPORTANT_ otherwise the correct adafruit method does + // not get used (they made a mistake in their typing) +} + +/** + * Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches + * + * @return false for failure + */ +bool SafeFile::close() +{ + if (!f) + return false; + + f.close(); + if (!testReadback()) + return false; + + // brief window of risk here ;-) + if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) { + LOG_ERROR("Can't remove old pref file\n"); + return false; + } + + String filenameTmp = filename; + filenameTmp += ".tmp"; + if (!renameFile(filenameTmp.c_str(), filename.c_str())) { + LOG_ERROR("Error: can't rename new pref file\n"); + return false; + } + + return true; +} + +/// Read our (closed) tempfile back in and compare the hash +bool SafeFile::testReadback() +{ + String filenameTmp = filename; + filenameTmp += ".tmp"; + auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ); + if (!f2) { + LOG_ERROR("Can't open tmp file for readback\n"); + return false; + } + + int c = 0; + uint8_t test_hash = 0; + while ((c = f2.read()) >= 0) { + test_hash ^= (uint8_t)c; + } + f2.close(); + + if (test_hash != hash) { + LOG_ERROR("Readback failed hash mismatch\n"); + return false; + } + + return true; +} + +#endif \ No newline at end of file diff --git a/src/SafeFile.h b/src/SafeFile.h new file mode 100644 index 000000000..7088074cd --- /dev/null +++ b/src/SafeFile.h @@ -0,0 +1,49 @@ +#pragma once + +#include "FSCommon.h" +#include "configuration.h" + +#ifdef FSCom + +/** + * This class provides 'safe'/paranoid file writing. + * + * Some of our filesystems (in particular the nrf52) may have bugs beneath our layer. Therefore we want to + * be very careful about how we write files. This class provides a restricted (Stream only) writing API for writing to files. + * + * Notably: + * - we keep a simple xor hash of all characters that were written. + * - We do not allow seeking (because we want to maintain our hash) + * - we provide an close() method which is similar to close but returns false if we were unable to successfully write the + * file. Also this method + * - atomically replaces any old version of the file on the disk with our new file (after first rereading the file from the disk + * to confirm the hash matches) + * - Some files are super huge so we can't do the full atomic rename/copy (because of filesystem size limits). If !fullAtomic + * then we still do the readback to verify file is valid so higher level code can handle failures. + */ +class SafeFile : public Print +{ + public: + SafeFile(char const *filepath, bool fullAtomic = false); + + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buffer, size_t size); + + /** + * Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches + * + * @return false for failure + */ + bool close(); + + private: + /// Read our (closed) tempfile back in and compare the hash + bool testReadback(); + + String filename; + File f; + bool fullAtomic; + uint8_t hash = 0; +}; + +#endif \ No newline at end of file diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 0d937ae63..93742a99a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1090,7 +1090,7 @@ int32_t GPS::runOnce() if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) { LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); devicestate.did_gps_reset = false; - nodeDB->saveDeviceStateToDisk(); + nodeDB->saveToDisk(SEGMENT_DEVICESTATE); return disable(); // Stop the GPS thread as it can do nothing useful until next reboot. } } diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index de63af08e..61f08fe65 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -14,6 +14,7 @@ #include "PowerFSM.h" #include "RTC.h" #include "Router.h" +#include "SafeFile.h" #include "TypeConversions.h" #include "error.h" #include "main.h" @@ -53,6 +54,28 @@ meshtastic_LocalConfig config; meshtastic_LocalModuleConfig moduleConfig; meshtastic_ChannelFile channelFile; meshtastic_OEMStore oemStore; +static bool hasOemStore = false; + +// These are not publically exposed - copied from InternalFileSystem.cpp +// #define FLASH_NRF52_PAGE_SIZE 4096 +// #define LFS_FLASH_TOTAL_SIZE (7*FLASH_NRF52_PAGE_SIZE) +// #define LFS_BLOCK_SIZE 128 + +/// List all files in the FS and test write and readback. +/// Useful for filesystem stress testing - normally stripped from build by the linker. +void flashTest() +{ + auto filesManifest = getFiles("/", 5); + + uint32_t totalSize = 0; + for (size_t i = 0; i < filesManifest.size(); i++) { + LOG_INFO("File %s (size %d)\n", filesManifest[i].file_name, filesManifest[i].size_bytes); + totalSize += filesManifest[i].size_bytes; + } + LOG_INFO("%d files (total size %u)\n", filesManifest.size(), totalSize); + // LOG_INFO("Filesystem block size %u, total bytes %u", LFS_FLASH_TOTAL_SIZE, LFS_BLOCK_SIZE); + nodeDB->saveToDisk(); +} bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field) { @@ -608,22 +631,30 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t void NodeDB::loadFromDisk() { + devicestate.version = + 0; // Mark the current device state as completely unusable, so that if we fail reading the entire file from + // disk we will still factoryReset to restore things. + // static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES * sizeof(meshtastic_NodeInfo), sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate); - if (state != LoadFileResult::LOAD_SUCCESS) { - installDefaultDeviceState(); // Our in RAM copy might now be corrupt + // See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786 + // It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of our + // critical config may still be valid (in the other files - loaded next). + // Also, if we did fail on reading we probably failed on the enormous (and non critical) nodeDB. So DO NOT install default + // device state. + // if (state != LoadFileResult::LOAD_SUCCESS) { + // installDefaultDeviceState(); // Our in RAM copy might now be corrupt + //} else { + if (devicestate.version < DEVICESTATE_MIN_VER) { + LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version); + factoryReset(); } else { - if (devicestate.version < DEVICESTATE_MIN_VER) { - LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version); - factoryReset(); - } else { - LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version, - devicestate.node_db_lite.size()); - meshNodes = &devicestate.node_db_lite; - numMeshNodes = devicestate.node_db_lite.size(); - } + LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version, + devicestate.node_db_lite.size()); + meshNodes = &devicestate.node_db_lite; + numMeshNodes = devicestate.node_db_lite.size(); } meshNodes->resize(MAX_NUM_NODES); @@ -669,6 +700,7 @@ void NodeDB::loadFromDisk() state = loadProto(oemConfigFile, meshtastic_OEMStore_size, sizeof(meshtastic_OEMStore), &meshtastic_OEMStore_msg, &oemStore); if (state == LoadFileResult::LOAD_SUCCESS) { LOG_INFO("Loaded OEMStore\n"); + hasOemStore = true; } // 2.4.X - configuration migration to update new default intervals @@ -693,48 +725,26 @@ void NodeDB::loadFromDisk() } /** Save a protobuf from a file, return true for success */ -bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct) +bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct, + bool fullAtomic) { bool okay = false; #ifdef FSCom - // static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM - String filenameTmp = filename; - filenameTmp += ".tmp"; - auto f = FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); - if (f) { - LOG_INFO("Saving %s\n", filename); - pb_ostream_t stream = {&writecb, &f, protoSize}; + auto f = SafeFile(filename, fullAtomic); - if (!pb_encode(&stream, fields, dest_struct)) { - LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); - } else { - okay = true; - } - f.flush(); - f.close(); + LOG_INFO("Saving %s\n", filename); + pb_ostream_t stream = {&writecb, static_cast(&f), protoSize}; - // brief window of risk here ;-) - if (FSCom.exists(filename) && !FSCom.remove(filename)) { - LOG_WARN("Can't remove old pref file\n"); - } - if (!renameFile(filenameTmp.c_str(), filename)) { - LOG_ERROR("Error: can't rename new pref file\n"); - } + if (!pb_encode(&stream, fields, dest_struct)) { + LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); } else { - LOG_ERROR("Can't write prefs\n"); -#ifdef ARCH_NRF52 - static uint8_t failedCounter = 0; - failedCounter++; - if (failedCounter >= 2) { - LOG_ERROR("Failed to save file twice. Rebooting...\n"); - delay(100); - NVIC_SystemReset(); - // We used to blow away the filesystem here, but that's a bit extreme - // FSCom.format(); - // // After formatting, the device needs to be restarted - // nodeDB->resetRadioConfig(true); - } -#endif + okay = true; + } + + bool writeSucceeded = f.close(); + + if (!okay || !writeSucceeded) { + LOG_ERROR("Can't write prefs!\n"); } #else LOG_ERROR("ERROR: Filesystem not implemented\n"); @@ -742,32 +752,32 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_ return okay; } -void NodeDB::saveChannelsToDisk() +bool NodeDB::saveChannelsToDisk() { #ifdef FSCom FSCom.mkdir("/prefs"); #endif - saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile); + return saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile); } -void NodeDB::saveDeviceStateToDisk() +bool NodeDB::saveDeviceStateToDisk() { #ifdef FSCom FSCom.mkdir("/prefs"); #endif - saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg, - &devicestate); + // Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB + // Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this + return saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg, + &devicestate, false); } -void NodeDB::saveToDisk(int saveWhat) +bool NodeDB::saveToDiskNoRetry(int saveWhat) { + bool success = true; + #ifdef FSCom FSCom.mkdir("/prefs"); #endif - if (saveWhat & SEGMENT_DEVICESTATE) { - saveDeviceStateToDisk(); - } - if (saveWhat & SEGMENT_CONFIG) { config.has_device = true; config.has_display = true; @@ -777,7 +787,7 @@ void NodeDB::saveToDisk(int saveWhat) config.has_network = true; config.has_bluetooth = true; - saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config); + success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config); } if (saveWhat & SEGMENT_MODULECONFIG) { @@ -794,12 +804,45 @@ void NodeDB::saveToDisk(int saveWhat) moduleConfig.has_audio = true; moduleConfig.has_paxcounter = true; - saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig); + success &= + saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig); + } + + // We might need to rewrite the OEM data if we are reformatting the FS + if ((saveWhat & SEGMENT_OEM) && hasOemStore) { + success &= saveProto(oemConfigFile, meshtastic_OEMStore_size, &meshtastic_OEMStore_msg, &oemStore); } if (saveWhat & SEGMENT_CHANNELS) { - saveChannelsToDisk(); + success &= saveChannelsToDisk(); } + + if (saveWhat & SEGMENT_DEVICESTATE) { + success &= saveDeviceStateToDisk(); + } + + return success; +} + +bool NodeDB::saveToDisk(int saveWhat) +{ + bool success = saveToDiskNoRetry(saveWhat); + + if (!success) { + LOG_ERROR("Failed to save to disk, retrying...\n"); +#ifdef ARCH_NRF52 // @geeksville is not ready yet to say we should do this on other platforms. See bug #4184 discussion + FSCom.format(); + + // We need to rewrite the OEM data if we are reformatting the FS + saveWhat |= SEGMENT_OEM; +#endif + success = saveToDiskNoRetry(saveWhat); + + RECORD_CRITICALERROR(success ? meshtastic_CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE + : meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + } + + return success; } const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex) @@ -1059,4 +1102,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} +} \ No newline at end of file diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index e2c2471d4..c00ab8c03 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -19,6 +19,7 @@ DeviceState versions used to be defined in the .proto file but really only this #define SEGMENT_MODULECONFIG 2 #define SEGMENT_DEVICESTATE 4 #define SEGMENT_CHANNELS 8 +#define SEGMENT_OEM 16 #define DEVICESTATE_CUR_VER 23 #define DEVICESTATE_MIN_VER 22 @@ -72,8 +73,8 @@ class NodeDB NodeDB(); /// write to flash - void saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS), - saveChannelsToDisk(), saveDeviceStateToDisk(); + /// @return true if the save was successful + bool saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); /** Reinit radio config if needed, because either: * a) sometimes a buggy android app might send us bogus settings or @@ -130,7 +131,8 @@ class NodeDB LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct); - bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct); + bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct, + bool fullAtomic = true); void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role); @@ -181,6 +183,13 @@ class NodeDB /// Reinit device state from scratch (not loading from disk) void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig(); + + /// write to flash + /// @return true if the save was successful + bool saveToDiskNoRetry(int saveWhat); + + bool saveChannelsToDisk(); + bool saveDeviceStateToDisk(); }; extern NodeDB *nodeDB; @@ -228,4 +237,4 @@ extern uint32_t error_address; ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \ ModuleConfig_TelemetryConfig_size + ModuleConfig_size) -// Please do not remove this comment, it makes trunk and compiler happy at the same time. +// Please do not remove this comment, it makes trunk and compiler happy at the same time. \ No newline at end of file diff --git a/src/mesh/mesh-pb-constants.cpp b/src/mesh/mesh-pb-constants.cpp index 676208e25..5b5d669cc 100644 --- a/src/mesh/mesh-pb-constants.cpp +++ b/src/mesh/mesh-pb-constants.cpp @@ -58,7 +58,7 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count) /// Write to an arduino file bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count) { - File *file = (File *)stream->state; + auto file = (Print *)stream->state; // LOG_DEBUG("writing %d bytes to protobuf file\n", count); return file->write(buf, count) == count; } @@ -71,4 +71,4 @@ bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count) return true; return false; -} +} \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp index 39ac4b08b..3560c6580 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp @@ -5,6 +5,7 @@ #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "FSCommon.h" #include "NAU7802Sensor.h" +#include "SafeFile.h" #include "TelemetrySensor.h" #include #include @@ -95,27 +96,21 @@ void NAU7802Sensor::tare() bool NAU7802Sensor::saveCalibrationData() { - if (FSCom.exists(nau7802ConfigFileName) && !FSCom.remove(nau7802ConfigFileName)) { - LOG_WARN("Can't remove old state file\n"); - } - auto file = FSCom.open(nau7802ConfigFileName, FILE_O_WRITE); + auto file = SafeFile(nau7802ConfigFileName); nau7802config.zeroOffset = nau7802.getZeroOffset(); nau7802config.calibrationFactor = nau7802.getCalibrationFactor(); bool okay = false; - if (file) { - LOG_INFO("%s state write to %s.\n", sensorName, nau7802ConfigFileName); - pb_ostream_t stream = {&writecb, &file, meshtastic_Nau7802Config_size}; - if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) { - LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); - } else { - okay = true; - } - file.flush(); - file.close(); + LOG_INFO("%s state write to %s.\n", sensorName, nau7802ConfigFileName); + pb_ostream_t stream = {&writecb, static_cast(&file), meshtastic_Nau7802Config_size}; + + if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) { + LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); } else { - LOG_INFO("Can't write %s state (File: %s).\n", sensorName, nau7802ConfigFileName); + okay = true; } + okay &= file.close(); + return okay; } From 9ec7dbd69504581c789d865624761fa841a6e7c4 Mon Sep 17 00:00:00 2001 From: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:59:33 -0400 Subject: [PATCH 067/305] Initial Support for Heltec VM-T190 (#4391) Initial Support for Heltec VM-T190 --- boards/heltec_vision_master_t190.json | 42 +++++++++++++++++++ .../heltec_vision_master_t190/pins_arduino.h | 4 +- .../heltec_vision_master_t190/platformio.ini | 2 +- variants/heltec_vision_master_t190/variant.h | 38 +++++++---------- 4 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 boards/heltec_vision_master_t190.json diff --git a/boards/heltec_vision_master_t190.json b/boards/heltec_vision_master_t190.json new file mode 100644 index 000000000..341e70218 --- /dev/null +++ b/boards/heltec_vision_master_t190.json @@ -0,0 +1,42 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + ["0x303A", "0x1001"], + ["0x303A", "0x0002"] + ], + "mcu": "esp32s3", + "variant": "heltec_vision_master_t190" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "Heltec Vision Master t190", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://heltec.org/project/vision-master-t190/", + "vendor": "Heltec" +} diff --git a/variants/heltec_vision_master_t190/pins_arduino.h b/variants/heltec_vision_master_t190/pins_arduino.h index e5d507846..eeef95ff1 100644 --- a/variants/heltec_vision_master_t190/pins_arduino.h +++ b/variants/heltec_vision_master_t190/pins_arduino.h @@ -10,8 +10,8 @@ static const uint8_t LED_BUILTIN = 35; static const uint8_t TX = 43; static const uint8_t RX = 44; -static const uint8_t SDA = 41; -static const uint8_t SCL = 42; +static const uint8_t SDA = 2; +static const uint8_t SCL = 1; static const uint8_t SS = 8; static const uint8_t MOSI = 10; diff --git a/variants/heltec_vision_master_t190/platformio.ini b/variants/heltec_vision_master_t190/platformio.ini index bbaa0075c..38a3169e8 100644 --- a/variants/heltec_vision_master_t190/platformio.ini +++ b/variants/heltec_vision_master_t190/platformio.ini @@ -1,6 +1,6 @@ [env:heltec-vision-master-t190] extends = esp32s3_base -board = heltec_wifi_lora_32_V3 +board = heltec_vision_master_t190 build_flags = ${esp32s3_base.build_flags} -Ivariants/heltec_vision_master_t190 diff --git a/variants/heltec_vision_master_t190/variant.h b/variants/heltec_vision_master_t190/variant.h index 97500d357..726f6d864 100644 --- a/variants/heltec_vision_master_t190/variant.h +++ b/variants/heltec_vision_master_t190/variant.h @@ -1,12 +1,12 @@ -// #define LED_PIN 18 +#define BUTTON_PIN 0 -// Enable bus for external periherals -#define I2C_SDA 1 -#define I2C_SCL 2 +// I2C +#define I2C_SDA SDA +#define I2C_SCL SCL + +// Display (TFT) #define USE_ST7789 - #define ST7789_NSS 39 -// #define ST7789_CS 39 #define ST7789_RS 47 // DC #define ST7789_SDA 48 // MOSI #define ST7789_SCK 38 @@ -14,14 +14,9 @@ #define ST7789_MISO 4 #define ST7789_BUSY -1 #define VTFT_CTRL 7 -// #define TFT_BL 3 #define VTFT_LEDA 17 #define TFT_BACKLIGHT_ON HIGH -// #define TFT_BL 17 -// #define TFT_BACKLIGHT_ON HIGH -// #define ST7789_BL 3 #define ST7789_SPI_HOST SPI2_HOST -// #define ST7789_BACKLIGHT_EN 17 #define SPI_FREQUENCY 10000000 #define SPI_READ_FREQUENCY 10000000 #define TFT_HEIGHT 170 @@ -35,28 +30,27 @@ // #define SLEEP_TIME 120 -/* - * SPI interfaces - */ +// SPI #define SPI_INTERFACES_COUNT 2 +#define PIN_SPI_MISO 11 +#define PIN_SPI_MOSI 10 +#define PIN_SPI_SCK 9 -#define PIN_SPI_MISO 10 // MISO P0.17 -#define PIN_SPI_MOSI 11 // MOSI P0.15 -#define PIN_SPI_SCK 9 // SCK P0.13 - -// #define VEXT_ENABLE 7 // active low, powers the oled display and the lora antenna boost -#define BUTTON_PIN 0 - +// Power +#define VEXT_ENABLE 5 +#define VEXT_ON_VALUE HIGH #define ADC_CTRL 46 #define ADC_CTRL_ENABLED HIGH #define BATTERY_PIN 6 #define ADC_CHANNEL ADC1_GPIO6_CHANNEL #define ADC_MULTIPLIER 4.9 * 1.03 // Voltage divider is roughly 1:1 #define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // Voltage divider output is quite high +#define HAS_32768HZ +// LoRa #define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_DIO0 RADIOLIB_NC // a No connect on the SX1262 module #define LORA_RESET 12 #define LORA_DIO1 14 // SX1262 IRQ #define LORA_DIO2 13 // SX1262 BUSY From 92526fca23bb2d9e585937fd0769debcced96879 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Wed, 7 Aug 2024 10:16:56 +1200 Subject: [PATCH 068/305] "Scan and Select" input for Canned Messages (#4365) * Add "Scan and Select" input method for canned messages * Adapt canned message drawing if USE_EINK * Indicate current selection with indent rather than inverse text * Avoid large text on "sending" and delivery report pop-ups * Fit SNR and RSSI details on screen * Change hash function which detects changes in E-Ink images The old function struggled to distingush between images on the canned-message frame, failing to update when scrolling between messages. No real justification for the new algorithm, other than "it works" and doesn't seem "too expensive". For context, this function runs once a second. * Use canned messages (scan and select) by default with HT-VME213 and HT-VME290 * Guard for HAS_SCREEN --- src/graphics/EInkDynamicDisplay.cpp | 2 +- src/graphics/Screen.cpp | 6 + src/input/ScanAndSelect.cpp | 204 +++++++++++++++++++ src/input/ScanAndSelect.h | 50 +++++ src/mesh/NodeDB.cpp | 7 + src/modules/CannedMessageModule.cpp | 63 +++++- src/modules/CannedMessageModule.h | 1 + src/modules/Modules.cpp | 11 + variants/heltec_vision_master_e213/variant.h | 2 + variants/heltec_vision_master_e290/variant.h | 2 + 10 files changed, 338 insertions(+), 10 deletions(-) create mode 100644 src/input/ScanAndSelect.cpp create mode 100644 src/input/ScanAndSelect.h diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp index 5b97b8d48..c31941a60 100644 --- a/src/graphics/EInkDynamicDisplay.cpp +++ b/src/graphics/EInkDynamicDisplay.cpp @@ -375,7 +375,7 @@ void EInkDynamicDisplay::hashImage() // Sum all bytes of the image buffer together for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) { - imageHash += buffer[b]; + imageHash ^= buffer[b] << b; } } diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index fe6fd3f06..ea5ab9788 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -37,6 +37,7 @@ along with this program. If not, see . #include "gps/RTC.h" #include "graphics/ScreenFonts.h" #include "graphics/images.h" +#include "input/ScanAndSelect.h" #include "input/TouchScreenImpl1.h" #include "main.h" #include "mesh-pb-constants.h" @@ -2291,6 +2292,11 @@ void Screen::handlePrint(const char *text) void Screen::handleOnPress() { + // If Canned Messages is using the "Scan and Select" input, dismiss the canned message frame when user button is pressed + // Minimize impact as a courtesy, as "scan and select" may be used as default config for some boards + if (scanAndSelectInput != nullptr && scanAndSelectInput->dismissCannedMessageFrame()) + return; + // If screen was off, just wake it, otherwise advance to next frame // If we are in a transition, the press must have bounced, drop it. if (ui->getUiState()->frameState == FIXED) { diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp new file mode 100644 index 000000000..d693d768c --- /dev/null +++ b/src/input/ScanAndSelect.cpp @@ -0,0 +1,204 @@ +#include "configuration.h" + +// Normally these input methods are protected by guarding in setupModules +// In order to have the user button dismiss the canned message frame, this class lightly interacts with the Screen class +#if HAS_SCREEN + +#include "ScanAndSelect.h" +#include "modules/CannedMessageModule.h" + +// Config +static const char name[] = "scanAndSelect"; // should match "allow input source" string +static constexpr uint32_t durationShortMs = 50; +static constexpr uint32_t durationLongMs = 1500; +static constexpr uint32_t durationAlertMs = 2000; + +// Constructor: init base class +ScanAndSelectInput::ScanAndSelectInput() : concurrency::OSThread(name) {} + +// Attempt to setup class; true if success. +// Called by setupModules method. Instance deleted if setup fails. +bool ScanAndSelectInput::init() +{ + // Short circuit: Canned messages enabled? + if (!moduleConfig.canned_message.enabled) + return false; + + // Short circuit: Using correct "input source"? + // Todo: protobuf enum instead of string? + if (strcasecmp(moduleConfig.canned_message.allow_input_source, name) != 0) + return false; + + // Use any available inputbroker pin as the button + if (moduleConfig.canned_message.inputbroker_pin_press) + pin = moduleConfig.canned_message.inputbroker_pin_press; + else if (moduleConfig.canned_message.inputbroker_pin_a) + pin = moduleConfig.canned_message.inputbroker_pin_a; + else if (moduleConfig.canned_message.inputbroker_pin_b) + pin = moduleConfig.canned_message.inputbroker_pin_b; + else + return false; // Short circuit: no button found + + // Set-up the button + pinMode(pin, INPUT_PULLUP); + attachInterrupt(pin, handleChangeInterrupt, CHANGE); + + // Connect our class to the canned message module + inputBroker->registerSource(this); + + LOG_INFO("Initialized 'Scan and Select' input for Canned Messages, using pin %d\n", pin); + return true; // Init succeded +} + +// Runs periodically, unless sleeping between presses +int32_t ScanAndSelectInput::runOnce() +{ + uint32_t now = millis(); + + // If: "no messages added" alert screen currently shown + if (alertingNoMessage) { + // Dismiss the alert screen several seconds after it appears + if (now > alertingSinceMs + durationAlertMs) { + alertingNoMessage = false; + screen->endAlert(); + } + } + + // If: Button is pressed + if (digitalRead(pin) == LOW) { + // New press + if (!held) { + downSinceMs = now; + } + + // Existing press + else { + // Duration enough for long press + // Long press not yet fired (prevent repeat firing while held) + if (!longPressFired && now - downSinceMs > durationLongMs) { + longPressFired = true; + longPress(); + } + } + + // Record the change of state: button is down + held = true; + } + + // If: Button is not pressed + else { + // Button newly released + // Long press event didn't already fire + if (held && !longPressFired) { + // Duration enough for short press + if (now - downSinceMs > durationShortMs) { + shortPress(); + } + } + + // Record the change of state: button is up + held = false; + longPressFired = false; // Re-Arm: allow another long press + } + + // If thread's job is done, let it sleep + if (!held && !alertingNoMessage) { + Thread::canSleep = true; + return OSThread::disable(); + } + + // Run this method again is a few ms + return durationShortMs; +} + +void ScanAndSelectInput::longPress() +{ + // (If canned messages set) + if (cannedMessageModule->hasMessages()) { + // If module frame displayed already, send the current message + if (cannedMessageModule->shouldDraw()) + raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT); + + // Otherwise, initial long press opens the module frame + else + raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN); + } + + // (If canned messages not set) tell the user + else + alertNoMessage(); +} + +void ScanAndSelectInput::shortPress() +{ + // (If canned messages set) scroll to next message + if (cannedMessageModule->hasMessages()) + raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN); + + // (If canned messages not yet set) tell the user + else + alertNoMessage(); +} + +// Begin running runOnce at regular intervals +// Called from pin change interrupt +void ScanAndSelectInput::enableThread() +{ + Thread::canSleep = false; + OSThread::enabled = true; + OSThread::setIntervalFromNow(0); +} + +// Inform user (screen) that no canned messages have been added +// Automatically dismissed after several seconds +void ScanAndSelectInput::alertNoMessage() +{ + alertingNoMessage = true; + alertingSinceMs = millis(); + + // Graphics code: the alert frame to show on screen + screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { + display->setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display->setFont(FONT_SMALL); + int16_t textX = display->getWidth() / 2; + int16_t textY = display->getHeight() / 2; + display->drawString(textX + x, textY + y, "No Canned Messages"); + }); +} + +// Remove the canned message frame from screen +// Used to dismiss the module frame when user button pressed +// Returns true if the frame was previously displayed, and has now been closed +// Return value consumed by Screen class when determining how to handle user button +bool ScanAndSelectInput::dismissCannedMessageFrame() +{ + if (cannedMessageModule->shouldDraw()) { + raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL); + return true; + } + + return false; +} + +// Feed input to the canned messages module +void ScanAndSelectInput::raiseEvent(_meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar key) +{ + InputEvent e; + e.source = name; + e.inputEvent = key; + notifyObservers(&e); +} + +// Pin change interrupt +void ScanAndSelectInput::handleChangeInterrupt() +{ + // Because we need to detect both press and release (rising and falling edge), the interrupt itself can't determine the + // action. Instead, we start up the thread and get it to read the button for us + + // The instance we're referring to here is created in setupModules() + scanAndSelectInput->enableThread(); +} + +ScanAndSelectInput *scanAndSelectInput = nullptr; // Instantiated in setupModules method. Deleted if unused, or init() fails + +#endif \ No newline at end of file diff --git a/src/input/ScanAndSelect.h b/src/input/ScanAndSelect.h new file mode 100644 index 000000000..0b3e2716e --- /dev/null +++ b/src/input/ScanAndSelect.h @@ -0,0 +1,50 @@ +/* + A "single button" input method for Canned Messages + + - Short press to cycle through messages + - Long Press to send + + To use: + - set "allow input source" to "scanAndSelect" + - set the single button's GPIO as either pin A, pin B, or pin Press + + Originally designed to make use of "extra" built-in button on some boards. + Non-intrusive; suitable for use as a default module config. +*/ + +#pragma once +#include "concurrency/OSThread.h" +#include "main.h" + +// Normally these input methods are protected by guarding in setupModules +// In order to have the user button dismiss the canned message frame, this class lightly interacts with the Screen class +#if HAS_SCREEN + +class ScanAndSelectInput : public Observable, public concurrency::OSThread +{ + public: + ScanAndSelectInput(); // No-op constructor, only initializes OSThread base class + bool init(); // Attempt to setup class; true if success. Instance deleted if setup fails + bool dismissCannedMessageFrame(); // Remove the canned message frame from screen. True if frame was open, and now closed. + void alertNoMessage(); // Inform user (screen) that no canned messages have been added + + protected: + int32_t runOnce() override; // Runs at regular intervals, when enabled + void enableThread(); // Begin running runOnce at regular intervals + static void handleChangeInterrupt(); // Calls enableThread from pin change interrupt + void shortPress(); // Code to run when short press fires + void longPress(); // Code to run when long press fires + void raiseEvent(_meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar key); // Feed input to canned message module + + bool held = false; // Have we handled a change in button state? + bool longPressFired = false; // Long press fires while button still held. This bool ensures the release is no-op + uint32_t downSinceMs = 0; // Debouncing for short press, timing for long press + uint8_t pin = -1; // Read from cannned message config during init + + bool alertingNoMessage = false; // Is the "no canned messages" alert shown on screen? + uint32_t alertingSinceMs = 0; // Used to dismiss the "no canned message" alert several seconds +}; + +extern ScanAndSelectInput *scanAndSelectInput; // Instantiated in setupModules method. Deleted if unused, or init() fails + +#endif \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 61f08fe65..d74e96bc6 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -394,6 +394,13 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.output_ms = 100; moduleConfig.external_notification.active = true; #endif +#ifdef BUTTON_SECONDARY_CANNEDMESSAGES + // Use a board's second built-in button as input source for canned messages + moduleConfig.canned_message.enabled = true; + moduleConfig.canned_message.inputbroker_pin_press = BUTTON_PIN_SECONDARY; + strcpy(moduleConfig.canned_message.allow_input_source, "scanAndSelect"); +#endif + moduleConfig.has_canned_message = true; strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address)); diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index f4ee3abd2..4df5a03fc 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -10,6 +10,7 @@ #include "NodeDB.h" #include "PowerFSM.h" // needed for button bypass #include "detect/ScanI2C.h" +#include "input/ScanAndSelect.h" #include "mesh/generated/meshtastic/cannedmessages.pb.h" #include "main.h" // for cardkb_found @@ -694,9 +695,22 @@ bool CannedMessageModule::shouldDraw() if (!moduleConfig.canned_message.enabled && !CANNED_MESSAGE_MODULE_ENABLE) { return false; } + + // If using "scan and select" input, don't draw the module frame just to say "disabled" + // The scanAndSelectInput class will draw its own temporary alert for user, when the input button is pressed + else if (scanAndSelectInput != nullptr && !hasMessages()) + return false; + return (currentMessageIndex != -1) || (this->runState != CANNED_MESSAGE_RUN_STATE_INACTIVE); } +// Has the user defined any canned messages? +// Expose publicly whether canned message module is ready for use +bool CannedMessageModule::hasMessages() +{ + return (this->messagesCount > 0); +} + int CannedMessageModule::getNextIndex() { if (this->currentMessageIndex >= (this->messagesCount - 1)) { @@ -931,13 +945,17 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->setFont(FONT_MEDIUM); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage); } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) { - // E-Ink: clean the screen *after* this pop-up - EINK_ADD_FRAMEFLAG(display, COSMETIC); + requestFocus(); // Tell Screen::setFrames to move to our module's frame + EINK_ADD_FRAMEFLAG(display, COSMETIC); // Clean after this popup. Layout makes ghosting particularly obvious + +#ifdef USE_EINK + display->setFont(FONT_SMALL); // No chunky text +#else + display->setFont(FONT_MEDIUM); // Chunky text +#endif - requestFocus(); // Tell Screen::setFrames to move to our module's frame - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(FONT_MEDIUM); String displayString; + display->setTextAlignment(TEXT_ALIGN_CENTER); if (this->ack) { displayString = "Delivered to\n%s"; } else { @@ -951,17 +969,37 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st String snrString = "Last Rx SNR: %f"; String rssiString = "Last Rx RSSI: %d"; - if (this->ack) { - display->drawStringf(display->getWidth() / 2 + x, y + 100, buffer, snrString, this->lastRxSnr); - display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi); + // Don't bother drawing snr and rssi for tiny displays + if (display->getHeight() > 100) { + + // Original implementation used constants of y = 100 and y = 130. Shrink this if screen is *slightly* small + int16_t snrY = 100; + int16_t rssiY = 130; + + // If dislay is *slighly* too small for the original consants, squish up a bit + if (display->getHeight() < rssiY) { + snrY = display->getHeight() - ((1.5) * FONT_HEIGHT_SMALL); + rssiY = display->getHeight() - ((2.5) * FONT_HEIGHT_SMALL); + } + + if (this->ack) { + display->drawStringf(display->getWidth() / 2 + x, snrY + y, buffer, snrString, this->lastRxSnr); + display->drawStringf(display->getWidth() / 2 + x, rssiY + y, buffer, rssiString, this->lastRxRssi); + } } } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { // E-Ink: clean the screen *after* this pop-up EINK_ADD_FRAMEFLAG(display, COSMETIC); requestFocus(); // Tell Screen::setFrames to move to our module's frame + +#ifdef USE_EINK + display->setFont(FONT_SMALL); // No chunky text +#else + display->setFont(FONT_MEDIUM); // Chunky text +#endif + display->setTextAlignment(TEXT_ALIGN_CENTER); - display->setFont(FONT_MEDIUM); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending..."); } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) { display->setTextAlignment(TEXT_ALIGN_LEFT); @@ -1033,11 +1071,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st int topMsg = (messagesCount > lines && currentMessageIndex >= lines - 1) ? currentMessageIndex - lines + 2 : 0; for (int i = 0; i < std::min(messagesCount, lines); i++) { if (i == currentMessageIndex - topMsg) { +#ifdef USE_EINK + // Avoid drawing solid black with fillRect: harder to clear for E-Ink + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), ">"); + display->drawString(12 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), + cannedMessageModule->getCurrentMessage()); +#else display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->setColor(BLACK); display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), cannedMessageModule->getCurrentMessage()); display->setColor(WHITE); +#endif } else { display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), cannedMessageModule->getMessageByIndex(topMsg + i)); diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 797b9f7cf..9e6af8890 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -56,6 +56,7 @@ class CannedMessageModule : public SinglePortModule, public Observableinit()) { + delete scanAndSelectInput; + scanAndSelectInput = nullptr; + } +#endif + cardKbI2cImpl = new CardKbI2cImpl(); cardKbI2cImpl->init(); #ifdef INPUTBROKER_MATRIX_TYPE diff --git a/variants/heltec_vision_master_e213/variant.h b/variants/heltec_vision_master_e213/variant.h index bbc697f09..0771b3517 100644 --- a/variants/heltec_vision_master_e213/variant.h +++ b/variants/heltec_vision_master_e213/variant.h @@ -1,4 +1,6 @@ #define BUTTON_PIN 0 +#define BUTTON_PIN_SECONDARY 21 // Second built-in button +#define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input // I2C #define I2C_SDA SDA diff --git a/variants/heltec_vision_master_e290/variant.h b/variants/heltec_vision_master_e290/variant.h index 6af4b06a5..72a82cfdb 100644 --- a/variants/heltec_vision_master_e290/variant.h +++ b/variants/heltec_vision_master_e290/variant.h @@ -1,4 +1,6 @@ #define BUTTON_PIN 0 +#define BUTTON_PIN_SECONDARY 21 // Second built-in button +#define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input // I2C #define I2C_SDA SDA From 789e8f02bf914b3582be69270925cc930651b6ff Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 6 Aug 2024 18:48:55 -0500 Subject: [PATCH 069/305] Add more exclude options to save program ram/flash (#4408) * Add PowerFSM Exclude option * Add TEXTMESSAGE module exclude option --------- Co-authored-by: Ben Meadors --- src/PowerFSM.cpp | 8 ++++++-- src/PowerFSM.h | 25 ++++++++++++++++++++++++- src/PowerFSMThread.h | 4 ++++ src/configuration.h | 2 ++ src/modules/AdminModule.cpp | 5 ++++- src/modules/Modules.cpp | 2 ++ 6 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 0a954c1b8..8bf7d3218 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -22,7 +22,10 @@ #ifndef SLEEP_TIME #define SLEEP_TIME 30 #endif - +#if EXCLUDE_POWER_FSM +FakeFsm powerFSM; +void PowerFSM_setup(){}; +#else /// Should we behave as if we have AC power now? static bool isPowered() { @@ -395,4 +398,5 @@ void PowerFSM_setup() #endif powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/PowerFSM.h b/src/PowerFSM.h index 752a9f7dd..13dfdc4cc 100644 --- a/src/PowerFSM.h +++ b/src/PowerFSM.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "configuration.h" // See sw-design.md for documentation @@ -22,7 +22,30 @@ #define EVENT_SHUTDOWN 16 // force a full shutdown now (not just sleep) #define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen +#if EXCLUDE_POWER_FSM +class FakeFsm +{ + public: + void trigger(int event) + { + if (event == EVENT_SERIAL_CONNECTED) { + serialConnected = true; + } else if (event == EVENT_SERIAL_DISCONNECTED) { + serialConnected = false; + } + }; + bool getState() { return serialConnected; }; + + private: + bool serialConnected = false; +}; +extern FakeFsm powerFSM; +void PowerFSM_setup(); + +#else +#include extern Fsm powerFSM; extern State stateON, statePOWER, stateSERIAL, stateDARK; void PowerFSM_setup(); +#endif \ No newline at end of file diff --git a/src/PowerFSMThread.h b/src/PowerFSMThread.h index fb640dd8b..c842f4515 100644 --- a/src/PowerFSMThread.h +++ b/src/PowerFSMThread.h @@ -18,6 +18,7 @@ class PowerFSMThread : public OSThread protected: int32_t runOnce() override { +#if !EXCLUDE_POWER_FSM powerFSM.run_machine(); /// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake @@ -35,6 +36,9 @@ class PowerFSMThread : public OSThread } return 100; +#else + return INT32_MAX; +#endif } }; diff --git a/src/configuration.h b/src/configuration.h index 4ebc59ffc..5365db239 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -256,6 +256,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_MQTT 1 #define MESHTASTIC_EXCLUDE_POWERMON 1 #define MESHTASTIC_EXCLUDE_I2C 1 +#define MESHTASTIC_EXCLUDE_POWER_FSM 1 #endif // Turn off all optional modules @@ -269,6 +270,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_RANGETEST 1 #define MESHTASTIC_EXCLUDE_REMOTEHARDWARE 1 #define MESHTASTIC_EXCLUDE_STOREFORWARD 1 +#define MESHTASTIC_EXCLUDE_TEXTMESSAGE 1 #define MESHTASTIC_EXCLUDE_ATAK 1 #define MESHTASTIC_EXCLUDE_CANNEDMESSAGES 1 #define MESHTASTIC_EXCLUDE_NEIGHBORINFO 1 diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 8287f8a00..0f053a049 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -818,8 +818,11 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r #endif #endif conn.has_serial = true; // No serial-less devices +#if !EXCLUDE_POWER_FSM conn.serial.is_connected = powerFSM.getState() == &stateSERIAL; - conn.serial.baud = SERIAL_BAUD; +#else + conn.serial.is_connected = powerFSM.getState(); +#endif conn.serial.baud = SERIAL_BAUD; r.get_device_connection_status_response = conn; r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 3df8fc170..a1c9f4a03 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -106,7 +106,9 @@ void setupModules() #if !MESHTASTIC_EXCLUDE_WAYPOINT waypointModule = new WaypointModule(); #endif +#if !MESHTASTIC_EXCLUDE_TEXTMESSAGE textMessageModule = new TextMessageModule(); +#endif #if !MESHTASTIC_EXCLUDE_TRACEROUTE traceRouteModule = new TraceRouteModule(); #endif From 5111bd703a20a762c8399f85e6d40ec730be109e Mon Sep 17 00:00:00 2001 From: Tilen Komel <46865859+KomelT@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:23:31 +0200 Subject: [PATCH 070/305] Updted protobuf url (#4411) --- src/mesh/http/ContentHandler.cpp | 4 ++-- src/mesh/raspihttp/PiWebServer.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 9a981ee54..772b3e821 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -161,7 +161,7 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) res->setHeader("Content-Type", "application/x-protobuf"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); - res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto"); + res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); uint8_t txBuf[MAX_STREAM_BUF_SIZE]; uint32_t len = 1; @@ -205,7 +205,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Headers", "Content-Type"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "PUT, OPTIONS"); - res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto"); + res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); if (req->getMethod() == "OPTIONS") { res->setStatusCode(204); // Success with no content diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp index bffd6c340..d7e596759 100644 --- a/src/mesh/raspihttp/PiWebServer.cpp +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -230,7 +230,7 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo ulfius_add_header_to_response(res, "Access-Control-Allow-Origin", "*"); ulfius_add_header_to_response(res, "Access-Control-Allow-Methods", "PUT, OPTIONS"); ulfius_add_header_to_response(res, "X-Protobuf-Schema", - "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto"); + "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); if (req->http_verb == "OPTIONS") { ulfius_set_response_properties(res, U_OPT_STATUS, 204); @@ -267,7 +267,7 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, ulfius_add_header_to_response(res, "Access-Control-Allow-Origin", "*"); ulfius_add_header_to_response(res, "Access-Control-Allow-Methods", "GET"); ulfius_add_header_to_response(res, "X-Protobuf-Schema", - "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto"); + "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); uint8_t txBuf[MAX_STREAM_BUF_SIZE]; uint32_t len = 1; From 02ae24b6fa7fb66b9126fb8a24e7b9f55e88e698 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 9 Aug 2024 09:08:14 +0800 Subject: [PATCH 071/305] Remove outdated comments (#4417) These comments from four years ago no longer reflect how things work. --- src/mesh/MeshService.cpp | 15 +-------------- src/mesh/Router.cpp | 10 +--------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index fd07c3801..07dd71ddc 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -44,17 +44,6 @@ arbitrating to select a node number and keeping the current nodedb. The algorithm is as follows: * when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so the new node can build its node db) -* If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that -indicates a collision has occurred and the following steps should happen: - -If the receiving node (that was already in the mesh)'s macaddr is LOWER than the new User who just tried to sign in: it gets to -keep its nodenum. We send a broadcast message of OUR User (we use a broadcast so that the other node can receive our message, -considering we have the same id - it also serves to let observers correct their nodedb) - this case is rare so it should be okay. - -If any node receives a User where the macaddr is GTE than their local macaddr, they have been vetoed and should pick a new random -nodenum (filtering against whatever it knows about the nodedb) and rebroadcast their User. - -FIXME in the initial proof of concept we just skip the entire want/deny flow and just hand pick node numbers at first. */ MeshService *service; @@ -77,8 +66,6 @@ MeshService::MeshService() void MeshService::init() { - // moved much earlier in boot (called from setup()) - // nodeDB.init(); #if HAS_GPS if (gps) gpsObserver.observe(&gps->newStatus); @@ -405,4 +392,4 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) bool MeshService::isToPhoneQueueEmpty() { return toPhoneQueue.isEmpty(); -} \ No newline at end of file +} diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1260b7c04..87fcfe1b6 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -19,14 +19,6 @@ #include "serialization/MeshPacketSerializer.h" #endif #include "../userPrefs.h" -/** - * Router todo - * - * DONE: Implement basic interface and use it elsewhere in app - * Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s - * Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet) - * - **/ #define MAX_RX_FROMRADIO \ 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big @@ -546,4 +538,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) handleReceived(p); packetPool.release(p); -} \ No newline at end of file +} From b498c0bfbfe2630605318482b0ba2b619bee4663 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 9 Aug 2024 09:18:18 +0800 Subject: [PATCH 072/305] [WIP] Add support for Airoha AG3335 GPS (#4394) * Add GPS detection code for Airoha AG3335 Airoha AG3335 is used in Seeed T-1000E Tracker * Add support for Airoha AG3335 Airoha AG3335 is used in Seeed T-1000E Tracker. This adds detection code, and code to configure its use. --------- Co-authored-by: Ben Meadors --- src/gps/GPS.cpp | 24 ++++++++++++++++++++++++ src/gps/GPS.h | 5 +++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 93742a99a..fcc7c0bd6 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -504,6 +504,22 @@ bool GPS::setup() delay(250); _serial_gps->write("$CFGMSG,6,1,0\r\n"); delay(250); + } else if (gnssModel == GNSS_MODEL_AG3335) { + + _serial_gps->write("$PAIR066,1,0,1,0,0,1*3B"); // Enable GPS+GALILEO+NAVIC + + // Configure NMEA (sentences will output once per fix) + _serial_gps->write("$PAIR062,0,0*3F"); // GGA ON + _serial_gps->write("$PAIR062,1,0*3F"); // GLL OFF + _serial_gps->write("$PAIR062,2,1*3D"); // GSA ON + _serial_gps->write("$PAIR062,3,0*3D"); // GSV OFF + _serial_gps->write("$PAIR062,4,0*3B"); // RMC ON + _serial_gps->write("$PAIR062,5,0*3B"); // VTG OFF + _serial_gps->write("$PAIR062,6,1*39"); // ZDA ON + + delay(250); + _serial_gps->write("$PAIR513*3D"); // save configuration + } else if (gnssModel == GNSS_MODEL_UBLOX) { // Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command) // We need set it because by default it is GPS only, and we want to use GLONASS too @@ -1218,6 +1234,14 @@ GnssModel_t GPS::probe(int serialSpeed) return GNSS_MODEL_ATGM336H; } + /* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */ + clearBuffer(); + _serial_gps->write("PAIR020*38\r\n"); + if (getACK("$PAIR020,AG3335", 500) == GNSS_RESPONSE_OK) { + LOG_INFO("Aioha AG3335 detected, using AG3335 Module\n"); + return GNSS_MODEL_AG3335; + } + // Get version information clearBuffer(); _serial_gps->write("$PCAS06,0*1B\r\n"); diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 87d03c592..1c0977bdd 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -28,7 +28,8 @@ typedef enum { GNSS_MODEL_UBLOX, GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, - GNSS_MODEL_MTK_L76B + GNSS_MODEL_MTK_L76B, + GNSS_MODEL_AG3335 } GnssModel_t; typedef enum { @@ -302,4 +303,4 @@ class GPS : private concurrency::OSThread }; extern GPS *gps; -#endif // Exclude GPS \ No newline at end of file +#endif // Exclude GPS From 5b4530325f83d836740128cec1a6242613039f65 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 8 Aug 2024 20:53:26 -0500 Subject: [PATCH 073/305] Short circuit while the probe code does not auto-detect --- src/gps/GPS.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index fcc7c0bd6..82370937b 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1195,6 +1195,9 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->updateBaudRate(serialSpeed); } #endif +#ifdef GNSS_AIROHA + return GNSS_MODEL_AG3335; +#endif #ifdef GPS_DEBUG for (int i = 0; i < 20; i++) { getACK("$GP", 200); @@ -1241,6 +1244,9 @@ GnssModel_t GPS::probe(int serialSpeed) LOG_INFO("Aioha AG3335 detected, using AG3335 Module\n"); return GNSS_MODEL_AG3335; } + // Get version information for Airoha AG3335 + clearBuffer(); + _serial_gps->write("$PMTK605*31\r\n"); // Get version information clearBuffer(); From a7da3537e2710cbebc27151c63c0ba612a2d876a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 9 Aug 2024 00:52:31 -0500 Subject: [PATCH 074/305] Adds MESHTASTIC_EXCLUDE_TZ option (#4423) --- src/configuration.h | 3 ++- src/gps/RTC.cpp | 4 ++++ src/main.cpp | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/configuration.h b/src/configuration.h index 5365db239..9148f1d37 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -257,6 +257,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_POWERMON 1 #define MESHTASTIC_EXCLUDE_I2C 1 #define MESHTASTIC_EXCLUDE_POWER_FSM 1 +#define MESHTASTIC_EXCLUDE_TZ 1 #endif // Turn off all optional modules @@ -312,4 +313,4 @@ along with this program. If not, see . #endif #include "DebugConfiguration.h" -#include "RF95Configuration.h" +#include "RF95Configuration.h" \ No newline at end of file diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index d60e3825c..710baca98 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -256,6 +256,7 @@ uint32_t getValidTime(RTCQuality minQuality, bool local) time_t gm_mktime(struct tm *tm) { +#if !MESHTASTIC_EXCLUDE_TZ setenv("TZ", "GMT0", 1); time_t res = mktime(tm); if (*config.device.tzdef) { @@ -264,4 +265,7 @@ time_t gm_mktime(struct tm *tm) setenv("TZ", "UTC0", 1); } return res; +#else + return mktime(tm); +#endif } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f4f7ad537..0a3c1ae0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -689,6 +689,7 @@ void setup() screen = new graphics::Screen(screen_found, screen_model, screen_geometry); // setup TZ prior to time actions. +#if !MESHTASTIC_EXCLUDE_TZ if (*config.device.tzdef) { setenv("TZ", config.device.tzdef, 1); } else { @@ -696,6 +697,7 @@ void setup() } tzset(); LOG_DEBUG("Set Timezone to %s\n", getenv("TZ")); +#endif readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time) From c6a9edf8c7fdf0df8da46a5c9930748309e7cf89 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 9 Aug 2024 01:43:13 -0500 Subject: [PATCH 075/305] Move printBytes to meshUtils (#4424) --- src/mesh/Router.cpp | 9 +-------- src/meshUtils.cpp | 8 ++++++++ src/meshUtils.h | 2 ++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 87fcfe1b6..79095805d 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -7,6 +7,7 @@ #include "configuration.h" #include "main.h" #include "mesh-pb-constants.h" +#include "meshUtils.h" #include "modules/RoutingModule.h" #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" @@ -188,14 +189,6 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) } } -void printBytes(const char *label, const uint8_t *p, size_t numbytes) -{ - LOG_DEBUG("%s: ", label); - for (size_t i = 0; i < numbytes; i++) - LOG_DEBUG("%02x ", p[i]); - LOG_DEBUG("\n"); -} - /** * Send a packet on a suitable interface. This routine will * later free() the packet to pool. This routine is not allowed to stall. diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index 59d4e6714..86d237129 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -56,4 +56,12 @@ char *strnstr(const char *s, const char *find, size_t slen) s--; } return ((char *)s); +} + +void printBytes(const char *label, const uint8_t *p, size_t numbytes) +{ + LOG_DEBUG("%s: ", label); + for (size_t i = 0; i < numbytes; i++) + LOG_DEBUG("%02x ", p[i]); + LOG_DEBUG("\n"); } \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index e32ef230a..9dfe9b558 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -1,4 +1,6 @@ #pragma once +#include "DebugConfiguration.h" +#include /// C++ v17+ clamp function, limits a given value to a range defined by lo and hi template constexpr const T &clamp(const T &v, const T &lo, const T &hi) From d8bdb92efe47d6dc180026def55b973e20db1048 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 06:35:26 -0500 Subject: [PATCH 076/305] [create-pull-request] automated change (#4409) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index b8bb5e67d..0450e7054 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 4 -build = 2 +build = 3 From e38aca3cba890d5aa00bd94c052f1bf2195fb5fb Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 9 Aug 2024 19:35:42 +0800 Subject: [PATCH 077/305] NimbleBluetooth.h is not required in MeshService. (#4419) There are no calls to the functions defined in Nimble from this class. See also older comment on line 8 about the dream to seperate mesh and bluetooth :) --- src/mesh/MeshService.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 07dd71ddc..697644a4f 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -19,10 +19,6 @@ #include #include -#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH -#include "nimble/NimbleBluetooth.h" -#endif - #if ARCH_PORTDUINO #include "PortduinoGlue.h" #endif From 3ab4bebdcba570686e97f6c75b0dfed44ea7b972 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 06:37:49 -0500 Subject: [PATCH 078/305] [create-pull-request] automated change (#4426) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/protobufs b/protobufs index d0fe91ab9..2fa7d6a4b 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit d0fe91ab99734cacdc188403f73fe30f766917cf +Subproject commit 2fa7d6a4b702fcd58b54b0d1d6e4b3b85164f649 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index d0e643dff..bef2abf9b 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,6 +168,8 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; + /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ + int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -178,8 +180,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ - int32_t factory_reset; + /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ + int32_t factory_reset_config; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -254,11 +256,12 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 +#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_tag 99 +#define meshtastic_AdminMessage_factory_reset_config_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -298,11 +301,12 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL From 3878e025e4c467f151f3489ea30ff4261e3dabd7 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 9 Aug 2024 08:38:29 -0500 Subject: [PATCH 079/305] Split factory reset into config and device variants (#4427) * Split factory reset into config and device variants * Trunk * Default only in header --- src/mesh/NodeDB.cpp | 21 ++++++++++++--------- src/mesh/NodeDB.h | 2 +- src/modules/AdminModule.cpp | 10 ++++++++-- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index d74e96bc6..0f6c444ce 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -211,7 +211,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset) return didFactoryReset; } -bool NodeDB::factoryReset() +bool NodeDB::factoryReset(bool eraseBleBonds) { LOG_INFO("Performing factory reset!\n"); // first, remove the "/prefs" (this removes most prefs) @@ -228,18 +228,21 @@ bool NodeDB::factoryReset() installDefaultChannels(); // third, write everything to disk saveToDisk(); + if (eraseBleBonds) { + LOG_INFO("Erasing BLE bonds\n"); #ifdef ARCH_ESP32 - // This will erase what's in NVS including ssl keys, persistent variables and ble pairing - nvs_flash_erase(); + // This will erase what's in NVS including ssl keys, persistent variables and ble pairing + nvs_flash_erase(); #endif #ifdef ARCH_NRF52 - Bluefruit.begin(); - LOG_INFO("Clearing bluetooth bonds!\n"); - bond_print_list(BLE_GAP_ROLE_PERIPH); - bond_print_list(BLE_GAP_ROLE_CENTRAL); - Bluefruit.Periph.clearBonds(); - Bluefruit.Central.clearBonds(); + Bluefruit.begin(); + LOG_INFO("Clearing bluetooth bonds!\n"); + bond_print_list(BLE_GAP_ROLE_PERIPH); + bond_print_list(BLE_GAP_ROLE_CENTRAL); + Bluefruit.Periph.clearBonds(); + Bluefruit.Central.clearBonds(); #endif + } return true; } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index c00ab8c03..447ce10d4 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -127,7 +127,7 @@ class NodeDB void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(NodeNum nodeNum); - bool factoryReset(); + bool factoryReset(bool eraseBleBonds = false); LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct); diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 0f053a049..778b7193d 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -162,12 +162,18 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta handleGetDeviceMetadata(mp); break; } - case meshtastic_AdminMessage_factory_reset_tag: { - LOG_INFO("Initiating factory reset\n"); + case meshtastic_AdminMessage_factory_reset_config_tag: { + LOG_INFO("Initiating factory config reset\n"); nodeDB->factoryReset(); reboot(DEFAULT_REBOOT_SECONDS); break; } + case meshtastic_AdminMessage_factory_reset_device_tag: { + LOG_INFO("Initiating full factory reset\n"); + nodeDB->factoryReset(true); + reboot(DEFAULT_REBOOT_SECONDS); + break; + } case meshtastic_AdminMessage_nodedb_reset_tag: { LOG_INFO("Initiating node-db reset\n"); nodeDB->resetNodes(); From debf4b934f3cffe6f9410cb320406c395e26030d Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:26:22 +0200 Subject: [PATCH 080/305] Fix for "has default channel" with empty channel name (#4430) --- src/mesh/Channels.cpp | 9 +++++---- src/mesh/Channels.h | 4 ++-- src/modules/esp32/StoreForwardModule.cpp | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 8d5b4353b..6e721d9b0 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -309,12 +309,14 @@ const char *Channels::getName(size_t chIndex) return channelName; } -bool Channels::isDefaultChannel(const meshtastic_Channel &ch) +bool Channels::isDefaultChannel(ChannelIndex chIndex) { + const auto &ch = getByIndex(chIndex); if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) { + const char *name = getName(chIndex); const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false); // Check if the name is the default derived from the modem preset - if (strcmp(ch.settings.name, presetName) == 0) + if (strcmp(name, presetName) == 0) return true; } return false; @@ -327,8 +329,7 @@ bool Channels::hasDefaultChannel() return false; // Check if any of the channels are using the default name and PSK for (size_t i = 0; i < getNumChannels(); i++) { - const auto &ch = getByIndex(i); - if (isDefaultChannel(ch)) + if (isDefaultChannel(i)) return true; } return false; diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index eaccea8e1..4f87cb309 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -84,7 +84,7 @@ class Channels int16_t setActiveByIndex(ChannelIndex channelIndex); // Returns true if the channel has the default name and PSK - bool isDefaultChannel(const meshtastic_Channel &ch); + bool isDefaultChannel(ChannelIndex chIndex); // Returns true if we can be reached via a channel with the default settings given a region and modem preset bool hasDefaultChannel(); @@ -129,4 +129,4 @@ class Channels }; /// Singleton channel table -extern Channels channels; +extern Channels channels; \ No newline at end of file diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index 7581bbc38..c696d342a 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -382,7 +382,7 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m LOG_DEBUG("*** Legacy Request to send\n"); // Send the last 60 minutes of messages. - if (this->busy || channels.isDefaultChannel(channels.getByIndex(mp.channel))) { + if (this->busy || channels.isDefaultChannel(mp.channel)) { sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response); } else { storeForwardModule->historySend(historyReturnWindow * 60, getFrom(&mp)); @@ -447,7 +447,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, requests_history++; LOG_INFO("*** Client Request to send HISTORY\n"); // Send the last 60 minutes of messages. - if (this->busy || channels.isDefaultChannel(channels.getByIndex(mp.channel))) { + if (this->busy || channels.isDefaultChannel(mp.channel)) { sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response); } else { if ((p->which_variant == meshtastic_StoreAndForward_history_tag) && (p->variant.history.window > 0)) { From 3513d88794e83ddf436df77c5ca1e93709ae8611 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 07:25:05 -0500 Subject: [PATCH 081/305] Protobufs --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 +- src/mesh/generated/meshtastic/config.pb.cpp | 3 + src/mesh/generated/meshtastic/config.pb.h | 83 +++++++++-- src/mesh/generated/meshtastic/deviceonly.pb.h | 4 +- src/mesh/generated/meshtastic/localonly.pb.h | 14 +- src/mesh/generated/meshtastic/mesh.pb.cpp | 5 +- src/mesh/generated/meshtastic/mesh.pb.h | 110 +++++++++++--- src/mesh/generated/meshtastic/telemetry.pb.h | 136 +++++++++++------- 9 files changed, 273 insertions(+), 96 deletions(-) diff --git a/protobufs b/protobufs index 2fa7d6a4b..c112ce6e1 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 2fa7d6a4b702fcd58b54b0d1d6e4b3b85164f649 +Subproject commit c112ce6e1392e4bc812655fae5e8461c932b5267 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bef2abf9b..d0e643dff 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,8 +168,6 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; - /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ - int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -180,8 +178,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ - int32_t factory_reset_config; + /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ + int32_t factory_reset; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -256,12 +254,11 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 -#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_config_tag 99 +#define meshtastic_AdminMessage_factory_reset_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -301,12 +298,11 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp index bb82198c0..c6274aed4 100644 --- a/src/mesh/generated/meshtastic/config.pb.cpp +++ b/src/mesh/generated/meshtastic/config.pb.cpp @@ -33,6 +33,9 @@ PB_BIND(meshtastic_Config_LoRaConfig, meshtastic_Config_LoRaConfig, 2) PB_BIND(meshtastic_Config_BluetoothConfig, meshtastic_Config_BluetoothConfig, AUTO) +PB_BIND(meshtastic_Config_SecurityConfig, meshtastic_Config_SecurityConfig, AUTO) + + diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 44a86f4d6..dbb0deb00 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -248,7 +248,8 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0, /* Long Range - Slow */ meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, - /* Very Long Range - Slow */ + /* Very Long Range - Slow + Deprecated in 2.5: Works only with txco and is unusably slow */ meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW = 2, /* Medium Range - Slow */ meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW = 3, @@ -259,7 +260,11 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { /* Short Range - Fast */ meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST = 6, /* Long Range - Moderately Fast */ - meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7 + meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7, + /* Short Range - Turbo + This is the fastest preset and the only one with 500kHz bandwidth. + It is not legal to use in all regions due to this wider bandwidth. */ + meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8 } meshtastic_Config_LoRaConfig_ModemPreset; typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { @@ -276,10 +281,12 @@ typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { typedef struct _meshtastic_Config_DeviceConfig { /* Sets the role of node */ meshtastic_Config_DeviceConfig_Role role; - /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI */ + /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI + Moved to SecurityConfig */ bool serial_enabled; /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Set this to true to leave the debug log outputting even when API is active. */ + Set this to true to leave the debug log outputting even when API is active. + Moved to SecurityConfig */ bool debug_log_enabled; /* For boards without a hard wired button, this is the pin number that will be used Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */ @@ -295,7 +302,8 @@ typedef struct _meshtastic_Config_DeviceConfig { /* Treat double tap interrupt on supported accelerometers as a button press if set to true */ bool double_tap_as_button_press; /* If true, device is considered to be "managed" by a mesh administrator - Clients should then limit available configuration and administrative options inside the user interface */ + Clients should then limit available configuration and administrative options inside the user interface + Moved to SecurityConfig */ bool is_managed; /* Disables the triple-press of user button to enable or disable GPS */ bool disable_triple_click; @@ -515,10 +523,37 @@ typedef struct _meshtastic_Config_BluetoothConfig { meshtastic_Config_BluetoothConfig_PairingMode mode; /* Specified PIN for PairingMode.FixedPin */ uint32_t fixed_pin; - /* Enables device (serial style logs) over Bluetooth */ + /* Enables device (serial style logs) over Bluetooth + Moved to SecurityConfig */ bool device_logging_enabled; } meshtastic_Config_BluetoothConfig; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_public_key_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_private_key_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_admin_key_t; +typedef struct _meshtastic_Config_SecurityConfig { + /* The public key of the user's device. + Sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_Config_SecurityConfig_public_key_t public_key; + /* The private key of the device. + Used to create a shared key with a remote device. */ + meshtastic_Config_SecurityConfig_private_key_t private_key; + /* The public key authorized to send admin messages to this node. */ + meshtastic_Config_SecurityConfig_admin_key_t admin_key; + /* If true, device is considered to be "managed" by a mesh administrator via admin messages + Device is managed by a mesh administrator. */ + bool is_managed; + /* Serial Console over the Stream API." */ + bool serial_enabled; + /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). + Output live debug logging over serial. */ + bool debug_log_api_enabled; + /* Enables device (serial style logs) over Bluetooth */ + bool bluetooth_logging_enabled; + /* Allow incoming device control over the insecure legacy admin channel. */ + bool admin_channel_enabled; +} meshtastic_Config_SecurityConfig; + typedef struct _meshtastic_Config { pb_size_t which_payload_variant; union { @@ -529,6 +564,7 @@ typedef struct _meshtastic_Config { meshtastic_Config_DisplayConfig display; meshtastic_Config_LoRaConfig lora; meshtastic_Config_BluetoothConfig bluetooth; + meshtastic_Config_SecurityConfig security; } payload_variant; } meshtastic_Config; @@ -583,8 +619,8 @@ extern "C" { #define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1)) #define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST -#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE -#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE+1)) +#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO +#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1)) #define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN #define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN @@ -612,6 +648,7 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_mode_ENUMTYPE meshtastic_Config_BluetoothConfig_PairingMode + /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} #define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -622,6 +659,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} @@ -631,6 +669,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Config_DeviceConfig_role_tag 1 @@ -711,6 +750,14 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_mode_tag 2 #define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3 #define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4 +#define meshtastic_Config_SecurityConfig_public_key_tag 1 +#define meshtastic_Config_SecurityConfig_private_key_tag 2 +#define meshtastic_Config_SecurityConfig_admin_key_tag 3 +#define meshtastic_Config_SecurityConfig_is_managed_tag 4 +#define meshtastic_Config_SecurityConfig_serial_enabled_tag 5 +#define meshtastic_Config_SecurityConfig_debug_log_api_enabled_tag 6 +#define meshtastic_Config_SecurityConfig_bluetooth_logging_enabled_tag 7 +#define meshtastic_Config_SecurityConfig_admin_channel_enabled_tag 8 #define meshtastic_Config_device_tag 1 #define meshtastic_Config_position_tag 2 #define meshtastic_Config_power_tag 3 @@ -718,6 +765,7 @@ extern "C" { #define meshtastic_Config_display_tag 5 #define meshtastic_Config_lora_tag 6 #define meshtastic_Config_bluetooth_tag 7 +#define meshtastic_Config_security_tag 8 /* Struct field encoding specification for nanopb */ #define meshtastic_Config_FIELDLIST(X, a) \ @@ -727,7 +775,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,power,payload_variant.power) X(a, STATIC, ONEOF, MESSAGE, (payload_variant,network,payload_variant.network), 4) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.display), 5) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) #define meshtastic_Config_CALLBACK NULL #define meshtastic_Config_DEFAULT NULL #define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -737,6 +786,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bl #define meshtastic_Config_payload_variant_display_MSGTYPE meshtastic_Config_DisplayConfig #define meshtastic_Config_payload_variant_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig +#define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ @@ -849,6 +899,18 @@ X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4) #define meshtastic_Config_BluetoothConfig_CALLBACK NULL #define meshtastic_Config_BluetoothConfig_DEFAULT NULL +#define meshtastic_Config_SecurityConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, private_key, 2) \ +X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ +X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ +X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ +X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ +X(a, STATIC, SINGULAR, BOOL, bluetooth_logging_enabled, 7) \ +X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8) +#define meshtastic_Config_SecurityConfig_CALLBACK NULL +#define meshtastic_Config_SecurityConfig_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_Config_msg; extern const pb_msgdesc_t meshtastic_Config_DeviceConfig_msg; extern const pb_msgdesc_t meshtastic_Config_PositionConfig_msg; @@ -858,6 +920,7 @@ extern const pb_msgdesc_t meshtastic_Config_NetworkConfig_IpV4Config_msg; extern const pb_msgdesc_t meshtastic_Config_DisplayConfig_msg; extern const pb_msgdesc_t meshtastic_Config_LoRaConfig_msg; extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; +extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_Config_fields &meshtastic_Config_msg @@ -869,6 +932,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; #define meshtastic_Config_DisplayConfig_fields &meshtastic_Config_DisplayConfig_msg #define meshtastic_Config_LoRaConfig_fields &meshtastic_Config_LoRaConfig_msg #define meshtastic_Config_BluetoothConfig_fields &meshtastic_Config_BluetoothConfig_msg +#define meshtastic_Config_SecurityConfig_fields &meshtastic_Config_SecurityConfig_msg /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size @@ -880,6 +944,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 +#define meshtastic_Config_SecurityConfig_size 112 #define meshtastic_Config_size 199 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index eb37f4f95..2c91fe30e 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -306,8 +306,8 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* meshtastic_DeviceState_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 166 -#define meshtastic_OEMStore_size 3388 +#define meshtastic_NodeInfoLite_size 200 +#define meshtastic_OEMStore_size 3502 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 983f48ad3..c612b24ab 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -38,6 +38,9 @@ typedef struct _meshtastic_LocalConfig { incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. */ uint32_t version; + /* The part of the config that is specific to Security settings */ + bool has_security; + meshtastic_Config_SecurityConfig security; } meshtastic_LocalConfig; typedef struct _meshtastic_LocalModuleConfig { @@ -92,9 +95,9 @@ extern "C" { #endif /* Initializer values for message structs */ -#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0} +#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default} #define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default} -#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0} +#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero} #define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero} /* Field tags (for use in manual encoding/decoding) */ @@ -106,6 +109,7 @@ extern "C" { #define meshtastic_LocalConfig_lora_tag 6 #define meshtastic_LocalConfig_bluetooth_tag 7 #define meshtastic_LocalConfig_version_tag 8 +#define meshtastic_LocalConfig_security_tag 9 #define meshtastic_LocalModuleConfig_mqtt_tag 1 #define meshtastic_LocalModuleConfig_serial_tag 2 #define meshtastic_LocalModuleConfig_external_notification_tag 3 @@ -130,7 +134,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, network, 4) \ X(a, STATIC, OPTIONAL, MESSAGE, display, 5) \ X(a, STATIC, OPTIONAL, MESSAGE, lora, 6) \ X(a, STATIC, OPTIONAL, MESSAGE, bluetooth, 7) \ -X(a, STATIC, SINGULAR, UINT32, version, 8) +X(a, STATIC, SINGULAR, UINT32, version, 8) \ +X(a, STATIC, OPTIONAL, MESSAGE, security, 9) #define meshtastic_LocalConfig_CALLBACK NULL #define meshtastic_LocalConfig_DEFAULT NULL #define meshtastic_LocalConfig_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -140,6 +145,7 @@ X(a, STATIC, SINGULAR, UINT32, version, 8) #define meshtastic_LocalConfig_display_MSGTYPE meshtastic_Config_DisplayConfig #define meshtastic_LocalConfig_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_LocalConfig_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig +#define meshtastic_LocalConfig_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_LocalModuleConfig_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, mqtt, 1) \ @@ -181,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 555 +#define meshtastic_LocalConfig_size 669 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp index 3fa81e131..8c8b9ded7 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.cpp +++ b/src/mesh/generated/meshtastic/mesh.pb.cpp @@ -30,7 +30,7 @@ PB_BIND(meshtastic_MqttClientProxyMessage, meshtastic_MqttClientProxyMessage, 2) PB_BIND(meshtastic_MeshPacket, meshtastic_MeshPacket, 2) -PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO) +PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, 2) PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO) @@ -45,6 +45,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO) PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2) +PB_BIND(meshtastic_ClientNotification, meshtastic_ClientNotification, 2) + + PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO) diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 59664b792..1d677e0d5 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -367,10 +367,13 @@ typedef enum _meshtastic_LogRecord_Level { typedef struct _meshtastic_Position { /* The new preferred location encoding, multiply by 1e-7 to get degrees in floating point */ + bool has_latitude_i; int32_t latitude_i; /* TODO: REPLACE */ + bool has_longitude_i; int32_t longitude_i; /* In meters above MSL (but see issue #359) */ + bool has_altitude; int32_t altitude; /* This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its time if it is sent over @@ -386,8 +389,10 @@ typedef struct _meshtastic_Position { /* Pos. timestamp milliseconds adjustment (rarely available or required) */ int32_t timestamp_millis_adjust; /* HAE altitude in meters - can be used instead of MSL altitude */ + bool has_altitude_hae; int32_t altitude_hae; /* Geoidal separation in meters */ + bool has_altitude_geoidal_separation; int32_t altitude_geoidal_separation; /* Horizontal, Vertical and Position Dilution of Precision, in 1/100 units - PDOP is sufficient for most cases @@ -409,8 +414,10 @@ typedef struct _meshtastic_Position { - "heading" is where the fuselage points (measured in horizontal plane) - "yaw" indicates a relative rotation about the vertical axis TODO: REMOVE/INTEGRATE */ + bool has_ground_speed; uint32_t ground_speed; /* TODO: REPLACE */ + bool has_ground_track; uint32_t ground_track; /* GPS fix quality (from NMEA GxGGA statement or similar) */ uint32_t fix_quality; @@ -432,6 +439,7 @@ typedef struct _meshtastic_Position { uint32_t precision_bits; } meshtastic_Position; +typedef PB_BYTES_ARRAY_T(32) meshtastic_User_public_key_t; /* Broadcast when a newly powered mesh node wants to find a node num it can use Sent from the phone over bluetooth to set the user id for the owner of this node. Also sent from nodes to each other when a new node signs on (so all clients can have this info) @@ -478,6 +486,9 @@ typedef struct _meshtastic_User { bool is_licensed; /* Indicates that the user's role in the mesh */ meshtastic_Config_DeviceConfig_Role role; + /* The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_User_public_key_t public_key; } meshtastic_User; /* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */ @@ -539,8 +550,10 @@ typedef struct _meshtastic_Waypoint { /* Id of the waypoint */ uint32_t id; /* latitude_i */ + bool has_latitude_i; int32_t latitude_i; /* longitude_i */ + bool has_longitude_i; int32_t longitude_i; /* Time the waypoint is to expire (epoch) */ uint32_t expire; @@ -572,6 +585,7 @@ typedef struct _meshtastic_MqttClientProxyMessage { } meshtastic_MqttClientProxyMessage; typedef PB_BYTES_ARRAY_T(256) meshtastic_MeshPacket_encrypted_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_MeshPacket_public_key_t; /* A packet envelope sent/received over the mesh only payload_variant is sent in the payload portion of the LORA packet. The other fields are either not sent at all, or sent in the special 16 byte LORA header. */ @@ -642,6 +656,10 @@ typedef struct _meshtastic_MeshPacket { /* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */ uint8_t hop_start; + /* Records the public key the packet was encrypted with, if applicable. */ + meshtastic_MeshPacket_public_key_t public_key; + /* Indicates whether the packet was en/decrypted using PKI */ + bool pki_encrypted; } meshtastic_MeshPacket; /* The bluetooth to device link: @@ -731,6 +749,22 @@ typedef struct _meshtastic_QueueStatus { uint32_t mesh_packet_id; } meshtastic_QueueStatus; +/* A notification message from the device to the client + To be used for important messages that should to be displayed to the user + in the form of push notifications or validation messages when saving + invalid configuration. */ +typedef struct _meshtastic_ClientNotification { + /* The id of the packet we're notifying in response to */ + bool has_reply_id; + uint32_t reply_id; + /* Seconds since 1970 - or 0 for unknown/unset */ + uint32_t time; + /* The level type of notification */ + meshtastic_LogRecord_Level level; + /* The message body of the notification */ + char message[400]; +} meshtastic_ClientNotification; + /* Individual File info for the device */ typedef struct _meshtastic_FileInfo { /* The fully qualified path of the file */ @@ -845,6 +879,8 @@ typedef struct _meshtastic_FromRadio { meshtastic_MqttClientProxyMessage mqttClientProxyMessage; /* File system manifest messages */ meshtastic_FileInfo fileInfo; + /* Notification message to the client */ + meshtastic_ClientNotification clientNotification; }; } meshtastic_FromRadio; @@ -987,6 +1023,8 @@ extern "C" { +#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level + #define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum @@ -1003,19 +1041,20 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN} +#define meshtastic_Position_init_default {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}} #define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} -#define meshtastic_Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0} +#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0} +#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} #define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} #define meshtastic_MyNodeInfo_init_default {0, 0, 0} #define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_default {0, 0, 0, 0} #define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}} +#define meshtastic_ClientNotification_init_default {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""} #define meshtastic_FileInfo_init_default {"", 0} #define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}} #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} @@ -1027,19 +1066,20 @@ extern "C" { #define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}} #define meshtastic_resend_chunks_init_default {{{NULL}, NULL}} #define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}} -#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN} +#define meshtastic_Position_init_zero {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}} #define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} -#define meshtastic_Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0} +#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0} +#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} #define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} #define meshtastic_MyNodeInfo_init_zero {0, 0, 0} #define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_zero {0, 0, 0, 0} #define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}} +#define meshtastic_ClientNotification_init_zero {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""} #define meshtastic_FileInfo_init_zero {"", 0} #define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} @@ -1083,6 +1123,7 @@ extern "C" { #define meshtastic_User_hw_model_tag 5 #define meshtastic_User_is_licensed_tag 6 #define meshtastic_User_role_tag 7 +#define meshtastic_User_public_key_tag 8 #define meshtastic_RouteDiscovery_route_tag 1 #define meshtastic_Routing_route_request_tag 1 #define meshtastic_Routing_route_reply_tag 2 @@ -1122,6 +1163,8 @@ extern "C" { #define meshtastic_MeshPacket_delayed_tag 13 #define meshtastic_MeshPacket_via_mqtt_tag 14 #define meshtastic_MeshPacket_hop_start_tag 15 +#define meshtastic_MeshPacket_public_key_tag 16 +#define meshtastic_MeshPacket_pki_encrypted_tag 17 #define meshtastic_NodeInfo_num_tag 1 #define meshtastic_NodeInfo_user_tag 2 #define meshtastic_NodeInfo_position_tag 3 @@ -1143,6 +1186,10 @@ extern "C" { #define meshtastic_QueueStatus_free_tag 2 #define meshtastic_QueueStatus_maxlen_tag 3 #define meshtastic_QueueStatus_mesh_packet_id_tag 4 +#define meshtastic_ClientNotification_reply_id_tag 1 +#define meshtastic_ClientNotification_time_tag 2 +#define meshtastic_ClientNotification_level_tag 3 +#define meshtastic_ClientNotification_message_tag 4 #define meshtastic_FileInfo_file_name_tag 1 #define meshtastic_FileInfo_size_bytes_tag 2 #define meshtastic_Compressed_portnum_tag 1 @@ -1180,6 +1227,7 @@ extern "C" { #define meshtastic_FromRadio_metadata_tag 13 #define meshtastic_FromRadio_mqttClientProxyMessage_tag 14 #define meshtastic_FromRadio_fileInfo_tag 15 +#define meshtastic_FromRadio_clientNotification_tag 16 #define meshtastic_ToRadio_packet_tag 1 #define meshtastic_ToRadio_want_config_id_tag 3 #define meshtastic_ToRadio_disconnect_tag 4 @@ -1200,22 +1248,22 @@ extern "C" { /* Struct field encoding specification for nanopb */ #define meshtastic_Position_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \ -X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \ -X(a, STATIC, SINGULAR, INT32, altitude, 3) \ +X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 1) \ +X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 2) \ +X(a, STATIC, OPTIONAL, INT32, altitude, 3) \ X(a, STATIC, SINGULAR, FIXED32, time, 4) \ X(a, STATIC, SINGULAR, UENUM, location_source, 5) \ X(a, STATIC, SINGULAR, UENUM, altitude_source, 6) \ X(a, STATIC, SINGULAR, FIXED32, timestamp, 7) \ X(a, STATIC, SINGULAR, INT32, timestamp_millis_adjust, 8) \ -X(a, STATIC, SINGULAR, SINT32, altitude_hae, 9) \ -X(a, STATIC, SINGULAR, SINT32, altitude_geoidal_separation, 10) \ +X(a, STATIC, OPTIONAL, SINT32, altitude_hae, 9) \ +X(a, STATIC, OPTIONAL, SINT32, altitude_geoidal_separation, 10) \ X(a, STATIC, SINGULAR, UINT32, PDOP, 11) \ X(a, STATIC, SINGULAR, UINT32, HDOP, 12) \ X(a, STATIC, SINGULAR, UINT32, VDOP, 13) \ X(a, STATIC, SINGULAR, UINT32, gps_accuracy, 14) \ -X(a, STATIC, SINGULAR, UINT32, ground_speed, 15) \ -X(a, STATIC, SINGULAR, UINT32, ground_track, 16) \ +X(a, STATIC, OPTIONAL, UINT32, ground_speed, 15) \ +X(a, STATIC, OPTIONAL, UINT32, ground_track, 16) \ X(a, STATIC, SINGULAR, UINT32, fix_quality, 17) \ X(a, STATIC, SINGULAR, UINT32, fix_type, 18) \ X(a, STATIC, SINGULAR, UINT32, sats_in_view, 19) \ @@ -1233,7 +1281,8 @@ X(a, STATIC, SINGULAR, STRING, short_name, 3) \ X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \ X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \ X(a, STATIC, SINGULAR, BOOL, is_licensed, 6) \ -X(a, STATIC, SINGULAR, UENUM, role, 7) +X(a, STATIC, SINGULAR, UENUM, role, 7) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 8) #define meshtastic_User_CALLBACK NULL #define meshtastic_User_DEFAULT NULL @@ -1265,8 +1314,8 @@ X(a, STATIC, SINGULAR, FIXED32, emoji, 8) #define meshtastic_Waypoint_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, id, 1) \ -X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 2) \ -X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \ +X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \ +X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 3) \ X(a, STATIC, SINGULAR, UINT32, expire, 4) \ X(a, STATIC, SINGULAR, UINT32, locked_to, 5) \ X(a, STATIC, SINGULAR, STRING, name, 6) \ @@ -1298,7 +1347,9 @@ X(a, STATIC, SINGULAR, UENUM, priority, 11) \ X(a, STATIC, SINGULAR, INT32, rx_rssi, 12) \ X(a, STATIC, SINGULAR, UENUM, delayed, 13) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14) \ -X(a, STATIC, SINGULAR, UINT32, hop_start, 15) +X(a, STATIC, SINGULAR, UINT32, hop_start, 15) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 16) \ +X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) #define meshtastic_MeshPacket_CALLBACK NULL #define meshtastic_MeshPacket_DEFAULT NULL #define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data @@ -1358,7 +1409,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 1 X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,clientNotification,clientNotification), 16) #define meshtastic_FromRadio_CALLBACK NULL #define meshtastic_FromRadio_DEFAULT NULL #define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket @@ -1373,6 +1425,15 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) #define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata #define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage #define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo +#define meshtastic_FromRadio_payload_variant_clientNotification_MSGTYPE meshtastic_ClientNotification + +#define meshtastic_ClientNotification_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, UINT32, reply_id, 1) \ +X(a, STATIC, SINGULAR, FIXED32, time, 2) \ +X(a, STATIC, SINGULAR, UENUM, level, 3) \ +X(a, STATIC, SINGULAR, STRING, message, 4) +#define meshtastic_ClientNotification_CALLBACK NULL +#define meshtastic_ClientNotification_DEFAULT NULL #define meshtastic_FileInfo_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, STRING, file_name, 1) \ @@ -1478,6 +1539,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg; extern const pb_msgdesc_t meshtastic_LogRecord_msg; extern const pb_msgdesc_t meshtastic_QueueStatus_msg; extern const pb_msgdesc_t meshtastic_FromRadio_msg; +extern const pb_msgdesc_t meshtastic_ClientNotification_msg; extern const pb_msgdesc_t meshtastic_FileInfo_msg; extern const pb_msgdesc_t meshtastic_ToRadio_msg; extern const pb_msgdesc_t meshtastic_Compressed_msg; @@ -1504,6 +1566,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg #define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg #define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg +#define meshtastic_ClientNotification_fields &meshtastic_ClientNotification_msg #define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg #define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg #define meshtastic_Compressed_fields &meshtastic_Compressed_msg @@ -1521,6 +1584,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; /* meshtastic_ChunkedPayloadResponse_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size #define meshtastic_ChunkedPayload_size 245 +#define meshtastic_ClientNotification_size 415 #define meshtastic_Compressed_size 243 #define meshtastic_Data_size 270 #define meshtastic_DeviceMetadata_size 46 @@ -1528,19 +1592,19 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_FromRadio_size 510 #define meshtastic_Heartbeat_size 0 #define meshtastic_LogRecord_size 426 -#define meshtastic_MeshPacket_size 326 +#define meshtastic_MeshPacket_size 364 #define meshtastic_MqttClientProxyMessage_size 501 #define meshtastic_MyNodeInfo_size 18 #define meshtastic_NeighborInfo_size 258 #define meshtastic_Neighbor_size 22 -#define meshtastic_NodeInfo_size 283 +#define meshtastic_NodeInfo_size 317 #define meshtastic_NodeRemoteHardwarePin_size 29 #define meshtastic_Position_size 144 #define meshtastic_QueueStatus_size 23 #define meshtastic_RouteDiscovery_size 40 #define meshtastic_Routing_size 42 #define meshtastic_ToRadio_size 504 -#define meshtastic_User_size 79 +#define meshtastic_User_size 113 #define meshtastic_Waypoint_size 165 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 82cd0a55d..43899ac9c 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -70,98 +70,138 @@ typedef enum _meshtastic_TelemetrySensorType { /* Key native device metrics such as battery level */ typedef struct _meshtastic_DeviceMetrics { /* 0-100 (>100 means powered) */ + bool has_battery_level; uint32_t battery_level; /* Voltage measured */ + bool has_voltage; float voltage; /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */ + bool has_channel_utilization; float channel_utilization; /* Percent of airtime for transmission used within the last hour. */ + bool has_air_util_tx; float air_util_tx; /* How long the device has been running since the last reboot (in seconds) */ + bool has_uptime_seconds; uint32_t uptime_seconds; } meshtastic_DeviceMetrics; /* Weather station or other environmental metrics */ typedef struct _meshtastic_EnvironmentMetrics { /* Temperature measured */ + bool has_temperature; float temperature; /* Relative humidity percent measured */ + bool has_relative_humidity; float relative_humidity; /* Barometric pressure in hPA measured */ + bool has_barometric_pressure; float barometric_pressure; /* Gas resistance in MOhm measured */ + bool has_gas_resistance; float gas_resistance; /* Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */ + bool has_voltage; float voltage; /* Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */ + bool has_current; float current; /* relative scale IAQ value as measured by Bosch BME680 . value 0-500. Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */ + bool has_iaq; uint16_t iaq; /* RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. */ + bool has_distance; float distance; /* VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. */ + bool has_lux; float lux; /* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */ + bool has_white_lux; float white_lux; /* Infrared lux */ + bool has_ir_lux; float ir_lux; /* Ultraviolet lux */ + bool has_uv_lux; float uv_lux; /* Wind direction in degrees 0 degrees = North, 90 = East, etc... */ + bool has_wind_direction; uint16_t wind_direction; /* Wind speed in m/s */ + bool has_wind_speed; float wind_speed; /* Weight in KG */ + bool has_weight; float weight; /* Wind gust in m/s */ + bool has_wind_gust; float wind_gust; /* Wind lull in m/s */ + bool has_wind_lull; float wind_lull; } meshtastic_EnvironmentMetrics; /* Power Metrics (voltage / current / etc) */ typedef struct _meshtastic_PowerMetrics { /* Voltage (Ch1) */ + bool has_ch1_voltage; float ch1_voltage; /* Current (Ch1) */ + bool has_ch1_current; float ch1_current; /* Voltage (Ch2) */ + bool has_ch2_voltage; float ch2_voltage; /* Current (Ch2) */ + bool has_ch2_current; float ch2_current; /* Voltage (Ch3) */ + bool has_ch3_voltage; float ch3_voltage; /* Current (Ch3) */ + bool has_ch3_current; float ch3_current; } meshtastic_PowerMetrics; /* Air quality metrics */ typedef struct _meshtastic_AirQualityMetrics { /* Concentration Units Standard PM1.0 */ + bool has_pm10_standard; uint32_t pm10_standard; /* Concentration Units Standard PM2.5 */ + bool has_pm25_standard; uint32_t pm25_standard; /* Concentration Units Standard PM10.0 */ + bool has_pm100_standard; uint32_t pm100_standard; /* Concentration Units Environmental PM1.0 */ + bool has_pm10_environmental; uint32_t pm10_environmental; /* Concentration Units Environmental PM2.5 */ + bool has_pm25_environmental; uint32_t pm25_environmental; /* Concentration Units Environmental PM10.0 */ + bool has_pm100_environmental; uint32_t pm100_environmental; /* 0.3um Particle Count */ + bool has_particles_03um; uint32_t particles_03um; /* 0.5um Particle Count */ + bool has_particles_05um; uint32_t particles_05um; /* 1.0um Particle Count */ + bool has_particles_10um; uint32_t particles_10um; /* 2.5um Particle Count */ + bool has_particles_25um; uint32_t particles_25um; /* 5.0um Particle Count */ + bool has_particles_50um; uint32_t particles_50um; /* 10.0um Particle Count */ + bool has_particles_100um; uint32_t particles_100um; } meshtastic_AirQualityMetrics; @@ -208,16 +248,16 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0} -#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} -#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0} -#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Nau7802Config_init_zero {0, 0} @@ -272,58 +312,58 @@ extern "C" { /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, battery_level, 1) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 2) \ -X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 3) \ -X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 4) \ -X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 5) +X(a, STATIC, OPTIONAL, UINT32, battery_level, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, voltage, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, channel_utilization, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, air_util_tx, 4) \ +X(a, STATIC, OPTIONAL, UINT32, uptime_seconds, 5) #define meshtastic_DeviceMetrics_CALLBACK NULL #define meshtastic_DeviceMetrics_DEFAULT NULL #define meshtastic_EnvironmentMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \ -X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \ -X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \ -X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \ -X(a, STATIC, SINGULAR, FLOAT, current, 6) \ -X(a, STATIC, SINGULAR, UINT32, iaq, 7) \ -X(a, STATIC, SINGULAR, FLOAT, distance, 8) \ -X(a, STATIC, SINGULAR, FLOAT, lux, 9) \ -X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \ -X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \ -X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \ -X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \ -X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \ -X(a, STATIC, SINGULAR, FLOAT, weight, 15) \ -X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \ -X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17) +X(a, STATIC, OPTIONAL, FLOAT, temperature, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, relative_humidity, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, barometric_pressure, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, gas_resistance, 4) \ +X(a, STATIC, OPTIONAL, FLOAT, voltage, 5) \ +X(a, STATIC, OPTIONAL, FLOAT, current, 6) \ +X(a, STATIC, OPTIONAL, UINT32, iaq, 7) \ +X(a, STATIC, OPTIONAL, FLOAT, distance, 8) \ +X(a, STATIC, OPTIONAL, FLOAT, lux, 9) \ +X(a, STATIC, OPTIONAL, FLOAT, white_lux, 10) \ +X(a, STATIC, OPTIONAL, FLOAT, ir_lux, 11) \ +X(a, STATIC, OPTIONAL, FLOAT, uv_lux, 12) \ +X(a, STATIC, OPTIONAL, UINT32, wind_direction, 13) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \ +X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL #define meshtastic_PowerMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FLOAT, ch1_voltage, 1) \ -X(a, STATIC, SINGULAR, FLOAT, ch1_current, 2) \ -X(a, STATIC, SINGULAR, FLOAT, ch2_voltage, 3) \ -X(a, STATIC, SINGULAR, FLOAT, ch2_current, 4) \ -X(a, STATIC, SINGULAR, FLOAT, ch3_voltage, 5) \ -X(a, STATIC, SINGULAR, FLOAT, ch3_current, 6) +X(a, STATIC, OPTIONAL, FLOAT, ch1_voltage, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, ch1_current, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, ch2_voltage, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, ch2_current, 4) \ +X(a, STATIC, OPTIONAL, FLOAT, ch3_voltage, 5) \ +X(a, STATIC, OPTIONAL, FLOAT, ch3_current, 6) #define meshtastic_PowerMetrics_CALLBACK NULL #define meshtastic_PowerMetrics_DEFAULT NULL #define meshtastic_AirQualityMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, pm10_standard, 1) \ -X(a, STATIC, SINGULAR, UINT32, pm25_standard, 2) \ -X(a, STATIC, SINGULAR, UINT32, pm100_standard, 3) \ -X(a, STATIC, SINGULAR, UINT32, pm10_environmental, 4) \ -X(a, STATIC, SINGULAR, UINT32, pm25_environmental, 5) \ -X(a, STATIC, SINGULAR, UINT32, pm100_environmental, 6) \ -X(a, STATIC, SINGULAR, UINT32, particles_03um, 7) \ -X(a, STATIC, SINGULAR, UINT32, particles_05um, 8) \ -X(a, STATIC, SINGULAR, UINT32, particles_10um, 9) \ -X(a, STATIC, SINGULAR, UINT32, particles_25um, 10) \ -X(a, STATIC, SINGULAR, UINT32, particles_50um, 11) \ -X(a, STATIC, SINGULAR, UINT32, particles_100um, 12) +X(a, STATIC, OPTIONAL, UINT32, pm10_standard, 1) \ +X(a, STATIC, OPTIONAL, UINT32, pm25_standard, 2) \ +X(a, STATIC, OPTIONAL, UINT32, pm100_standard, 3) \ +X(a, STATIC, OPTIONAL, UINT32, pm10_environmental, 4) \ +X(a, STATIC, OPTIONAL, UINT32, pm25_environmental, 5) \ +X(a, STATIC, OPTIONAL, UINT32, pm100_environmental, 6) \ +X(a, STATIC, OPTIONAL, UINT32, particles_03um, 7) \ +X(a, STATIC, OPTIONAL, UINT32, particles_05um, 8) \ +X(a, STATIC, OPTIONAL, UINT32, particles_10um, 9) \ +X(a, STATIC, OPTIONAL, UINT32, particles_25um, 10) \ +X(a, STATIC, OPTIONAL, UINT32, particles_50um, 11) \ +X(a, STATIC, OPTIONAL, UINT32, particles_100um, 12) #define meshtastic_AirQualityMetrics_CALLBACK NULL #define meshtastic_AirQualityMetrics_DEFAULT NULL From 2012a0ae1c166dad9c9274252eff22f46895e8e7 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 07:51:59 -0500 Subject: [PATCH 082/305] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/protobufs b/protobufs index c112ce6e1..f5e84249f 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c112ce6e1392e4bc812655fae5e8461c932b5267 +Subproject commit f5e84249fe47fbddfb1d007c465f8f9623771290 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index d0e643dff..bef2abf9b 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,6 +168,8 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; + /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ + int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -178,8 +180,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ - int32_t factory_reset; + /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ + int32_t factory_reset_config; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -254,11 +256,12 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 +#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_tag 99 +#define meshtastic_AdminMessage_factory_reset_config_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -298,11 +301,12 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL From 861f0b6769cda66c624daddfe969b9e15700bfc1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 08:33:42 -0500 Subject: [PATCH 083/305] Add ClientNotification hello world --- src/mesh/MeshService.cpp | 20 +++++++++++++++++++- src/mesh/MeshService.h | 10 ++++++++++ src/mesh/PhoneAPI.h | 3 +++ src/mesh/Router.cpp | 8 ++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 697644a4f..d05f43b01 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -48,14 +48,18 @@ static MemoryDynamic staticMqttClientProxyMes static MemoryDynamic staticQueueStatusPool; +static MemoryDynamic staticClientNotificationPool; + Allocator &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool; +Allocator &clientNotificationPool = staticClientNotificationPool; + Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE) + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) { lastQueueStatus = {0, 0, 16, 0}; } @@ -324,6 +328,20 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage fromNum++; } +void MeshService::sendClientNotification(meshtastic_ClientNotification *n) +{ + LOG_DEBUG("Sending client notification to phone\n"); + if (toPhoneClientNotificationQueue.numFree() == 0) { + LOG_WARN("ClientNotification queue is full, discarding oldest\n"); + meshtastic_ClientNotification *d = toPhoneClientNotificationQueue.dequeuePtr(0); + if (d) + releaseClientNotificationToPool(d); + } + + assert(toPhoneClientNotificationQueue.enqueue(n, 0)); + fromNum++; +} + meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode() { meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 528adb137..ea1c4e345 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -21,6 +21,7 @@ extern Allocator &queueStatusPool; extern Allocator &mqttClientProxyMessagePool; +extern Allocator &clientNotificationPool; /** * Top level app for this service. keeps the mesh, the radio config and the queue of received packets. @@ -44,6 +45,9 @@ class MeshService // keep list of MqttClientProxyMessages to be send to the client for delivery PointerQueue toPhoneMqttProxyQueue; + // keep list of ClientNotifications to be send to the client (phone) + PointerQueue toPhoneClientNotificationQueue; + // This holds the last QueueStatus send meshtastic_QueueStatus lastQueueStatus; @@ -97,6 +101,9 @@ class MeshService // Release MqttClientProxyMessage packet to pool void releaseMqttClientProxyMessageToPool(meshtastic_MqttClientProxyMessage *p) { mqttClientProxyMessagePool.release(p); } + /// Release the next ClientNotification packet to pool. + void releaseClientNotificationToPool(meshtastic_ClientNotification *p) { clientNotificationPool.release(p); } + /** * Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep @@ -134,6 +141,9 @@ class MeshService /// Send an MQTT message to the phone for client proxying void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m); + /// Send a ClientNotification to the phone + void sendClientNotification(meshtastic_ClientNotification *cn); + bool isToPhoneQueueEmpty(); ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id); diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 3c3668300..5feb1c4bf 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -66,6 +66,9 @@ class PhoneAPI // Keep MqttClientProxyMessage packet just as packetForPhone meshtastic_MqttClientProxyMessage *mqttClientProxyMessageForPhone = NULL; + // Keep ClientNotification packet just as packetForPhone + meshtastic_ClientNotification *clientNotification = NULL; + /// We temporarily keep the nodeInfo here between the call to available and getFromRadio meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 79095805d..f59d61ea2 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -2,6 +2,7 @@ #include "Channels.h" #include "CryptoEngine.h" #include "MeshRadio.h" +#include "MeshService.h" #include "NodeDB.h" #include "RTC.h" #include "configuration.h" @@ -209,6 +210,13 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) #ifdef DEBUG_PORT uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle); LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes); + meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); + cn->has_reply_id = true; + cn->reply_id = p->id; + cn->level = meshtastic_LogRecord_Level_WARNING; + cn->time = getValidTime(RTCQualityFromNet); + sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d minutes.", silentMinutes); + service->sendClientNotification(cn); #endif meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT; if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh From a767997cea4d940d3f4e6418145bab66b3b551e0 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 08:57:37 -0500 Subject: [PATCH 084/305] Get in the trunk! --- src/mesh/MeshService.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index d05f43b01..ac97d51a7 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -59,7 +59,8 @@ Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), + toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) { lastQueueStatus = {0, 0, 16, 0}; } From 8daebf80dd958e55ea3dae22a32ceb13f8aa1463 Mon Sep 17 00:00:00 2001 From: Mictronics Date: Sat, 10 Aug 2024 19:32:52 +0200 Subject: [PATCH 085/305] Fix warning: extra tokens at end of #endif directive. (#4432) --- src/modules/AdminModule.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 778b7193d..25450992b 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -828,7 +828,8 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r conn.serial.is_connected = powerFSM.getState() == &stateSERIAL; #else conn.serial.is_connected = powerFSM.getState(); -#endif conn.serial.baud = SERIAL_BAUD; +#endif + conn.serial.baud = SERIAL_BAUD; r.get_device_connection_status_response = conn; r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag; From 74afd131712ac8ae2988b9a74f5a9e39867b3e86 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 13:45:41 -0500 Subject: [PATCH 086/305] Re-implement PKI from #1509 (#4379) * Re-implement PKI from #1509 co-authored-by: edinnen * Set the key lengnth to actually make PKI work. * Remove unused variable and initialize keys to null * move printBytes() to meshUtils * Don't reset PKI key son reboot unless needed. * Remove double encryption for PKI messages * Cleanup encrypt logic * Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now. * Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32 * Fix a crash when node is null * Don't send PKI encrypted packets while licensed * use chIndex 8 for PKI * Don't be so clever, that you corrupt incoming packets * Pass on channel 8 for now * Typo * Lock keys once non-zero * We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf. * Lighter approach to retaining known key * Attach the public key to PKI decrypted packets in device memory * Turn PKI back off for STM32 :( * Don't just memcp over a protobuf * Don't PKI encrypt nodeinfo packets * Add a bit more memory logging around nodeDB * Use the proper macro to refer to NODENUM_BROADCAST * Typo fix * Don't PKI encrypt ROUTING (naks and acks) * Adds SecurityConfig protobuf * Add admin messages over PKI * Disable PKI for the WIO-e5 * Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k * Add missed "has_security" * Add the admin_channel_enabled option * STM32 again * add missed configuration.h at the top of files * Add EXCLUDE_TZ and RTC * Enable PKI build on STM32 once again * Attempt 1 at moving PKI to aes-ccm * Fix buffers for encrypt/decrypt * Eliminate unused aes variable * Add debugging lines * Set hash to 0 for PKI * Fix debug lines so they don't print pointers. * logic fix and more debug * Rather important typo * Check for short packets before attempting decrypt * Don't forget to give cryptoEngine the keys! * Use the right scratch buffer * Cleanup * moar cleanups * Minor hardening * Remove some in-progress stuff * Turn PKI back off on STM32 * Return false * 2.5 protos * Sync up protos * Add initial cryptography test vector tests * re-add MINIMUM_SAFE_FREE_HEAP * Housekeeping and comment fixes * Add explanatory comment about weak dh25519 keys --------- Co-authored-by: Ben Meadors --- arch/esp32/esp32.ini | 1 + arch/nrf52/nrf52.ini | 1 + platformio.ini | 1 + src/RedirectablePrint.cpp | 4 +- src/SerialConsole.cpp | 4 +- src/configuration.h | 5 + src/main.cpp | 9 +- src/mesh/CryptoEngine.cpp | 170 ++++++++++++++++++++++++++++++++ src/mesh/CryptoEngine.h | 31 +++++- src/mesh/NodeDB.cpp | 50 +++++++++- src/mesh/NodeDB.h | 2 +- src/mesh/PhoneAPI.cpp | 4 + src/mesh/Router.cpp | 173 +++++++++++++++++++++------------ src/mesh/aes-ccm.cpp | 157 ++++++++++++++++++++++++++++++ src/mesh/aes-ccm.h | 10 ++ src/meshUtils.h | 4 +- src/modules/AdminModule.cpp | 41 +++++++- test/test_crypto/test_main.cpp | 50 ++++++++++ userPrefs.h | 6 ++ 19 files changed, 643 insertions(+), 80 deletions(-) create mode 100644 src/mesh/aes-ccm.cpp create mode 100644 src/mesh/aes-ccm.h create mode 100644 test/test_crypto/test_main.cpp diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 58c1302da..0dd6cbc1d 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -48,6 +48,7 @@ lib_deps = https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f + rweather/Crypto@^0.4.0 lib_ignore = segger_rtt diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 1a371e920..762c81d41 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -16,6 +16,7 @@ build_src_filter = lib_deps= ${arduino_base.lib_deps} + rweather/Crypto@^0.4.0 lib_ignore = BluetoothOTA \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e60f0d7b9..5ad7d60a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,6 +45,7 @@ extra_configs = variants/*/platformio.ini [env] +test_build_src = true extra_scripts = bin/platformio-custom.py ; note: we add src to our include search path so that lmic_project_config can override diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 05d349de9..02cd8b309 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -39,7 +39,7 @@ size_t RedirectablePrint::write(uint8_t c) SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); #endif - if (!config.has_lora || config.device.serial_enabled) + if (!config.has_lora || config.security.serial_enabled) dest->write(c); return 1; // We always claim one was written, rather than trusting what the @@ -180,7 +180,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) { #if !MESHTASTIC_EXCLUDE_BLUETOOTH - if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) { + if (config.security.bluetooth_logging_enabled && !pauseBluetoothLogging) { bool isBleConnected = false; #ifdef ARCH_ESP32 isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected(); diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index d25b81da7..b911e15da 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -83,7 +83,7 @@ bool SerialConsole::checkIsConnected() bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { // only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled. - if (config.has_lora && config.device.serial_enabled) { + if (config.has_lora && config.security.serial_enabled) { // Switch to protobufs for log messages usingProtobufs = true; canWrite = true; @@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) { - if (usingProtobufs && config.device.debug_log_enabled) { + if (usingProtobufs && config.security.debug_log_api_enabled) { meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset switch (logLevel[0]) { case 'D': diff --git a/src/configuration.h b/src/configuration.h index 9148f1d37..c9a5d7fb0 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -193,6 +193,10 @@ along with this program. If not, see . #define DEFAULT_SHUTDOWN_SECONDS 2 #endif +#ifndef MINIMUM_SAFE_FREE_HEAP +#define MINIMUM_SAFE_FREE_HEAP 1500 +#endif + /* Step #3: mop up with disabled values for HAS_ options not handled by the above two */ #ifndef HAS_WIFI @@ -256,6 +260,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_MQTT 1 #define MESHTASTIC_EXCLUDE_POWERMON 1 #define MESHTASTIC_EXCLUDE_I2C 1 +#define MESHTASTIC_EXCLUDE_PKI 1 #define MESHTASTIC_EXCLUDE_POWER_FSM 1 #define MESHTASTIC_EXCLUDE_TZ 1 #endif diff --git a/src/main.cpp b/src/main.cpp index 0a3c1ae0b..1c4a64843 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -227,7 +227,7 @@ void printInfo() { LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION)); } - +#ifndef PIO_UNIT_TESTING void setup() { concurrency::hasBeenSetup = true; @@ -1034,7 +1034,7 @@ void setup() powerFSMthread = new PowerFSMThread(); setCPUFast(false); // 80MHz is fine for our slow peripherals } - +#endif uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes) uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client) @@ -1057,7 +1057,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled; return deviceMetadata; } - +#ifndef PIO_UNIT_TESTING void loop() { runASAP = false; @@ -1102,4 +1102,5 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 1e44cb9b7..677667aef 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -1,6 +1,176 @@ #include "CryptoEngine.h" +#include "NodeDB.h" +#include "RadioInterface.h" #include "configuration.h" +#if !(MESHTASTIC_EXCLUDE_PKI) +#include "aes-ccm.h" +#include "meshUtils.h" +#include +#include +#include +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) +/** + * Create a public/private key pair with Curve25519. + * + * @param pubKey The destination for the public key. + * @param privKey The destination for the private key. + */ +void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) +{ + LOG_DEBUG("Generating Curve25519 key pair...\n"); + Curve25519::dh1(public_key, private_key); + memcpy(pubKey, public_key, sizeof(public_key)); + memcpy(privKey, private_key, sizeof(private_key)); +} +#endif +uint8_t shared_key[32]; +void CryptoEngine::clearKeys() +{ + memset(public_key, 0, sizeof(public_key)); + memset(private_key, 0, sizeof(private_key)); +} + +/** + * Encrypt a packet's payload using a key generated with Curve25519 and SHA256 + * for a specific node. + * + * @param bytes is updated in place + */ +bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, + uint8_t *bytesOut) +{ + uint8_t *auth; + auth = bytesOut + numBytes; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode); + if (node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node %d or their public_key not found\n", toNode); + return false; + } + if (!crypto->setDHKey(toNode)) { + return false; + } + initNonce(fromNode, packetNum); + + // Calculate the shared secret with the destination node and encrypt + printBytes("Attempting encrypt using nonce: ", nonce, 16); + printBytes("Attempting encrypt using shared_key: ", shared_key, 32); + aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); + return true; +} + +/** + * Decrypt a packet's payload using a key generated with Curve25519 and SHA256 + * for a specific node. + * + * @param bytes is updated in place + */ +bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) +{ + uint8_t *auth; // set to last 8 bytes of text? + auth = bytes + numBytes - 8; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode); + + if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node or its public key not found in database\n"); + return false; + } + + // Calculate the shared secret with the sending node and decrypt + if (!crypto->setDHKey(fromNode)) { + return false; + } + initNonce(fromNode, packetNum); + printBytes("Attempting decrypt using nonce: ", nonce, 16); + printBytes("Attempting decrypt using shared_key: ", shared_key, 32); + return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); +} + +void CryptoEngine::setPrivateKey(uint8_t *_private_key) +{ + memcpy(private_key, _private_key, 32); +} +/** + * Set the PKI key used for encrypt, decrypt. + * + * @param nodeNum the node number of the node who's public key we want to use + */ +bool CryptoEngine::setDHKey(uint32_t nodeNum) +{ + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum); + if (node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node %d or their public_key not found\n", nodeNum); + return false; + } + + uint8_t *pubKey = node->user.public_key.bytes; + uint8_t local_priv[32]; + memcpy(shared_key, pubKey, 32); + memcpy(local_priv, private_key, 32); + // Calculate the shared secret with the specified node's public key and our private key + // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. + if (!Curve25519::dh2(shared_key, local_priv)) { + LOG_WARN("Curve25519DH step 2 failed!\n"); + return false; + } + + printBytes("DH Output: ", shared_key, 32); + + /** + * D.J. Bernstein reccomends hashing the shared key. We want to do this because there are + * at least 128 bits of entropy in the 256-bit output of the DH key exchange, but we don't + * really know where. If you extract, for instance, the first 128 bits with basic truncation, + * then you don't know if you got all of your 128 entropy bits, or less, possibly much less. + * + * No exploitable bias is really known at that point, but we know enough to be wary. + * Hashing the DH output is a simple and safe way to gather all the entropy and spread + * it around as needed. + */ + crypto->hash(shared_key, 32); + return true; +} + +/** + * Hash arbitrary data using SHA256. + * + * @param bytes + * @param numBytes + */ +void CryptoEngine::hash(uint8_t *bytes, size_t numBytes) +{ + SHA256 hash; + size_t posn, len; + uint8_t size = numBytes; + uint8_t inc = 16; + hash.reset(); + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash.update(bytes + posn, len); + } + hash.finalize(bytes, 32); +} + +void CryptoEngine::aesSetKey(const uint8_t *key_bytes, size_t key_len) +{ + if (aes) { + delete aes; + aes = nullptr; + } + if (key_len != 0) { + aes = new AESSmall256(); + aes->setKey(key_bytes, key_len); + } +} + +void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) +{ + aes->encryptBlock(out, in); +} + +#endif + concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 2737dab2d..51080fd59 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -1,6 +1,8 @@ #pragma once - +#include "AES.h" #include "concurrency/LockGuard.h" +#include "configuration.h" +#include "mesh-pb-constants.h" #include extern concurrency::Lock *cryptLock; @@ -26,9 +28,34 @@ class CryptoEngine uint8_t nonce[16] = {0}; CryptoKey key = {}; +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t private_key[32] = {0}; +#endif public: +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t public_key[32] = {0}; +#endif + virtual ~CryptoEngine() {} +#if !(MESHTASTIC_EXCLUDE_PKI) +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); +#endif + void clearKeys(); + void setPrivateKey(uint8_t *_private_key); + virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, + uint8_t *bytesOut); + virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); + virtual bool setDHKey(uint32_t nodeNum); + virtual void hash(uint8_t *bytes, size_t numBytes); + + virtual void aesSetKey(const uint8_t *key, size_t key_len); + + virtual void aesEncrypt(uint8_t *in, uint8_t *out); + AESSmall256 *aes = NULL; + +#endif /** * Set the key used for encrypt, decrypt. @@ -61,4 +88,4 @@ class CryptoEngine void initNonce(uint32_t fromNode, uint64_t packetId); }; -extern CryptoEngine *crypto; +extern CryptoEngine *crypto; \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0f6c444ce..3400529ab 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -19,6 +19,7 @@ #include "error.h" #include "main.h" #include "mesh-pb-constants.h" +#include "meshUtils.h" #include "modules/NeighborInfoModule.h" #include #include @@ -144,6 +145,31 @@ NodeDB::NodeDB() // Include our owner in the node db under our nodenum meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); +#if !(MESHTASTIC_EXCLUDE_PKI) + // Calculate Curve25519 public and private keys + printBytes("Old Pubkey", config.security.public_key.bytes, 32); + if (config.security.private_key.size == 32 && config.security.public_key.size == 32) { + LOG_INFO("Using saved PKI keys\n"); + owner.public_key.size = config.security.public_key.size; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + crypto->setPrivateKey(config.security.private_key.bytes); + } else { +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + LOG_INFO("Generating new PKI keys\n"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + config.security.public_key.size = 32; + config.security.private_key.size = 32; + + printBytes("New Pubkey", config.security.public_key.bytes, 32); + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); +#else + LOG_INFO("No PKI keys set, and generation disabled!\n"); +#endif + } + +#endif + info->user = owner; info->has_user = true; @@ -258,6 +284,7 @@ void NodeDB::installDefaultConfig() config.has_power = true; config.has_network = true; config.has_bluetooth = (HAS_BLUETOOTH ? true : false); + config.has_security = true; config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL; config.lora.sx126x_rx_boosted_gain = true; @@ -280,6 +307,14 @@ void NodeDB::installDefaultConfig() #else config.lora.ignore_mqtt = false; #endif +#ifdef ADMIN_KEY_USERPREFS + memcpy(config.security.admin_key.bytes, admin_key_userprefs, 32); + config.security.admin_key.size = 32; +#else + config.security.admin_key.size = 0; +#endif + config.security.public_key.size = 0; + config.security.private_key.size = 0; #ifdef PIN_GPS_EN config.position.gps_en_gpio = PIN_GPS_EN; #endif @@ -303,7 +338,8 @@ void NodeDB::installDefaultConfig() config.position.broadcast_smart_minimum_interval_secs = 30; if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER) config.device.node_info_broadcast_secs = default_node_info_broadcast_secs; - config.device.serial_enabled = true; + config.security.serial_enabled = true; + config.security.admin_channel_enabled = false; resetRadioConfig(); strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32); // FIXME: Default to bluetooth capability of platform as default @@ -796,6 +832,7 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat) config.has_power = true; config.has_network = true; config.has_bluetooth = true; + config.has_security = true; success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config); } @@ -975,7 +1012,7 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS /** Update user info and channel for this node based on received user data */ -bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex) +bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex) { meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); if (!info) { @@ -983,6 +1020,12 @@ bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t chann } LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel); +#if !(MESHTASTIC_EXCLUDE_PKI) + if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one + printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); + memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + } +#endif // Both of info->user and p start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); @@ -1060,7 +1103,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) meshtastic_NodeInfoLite *lite = getMeshNode(n); if (!lite) { - if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) { + if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP)) { if (screen) screen->print("Warn: node database full!\nErasing oldest entry\n"); LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes, @@ -1086,6 +1129,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // everything is missing except the nodenum memset(lite, 0, sizeof(*lite)); lite->num = n; + LOG_INFO("Adding node to database with %i nodes and %i bytes free!\n", numMeshNodes, memGet.getFreeHeap()); } return lite; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 447ce10d4..a71f3e134 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -98,7 +98,7 @@ class NodeDB /** Update user info and channel for this node based on received user data */ - bool updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex = 0); + bool updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex = 0); /// @return our node number NodeNum getNodeNum() { return myNodeInfo.my_node_num; } diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index fc0099e87..0a9bb5b10 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -255,6 +255,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.config.which_payload_variant = meshtastic_Config_bluetooth_tag; fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; break; + case meshtastic_Config_security_tag: + fromRadioScratch.config.which_payload_variant = meshtastic_Config_security_tag; + fromRadioScratch.config.payload_variant.security = config.security; + break; default: LOG_ERROR("Unknown config type %d\n", config_state); } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index f59d61ea2..1fecef6d7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -37,6 +37,7 @@ static MemoryDynamic staticPool; Allocator &packetPool = staticPool; static uint8_t bytes[MAX_RHPACKETLEN]; +static uint8_t ScratchEncrypted[MAX_RHPACKETLEN]; /** * Constructor @@ -307,70 +308,105 @@ bool perhapsDecode(meshtastic_MeshPacket *p) if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) return true; // If packet was already decoded just return - // assert(p->which_payloadVariant == MeshPacket_encrypted_tag); + size_t rawSize = p->encrypted.size; + if (rawSize > sizeof(bytes)) { + LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize); + return false; + } + bool decrypted = false; + ChannelIndex chIndex = 0; + memcpy(bytes, p->encrypted.bytes, + rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf + memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); +#if !(MESHTASTIC_EXCLUDE_PKI) + // Attempt PKI decryption first + if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && nodeDB->getMeshNode(p->from) != nullptr && + nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && + rawSize > 8) { + LOG_DEBUG("Attempting PKI decryption\n"); - // Try to find a channel that works with this hash - for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { - // Try to use this hash/channel pair - if (channels.decryptForHash(chIndex, p->channel)) { - // Try to decrypt the packet if we can - size_t rawSize = p->encrypted.size; - if (rawSize > sizeof(bytes)) { - LOG_ERROR("Packet too large to attempt decription! (rawSize=%d > 256)\n", rawSize); - return false; - } - memcpy(bytes, p->encrypted.bytes, - rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf - crypto->decrypt(p->from, p->id, rawSize, bytes); - - // printBytes("plaintext", bytes, p->encrypted.size); - - // Take those raw bytes and convert them back into a well structured protobuf we can understand + if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) { + LOG_INFO("PKI Decryption worked!\n"); memset(&p->decoded, 0, sizeof(p->decoded)); - if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { - LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n"); - } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { - LOG_ERROR("Invalid portnum (bad psk?)!\n"); + rawSize -= 8; + if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) && + p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) { + decrypted = true; + LOG_INFO("Packet decrypted using PKI!\n"); + p->pki_encrypted = true; + memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); + p->public_key.size = 32; + // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers + // chIndex = 8; } else { - // parsing was successful - p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded - p->channel = chIndex; // change to store the index instead of the hash - - /* Not actually ever used. - // Decompress if needed. jm - if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) { - // Decompress the payload - char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; - char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; - int decompressed_len; - - memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); - - decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); - - // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); - - memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); - - // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP - p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; - } */ - - printPacket("decoded message", p); -#if ENABLE_JSON_LOGGING - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); -#elif ARCH_PORTDUINO - if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); - } -#endif - return true; + return false; } } } +#endif - LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); - return false; + // assert(p->which_payloadVariant == MeshPacket_encrypted_tag); + if (!decrypted) { + // Try to find a channel that works with this hash + for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { + // Try to use this hash/channel pair + if (channels.decryptForHash(chIndex, p->channel)) { + // Try to decrypt the packet if we can + crypto->decrypt(p->from, p->id, rawSize, bytes); + + // printBytes("plaintext", bytes, p->encrypted.size); + + // Take those raw bytes and convert them back into a well structured protobuf we can understand + memset(&p->decoded, 0, sizeof(p->decoded)); + if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { + LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id); + } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { + LOG_ERROR("Invalid portnum (bad psk?)!\n"); + } else { + decrypted = true; + break; + } + } + } + } + if (decrypted) { + // parsing was successful + p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded + p->channel = chIndex; // change to store the index instead of the hash + + /* Not actually ever used. + // Decompress if needed. jm + if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) { + // Decompress the payload + char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; + char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; + int decompressed_len; + + memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); + + decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); + + // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); + + memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); + + // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP + p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; + } */ + + printPacket("decoded message", p); +#if ENABLE_JSON_LOGGING + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); +#elif ARCH_PORTDUINO + if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); + } +#endif + return true; + } else { + LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); + return false; + } } /** Return 0 for success or a Routing_Errror code for failure @@ -384,7 +420,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); /* Not actually used, so save the cycles - // Only allow encryption on the text message app. // TODO: Allow modules to opt into compression. if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) { @@ -432,10 +467,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Now that we are encrypting the packet channel should be the hash (no longer the index) p->channel = hash; +#if !(MESHTASTIC_EXCLUDE_PKI) + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); + if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && + p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && + p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag + LOG_DEBUG("Using PKI!\n"); + if (numbytes + 8 > MAX_RHPACKETLEN) + return meshtastic_Routing_Error_TOO_LARGE; + crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); + numbytes += 8; + memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); + p->channel = 0; + } else { + crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + memcpy(p->encrypted.bytes, bytes, numbytes); + } +#else crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + memcpy(p->encrypted.bytes, bytes, numbytes); +#endif // Copy back into the packet and set the variant type - memcpy(p->encrypted.bytes, bytes, numbytes); p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } @@ -539,4 +592,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) handleReceived(p); packetPool.release(p); -} +} \ No newline at end of file diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp new file mode 100644 index 000000000..cd18ae6c5 --- /dev/null +++ b/src/mesh/aes-ccm.cpp @@ -0,0 +1,157 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#define AES_BLOCK_SIZE 16 +#include "aes-ccm.h" +#if !MESHTASTIC_EXCLUDE_PKI + +static inline void WPA_PUT_BE16(uint8_t *a, uint16_t val) +{ + a[0] = val >> 8; + a[1] = val & 0xff; +} + +static void xor_aes_block(uint8_t *dst, const uint8_t *src) +{ + uint32_t *d = (uint32_t *)dst; + uint32_t *s = (uint32_t *)src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} +static void aes_ccm_auth_start(size_t M, size_t L, const uint8_t *nonce, const uint8_t *aad, size_t aad_len, size_t plain_len, + uint8_t *x) +{ + uint8_t aad_buf[2 * AES_BLOCK_SIZE]; + uint8_t b[AES_BLOCK_SIZE]; + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + crypto->aesEncrypt(b, x); /* X_1 = E(K, B_0) */ + if (!aad_len) + return; + WPA_PUT_BE16(aad_buf, aad_len); + memcpy(aad_buf + 2, aad, aad_len); + memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + xor_aes_block(aad_buf, x); + crypto->aesEncrypt(aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + crypto->aesEncrypt(&aad_buf[AES_BLOCK_SIZE], x); + } +} +static void aes_ccm_auth(const uint8_t *data, size_t len, uint8_t *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + crypto->aesEncrypt(x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + crypto->aesEncrypt(x, x); + } +} +static void aes_ccm_encr_start(size_t L, const uint8_t *nonce, uint8_t *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + memcpy(&a[1], nonce, 15 - L); +} +static void aes_ccm_encr(size_t L, const uint8_t *in, size_t len, uint8_t *out, uint8_t *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + crypto->aesEncrypt(a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + crypto->aesEncrypt(a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} +static void aes_ccm_encr_auth(size_t M, uint8_t *x, uint8_t *a, uint8_t *auth) +{ + size_t i; + uint8_t tmp[AES_BLOCK_SIZE]; + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + crypto->aesEncrypt(a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; +} +static void aes_ccm_decr_auth(size_t M, uint8_t *a, const uint8_t *auth, uint8_t *t) +{ + size_t i; + uint8_t tmp[AES_BLOCK_SIZE]; + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + crypto->aesEncrypt(a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; +} +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth) +{ + const size_t L = 2; + uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + crypto->aesSetKey(key, key_len); + aes_ccm_auth_start(M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(plain, plain_len, x); + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(M, x, a, auth); + return 0; +} +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain) +{ + const size_t L = 2; + uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + uint8_t t[AES_BLOCK_SIZE]; + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return false; + crypto->aesSetKey(key, key_len); + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(M, a, auth, t); + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(L, crypt, crypt_len, plain, a); + aes_ccm_auth_start(M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(plain, crypt_len, x); + if (memcmp(x, t, M) != 0) { // FIXME make const comp + return false; + } + return true; +} +#endif \ No newline at end of file diff --git a/src/mesh/aes-ccm.h b/src/mesh/aes-ccm.h new file mode 100644 index 000000000..6b8edcde4 --- /dev/null +++ b/src/mesh/aes-ccm.h @@ -0,0 +1,10 @@ +#pragma once +#include "CryptoEngine.h" +#if !MESHTASTIC_EXCLUDE_PKI + +int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth); + +bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain); +#endif \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index 9dfe9b558..e2d4188d7 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -12,4 +12,6 @@ template constexpr const T &clamp(const T &v, const T &lo, const T &hi #define STRNSTR #include char *strnstr(const char *s, const char *find, size_t slen); -#endif \ No newline at end of file +#endif + +void printBytes(const char *label, const uint8_t *p, size_t numbytes); \ No newline at end of file diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 778b7193d..fe426f8f5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -65,7 +65,29 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta bool handled = false; assert(r); bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum(); - + if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { + return handled; + } + meshtastic_Channel *ch = &channels.getByIndex(mp.channel); + // Could tighten this up further by tracking the last poblic_key we went an AdminMessage request to + // and only allowing responses from that remote. + if (!((mp.from == 0 && !config.security.is_managed) || + r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || + r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag || + (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || + (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) { + LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); + return handled; + } + LOG_INFO("Handling admin payload %i\n", r->which_payload_variant); switch (r->which_payload_variant) { /** @@ -383,8 +405,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) #endif if (config.device.button_gpio == c.payload_variant.device.button_gpio && config.device.buzzer_gpio == c.payload_variant.device.buzzer_gpio && - config.device.debug_log_enabled == c.payload_variant.device.debug_log_enabled && - config.device.serial_enabled == c.payload_variant.device.serial_enabled && config.device.role == c.payload_variant.device.role && config.device.disable_triple_click == c.payload_variant.device.disable_triple_click && config.device.rebroadcast_mode == c.payload_variant.device.rebroadcast_mode) { @@ -501,6 +521,16 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.has_bluetooth = true; config.bluetooth = c.payload_variant.bluetooth; break; + case meshtastic_Config_security_tag: + LOG_INFO("Setting config: Security\n"); + config.security = c.payload_variant.security; + owner.public_key.size = config.security.public_key.size; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled && + config.security.serial_enabled == c.payload_variant.security.serial_enabled) + requiresReboot = false; + + break; } saveChanges(changes, requiresReboot); @@ -828,7 +858,8 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r conn.serial.is_connected = powerFSM.getState() == &stateSERIAL; #else conn.serial.is_connected = powerFSM.getState(); -#endif conn.serial.baud = SERIAL_BAUD; +#endif + conn.serial.baud = SERIAL_BAUD; r.get_device_connection_status_response = conn; r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag; @@ -895,5 +926,5 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg) { // restrict to the admin channel for rx - boundChannel = Channels::adminChannel; + // boundChannel = Channels::adminChannel; } \ No newline at end of file diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp new file mode 100644 index 000000000..e564d5d0e --- /dev/null +++ b/test/test_crypto/test_main.cpp @@ -0,0 +1,50 @@ +#include "CryptoEngine.h" + +#include + +void setUp(void) +{ + // set stuff up here +} + +void tearDown(void) +{ + // clean stuff up here +} + +void test_SHA256(void) +{ + uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, + 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + uint8_t hash[32] = {0}; + crypto->hash(hash, 0); + TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32); +} +void test_ECB_AES256(void) +{ + uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; + uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; + uint8_t scratch[16] = {0}; + + uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}; + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16); +} + +void setup() +{ + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + UNITY_BEGIN(); // IMPORTANT LINE! + RUN_TEST(test_SHA256); + RUN_TEST(test_ECB_AES256); +} + +void loop() +{ + UNITY_END(); // stop unit testing +} \ No newline at end of file diff --git a/userPrefs.h b/userPrefs.h index b365e8c6f..5812fa0c8 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -33,4 +33,10 @@ static unsigned char icon_bits[] = { 0x98, 0x3F, 0xF0, 0x23, 0x00, 0xFC, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0x80, 0xFF, 0x01, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; */ +/* +#define ADMIN_KEY_USERPREFS 1 +static unsigned char admin_key_userprefs[] = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, + 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, + 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c}; +*/ #endif \ No newline at end of file From 8ca884bafd12881e4a16b3b26aab6b671e017a7c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 15:45:29 -0500 Subject: [PATCH 087/305] Add DH25519 unit test --- src/DebugConfiguration.h | 2 +- src/mesh/CryptoEngine.cpp | 27 ++++++----- src/mesh/CryptoEngine.h | 25 +++++----- src/mesh/NodeDB.cpp | 2 +- test/test_crypto/test_main.cpp | 89 +++++++++++++++++++++++++++++----- 5 files changed, 109 insertions(+), 36 deletions(-) diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index f5b3fa25a..6f55dfa90 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -45,7 +45,7 @@ #define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__) #define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else -#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) +#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) && !defined(PIO_UNIT_TESTING) #define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__) #define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__) #define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 677667aef..d284f3410 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -24,7 +24,6 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) memcpy(privKey, private_key, sizeof(private_key)); } #endif -uint8_t shared_key[32]; void CryptoEngine::clearKeys() { memset(public_key, 0, sizeof(public_key)); @@ -86,7 +85,7 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); } -void CryptoEngine::setPrivateKey(uint8_t *_private_key) +void CryptoEngine::setDHPrivateKey(uint8_t *_private_key) { memcpy(private_key, _private_key, 32); } @@ -103,16 +102,8 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum) return false; } - uint8_t *pubKey = node->user.public_key.bytes; - uint8_t local_priv[32]; - memcpy(shared_key, pubKey, 32); - memcpy(local_priv, private_key, 32); - // Calculate the shared secret with the specified node's public key and our private key - // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. - if (!Curve25519::dh2(shared_key, local_priv)) { - LOG_WARN("Curve25519DH step 2 failed!\n"); + if (!setDHPublicKey(node->user.public_key.bytes)) return false; - } printBytes("DH Output: ", shared_key, 32); @@ -171,6 +162,20 @@ void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) #endif +bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) +{ + uint8_t local_priv[32]; + memcpy(shared_key, pubKey, 32); + memcpy(local_priv, private_key, 32); + // Calculate the shared secret with the specified node's public key and our private key + // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. + if (!Curve25519::dh2(shared_key, local_priv)) { + LOG_WARN("Curve25519DH step 2 failed!\n"); + return false; + } + return true; +} + concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 51080fd59..fd607c29e 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -23,15 +23,6 @@ struct CryptoKey { class CryptoEngine { - protected: - /** Our per packet nonce */ - uint8_t nonce[16] = {0}; - - CryptoKey key = {}; -#if !(MESHTASTIC_EXCLUDE_PKI) - uint8_t private_key[32] = {0}; -#endif - public: #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t public_key[32] = {0}; @@ -43,11 +34,12 @@ class CryptoEngine virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); #endif void clearKeys(); - void setPrivateKey(uint8_t *_private_key); + void setDHPrivateKey(uint8_t *_private_key); virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); - virtual bool setDHKey(uint32_t nodeNum); + bool setDHKey(uint32_t nodeNum); + virtual bool setDHPublicKey(uint8_t *publicKey); virtual void hash(uint8_t *bytes, size_t numBytes); virtual void aesSetKey(const uint8_t *key, size_t key_len); @@ -75,8 +67,17 @@ class CryptoEngine */ virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); - +#ifndef PIO_UNIT_TESTING protected: +#endif + /** Our per packet nonce */ + uint8_t nonce[16] = {0}; + + CryptoKey key = {}; +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t shared_key[32] = {0}; + uint8_t private_key[32] = {0}; +#endif /** * Init our 128 bit nonce for a new packet * diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 3400529ab..ac77e7830 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -152,7 +152,7 @@ NodeDB::NodeDB() LOG_INFO("Using saved PKI keys\n"); owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); - crypto->setPrivateKey(config.security.private_key.bytes); + crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) LOG_INFO("Generating new PKI keys\n"); diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index e564d5d0e..e9aee928e 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -2,6 +2,18 @@ #include +void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0) +{ + if (len) { + memset(result, 0, len); + } + for (unsigned int i = 0; i < hex.length(); i += 2) { + std::string byteString = hex.substr(i, 2); + result[i / 2] = (uint8_t)strtol(byteString.c_str(), NULL, 16); + } + return; +} + void setUp(void) { // set stuff up here @@ -14,25 +26,79 @@ void tearDown(void) void test_SHA256(void) { - uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, - 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + uint8_t expected[32]; uint8_t hash[32] = {0}; + + HexToBytes(expected, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); crypto->hash(hash, 0); - TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); + + HexToBytes(hash, "d3", 32); + HexToBytes(expected, "28969cdfa74a12c82f3bad960b0b000aca2ac329deea5c2328ebc6f2ba9802c1"); + crypto->hash(hash, 1); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); + + HexToBytes(hash, "11af", 32); + HexToBytes(expected, "5ca7133fa735326081558ac312c620eeca9970d1e70a4b95533d956f072d1f98"); + crypto->hash(hash, 2); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); } void test_ECB_AES256(void) { - uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; - uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; - uint8_t scratch[16] = {0}; + // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_ECB.pdf - uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}; + uint8_t key[32] = {0}; + uint8_t plain[16] = {0}; + uint8_t result[16] = {0}; + uint8_t expected[16] = {0}; + + HexToBytes(key, "603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4"); + + HexToBytes(plain, "6BC1BEE22E409F96E93D7E117393172A"); + HexToBytes(expected, "F3EED1BDB5D2A03C064B5A7E3DB181F8"); crypto->aesSetKey(key, 32); - crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time - TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16); -} + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); + HexToBytes(plain, "AE2D8A571E03AC9C9EB76FAC45AF8E51"); + HexToBytes(expected, "591CCB10D410ED26DC5BA74A31362870"); + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); + + HexToBytes(plain, "30C81C46A35CE411E5FBC1191A0A52EF"); + HexToBytes(expected, "B6ED21B99CA6F4F9F153E7B1BEAFED1D"); + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); +} +void test_DH25519(void) +{ + // test vectors from wycheproof x25519 + // https://github.com/C2SP/wycheproof/blob/master/testvectors/x25519_test.json + uint8_t private_key[32]; + uint8_t public_key[32]; + uint8_t expected_shared[32]; + + HexToBytes(public_key, "504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"); + HexToBytes(private_key, "c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475"); + HexToBytes(expected_shared, "436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); + + HexToBytes(public_key, "63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733"); + HexToBytes(private_key, "d85d8c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211958"); + HexToBytes(expected_shared, "279df67a7c4611db4708a0e8282b195e5ac0ed6f4b2f292c6fbd0acac30d1332"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); + + HexToBytes(public_key, "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"); + HexToBytes(private_key, "18630f93598637c35da623a74559cf944374a559114c7937811041fc8605564a"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key +} void setup() { // NOTE!!! Wait for >2 secs @@ -42,6 +108,7 @@ void setup() UNITY_BEGIN(); // IMPORTANT LINE! RUN_TEST(test_SHA256); RUN_TEST(test_ECB_AES256); + RUN_TEST(test_DH25519); } void loop() From b573e0eacc1683a5ef810ad4601ec106ce7ddeb1 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 20:04:38 -0500 Subject: [PATCH 088/305] Fix compile on STM32 --- src/mesh/CryptoEngine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index d284f3410..fd7246fa9 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -160,8 +160,6 @@ void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) aes->encryptBlock(out, in); } -#endif - bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) { uint8_t local_priv[32]; @@ -176,6 +174,7 @@ bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) return true; } +#endif concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) From 1cfd5d12d2daa1237835aa67a0520598c8b7b83e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 22:38:05 -0500 Subject: [PATCH 089/305] Refactor platform cryptography, add tests --- src/mesh/CryptoEngine.cpp | 42 +++++++++- src/mesh/CryptoEngine.h | 6 +- src/mesh/Router.cpp | 2 +- src/platform/esp32/ESP32CryptoEngine.cpp | 41 ++-------- src/platform/esp32/architecture.h | 3 + src/platform/nrf52/NRF52CryptoEngine.cpp | 29 ++----- src/platform/nrf52/architecture.h | 3 + .../portduino/CrossPlatformCryptoEngine.cpp | 78 ------------------- src/platform/rp2040/rp2040CryptoEngine.cpp | 66 ---------------- src/platform/stm32wl/STM32WLCryptoEngine.cpp | 67 ---------------- test/test_crypto/test_main.cpp | 27 +++++++ 11 files changed, 88 insertions(+), 276 deletions(-) delete mode 100644 src/platform/portduino/CrossPlatformCryptoEngine.cpp delete mode 100644 src/platform/rp2040/rp2040CryptoEngine.cpp delete mode 100644 src/platform/stm32wl/STM32WLCryptoEngine.cpp diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index fd7246fa9..e83236eab 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -1,6 +1,7 @@ #include "CryptoEngine.h" #include "NodeDB.h" #include "RadioInterface.h" +#include "architecture.h" #include "configuration.h" #if !(MESHTASTIC_EXCLUDE_PKI) @@ -188,14 +189,44 @@ void CryptoEngine::setKey(const CryptoKey &k) * * @param bytes is updated in place */ -void CryptoEngine::encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) +void CryptoEngine::encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) { - LOG_WARN("noop encryption!\n"); + if (key.length > 0) { + initNonce(fromNode, packetId); + if (numBytes <= MAX_BLOCKSIZE) { + encryptAESCtr(key, nonce, numBytes, bytes); + } else { + LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); + } + } } void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) { - LOG_WARN("noop decryption!\n"); + // For CTR, the implementation is the same + encryptPacket(fromNode, packetId, numBytes, bytes); +} + +// Generic implementation of AES-CTR encryption. +void CryptoEngine::encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) +{ + if (ctr) { + delete ctr; + ctr = nullptr; + } + if (_key.length == 16) + ctr = new CTR(); + else + ctr = new CTR(); + ctr->setKey(_key.bytes, _key.length); + static uint8_t scratch[MAX_BLOCKSIZE]; + memcpy(scratch, bytes, numBytes); + memset(scratch + numBytes, 0, + sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) + + ctr->setIV(_nonce, 16); + ctr->setCounterSize(4); + ctr->encrypt(bytes, scratch, numBytes); } /** @@ -208,4 +239,7 @@ void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId) // use memcpy to avoid breaking strict-aliasing memcpy(nonce, &packetId, sizeof(uint64_t)); memcpy(nonce + sizeof(uint64_t), &fromNode, sizeof(uint32_t)); -} \ No newline at end of file +} +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +CryptoEngine *crypto = new CryptoEngine; +#endif \ No newline at end of file diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index fd607c29e..5ca9db7c1 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -1,5 +1,6 @@ #pragma once #include "AES.h" +#include "CTR.h" #include "concurrency/LockGuard.h" #include "configuration.h" #include "mesh-pb-constants.h" @@ -65,15 +66,16 @@ class CryptoEngine * * @param bytes is updated in place */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); + virtual void encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); + virtual void encryptAESCtr(CryptoKey key, uint8_t *nonce, size_t numBytes, uint8_t *bytes); #ifndef PIO_UNIT_TESTING protected: #endif /** Our per packet nonce */ uint8_t nonce[16] = {0}; - CryptoKey key = {}; + CTRCommon *ctr = NULL; #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t shared_key[32] = {0}; uint8_t private_key[32] = {0}; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1fecef6d7..b00b66a47 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -480,7 +480,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; } else { - crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); } #else diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp index 998419df8..230139036 100644 --- a/src/platform/esp32/ESP32CryptoEngine.cpp +++ b/src/platform/esp32/ESP32CryptoEngine.cpp @@ -13,58 +13,29 @@ class ESP32CryptoEngine : public CryptoEngine ~ESP32CryptoEngine() { mbedtls_aes_free(&aes); } - /** - * Set the key used for encrypt, decrypt. - * - * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. - * - * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) - * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the - * provided pointer) - */ - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - - if (key.length != 0) { - auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8); - assert(!res); - } - } - /** * Encrypt a packet * * @param bytes is updated in place + * TODO: return bool, and handle graciously when something fails */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override + virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override { - if (key.length > 0) { - LOG_DEBUG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); - initNonce(fromNode, packetId); + if (_key.length > 0) { if (numBytes <= MAX_BLOCKSIZE) { + mbedtls_aes_setkey_enc(&aes, _key.bytes, _key.length * 8); static uint8_t scratch[MAX_BLOCKSIZE]; uint8_t stream_block[16]; size_t nc_off = 0; memcpy(scratch, bytes, numBytes); memset(scratch + numBytes, 0, sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes); - assert(!res); + mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes); } else { LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); } } } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: }; -CryptoEngine *crypto = new ESP32CryptoEngine(); +CryptoEngine *crypto = new ESP32CryptoEngine(); \ No newline at end of file diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index fd3f92a9c..b6def5b01 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -42,6 +42,9 @@ #ifndef DEFAULT_VREF #define DEFAULT_VREF 1100 #endif +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +#define HAS_CUSTOM_CRYPTO_ENGINE 1 +#endif #if defined(HAS_AXP192) || defined(HAS_AXP2101) #define HAS_PMU diff --git a/src/platform/nrf52/NRF52CryptoEngine.cpp b/src/platform/nrf52/NRF52CryptoEngine.cpp index a7cf3d5bf..5de13c58b 100644 --- a/src/platform/nrf52/NRF52CryptoEngine.cpp +++ b/src/platform/nrf52/NRF52CryptoEngine.cpp @@ -9,41 +9,24 @@ class NRF52CryptoEngine : public CryptoEngine ~NRF52CryptoEngine() {} - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override + virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override { - if (key.length > 16) { - LOG_DEBUG("Software encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); + if (_key.length > 16) { AES_ctx ctx; - initNonce(fromNode, packetId); - AES_init_ctx_iv(&ctx, key.bytes, nonce); + AES_init_ctx_iv(&ctx, _key.bytes, _nonce); AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes); - } else if (key.length > 0) { - LOG_DEBUG("nRF52 encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); + } else if (_key.length > 0) { nRFCrypto.begin(); nRFCrypto_AES ctx; uint8_t myLen = ctx.blockLen(numBytes); char encBuf[myLen] = {0}; - initNonce(fromNode, packetId); ctx.begin(); - ctx.Process((char *)bytes, numBytes, nonce, key.bytes, key.length, encBuf, ctx.encryptFlag, ctx.ctrMode); + ctx.Process((char *)bytes, numBytes, _nonce, _key.bytes, _key.length, encBuf, ctx.encryptFlag, ctx.ctrMode); ctx.end(); nRFCrypto.end(); memcpy(bytes, encBuf, numBytes); } } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: }; -CryptoEngine *crypto = new NRF52CryptoEngine(); +CryptoEngine *crypto = new NRF52CryptoEngine(); \ No newline at end of file diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index d5685d611..8781347c3 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -32,6 +32,9 @@ #ifndef HAS_CPU_SHUTDOWN #define HAS_CPU_SHUTDOWN 1 #endif +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +#define HAS_CUSTOM_CRYPTO_ENGINE 1 +#endif // // set HW_VENDOR diff --git a/src/platform/portduino/CrossPlatformCryptoEngine.cpp b/src/platform/portduino/CrossPlatformCryptoEngine.cpp deleted file mode 100644 index 46ef942f0..000000000 --- a/src/platform/portduino/CrossPlatformCryptoEngine.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -/** A platform independent AES engine implemented using Tiny-AES - */ -class CrossPlatformCryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - CrossPlatformCryptoEngine() {} - - ~CrossPlatformCryptoEngine() {} - - /** - * Set the key used for encrypt, decrypt. - * - * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. - * - * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) - * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the - * provided pointer) - */ - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new CrossPlatformCryptoEngine(); diff --git a/src/platform/rp2040/rp2040CryptoEngine.cpp b/src/platform/rp2040/rp2040CryptoEngine.cpp deleted file mode 100644 index 5486e51e5..000000000 --- a/src/platform/rp2040/rp2040CryptoEngine.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -class RP2040CryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - RP2040CryptoEngine() {} - - ~RP2040CryptoEngine() {} - - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new RP2040CryptoEngine(); diff --git a/src/platform/stm32wl/STM32WLCryptoEngine.cpp b/src/platform/stm32wl/STM32WLCryptoEngine.cpp deleted file mode 100644 index 4debdf78e..000000000 --- a/src/platform/stm32wl/STM32WLCryptoEngine.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#undef RNG -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -class STM32WLCryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - STM32WLCryptoEngine() {} - - ~STM32WLCryptoEngine() {} - - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new STM32WLCryptoEngine(); diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index e9aee928e..129c88283 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -99,6 +99,32 @@ void test_DH25519(void) crypto->setDHPrivateKey(private_key); TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key } +void test_AES_CTR(void) +{ + uint8_t expected[32]; + uint8_t plain[32]; + uint8_t nonce[32]; + CryptoKey k; + + // vectors from https://www.rfc-editor.org/rfc/rfc3686#section-6 + k.length = 32; + HexToBytes(k.bytes, "776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104"); + HexToBytes(nonce, "00000060DB5672C97AA8F0B200000001"); + HexToBytes(expected, "145AD01DBF824EC7560863DC71E3E0C0"); + memcpy(plain, "Single block msg", 16); + + crypto->encryptAESCtr(k, nonce, 16, plain); + TEST_ASSERT_EQUAL_MEMORY(expected, plain, 16); + + k.length = 16; + memcpy(plain, "Single block msg", 16); + HexToBytes(k.bytes, "AE6852F8121067CC4BF7A5765577F39E"); + HexToBytes(nonce, "00000030000000000000000000000001"); + HexToBytes(expected, "E4095D4FB7A7B3792D6175A3261311B8"); + crypto->encryptAESCtr(k, nonce, 16, plain); + TEST_ASSERT_EQUAL_MEMORY(expected, plain, 16); +} + void setup() { // NOTE!!! Wait for >2 secs @@ -109,6 +135,7 @@ void setup() RUN_TEST(test_SHA256); RUN_TEST(test_ECB_AES256); RUN_TEST(test_DH25519); + RUN_TEST(test_AES_CTR); } void loop() From 54a2e14e356b6c0a08b70ace9caaa8458ea3e606 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 23:11:04 -0500 Subject: [PATCH 090/305] Add missed function rename. (Thanks VSCode) --- src/mesh/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index b00b66a47..2a6fb31fc 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -484,7 +484,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) memcpy(p->encrypted.bytes, bytes, numbytes); } #else - crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); #endif From cf392a4c201f03361563fbcf54caf303d0617fd6 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sun, 11 Aug 2024 20:06:38 +0800 Subject: [PATCH 091/305] Address some FIXME comments (#4435) * Address some FIXME comments These comments have since been addressed by more modern code. Remove them to reduce the clutter in the codebase. * Remove 'dumb idea' from SimpleAllocator 4 year old code that was set never to run can probably be safely deleted. --- src/gps/GPS.h | 2 +- src/main.h | 10 ++----- src/platform/esp32/SimpleAllocator.cpp | 38 -------------------------- 3 files changed, 3 insertions(+), 47 deletions(-) diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 1c0977bdd..96171cba5 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -51,7 +51,7 @@ enum GPSPowerState : uint8_t { const char *getDOPString(uint32_t dop); /** - * A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading) + * A gps class that only reads from the GPS periodically and keeps the gps powered down except when reading * * When new data is available it will notify observers. */ diff --git a/src/main.h b/src/main.h index ea2d80f94..52a3fff1f 100644 --- a/src/main.h +++ b/src/main.h @@ -65,12 +65,6 @@ extern bool isVibrating; extern int TCPPort; // set by Portduino -// extern Observable newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class - -// extern meshtastic::PowerStatus *powerStatus; -// extern meshtastic::GPSStatus *gpsStatus; -// extern meshtastic::NodeStatusHandler *nodeStatusHandler; - // Return a human readable string of the form "Meshtastic_ab13" const char *getDeviceName(); @@ -91,5 +85,5 @@ void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearB meshtastic_DeviceMetadata getDeviceMetadata(); -// FIXME, we default to 4MHz SPI, SPI mode 0, check if the datasheet says it can really do that -extern SPISettings spiSettings; \ No newline at end of file +// We default to 4MHz SPI, SPI mode 0 +extern SPISettings spiSettings; diff --git a/src/platform/esp32/SimpleAllocator.cpp b/src/platform/esp32/SimpleAllocator.cpp index 63f3b02de..04ce35eb3 100644 --- a/src/platform/esp32/SimpleAllocator.cpp +++ b/src/platform/esp32/SimpleAllocator.cpp @@ -26,41 +26,3 @@ void *operator new(size_t size, SimpleAllocator &p) { return p.alloc(size); } - -#if 0 -// This was a dumb idea, turn off for now - -SimpleAllocator *activeAllocator; - -AllocatorScope::AllocatorScope(SimpleAllocator &a) -{ - assert(!activeAllocator); - activeAllocator = &a; -} - -AllocatorScope::~AllocatorScope() -{ - assert(activeAllocator); - activeAllocator = NULL; -} - -/// Global new/delete, uses a simple allocator if it is in scope - -void *operator new(size_t sz) throw(std::bad_alloc) -{ - void *mem = activeAllocator ? activeAllocator->alloc(sz) : malloc(sz); - if (mem) - return mem; - else - throw std::bad_alloc(); -} - -void operator delete(void *ptr) throw() -{ - if (activeAllocator) - LOG_WARN("Leaking an active allocator object\n"); // We don't properly handle this yet - else - free(ptr); -} - -#endif \ No newline at end of file From e1b4b226c9d9aae9c163510f3bf1c9f74db0d493 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 11 Aug 2024 14:12:20 -0500 Subject: [PATCH 092/305] Manual protobuf update --- src/mesh/generated/meshtastic/admin.pb.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bef2abf9b..13093839d 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -30,7 +30,9 @@ typedef enum _meshtastic_AdminMessage_ConfigType { /* TODO: REPLACE */ meshtastic_AdminMessage_ConfigType_LORA_CONFIG = 5, /* TODO: REPLACE */ - meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6 + meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6, + /* TODO: REPLACE */ + meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG = 7 } meshtastic_AdminMessage_ConfigType; /* TODO: REPLACE */ @@ -194,8 +196,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG -#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG -#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG+1)) +#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG +#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG+1)) #define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG #define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG From 9bc222416414a2efec8b36602c2b0e0871f9a31f Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 11 Aug 2024 14:18:33 -0500 Subject: [PATCH 093/305] Exclude position packets from PKI (at least for now) --- src/mesh/Router.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 2a6fb31fc..945f92bb7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -470,8 +470,9 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) #if !(MESHTASTIC_EXCLUDE_PKI) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && - p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && - p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag + numbytes <= MAX_RHPACKETLEN - 8 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && + p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && + p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; From 6cd1882aaa861ef44882d5e55a6c769d42edaccd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 11 Aug 2024 17:22:01 -0500 Subject: [PATCH 094/305] [create-pull-request] automated change (#4439) Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 4 ++++ src/mesh/generated/meshtastic/telemetry.pb.h | 12 +++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/protobufs b/protobufs index 2fa7d6a4b..071fd931e 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 2fa7d6a4b702fcd58b54b0d1d6e4b3b85164f649 +Subproject commit 071fd931ec6679bb21427c872f9839edea63e351 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 59664b792..1bea952ce 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -180,6 +180,10 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_SENSECAP_INDICATOR = 70, /* Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. */ meshtastic_HardwareModel_TRACKER_T1000_E = 71, + /* RAK3172 STM32WLE5 Module (https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172) */ + meshtastic_HardwareModel_RAK3172 = 72, + /* Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. */ + meshtastic_HardwareModel_WIO_E5 = 73, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 82cd0a55d..a4acd3f4a 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -63,7 +63,13 @@ typedef enum _meshtastic_TelemetrySensorType { /* DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) */ meshtastic_TelemetrySensorType_DFROBOT_LARK = 24, /* NAU7802 Scale Chip or compatible */ - meshtastic_TelemetrySensorType_NAU7802 = 25 + meshtastic_TelemetrySensorType_NAU7802 = 25, + /* BMP3XX High accuracy temperature and pressure */ + meshtastic_TelemetrySensorType_BMP3XX = 26, + /* ICM-20948 9-Axis digital motion processor */ + meshtastic_TelemetrySensorType_ICM20948 = 27, + /* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */ + meshtastic_TelemetrySensorType_MAX17048 = 28 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -197,8 +203,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_NAU7802 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_NAU7802+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_MAX17048 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_MAX17048+1)) From a28f10e0c2525b15c6d415c86026d7bba255a7a3 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 11 Aug 2024 17:22:11 -0500 Subject: [PATCH 095/305] User to UserLite in NodeDB (#4438) * User to UserLite in the nodedb * Tronkdor the burninator --- protobufs | 2 +- src/RedirectablePrint.cpp | 5 +- src/mesh/NodeDB.cpp | 14 +++-- src/mesh/TypeConversions.cpp | 32 +++++++++- src/mesh/TypeConversions.h | 2 + .../generated/meshtastic/deviceonly.pb.cpp | 3 + src/mesh/generated/meshtastic/deviceonly.pb.h | 62 +++++++++++++++++-- 7 files changed, 106 insertions(+), 14 deletions(-) diff --git a/protobufs b/protobufs index f5e84249f..ee41c42e4 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit f5e84249fe47fbddfb1d007c465f8f9623771290 +Subproject commit ee41c42e4f89d4153415b941305d23dec3647210 diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 02cd8b309..0eab0de0a 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -38,8 +38,9 @@ size_t RedirectablePrint::write(uint8_t c) #ifdef USE_SEGGER SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); #endif - - if (!config.has_lora || config.security.serial_enabled) + // Account for legacy config transition + bool serialEnabled = config.has_security ? config.security.serial_enabled : config.device.serial_enabled; + if (!config.has_lora || serialEnabled) dest->write(c); return 1; // We always claim one was written, rather than trusting what the diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ac77e7830..e3030fb02 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -155,6 +155,10 @@ NodeDB::NodeDB() crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + config.has_security = true; + config.security.serial_enabled = config.device.serial_enabled; + config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; + config.security.is_managed = config.device.is_managed; LOG_INFO("Generating new PKI keys\n"); crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); config.security.public_key.size = 32; @@ -170,7 +174,7 @@ NodeDB::NodeDB() #endif - info->user = owner; + info->user = TypeConversions::ConvertToUserLite(owner); info->has_user = true; #ifdef ARCH_ESP32 @@ -1019,7 +1023,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde return false; } - LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel); + LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); @@ -1030,11 +1034,11 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde // Both of info->user and p start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); - info->user = p; + info->user = TypeConversions::ConvertToUserLite(p); if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) - LOG_DEBUG("updating changed=%d user %s/%s/%s, channel=%d\n", changed, info->user.id, info->user.long_name, - info->user.short_name, info->channel); + LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, + info->channel); info->has_user = true; if (changed) { diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index bcd600f24..30b06d0ad 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -24,7 +24,7 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo } if (lite->has_user) { info.has_user = true; - info.user = lite->user; + info.user = ConvertToUser(lite->num, lite->user); } if (lite->has_device_metrics) { info.has_device_metrics = true; @@ -55,4 +55,34 @@ meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite l position.time = lite.time; return position; +} + +meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) +{ + meshtastic_UserLite lite = meshtastic_UserLite_init_default; + + strncpy(lite.long_name, user.long_name, sizeof(lite.long_name)); + strncpy(lite.short_name, user.short_name, sizeof(lite.short_name)); + lite.hw_model = user.hw_model; + lite.role = user.role; + lite.is_licensed = user.is_licensed; + memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); + memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); + return lite; +} + +meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite) +{ + meshtastic_User user = meshtastic_User_init_default; + + snprintf(user.id, sizeof(user.id), "!%08x", nodeNum); + strncpy(user.long_name, lite.long_name, sizeof(user.long_name)); + strncpy(user.short_name, lite.short_name, sizeof(user.short_name)); + user.hw_model = lite.hw_model; + user.role = lite.role; + user.is_licensed = lite.is_licensed; + memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); + memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); + + return user; } \ No newline at end of file diff --git a/src/mesh/TypeConversions.h b/src/mesh/TypeConversions.h index ffc3c12a7..19e471f98 100644 --- a/src/mesh/TypeConversions.h +++ b/src/mesh/TypeConversions.h @@ -10,4 +10,6 @@ class TypeConversions static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite); static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position); static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite); + static meshtastic_UserLite ConvertToUserLite(meshtastic_User user); + static meshtastic_User ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite); }; diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 672192f67..2747ac9d9 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -9,6 +9,9 @@ PB_BIND(meshtastic_PositionLite, meshtastic_PositionLite, AUTO) +PB_BIND(meshtastic_UserLite, meshtastic_UserLite, AUTO) + + PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO) diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 2c91fe30e..343e5f48a 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -9,6 +9,7 @@ #include "meshtastic/localonly.pb.h" #include "meshtastic/mesh.pb.h" #include "meshtastic/telemetry.pb.h" +#include "meshtastic/config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -45,12 +46,37 @@ typedef struct _meshtastic_PositionLite { meshtastic_Position_LocSource location_source; } meshtastic_PositionLite; +typedef PB_BYTES_ARRAY_T(32) meshtastic_UserLite_public_key_t; +typedef struct _meshtastic_UserLite { + /* This is the addr of the radio. */ + pb_byte_t macaddr[6]; + /* A full name for this user, i.e. "Kevin Hester" */ + char long_name[40]; + /* A VERY short name, ideally two characters. + Suitable for a tiny OLED screen */ + char short_name[5]; + /* TBEAM, HELTEC, etc... + Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. + Apps will still need the string here for older builds + (so OTA update can find the right image), but if the enum is available it will be used instead. */ + meshtastic_HardwareModel hw_model; + /* In some regions Ham radio operators have different bandwidth limitations than others. + If this user is a licensed operator, set this flag. + Also, "long_name" should be their licence number. */ + bool is_licensed; + /* Indicates that the user's role in the mesh */ + meshtastic_Config_DeviceConfig_Role role; + /* The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_UserLite_public_key_t public_key; +} meshtastic_UserLite; + typedef struct _meshtastic_NodeInfoLite { /* The node number */ uint32_t num; /* The user info for this node */ bool has_user; - meshtastic_User user; + meshtastic_UserLite user; /* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. Position.time now indicates the last time we received a POSITION from that node. */ bool has_position; @@ -164,6 +190,9 @@ extern "C" { #define meshtastic_PositionLite_location_source_ENUMTYPE meshtastic_Position_LocSource +#define meshtastic_UserLite_hw_model_ENUMTYPE meshtastic_HardwareModel +#define meshtastic_UserLite_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role + @@ -172,12 +201,14 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} -#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_User_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} +#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} +#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} #define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}} #define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0} #define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default} #define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} -#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} +#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} +#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} #define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}} #define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0} #define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero} @@ -188,6 +219,13 @@ extern "C" { #define meshtastic_PositionLite_altitude_tag 3 #define meshtastic_PositionLite_time_tag 4 #define meshtastic_PositionLite_location_source_tag 5 +#define meshtastic_UserLite_macaddr_tag 1 +#define meshtastic_UserLite_long_name_tag 2 +#define meshtastic_UserLite_short_name_tag 3 +#define meshtastic_UserLite_hw_model_tag 4 +#define meshtastic_UserLite_is_licensed_tag 5 +#define meshtastic_UserLite_role_tag 6 +#define meshtastic_UserLite_public_key_tag 7 #define meshtastic_NodeInfoLite_num_tag 1 #define meshtastic_NodeInfoLite_user_tag 2 #define meshtastic_NodeInfoLite_position_tag 3 @@ -229,6 +267,17 @@ X(a, STATIC, SINGULAR, UENUM, location_source, 5) #define meshtastic_PositionLite_CALLBACK NULL #define meshtastic_PositionLite_DEFAULT NULL +#define meshtastic_UserLite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 1) \ +X(a, STATIC, SINGULAR, STRING, long_name, 2) \ +X(a, STATIC, SINGULAR, STRING, short_name, 3) \ +X(a, STATIC, SINGULAR, UENUM, hw_model, 4) \ +X(a, STATIC, SINGULAR, BOOL, is_licensed, 5) \ +X(a, STATIC, SINGULAR, UENUM, role, 6) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 7) +#define meshtastic_UserLite_CALLBACK NULL +#define meshtastic_UserLite_DEFAULT NULL + #define meshtastic_NodeInfoLite_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ @@ -242,7 +291,7 @@ X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) #define meshtastic_NodeInfoLite_CALLBACK NULL #define meshtastic_NodeInfoLite_DEFAULT NULL -#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_User +#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_UserLite #define meshtastic_NodeInfoLite_position_MSGTYPE meshtastic_PositionLite #define meshtastic_NodeInfoLite_device_metrics_MSGTYPE meshtastic_DeviceMetrics @@ -290,6 +339,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, oem_local_module_config, 8) #define meshtastic_OEMStore_oem_local_module_config_MSGTYPE meshtastic_LocalModuleConfig extern const pb_msgdesc_t meshtastic_PositionLite_msg; +extern const pb_msgdesc_t meshtastic_UserLite_msg; extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg; extern const pb_msgdesc_t meshtastic_DeviceState_msg; extern const pb_msgdesc_t meshtastic_ChannelFile_msg; @@ -297,6 +347,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg +#define meshtastic_UserLite_fields &meshtastic_UserLite_msg #define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg #define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg #define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg @@ -306,9 +357,10 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* meshtastic_DeviceState_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 200 +#define meshtastic_NodeInfoLite_size 183 #define meshtastic_OEMStore_size 3502 #define meshtastic_PositionLite_size 28 +#define meshtastic_UserLite_size 96 #ifdef __cplusplus } /* extern "C" */ From 48eee747da8a092d8272ee4f16fa0dcd8da8590c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 11 Aug 2024 18:25:32 -0500 Subject: [PATCH 096/305] protos --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index ee41c42e4..0c052b5d2 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit ee41c42e4f89d4153415b941305d23dec3647210 +Subproject commit 0c052b5d25fe8ed74c675178702f20a3fbc29afa From c74bce93607cd1749d7e8157089b6e1172f1b521 Mon Sep 17 00:00:00 2001 From: Ben Loomis Date: Mon, 12 Aug 2024 04:40:57 -0700 Subject: [PATCH 097/305] Detect UM600 as UC6580 (#4444) --- src/gps/GPS.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 82370937b..0164b554d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1220,6 +1220,14 @@ GnssModel_t GPS::probe(int serialSpeed) return GNSS_MODEL_UC6580; } + clearBuffer(); + _serial_gps->write("$PDTINFO\r\n"); + delay(750); + if (getACK("UM600", 500) == GNSS_RESPONSE_OK) { + LOG_INFO("UM600 detected, using UC6580 Module\n"); + return GNSS_MODEL_UC6580; + } + // Get version information for ATGM336H clearBuffer(); _serial_gps->write("$PCAS06,1*1A\r\n"); @@ -1822,4 +1830,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file From bee959150b0938f983af370958240dea654e1d00 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 06:43:54 -0500 Subject: [PATCH 098/305] Add logic to nodeDB to prefer evicting boring nodes (#4441) --- src/mesh/NodeDB.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index e3030fb02..b68fdaae8 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1115,11 +1115,24 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // look for oldest node and erase it uint32_t oldest = UINT32_MAX; int oldestIndex = -1; + int oldestIndex = -1; + int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { + // Simply the oldest non-favorite node if (!meshNodes->at(i).is_favorite && meshNodes->at(i).last_heard < oldest) { oldest = meshNodes->at(i).last_heard; oldestIndex = i; } + // The oldest "boring" node + if (!meshNodes->at(i).is_favorite && meshNodes->at(i).user.public_key.size == 0 && + meshNodes->at(i).last_heard < oldestBoring) { + oldestBoring = meshNodes->at(i).last_heard; + oldestBoringIndex = i; + } + } + // if we found a "boring" node, evict it + if (oldestBoringIndex != -1) { + oldestIndex = oldestBoringIndex; } // Shove the remaining nodes down the chain for (int i = oldestIndex; i < numMeshNodes - 1; i++) { @@ -1160,4 +1173,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} \ No newline at end of file +} From 2ee53d1500679a8b386c68eae53facf6b74f5943 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 11:26:43 -0500 Subject: [PATCH 099/305] Don't goober public_key in Userlite conversion --- src/mesh/NodeDB.cpp | 15 ++++++++++++--- src/mesh/TypeConversions.cpp | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index b68fdaae8..376593fcd 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1025,9 +1025,15 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) - if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one - printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); - memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + if (p.public_key.size > 0) { + printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); + if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one + LOG_INFO("Public Key set for node, not updateing!\n"); + // we copy the key into the incoming packet, to prevent overwrite + memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + } else { + LOG_INFO("Updating Node Pubkey!\n"); + } } #endif @@ -1035,6 +1041,9 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); info->user = TypeConversions::ConvertToUserLite(p); + if (info->user.public_key.size == 32) { + printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32); + } if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 30b06d0ad..5303dfb49 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -68,6 +68,7 @@ meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) lite.is_licensed = user.is_licensed; memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); + lite.public_key.size = user.public_key.size; return lite; } From bc69621c3e8d4945bdca2790d84459bc14711daa Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 11:37:50 -0500 Subject: [PATCH 100/305] Ungoober oldestBoring --- src/mesh/NodeDB.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 376593fcd..33d8b51ef 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1123,7 +1123,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) memGet.getFreeHeap()); // look for oldest node and erase it uint32_t oldest = UINT32_MAX; - int oldestIndex = -1; + uint32_t oldestBoring = UINT32_MAX; int oldestIndex = -1; int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { From 9bd293a94106f50abcc86ede4c0fef4b92852b7c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 16:20:07 -0500 Subject: [PATCH 101/305] Don't forget public_key.size in converting back --- src/mesh/TypeConversions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 5303dfb49..d8ee6afc7 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -84,6 +84,7 @@ meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_User user.is_licensed = lite.is_licensed; memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); + user.public_key.size = lite.public_key.size; return user; } \ No newline at end of file From f97ae522630f9ae73005807bb6e9bc7e8b8e6ddd Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Tue, 13 Aug 2024 03:31:45 +0200 Subject: [PATCH 102/305] STM32WL improvements (#4449) * STM32WL: Enable DeviceTelemetry * Add long/short name user preference options * Add new STM32WL-based hardware models --- arch/stm32/stm32.ini | 4 ++-- src/mesh/NodeDB.cpp | 8 ++++++++ src/platform/stm32wl/architecture.h | 12 +++++++++--- userPrefs.h | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index d6d14e2c9..ffbc51680 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -21,7 +21,7 @@ build_flags = -fdata-sections build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink @@ -33,4 +33,4 @@ lib_deps = lib_ignore = https://github.com/mathertel/OneButton@~2.6.1 - Wire + Wire \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0f6c444ce..666255c74 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -563,8 +563,16 @@ void NodeDB::installDefaultDeviceState() // Set default owner name pickNewNodeNum(); // based on macaddr now +#ifdef CONFIG_OWNER_LONG_NAME_USERPREFS + snprintf(owner.long_name, sizeof(owner.long_name), CONFIG_OWNER_LONG_NAME_USERPREFS); +#else snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]); +#endif +#ifdef CONFIG_OWNER_SHORT_NAME_USERPREFS + snprintf(owner.short_name, sizeof(owner.short_name), CONFIG_OWNER_SHORT_NAME_USERPREFS); +#else snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]); +#endif snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr)); } diff --git a/src/platform/stm32wl/architecture.h b/src/platform/stm32wl/architecture.h index 1c021903a..325a192a4 100644 --- a/src/platform/stm32wl/architecture.h +++ b/src/platform/stm32wl/architecture.h @@ -9,12 +9,18 @@ #ifndef HAS_RADIO #define HAS_RADIO 1 #endif +#ifndef HAS_TELEMETRY +#define HAS_TELEMETRY 1 +#endif // // set HW_VENDOR // - -#ifndef HW_VENDOR +#ifdef _VARIANT_WIOE5_ +#define HW_VENDOR meshtastic_HardwareModel_WIO_E5 +#elif defined(_VARIANT_RAK3172_) +#define HW_VENDOR meshtastic_HardwareModel_RAK3172 +#else #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #endif @@ -22,4 +28,4 @@ #define SX126X_CS 1000 #define SX126X_DIO1 1001 #define SX126X_RESET 1003 -#define SX126X_BUSY 1004 +#define SX126X_BUSY 1004 \ No newline at end of file diff --git a/userPrefs.h b/userPrefs.h index b365e8c6f..3ebbefcaf 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -19,6 +19,9 @@ // #define CHANNEL_0_NAME_USERPREFS "DEFCONnect" // #define CHANNEL_0_PRECISION_USERPREFS 13 +// #define CONFIG_OWNER_LONG_NAME_USERPREFS "My Long Name" +// #define CONFIG_OWNER_SHORT_NAME_USERPREFS "MLN" + // #define SPLASH_TITLE_USERPREFS "DEFCONtastic" // #define icon_width 34 // #define icon_height 29 From 6e8300287b8ff056f5987242456334480493d467 Mon Sep 17 00:00:00 2001 From: "Aaron.Lee" <32860565+Heltec-Aaron-Lee@users.noreply.github.com> Date: Tue, 13 Aug 2024 19:30:35 +0800 Subject: [PATCH 103/305] Heltec boards sensor and low power features update (#4418) * Update sensor drive and low power features. * Update ST7789 TFT control logic. * Update Heltec nRF board low power features. * Update the GPS UART port pointer --- src/gps/GPS.cpp | 10 ++++++++++ src/graphics/Screen.cpp | 20 +++++++++++++++++--- src/main.cpp | 19 +++++++++++++++++++ src/platform/esp32/main-esp32.cpp | 5 +++++ src/platform/nrf52/NRF52Bluetooth.cpp | 11 +++++++++-- src/platform/nrf52/main-nrf52.cpp | 6 ++++++ src/sleep.cpp | 7 +++++++ variants/heltec_capsule_sensor_v3/variant.h | 10 ++++++++++ variants/heltec_mesh_node_t114/variant.h | 2 +- 9 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 0164b554d..89af0430a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -810,6 +810,16 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) powerState = newState; LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); +#ifdef HELTEC_MESH_NODE_T114 + if( (oldState==GPS_OFF || oldState==GPS_HARDSLEEP) && (newState!=GPS_OFF && newState!=GPS_HARDSLEEP) ) + { + _serial_gps->begin(serialSpeeds[speedSelect]); + } + else if( (newState==GPS_OFF || newState==GPS_HARDSLEEP) && (oldState!=GPS_OFF && oldState!=GPS_HARDSLEEP) ) + { + _serial_gps->end(); + } +#endif switch (newState) { case GPS_ACTIVE: case GPS_IDLE: diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index ea5ab9788..bc7b3d1e6 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1591,6 +1591,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #ifdef USE_ST7789 + pinMode(VTFT_CTRL, OUTPUT); + digitalWrite(VTFT_CTRL,LOW); + ui->init(); #ifdef ESP_PLATFORM analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT); #else @@ -1609,10 +1612,21 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #endif LOG_INFO("Turning off screen\n"); dispdev->displayOff(); - #ifdef USE_ST7789 - pinMode(VTFT_LEDA, OUTPUT); - digitalWrite(VTFT_LEDA, !TFT_BACKLIGHT_ON); + SPI1.end(); +#if defined(ARCH_ESP32) + pinMode(VTFT_LEDA, ANALOG); + pinMode(VTFT_CTRL, ANALOG); + pinMode(ST7789_RESET,ANALOG); + pinMode(ST7789_RS,ANALOG); + pinMode(ST7789_NSS,ANALOG); +#else + nrf_gpio_cfg_default(VTFT_LEDA); + nrf_gpio_cfg_default(VTFT_CTRL); + nrf_gpio_cfg_default(ST7789_RESET); + nrf_gpio_cfg_default(ST7789_RS); + nrf_gpio_cfg_default(ST7789_NSS); +#endif #endif #ifdef T_WATCH_S3 diff --git a/src/main.cpp b/src/main.cpp index 0a3c1ae0b..c3e779554 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -319,6 +319,14 @@ void setup() digitalWrite(RESET_OLED, 1); #endif +#ifdef SENSOR_POWER_CTRL_PIN + pinMode(SENSOR_POWER_CTRL_PIN,OUTPUT); + digitalWrite(SENSOR_POWER_CTRL_PIN,SENSOR_POWER_ON); +#endif + +#ifdef SENSOR_GPS_CONFLICT + bool sensor_detected=false; +#endif #ifdef PERIPHERAL_WARMUP_MS // Some peripherals may require additional time to stabilize after power is connected // e.g. I2C on Heltec Vision Master @@ -458,6 +466,9 @@ void setup() LOG_INFO("No I2C devices found\n"); } else { LOG_INFO("%i I2C devices found\n", i2cCount); +#ifdef SENSOR_GPS_CONFLICT + sensor_detected=true; +#endif } #ifdef ARCH_ESP32 @@ -703,6 +714,10 @@ void setup() #if !MESHTASTIC_EXCLUDE_GPS // If we're taking on the repeater role, ignore GPS +#ifdef SENSOR_GPS_CONFLICT + if(sensor_detected==false) + { +#endif if (HAS_GPS) { if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { @@ -714,6 +729,10 @@ void setup() } } } +#ifdef SENSOR_GPS_CONFLICT + } +#endif + #endif nodeStatus->observe(&nodeDB->newStatus); diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 19f435908..36bd3fbb3 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -228,6 +228,9 @@ void cpuDeepSleep(uint32_t msecToWake) // FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead // of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN); +#ifdef ESP32S3_WAKE_TYPE + esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE); +#else #if SOC_PM_SUPPORT_EXT_WAKEUP #ifdef CONFIG_IDF_TARGET_ESP32 // ESP_EXT1_WAKEUP_ALL_LOW has been deprecated since esp-idf v5.4 for any other target. @@ -236,6 +239,8 @@ void cpuDeepSleep(uint32_t msecToWake) esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_LOW); #endif #endif + +#endif //#end ESP32S3_WAKE_TYPE #endif // We want RTC peripherals to stay on diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 81a165f2d..b27f9df0c 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -198,8 +198,15 @@ void NRF52Bluetooth::shutdown() { // Shutdown bluetooth for minimum power draw LOG_INFO("Disable NRF52 bluetooth\n"); - if (connectionHandle != 0) { - Bluefruit.disconnect(connectionHandle); + uint8_t connection_num=Bluefruit.connected(); + if(connection_num) + { + for(uint8_t i=0;i 100ms will reset the L76K +//#define PIN_GPS_RESET (32 + 6) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K #define GPS_RESET_MODE LOW #define PIN_GPS_EN (21) #define GPS_EN_ACTIVE HIGH From 62a0321c7d0dc351bb87ea6abda3b1a991b47e20 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 13 Aug 2024 04:45:39 -0700 Subject: [PATCH 104/305] Fixes for #4395: nrf52 flash filesystem reliability (#4406) * bug #4184: fix config file loss due to filesystem write errors * Use SafeFile for atomic file writing (with xor checksum readback) * Write db.proto last because it could be the largest file on the FS (and less critical) * Don't keep a tmp file around while writing db.proto (because too big to fit two files in the filesystem) * generate a new critial fault if we encounter errors writing to flash either CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE or CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE (depending on if the second write attempt worked) * reformat the filesystem if we detect it is corrupted (then rewrite our config files) (only on nrf52 - not sure yet if we should bother on ESP32) * If we have to format the FS, make sure to preserve the oem.proto if it exists * add logLegacy() so old C code in libs can log via our logging * move filesList() to a better location (used only in developer builds) * Reformat with "trunk fmt" to match our coding conventions * for #4395: don't use .exists() to before attempting file open If a LFS filesystem is corrupted, .exists() can fail when a mere .open() attempt would have succeeded. Therefore better to do the .open() in hopes that we can read the file (in case we need to reformat to fix the FS). (Seen and confirmed in stress testing) * for #4395 more fixes, see below for details: * check for LFS assertion failures during file operations (needs customized lfs_util.h to provide suitable hooks) * Remove fsCheck() because checking filesystem by writing to it is very high risk, it makes likelyhood that we will be able to read the config protobufs quite low. * Update the LFS inside of adafruitnrf52 to 1.7.2 (from their old 1.6.1) to get the following fix: https://github.com/littlefs-project/littlefs/commit/97d8d5e96a7781596708664f18f2ea6c3a179330 * use disable_adafruit_usb.py now that we are (temporarily?) using a forked adafruit lib We need to reach inside the adafruit project and turn off USE_TINYUSB, just doing that from platformio.ini is no longer sufficient. Tested on a wio-sdk-wm1110 board (which is the only board that had this problem) --------- Co-authored-by: Ben Meadors --- arch/nrf52/cpp_overrides/lfs_util.h | 208 +++++++++++++++++++++++++ arch/nrf52/nrf52.ini | 6 +- extra_scripts/disable_adafruit_usb.py | 18 ++- monitor/filter_c3_exception_decoder.py | 21 +-- src/DebugConfiguration.cpp | 12 +- src/DebugConfiguration.h | 3 + src/FSCommon.cpp | 100 ++---------- src/FSCommon.h | 8 +- src/SafeFile.cpp | 8 +- src/mesh/NodeDB.cpp | 26 ---- variants/wio-sdk-wm1110/platformio.ini | 30 ++-- 11 files changed, 285 insertions(+), 155 deletions(-) create mode 100644 arch/nrf52/cpp_overrides/lfs_util.h diff --git a/arch/nrf52/cpp_overrides/lfs_util.h b/arch/nrf52/cpp_overrides/lfs_util.h new file mode 100644 index 000000000..bf5a347b1 --- /dev/null +++ b/arch/nrf52/cpp_overrides/lfs_util.h @@ -0,0 +1,208 @@ +/* + * lfs utility functions + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +// MESHTASTIC/@geeksville note: This file is copied from the Adafruit nrf52 arduino lib. And we use a special -include in +// nrf52.ini to load it before EVERY file we do this hack because the default definitions for LFS_ASSERT are quite poor and we +// don't want to fork the adafruit lib (again) and send in a PR that they probably won't merge anyways. This file might break if +// they ever update lfs.util on their side, in which case we'll need to update this file to match their new version. The version +// this is a copy from is almost exactly +// https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/c25d93268a3b9c23e9a1ccfcaf9b208beca624ca/libraries/Adafruit_LittleFS/src/littlefs/lfs_util.h + +#ifndef LFS_UTIL_H +#define LFS_UTIL_H + +// Users can override lfs_util.h with their own configuration by defining +// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). +// +// If LFS_CONFIG is used, none of the default utils will be emitted and must be +// provided by the config file. To start I would suggest copying lfs_util.h and +// modifying as needed. +#ifdef LFS_CONFIG +#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) +#define LFS_STRINGIZE2(x) #x +#include LFS_STRINGIZE(LFS_CONFIG) +#else + +// System includes +#include +#include +#include + +#ifndef LFS_NO_MALLOC +#include +#endif +#ifndef LFS_NO_ASSERT +#include +#endif + +#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Macros, may be replaced by system specific wrappers. Arguments to these +// macros must not have side-effects as the macros can be removed for a smaller +// code footprint + +// Logging functions +#ifndef LFS_NO_DEBUG + +void logLegacy(const char *level, const char *fmt, ...); +#define LFS_DEBUG(fmt, ...) logLegacy("DEBUG", "lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#else +#define LFS_DEBUG(fmt, ...) +#endif + +#ifndef LFS_NO_WARN +#define LFS_WARN(fmt, ...) logLegacy("WARN", "lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#else +#define LFS_WARN(fmt, ...) +#endif + +#ifndef LFS_NO_ERROR +#define LFS_ERROR(fmt, ...) logLegacy("ERROR", "lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#else +#define LFS_ERROR(fmt, ...) +#endif + +// Runtime assertions +#ifndef LFS_NO_ASSERT +#define LFS_ASSERT(test) assert(test) +#else +extern void lfs_assert(const char *reason); +#define LFS_ASSERT(test) \ + if (!(test)) \ + lfs_assert(#test) +#endif + +// Builtin functions, these may be replaced by more efficient +// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more +// expensive basic C implementation for debugging purposes + +// Min/max functions for unsigned 32-bit numbers +static inline uint32_t lfs_max(uint32_t a, uint32_t b) +{ + return (a > b) ? a : b; +} + +static inline uint32_t lfs_min(uint32_t a, uint32_t b) +{ + return (a < b) ? a : b; +} + +// Find the next smallest power of 2 less than or equal to a +static inline uint32_t lfs_npw2(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return 32 - __builtin_clz(a - 1); +#else + uint32_t r = 0; + uint32_t s; + a -= 1; + s = (a > 0xffff) << 4; + a >>= s; + r |= s; + s = (a > 0xff) << 3; + a >>= s; + r |= s; + s = (a > 0xf) << 2; + a >>= s; + r |= s; + s = (a > 0x3) << 1; + a >>= s; + r |= s; + return (r | (a >> 1)) + 1; +#endif +} + +// Count the number of trailing binary zeros in a +// lfs_ctz(0) may be undefined +static inline uint32_t lfs_ctz(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) + return __builtin_ctz(a); +#else + return lfs_npw2((a & -a) + 1) - 1; +#endif +} + +// Count the number of binary ones in a +static inline uint32_t lfs_popc(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return __builtin_popcount(a); +#else + a = a - ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; +#endif +} + +// Find the sequence comparison of a and b, this is the distance +// between a and b ignoring overflow +static inline int lfs_scmp(uint32_t a, uint32_t b) +{ + return (int)(unsigned)(a - b); +} + +// Convert from 32-bit little-endian to native order +static inline uint32_t lfs_fromle32(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + return a; +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + return __builtin_bswap32(a); +#else + return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) | (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24); +#endif +} + +// Convert to 32-bit little-endian from native order +static inline uint32_t lfs_tole32(uint32_t a) +{ + return lfs_fromle32(a); +} + +// Calculate CRC-32 with polynomial = 0x04c11db7 +void lfs_crc(uint32_t *crc, const void *buffer, size_t size); + +// Allocate memory, only used if buffers are not provided to littlefs +static inline void *lfs_malloc(size_t size) +{ +#ifndef LFS_NO_MALLOC + extern void *pvPortMalloc(size_t xWantedSize); + return pvPortMalloc(size); +#else + (void)size; + return NULL; +#endif +} + +// Deallocate memory, only used if buffers are not provided to littlefs +static inline void lfs_free(void *p) +{ +#ifndef LFS_NO_MALLOC + extern void vPortFree(void *pv); + vPortFree(p); +#else + (void)p; +#endif +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif \ No newline at end of file diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 1a371e920..f3e7c3036 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -2,9 +2,13 @@ ; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files platform = platformio/nordicnrf52@^10.5.0 extends = arduino_base +platform_packages = + ; our custom Git version until they merge our PR + framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git build_type = debug -build_flags = +build_flags = + -include arch/nrf52/cpp_overrides/lfs_util.h ${arduino_base.build_flags} -DSERIAL_BUFFER_SIZE=1024 -Wno-unused-variable diff --git a/extra_scripts/disable_adafruit_usb.py b/extra_scripts/disable_adafruit_usb.py index fb391715c..596242184 100644 --- a/extra_scripts/disable_adafruit_usb.py +++ b/extra_scripts/disable_adafruit_usb.py @@ -5,16 +5,24 @@ Import("env") # NOTE: This is not currently used, but can serve as an example on how to write extra_scripts -print("Current CLI targets", COMMAND_LINE_TARGETS) -print("Current Build targets", BUILD_TARGETS) -print("CPP defs", env.get("CPPDEFINES")) +# print("Current CLI targets", COMMAND_LINE_TARGETS) +# print("Current Build targets", BUILD_TARGETS) +# print("CPP defs", env.get("CPPDEFINES")) +# print(env.Dump()) # Adafruit.py in the platformio build tree is a bit naive and always enables their USB stack for building. We don't want this. # So come in after that python script has run and disable it. This hack avoids us having to fork that big project and send in a PR # which might not be accepted. -@geeksville -env["CPPDEFINES"].remove("USBCON") -env["CPPDEFINES"].remove("USE_TINYUSB") +lib_builders = env.get("__PIO_LIB_BUILDERS", None) +if lib_builders is not None: + print("Disabling Adafruit USB stack") + for k in lib_builders: + if k.name == "Adafruit TinyUSB Library": + libenv = k.env + # print(f"{k.name }: { libenv.Dump() } ") + # libenv["CPPDEFINES"].remove("USBCON") + libenv["CPPDEFINES"].remove("USE_TINYUSB") # Custom actions when building program/firmware # env.AddPreAction("buildprog", callback...) diff --git a/monitor/filter_c3_exception_decoder.py b/monitor/filter_c3_exception_decoder.py index e59b0be2a..6d7b5370c 100644 --- a/monitor/filter_c3_exception_decoder.py +++ b/monitor/filter_c3_exception_decoder.py @@ -18,10 +18,7 @@ import subprocess import sys from platformio.project.exception import PlatformioException -from platformio.public import ( - DeviceMonitorFilterBase, - load_build_metadata, -) +from platformio.public import DeviceMonitorFilterBase, load_build_metadata # By design, __init__ is called inside miniterm and we can't pass context to it. # pylint: disable=attribute-defined-outside-init @@ -32,7 +29,7 @@ IS_WINDOWS = sys.platform.startswith("win") class Esp32C3ExceptionDecoder(DeviceMonitorFilterBase): NAME = "esp32_c3_exception_decoder" - PCADDR_PATTERN = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE) + PCADDR_PATTERN = re.compile(r"0x4[0-9a-f]{7}", re.IGNORECASE) def __call__(self): self.buffer = "" @@ -75,14 +72,14 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html % self.__class__.__name__ ) return False - + if not os.path.isfile(self.addr2line_path): sys.stderr.write( "%s: disabling, addr2line at %s does not exist\n" % (self.__class__.__name__, self.addr2line_path) ) return False - + return True except PlatformioException as e: sys.stderr.write( @@ -117,7 +114,7 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html trace = self.get_backtrace(m) if len(trace) != "": - text = text[: last] + trace + text[last :] + text = text[:last] + trace + text[last:] last += len(trace) return text @@ -125,14 +122,10 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html def get_backtrace(self, match): trace = "\n" enc = "mbcs" if IS_WINDOWS else "utf-8" - args = [self.addr2line_path, u"-fipC", u"-e", self.firmware_path] + args = [self.addr2line_path, "-fipC", "-e", self.firmware_path] try: addr = match.group() - output = ( - subprocess.check_output(args + [addr]) - .decode(enc) - .strip() - ) + output = subprocess.check_output(args + [addr]).decode(enc).strip() output = output.replace( "\n", "\n " ) # newlines happen with inlined methods diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp index d9ecd9fe3..d58856c4d 100644 --- a/src/DebugConfiguration.cpp +++ b/src/DebugConfiguration.cpp @@ -26,6 +26,16 @@ SOFTWARE.*/ #include "DebugConfiguration.h" +/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic +extern "C" void logLegacy(const char *level, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (console) + console->vprintf(level, fmt, args); + va_end(args); +} + #if HAS_NETWORKING Syslog::Syslog(UDP &client) @@ -169,4 +179,4 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess return true; } -#endif +#endif \ No newline at end of file diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index f5b3fa25a..7987e7fa1 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -62,6 +62,9 @@ #endif #endif +/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic +extern "C" void logLegacy(const char *level, const char *fmt, ...); + #define SYSLOG_NILVALUE "-" #define SYSLOG_CRIT 2 /* critical conditions */ diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 3017ec085..f45319c0b 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -48,6 +48,15 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input) } #endif +bool lfs_assert_failed = + false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h) + +extern "C" void lfs_assert(const char *reason) +{ + LOG_ERROR("LFS assert: %s\n", reason); + lfs_assert_failed = true; +} + /** * @brief Copies a file from one location to another. * @@ -199,7 +208,7 @@ std::vector getFiles(const char *dirname, uint8_t levels) * @param levels The number of levels of subdirectories to list. * @param del Whether or not to delete the contents of the directory after listing. */ -void listDir(const char *dirname, uint8_t levels, bool del = false) +void listDir(const char *dirname, uint8_t levels, bool del) { #ifdef FSCom #if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) @@ -214,7 +223,9 @@ void listDir(const char *dirname, uint8_t levels, bool del = false) } File file = root.openNextFile(); - while (file) { + while ( + file && + file.name()[0]) { // This file.name() check is a workaround for a bug in the Adafruit LittleFS nrf52 glue (see issue 4395) if (file.isDirectory() && !String(file.name()).endsWith(".")) { if (levels) { #ifdef ARCH_ESP32 @@ -313,62 +324,6 @@ void rmDir(const char *dirname) #endif } -bool fsCheck() -{ -#if defined(ARCH_NRF52) - size_t write_size = 0; - size_t read_size = 0; - char buf[32] = {0}; - - Adafruit_LittleFS_Namespace::File file(FSCom); - const char *text = "meshtastic fs test"; - size_t text_length = strlen(text); - const char *filename = "/meshtastic.txt"; - - LOG_DEBUG("Try create file .\n"); - if (file.open(filename, FILE_O_WRITE)) { - write_size = file.write(text); - } else { - LOG_DEBUG("Open file failed .\n"); - goto FORMAT_FS; - } - - if (write_size != text_length) { - LOG_DEBUG("Text bytes do not match .\n"); - file.close(); - goto FORMAT_FS; - } - - file.close(); - - if (!file.open(filename, FILE_O_READ)) { - LOG_DEBUG("Open file failed .\n"); - goto FORMAT_FS; - } - - read_size = file.readBytes(buf, text_length); - if (read_size != text_length) { - LOG_DEBUG("Text bytes do not match .\n"); - file.close(); - goto FORMAT_FS; - } - - if (memcmp(buf, text, text_length) != 0) { - LOG_DEBUG("The written bytes do not match the read bytes .\n"); - file.close(); - goto FORMAT_FS; - } - return true; -FORMAT_FS: - LOG_DEBUG("Format FS ....\n"); - FSCom.format(); - FSCom.begin(); - return false; -#else - return true; -#endif -} - void fsInit() { #ifdef FSCom @@ -378,35 +333,6 @@ void fsInit() } #if defined(ARCH_ESP32) LOG_DEBUG("Filesystem files (%d/%d Bytes):\n", FSCom.usedBytes(), FSCom.totalBytes()); -#elif defined(ARCH_NRF52) - /* - * nRF52840 has a certain chance of automatic formatting failure. - * Try to create a file after initializing the file system. If the creation fails, - * it means that the file system is not working properly. Please format it manually again. - * To check the normality of the file system, you need to disable the LFS_NO_ASSERT assertion. - * Otherwise, the assertion will be entered at the moment of reading or opening, and the FS will not be formatted. - * */ - bool ret = false; - uint8_t retry = 3; - - while (retry--) { - ret = fsCheck(); - if (ret) { - LOG_DEBUG("File system check is OK.\n"); - break; - } - delay(10); - } - - // It may not be possible to reach this step. - // Add a loop here to prevent unpredictable situations from happening. - // Can add a screen to display error status later. - if (!ret) { - while (1) { - LOG_ERROR("The file system is damaged and cannot proceed to the next step.\n"); - delay(1000); - } - } #else LOG_DEBUG("Filesystem files:\n"); #endif diff --git a/src/FSCommon.h b/src/FSCommon.h index 3d485d1b1..254245b29 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -51,9 +51,13 @@ using namespace Adafruit_LittleFS_Namespace; #endif void fsInit(); +void fsListFiles(); bool copyFile(const char *from, const char *to); bool renameFile(const char *pathFrom, const char *pathTo); std::vector getFiles(const char *dirname, uint8_t levels); -void listDir(const char *dirname, uint8_t levels, bool del); +void listDir(const char *dirname, uint8_t levels, bool del = false); void rmDir(const char *dirname); -void setupSDCard(); \ No newline at end of file +void setupSDCard(); + +extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our + // modified lfs_util.h) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index 161a80870..c17d422bd 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -11,6 +11,9 @@ static File openFile(const char *filename, bool fullAtomic) String filenameTmp = filename; filenameTmp += ".tmp"; + // clear any previous LFS errors + lfs_assert_failed = false; + return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -73,6 +76,9 @@ bool SafeFile::close() /// Read our (closed) tempfile back in and compare the hash bool SafeFile::testReadback() { + bool lfs_failed = lfs_assert_failed; + lfs_assert_failed = false; + String filenameTmp = filename; filenameTmp += ".tmp"; auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ); @@ -93,7 +99,7 @@ bool SafeFile::testReadback() return false; } - return true; + return !lfs_failed; } #endif \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 666255c74..000da335f 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -56,27 +56,6 @@ meshtastic_ChannelFile channelFile; meshtastic_OEMStore oemStore; static bool hasOemStore = false; -// These are not publically exposed - copied from InternalFileSystem.cpp -// #define FLASH_NRF52_PAGE_SIZE 4096 -// #define LFS_FLASH_TOTAL_SIZE (7*FLASH_NRF52_PAGE_SIZE) -// #define LFS_BLOCK_SIZE 128 - -/// List all files in the FS and test write and readback. -/// Useful for filesystem stress testing - normally stripped from build by the linker. -void flashTest() -{ - auto filesManifest = getFiles("/", 5); - - uint32_t totalSize = 0; - for (size_t i = 0; i < filesManifest.size(); i++) { - LOG_INFO("File %s (size %d)\n", filesManifest[i].file_name, filesManifest[i].size_bytes); - totalSize += filesManifest[i].size_bytes; - } - LOG_INFO("%d files (total size %u)\n", filesManifest.size(), totalSize); - // LOG_INFO("Filesystem block size %u, total bytes %u", LFS_FLASH_TOTAL_SIZE, LFS_BLOCK_SIZE); - nodeDB->saveToDisk(); -} - bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field) { if (ostream) { @@ -617,11 +596,6 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t LoadFileResult state = LoadFileResult::OTHER_FAILURE; #ifdef FSCom - if (!FSCom.exists(filename)) { - LOG_INFO("File %s not found\n", filename); - return LoadFileResult::NOT_FOUND; - } - auto f = FSCom.open(filename, FILE_O_READ); if (f) { diff --git a/variants/wio-sdk-wm1110/platformio.ini b/variants/wio-sdk-wm1110/platformio.ini index 077356553..e77455bcf 100644 --- a/variants/wio-sdk-wm1110/platformio.ini +++ b/variants/wio-sdk-wm1110/platformio.ini @@ -3,29 +3,23 @@ extends = nrf52840_base board = wio-sdk-wm1110 +extra_scripts = + bin/platformio-custom.py + extra_scripts/disable_adafruit_usb.py + # Remove adafruit USB serial from the build (it is incompatible with using the ch340 serial chip on this board) build_unflags = ${nrf52840_base:build_unflags} -DUSBCON -DUSE_TINYUSB build_flags = ${nrf52840_base.build_flags} -Ivariants/wio-sdk-wm1110 -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DWIO_WM1110 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DCFG_TUD_CDC=0 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/wio-sdk-wm1110> -lib_deps = - ${nrf52840_base.lib_deps} -debug_tool = jlink -;debug_tool = stlink -;debug_speed = 4000 -; No need to reflash if the binary hasn't changed -debug_load_mode = modified -; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -upload_protocol = nrfutil -;upload_protocol = stlink -; we prefer to stop in setup() because we are an 'ardiuno' app -debug_init_break = tbreak setup -; we need to turn off BLE/soft device if we are debugging otherwise it will watchdog reset us. -debug_extra_cmds = - echo Running .gdbinit script - commands 1 - set useSoftDevice = false - end +;debug_tool = jlink +debug_tool = stlink +; No need to reflash if the binary hasn't changed + +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +;upload_protocol = nrfutil +;upload_protocol = stlink \ No newline at end of file From e85a2e827bfe455648c644ac263adc23b4d6bc8c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 13 Aug 2024 06:49:32 -0500 Subject: [PATCH 105/305] Update protos --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 071fd931e..c9ca0dbe9 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 071fd931ec6679bb21427c872f9839edea63e351 +Subproject commit c9ca0dbe9cc7105399e0486c07e0601f849b94af diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 1bea952ce..955484cb0 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -184,6 +184,9 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_RAK3172 = 72, /* Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. */ meshtastic_HardwareModel_WIO_E5 = 73, + /* RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module + SSD1306 OLED and No GPS */ + meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From 7740b4bccd8859fa92a5bb6285836722b8fa5318 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 13 Aug 2024 06:52:03 -0500 Subject: [PATCH 106/305] Sweep up some missed trunk formatting --- src/gps/GPS.cpp | 7 ++--- src/graphics/Screen.cpp | 8 +++--- src/main.cpp | 29 ++++++++++----------- src/platform/esp32/main-esp32.cpp | 4 +-- src/platform/nrf52/NRF52Bluetooth.cpp | 12 ++++----- src/sleep.cpp | 6 ++--- variants/heltec_capsule_sensor_v3/variant.h | 2 +- variants/heltec_mesh_node_t114/variant.h | 2 +- 8 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 89af0430a..7d7de8700 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -811,12 +811,9 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); #ifdef HELTEC_MESH_NODE_T114 - if( (oldState==GPS_OFF || oldState==GPS_HARDSLEEP) && (newState!=GPS_OFF && newState!=GPS_HARDSLEEP) ) - { + if ((oldState == GPS_OFF || oldState == GPS_HARDSLEEP) && (newState != GPS_OFF && newState != GPS_HARDSLEEP)) { _serial_gps->begin(serialSpeeds[speedSelect]); - } - else if( (newState==GPS_OFF || newState==GPS_HARDSLEEP) && (oldState!=GPS_OFF && oldState!=GPS_HARDSLEEP) ) - { + } else if ((newState == GPS_OFF || newState == GPS_HARDSLEEP) && (oldState != GPS_OFF && oldState != GPS_HARDSLEEP)) { _serial_gps->end(); } #endif diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index bc7b3d1e6..3a4db6ee0 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1592,7 +1592,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #ifdef USE_ST7789 pinMode(VTFT_CTRL, OUTPUT); - digitalWrite(VTFT_CTRL,LOW); + digitalWrite(VTFT_CTRL, LOW); ui->init(); #ifdef ESP_PLATFORM analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT); @@ -1617,9 +1617,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #if defined(ARCH_ESP32) pinMode(VTFT_LEDA, ANALOG); pinMode(VTFT_CTRL, ANALOG); - pinMode(ST7789_RESET,ANALOG); - pinMode(ST7789_RS,ANALOG); - pinMode(ST7789_NSS,ANALOG); + pinMode(ST7789_RESET, ANALOG); + pinMode(ST7789_RS, ANALOG); + pinMode(ST7789_NSS, ANALOG); #else nrf_gpio_cfg_default(VTFT_LEDA); nrf_gpio_cfg_default(VTFT_CTRL); diff --git a/src/main.cpp b/src/main.cpp index c3e779554..7f45905d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -320,12 +320,12 @@ void setup() #endif #ifdef SENSOR_POWER_CTRL_PIN - pinMode(SENSOR_POWER_CTRL_PIN,OUTPUT); - digitalWrite(SENSOR_POWER_CTRL_PIN,SENSOR_POWER_ON); + pinMode(SENSOR_POWER_CTRL_PIN, OUTPUT); + digitalWrite(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON); #endif #ifdef SENSOR_GPS_CONFLICT - bool sensor_detected=false; + bool sensor_detected = false; #endif #ifdef PERIPHERAL_WARMUP_MS // Some peripherals may require additional time to stabilize after power is connected @@ -467,7 +467,7 @@ void setup() } else { LOG_INFO("%i I2C devices found\n", i2cCount); #ifdef SENSOR_GPS_CONFLICT - sensor_detected=true; + sensor_detected = true; #endif } @@ -715,20 +715,19 @@ void setup() #if !MESHTASTIC_EXCLUDE_GPS // If we're taking on the repeater role, ignore GPS #ifdef SENSOR_GPS_CONFLICT - if(sensor_detected==false) - { + if (sensor_detected == false) { #endif - if (HAS_GPS) { - if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && - config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { - gps = GPS::createGps(); - if (gps) { - gpsStatus->observe(&gps->newStatus); - } else { - LOG_DEBUG("Running without GPS.\n"); + if (HAS_GPS) { + if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && + config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { + gps = GPS::createGps(); + if (gps) { + gpsStatus->observe(&gps->newStatus); + } else { + LOG_DEBUG("Running without GPS.\n"); + } } } - } #ifdef SENSOR_GPS_CONFLICT } #endif diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 36bd3fbb3..1b997500a 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -229,7 +229,7 @@ void cpuDeepSleep(uint32_t msecToWake) // of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN); #ifdef ESP32S3_WAKE_TYPE - esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE); + esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE); #else #if SOC_PM_SUPPORT_EXT_WAKEUP #ifdef CONFIG_IDF_TARGET_ESP32 @@ -240,7 +240,7 @@ void cpuDeepSleep(uint32_t msecToWake) #endif #endif -#endif //#end ESP32S3_WAKE_TYPE +#endif // #end ESP32S3_WAKE_TYPE #endif // We want RTC peripherals to stay on diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index b27f9df0c..1405ea4f3 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -198,15 +198,13 @@ void NRF52Bluetooth::shutdown() { // Shutdown bluetooth for minimum power draw LOG_INFO("Disable NRF52 bluetooth\n"); - uint8_t connection_num=Bluefruit.connected(); - if(connection_num) - { - for(uint8_t i=0;i 100ms will reset the L76K +// #define PIN_GPS_RESET (32 + 6) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K #define GPS_RESET_MODE LOW #define PIN_GPS_EN (21) #define GPS_EN_ACTIVE HIGH From 464f270b12e31d5ec3545ca9033a38ab2c4d3a0e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 13 Aug 2024 06:56:20 -0500 Subject: [PATCH 107/305] More explicit guards for attempting to set RTC (#4452) * Guard against timesources from the mesh if we have good time * Trunk * Consider phone time in the past 24 hours authoritative as well * Rename * GPS can be null * Declaration * Remove RemoteHardware * Explicitly remove GPS * Exclude GPS earlier for RAK2560 --- arch/stm32/stm32.ini | 3 ++- src/gps/RTC.cpp | 4 ++++ src/gps/RTC.h | 2 ++ src/modules/PositionModule.cpp | 13 ++++++++++++- src/modules/PositionModule.h | 1 + variants/rak2560/platformio.ini | 1 + variants/rak2560/variant.h | 2 +- 7 files changed, 23 insertions(+), 3 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index ffbc51680..050dbf7f0 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -12,6 +12,7 @@ build_flags = -flto -Isrc/platform/stm32wl -g -DMESHTASTIC_MINIMIZE_BUILD + -DMESHTASTIC_EXCLUDE_GPS -DDEBUG_MUTE ; -DVECT_TAB_OFFSET=0x08000000 -DconfigUSE_CMSIS_RTOS_V2=1 @@ -21,7 +22,7 @@ build_flags = -fdata-sections build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 710baca98..070038672 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -6,6 +6,7 @@ #include static RTCQuality currentQuality = RTCQualityNone; +uint32_t lastSetFromPhoneNtpOrGps = 0; RTCQuality getRTCQuality() { @@ -121,6 +122,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) if (shouldSet) { currentQuality = q; lastSetMsec = now; + if (currentQuality >= RTCQualityNTP) { + lastSetFromPhoneNtpOrGps = now; + } // This delta value works on all platforms timeStartMsec = now; diff --git a/src/gps/RTC.h b/src/gps/RTC.h index d31e85433..caa48dc06 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -24,6 +24,8 @@ enum RTCQuality { RTCQuality getRTCQuality(); +extern uint32_t lastSetFromPhoneNtpOrGps; + /// If we haven't yet set our RTC this boot, set it from a GPS derived time bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false); bool perhapsSetRTC(RTCQuality q, struct tm &t); diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 5da918049..1618ba3ed 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -90,7 +90,6 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes // will always be an equivalent or lesser RTCQuality (RTCQualityNTP or RTCQualityNet). force = true; #endif - // Set from phone RTC Quality to RTCQualityNTP since it should be approximately so trySetRtc(p, isLocal, force); } @@ -126,14 +125,26 @@ void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate) { + if (hasQualityTimesource() && !isLocal) { + LOG_DEBUG("Ignoring time from mesh because we have a GPS, RTC, or Phone/NTP time source in the past day\n"); + return; + } struct timeval tv; uint32_t secs = p.time; tv.tv_sec = secs; tv.tv_usec = 0; + perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv, forceUpdate); } +bool PositionModule::hasQualityTimesource() +{ + bool setFromPhoneOrNtpToday = (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); + bool hasGpsOrRtc = (gps && gps->isConnected()) || (rtc_found.address != ScanI2C::ADDRESS_NONE.address); + return hasGpsOrRtc || setFromPhoneOrNtpToday; +} + meshtastic_MeshPacket *PositionModule::allocReply() { if (precision == 0) { diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index a5277aff6..c4ef66501 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -60,6 +60,7 @@ class PositionModule : public ProtobufModule, private concu void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false); uint32_t precision; void sendLostAndFoundText(); + bool hasQualityTimesource(); const uint32_t minimumTimeThreshold = Default::getConfiguredOrDefaultMsScaled(config.position.broadcast_smart_minimum_interval_secs, 30, numOnlineNodes); diff --git a/variants/rak2560/platformio.ini b/variants/rak2560/platformio.ini index 96c1bfa92..93c7acf71 100644 --- a/variants/rak2560/platformio.ini +++ b/variants/rak2560/platformio.ini @@ -6,6 +6,7 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -Ivariants/rak2560 -D RAK_4631 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DMESHTASTIC_EXCLUDE_GPS=1 -DHAS_RAKPROT=1 ; Define if RAk OneWireSerial is used (disables GPS) build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak2560> + + + lib_deps = diff --git a/variants/rak2560/variant.h b/variants/rak2560/variant.h index 0c1c9aed9..8e5d90553 100644 --- a/variants/rak2560/variant.h +++ b/variants/rak2560/variant.h @@ -227,7 +227,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG // #define GPS_TX_PIN PIN_SERIAL2_TX // #define PIN_GPS_EN PIN_3V3_EN // Disable GPS -#define MESHTASTIC_EXCLUDE_GPS 1 +// #define MESHTASTIC_EXCLUDE_GPS 1 // Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press // RAK12002 RTC Module From 8d1a34a4bf074aa3a5302d2105d128279f9a4fb1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 07:25:05 -0500 Subject: [PATCH 108/305] Protobufs --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 +- src/mesh/generated/meshtastic/config.pb.cpp | 3 + src/mesh/generated/meshtastic/config.pb.h | 83 +++++++++-- src/mesh/generated/meshtastic/deviceonly.pb.h | 4 +- src/mesh/generated/meshtastic/localonly.pb.h | 14 +- src/mesh/generated/meshtastic/mesh.pb.cpp | 5 +- src/mesh/generated/meshtastic/mesh.pb.h | 110 +++++++++++--- src/mesh/generated/meshtastic/telemetry.pb.h | 136 +++++++++++------- 9 files changed, 273 insertions(+), 96 deletions(-) diff --git a/protobufs b/protobufs index c9ca0dbe9..126293c38 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c9ca0dbe9cc7105399e0486c07e0601f849b94af +Subproject commit 126293c38ff974ca253606c36da48cfab7fe6446 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bef2abf9b..d0e643dff 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,8 +168,6 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; - /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ - int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -180,8 +178,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ - int32_t factory_reset_config; + /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ + int32_t factory_reset; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -256,12 +254,11 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 -#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_config_tag 99 +#define meshtastic_AdminMessage_factory_reset_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -301,12 +298,11 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp index bb82198c0..c6274aed4 100644 --- a/src/mesh/generated/meshtastic/config.pb.cpp +++ b/src/mesh/generated/meshtastic/config.pb.cpp @@ -33,6 +33,9 @@ PB_BIND(meshtastic_Config_LoRaConfig, meshtastic_Config_LoRaConfig, 2) PB_BIND(meshtastic_Config_BluetoothConfig, meshtastic_Config_BluetoothConfig, AUTO) +PB_BIND(meshtastic_Config_SecurityConfig, meshtastic_Config_SecurityConfig, AUTO) + + diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 44a86f4d6..dbb0deb00 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -248,7 +248,8 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0, /* Long Range - Slow */ meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, - /* Very Long Range - Slow */ + /* Very Long Range - Slow + Deprecated in 2.5: Works only with txco and is unusably slow */ meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW = 2, /* Medium Range - Slow */ meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW = 3, @@ -259,7 +260,11 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { /* Short Range - Fast */ meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST = 6, /* Long Range - Moderately Fast */ - meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7 + meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7, + /* Short Range - Turbo + This is the fastest preset and the only one with 500kHz bandwidth. + It is not legal to use in all regions due to this wider bandwidth. */ + meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8 } meshtastic_Config_LoRaConfig_ModemPreset; typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { @@ -276,10 +281,12 @@ typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { typedef struct _meshtastic_Config_DeviceConfig { /* Sets the role of node */ meshtastic_Config_DeviceConfig_Role role; - /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI */ + /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI + Moved to SecurityConfig */ bool serial_enabled; /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Set this to true to leave the debug log outputting even when API is active. */ + Set this to true to leave the debug log outputting even when API is active. + Moved to SecurityConfig */ bool debug_log_enabled; /* For boards without a hard wired button, this is the pin number that will be used Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */ @@ -295,7 +302,8 @@ typedef struct _meshtastic_Config_DeviceConfig { /* Treat double tap interrupt on supported accelerometers as a button press if set to true */ bool double_tap_as_button_press; /* If true, device is considered to be "managed" by a mesh administrator - Clients should then limit available configuration and administrative options inside the user interface */ + Clients should then limit available configuration and administrative options inside the user interface + Moved to SecurityConfig */ bool is_managed; /* Disables the triple-press of user button to enable or disable GPS */ bool disable_triple_click; @@ -515,10 +523,37 @@ typedef struct _meshtastic_Config_BluetoothConfig { meshtastic_Config_BluetoothConfig_PairingMode mode; /* Specified PIN for PairingMode.FixedPin */ uint32_t fixed_pin; - /* Enables device (serial style logs) over Bluetooth */ + /* Enables device (serial style logs) over Bluetooth + Moved to SecurityConfig */ bool device_logging_enabled; } meshtastic_Config_BluetoothConfig; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_public_key_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_private_key_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_admin_key_t; +typedef struct _meshtastic_Config_SecurityConfig { + /* The public key of the user's device. + Sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_Config_SecurityConfig_public_key_t public_key; + /* The private key of the device. + Used to create a shared key with a remote device. */ + meshtastic_Config_SecurityConfig_private_key_t private_key; + /* The public key authorized to send admin messages to this node. */ + meshtastic_Config_SecurityConfig_admin_key_t admin_key; + /* If true, device is considered to be "managed" by a mesh administrator via admin messages + Device is managed by a mesh administrator. */ + bool is_managed; + /* Serial Console over the Stream API." */ + bool serial_enabled; + /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). + Output live debug logging over serial. */ + bool debug_log_api_enabled; + /* Enables device (serial style logs) over Bluetooth */ + bool bluetooth_logging_enabled; + /* Allow incoming device control over the insecure legacy admin channel. */ + bool admin_channel_enabled; +} meshtastic_Config_SecurityConfig; + typedef struct _meshtastic_Config { pb_size_t which_payload_variant; union { @@ -529,6 +564,7 @@ typedef struct _meshtastic_Config { meshtastic_Config_DisplayConfig display; meshtastic_Config_LoRaConfig lora; meshtastic_Config_BluetoothConfig bluetooth; + meshtastic_Config_SecurityConfig security; } payload_variant; } meshtastic_Config; @@ -583,8 +619,8 @@ extern "C" { #define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1)) #define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST -#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE -#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE+1)) +#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO +#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1)) #define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN #define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN @@ -612,6 +648,7 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_mode_ENUMTYPE meshtastic_Config_BluetoothConfig_PairingMode + /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} #define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -622,6 +659,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} @@ -631,6 +669,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Config_DeviceConfig_role_tag 1 @@ -711,6 +750,14 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_mode_tag 2 #define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3 #define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4 +#define meshtastic_Config_SecurityConfig_public_key_tag 1 +#define meshtastic_Config_SecurityConfig_private_key_tag 2 +#define meshtastic_Config_SecurityConfig_admin_key_tag 3 +#define meshtastic_Config_SecurityConfig_is_managed_tag 4 +#define meshtastic_Config_SecurityConfig_serial_enabled_tag 5 +#define meshtastic_Config_SecurityConfig_debug_log_api_enabled_tag 6 +#define meshtastic_Config_SecurityConfig_bluetooth_logging_enabled_tag 7 +#define meshtastic_Config_SecurityConfig_admin_channel_enabled_tag 8 #define meshtastic_Config_device_tag 1 #define meshtastic_Config_position_tag 2 #define meshtastic_Config_power_tag 3 @@ -718,6 +765,7 @@ extern "C" { #define meshtastic_Config_display_tag 5 #define meshtastic_Config_lora_tag 6 #define meshtastic_Config_bluetooth_tag 7 +#define meshtastic_Config_security_tag 8 /* Struct field encoding specification for nanopb */ #define meshtastic_Config_FIELDLIST(X, a) \ @@ -727,7 +775,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,power,payload_variant.power) X(a, STATIC, ONEOF, MESSAGE, (payload_variant,network,payload_variant.network), 4) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.display), 5) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) #define meshtastic_Config_CALLBACK NULL #define meshtastic_Config_DEFAULT NULL #define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -737,6 +786,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bl #define meshtastic_Config_payload_variant_display_MSGTYPE meshtastic_Config_DisplayConfig #define meshtastic_Config_payload_variant_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig +#define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ @@ -849,6 +899,18 @@ X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4) #define meshtastic_Config_BluetoothConfig_CALLBACK NULL #define meshtastic_Config_BluetoothConfig_DEFAULT NULL +#define meshtastic_Config_SecurityConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, private_key, 2) \ +X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ +X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ +X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ +X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ +X(a, STATIC, SINGULAR, BOOL, bluetooth_logging_enabled, 7) \ +X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8) +#define meshtastic_Config_SecurityConfig_CALLBACK NULL +#define meshtastic_Config_SecurityConfig_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_Config_msg; extern const pb_msgdesc_t meshtastic_Config_DeviceConfig_msg; extern const pb_msgdesc_t meshtastic_Config_PositionConfig_msg; @@ -858,6 +920,7 @@ extern const pb_msgdesc_t meshtastic_Config_NetworkConfig_IpV4Config_msg; extern const pb_msgdesc_t meshtastic_Config_DisplayConfig_msg; extern const pb_msgdesc_t meshtastic_Config_LoRaConfig_msg; extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; +extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_Config_fields &meshtastic_Config_msg @@ -869,6 +932,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; #define meshtastic_Config_DisplayConfig_fields &meshtastic_Config_DisplayConfig_msg #define meshtastic_Config_LoRaConfig_fields &meshtastic_Config_LoRaConfig_msg #define meshtastic_Config_BluetoothConfig_fields &meshtastic_Config_BluetoothConfig_msg +#define meshtastic_Config_SecurityConfig_fields &meshtastic_Config_SecurityConfig_msg /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size @@ -880,6 +944,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 +#define meshtastic_Config_SecurityConfig_size 112 #define meshtastic_Config_size 199 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index eb37f4f95..2c91fe30e 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -306,8 +306,8 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* meshtastic_DeviceState_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 166 -#define meshtastic_OEMStore_size 3388 +#define meshtastic_NodeInfoLite_size 200 +#define meshtastic_OEMStore_size 3502 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 983f48ad3..c612b24ab 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -38,6 +38,9 @@ typedef struct _meshtastic_LocalConfig { incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. */ uint32_t version; + /* The part of the config that is specific to Security settings */ + bool has_security; + meshtastic_Config_SecurityConfig security; } meshtastic_LocalConfig; typedef struct _meshtastic_LocalModuleConfig { @@ -92,9 +95,9 @@ extern "C" { #endif /* Initializer values for message structs */ -#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0} +#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default} #define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default} -#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0} +#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero} #define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero} /* Field tags (for use in manual encoding/decoding) */ @@ -106,6 +109,7 @@ extern "C" { #define meshtastic_LocalConfig_lora_tag 6 #define meshtastic_LocalConfig_bluetooth_tag 7 #define meshtastic_LocalConfig_version_tag 8 +#define meshtastic_LocalConfig_security_tag 9 #define meshtastic_LocalModuleConfig_mqtt_tag 1 #define meshtastic_LocalModuleConfig_serial_tag 2 #define meshtastic_LocalModuleConfig_external_notification_tag 3 @@ -130,7 +134,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, network, 4) \ X(a, STATIC, OPTIONAL, MESSAGE, display, 5) \ X(a, STATIC, OPTIONAL, MESSAGE, lora, 6) \ X(a, STATIC, OPTIONAL, MESSAGE, bluetooth, 7) \ -X(a, STATIC, SINGULAR, UINT32, version, 8) +X(a, STATIC, SINGULAR, UINT32, version, 8) \ +X(a, STATIC, OPTIONAL, MESSAGE, security, 9) #define meshtastic_LocalConfig_CALLBACK NULL #define meshtastic_LocalConfig_DEFAULT NULL #define meshtastic_LocalConfig_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -140,6 +145,7 @@ X(a, STATIC, SINGULAR, UINT32, version, 8) #define meshtastic_LocalConfig_display_MSGTYPE meshtastic_Config_DisplayConfig #define meshtastic_LocalConfig_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_LocalConfig_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig +#define meshtastic_LocalConfig_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_LocalModuleConfig_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, mqtt, 1) \ @@ -181,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 555 +#define meshtastic_LocalConfig_size 669 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp index 3fa81e131..8c8b9ded7 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.cpp +++ b/src/mesh/generated/meshtastic/mesh.pb.cpp @@ -30,7 +30,7 @@ PB_BIND(meshtastic_MqttClientProxyMessage, meshtastic_MqttClientProxyMessage, 2) PB_BIND(meshtastic_MeshPacket, meshtastic_MeshPacket, 2) -PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO) +PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, 2) PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO) @@ -45,6 +45,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO) PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2) +PB_BIND(meshtastic_ClientNotification, meshtastic_ClientNotification, 2) + + PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO) diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 955484cb0..70423f673 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -374,10 +374,13 @@ typedef enum _meshtastic_LogRecord_Level { typedef struct _meshtastic_Position { /* The new preferred location encoding, multiply by 1e-7 to get degrees in floating point */ + bool has_latitude_i; int32_t latitude_i; /* TODO: REPLACE */ + bool has_longitude_i; int32_t longitude_i; /* In meters above MSL (but see issue #359) */ + bool has_altitude; int32_t altitude; /* This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its time if it is sent over @@ -393,8 +396,10 @@ typedef struct _meshtastic_Position { /* Pos. timestamp milliseconds adjustment (rarely available or required) */ int32_t timestamp_millis_adjust; /* HAE altitude in meters - can be used instead of MSL altitude */ + bool has_altitude_hae; int32_t altitude_hae; /* Geoidal separation in meters */ + bool has_altitude_geoidal_separation; int32_t altitude_geoidal_separation; /* Horizontal, Vertical and Position Dilution of Precision, in 1/100 units - PDOP is sufficient for most cases @@ -416,8 +421,10 @@ typedef struct _meshtastic_Position { - "heading" is where the fuselage points (measured in horizontal plane) - "yaw" indicates a relative rotation about the vertical axis TODO: REMOVE/INTEGRATE */ + bool has_ground_speed; uint32_t ground_speed; /* TODO: REPLACE */ + bool has_ground_track; uint32_t ground_track; /* GPS fix quality (from NMEA GxGGA statement or similar) */ uint32_t fix_quality; @@ -439,6 +446,7 @@ typedef struct _meshtastic_Position { uint32_t precision_bits; } meshtastic_Position; +typedef PB_BYTES_ARRAY_T(32) meshtastic_User_public_key_t; /* Broadcast when a newly powered mesh node wants to find a node num it can use Sent from the phone over bluetooth to set the user id for the owner of this node. Also sent from nodes to each other when a new node signs on (so all clients can have this info) @@ -485,6 +493,9 @@ typedef struct _meshtastic_User { bool is_licensed; /* Indicates that the user's role in the mesh */ meshtastic_Config_DeviceConfig_Role role; + /* The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_User_public_key_t public_key; } meshtastic_User; /* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */ @@ -546,8 +557,10 @@ typedef struct _meshtastic_Waypoint { /* Id of the waypoint */ uint32_t id; /* latitude_i */ + bool has_latitude_i; int32_t latitude_i; /* longitude_i */ + bool has_longitude_i; int32_t longitude_i; /* Time the waypoint is to expire (epoch) */ uint32_t expire; @@ -579,6 +592,7 @@ typedef struct _meshtastic_MqttClientProxyMessage { } meshtastic_MqttClientProxyMessage; typedef PB_BYTES_ARRAY_T(256) meshtastic_MeshPacket_encrypted_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_MeshPacket_public_key_t; /* A packet envelope sent/received over the mesh only payload_variant is sent in the payload portion of the LORA packet. The other fields are either not sent at all, or sent in the special 16 byte LORA header. */ @@ -649,6 +663,10 @@ typedef struct _meshtastic_MeshPacket { /* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */ uint8_t hop_start; + /* Records the public key the packet was encrypted with, if applicable. */ + meshtastic_MeshPacket_public_key_t public_key; + /* Indicates whether the packet was en/decrypted using PKI */ + bool pki_encrypted; } meshtastic_MeshPacket; /* The bluetooth to device link: @@ -738,6 +756,22 @@ typedef struct _meshtastic_QueueStatus { uint32_t mesh_packet_id; } meshtastic_QueueStatus; +/* A notification message from the device to the client + To be used for important messages that should to be displayed to the user + in the form of push notifications or validation messages when saving + invalid configuration. */ +typedef struct _meshtastic_ClientNotification { + /* The id of the packet we're notifying in response to */ + bool has_reply_id; + uint32_t reply_id; + /* Seconds since 1970 - or 0 for unknown/unset */ + uint32_t time; + /* The level type of notification */ + meshtastic_LogRecord_Level level; + /* The message body of the notification */ + char message[400]; +} meshtastic_ClientNotification; + /* Individual File info for the device */ typedef struct _meshtastic_FileInfo { /* The fully qualified path of the file */ @@ -852,6 +886,8 @@ typedef struct _meshtastic_FromRadio { meshtastic_MqttClientProxyMessage mqttClientProxyMessage; /* File system manifest messages */ meshtastic_FileInfo fileInfo; + /* Notification message to the client */ + meshtastic_ClientNotification clientNotification; }; } meshtastic_FromRadio; @@ -994,6 +1030,8 @@ extern "C" { +#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level + #define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum @@ -1010,19 +1048,20 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN} +#define meshtastic_Position_init_default {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}} #define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} -#define meshtastic_Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0} +#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0} +#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} #define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} #define meshtastic_MyNodeInfo_init_default {0, 0, 0} #define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_default {0, 0, 0, 0} #define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}} +#define meshtastic_ClientNotification_init_default {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""} #define meshtastic_FileInfo_init_default {"", 0} #define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}} #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} @@ -1034,19 +1073,20 @@ extern "C" { #define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}} #define meshtastic_resend_chunks_init_default {{{NULL}, NULL}} #define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}} -#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN} +#define meshtastic_Position_init_zero {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}} #define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} -#define meshtastic_Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0} +#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0} +#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} #define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} #define meshtastic_MyNodeInfo_init_zero {0, 0, 0} #define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_zero {0, 0, 0, 0} #define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}} +#define meshtastic_ClientNotification_init_zero {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""} #define meshtastic_FileInfo_init_zero {"", 0} #define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} @@ -1090,6 +1130,7 @@ extern "C" { #define meshtastic_User_hw_model_tag 5 #define meshtastic_User_is_licensed_tag 6 #define meshtastic_User_role_tag 7 +#define meshtastic_User_public_key_tag 8 #define meshtastic_RouteDiscovery_route_tag 1 #define meshtastic_Routing_route_request_tag 1 #define meshtastic_Routing_route_reply_tag 2 @@ -1129,6 +1170,8 @@ extern "C" { #define meshtastic_MeshPacket_delayed_tag 13 #define meshtastic_MeshPacket_via_mqtt_tag 14 #define meshtastic_MeshPacket_hop_start_tag 15 +#define meshtastic_MeshPacket_public_key_tag 16 +#define meshtastic_MeshPacket_pki_encrypted_tag 17 #define meshtastic_NodeInfo_num_tag 1 #define meshtastic_NodeInfo_user_tag 2 #define meshtastic_NodeInfo_position_tag 3 @@ -1150,6 +1193,10 @@ extern "C" { #define meshtastic_QueueStatus_free_tag 2 #define meshtastic_QueueStatus_maxlen_tag 3 #define meshtastic_QueueStatus_mesh_packet_id_tag 4 +#define meshtastic_ClientNotification_reply_id_tag 1 +#define meshtastic_ClientNotification_time_tag 2 +#define meshtastic_ClientNotification_level_tag 3 +#define meshtastic_ClientNotification_message_tag 4 #define meshtastic_FileInfo_file_name_tag 1 #define meshtastic_FileInfo_size_bytes_tag 2 #define meshtastic_Compressed_portnum_tag 1 @@ -1187,6 +1234,7 @@ extern "C" { #define meshtastic_FromRadio_metadata_tag 13 #define meshtastic_FromRadio_mqttClientProxyMessage_tag 14 #define meshtastic_FromRadio_fileInfo_tag 15 +#define meshtastic_FromRadio_clientNotification_tag 16 #define meshtastic_ToRadio_packet_tag 1 #define meshtastic_ToRadio_want_config_id_tag 3 #define meshtastic_ToRadio_disconnect_tag 4 @@ -1207,22 +1255,22 @@ extern "C" { /* Struct field encoding specification for nanopb */ #define meshtastic_Position_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \ -X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \ -X(a, STATIC, SINGULAR, INT32, altitude, 3) \ +X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 1) \ +X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 2) \ +X(a, STATIC, OPTIONAL, INT32, altitude, 3) \ X(a, STATIC, SINGULAR, FIXED32, time, 4) \ X(a, STATIC, SINGULAR, UENUM, location_source, 5) \ X(a, STATIC, SINGULAR, UENUM, altitude_source, 6) \ X(a, STATIC, SINGULAR, FIXED32, timestamp, 7) \ X(a, STATIC, SINGULAR, INT32, timestamp_millis_adjust, 8) \ -X(a, STATIC, SINGULAR, SINT32, altitude_hae, 9) \ -X(a, STATIC, SINGULAR, SINT32, altitude_geoidal_separation, 10) \ +X(a, STATIC, OPTIONAL, SINT32, altitude_hae, 9) \ +X(a, STATIC, OPTIONAL, SINT32, altitude_geoidal_separation, 10) \ X(a, STATIC, SINGULAR, UINT32, PDOP, 11) \ X(a, STATIC, SINGULAR, UINT32, HDOP, 12) \ X(a, STATIC, SINGULAR, UINT32, VDOP, 13) \ X(a, STATIC, SINGULAR, UINT32, gps_accuracy, 14) \ -X(a, STATIC, SINGULAR, UINT32, ground_speed, 15) \ -X(a, STATIC, SINGULAR, UINT32, ground_track, 16) \ +X(a, STATIC, OPTIONAL, UINT32, ground_speed, 15) \ +X(a, STATIC, OPTIONAL, UINT32, ground_track, 16) \ X(a, STATIC, SINGULAR, UINT32, fix_quality, 17) \ X(a, STATIC, SINGULAR, UINT32, fix_type, 18) \ X(a, STATIC, SINGULAR, UINT32, sats_in_view, 19) \ @@ -1240,7 +1288,8 @@ X(a, STATIC, SINGULAR, STRING, short_name, 3) \ X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \ X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \ X(a, STATIC, SINGULAR, BOOL, is_licensed, 6) \ -X(a, STATIC, SINGULAR, UENUM, role, 7) +X(a, STATIC, SINGULAR, UENUM, role, 7) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 8) #define meshtastic_User_CALLBACK NULL #define meshtastic_User_DEFAULT NULL @@ -1272,8 +1321,8 @@ X(a, STATIC, SINGULAR, FIXED32, emoji, 8) #define meshtastic_Waypoint_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, id, 1) \ -X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 2) \ -X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \ +X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \ +X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 3) \ X(a, STATIC, SINGULAR, UINT32, expire, 4) \ X(a, STATIC, SINGULAR, UINT32, locked_to, 5) \ X(a, STATIC, SINGULAR, STRING, name, 6) \ @@ -1305,7 +1354,9 @@ X(a, STATIC, SINGULAR, UENUM, priority, 11) \ X(a, STATIC, SINGULAR, INT32, rx_rssi, 12) \ X(a, STATIC, SINGULAR, UENUM, delayed, 13) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14) \ -X(a, STATIC, SINGULAR, UINT32, hop_start, 15) +X(a, STATIC, SINGULAR, UINT32, hop_start, 15) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 16) \ +X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) #define meshtastic_MeshPacket_CALLBACK NULL #define meshtastic_MeshPacket_DEFAULT NULL #define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data @@ -1365,7 +1416,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 1 X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,clientNotification,clientNotification), 16) #define meshtastic_FromRadio_CALLBACK NULL #define meshtastic_FromRadio_DEFAULT NULL #define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket @@ -1380,6 +1432,15 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) #define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata #define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage #define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo +#define meshtastic_FromRadio_payload_variant_clientNotification_MSGTYPE meshtastic_ClientNotification + +#define meshtastic_ClientNotification_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, UINT32, reply_id, 1) \ +X(a, STATIC, SINGULAR, FIXED32, time, 2) \ +X(a, STATIC, SINGULAR, UENUM, level, 3) \ +X(a, STATIC, SINGULAR, STRING, message, 4) +#define meshtastic_ClientNotification_CALLBACK NULL +#define meshtastic_ClientNotification_DEFAULT NULL #define meshtastic_FileInfo_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, STRING, file_name, 1) \ @@ -1485,6 +1546,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg; extern const pb_msgdesc_t meshtastic_LogRecord_msg; extern const pb_msgdesc_t meshtastic_QueueStatus_msg; extern const pb_msgdesc_t meshtastic_FromRadio_msg; +extern const pb_msgdesc_t meshtastic_ClientNotification_msg; extern const pb_msgdesc_t meshtastic_FileInfo_msg; extern const pb_msgdesc_t meshtastic_ToRadio_msg; extern const pb_msgdesc_t meshtastic_Compressed_msg; @@ -1511,6 +1573,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg #define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg #define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg +#define meshtastic_ClientNotification_fields &meshtastic_ClientNotification_msg #define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg #define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg #define meshtastic_Compressed_fields &meshtastic_Compressed_msg @@ -1528,6 +1591,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; /* meshtastic_ChunkedPayloadResponse_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size #define meshtastic_ChunkedPayload_size 245 +#define meshtastic_ClientNotification_size 415 #define meshtastic_Compressed_size 243 #define meshtastic_Data_size 270 #define meshtastic_DeviceMetadata_size 46 @@ -1535,19 +1599,19 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_FromRadio_size 510 #define meshtastic_Heartbeat_size 0 #define meshtastic_LogRecord_size 426 -#define meshtastic_MeshPacket_size 326 +#define meshtastic_MeshPacket_size 364 #define meshtastic_MqttClientProxyMessage_size 501 #define meshtastic_MyNodeInfo_size 18 #define meshtastic_NeighborInfo_size 258 #define meshtastic_Neighbor_size 22 -#define meshtastic_NodeInfo_size 283 +#define meshtastic_NodeInfo_size 317 #define meshtastic_NodeRemoteHardwarePin_size 29 #define meshtastic_Position_size 144 #define meshtastic_QueueStatus_size 23 #define meshtastic_RouteDiscovery_size 40 #define meshtastic_Routing_size 42 #define meshtastic_ToRadio_size 504 -#define meshtastic_User_size 79 +#define meshtastic_User_size 113 #define meshtastic_Waypoint_size 165 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index a4acd3f4a..60681916f 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -76,98 +76,138 @@ typedef enum _meshtastic_TelemetrySensorType { /* Key native device metrics such as battery level */ typedef struct _meshtastic_DeviceMetrics { /* 0-100 (>100 means powered) */ + bool has_battery_level; uint32_t battery_level; /* Voltage measured */ + bool has_voltage; float voltage; /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */ + bool has_channel_utilization; float channel_utilization; /* Percent of airtime for transmission used within the last hour. */ + bool has_air_util_tx; float air_util_tx; /* How long the device has been running since the last reboot (in seconds) */ + bool has_uptime_seconds; uint32_t uptime_seconds; } meshtastic_DeviceMetrics; /* Weather station or other environmental metrics */ typedef struct _meshtastic_EnvironmentMetrics { /* Temperature measured */ + bool has_temperature; float temperature; /* Relative humidity percent measured */ + bool has_relative_humidity; float relative_humidity; /* Barometric pressure in hPA measured */ + bool has_barometric_pressure; float barometric_pressure; /* Gas resistance in MOhm measured */ + bool has_gas_resistance; float gas_resistance; /* Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */ + bool has_voltage; float voltage; /* Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */ + bool has_current; float current; /* relative scale IAQ value as measured by Bosch BME680 . value 0-500. Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */ + bool has_iaq; uint16_t iaq; /* RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. */ + bool has_distance; float distance; /* VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. */ + bool has_lux; float lux; /* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */ + bool has_white_lux; float white_lux; /* Infrared lux */ + bool has_ir_lux; float ir_lux; /* Ultraviolet lux */ + bool has_uv_lux; float uv_lux; /* Wind direction in degrees 0 degrees = North, 90 = East, etc... */ + bool has_wind_direction; uint16_t wind_direction; /* Wind speed in m/s */ + bool has_wind_speed; float wind_speed; /* Weight in KG */ + bool has_weight; float weight; /* Wind gust in m/s */ + bool has_wind_gust; float wind_gust; /* Wind lull in m/s */ + bool has_wind_lull; float wind_lull; } meshtastic_EnvironmentMetrics; /* Power Metrics (voltage / current / etc) */ typedef struct _meshtastic_PowerMetrics { /* Voltage (Ch1) */ + bool has_ch1_voltage; float ch1_voltage; /* Current (Ch1) */ + bool has_ch1_current; float ch1_current; /* Voltage (Ch2) */ + bool has_ch2_voltage; float ch2_voltage; /* Current (Ch2) */ + bool has_ch2_current; float ch2_current; /* Voltage (Ch3) */ + bool has_ch3_voltage; float ch3_voltage; /* Current (Ch3) */ + bool has_ch3_current; float ch3_current; } meshtastic_PowerMetrics; /* Air quality metrics */ typedef struct _meshtastic_AirQualityMetrics { /* Concentration Units Standard PM1.0 */ + bool has_pm10_standard; uint32_t pm10_standard; /* Concentration Units Standard PM2.5 */ + bool has_pm25_standard; uint32_t pm25_standard; /* Concentration Units Standard PM10.0 */ + bool has_pm100_standard; uint32_t pm100_standard; /* Concentration Units Environmental PM1.0 */ + bool has_pm10_environmental; uint32_t pm10_environmental; /* Concentration Units Environmental PM2.5 */ + bool has_pm25_environmental; uint32_t pm25_environmental; /* Concentration Units Environmental PM10.0 */ + bool has_pm100_environmental; uint32_t pm100_environmental; /* 0.3um Particle Count */ + bool has_particles_03um; uint32_t particles_03um; /* 0.5um Particle Count */ + bool has_particles_05um; uint32_t particles_05um; /* 1.0um Particle Count */ + bool has_particles_10um; uint32_t particles_10um; /* 2.5um Particle Count */ + bool has_particles_25um; uint32_t particles_25um; /* 5.0um Particle Count */ + bool has_particles_50um; uint32_t particles_50um; /* 10.0um Particle Count */ + bool has_particles_100um; uint32_t particles_100um; } meshtastic_AirQualityMetrics; @@ -214,16 +254,16 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0} -#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} -#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0} -#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Nau7802Config_init_zero {0, 0} @@ -278,58 +318,58 @@ extern "C" { /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, battery_level, 1) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 2) \ -X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 3) \ -X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 4) \ -X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 5) +X(a, STATIC, OPTIONAL, UINT32, battery_level, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, voltage, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, channel_utilization, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, air_util_tx, 4) \ +X(a, STATIC, OPTIONAL, UINT32, uptime_seconds, 5) #define meshtastic_DeviceMetrics_CALLBACK NULL #define meshtastic_DeviceMetrics_DEFAULT NULL #define meshtastic_EnvironmentMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \ -X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \ -X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \ -X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \ -X(a, STATIC, SINGULAR, FLOAT, current, 6) \ -X(a, STATIC, SINGULAR, UINT32, iaq, 7) \ -X(a, STATIC, SINGULAR, FLOAT, distance, 8) \ -X(a, STATIC, SINGULAR, FLOAT, lux, 9) \ -X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \ -X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \ -X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \ -X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \ -X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \ -X(a, STATIC, SINGULAR, FLOAT, weight, 15) \ -X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \ -X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17) +X(a, STATIC, OPTIONAL, FLOAT, temperature, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, relative_humidity, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, barometric_pressure, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, gas_resistance, 4) \ +X(a, STATIC, OPTIONAL, FLOAT, voltage, 5) \ +X(a, STATIC, OPTIONAL, FLOAT, current, 6) \ +X(a, STATIC, OPTIONAL, UINT32, iaq, 7) \ +X(a, STATIC, OPTIONAL, FLOAT, distance, 8) \ +X(a, STATIC, OPTIONAL, FLOAT, lux, 9) \ +X(a, STATIC, OPTIONAL, FLOAT, white_lux, 10) \ +X(a, STATIC, OPTIONAL, FLOAT, ir_lux, 11) \ +X(a, STATIC, OPTIONAL, FLOAT, uv_lux, 12) \ +X(a, STATIC, OPTIONAL, UINT32, wind_direction, 13) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \ +X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL #define meshtastic_PowerMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FLOAT, ch1_voltage, 1) \ -X(a, STATIC, SINGULAR, FLOAT, ch1_current, 2) \ -X(a, STATIC, SINGULAR, FLOAT, ch2_voltage, 3) \ -X(a, STATIC, SINGULAR, FLOAT, ch2_current, 4) \ -X(a, STATIC, SINGULAR, FLOAT, ch3_voltage, 5) \ -X(a, STATIC, SINGULAR, FLOAT, ch3_current, 6) +X(a, STATIC, OPTIONAL, FLOAT, ch1_voltage, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, ch1_current, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, ch2_voltage, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, ch2_current, 4) \ +X(a, STATIC, OPTIONAL, FLOAT, ch3_voltage, 5) \ +X(a, STATIC, OPTIONAL, FLOAT, ch3_current, 6) #define meshtastic_PowerMetrics_CALLBACK NULL #define meshtastic_PowerMetrics_DEFAULT NULL #define meshtastic_AirQualityMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, pm10_standard, 1) \ -X(a, STATIC, SINGULAR, UINT32, pm25_standard, 2) \ -X(a, STATIC, SINGULAR, UINT32, pm100_standard, 3) \ -X(a, STATIC, SINGULAR, UINT32, pm10_environmental, 4) \ -X(a, STATIC, SINGULAR, UINT32, pm25_environmental, 5) \ -X(a, STATIC, SINGULAR, UINT32, pm100_environmental, 6) \ -X(a, STATIC, SINGULAR, UINT32, particles_03um, 7) \ -X(a, STATIC, SINGULAR, UINT32, particles_05um, 8) \ -X(a, STATIC, SINGULAR, UINT32, particles_10um, 9) \ -X(a, STATIC, SINGULAR, UINT32, particles_25um, 10) \ -X(a, STATIC, SINGULAR, UINT32, particles_50um, 11) \ -X(a, STATIC, SINGULAR, UINT32, particles_100um, 12) +X(a, STATIC, OPTIONAL, UINT32, pm10_standard, 1) \ +X(a, STATIC, OPTIONAL, UINT32, pm25_standard, 2) \ +X(a, STATIC, OPTIONAL, UINT32, pm100_standard, 3) \ +X(a, STATIC, OPTIONAL, UINT32, pm10_environmental, 4) \ +X(a, STATIC, OPTIONAL, UINT32, pm25_environmental, 5) \ +X(a, STATIC, OPTIONAL, UINT32, pm100_environmental, 6) \ +X(a, STATIC, OPTIONAL, UINT32, particles_03um, 7) \ +X(a, STATIC, OPTIONAL, UINT32, particles_05um, 8) \ +X(a, STATIC, OPTIONAL, UINT32, particles_10um, 9) \ +X(a, STATIC, OPTIONAL, UINT32, particles_25um, 10) \ +X(a, STATIC, OPTIONAL, UINT32, particles_50um, 11) \ +X(a, STATIC, OPTIONAL, UINT32, particles_100um, 12) #define meshtastic_AirQualityMetrics_CALLBACK NULL #define meshtastic_AirQualityMetrics_DEFAULT NULL From da53b8152d8a3aa813bd0b9e1cb3093c02819a30 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 07:51:59 -0500 Subject: [PATCH 109/305] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/protobufs b/protobufs index 126293c38..778667d93 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 126293c38ff974ca253606c36da48cfab7fe6446 +Subproject commit 778667d93b9769d51c386c461456bdec4f14f433 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index d0e643dff..bef2abf9b 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,6 +168,8 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; + /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ + int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -178,8 +180,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ - int32_t factory_reset; + /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ + int32_t factory_reset_config; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -254,11 +256,12 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 +#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_tag 99 +#define meshtastic_AdminMessage_factory_reset_config_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -298,11 +301,12 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL From 95682c9095e1c36e92e846dd8c23639dfb4a6a0b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 08:33:42 -0500 Subject: [PATCH 110/305] Add ClientNotification hello world --- src/mesh/MeshService.cpp | 20 +++++++++++++++++++- src/mesh/MeshService.h | 10 ++++++++++ src/mesh/PhoneAPI.h | 3 +++ src/mesh/Router.cpp | 8 ++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 697644a4f..d05f43b01 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -48,14 +48,18 @@ static MemoryDynamic staticMqttClientProxyMes static MemoryDynamic staticQueueStatusPool; +static MemoryDynamic staticClientNotificationPool; + Allocator &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool; +Allocator &clientNotificationPool = staticClientNotificationPool; + Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE) + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) { lastQueueStatus = {0, 0, 16, 0}; } @@ -324,6 +328,20 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage fromNum++; } +void MeshService::sendClientNotification(meshtastic_ClientNotification *n) +{ + LOG_DEBUG("Sending client notification to phone\n"); + if (toPhoneClientNotificationQueue.numFree() == 0) { + LOG_WARN("ClientNotification queue is full, discarding oldest\n"); + meshtastic_ClientNotification *d = toPhoneClientNotificationQueue.dequeuePtr(0); + if (d) + releaseClientNotificationToPool(d); + } + + assert(toPhoneClientNotificationQueue.enqueue(n, 0)); + fromNum++; +} + meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode() { meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 528adb137..ea1c4e345 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -21,6 +21,7 @@ extern Allocator &queueStatusPool; extern Allocator &mqttClientProxyMessagePool; +extern Allocator &clientNotificationPool; /** * Top level app for this service. keeps the mesh, the radio config and the queue of received packets. @@ -44,6 +45,9 @@ class MeshService // keep list of MqttClientProxyMessages to be send to the client for delivery PointerQueue toPhoneMqttProxyQueue; + // keep list of ClientNotifications to be send to the client (phone) + PointerQueue toPhoneClientNotificationQueue; + // This holds the last QueueStatus send meshtastic_QueueStatus lastQueueStatus; @@ -97,6 +101,9 @@ class MeshService // Release MqttClientProxyMessage packet to pool void releaseMqttClientProxyMessageToPool(meshtastic_MqttClientProxyMessage *p) { mqttClientProxyMessagePool.release(p); } + /// Release the next ClientNotification packet to pool. + void releaseClientNotificationToPool(meshtastic_ClientNotification *p) { clientNotificationPool.release(p); } + /** * Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep @@ -134,6 +141,9 @@ class MeshService /// Send an MQTT message to the phone for client proxying void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m); + /// Send a ClientNotification to the phone + void sendClientNotification(meshtastic_ClientNotification *cn); + bool isToPhoneQueueEmpty(); ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id); diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 3c3668300..5feb1c4bf 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -66,6 +66,9 @@ class PhoneAPI // Keep MqttClientProxyMessage packet just as packetForPhone meshtastic_MqttClientProxyMessage *mqttClientProxyMessageForPhone = NULL; + // Keep ClientNotification packet just as packetForPhone + meshtastic_ClientNotification *clientNotification = NULL; + /// We temporarily keep the nodeInfo here between the call to available and getFromRadio meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 79095805d..f59d61ea2 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -2,6 +2,7 @@ #include "Channels.h" #include "CryptoEngine.h" #include "MeshRadio.h" +#include "MeshService.h" #include "NodeDB.h" #include "RTC.h" #include "configuration.h" @@ -209,6 +210,13 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) #ifdef DEBUG_PORT uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle); LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes); + meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); + cn->has_reply_id = true; + cn->reply_id = p->id; + cn->level = meshtastic_LogRecord_Level_WARNING; + cn->time = getValidTime(RTCQualityFromNet); + sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d minutes.", silentMinutes); + service->sendClientNotification(cn); #endif meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT; if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh From c451db3a3f1ac88668d86834404b89e6225f5abf Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 08:57:37 -0500 Subject: [PATCH 111/305] Get in the trunk! --- src/mesh/MeshService.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index d05f43b01..ac97d51a7 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -59,7 +59,8 @@ Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), + toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) { lastQueueStatus = {0, 0, 16, 0}; } From b726792efd1ea7ba34eeb23af22a48eda1a00ec9 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 13:45:41 -0500 Subject: [PATCH 112/305] Re-implement PKI from #1509 (#4379) * Re-implement PKI from #1509 co-authored-by: edinnen * Set the key lengnth to actually make PKI work. * Remove unused variable and initialize keys to null * move printBytes() to meshUtils * Don't reset PKI key son reboot unless needed. * Remove double encryption for PKI messages * Cleanup encrypt logic * Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now. * Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32 * Fix a crash when node is null * Don't send PKI encrypted packets while licensed * use chIndex 8 for PKI * Don't be so clever, that you corrupt incoming packets * Pass on channel 8 for now * Typo * Lock keys once non-zero * We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf. * Lighter approach to retaining known key * Attach the public key to PKI decrypted packets in device memory * Turn PKI back off for STM32 :( * Don't just memcp over a protobuf * Don't PKI encrypt nodeinfo packets * Add a bit more memory logging around nodeDB * Use the proper macro to refer to NODENUM_BROADCAST * Typo fix * Don't PKI encrypt ROUTING (naks and acks) * Adds SecurityConfig protobuf * Add admin messages over PKI * Disable PKI for the WIO-e5 * Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k * Add missed "has_security" * Add the admin_channel_enabled option * STM32 again * add missed configuration.h at the top of files * Add EXCLUDE_TZ and RTC * Enable PKI build on STM32 once again * Attempt 1 at moving PKI to aes-ccm * Fix buffers for encrypt/decrypt * Eliminate unused aes variable * Add debugging lines * Set hash to 0 for PKI * Fix debug lines so they don't print pointers. * logic fix and more debug * Rather important typo * Check for short packets before attempting decrypt * Don't forget to give cryptoEngine the keys! * Use the right scratch buffer * Cleanup * moar cleanups * Minor hardening * Remove some in-progress stuff * Turn PKI back off on STM32 * Return false * 2.5 protos * Sync up protos * Add initial cryptography test vector tests * re-add MINIMUM_SAFE_FREE_HEAP * Housekeeping and comment fixes * Add explanatory comment about weak dh25519 keys --------- Co-authored-by: Ben Meadors --- arch/esp32/esp32.ini | 1 + arch/nrf52/nrf52.ini | 1 + platformio.ini | 1 + src/RedirectablePrint.cpp | 4 +- src/SerialConsole.cpp | 4 +- src/configuration.h | 5 + src/main.cpp | 9 +- src/mesh/CryptoEngine.cpp | 170 ++++++++++++++++++++++++++++++++ src/mesh/CryptoEngine.h | 31 +++++- src/mesh/NodeDB.cpp | 50 +++++++++- src/mesh/NodeDB.h | 2 +- src/mesh/PhoneAPI.cpp | 4 + src/mesh/Router.cpp | 173 +++++++++++++++++++++------------ src/mesh/aes-ccm.cpp | 157 ++++++++++++++++++++++++++++++ src/mesh/aes-ccm.h | 10 ++ src/meshUtils.h | 4 +- src/modules/AdminModule.cpp | 38 +++++++- test/test_crypto/test_main.cpp | 50 ++++++++++ userPrefs.h | 6 ++ 19 files changed, 641 insertions(+), 79 deletions(-) create mode 100644 src/mesh/aes-ccm.cpp create mode 100644 src/mesh/aes-ccm.h create mode 100644 test/test_crypto/test_main.cpp diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 58c1302da..0dd6cbc1d 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -48,6 +48,7 @@ lib_deps = https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f + rweather/Crypto@^0.4.0 lib_ignore = segger_rtt diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index f3e7c3036..503da2aab 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -20,6 +20,7 @@ build_src_filter = lib_deps= ${arduino_base.lib_deps} + rweather/Crypto@^0.4.0 lib_ignore = BluetoothOTA \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e60f0d7b9..5ad7d60a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,6 +45,7 @@ extra_configs = variants/*/platformio.ini [env] +test_build_src = true extra_scripts = bin/platformio-custom.py ; note: we add src to our include search path so that lmic_project_config can override diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 05d349de9..02cd8b309 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -39,7 +39,7 @@ size_t RedirectablePrint::write(uint8_t c) SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); #endif - if (!config.has_lora || config.device.serial_enabled) + if (!config.has_lora || config.security.serial_enabled) dest->write(c); return 1; // We always claim one was written, rather than trusting what the @@ -180,7 +180,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) { #if !MESHTASTIC_EXCLUDE_BLUETOOTH - if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) { + if (config.security.bluetooth_logging_enabled && !pauseBluetoothLogging) { bool isBleConnected = false; #ifdef ARCH_ESP32 isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected(); diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index d25b81da7..b911e15da 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -83,7 +83,7 @@ bool SerialConsole::checkIsConnected() bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { // only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled. - if (config.has_lora && config.device.serial_enabled) { + if (config.has_lora && config.security.serial_enabled) { // Switch to protobufs for log messages usingProtobufs = true; canWrite = true; @@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) { - if (usingProtobufs && config.device.debug_log_enabled) { + if (usingProtobufs && config.security.debug_log_api_enabled) { meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset switch (logLevel[0]) { case 'D': diff --git a/src/configuration.h b/src/configuration.h index 9148f1d37..c9a5d7fb0 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -193,6 +193,10 @@ along with this program. If not, see . #define DEFAULT_SHUTDOWN_SECONDS 2 #endif +#ifndef MINIMUM_SAFE_FREE_HEAP +#define MINIMUM_SAFE_FREE_HEAP 1500 +#endif + /* Step #3: mop up with disabled values for HAS_ options not handled by the above two */ #ifndef HAS_WIFI @@ -256,6 +260,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_MQTT 1 #define MESHTASTIC_EXCLUDE_POWERMON 1 #define MESHTASTIC_EXCLUDE_I2C 1 +#define MESHTASTIC_EXCLUDE_PKI 1 #define MESHTASTIC_EXCLUDE_POWER_FSM 1 #define MESHTASTIC_EXCLUDE_TZ 1 #endif diff --git a/src/main.cpp b/src/main.cpp index 7f45905d1..b6af60d2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -227,7 +227,7 @@ void printInfo() { LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION)); } - +#ifndef PIO_UNIT_TESTING void setup() { concurrency::hasBeenSetup = true; @@ -1052,7 +1052,7 @@ void setup() powerFSMthread = new PowerFSMThread(); setCPUFast(false); // 80MHz is fine for our slow peripherals } - +#endif uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes) uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client) @@ -1075,7 +1075,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled; return deviceMetadata; } - +#ifndef PIO_UNIT_TESTING void loop() { runASAP = false; @@ -1120,4 +1120,5 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 1e44cb9b7..677667aef 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -1,6 +1,176 @@ #include "CryptoEngine.h" +#include "NodeDB.h" +#include "RadioInterface.h" #include "configuration.h" +#if !(MESHTASTIC_EXCLUDE_PKI) +#include "aes-ccm.h" +#include "meshUtils.h" +#include +#include +#include +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) +/** + * Create a public/private key pair with Curve25519. + * + * @param pubKey The destination for the public key. + * @param privKey The destination for the private key. + */ +void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) +{ + LOG_DEBUG("Generating Curve25519 key pair...\n"); + Curve25519::dh1(public_key, private_key); + memcpy(pubKey, public_key, sizeof(public_key)); + memcpy(privKey, private_key, sizeof(private_key)); +} +#endif +uint8_t shared_key[32]; +void CryptoEngine::clearKeys() +{ + memset(public_key, 0, sizeof(public_key)); + memset(private_key, 0, sizeof(private_key)); +} + +/** + * Encrypt a packet's payload using a key generated with Curve25519 and SHA256 + * for a specific node. + * + * @param bytes is updated in place + */ +bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, + uint8_t *bytesOut) +{ + uint8_t *auth; + auth = bytesOut + numBytes; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode); + if (node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node %d or their public_key not found\n", toNode); + return false; + } + if (!crypto->setDHKey(toNode)) { + return false; + } + initNonce(fromNode, packetNum); + + // Calculate the shared secret with the destination node and encrypt + printBytes("Attempting encrypt using nonce: ", nonce, 16); + printBytes("Attempting encrypt using shared_key: ", shared_key, 32); + aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); + return true; +} + +/** + * Decrypt a packet's payload using a key generated with Curve25519 and SHA256 + * for a specific node. + * + * @param bytes is updated in place + */ +bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) +{ + uint8_t *auth; // set to last 8 bytes of text? + auth = bytes + numBytes - 8; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode); + + if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node or its public key not found in database\n"); + return false; + } + + // Calculate the shared secret with the sending node and decrypt + if (!crypto->setDHKey(fromNode)) { + return false; + } + initNonce(fromNode, packetNum); + printBytes("Attempting decrypt using nonce: ", nonce, 16); + printBytes("Attempting decrypt using shared_key: ", shared_key, 32); + return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); +} + +void CryptoEngine::setPrivateKey(uint8_t *_private_key) +{ + memcpy(private_key, _private_key, 32); +} +/** + * Set the PKI key used for encrypt, decrypt. + * + * @param nodeNum the node number of the node who's public key we want to use + */ +bool CryptoEngine::setDHKey(uint32_t nodeNum) +{ + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum); + if (node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node %d or their public_key not found\n", nodeNum); + return false; + } + + uint8_t *pubKey = node->user.public_key.bytes; + uint8_t local_priv[32]; + memcpy(shared_key, pubKey, 32); + memcpy(local_priv, private_key, 32); + // Calculate the shared secret with the specified node's public key and our private key + // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. + if (!Curve25519::dh2(shared_key, local_priv)) { + LOG_WARN("Curve25519DH step 2 failed!\n"); + return false; + } + + printBytes("DH Output: ", shared_key, 32); + + /** + * D.J. Bernstein reccomends hashing the shared key. We want to do this because there are + * at least 128 bits of entropy in the 256-bit output of the DH key exchange, but we don't + * really know where. If you extract, for instance, the first 128 bits with basic truncation, + * then you don't know if you got all of your 128 entropy bits, or less, possibly much less. + * + * No exploitable bias is really known at that point, but we know enough to be wary. + * Hashing the DH output is a simple and safe way to gather all the entropy and spread + * it around as needed. + */ + crypto->hash(shared_key, 32); + return true; +} + +/** + * Hash arbitrary data using SHA256. + * + * @param bytes + * @param numBytes + */ +void CryptoEngine::hash(uint8_t *bytes, size_t numBytes) +{ + SHA256 hash; + size_t posn, len; + uint8_t size = numBytes; + uint8_t inc = 16; + hash.reset(); + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash.update(bytes + posn, len); + } + hash.finalize(bytes, 32); +} + +void CryptoEngine::aesSetKey(const uint8_t *key_bytes, size_t key_len) +{ + if (aes) { + delete aes; + aes = nullptr; + } + if (key_len != 0) { + aes = new AESSmall256(); + aes->setKey(key_bytes, key_len); + } +} + +void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) +{ + aes->encryptBlock(out, in); +} + +#endif + concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 2737dab2d..51080fd59 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -1,6 +1,8 @@ #pragma once - +#include "AES.h" #include "concurrency/LockGuard.h" +#include "configuration.h" +#include "mesh-pb-constants.h" #include extern concurrency::Lock *cryptLock; @@ -26,9 +28,34 @@ class CryptoEngine uint8_t nonce[16] = {0}; CryptoKey key = {}; +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t private_key[32] = {0}; +#endif public: +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t public_key[32] = {0}; +#endif + virtual ~CryptoEngine() {} +#if !(MESHTASTIC_EXCLUDE_PKI) +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); +#endif + void clearKeys(); + void setPrivateKey(uint8_t *_private_key); + virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, + uint8_t *bytesOut); + virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); + virtual bool setDHKey(uint32_t nodeNum); + virtual void hash(uint8_t *bytes, size_t numBytes); + + virtual void aesSetKey(const uint8_t *key, size_t key_len); + + virtual void aesEncrypt(uint8_t *in, uint8_t *out); + AESSmall256 *aes = NULL; + +#endif /** * Set the key used for encrypt, decrypt. @@ -61,4 +88,4 @@ class CryptoEngine void initNonce(uint32_t fromNode, uint64_t packetId); }; -extern CryptoEngine *crypto; +extern CryptoEngine *crypto; \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 000da335f..fb7926977 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -19,6 +19,7 @@ #include "error.h" #include "main.h" #include "mesh-pb-constants.h" +#include "meshUtils.h" #include "modules/NeighborInfoModule.h" #include #include @@ -123,6 +124,31 @@ NodeDB::NodeDB() // Include our owner in the node db under our nodenum meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); +#if !(MESHTASTIC_EXCLUDE_PKI) + // Calculate Curve25519 public and private keys + printBytes("Old Pubkey", config.security.public_key.bytes, 32); + if (config.security.private_key.size == 32 && config.security.public_key.size == 32) { + LOG_INFO("Using saved PKI keys\n"); + owner.public_key.size = config.security.public_key.size; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + crypto->setPrivateKey(config.security.private_key.bytes); + } else { +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + LOG_INFO("Generating new PKI keys\n"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + config.security.public_key.size = 32; + config.security.private_key.size = 32; + + printBytes("New Pubkey", config.security.public_key.bytes, 32); + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); +#else + LOG_INFO("No PKI keys set, and generation disabled!\n"); +#endif + } + +#endif + info->user = owner; info->has_user = true; @@ -237,6 +263,7 @@ void NodeDB::installDefaultConfig() config.has_power = true; config.has_network = true; config.has_bluetooth = (HAS_BLUETOOTH ? true : false); + config.has_security = true; config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL; config.lora.sx126x_rx_boosted_gain = true; @@ -259,6 +286,14 @@ void NodeDB::installDefaultConfig() #else config.lora.ignore_mqtt = false; #endif +#ifdef ADMIN_KEY_USERPREFS + memcpy(config.security.admin_key.bytes, admin_key_userprefs, 32); + config.security.admin_key.size = 32; +#else + config.security.admin_key.size = 0; +#endif + config.security.public_key.size = 0; + config.security.private_key.size = 0; #ifdef PIN_GPS_EN config.position.gps_en_gpio = PIN_GPS_EN; #endif @@ -282,7 +317,8 @@ void NodeDB::installDefaultConfig() config.position.broadcast_smart_minimum_interval_secs = 30; if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER) config.device.node_info_broadcast_secs = default_node_info_broadcast_secs; - config.device.serial_enabled = true; + config.security.serial_enabled = true; + config.security.admin_channel_enabled = false; resetRadioConfig(); strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32); // FIXME: Default to bluetooth capability of platform as default @@ -778,6 +814,7 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat) config.has_power = true; config.has_network = true; config.has_bluetooth = true; + config.has_security = true; success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config); } @@ -957,7 +994,7 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS /** Update user info and channel for this node based on received user data */ -bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex) +bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex) { meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); if (!info) { @@ -965,6 +1002,12 @@ bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t chann } LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel); +#if !(MESHTASTIC_EXCLUDE_PKI) + if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one + printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); + memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + } +#endif // Both of info->user and p start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); @@ -1042,7 +1085,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) meshtastic_NodeInfoLite *lite = getMeshNode(n); if (!lite) { - if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) { + if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP)) { if (screen) screen->print("Warn: node database full!\nErasing oldest entry\n"); LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes, @@ -1068,6 +1111,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // everything is missing except the nodenum memset(lite, 0, sizeof(*lite)); lite->num = n; + LOG_INFO("Adding node to database with %i nodes and %i bytes free!\n", numMeshNodes, memGet.getFreeHeap()); } return lite; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 447ce10d4..a71f3e134 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -98,7 +98,7 @@ class NodeDB /** Update user info and channel for this node based on received user data */ - bool updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex = 0); + bool updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex = 0); /// @return our node number NodeNum getNodeNum() { return myNodeInfo.my_node_num; } diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index fc0099e87..0a9bb5b10 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -255,6 +255,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.config.which_payload_variant = meshtastic_Config_bluetooth_tag; fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; break; + case meshtastic_Config_security_tag: + fromRadioScratch.config.which_payload_variant = meshtastic_Config_security_tag; + fromRadioScratch.config.payload_variant.security = config.security; + break; default: LOG_ERROR("Unknown config type %d\n", config_state); } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index f59d61ea2..1fecef6d7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -37,6 +37,7 @@ static MemoryDynamic staticPool; Allocator &packetPool = staticPool; static uint8_t bytes[MAX_RHPACKETLEN]; +static uint8_t ScratchEncrypted[MAX_RHPACKETLEN]; /** * Constructor @@ -307,70 +308,105 @@ bool perhapsDecode(meshtastic_MeshPacket *p) if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) return true; // If packet was already decoded just return - // assert(p->which_payloadVariant == MeshPacket_encrypted_tag); + size_t rawSize = p->encrypted.size; + if (rawSize > sizeof(bytes)) { + LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize); + return false; + } + bool decrypted = false; + ChannelIndex chIndex = 0; + memcpy(bytes, p->encrypted.bytes, + rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf + memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); +#if !(MESHTASTIC_EXCLUDE_PKI) + // Attempt PKI decryption first + if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && nodeDB->getMeshNode(p->from) != nullptr && + nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && + rawSize > 8) { + LOG_DEBUG("Attempting PKI decryption\n"); - // Try to find a channel that works with this hash - for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { - // Try to use this hash/channel pair - if (channels.decryptForHash(chIndex, p->channel)) { - // Try to decrypt the packet if we can - size_t rawSize = p->encrypted.size; - if (rawSize > sizeof(bytes)) { - LOG_ERROR("Packet too large to attempt decription! (rawSize=%d > 256)\n", rawSize); - return false; - } - memcpy(bytes, p->encrypted.bytes, - rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf - crypto->decrypt(p->from, p->id, rawSize, bytes); - - // printBytes("plaintext", bytes, p->encrypted.size); - - // Take those raw bytes and convert them back into a well structured protobuf we can understand + if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) { + LOG_INFO("PKI Decryption worked!\n"); memset(&p->decoded, 0, sizeof(p->decoded)); - if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { - LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n"); - } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { - LOG_ERROR("Invalid portnum (bad psk?)!\n"); + rawSize -= 8; + if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) && + p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) { + decrypted = true; + LOG_INFO("Packet decrypted using PKI!\n"); + p->pki_encrypted = true; + memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); + p->public_key.size = 32; + // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers + // chIndex = 8; } else { - // parsing was successful - p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded - p->channel = chIndex; // change to store the index instead of the hash - - /* Not actually ever used. - // Decompress if needed. jm - if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) { - // Decompress the payload - char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; - char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; - int decompressed_len; - - memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); - - decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); - - // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); - - memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); - - // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP - p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; - } */ - - printPacket("decoded message", p); -#if ENABLE_JSON_LOGGING - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); -#elif ARCH_PORTDUINO - if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); - } -#endif - return true; + return false; } } } +#endif - LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); - return false; + // assert(p->which_payloadVariant == MeshPacket_encrypted_tag); + if (!decrypted) { + // Try to find a channel that works with this hash + for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { + // Try to use this hash/channel pair + if (channels.decryptForHash(chIndex, p->channel)) { + // Try to decrypt the packet if we can + crypto->decrypt(p->from, p->id, rawSize, bytes); + + // printBytes("plaintext", bytes, p->encrypted.size); + + // Take those raw bytes and convert them back into a well structured protobuf we can understand + memset(&p->decoded, 0, sizeof(p->decoded)); + if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { + LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id); + } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { + LOG_ERROR("Invalid portnum (bad psk?)!\n"); + } else { + decrypted = true; + break; + } + } + } + } + if (decrypted) { + // parsing was successful + p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded + p->channel = chIndex; // change to store the index instead of the hash + + /* Not actually ever used. + // Decompress if needed. jm + if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) { + // Decompress the payload + char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; + char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; + int decompressed_len; + + memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); + + decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); + + // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); + + memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); + + // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP + p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; + } */ + + printPacket("decoded message", p); +#if ENABLE_JSON_LOGGING + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); +#elif ARCH_PORTDUINO + if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); + } +#endif + return true; + } else { + LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); + return false; + } } /** Return 0 for success or a Routing_Errror code for failure @@ -384,7 +420,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); /* Not actually used, so save the cycles - // Only allow encryption on the text message app. // TODO: Allow modules to opt into compression. if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) { @@ -432,10 +467,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Now that we are encrypting the packet channel should be the hash (no longer the index) p->channel = hash; +#if !(MESHTASTIC_EXCLUDE_PKI) + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); + if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && + p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && + p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag + LOG_DEBUG("Using PKI!\n"); + if (numbytes + 8 > MAX_RHPACKETLEN) + return meshtastic_Routing_Error_TOO_LARGE; + crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); + numbytes += 8; + memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); + p->channel = 0; + } else { + crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + memcpy(p->encrypted.bytes, bytes, numbytes); + } +#else crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + memcpy(p->encrypted.bytes, bytes, numbytes); +#endif // Copy back into the packet and set the variant type - memcpy(p->encrypted.bytes, bytes, numbytes); p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } @@ -539,4 +592,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) handleReceived(p); packetPool.release(p); -} +} \ No newline at end of file diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp new file mode 100644 index 000000000..cd18ae6c5 --- /dev/null +++ b/src/mesh/aes-ccm.cpp @@ -0,0 +1,157 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#define AES_BLOCK_SIZE 16 +#include "aes-ccm.h" +#if !MESHTASTIC_EXCLUDE_PKI + +static inline void WPA_PUT_BE16(uint8_t *a, uint16_t val) +{ + a[0] = val >> 8; + a[1] = val & 0xff; +} + +static void xor_aes_block(uint8_t *dst, const uint8_t *src) +{ + uint32_t *d = (uint32_t *)dst; + uint32_t *s = (uint32_t *)src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} +static void aes_ccm_auth_start(size_t M, size_t L, const uint8_t *nonce, const uint8_t *aad, size_t aad_len, size_t plain_len, + uint8_t *x) +{ + uint8_t aad_buf[2 * AES_BLOCK_SIZE]; + uint8_t b[AES_BLOCK_SIZE]; + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + crypto->aesEncrypt(b, x); /* X_1 = E(K, B_0) */ + if (!aad_len) + return; + WPA_PUT_BE16(aad_buf, aad_len); + memcpy(aad_buf + 2, aad, aad_len); + memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + xor_aes_block(aad_buf, x); + crypto->aesEncrypt(aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + crypto->aesEncrypt(&aad_buf[AES_BLOCK_SIZE], x); + } +} +static void aes_ccm_auth(const uint8_t *data, size_t len, uint8_t *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + crypto->aesEncrypt(x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + crypto->aesEncrypt(x, x); + } +} +static void aes_ccm_encr_start(size_t L, const uint8_t *nonce, uint8_t *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + memcpy(&a[1], nonce, 15 - L); +} +static void aes_ccm_encr(size_t L, const uint8_t *in, size_t len, uint8_t *out, uint8_t *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + crypto->aesEncrypt(a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + crypto->aesEncrypt(a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} +static void aes_ccm_encr_auth(size_t M, uint8_t *x, uint8_t *a, uint8_t *auth) +{ + size_t i; + uint8_t tmp[AES_BLOCK_SIZE]; + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + crypto->aesEncrypt(a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; +} +static void aes_ccm_decr_auth(size_t M, uint8_t *a, const uint8_t *auth, uint8_t *t) +{ + size_t i; + uint8_t tmp[AES_BLOCK_SIZE]; + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + crypto->aesEncrypt(a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; +} +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth) +{ + const size_t L = 2; + uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + crypto->aesSetKey(key, key_len); + aes_ccm_auth_start(M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(plain, plain_len, x); + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(M, x, a, auth); + return 0; +} +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain) +{ + const size_t L = 2; + uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + uint8_t t[AES_BLOCK_SIZE]; + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return false; + crypto->aesSetKey(key, key_len); + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(M, a, auth, t); + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(L, crypt, crypt_len, plain, a); + aes_ccm_auth_start(M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(plain, crypt_len, x); + if (memcmp(x, t, M) != 0) { // FIXME make const comp + return false; + } + return true; +} +#endif \ No newline at end of file diff --git a/src/mesh/aes-ccm.h b/src/mesh/aes-ccm.h new file mode 100644 index 000000000..6b8edcde4 --- /dev/null +++ b/src/mesh/aes-ccm.h @@ -0,0 +1,10 @@ +#pragma once +#include "CryptoEngine.h" +#if !MESHTASTIC_EXCLUDE_PKI + +int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth); + +bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain); +#endif \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index 9dfe9b558..e2d4188d7 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -12,4 +12,6 @@ template constexpr const T &clamp(const T &v, const T &lo, const T &hi #define STRNSTR #include char *strnstr(const char *s, const char *find, size_t slen); -#endif \ No newline at end of file +#endif + +void printBytes(const char *label, const uint8_t *p, size_t numbytes); \ No newline at end of file diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 25450992b..fe426f8f5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -65,7 +65,29 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta bool handled = false; assert(r); bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum(); - + if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { + return handled; + } + meshtastic_Channel *ch = &channels.getByIndex(mp.channel); + // Could tighten this up further by tracking the last poblic_key we went an AdminMessage request to + // and only allowing responses from that remote. + if (!((mp.from == 0 && !config.security.is_managed) || + r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || + r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag || + (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || + (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) { + LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); + return handled; + } + LOG_INFO("Handling admin payload %i\n", r->which_payload_variant); switch (r->which_payload_variant) { /** @@ -383,8 +405,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) #endif if (config.device.button_gpio == c.payload_variant.device.button_gpio && config.device.buzzer_gpio == c.payload_variant.device.buzzer_gpio && - config.device.debug_log_enabled == c.payload_variant.device.debug_log_enabled && - config.device.serial_enabled == c.payload_variant.device.serial_enabled && config.device.role == c.payload_variant.device.role && config.device.disable_triple_click == c.payload_variant.device.disable_triple_click && config.device.rebroadcast_mode == c.payload_variant.device.rebroadcast_mode) { @@ -501,6 +521,16 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.has_bluetooth = true; config.bluetooth = c.payload_variant.bluetooth; break; + case meshtastic_Config_security_tag: + LOG_INFO("Setting config: Security\n"); + config.security = c.payload_variant.security; + owner.public_key.size = config.security.public_key.size; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled && + config.security.serial_enabled == c.payload_variant.security.serial_enabled) + requiresReboot = false; + + break; } saveChanges(changes, requiresReboot); @@ -896,5 +926,5 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg) { // restrict to the admin channel for rx - boundChannel = Channels::adminChannel; + // boundChannel = Channels::adminChannel; } \ No newline at end of file diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp new file mode 100644 index 000000000..e564d5d0e --- /dev/null +++ b/test/test_crypto/test_main.cpp @@ -0,0 +1,50 @@ +#include "CryptoEngine.h" + +#include + +void setUp(void) +{ + // set stuff up here +} + +void tearDown(void) +{ + // clean stuff up here +} + +void test_SHA256(void) +{ + uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, + 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + uint8_t hash[32] = {0}; + crypto->hash(hash, 0); + TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32); +} +void test_ECB_AES256(void) +{ + uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; + uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; + uint8_t scratch[16] = {0}; + + uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}; + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16); +} + +void setup() +{ + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + UNITY_BEGIN(); // IMPORTANT LINE! + RUN_TEST(test_SHA256); + RUN_TEST(test_ECB_AES256); +} + +void loop() +{ + UNITY_END(); // stop unit testing +} \ No newline at end of file diff --git a/userPrefs.h b/userPrefs.h index 3ebbefcaf..4e80b579f 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -36,4 +36,10 @@ static unsigned char icon_bits[] = { 0x98, 0x3F, 0xF0, 0x23, 0x00, 0xFC, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0x80, 0xFF, 0x01, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; */ +/* +#define ADMIN_KEY_USERPREFS 1 +static unsigned char admin_key_userprefs[] = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, + 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, + 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c}; +*/ #endif \ No newline at end of file From 26d0b2b4771de39f10f589392fb0351e5d96fdd4 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 15:45:29 -0500 Subject: [PATCH 113/305] Add DH25519 unit test --- src/DebugConfiguration.h | 2 +- src/mesh/CryptoEngine.cpp | 27 ++++++----- src/mesh/CryptoEngine.h | 25 +++++----- src/mesh/NodeDB.cpp | 2 +- test/test_crypto/test_main.cpp | 89 +++++++++++++++++++++++++++++----- 5 files changed, 109 insertions(+), 36 deletions(-) diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index 7987e7fa1..55453ea1e 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -45,7 +45,7 @@ #define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__) #define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else -#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) +#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) && !defined(PIO_UNIT_TESTING) #define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__) #define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__) #define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 677667aef..d284f3410 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -24,7 +24,6 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) memcpy(privKey, private_key, sizeof(private_key)); } #endif -uint8_t shared_key[32]; void CryptoEngine::clearKeys() { memset(public_key, 0, sizeof(public_key)); @@ -86,7 +85,7 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); } -void CryptoEngine::setPrivateKey(uint8_t *_private_key) +void CryptoEngine::setDHPrivateKey(uint8_t *_private_key) { memcpy(private_key, _private_key, 32); } @@ -103,16 +102,8 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum) return false; } - uint8_t *pubKey = node->user.public_key.bytes; - uint8_t local_priv[32]; - memcpy(shared_key, pubKey, 32); - memcpy(local_priv, private_key, 32); - // Calculate the shared secret with the specified node's public key and our private key - // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. - if (!Curve25519::dh2(shared_key, local_priv)) { - LOG_WARN("Curve25519DH step 2 failed!\n"); + if (!setDHPublicKey(node->user.public_key.bytes)) return false; - } printBytes("DH Output: ", shared_key, 32); @@ -171,6 +162,20 @@ void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) #endif +bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) +{ + uint8_t local_priv[32]; + memcpy(shared_key, pubKey, 32); + memcpy(local_priv, private_key, 32); + // Calculate the shared secret with the specified node's public key and our private key + // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. + if (!Curve25519::dh2(shared_key, local_priv)) { + LOG_WARN("Curve25519DH step 2 failed!\n"); + return false; + } + return true; +} + concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 51080fd59..fd607c29e 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -23,15 +23,6 @@ struct CryptoKey { class CryptoEngine { - protected: - /** Our per packet nonce */ - uint8_t nonce[16] = {0}; - - CryptoKey key = {}; -#if !(MESHTASTIC_EXCLUDE_PKI) - uint8_t private_key[32] = {0}; -#endif - public: #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t public_key[32] = {0}; @@ -43,11 +34,12 @@ class CryptoEngine virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); #endif void clearKeys(); - void setPrivateKey(uint8_t *_private_key); + void setDHPrivateKey(uint8_t *_private_key); virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); - virtual bool setDHKey(uint32_t nodeNum); + bool setDHKey(uint32_t nodeNum); + virtual bool setDHPublicKey(uint8_t *publicKey); virtual void hash(uint8_t *bytes, size_t numBytes); virtual void aesSetKey(const uint8_t *key, size_t key_len); @@ -75,8 +67,17 @@ class CryptoEngine */ virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); - +#ifndef PIO_UNIT_TESTING protected: +#endif + /** Our per packet nonce */ + uint8_t nonce[16] = {0}; + + CryptoKey key = {}; +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t shared_key[32] = {0}; + uint8_t private_key[32] = {0}; +#endif /** * Init our 128 bit nonce for a new packet * diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index fb7926977..468bffab3 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -131,7 +131,7 @@ NodeDB::NodeDB() LOG_INFO("Using saved PKI keys\n"); owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); - crypto->setPrivateKey(config.security.private_key.bytes); + crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) LOG_INFO("Generating new PKI keys\n"); diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index e564d5d0e..e9aee928e 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -2,6 +2,18 @@ #include +void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0) +{ + if (len) { + memset(result, 0, len); + } + for (unsigned int i = 0; i < hex.length(); i += 2) { + std::string byteString = hex.substr(i, 2); + result[i / 2] = (uint8_t)strtol(byteString.c_str(), NULL, 16); + } + return; +} + void setUp(void) { // set stuff up here @@ -14,25 +26,79 @@ void tearDown(void) void test_SHA256(void) { - uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, - 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + uint8_t expected[32]; uint8_t hash[32] = {0}; + + HexToBytes(expected, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); crypto->hash(hash, 0); - TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); + + HexToBytes(hash, "d3", 32); + HexToBytes(expected, "28969cdfa74a12c82f3bad960b0b000aca2ac329deea5c2328ebc6f2ba9802c1"); + crypto->hash(hash, 1); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); + + HexToBytes(hash, "11af", 32); + HexToBytes(expected, "5ca7133fa735326081558ac312c620eeca9970d1e70a4b95533d956f072d1f98"); + crypto->hash(hash, 2); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); } void test_ECB_AES256(void) { - uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; - uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; - uint8_t scratch[16] = {0}; + // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_ECB.pdf - uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}; + uint8_t key[32] = {0}; + uint8_t plain[16] = {0}; + uint8_t result[16] = {0}; + uint8_t expected[16] = {0}; + + HexToBytes(key, "603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4"); + + HexToBytes(plain, "6BC1BEE22E409F96E93D7E117393172A"); + HexToBytes(expected, "F3EED1BDB5D2A03C064B5A7E3DB181F8"); crypto->aesSetKey(key, 32); - crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time - TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16); -} + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); + HexToBytes(plain, "AE2D8A571E03AC9C9EB76FAC45AF8E51"); + HexToBytes(expected, "591CCB10D410ED26DC5BA74A31362870"); + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); + + HexToBytes(plain, "30C81C46A35CE411E5FBC1191A0A52EF"); + HexToBytes(expected, "B6ED21B99CA6F4F9F153E7B1BEAFED1D"); + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); +} +void test_DH25519(void) +{ + // test vectors from wycheproof x25519 + // https://github.com/C2SP/wycheproof/blob/master/testvectors/x25519_test.json + uint8_t private_key[32]; + uint8_t public_key[32]; + uint8_t expected_shared[32]; + + HexToBytes(public_key, "504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"); + HexToBytes(private_key, "c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475"); + HexToBytes(expected_shared, "436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); + + HexToBytes(public_key, "63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733"); + HexToBytes(private_key, "d85d8c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211958"); + HexToBytes(expected_shared, "279df67a7c4611db4708a0e8282b195e5ac0ed6f4b2f292c6fbd0acac30d1332"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); + + HexToBytes(public_key, "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"); + HexToBytes(private_key, "18630f93598637c35da623a74559cf944374a559114c7937811041fc8605564a"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key +} void setup() { // NOTE!!! Wait for >2 secs @@ -42,6 +108,7 @@ void setup() UNITY_BEGIN(); // IMPORTANT LINE! RUN_TEST(test_SHA256); RUN_TEST(test_ECB_AES256); + RUN_TEST(test_DH25519); } void loop() From 192af05a25ef219c14eec702129bdc800ce31721 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 20:04:38 -0500 Subject: [PATCH 114/305] Fix compile on STM32 --- src/mesh/CryptoEngine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index d284f3410..fd7246fa9 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -160,8 +160,6 @@ void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) aes->encryptBlock(out, in); } -#endif - bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) { uint8_t local_priv[32]; @@ -176,6 +174,7 @@ bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) return true; } +#endif concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) From c3aa56ef30ff4069065c1819d8ab0ac8ca8d7aba Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 22:38:05 -0500 Subject: [PATCH 115/305] Refactor platform cryptography, add tests --- src/mesh/CryptoEngine.cpp | 42 +++++++++- src/mesh/CryptoEngine.h | 6 +- src/mesh/Router.cpp | 2 +- src/platform/esp32/ESP32CryptoEngine.cpp | 41 ++-------- src/platform/esp32/architecture.h | 3 + src/platform/nrf52/NRF52CryptoEngine.cpp | 29 ++----- src/platform/nrf52/architecture.h | 3 + .../portduino/CrossPlatformCryptoEngine.cpp | 78 ------------------- src/platform/rp2040/rp2040CryptoEngine.cpp | 66 ---------------- src/platform/stm32wl/STM32WLCryptoEngine.cpp | 67 ---------------- test/test_crypto/test_main.cpp | 27 +++++++ 11 files changed, 88 insertions(+), 276 deletions(-) delete mode 100644 src/platform/portduino/CrossPlatformCryptoEngine.cpp delete mode 100644 src/platform/rp2040/rp2040CryptoEngine.cpp delete mode 100644 src/platform/stm32wl/STM32WLCryptoEngine.cpp diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index fd7246fa9..e83236eab 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -1,6 +1,7 @@ #include "CryptoEngine.h" #include "NodeDB.h" #include "RadioInterface.h" +#include "architecture.h" #include "configuration.h" #if !(MESHTASTIC_EXCLUDE_PKI) @@ -188,14 +189,44 @@ void CryptoEngine::setKey(const CryptoKey &k) * * @param bytes is updated in place */ -void CryptoEngine::encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) +void CryptoEngine::encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) { - LOG_WARN("noop encryption!\n"); + if (key.length > 0) { + initNonce(fromNode, packetId); + if (numBytes <= MAX_BLOCKSIZE) { + encryptAESCtr(key, nonce, numBytes, bytes); + } else { + LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); + } + } } void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) { - LOG_WARN("noop decryption!\n"); + // For CTR, the implementation is the same + encryptPacket(fromNode, packetId, numBytes, bytes); +} + +// Generic implementation of AES-CTR encryption. +void CryptoEngine::encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) +{ + if (ctr) { + delete ctr; + ctr = nullptr; + } + if (_key.length == 16) + ctr = new CTR(); + else + ctr = new CTR(); + ctr->setKey(_key.bytes, _key.length); + static uint8_t scratch[MAX_BLOCKSIZE]; + memcpy(scratch, bytes, numBytes); + memset(scratch + numBytes, 0, + sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) + + ctr->setIV(_nonce, 16); + ctr->setCounterSize(4); + ctr->encrypt(bytes, scratch, numBytes); } /** @@ -208,4 +239,7 @@ void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId) // use memcpy to avoid breaking strict-aliasing memcpy(nonce, &packetId, sizeof(uint64_t)); memcpy(nonce + sizeof(uint64_t), &fromNode, sizeof(uint32_t)); -} \ No newline at end of file +} +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +CryptoEngine *crypto = new CryptoEngine; +#endif \ No newline at end of file diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index fd607c29e..5ca9db7c1 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -1,5 +1,6 @@ #pragma once #include "AES.h" +#include "CTR.h" #include "concurrency/LockGuard.h" #include "configuration.h" #include "mesh-pb-constants.h" @@ -65,15 +66,16 @@ class CryptoEngine * * @param bytes is updated in place */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); + virtual void encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); + virtual void encryptAESCtr(CryptoKey key, uint8_t *nonce, size_t numBytes, uint8_t *bytes); #ifndef PIO_UNIT_TESTING protected: #endif /** Our per packet nonce */ uint8_t nonce[16] = {0}; - CryptoKey key = {}; + CTRCommon *ctr = NULL; #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t shared_key[32] = {0}; uint8_t private_key[32] = {0}; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1fecef6d7..b00b66a47 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -480,7 +480,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; } else { - crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); } #else diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp index 998419df8..230139036 100644 --- a/src/platform/esp32/ESP32CryptoEngine.cpp +++ b/src/platform/esp32/ESP32CryptoEngine.cpp @@ -13,58 +13,29 @@ class ESP32CryptoEngine : public CryptoEngine ~ESP32CryptoEngine() { mbedtls_aes_free(&aes); } - /** - * Set the key used for encrypt, decrypt. - * - * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. - * - * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) - * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the - * provided pointer) - */ - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - - if (key.length != 0) { - auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8); - assert(!res); - } - } - /** * Encrypt a packet * * @param bytes is updated in place + * TODO: return bool, and handle graciously when something fails */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override + virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override { - if (key.length > 0) { - LOG_DEBUG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); - initNonce(fromNode, packetId); + if (_key.length > 0) { if (numBytes <= MAX_BLOCKSIZE) { + mbedtls_aes_setkey_enc(&aes, _key.bytes, _key.length * 8); static uint8_t scratch[MAX_BLOCKSIZE]; uint8_t stream_block[16]; size_t nc_off = 0; memcpy(scratch, bytes, numBytes); memset(scratch + numBytes, 0, sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes); - assert(!res); + mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes); } else { LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); } } } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: }; -CryptoEngine *crypto = new ESP32CryptoEngine(); +CryptoEngine *crypto = new ESP32CryptoEngine(); \ No newline at end of file diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index fd3f92a9c..b6def5b01 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -42,6 +42,9 @@ #ifndef DEFAULT_VREF #define DEFAULT_VREF 1100 #endif +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +#define HAS_CUSTOM_CRYPTO_ENGINE 1 +#endif #if defined(HAS_AXP192) || defined(HAS_AXP2101) #define HAS_PMU diff --git a/src/platform/nrf52/NRF52CryptoEngine.cpp b/src/platform/nrf52/NRF52CryptoEngine.cpp index a7cf3d5bf..5de13c58b 100644 --- a/src/platform/nrf52/NRF52CryptoEngine.cpp +++ b/src/platform/nrf52/NRF52CryptoEngine.cpp @@ -9,41 +9,24 @@ class NRF52CryptoEngine : public CryptoEngine ~NRF52CryptoEngine() {} - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override + virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override { - if (key.length > 16) { - LOG_DEBUG("Software encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); + if (_key.length > 16) { AES_ctx ctx; - initNonce(fromNode, packetId); - AES_init_ctx_iv(&ctx, key.bytes, nonce); + AES_init_ctx_iv(&ctx, _key.bytes, _nonce); AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes); - } else if (key.length > 0) { - LOG_DEBUG("nRF52 encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); + } else if (_key.length > 0) { nRFCrypto.begin(); nRFCrypto_AES ctx; uint8_t myLen = ctx.blockLen(numBytes); char encBuf[myLen] = {0}; - initNonce(fromNode, packetId); ctx.begin(); - ctx.Process((char *)bytes, numBytes, nonce, key.bytes, key.length, encBuf, ctx.encryptFlag, ctx.ctrMode); + ctx.Process((char *)bytes, numBytes, _nonce, _key.bytes, _key.length, encBuf, ctx.encryptFlag, ctx.ctrMode); ctx.end(); nRFCrypto.end(); memcpy(bytes, encBuf, numBytes); } } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: }; -CryptoEngine *crypto = new NRF52CryptoEngine(); +CryptoEngine *crypto = new NRF52CryptoEngine(); \ No newline at end of file diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index d5685d611..8781347c3 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -32,6 +32,9 @@ #ifndef HAS_CPU_SHUTDOWN #define HAS_CPU_SHUTDOWN 1 #endif +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +#define HAS_CUSTOM_CRYPTO_ENGINE 1 +#endif // // set HW_VENDOR diff --git a/src/platform/portduino/CrossPlatformCryptoEngine.cpp b/src/platform/portduino/CrossPlatformCryptoEngine.cpp deleted file mode 100644 index 46ef942f0..000000000 --- a/src/platform/portduino/CrossPlatformCryptoEngine.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -/** A platform independent AES engine implemented using Tiny-AES - */ -class CrossPlatformCryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - CrossPlatformCryptoEngine() {} - - ~CrossPlatformCryptoEngine() {} - - /** - * Set the key used for encrypt, decrypt. - * - * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. - * - * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) - * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the - * provided pointer) - */ - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new CrossPlatformCryptoEngine(); diff --git a/src/platform/rp2040/rp2040CryptoEngine.cpp b/src/platform/rp2040/rp2040CryptoEngine.cpp deleted file mode 100644 index 5486e51e5..000000000 --- a/src/platform/rp2040/rp2040CryptoEngine.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -class RP2040CryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - RP2040CryptoEngine() {} - - ~RP2040CryptoEngine() {} - - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new RP2040CryptoEngine(); diff --git a/src/platform/stm32wl/STM32WLCryptoEngine.cpp b/src/platform/stm32wl/STM32WLCryptoEngine.cpp deleted file mode 100644 index 4debdf78e..000000000 --- a/src/platform/stm32wl/STM32WLCryptoEngine.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#undef RNG -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -class STM32WLCryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - STM32WLCryptoEngine() {} - - ~STM32WLCryptoEngine() {} - - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new STM32WLCryptoEngine(); diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index e9aee928e..129c88283 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -99,6 +99,32 @@ void test_DH25519(void) crypto->setDHPrivateKey(private_key); TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key } +void test_AES_CTR(void) +{ + uint8_t expected[32]; + uint8_t plain[32]; + uint8_t nonce[32]; + CryptoKey k; + + // vectors from https://www.rfc-editor.org/rfc/rfc3686#section-6 + k.length = 32; + HexToBytes(k.bytes, "776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104"); + HexToBytes(nonce, "00000060DB5672C97AA8F0B200000001"); + HexToBytes(expected, "145AD01DBF824EC7560863DC71E3E0C0"); + memcpy(plain, "Single block msg", 16); + + crypto->encryptAESCtr(k, nonce, 16, plain); + TEST_ASSERT_EQUAL_MEMORY(expected, plain, 16); + + k.length = 16; + memcpy(plain, "Single block msg", 16); + HexToBytes(k.bytes, "AE6852F8121067CC4BF7A5765577F39E"); + HexToBytes(nonce, "00000030000000000000000000000001"); + HexToBytes(expected, "E4095D4FB7A7B3792D6175A3261311B8"); + crypto->encryptAESCtr(k, nonce, 16, plain); + TEST_ASSERT_EQUAL_MEMORY(expected, plain, 16); +} + void setup() { // NOTE!!! Wait for >2 secs @@ -109,6 +135,7 @@ void setup() RUN_TEST(test_SHA256); RUN_TEST(test_ECB_AES256); RUN_TEST(test_DH25519); + RUN_TEST(test_AES_CTR); } void loop() From c86a3200f04fc90ea045988cdbbb62078390f115 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 23:11:04 -0500 Subject: [PATCH 116/305] Add missed function rename. (Thanks VSCode) --- src/mesh/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index b00b66a47..2a6fb31fc 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -484,7 +484,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) memcpy(p->encrypted.bytes, bytes, numbytes); } #else - crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); #endif From 185eb318ad274fd95315ebd2da3aafe20e5802ca Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 11 Aug 2024 14:12:20 -0500 Subject: [PATCH 117/305] Manual protobuf update --- src/mesh/generated/meshtastic/admin.pb.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bef2abf9b..13093839d 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -30,7 +30,9 @@ typedef enum _meshtastic_AdminMessage_ConfigType { /* TODO: REPLACE */ meshtastic_AdminMessage_ConfigType_LORA_CONFIG = 5, /* TODO: REPLACE */ - meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6 + meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6, + /* TODO: REPLACE */ + meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG = 7 } meshtastic_AdminMessage_ConfigType; /* TODO: REPLACE */ @@ -194,8 +196,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG -#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG -#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG+1)) +#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG +#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG+1)) #define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG #define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG From e7dfabc20fa5518103821c7ca95bd77cdcea9768 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 11 Aug 2024 14:18:33 -0500 Subject: [PATCH 118/305] Exclude position packets from PKI (at least for now) --- src/mesh/Router.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 2a6fb31fc..945f92bb7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -470,8 +470,9 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) #if !(MESHTASTIC_EXCLUDE_PKI) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && - p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && - p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag + numbytes <= MAX_RHPACKETLEN - 8 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && + p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && + p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; From 8f3614d66c2671d5160c3bb95374c0ba712dc2f6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 11 Aug 2024 17:22:11 -0500 Subject: [PATCH 119/305] User to UserLite in NodeDB (#4438) * User to UserLite in the nodedb * Tronkdor the burninator --- protobufs | 2 +- src/RedirectablePrint.cpp | 5 +- src/mesh/NodeDB.cpp | 14 +++-- src/mesh/TypeConversions.cpp | 32 +++++++++- src/mesh/TypeConversions.h | 2 + .../generated/meshtastic/deviceonly.pb.cpp | 3 + src/mesh/generated/meshtastic/deviceonly.pb.h | 62 +++++++++++++++++-- 7 files changed, 106 insertions(+), 14 deletions(-) diff --git a/protobufs b/protobufs index 778667d93..c7a5a410b 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 778667d93b9769d51c386c461456bdec4f14f433 +Subproject commit c7a5a410b9652a91533f44c5fa4c28f0a6c96429 diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 02cd8b309..0eab0de0a 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -38,8 +38,9 @@ size_t RedirectablePrint::write(uint8_t c) #ifdef USE_SEGGER SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); #endif - - if (!config.has_lora || config.security.serial_enabled) + // Account for legacy config transition + bool serialEnabled = config.has_security ? config.security.serial_enabled : config.device.serial_enabled; + if (!config.has_lora || serialEnabled) dest->write(c); return 1; // We always claim one was written, rather than trusting what the diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 468bffab3..a9c8bbb44 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -134,6 +134,10 @@ NodeDB::NodeDB() crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + config.has_security = true; + config.security.serial_enabled = config.device.serial_enabled; + config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; + config.security.is_managed = config.device.is_managed; LOG_INFO("Generating new PKI keys\n"); crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); config.security.public_key.size = 32; @@ -149,7 +153,7 @@ NodeDB::NodeDB() #endif - info->user = owner; + info->user = TypeConversions::ConvertToUserLite(owner); info->has_user = true; #ifdef ARCH_ESP32 @@ -1001,7 +1005,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde return false; } - LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel); + LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); @@ -1012,11 +1016,11 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde // Both of info->user and p start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); - info->user = p; + info->user = TypeConversions::ConvertToUserLite(p); if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) - LOG_DEBUG("updating changed=%d user %s/%s/%s, channel=%d\n", changed, info->user.id, info->user.long_name, - info->user.short_name, info->channel); + LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, + info->channel); info->has_user = true; if (changed) { diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index bcd600f24..30b06d0ad 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -24,7 +24,7 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo } if (lite->has_user) { info.has_user = true; - info.user = lite->user; + info.user = ConvertToUser(lite->num, lite->user); } if (lite->has_device_metrics) { info.has_device_metrics = true; @@ -55,4 +55,34 @@ meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite l position.time = lite.time; return position; +} + +meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) +{ + meshtastic_UserLite lite = meshtastic_UserLite_init_default; + + strncpy(lite.long_name, user.long_name, sizeof(lite.long_name)); + strncpy(lite.short_name, user.short_name, sizeof(lite.short_name)); + lite.hw_model = user.hw_model; + lite.role = user.role; + lite.is_licensed = user.is_licensed; + memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); + memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); + return lite; +} + +meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite) +{ + meshtastic_User user = meshtastic_User_init_default; + + snprintf(user.id, sizeof(user.id), "!%08x", nodeNum); + strncpy(user.long_name, lite.long_name, sizeof(user.long_name)); + strncpy(user.short_name, lite.short_name, sizeof(user.short_name)); + user.hw_model = lite.hw_model; + user.role = lite.role; + user.is_licensed = lite.is_licensed; + memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); + memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); + + return user; } \ No newline at end of file diff --git a/src/mesh/TypeConversions.h b/src/mesh/TypeConversions.h index ffc3c12a7..19e471f98 100644 --- a/src/mesh/TypeConversions.h +++ b/src/mesh/TypeConversions.h @@ -10,4 +10,6 @@ class TypeConversions static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite); static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position); static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite); + static meshtastic_UserLite ConvertToUserLite(meshtastic_User user); + static meshtastic_User ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite); }; diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 672192f67..2747ac9d9 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -9,6 +9,9 @@ PB_BIND(meshtastic_PositionLite, meshtastic_PositionLite, AUTO) +PB_BIND(meshtastic_UserLite, meshtastic_UserLite, AUTO) + + PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO) diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 2c91fe30e..343e5f48a 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -9,6 +9,7 @@ #include "meshtastic/localonly.pb.h" #include "meshtastic/mesh.pb.h" #include "meshtastic/telemetry.pb.h" +#include "meshtastic/config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -45,12 +46,37 @@ typedef struct _meshtastic_PositionLite { meshtastic_Position_LocSource location_source; } meshtastic_PositionLite; +typedef PB_BYTES_ARRAY_T(32) meshtastic_UserLite_public_key_t; +typedef struct _meshtastic_UserLite { + /* This is the addr of the radio. */ + pb_byte_t macaddr[6]; + /* A full name for this user, i.e. "Kevin Hester" */ + char long_name[40]; + /* A VERY short name, ideally two characters. + Suitable for a tiny OLED screen */ + char short_name[5]; + /* TBEAM, HELTEC, etc... + Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. + Apps will still need the string here for older builds + (so OTA update can find the right image), but if the enum is available it will be used instead. */ + meshtastic_HardwareModel hw_model; + /* In some regions Ham radio operators have different bandwidth limitations than others. + If this user is a licensed operator, set this flag. + Also, "long_name" should be their licence number. */ + bool is_licensed; + /* Indicates that the user's role in the mesh */ + meshtastic_Config_DeviceConfig_Role role; + /* The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_UserLite_public_key_t public_key; +} meshtastic_UserLite; + typedef struct _meshtastic_NodeInfoLite { /* The node number */ uint32_t num; /* The user info for this node */ bool has_user; - meshtastic_User user; + meshtastic_UserLite user; /* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. Position.time now indicates the last time we received a POSITION from that node. */ bool has_position; @@ -164,6 +190,9 @@ extern "C" { #define meshtastic_PositionLite_location_source_ENUMTYPE meshtastic_Position_LocSource +#define meshtastic_UserLite_hw_model_ENUMTYPE meshtastic_HardwareModel +#define meshtastic_UserLite_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role + @@ -172,12 +201,14 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} -#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_User_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} +#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} +#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} #define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}} #define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0} #define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default} #define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} -#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} +#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} +#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} #define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}} #define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0} #define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero} @@ -188,6 +219,13 @@ extern "C" { #define meshtastic_PositionLite_altitude_tag 3 #define meshtastic_PositionLite_time_tag 4 #define meshtastic_PositionLite_location_source_tag 5 +#define meshtastic_UserLite_macaddr_tag 1 +#define meshtastic_UserLite_long_name_tag 2 +#define meshtastic_UserLite_short_name_tag 3 +#define meshtastic_UserLite_hw_model_tag 4 +#define meshtastic_UserLite_is_licensed_tag 5 +#define meshtastic_UserLite_role_tag 6 +#define meshtastic_UserLite_public_key_tag 7 #define meshtastic_NodeInfoLite_num_tag 1 #define meshtastic_NodeInfoLite_user_tag 2 #define meshtastic_NodeInfoLite_position_tag 3 @@ -229,6 +267,17 @@ X(a, STATIC, SINGULAR, UENUM, location_source, 5) #define meshtastic_PositionLite_CALLBACK NULL #define meshtastic_PositionLite_DEFAULT NULL +#define meshtastic_UserLite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 1) \ +X(a, STATIC, SINGULAR, STRING, long_name, 2) \ +X(a, STATIC, SINGULAR, STRING, short_name, 3) \ +X(a, STATIC, SINGULAR, UENUM, hw_model, 4) \ +X(a, STATIC, SINGULAR, BOOL, is_licensed, 5) \ +X(a, STATIC, SINGULAR, UENUM, role, 6) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 7) +#define meshtastic_UserLite_CALLBACK NULL +#define meshtastic_UserLite_DEFAULT NULL + #define meshtastic_NodeInfoLite_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ @@ -242,7 +291,7 @@ X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) #define meshtastic_NodeInfoLite_CALLBACK NULL #define meshtastic_NodeInfoLite_DEFAULT NULL -#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_User +#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_UserLite #define meshtastic_NodeInfoLite_position_MSGTYPE meshtastic_PositionLite #define meshtastic_NodeInfoLite_device_metrics_MSGTYPE meshtastic_DeviceMetrics @@ -290,6 +339,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, oem_local_module_config, 8) #define meshtastic_OEMStore_oem_local_module_config_MSGTYPE meshtastic_LocalModuleConfig extern const pb_msgdesc_t meshtastic_PositionLite_msg; +extern const pb_msgdesc_t meshtastic_UserLite_msg; extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg; extern const pb_msgdesc_t meshtastic_DeviceState_msg; extern const pb_msgdesc_t meshtastic_ChannelFile_msg; @@ -297,6 +347,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg +#define meshtastic_UserLite_fields &meshtastic_UserLite_msg #define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg #define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg #define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg @@ -306,9 +357,10 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* meshtastic_DeviceState_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 200 +#define meshtastic_NodeInfoLite_size 183 #define meshtastic_OEMStore_size 3502 #define meshtastic_PositionLite_size 28 +#define meshtastic_UserLite_size 96 #ifdef __cplusplus } /* extern "C" */ From 884bc529f0ce5c52ec3402f10446023a75fbd9e5 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 11 Aug 2024 18:25:32 -0500 Subject: [PATCH 120/305] protos --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index c7a5a410b..8063f8026 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c7a5a410b9652a91533f44c5fa4c28f0a6c96429 +Subproject commit 8063f80260a5af438d5b68c6587b4fed77d6c46d From 67ddae2851a6d51a059baaf70540f4770b37c320 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 06:43:54 -0500 Subject: [PATCH 121/305] Add logic to nodeDB to prefer evicting boring nodes (#4441) --- src/mesh/NodeDB.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a9c8bbb44..f2a0520e3 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1097,11 +1097,24 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // look for oldest node and erase it uint32_t oldest = UINT32_MAX; int oldestIndex = -1; + int oldestIndex = -1; + int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { + // Simply the oldest non-favorite node if (!meshNodes->at(i).is_favorite && meshNodes->at(i).last_heard < oldest) { oldest = meshNodes->at(i).last_heard; oldestIndex = i; } + // The oldest "boring" node + if (!meshNodes->at(i).is_favorite && meshNodes->at(i).user.public_key.size == 0 && + meshNodes->at(i).last_heard < oldestBoring) { + oldestBoring = meshNodes->at(i).last_heard; + oldestBoringIndex = i; + } + } + // if we found a "boring" node, evict it + if (oldestBoringIndex != -1) { + oldestIndex = oldestBoringIndex; } // Shove the remaining nodes down the chain for (int i = oldestIndex; i < numMeshNodes - 1; i++) { @@ -1142,4 +1155,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} \ No newline at end of file +} From 2d1813023500dccb2f42763aec8810a3448c849c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 11:26:43 -0500 Subject: [PATCH 122/305] Don't goober public_key in Userlite conversion --- src/mesh/NodeDB.cpp | 15 ++++++++++++--- src/mesh/TypeConversions.cpp | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index f2a0520e3..3fdc22685 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1007,9 +1007,15 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) - if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one - printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); - memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + if (p.public_key.size > 0) { + printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); + if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one + LOG_INFO("Public Key set for node, not updateing!\n"); + // we copy the key into the incoming packet, to prevent overwrite + memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + } else { + LOG_INFO("Updating Node Pubkey!\n"); + } } #endif @@ -1017,6 +1023,9 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); info->user = TypeConversions::ConvertToUserLite(p); + if (info->user.public_key.size == 32) { + printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32); + } if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 30b06d0ad..5303dfb49 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -68,6 +68,7 @@ meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) lite.is_licensed = user.is_licensed; memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); + lite.public_key.size = user.public_key.size; return lite; } From 7537b55586aaaa526098a798cc3a9da59aedb589 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 11:37:50 -0500 Subject: [PATCH 123/305] Ungoober oldestBoring --- src/mesh/NodeDB.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 3fdc22685..ef649288e 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1105,7 +1105,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) memGet.getFreeHeap()); // look for oldest node and erase it uint32_t oldest = UINT32_MAX; - int oldestIndex = -1; + uint32_t oldestBoring = UINT32_MAX; int oldestIndex = -1; int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { From b91d66b436d11f98963f5a6d93390d7517cb5c53 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 16:20:07 -0500 Subject: [PATCH 124/305] Don't forget public_key.size in converting back --- src/mesh/TypeConversions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 5303dfb49..d8ee6afc7 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -84,6 +84,7 @@ meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_User user.is_licensed = lite.is_licensed; memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); + user.public_key.size = lite.public_key.size; return user; } \ No newline at end of file From 0e7253d309d75a048abb59459d8dd5b037673727 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 12 Aug 2024 19:37:39 -0500 Subject: [PATCH 125/305] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/protobufs b/protobufs index 8063f8026..6307b44cd 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 8063f80260a5af438d5b68c6587b4fed77d6c46d +Subproject commit 6307b44cd98ef1d86d5f39edefe9361e79d9ece4 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 70423f673..3800e6c0c 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -300,7 +300,9 @@ typedef enum _meshtastic_Routing_Error { meshtastic_Routing_Error_BAD_REQUEST = 32, /* The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) */ - meshtastic_Routing_Error_NOT_AUTHORIZED = 33 + meshtastic_Routing_Error_NOT_AUTHORIZED = 33, + /* This message is not a failure, and indicates that the message was sent via PKI */ + meshtastic_Routing_Error_NONE_PKI = 34 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -993,8 +995,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NOT_AUTHORIZED -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NOT_AUTHORIZED+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NONE_PKI +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NONE_PKI+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX From b4cbea1b3d63c759eb771c1f752b88e4dca66661 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:29:54 -0500 Subject: [PATCH 126/305] Move security migrate to if has_security --- src/mesh/NodeDB.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ef649288e..521a9d6d7 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -124,6 +124,12 @@ NodeDB::NodeDB() // Include our owner in the node db under our nodenum meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); + if (!config.has_security) { + config.has_security = true; + config.security.serial_enabled = config.device.serial_enabled; + config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; + config.security.is_managed = config.device.is_managed; + } #if !(MESHTASTIC_EXCLUDE_PKI) // Calculate Curve25519 public and private keys printBytes("Old Pubkey", config.security.public_key.bytes, 32); From c16f20de21cded1d56ea9992b86ebe9b2ce3c816 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:30:34 -0500 Subject: [PATCH 127/305] Make "Alloc an error" a LOG_WARN --- src/mesh/MeshModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 604ac9dc4..3fe4c85d5 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -54,8 +54,8 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; - if (err != meshtastic_Routing_Error_NONE) - LOG_ERROR("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + if (err != meshtastic_Routing_Error_NONE && err != meshtastic_Routing_Error_NONE_PKI) + LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); return p; } From 754db3f2bcff99e38aea1443934bc64416549e9a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:32:56 -0500 Subject: [PATCH 128/305] Finish fixing config migrate --- src/mesh/NodeDB.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 521a9d6d7..7f403c53a 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -140,10 +140,6 @@ NodeDB::NodeDB() crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) - config.has_security = true; - config.security.serial_enabled = config.device.serial_enabled; - config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; - config.security.is_managed = config.device.is_managed; LOG_INFO("Generating new PKI keys\n"); crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); config.security.public_key.size = 32; From 308c0a6bb8e5f4366e079b9c767af049b4263ef5 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:39:45 -0500 Subject: [PATCH 129/305] Add Routing_Error_NONE --- src/mesh/MeshService.cpp | 3 ++- src/mesh/MeshTypes.h | 2 +- src/mesh/ReliableRouter.cpp | 23 ++++++++++------------- src/mesh/Router.cpp | 8 +++++--- src/modules/CannedMessageModule.cpp | 3 ++- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index ac97d51a7..f22949576 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -253,7 +253,8 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh LOG_DEBUG("Can't send status to phone"); } - if (res == ERRNO_OK && ccToPhone) { // Check if p is not released in case it couldn't be sent + if ((res == ERRNO_OK || res == meshtastic_Routing_Error_NONE_PKI) && + ccToPhone) { // Check if p is not released in case it couldn't be sent sendToPhone(packetPool.allocCopy(*p)); } } diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index c0919bf5d..ecac626a8 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -15,7 +15,7 @@ typedef uint32_t PacketId; // A packet sequence number #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -#define ERRNO_DISABLED 34 // the interface is disabled +#define ERRNO_DISABLED 35 // the interface is disabled /* * Source of a received message diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index c91ce50c5..6b1cf92e7 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -117,20 +117,17 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error - PacketId ackId = ((c && c->error_reason == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; + if ((c && (c->error_reason == meshtastic_Routing_Error_NONE || c->error_reason == meshtastic_Routing_Error_NONE_PKI)) || + !c) { + LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", p->decoded.request_id); + stopRetransmission(p->to, p->decoded.request_id); + // } else if (c && (c->error_reason == meshtastic_Routing_Error_NO_CHANNEL)) { + // noop? + } else if (c && + (c->error_reason != meshtastic_Routing_Error_NONE && c->error_reason != meshtastic_Routing_Error_NONE_PKI)) { - // A nak is a routing packt that has an error code - PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0; - - // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records - if (ackId || nakId) { - if (ackId) { - LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId); - stopRetransmission(p->to, ackId); - } else { - LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId); - stopRetransmission(p->to, nakId); - } + LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", p->decoded.request_id); + stopRetransmission(p->to, p->decoded.request_id); } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 945f92bb7..64fa4cca2 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -258,7 +258,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p); auto encodeResult = perhapsEncode(p); - if (encodeResult != meshtastic_Routing_Error_NONE) { + if (encodeResult != meshtastic_Routing_Error_NONE && encodeResult != meshtastic_Routing_Error_NONE_PKI) { packetPool.release(p_decoded); abortSendAndNak(encodeResult, p); return encodeResult; // FIXME - this isn't a valid ErrorCode @@ -493,8 +493,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } - - return meshtastic_Routing_Error_NONE; + if (p->pki_encrypted) + return meshtastic_Routing_Error_NONE_PKI; + else + return meshtastic_Routing_Error_NONE; } NodeNum Router::getNodeNum() diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 4df5a03fc..2ec914668 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -1105,7 +1105,8 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket & this->incoming = service->getNodenumFromRequestId(mp.decoded.request_id); meshtastic_Routing decoded = meshtastic_Routing_init_default; pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); - this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; + this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE || + decoded.error_reason == meshtastic_Routing_Error_NONE_PKI; waitingForAck = false; // No longer want routing packets this->notifyObservers(&e); // run the next time 2 seconds later From bcd77c45232c2b05c63f7f646452ef53182e7dad Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 06:31:05 -0500 Subject: [PATCH 130/305] Cleanup public_keys (#4450) --- src/mesh/NodeDB.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 7f403c53a..1caaaf39b 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -554,10 +554,21 @@ void NodeDB::cleanupMeshDB() { int newPos = 0, removed = 0; for (int i = 0; i < numMeshNodes; i++) { - if (meshNodes->at(i).has_user) + if (meshNodes->at(i).has_user) { + if (meshNodes->at(i).user.public_key.size > 0) { + for (int j = 0; j < numMeshNodes; j++) { + if (meshNodes->at(i).user.public_key.bytes[j] != 0) { + break; + } + if (j == 31) { + meshNodes->at(i).user.public_key.size = 0; + } + } + } meshNodes->at(newPos++) = meshNodes->at(i); - else + } else { removed++; + } } numMeshNodes -= removed; std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + removed, @@ -1166,4 +1177,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} +} \ No newline at end of file From f3fa8daedf9da40ca9e2f4c92880e349da5fe36a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 09:15:20 -0500 Subject: [PATCH 131/305] Revert "Add Routing_Error_NONE" This reverts commit e1985fa0f90db0cbc346db18cecbf3604f6973c1. --- src/mesh/MeshService.cpp | 3 +-- src/mesh/MeshTypes.h | 2 +- src/mesh/ReliableRouter.cpp | 23 +++++++++++++---------- src/mesh/Router.cpp | 8 +++----- src/modules/CannedMessageModule.cpp | 3 +-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index f22949576..ac97d51a7 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -253,8 +253,7 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh LOG_DEBUG("Can't send status to phone"); } - if ((res == ERRNO_OK || res == meshtastic_Routing_Error_NONE_PKI) && - ccToPhone) { // Check if p is not released in case it couldn't be sent + if (res == ERRNO_OK && ccToPhone) { // Check if p is not released in case it couldn't be sent sendToPhone(packetPool.allocCopy(*p)); } } diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index ecac626a8..c0919bf5d 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -15,7 +15,7 @@ typedef uint32_t PacketId; // A packet sequence number #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -#define ERRNO_DISABLED 35 // the interface is disabled +#define ERRNO_DISABLED 34 // the interface is disabled /* * Source of a received message diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 6b1cf92e7..c91ce50c5 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -117,17 +117,20 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error - if ((c && (c->error_reason == meshtastic_Routing_Error_NONE || c->error_reason == meshtastic_Routing_Error_NONE_PKI)) || - !c) { - LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", p->decoded.request_id); - stopRetransmission(p->to, p->decoded.request_id); - // } else if (c && (c->error_reason == meshtastic_Routing_Error_NO_CHANNEL)) { - // noop? - } else if (c && - (c->error_reason != meshtastic_Routing_Error_NONE && c->error_reason != meshtastic_Routing_Error_NONE_PKI)) { + PacketId ackId = ((c && c->error_reason == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; - LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", p->decoded.request_id); - stopRetransmission(p->to, p->decoded.request_id); + // A nak is a routing packt that has an error code + PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0; + + // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records + if (ackId || nakId) { + if (ackId) { + LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId); + stopRetransmission(p->to, ackId); + } else { + LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId); + stopRetransmission(p->to, nakId); + } } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 64fa4cca2..945f92bb7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -258,7 +258,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p); auto encodeResult = perhapsEncode(p); - if (encodeResult != meshtastic_Routing_Error_NONE && encodeResult != meshtastic_Routing_Error_NONE_PKI) { + if (encodeResult != meshtastic_Routing_Error_NONE) { packetPool.release(p_decoded); abortSendAndNak(encodeResult, p); return encodeResult; // FIXME - this isn't a valid ErrorCode @@ -493,10 +493,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } - if (p->pki_encrypted) - return meshtastic_Routing_Error_NONE_PKI; - else - return meshtastic_Routing_Error_NONE; + + return meshtastic_Routing_Error_NONE; } NodeNum Router::getNodeNum() diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 2ec914668..4df5a03fc 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -1105,8 +1105,7 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket & this->incoming = service->getNodenumFromRequestId(mp.decoded.request_id); meshtastic_Routing decoded = meshtastic_Routing_init_default; pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); - this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE || - decoded.error_reason == meshtastic_Routing_Error_NONE_PKI; + this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; waitingForAck = false; // No longer want routing packets this->notifyObservers(&e); // run the next time 2 seconds later From 80fd121d87eac1dbb447828ada7c4a5122795832 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 12:10:46 -0500 Subject: [PATCH 132/305] Add meshtastic_Routing_Error_NO_CHANNEL --- src/mesh/MeshModule.cpp | 2 +- src/mesh/Router.cpp | 20 +++++++++++++++++--- src/mesh/generated/meshtastic/mesh.pb.h | 8 ++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 3fe4c85d5..3b137d4bd 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -54,7 +54,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; - if (err != meshtastic_Routing_Error_NONE && err != meshtastic_Routing_Error_NONE_PKI) + if (err != meshtastic_Routing_Error_NONE) LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); return p; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 945f92bb7..832d7b91f 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -461,9 +461,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it auto hash = channels.setActiveByIndex(chIndex); - if (hash < 0) - // No suitable channel could be found for sending - return meshtastic_Routing_Error_NO_CHANNEL; // Now that we are encrypting the packet channel should be the hash (no longer the index) p->channel = hash; @@ -480,11 +477,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) numbytes += 8; memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; + p->pki_encrypted = true; } else { + if (p->pki_encrypted == true) { + // Client specifically requested PKI encryption + return meshtastic_Routing_Error_PKI_FAILED; + } + if (hash < 0) { + // No suitable channel could be found for sending + return meshtastic_Routing_Error_NO_CHANNEL; + } crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); } #else + if (p->pki_encrypted == true) { + // Client specifically requested PKI encryption + return meshtastic_Routing_Error_PKI_FAILED; + } + if (hash < 0) { + // No suitable channel could be found for sending + return meshtastic_Routing_Error_NO_CHANNEL; + } crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); #endif diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 3800e6c0c..7625fbf92 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -301,8 +301,8 @@ typedef enum _meshtastic_Routing_Error { /* The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) */ meshtastic_Routing_Error_NOT_AUTHORIZED = 33, - /* This message is not a failure, and indicates that the message was sent via PKI */ - meshtastic_Routing_Error_NONE_PKI = 34 + /* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */ + meshtastic_Routing_Error_PKI_FAILED = 34 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -995,8 +995,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NONE_PKI -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NONE_PKI+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_FAILED +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_FAILED+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX From ff89dca5b39db03defbbfe4fd9e2ca5d6b9fe9b0 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 14:50:35 -0500 Subject: [PATCH 133/305] Add PKI indicator to printPacket --- src/mesh/RadioInterface.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 262d2d6a9..968b9d251 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -283,6 +283,9 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) if (s.want_response) out += DEBUG_PORT.mt_sprintf(" WANTRESP"); + if (p->pki_encrypted) + out += DEBUG_PORT.mt_sprintf(" PKI"); + if (s.source != 0) out += DEBUG_PORT.mt_sprintf(" source=%08x", s.source); From b528290fde246205d7888c9d276d66f0d8a4403d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 17:16:40 -0500 Subject: [PATCH 134/305] Failure returns PKI_FAILED message if client requested PKI --- src/mesh/Router.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 832d7b91f..015c2c809 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -415,6 +415,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) { concurrency::LockGuard g(cryptLock); + int16_t hash; + // If the packet is not yet encrypted, do so now if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); @@ -460,19 +462,20 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // printBytes("plaintext", bytes, numbytes); ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it - auto hash = channels.setActiveByIndex(chIndex); - // Now that we are encrypting the packet channel should be the hash (no longer the index) - p->channel = hash; #if !(MESHTASTIC_EXCLUDE_PKI) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); - if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && - numbytes <= MAX_RHPACKETLEN - 8 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && + if (!owner.is_licensed && config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr && + node->user.public_key.size > 0 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; + if (memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { + LOG_WARN("Client public key for client differs from requested!\n"); + return meshtastic_Routing_Error_PKI_FAILED; + } crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); numbytes += 8; memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); @@ -483,6 +486,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Client specifically requested PKI encryption return meshtastic_Routing_Error_PKI_FAILED; } + hash = channels.setActiveByIndex(chIndex); + + // Now that we are encrypting the packet channel should be the hash (no longer the index) + p->channel = hash; if (hash < 0) { // No suitable channel could be found for sending return meshtastic_Routing_Error_NO_CHANNEL; @@ -495,6 +502,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Client specifically requested PKI encryption return meshtastic_Routing_Error_PKI_FAILED; } + hash = channels.setActiveByIndex(chIndex); + + // Now that we are encrypting the packet channel should be the hash (no longer the index) + p->channel = hash; if (hash < 0) { // No suitable channel could be found for sending return meshtastic_Routing_Error_NO_CHANNEL; From 2661fc694f21c0692bc7d18f0b3e3d0ed5f4765d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 20:06:36 -0500 Subject: [PATCH 135/305] sync protobufs --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 6307b44cd..97fa34517 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 6307b44cd98ef1d86d5f39edefe9361e79d9ece4 +Subproject commit 97fa34517f80332a11046a73f26d55100fbee9e2 From 8ce1c07c4ea36bc53ede1c77219fe5d5fc4e3e61 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 22:34:21 -0500 Subject: [PATCH 136/305] Check for blank key coming from client --- src/mesh/Router.cpp | 6 ++++-- src/meshUtils.cpp | 9 +++++++++ src/meshUtils.h | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 015c2c809..a96773042 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -472,8 +472,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; - if (memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { - LOG_WARN("Client public key for client differs from requested!\n"); + if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && + memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { + LOG_WARN("Client public key for client differs from requested! Requested 0x%02x, but stored key begins 0x%02x\n", + *p->public_key.bytes, *node->user.public_key.bytes); return meshtastic_Routing_Error_PKI_FAILED; } crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index 86d237129..99fcd2a57 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -64,4 +64,13 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes) for (size_t i = 0; i < numbytes; i++) LOG_DEBUG("%02x ", p[i]); LOG_DEBUG("\n"); +} + +bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) +{ + for (int i = 0; i < numbytes; i++) { + if (mem[i] != find) + return false; + } + return true; } \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index e2d4188d7..ce063cb6a 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -14,4 +14,7 @@ template constexpr const T &clamp(const T &v, const T &lo, const T &hi char *strnstr(const char *s, const char *find, size_t slen); #endif -void printBytes(const char *label, const uint8_t *p, size_t numbytes); \ No newline at end of file +void printBytes(const char *label, const uint8_t *p, size_t numbytes); + +// is the memory region filled with a single character? +bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes); \ No newline at end of file From 207b9b49a575b02db6cf32cf7b808f753755b51f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 14 Aug 2024 07:42:30 -0500 Subject: [PATCH 137/305] Always attempt to set NTP or GPS time on a fresh position packet (#4460) --- src/modules/PositionModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 1618ba3ed..1756e8508 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -186,7 +186,7 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.longitude_i = localPosition.longitude_i; } p.precision_bits = precision; - p.time = localPosition.time; + p.time = getValidTime(RTCQualityNTP) > 0 ? getValidTime(RTCQualityNTP) : localPosition.time; if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) { if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) From 181325103a614f78cbd0806e136f81df35dd873d Mon Sep 17 00:00:00 2001 From: Christopher Hoover Date: Wed, 14 Aug 2024 05:51:32 -0700 Subject: [PATCH 138/305] Improves ignore messages in Router.cpp (#4442) Signed-off-by: Christopher Hoover . --- src/mesh/Router.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 79095805d..db968ce51 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -517,18 +517,26 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) } #endif // assert(radioConfig.has_preferences); - bool ignore = is_in_repeated(config.lora.ignore_incoming, p->from) || (config.lora.ignore_mqtt && p->via_mqtt); + if (is_in_repeated(config.lora.ignore_incoming, p->from)) { + LOG_DEBUG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from); + packetPool.release(p); + return; + } - if (ignore) { - LOG_DEBUG("Ignoring incoming message, 0x%x is in our ignore list or came via MQTT\n", p->from); - } else if (ignore |= shouldFilterReceived(p)) { - LOG_DEBUG("Incoming message was filtered 0x%x\n", p->from); + if (config.lora.ignore_mqtt && p->via_mqtt) { + LOG_DEBUG("Message came in via MQTT from 0x%x\n", p->from); + packetPool.release(p); + return; + } + + if (shouldFilterReceived(p)) { + LOG_DEBUG("Incoming message was filtered from 0x%x\n", p->from); + packetPool.release(p); + return; } // Note: we avoid calling shouldFilterReceived if we are supposed to ignore certain nodes - because some overrides might // cache/learn of the existence of nodes (i.e. FloodRouter) that they should not - if (!ignore) - handleReceived(p); - + handleReceived(p); packetPool.release(p); } From 837c4e9e7ba89efaeefca6bc6fb8cd1446f66f3e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:33:57 -0500 Subject: [PATCH 139/305] [create-pull-request] automated change (#4461) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index c9ca0dbe9..666b481ae 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c9ca0dbe9cc7105399e0486c07e0601f849b94af +Subproject commit 666b481ae02f1f88ec30f10a2d80184b31c0fc4e From efc27f205106f7caf7b1541b64770e98e8b40872 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 14 Aug 2024 16:24:28 -0500 Subject: [PATCH 140/305] Initial telemetry with time and variant tags (#4463) --- src/modules/Telemetry/DeviceTelemetry.cpp | 3 +-- src/modules/Telemetry/EnvironmentTelemetry.cpp | 2 ++ src/modules/Telemetry/PowerTelemetry.cpp | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 4bde73f41..4b4869341 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -80,9 +80,8 @@ meshtastic_MeshPacket *DeviceTelemetryModule::allocReply() meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() { meshtastic_Telemetry t = meshtastic_Telemetry_init_zero; - - t.time = getTime(); t.which_variant = meshtastic_Telemetry_device_metrics_tag; + t.time = getTime(); t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent(); #if ARCH_PORTDUINO t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index a100d1ef5..db56fb1a5 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -418,6 +418,8 @@ meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply() bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; + m.which_variant = meshtastic_Telemetry_environment_metrics_tag; + m.time = getTime(); #ifdef T1000X_SENSOR_EN if (t1000xSensor.getMetrics(&m)) { #else diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 90371780f..318acf456 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -222,6 +222,8 @@ meshtastic_MeshPacket *PowerTelemetryModule::allocReply() bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; + m.which_variant = meshtastic_Telemetry_power_metrics_tag; + m.time = getTime(); if (getPowerTelemetry(&m)) { LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, " "ch3_voltage=%f, ch3_current=%f\n", From 8ef72a5c080086d6ced5bd276ea1a098b16c62e3 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 14 Aug 2024 17:17:53 -0500 Subject: [PATCH 141/305] Shorter nodeinfo timeout redux (#4458) * Add shorterTimeout bool to sendOurNodeInfo * Respond to likely PKI decode errors with a quick nodeinfo * Protbufs * Move to PKI_UNKNOWN_PUBKEY for PKI decode error --- protobufs | 2 +- src/mesh/ReliableRouter.cpp | 14 +++++++++++++- src/mesh/generated/meshtastic/mesh.pb.h | 8 +++++--- src/modules/NodeInfoModule.cpp | 16 ++++++++++++---- src/modules/NodeInfoModule.h | 4 +++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/protobufs b/protobufs index 97fa34517..8b5b2faf6 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 97fa34517f80332a11046a73f26d55100fbee9e2 +Subproject commit 8b5b2faf662b364754809f923271022f4f1492ed diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index c91ce50c5..0c9180eb5 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -4,6 +4,7 @@ #include "MeshTypes.h" #include "configuration.h" #include "mesh-pb-constants.h" +#include "modules/NodeInfoModule.h" // ReliableRouter::ReliableRouter() {} @@ -109,13 +110,24 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n"); } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); + } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && + (nodeDB->getMeshNode(p->from) != nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { + // This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY + sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), + p->hop_start, p->hop_limit); } else { // Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, p->hop_limit); } } - + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c && + c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) { + if (owner.public_key.size == 32) { + LOG_INFO("This seems like a remote PKI decrypt failure, so send a NodeInfo"); + nodeInfoModule->sendOurNodeInfo(p->from, false, p->channel, true); + } + } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error PacketId ackId = ((c && c->error_reason == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 7625fbf92..1f0621f9a 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -302,7 +302,9 @@ typedef enum _meshtastic_Routing_Error { (i.e you did not send the request on the required bound channel) */ meshtastic_Routing_Error_NOT_AUTHORIZED = 33, /* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */ - meshtastic_Routing_Error_PKI_FAILED = 34 + meshtastic_Routing_Error_PKI_FAILED = 34, + /* The receiving node does not have a Public Key to decode with */ + meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -995,8 +997,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_FAILED -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_FAILED+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 62cf9d2a1..cb047a4dc 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -32,19 +32,22 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes return false; // Let others look at this message also if they want } -void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel) +void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel, bool _shorterTimeout) { // cancel any not yet sent (now stale) position packets if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal) service->cancelSending(prevPacketId); - + shorterTimeout = _shorterTimeout; meshtastic_MeshPacket *p = allocReply(); if (p) { // Check whether we didn't ignore it p->to = dest; p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER && config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && wantReplies; - p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; + if (_shorterTimeout) + p->priority = meshtastic_MeshPacket_Priority_DEFAULT; + else + p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; if (channel > 0) { LOG_DEBUG("sending ourNodeInfo to channel %d\n", channel); p->channel = channel; @@ -53,6 +56,7 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha prevPacketId = p->id; service->sendToMesh(p); + shorterTimeout = false; } } @@ -65,10 +69,14 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() } uint32_t now = millis(); // If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway. - if (lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { + if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; + } else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) { + LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n"); + ignoreRequest = true; // Mark it as ignored for MeshModule + return NULL; } else { ignoreRequest = false; // Don't ignore requests anymore meshtastic_User &u = owner; diff --git a/src/modules/NodeInfoModule.h b/src/modules/NodeInfoModule.h index b10cccdf1..c1fb9ccce 100644 --- a/src/modules/NodeInfoModule.h +++ b/src/modules/NodeInfoModule.h @@ -20,7 +20,8 @@ class NodeInfoModule : public ProtobufModule, private concurren /** * Send our NodeInfo into the mesh */ - void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0); + void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0, + bool _shorterTimeout = false); protected: /** Called to handle a particular incoming message @@ -38,6 +39,7 @@ class NodeInfoModule : public ProtobufModule, private concurren private: uint32_t lastSentToMesh = 0; // Last time we sent our NodeInfo to the mesh + bool shorterTimeout = false; }; extern NodeInfoModule *nodeInfoModule; From ced87596cb6f94cb24e42cb73490667daeb266b7 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 14 Aug 2024 19:32:45 -0500 Subject: [PATCH 142/305] Add PKI channel for MQTT (#4464) * Add PKI channel for MQTT --- src/mqtt/MQTT.cpp | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 4bb9cd5eb..1d2d9bca0 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -152,7 +152,8 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) LOG_INFO("Ignoring downlink message we originally sent.\n"); } else { // Find channel by channel_id and check downlink_enabled - if (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled) { + if ((strcmp(e.channel_id, "PKI") && e.packet) || + (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) { LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length); meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); p->via_mqtt = true; // Mark that the packet was received via MQTT @@ -161,8 +162,11 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) p->channel = ch.index; } + // PKI messages get accepted even if we can't decrypt + if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0) + router->enqueueReceivedMessage(p); // ignore messages if we don't have the channel key - if (router && perhapsDecode(p)) + else if (router && perhapsDecode(p)) router->enqueueReceivedMessage(p); else packetPool.release(p); @@ -377,6 +381,11 @@ void MQTT::sendSubscriptions() #endif // ARCH_NRF52 } } +#if !MESHTASTIC_EXCLUDE_PKI + std::string topic = cryptTopic + "PKI/#"; + LOG_INFO("Subscribing to %s\n", topic.c_str()); + pubSub.subscribe(topic.c_str(), 1); +#endif #endif } @@ -452,8 +461,12 @@ void MQTT::publishQueuedMessages() meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0); static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - - std::string topic = cryptTopic + env->channel_id + "/" + owner.id; + std::string topic; + if (env->packet->pki_encrypted) { + topic = cryptTopic + "PKI/" + owner.id; + } else { + topic = cryptTopic + env->channel_id + "/" + owner.id; + } LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes); publish(topic.c_str(), bytes, numBytes, false); @@ -463,7 +476,12 @@ void MQTT::publishQueuedMessages() // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); if (jsonString.length() != 0) { - std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id; + std::string topicJson; + if (env->packet->pki_encrypted) { + topicJson = jsonTopic + "PKI/" + owner.id; + } else { + topicJson = jsonTopic + env->channel_id + "/" + owner.id; + } LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str()); publish(topicJson.c_str(), jsonString.c_str(), false); @@ -513,8 +531,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - - std::string topic = cryptTopic + channelId + "/" + owner.id; + std::string topic; + if (mp.pki_encrypted) { + topic = cryptTopic + "PKI/" + owner.id; + } else { + topic = cryptTopic + channelId + "/" + owner.id; + } LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes); publish(topic.c_str(), bytes, numBytes, false); @@ -524,7 +546,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); if (jsonString.length() != 0) { - std::string topicJson = jsonTopic + channelId + "/" + owner.id; + std::string topicJson; + if (mp.pki_encrypted) { + topicJson = jsonTopic + "PKI/" + owner.id; + } else { + topicJson = jsonTopic + channelId + "/" + owner.id; + } LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str()); publish(topicJson.c_str(), jsonString.c_str(), false); From 96cf78aadd5e84f661a7942158dde0fe482c7729 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 14 Aug 2024 21:16:21 -0500 Subject: [PATCH 143/305] Short turbo preset (#4465) --- src/DisplayFormatters.cpp | 3 +++ src/mesh/RadioInterface.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/DisplayFormatters.cpp b/src/DisplayFormatters.cpp index f15052de6..0718ffcbd 100644 --- a/src/DisplayFormatters.cpp +++ b/src/DisplayFormatters.cpp @@ -3,6 +3,9 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName) { switch (preset) { + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: + return useShortName ? "ShortT" : "ShortTurbo"; + break; case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW: return useShortName ? "ShortS" : "ShortSlow"; break; diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 968b9d251..7fe02f291 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -415,6 +415,11 @@ void RadioInterface::applyModemConfig() if (loraConfig.use_preset) { switch (loraConfig.modem_preset) { + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: + bw = (myRegion->wideLora) ? 812.5 : 500; + cr = 5; + sf = 7; + break; case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST: bw = (myRegion->wideLora) ? 812.5 : 250; cr = 5; From d398419aef5303c7121d551589d8dcaa470d2b10 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 15 Aug 2024 08:47:49 -0500 Subject: [PATCH 144/305] Router and sensor are impolite (#4468) --- src/modules/Telemetry/DeviceTelemetry.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 4b4869341..1104e6c4a 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -16,12 +16,14 @@ int32_t DeviceTelemetryModule::runOnce() { refreshUptime(); + bool isImpoliteRole = config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR || + config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER; if (((lastSentToMesh == 0) || ((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval, default_telemetry_broadcast_interval_secs, numOnlineNodes))) && - airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && - airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && + airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() && + config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { sendTelemetry(); lastSentToMesh = uptimeLastMs; From 6f1dae1b1b0eeda18217c13fd7ea06dd7b24e642 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 15 Aug 2024 15:05:38 -0500 Subject: [PATCH 145/305] Re-compute correct timeslot on applyModemConfig (#4469) * Re-compute correct timeslot on applyModemConfig * Cap contention window max at 7 --- src/mesh/RadioInterface.cpp | 1 + src/mesh/RadioInterface.h | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 7fe02f291..104ddbd1d 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -520,6 +520,7 @@ void RadioInterface::applyModemConfig() saveChannelNum(channel_num); saveFreq(freq + loraConfig.frequency_offset); + slotTimeMsec = computeSlotTimeMsec(bw, sf); preambleTimeMsec = getPacketTime((uint32_t)0); maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader)); diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index b965328e4..1f2ec9bab 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -71,18 +71,20 @@ class RadioInterface - roundtrip air propagation time (assuming max. 30km between nodes); - Tx/Rx turnaround time (maximum of SX126x and SX127x); - MAC processing time (measured on T-beam) */ - uint32_t slotTimeMsec = 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7; + uint32_t slotTimeMsec = computeSlotTimeMsec(bw, sf); uint16_t preambleLength = 16; // 8 is default, but we use longer to increase the amount of sleep time when receiving uint32_t preambleTimeMsec = 165; // calculated on startup, this is the default for LongFast uint32_t maxPacketTimeMsec = 3246; // calculated on startup, this is the default for LongFast const uint32_t PROCESSING_TIME_MSEC = 4500; // time to construct, process and construct a packet again (empirically determined) const uint8_t CWmin = 2; // minimum CWsize - const uint8_t CWmax = 8; // maximum CWsize + const uint8_t CWmax = 7; // maximum CWsize meshtastic_MeshPacket *sendingPacket = NULL; // The packet we are currently sending uint32_t lastTxStart = 0L; + uint32_t computeSlotTimeMsec(float bw, float sf) { return 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7; } + /** * A temporary buffer used for sending/receiving packets, sized to hold the biggest buffer we might need * */ From 85176756ec9c148a28e76d94e58477d4b0bac340 Mon Sep 17 00:00:00 2001 From: Christopher Hoover Date: Thu, 15 Aug 2024 17:09:06 -0700 Subject: [PATCH 146/305] Adds ASCII log option needed by portudino (#4443) * Adds ASCII logs useful to portudino Activates ASCII log option when stdout is not a terminal. This is generally the right thing to do; if not, the behavior can be overridden in config.yaml using AsciiLogs under Logging. The result is reasonable system logs for portudino when running under systemd or the like. Signed-off-by: Christopher Hoover --- bin/config-dist.yaml | 3 +- src/DebugConfiguration.cpp | 16 +++++- src/RedirectablePrint.cpp | 70 +++++++++++++++++------- src/platform/portduino/PortduinoGlue.cpp | 5 ++ src/platform/portduino/PortduinoGlue.h | 5 +- 5 files changed, 76 insertions(+), 23 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 1cd219c4c..5590ed3b0 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -145,6 +145,7 @@ Input: Logging: LogLevel: info # debug, info, warn, error # TraceFile: /var/log/meshtasticd.json +# AsciiLogs: true # default if not specified is !isatty() on stdout Webserver: # Port: 443 # Port for Webserver & Webservices @@ -152,4 +153,4 @@ Webserver: General: MaxNodes: 200 - MaxMessageQueue: 100 \ No newline at end of file + MaxMessageQueue: 100 diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp index d58856c4d..23b140daf 100644 --- a/src/DebugConfiguration.cpp +++ b/src/DebugConfiguration.cpp @@ -26,6 +26,10 @@ SOFTWARE.*/ #include "DebugConfiguration.h" +#ifdef ARCH_PORTDUINO +#include "platform/portduino/PortduinoGlue.h" +#endif + /// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic extern "C" void logLegacy(const char *level, const char *fmt, ...) { @@ -139,6 +143,11 @@ bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list a inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message) { int result; +#ifdef ARCH_PORTDUINO + bool utf = !settingsMap[ascii_logs]; +#else + bool utf = true; +#endif if (!this->_enabled) return false; @@ -169,7 +178,12 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess this->_client->print(this->_deviceHostname); this->_client->print(' '); this->_client->print(appName); - this->_client->print(F(" - - - \xEF\xBB\xBF")); + this->_client->print(F(" - - - ")); + if (utf) { + this->_client->print(F("\xEF\xBB\xBF")); + } else { + this->_client->print(F(" ")); + } this->_client->print(F("[")); this->_client->print(int(millis() / 1000)); this->_client->print(F("]: ")); diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 05d349de9..c2b0c55db 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -55,6 +55,12 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l static char printBuf[160]; #endif +#ifdef ARCH_PORTDUINO + bool color = !settingsMap[ascii_logs]; +#else + bool color = true; +#endif + va_copy(copy, arg); size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy); va_end(copy); @@ -70,7 +76,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l if (!std::isprint(static_cast(printBuf[f])) && printBuf[f] != '\n') printBuf[f] = '#'; } - if (logLevel != nullptr) { + if (color && logLevel != nullptr) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) Print::write("\u001b[34m", 6); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) @@ -81,7 +87,9 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l Print::write("\u001b[31m", 6); } len = Print::write(printBuf, len); - Print::write("\u001b[0m", 5); + if (color && logLevel != nullptr) { + Print::write("\u001b[0m", 5); + } return len; } @@ -91,19 +99,27 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, // Cope with 0 len format strings, but look for new line terminator bool hasNewline = *format && format[strlen(format) - 1] == '\n'; +#ifdef ARCH_PORTDUINO + bool color = !settingsMap[ascii_logs]; +#else + bool color = true; +#endif // If we are the first message on a report, include the header if (!isContinuationMessage) { - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) - Print::write("\u001b[35m", 6); + if (color) { + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) + Print::write("\u001b[34m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) + Print::write("\u001b[32m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) + Print::write("\u001b[33m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) + Print::write("\u001b[31m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) + Print::write("\u001b[35m", 6); + } + uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile if (rtc_sec > 0) { long hms = rtc_sec % SEC_PER_DAY; @@ -117,17 +133,33 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN #ifdef ARCH_PORTDUINO - ::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000); + ::printf("%s ", logLevel); + if (color) { + ::printf("\u001b[0m"); + } + ::printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); #else - printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000); + printf("%s ", logLevel); + if (color) { + printf("\u001b[0m"); + } + printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); #endif - } else + } else { #ifdef ARCH_PORTDUINO - ::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000); + ::printf("%s ", logLevel); + if (color) { + ::printf("\u001b[0m"); + } + ::printf("| ??:??:?? %u ", millis() / 1000); #else - printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000); + printf("%s ", logLevel); + if (color) { + printf("\u001b[0m"); + } + printf("| ??:??:?? %u ", millis() / 1000); #endif - + } auto thread = concurrency::OSThread::currentThread; if (thread) { print("["); @@ -350,4 +382,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) break; } return std::string(formatted.get()); -} \ No newline at end of file +} diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index f8dd79b20..3532d2e79 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -99,6 +99,7 @@ void portduinoSetup() settingsStrings[spidev] = ""; settingsStrings[displayspidev] = ""; settingsMap[spiSpeed] = 2000000; + settingsMap[ascii_logs] = !isatty(1); YAML::Node yamlConfig; @@ -152,6 +153,10 @@ void portduinoSetup() settingsMap[logoutputlevel] = level_error; } settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as(""); + if (yamlConfig["Logging"]["AsciiLogs"]) { + // Default is !isatty(1) but can be set explicitly in config.yaml + settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as(); + } } if (yamlConfig["Lora"]) { settingsMap[use_sx1262] = false; diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 0c81b8686..3fee1db40 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -53,7 +53,8 @@ enum configNames { webserverport, webserverrootpath, maxtophone, - maxnodes + maxnodes, + ascii_logs }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; @@ -62,4 +63,4 @@ enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; -int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file +int initGPIOPin(int pinNum, std::string gpioChipname); From 390de724ba1834e34a05d5918f2cc13f03f9c42d Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 16 Aug 2024 06:09:02 -0500 Subject: [PATCH 147/305] Update 2.5 protos --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 15 ++++-- .../generated/meshtastic/telemetry.pb.cpp | 3 ++ src/mesh/generated/meshtastic/telemetry.pb.h | 53 ++++++++++++++++++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/protobufs b/protobufs index 8b5b2faf6..4eb4f4251 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 8b5b2faf662b364754809f923271022f4f1492ed +Subproject commit 4eb4f425170f08839abc6ececd13e8db30094ad5 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index 13093839d..b4971991a 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -87,6 +87,7 @@ typedef struct _meshtastic_NodeRemoteHardwarePinsResponse { meshtastic_NodeRemoteHardwarePin node_remote_hardware_pins[16]; } meshtastic_NodeRemoteHardwarePinsResponse; +typedef PB_BYTES_ARRAY_T(8) meshtastic_AdminMessage_session_passkey_t; /* This message is handled by the Admin module and is responsible for all settings/channel read/write operations. This message is used to do settings operations to both remote AND local nodes. (Prior to 1.2 these operations were done via special ToRadio operations) */ @@ -187,6 +188,10 @@ typedef struct _meshtastic_AdminMessage { /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; + /* The node generates this key and sends it with any get_x_response packets. + The client MUST include the same key with any set_x commands. Key expires after 300 seconds. + Prevents replay attacks for admin messages. */ + meshtastic_AdminMessage_session_passkey_t session_passkey; } meshtastic_AdminMessage; @@ -210,10 +215,10 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_AdminMessage_init_default {0, {0}} +#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}} #define meshtastic_HamParameters_init_default {"", 0, 0, ""} #define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}} -#define meshtastic_AdminMessage_init_zero {0, {0}} +#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}} #define meshtastic_HamParameters_init_zero {"", 0, 0, ""} #define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}} @@ -265,6 +270,7 @@ extern "C" { #define meshtastic_AdminMessage_shutdown_seconds_tag 98 #define meshtastic_AdminMessage_factory_reset_config_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 +#define meshtastic_AdminMessage_session_passkey_tag 101 /* Struct field encoding specification for nanopb */ #define meshtastic_AdminMessage_FIELDLIST(X, a) \ @@ -309,7 +315,8 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulato X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) +X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) \ +X(a, STATIC, SINGULAR, BYTES, session_passkey, 101) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL #define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel @@ -351,7 +358,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size -#define meshtastic_AdminMessage_size 500 +#define meshtastic_AdminMessage_size 511 #define meshtastic_HamParameters_size 31 #define meshtastic_NodeRemoteHardwarePinsResponse_size 496 diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp index c93483a15..90859c98e 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.cpp +++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp @@ -18,6 +18,9 @@ PB_BIND(meshtastic_PowerMetrics, meshtastic_PowerMetrics, AUTO) PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO) +PB_BIND(meshtastic_LocalStats, meshtastic_LocalStats, AUTO) + + PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO) diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 60681916f..2d3eb407a 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -211,6 +211,26 @@ typedef struct _meshtastic_AirQualityMetrics { uint32_t particles_100um; } meshtastic_AirQualityMetrics; +/* Local device mesh statistics */ +typedef struct _meshtastic_LocalStats { + /* How long the device has been running since the last reboot (in seconds) */ + uint32_t uptime_seconds; + /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */ + float channel_utilization; + /* Percent of airtime for transmission used within the last hour. */ + float air_util_tx; + /* Number of packets sent */ + uint32_t num_packets_tx; + /* Number of packets received good */ + uint32_t num_packets_rx; + /* Number of packets received that are malformed or violate the protocol */ + uint32_t num_packets_rx_bad; + /* Number of nodes online (in the past 2 hours) */ + uint16_t num_online_nodes; + /* Number of nodes total */ + uint16_t num_total_nodes; +} meshtastic_LocalStats; + /* Types of Measurements the telemetry module is equipped to handle */ typedef struct _meshtastic_Telemetry { /* Seconds since 1970 - or 0 for unknown/unset */ @@ -225,6 +245,8 @@ typedef struct _meshtastic_Telemetry { meshtastic_AirQualityMetrics air_quality_metrics; /* Power Metrics */ meshtastic_PowerMetrics power_metrics; + /* Local device mesh statistics */ + meshtastic_LocalStats local_stats; } variant; } meshtastic_Telemetry; @@ -253,17 +275,20 @@ extern "C" { + /* Initializer values for message structs */ #define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} #define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Nau7802Config_init_zero {0, 0} @@ -308,11 +333,20 @@ extern "C" { #define meshtastic_AirQualityMetrics_particles_25um_tag 10 #define meshtastic_AirQualityMetrics_particles_50um_tag 11 #define meshtastic_AirQualityMetrics_particles_100um_tag 12 +#define meshtastic_LocalStats_uptime_seconds_tag 1 +#define meshtastic_LocalStats_channel_utilization_tag 2 +#define meshtastic_LocalStats_air_util_tx_tag 3 +#define meshtastic_LocalStats_num_packets_tx_tag 4 +#define meshtastic_LocalStats_num_packets_rx_tag 5 +#define meshtastic_LocalStats_num_packets_rx_bad_tag 6 +#define meshtastic_LocalStats_num_online_nodes_tag 7 +#define meshtastic_LocalStats_num_total_nodes_tag 8 #define meshtastic_Telemetry_time_tag 1 #define meshtastic_Telemetry_device_metrics_tag 2 #define meshtastic_Telemetry_environment_metrics_tag 3 #define meshtastic_Telemetry_air_quality_metrics_tag 4 #define meshtastic_Telemetry_power_metrics_tag 5 +#define meshtastic_Telemetry_local_stats_tag 6 #define meshtastic_Nau7802Config_zeroOffset_tag 1 #define meshtastic_Nau7802Config_calibrationFactor_tag 2 @@ -373,18 +407,32 @@ X(a, STATIC, OPTIONAL, UINT32, particles_100um, 12) #define meshtastic_AirQualityMetrics_CALLBACK NULL #define meshtastic_AirQualityMetrics_DEFAULT NULL +#define meshtastic_LocalStats_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 1) \ +X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 2) \ +X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 3) \ +X(a, STATIC, SINGULAR, UINT32, num_packets_tx, 4) \ +X(a, STATIC, SINGULAR, UINT32, num_packets_rx, 5) \ +X(a, STATIC, SINGULAR, UINT32, num_packets_rx_bad, 6) \ +X(a, STATIC, SINGULAR, UINT32, num_online_nodes, 7) \ +X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8) +#define meshtastic_LocalStats_CALLBACK NULL +#define meshtastic_LocalStats_DEFAULT NULL + #define meshtastic_Telemetry_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, FIXED32, time, 1) \ X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \ X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) \ X(a, STATIC, ONEOF, MESSAGE, (variant,air_quality_metrics,variant.air_quality_metrics), 4) \ -X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5) +X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6) #define meshtastic_Telemetry_CALLBACK NULL #define meshtastic_Telemetry_DEFAULT NULL #define meshtastic_Telemetry_variant_device_metrics_MSGTYPE meshtastic_DeviceMetrics #define meshtastic_Telemetry_variant_environment_metrics_MSGTYPE meshtastic_EnvironmentMetrics #define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics #define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics +#define meshtastic_Telemetry_variant_local_stats_MSGTYPE meshtastic_LocalStats #define meshtastic_Nau7802Config_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \ @@ -396,6 +444,7 @@ extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg; extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg; extern const pb_msgdesc_t meshtastic_PowerMetrics_msg; extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg; +extern const pb_msgdesc_t meshtastic_LocalStats_msg; extern const pb_msgdesc_t meshtastic_Telemetry_msg; extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; @@ -404,6 +453,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define meshtastic_EnvironmentMetrics_fields &meshtastic_EnvironmentMetrics_msg #define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg #define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg +#define meshtastic_LocalStats_fields &meshtastic_LocalStats_msg #define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg #define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg @@ -412,6 +462,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define meshtastic_AirQualityMetrics_size 72 #define meshtastic_DeviceMetrics_size 27 #define meshtastic_EnvironmentMetrics_size 85 +#define meshtastic_LocalStats_size 42 #define meshtastic_Nau7802Config_size 16 #define meshtastic_PowerMetrics_size 30 #define meshtastic_Telemetry_size 92 From eefe9efa9f11675792bd38a55db011b1aa3792d3 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 16 Aug 2024 07:42:19 -0500 Subject: [PATCH 148/305] Adds ASCII log option needed by portudino (#4443) (#4474) * Adds ASCII logs useful to portudino Activates ASCII log option when stdout is not a terminal. This is generally the right thing to do; if not, the behavior can be overridden in config.yaml using AsciiLogs under Logging. The result is reasonable system logs for portudino when running under systemd or the like. Signed-off-by: Christopher Hoover Co-authored-by: Christopher Hoover --- bin/config-dist.yaml | 3 +- src/DebugConfiguration.cpp | 16 +++++- src/RedirectablePrint.cpp | 70 +++++++++++++++++------- src/platform/portduino/PortduinoGlue.cpp | 5 ++ src/platform/portduino/PortduinoGlue.h | 5 +- 5 files changed, 76 insertions(+), 23 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 1cd219c4c..5590ed3b0 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -145,6 +145,7 @@ Input: Logging: LogLevel: info # debug, info, warn, error # TraceFile: /var/log/meshtasticd.json +# AsciiLogs: true # default if not specified is !isatty() on stdout Webserver: # Port: 443 # Port for Webserver & Webservices @@ -152,4 +153,4 @@ Webserver: General: MaxNodes: 200 - MaxMessageQueue: 100 \ No newline at end of file + MaxMessageQueue: 100 diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp index d58856c4d..23b140daf 100644 --- a/src/DebugConfiguration.cpp +++ b/src/DebugConfiguration.cpp @@ -26,6 +26,10 @@ SOFTWARE.*/ #include "DebugConfiguration.h" +#ifdef ARCH_PORTDUINO +#include "platform/portduino/PortduinoGlue.h" +#endif + /// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic extern "C" void logLegacy(const char *level, const char *fmt, ...) { @@ -139,6 +143,11 @@ bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list a inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message) { int result; +#ifdef ARCH_PORTDUINO + bool utf = !settingsMap[ascii_logs]; +#else + bool utf = true; +#endif if (!this->_enabled) return false; @@ -169,7 +178,12 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess this->_client->print(this->_deviceHostname); this->_client->print(' '); this->_client->print(appName); - this->_client->print(F(" - - - \xEF\xBB\xBF")); + this->_client->print(F(" - - - ")); + if (utf) { + this->_client->print(F("\xEF\xBB\xBF")); + } else { + this->_client->print(F(" ")); + } this->_client->print(F("[")); this->_client->print(int(millis() / 1000)); this->_client->print(F("]: ")); diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 0eab0de0a..96cf851e4 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -56,6 +56,12 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l static char printBuf[160]; #endif +#ifdef ARCH_PORTDUINO + bool color = !settingsMap[ascii_logs]; +#else + bool color = true; +#endif + va_copy(copy, arg); size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy); va_end(copy); @@ -71,7 +77,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l if (!std::isprint(static_cast(printBuf[f])) && printBuf[f] != '\n') printBuf[f] = '#'; } - if (logLevel != nullptr) { + if (color && logLevel != nullptr) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) Print::write("\u001b[34m", 6); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) @@ -82,7 +88,9 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l Print::write("\u001b[31m", 6); } len = Print::write(printBuf, len); - Print::write("\u001b[0m", 5); + if (color && logLevel != nullptr) { + Print::write("\u001b[0m", 5); + } return len; } @@ -92,19 +100,27 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, // Cope with 0 len format strings, but look for new line terminator bool hasNewline = *format && format[strlen(format) - 1] == '\n'; +#ifdef ARCH_PORTDUINO + bool color = !settingsMap[ascii_logs]; +#else + bool color = true; +#endif // If we are the first message on a report, include the header if (!isContinuationMessage) { - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) - Print::write("\u001b[35m", 6); + if (color) { + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) + Print::write("\u001b[34m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) + Print::write("\u001b[32m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) + Print::write("\u001b[33m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) + Print::write("\u001b[31m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) + Print::write("\u001b[35m", 6); + } + uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile if (rtc_sec > 0) { long hms = rtc_sec % SEC_PER_DAY; @@ -118,17 +134,33 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN #ifdef ARCH_PORTDUINO - ::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000); + ::printf("%s ", logLevel); + if (color) { + ::printf("\u001b[0m"); + } + ::printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); #else - printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000); + printf("%s ", logLevel); + if (color) { + printf("\u001b[0m"); + } + printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); #endif - } else + } else { #ifdef ARCH_PORTDUINO - ::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000); + ::printf("%s ", logLevel); + if (color) { + ::printf("\u001b[0m"); + } + ::printf("| ??:??:?? %u ", millis() / 1000); #else - printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000); + printf("%s ", logLevel); + if (color) { + printf("\u001b[0m"); + } + printf("| ??:??:?? %u ", millis() / 1000); #endif - + } auto thread = concurrency::OSThread::currentThread; if (thread) { print("["); @@ -351,4 +383,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) break; } return std::string(formatted.get()); -} \ No newline at end of file +} diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index f8dd79b20..3532d2e79 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -99,6 +99,7 @@ void portduinoSetup() settingsStrings[spidev] = ""; settingsStrings[displayspidev] = ""; settingsMap[spiSpeed] = 2000000; + settingsMap[ascii_logs] = !isatty(1); YAML::Node yamlConfig; @@ -152,6 +153,10 @@ void portduinoSetup() settingsMap[logoutputlevel] = level_error; } settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as(""); + if (yamlConfig["Logging"]["AsciiLogs"]) { + // Default is !isatty(1) but can be set explicitly in config.yaml + settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as(); + } } if (yamlConfig["Lora"]) { settingsMap[use_sx1262] = false; diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 0c81b8686..3fee1db40 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -53,7 +53,8 @@ enum configNames { webserverport, webserverrootpath, maxtophone, - maxnodes + maxnodes, + ascii_logs }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; @@ -62,4 +63,4 @@ enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; -int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file +int initGPIOPin(int pinNum, std::string gpioChipname); From b0c1b7b7b5fd295161164e9696673e482160941f Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 16 Aug 2024 10:10:08 -0500 Subject: [PATCH 149/305] MQTT PKI fixes --- src/mqtt/MQTT.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 1d2d9bca0..d3a2bb92c 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -152,7 +152,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) LOG_INFO("Ignoring downlink message we originally sent.\n"); } else { // Find channel by channel_id and check downlink_enabled - if ((strcmp(e.channel_id, "PKI") && e.packet) || + if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) || (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) { LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length); meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); @@ -163,7 +163,8 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) } // PKI messages get accepted even if we can't decrypt - if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0) + if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && + strcmp(e.channel_id, "PKI") == 0) router->enqueueReceivedMessage(p); // ignore messages if we don't have the channel key else if (router && perhapsDecode(p)) @@ -365,10 +366,12 @@ void MQTT::reconnect() void MQTT::sendSubscriptions() { #if HAS_NETWORKING + bool hasDownlink = false; size_t numChan = channels.getNumChannels(); for (size_t i = 0; i < numChan; i++) { const auto &ch = channels.getByIndex(i); if (ch.settings.downlink_enabled) { + hasDownlink = true; std::string topic = cryptTopic + channels.getGlobalId(i) + "/#"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? @@ -382,9 +385,11 @@ void MQTT::sendSubscriptions() } } #if !MESHTASTIC_EXCLUDE_PKI - std::string topic = cryptTopic + "PKI/#"; - LOG_INFO("Subscribing to %s\n", topic.c_str()); - pubSub.subscribe(topic.c_str(), 1); + if (hasDownlink) { + std::string topic = cryptTopic + "PKI/#"; + LOG_INFO("Subscribing to %s\n", topic.c_str()); + pubSub.subscribe(topic.c_str(), 1); + } #endif #endif } @@ -496,7 +501,13 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & { if (mp.via_mqtt) return; // Don't send messages that came from MQTT back into MQTT - + bool uplinkEnabled = false; + for (int i = 0; i <= 7; i++) { + if (channels.getByIndex(i).settings.uplink_enabled) + uplinkEnabled = true; + } + if (!uplinkEnabled) + return; // no channels have an uplink enabled auto &ch = channels.getByIndex(chIndex); if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { @@ -511,7 +522,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & return; } - if (ch.settings.uplink_enabled) { + if (ch.settings.uplink_enabled || mp.pki_encrypted) { const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed(); From e61bd841169b38ba3049b2642fd080bb5750f553 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 16 Aug 2024 17:15:51 -0500 Subject: [PATCH 150/305] Send local stats telemetry to phone every 15 minutes (#4475) * Send local stats telemetry to phone every 10 minutes * Add debug log and bump to 15 minutes * Tronk * Explicit has_ optional --- src/mesh/RadioLibInterface.h | 10 ++-- src/modules/Telemetry/DeviceTelemetry.cpp | 46 +++++++++++++++++++ src/modules/Telemetry/DeviceTelemetry.h | 5 +- .../Telemetry/EnvironmentTelemetry.cpp | 1 + src/modules/Telemetry/Sensor/AHT10.cpp | 3 ++ src/modules/Telemetry/Sensor/BME280Sensor.cpp | 4 ++ src/modules/Telemetry/Sensor/BME680Sensor.cpp | 7 +++ src/modules/Telemetry/Sensor/BMP085Sensor.cpp | 3 ++ src/modules/Telemetry/Sensor/BMP280Sensor.cpp | 3 ++ .../Telemetry/Sensor/DFRobotLarkSensor.cpp | 6 +++ src/modules/Telemetry/Sensor/INA219Sensor.cpp | 3 ++ src/modules/Telemetry/Sensor/INA260Sensor.cpp | 3 ++ .../Telemetry/Sensor/INA3221Sensor.cpp | 10 ++++ .../Telemetry/Sensor/LPS22HBSensor.cpp | 3 ++ .../Telemetry/Sensor/MCP9808Sensor.cpp | 2 + .../Telemetry/Sensor/MLX90632Sensor.cpp | 1 + .../Telemetry/Sensor/NAU7802Sensor.cpp | 1 + .../Telemetry/Sensor/OPT3001Sensor.cpp | 1 + .../Telemetry/Sensor/RCWL9620Sensor.cpp | 1 + src/modules/Telemetry/Sensor/SHT31Sensor.cpp | 2 + src/modules/Telemetry/Sensor/SHT4XSensor.cpp | 3 ++ src/modules/Telemetry/Sensor/SHTC3Sensor.cpp | 3 ++ src/modules/Telemetry/Sensor/T1000xSensor.cpp | 3 ++ .../Telemetry/Sensor/TSL2591Sensor.cpp | 1 + .../Telemetry/Sensor/VEML7700Sensor.cpp | 3 ++ 25 files changed, 122 insertions(+), 6 deletions(-) diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index dd01d2037..edcbb394f 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -61,11 +61,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified */ static void isrTxLevel0(), isrLevel0Common(PendingISR code); - /** - * Debugging counts - */ - uint32_t rxBad = 0, rxGood = 0, txGood = 0; - MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE); protected: @@ -109,6 +104,11 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified */ virtual void enableInterrupt(void (*)()) = 0; + /** + * Debugging counts + */ + uint32_t rxBad = 0, rxGood = 0, txGood = 0; + public: RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, PhysicalLayer *iface = NULL); diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 1104e6c4a..f22685d43 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -5,6 +5,7 @@ #include "NodeDB.h" #include "PowerFSM.h" #include "RTC.h" +#include "RadioLibInterface.h" #include "Router.h" #include "configuration.h" #include "main.h" @@ -31,6 +32,10 @@ int32_t DeviceTelemetryModule::runOnce() // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); + if (lastSentStatsToPhone == 0 || (uptimeLastMs - lastSentStatsToPhone) >= sendStatsToPhoneIntervalMs) { + sendLocalStatsToPhone(); + lastSentStatsToPhone = uptimeLastMs; + } } return sendToPhoneIntervalMs; } @@ -84,6 +89,13 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() meshtastic_Telemetry t = meshtastic_Telemetry_init_zero; t.which_variant = meshtastic_Telemetry_device_metrics_tag; t.time = getTime(); + t.variant.device_metrics = meshtastic_DeviceMetrics_init_zero; + t.variant.device_metrics.has_air_util_tx = true; + t.variant.device_metrics.has_battery_level = true; + t.variant.device_metrics.has_channel_utilization = true; + t.variant.device_metrics.has_voltage = true; + t.variant.device_metrics.has_uptime_seconds = true; + t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent(); #if ARCH_PORTDUINO t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL; @@ -98,6 +110,40 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() return t; } +void DeviceTelemetryModule::sendLocalStatsToPhone() +{ + meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero; + telemetry.which_variant = meshtastic_Telemetry_local_stats_tag; + telemetry.variant.local_stats = meshtastic_LocalStats_init_zero; + telemetry.time = getTime(); + telemetry.variant.local_stats.uptime_seconds = getUptimeSeconds(); + telemetry.variant.local_stats.channel_utilization = airTime->channelUtilizationPercent(); + telemetry.variant.local_stats.air_util_tx = airTime->utilizationTXPercent(); + telemetry.variant.local_stats.num_online_nodes = numOnlineNodes; + telemetry.variant.local_stats.num_total_nodes = nodeDB->getNumMeshNodes(); + if (RadioLibInterface::instance) { + telemetry.variant.local_stats.num_packets_tx = RadioLibInterface::instance->txGood; + telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood; + telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad; + } + + LOG_INFO( + "(Sending local stats): uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i\n", + telemetry.variant.local_stats.uptime_seconds, telemetry.variant.local_stats.channel_utilization, + telemetry.variant.local_stats.air_util_tx, telemetry.variant.local_stats.num_online_nodes, + telemetry.variant.local_stats.num_total_nodes); + + LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i\n", telemetry.variant.local_stats.num_packets_tx, + telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad); + + meshtastic_MeshPacket *p = allocDataProtobuf(telemetry); + p->to = NODENUM_BROADCAST; + p->decoded.want_response = false; + p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; + + service->sendToPhone(p); +} + bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry telemetry = getDeviceTelemetry(); diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h index baaf59f28..6d7f69891 100644 --- a/src/modules/Telemetry/DeviceTelemetry.h +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -42,7 +42,10 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu private: meshtastic_Telemetry getDeviceTelemetry(); - uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute + void sendLocalStatsToPhone(); + uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute + uint32_t sendStatsToPhoneIntervalMs = 15 * SECONDS_IN_MINUTE * 1000; // Send stats to phone every 15 minutes + uint32_t lastSentStatsToPhone = 0; uint32_t lastSentToMesh = 0; void refreshUptime() diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index db56fb1a5..4755a5be5 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -289,6 +289,7 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m bool hasSensor = false; m->time = getTime(); m->which_variant = meshtastic_Telemetry_environment_metrics_tag; + m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero; #ifdef T1000X_SENSOR_EN // add by WayenWeng valid = valid && t1000xSensor.getMetrics(m); diff --git a/src/modules/Telemetry/Sensor/AHT10.cpp b/src/modules/Telemetry/Sensor/AHT10.cpp index a5212b39b..f9e8ba18a 100644 --- a/src/modules/Telemetry/Sensor/AHT10.cpp +++ b/src/modules/Telemetry/Sensor/AHT10.cpp @@ -32,6 +32,9 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement) sensors_event_t humidity, temp; aht10.getEvent(&humidity, &temp); + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + measurement->variant.environment_metrics.temperature = temp.temperature; measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity; diff --git a/src/modules/Telemetry/Sensor/BME280Sensor.cpp b/src/modules/Telemetry/Sensor/BME280Sensor.cpp index aea6f2c3d..55bc16744 100644 --- a/src/modules/Telemetry/Sensor/BME280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME280Sensor.cpp @@ -31,6 +31,10 @@ void BME280Sensor::setup() {} bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + LOG_DEBUG("BME280Sensor::getMetrics\n"); bme280.takeForcedMeasurement(); measurement->variant.environment_metrics.temperature = bme280.readTemperature(); diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp index 411cbbf69..328ec827d 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp @@ -54,6 +54,13 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement) { if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0) return false; + + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + measurement->variant.environment_metrics.has_gas_resistance = true; + measurement->variant.environment_metrics.has_iaq = true; + measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal; measurement->variant.environment_metrics.relative_humidity = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal; diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp index 0c4d0b5ca..15951126f 100644 --- a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp @@ -26,6 +26,9 @@ void BMP085Sensor::setup() {} bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + LOG_DEBUG("BMP085Sensor::getMetrics\n"); measurement->variant.environment_metrics.temperature = bmp085.readTemperature(); measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F; diff --git a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp index 8d0e4c180..6b0743d75 100644 --- a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp @@ -31,6 +31,9 @@ void BMP280Sensor::setup() {} bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + LOG_DEBUG("BMP280Sensor::getMetrics\n"); bmp280.takeForcedMeasurement(); measurement->variant.environment_metrics.temperature = bmp280.readTemperature(); diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp index 830552023..4b01eb444 100644 --- a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp +++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp @@ -35,6 +35,12 @@ void DFRobotLarkSensor::setup() {} bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + measurement->variant.environment_metrics.has_wind_speed = true; + measurement->variant.environment_metrics.has_wind_direction = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat(); measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat(); measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat(); diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp index 040e59575..f70d3705e 100644 --- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp @@ -32,6 +32,9 @@ void INA219Sensor::setup() {} bool INA219Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_voltage = true; + measurement->variant.environment_metrics.has_current = true; + measurement->variant.environment_metrics.voltage = ina219.getBusVoltage_V(); measurement->variant.environment_metrics.current = ina219.getCurrent_mA() * INA219_MULTIPLIER; return true; diff --git a/src/modules/Telemetry/Sensor/INA260Sensor.cpp b/src/modules/Telemetry/Sensor/INA260Sensor.cpp index f156a9aba..751608c82 100644 --- a/src/modules/Telemetry/Sensor/INA260Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA260Sensor.cpp @@ -26,6 +26,9 @@ void INA260Sensor::setup() {} bool INA260Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_voltage = true; + measurement->variant.environment_metrics.has_current = true; + // mV conversion to V measurement->variant.environment_metrics.voltage = ina260.readBusVoltage() / 1000; measurement->variant.environment_metrics.current = ina260.readCurrent(); diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp index dec99c551..549346d72 100644 --- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp @@ -67,6 +67,9 @@ bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement) { struct _INA3221Measurement m = getMeasurement(ENV_CH); + measurement->variant.environment_metrics.has_voltage = true; + measurement->variant.environment_metrics.has_current = true; + measurement->variant.environment_metrics.voltage = m.voltage; measurement->variant.environment_metrics.current = m.current; @@ -77,6 +80,13 @@ bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement) { struct _INA3221Measurements m = getMeasurements(); + measurement->variant.power_metrics.has_ch1_voltage = true; + measurement->variant.power_metrics.has_ch1_current = true; + measurement->variant.power_metrics.has_ch2_voltage = true; + measurement->variant.power_metrics.has_ch2_current = true; + measurement->variant.power_metrics.has_ch3_voltage = true; + measurement->variant.power_metrics.has_ch3_current = true; + measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage; measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current; measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage; diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp index c3c994cfa..111d86d1a 100644 --- a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp +++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp @@ -27,6 +27,9 @@ void LPS22HBSensor::setup() bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + sensors_event_t temp; sensors_event_t pressure; lps22hb.getEvent(&pressure, &temp); diff --git a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp index b01a19291..c1cda7227 100644 --- a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp @@ -26,6 +26,8 @@ void MCP9808Sensor::setup() bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + LOG_DEBUG("MCP9808Sensor::getMetrics\n"); measurement->variant.environment_metrics.temperature = mcp9808.readTempC(); return true; diff --git a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp index 4c459c365..0568a4652 100644 --- a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp @@ -32,6 +32,7 @@ void MLX90632Sensor::setup() {} bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit return true; diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp index 3560c6580..d7dcbd09f 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp @@ -45,6 +45,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) return false; } } + measurement->variant.environment_metrics.has_weight = true; // Check if we have correct calibration values after powerup LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp index d0e38bf88..d6cbcbb91 100644 --- a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp +++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp @@ -38,6 +38,7 @@ void OPT3001Sensor::setup() bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_lux = true; OPT3001 result = opt3001.readResult(); measurement->variant.environment_metrics.lux = result.lux; diff --git a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp index 49a509d38..b9a29ab7d 100644 --- a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp +++ b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp @@ -23,6 +23,7 @@ void RCWL9620Sensor::setup() {} bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_distance = true; LOG_DEBUG("RCWL9620Sensor::getMetrics\n"); measurement->variant.environment_metrics.distance = getDistance(); return true; diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp index aa2b5dcfc..c372f7986 100644 --- a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp @@ -27,6 +27,8 @@ void SHT31Sensor::setup() bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; measurement->variant.environment_metrics.temperature = sht31.readTemperature(); measurement->variant.environment_metrics.relative_humidity = sht31.readHumidity(); diff --git a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp index 7f37327c6..94367cba4 100644 --- a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp @@ -39,6 +39,9 @@ void SHT4XSensor::setup() bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + sensors_event_t humidity, temp; sht4x.getEvent(&humidity, &temp); measurement->variant.environment_metrics.temperature = temp.temperature; diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp index 37685fed7..64ebfb472 100644 --- a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp @@ -26,6 +26,9 @@ void SHTC3Sensor::setup() bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_relative_humidity = true; + sensors_event_t humidity, temp; shtc3.getEvent(&humidity, &temp); diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.cpp b/src/modules/Telemetry/Sensor/T1000xSensor.cpp index e544d0dc5..4079d8ae3 100644 --- a/src/modules/Telemetry/Sensor/T1000xSensor.cpp +++ b/src/modules/Telemetry/Sensor/T1000xSensor.cpp @@ -108,6 +108,9 @@ float T1000xSensor::getTemp() bool T1000xSensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_lux = true; + measurement->variant.environment_metrics.temperature = getTemp(); measurement->variant.environment_metrics.lux = getLux(); return true; diff --git a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp index d20e48dce..9002874b3 100644 --- a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp +++ b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp @@ -29,6 +29,7 @@ void TSL2591Sensor::setup() bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_lux = true; uint32_t lum = tsl.getFullLuminosity(); uint16_t ir, full; ir = lum >> 16; diff --git a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp index cbeaf4c2e..c176ed21b 100644 --- a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp +++ b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp @@ -53,6 +53,9 @@ float VEML7700Sensor::getResolution(void) bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement) { + measurement->variant.environment_metrics.has_lux = true; + measurement->variant.environment_metrics.has_white_lux = true; + int16_t white; measurement->variant.environment_metrics.lux = veml7700.readLux(VEML_LUX_AUTO); white = veml7700.readWhite(true); From 0ebdc7ab0c241adaeef5893c9bb4751b52869685 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Sat, 17 Aug 2024 00:37:22 +0200 Subject: [PATCH 151/305] Update architecture.h add Minewsemi ME25LS01 LR1110 breakout ME25LE01_V1.0 and fix buzzer (#4472) * Update architecture.h * Update variant.h * Update variant.h * Update architecture.h * Update architecture.h * Delete src/platform/nrf52/architecture.h * Add files via upload * Update architecture.h * Update architecture.h * Update architecture.h --- src/platform/nrf52/architecture.h | 4 +++- variants/ME25LS01-4Y10TD/variant.h | 4 ++-- variants/ME25LS01-4Y10TD_e-ink/variant.h | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index d5685d611..ebb847dc8 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -62,6 +62,8 @@ #define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110 #elif defined(TRACKER_T1000_E) #define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E +#elif defined(ME25LS01) +#define HW_VENDOR meshtastic_HardwareModel_ME25LS01 #elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #else @@ -116,4 +118,4 @@ #if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA) // No serial ports on this board - ONLY use segger in memory console #define USE_SEGGER -#endif \ No newline at end of file +#endif diff --git a/variants/ME25LS01-4Y10TD/variant.h b/variants/ME25LS01-4Y10TD/variant.h index 24ed65b33..715de8ee6 100644 --- a/variants/ME25LS01-4Y10TD/variant.h +++ b/variants/ME25LS01-4Y10TD/variant.h @@ -126,7 +126,7 @@ extern "C" { #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 // Buzzer -#define BUZZER_EN_PIN -1 +#define PIN_BUZZER (0 + 25) #ifdef __cplusplus } @@ -136,4 +136,4 @@ extern "C" { * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_ME25LS01_4Y10TD_ \ No newline at end of file +#endif // _VARIANT_ME25LS01_4Y10TD_ diff --git a/variants/ME25LS01-4Y10TD_e-ink/variant.h b/variants/ME25LS01-4Y10TD_e-ink/variant.h index 7c7740505..9006d5a63 100644 --- a/variants/ME25LS01-4Y10TD_e-ink/variant.h +++ b/variants/ME25LS01-4Y10TD_e-ink/variant.h @@ -149,7 +149,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 // Buzzer -#define BUZZER_EN_PIN -1 +#define PIN_BUZZER (0 + 25) #ifdef __cplusplus } @@ -159,4 +159,4 @@ static const uint8_t SCK = PIN_SPI_SCK; * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_ME25LS01_4Y10TD__ \ No newline at end of file +#endif // _VARIANT_ME25LS01_4Y10TD__ From cec8233cd1f7d8420f5a52596ad9ae904e1b2fdf Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 16 Aug 2024 19:33:06 -0500 Subject: [PATCH 152/305] Don't attempt PKI decryption on broadcast packets --- src/mesh/Router.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index a797e81b3..f8655b1e3 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -320,9 +320,9 @@ bool perhapsDecode(meshtastic_MeshPacket *p) memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); #if !(MESHTASTIC_EXCLUDE_PKI) // Attempt PKI decryption first - if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && nodeDB->getMeshNode(p->from) != nullptr && - nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && - rawSize > 8) { + if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && p->to != NODENUM_BROADCAST && + nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && + nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 8) { LOG_DEBUG("Attempting PKI decryption\n"); if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) { From 6eabbaf4321a3694fb48b4a55331c53d2ebb5d63 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 16 Aug 2024 19:37:28 -0500 Subject: [PATCH 153/305] Add PKI logiv to KNOWN_ONLY and LOCAL_ONLY routing modes. --- src/modules/RoutingModule.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 87015032d..b7be4abc9 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -13,6 +13,19 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh printPacket("Routing sniffing", &mp); router->sniffReceived(&mp, r); + bool maybePKI = + mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && mp.to != NODENUM_BROADCAST; + // Beginning of logic whether to drop the packet based on Rebroadcast mode + if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && + (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY || + config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY)) { + if (!maybePKI) + return false; + if ((nodeDB->getMeshNode(mp.from) == NULL || !nodeDB->getMeshNode(mp.from)->has_user) && + (nodeDB->getMeshNode(mp.to) == NULL || !nodeDB->getMeshNode(mp.to)->has_user)) + return false; + } + // FIXME - move this to a non promsicious PhoneAPI module? // Note: we are careful not to send back packets that started with the phone back to the phone if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB->getNodeNum()) && (mp.from != 0)) { @@ -65,6 +78,9 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg) { isPromiscuous = true; - encryptedOk = config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY && - config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY; + + // moved the ReboradcastMode logic into handleReceivedProtobuf + // LocalOnly requires either the from or to to be a known node + // knownOnly specifically requires the from to be a known node. + encryptedOk = true; } \ No newline at end of file From e0b4a8e31ea22206ad60de20dac24dfc9dcbb8da Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Sat, 17 Aug 2024 22:51:53 +1200 Subject: [PATCH 154/305] Radio Master Joystick (#4476) * Radio Master Bandit 5-Way Joystick: first draft Untested on genuine hardware * "Okay" moves to next frame, even when canned message disabled * Refactor to allow easier customization * Implement feedback from testing * guard toggleGPS() * show "Shutting down.." screen * split adhoc ping alert onto two lines * Don't block while waiting for shutdown Was preventing the alert from showing --- src/input/ExpressLRSFiveWay.cpp | 260 ++++++++++++++++++ src/input/ExpressLRSFiveWay.h | 85 ++++++ src/modules/Modules.cpp | 4 + .../radiomaster_900_bandit_nano/variant.h | 11 +- 4 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 src/input/ExpressLRSFiveWay.cpp create mode 100644 src/input/ExpressLRSFiveWay.h diff --git a/src/input/ExpressLRSFiveWay.cpp b/src/input/ExpressLRSFiveWay.cpp new file mode 100644 index 000000000..c444800ba --- /dev/null +++ b/src/input/ExpressLRSFiveWay.cpp @@ -0,0 +1,260 @@ + +#include "ExpressLRSFiveWay.h" + +#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE + +static const char inputSourceName[] = "ExpressLRS5Way"; // should match "allow input source" string + +/** + * @brief Calculate fuzz: half the distance to the next nearest neighbor for each joystick position. + * + * The goal is to avoid collisions between joystick positions while still maintaining + * the widest tolerance for the analog value. + * + * Example: {10,50,800,1000,300,1600} + * If we just choose the minimum difference for this array the value would + * be 40/2 = 20. + * + * 20 does not leave enough room for the joystick position using 1600 which + * could have a +-100 offset. + * + * Example Fuzz values: {20, 20, 100, 100, 125, 300} now the fuzz for the 1600 + * position is 300 instead of 20 + */ +void ExpressLRSFiveWay::calcFuzzValues() +{ + for (unsigned int i = 0; i < N_JOY_ADC_VALUES; i++) { + uint16_t closestDist = 0xffff; + uint16_t ival = joyAdcValues[i]; + // Find the closest value to ival + for (unsigned int j = 0; j < N_JOY_ADC_VALUES; j++) { + // Don't compare value with itself + if (j == i) + continue; + uint16_t jval = joyAdcValues[j]; + if (jval < ival && (ival - jval < closestDist)) + closestDist = ival - jval; + if (jval > ival && (jval - ival < closestDist)) + closestDist = jval - ival; + } // for j + + // And the fuzz is half the distance to the closest value + fuzzValues[i] = closestDist / 2; + // DBG("joy%u=%u f=%u, ", i, ival, fuzzValues[i]); + } // for i +} + +int ExpressLRSFiveWay::readKey() +{ + uint16_t value = analogRead(PIN_JOYSTICK); + + constexpr uint8_t IDX_TO_INPUT[N_JOY_ADC_VALUES - 1] = {UP, DOWN, LEFT, RIGHT, OK}; + for (unsigned int i = 0; i < N_JOY_ADC_VALUES - 1; ++i) { + if (value < (joyAdcValues[i] + fuzzValues[i]) && value > (joyAdcValues[i] - fuzzValues[i])) + return IDX_TO_INPUT[i]; + } + return NO_PRESS; +} + +ExpressLRSFiveWay::ExpressLRSFiveWay() : concurrency::OSThread(inputSourceName) +{ + // ExpressLRS: init values + isLongPressed = false; + keyInProcess = NO_PRESS; + keyDownStart = 0; + + // Express LRS: calculate the threshold for interpreting ADC values as various buttons + calcFuzzValues(); + + // Meshtastic: register with canned messages + inputBroker->registerSource(this); +} + +// ExpressLRS: interpret reading as key events +void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) +{ + *keyValue = NO_PRESS; + + int newKey = readKey(); + uint32_t now = millis(); + if (keyInProcess == NO_PRESS) { + // New key down + if (newKey != NO_PRESS) { + keyDownStart = now; + // DBGLN("down=%u", newKey); + } + } else { + // if key released + if (newKey == NO_PRESS) { + // DBGLN("up=%u", keyInProcess); + if (!isLongPressed) { + if ((now - keyDownStart) > KEY_DEBOUNCE_MS) { + *keyValue = keyInProcess; + *keyLongPressed = false; + } + } + isLongPressed = false; + } + // else if the key has changed while down, reset state for next go-around + else if (newKey != keyInProcess) { + newKey = NO_PRESS; + } + // else still pressing, waiting for long if not already signaled + else if (!isLongPressed) { + if ((now - keyDownStart) > KEY_LONG_PRESS_MS) { + *keyValue = keyInProcess; + *keyLongPressed = true; + isLongPressed = true; + } + } + } // if keyInProcess != NO_PRESS + + keyInProcess = newKey; +} + +// Meshtastic: runs at regular intervals +int32_t ExpressLRSFiveWay::runOnce() +{ + uint32_t now = millis(); + + // Dismiss any alert frames after 2 seconds + // Feedback for GPS toggle / adhoc ping + if (alerting && now > alertingSinceMs + 2000) { + alerting = false; + screen->endAlert(); + } + + // Get key events from ExpressLRS code + int keyValue; + bool longPressed; + update(&keyValue, &longPressed); + + // Do something about this key press + determineAction((KeyType)keyValue, longPressed ? LONG : SHORT); + + // If there has been recent key activity, poll the joystick slightly more frequently + if (now < keyDownStart + (20 * 1000UL)) // Within last 20 seconds + return 100; + + // Otherwise, poll slightly less often + // Too many missed pressed if much slower than 250ms + return 250; +} + +// Determine what action to take when a button press is detected +// Written verbose for easier remapping by user +void ExpressLRSFiveWay::determineAction(KeyType key, PressLength length) +{ + switch (key) { + case LEFT: + if (inCannedMessageMenu()) // If in canned message menu + sendKey(CANCEL); // exit the menu (press imaginary cancel key) + else + sendKey(LEFT); + break; + + case RIGHT: + if (inCannedMessageMenu()) // If in canned message menu: + sendKey(CANCEL); // exit the menu (press imaginary cancel key) + else + sendKey(RIGHT); + break; + + case UP: + if (length == LONG) + toggleGPS(); + else + sendKey(UP); + break; + + case DOWN: + if (length == LONG) + sendAdhocPing(); + else + sendKey(DOWN); + break; + + case OK: + if (length == LONG) + shutdown(); + else + click(); // Use instead of sendKey(OK). Works better when canned message module disabled + break; + + default: + break; + } +} + +// Feed input to the canned messages module +void ExpressLRSFiveWay::sendKey(KeyType key) +{ + InputEvent e; + e.source = inputSourceName; + e.inputEvent = key; + notifyObservers(&e); +} + +// Enable or Disable a connected GPS +// Contained as one method for easier remapping of buttons by user +void ExpressLRSFiveWay::toggleGPS() +{ +#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS + if (!config.device.disable_triple_click && (gps != nullptr)) { + gps->toggleGpsMode(); + screen->startAlert("GPS Toggled"); + alerting = true; + alertingSinceMs = millis(); + } +#endif +} + +// Send either node-info or position, on demand +// Contained as one method for easier remapping of buttons by user +void ExpressLRSFiveWay::sendAdhocPing() +{ + service->refreshLocalMeshNode(); + bool sentPosition = service->trySendPosition(NODENUM_BROADCAST, true); + + // Show custom alert frame, with multi-line centering + screen->startAlert([sentPosition](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { + uint16_t x_offset = display->width() / 2; + uint16_t y_offset = 26; // Same constant as the default startAlert frame + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(FONT_MEDIUM); + display->drawString(x_offset + x, y_offset + y, "Sent ad-hoc"); + display->drawString(x_offset + x, y_offset + FONT_HEIGHT_MEDIUM + y, sentPosition ? "position" : "nodeinfo"); + }); + + alerting = true; + alertingSinceMs = millis(); +} + +// Shutdown the node (enter deep-sleep) +// Contained as one method for easier remapping of buttons by user +void ExpressLRSFiveWay::shutdown() +{ + LOG_INFO("Shutdown from long press\n"); + powerFSM.trigger(EVENT_PRESS); + screen->startAlert("Shutting down..."); + // Don't set alerting = true. We don't want to auto-dismiss this alert. + + playShutdownMelody(); // In case user adds a buzzer + + shutdownAtMsec = millis() + 3000; +} + +// Emulate user button, or canned message SELECT +// This is necessary as canned message module doesn't translate SELECT to user button presses if the module is disabled +// Contained as one method for easier remapping of buttons by user +void ExpressLRSFiveWay::click() +{ + if (!moduleConfig.canned_message.enabled) + powerFSM.trigger(EVENT_PRESS); + else + sendKey(OK); +} + +ExpressLRSFiveWay *expressLRSFiveWayInput = nullptr; + +#endif \ No newline at end of file diff --git a/src/input/ExpressLRSFiveWay.h b/src/input/ExpressLRSFiveWay.h new file mode 100644 index 000000000..c53aa9c09 --- /dev/null +++ b/src/input/ExpressLRSFiveWay.h @@ -0,0 +1,85 @@ +/* + Input source for Radio Master Bandit Nano, and similar hardware. + Devices have a 5-button "resistor ladder" style joystick, read by ADC. + These devices do not use the ADC to monitor input voltage. + + Much of this code taken directly from ExpressLRS FiveWayButton class: + https://github.com/ExpressLRS/ExpressLRS/tree/d9f56f8bd6f9f7144d5f01caaca766383e1e0950/src/lib/SCREEN/FiveWayButton +*/ + +#pragma once + +#include "configuration.h" + +#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE + +#include +#include + +#include "InputBroker.h" +#include "MeshService.h" // For adhoc ping action +#include "buzz.h" +#include "concurrency/OSThread.h" +#include "graphics/Screen.h" // Feedback for adhoc ping / toggle GPS +#include "main.h" +#include "modules/CannedMessageModule.h" + +#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS +#include "GPS.h" // For toggle GPS action +#endif + +class ExpressLRSFiveWay : public Observable, public concurrency::OSThread +{ + private: + // Number of values in JOY_ADC_VALUES, if defined + // These must be ADC readings for {UP, DOWN, LEFT, RIGHT, ENTER, IDLE} + static constexpr size_t N_JOY_ADC_VALUES = 6; + static constexpr uint32_t KEY_DEBOUNCE_MS = 25; + static constexpr uint32_t KEY_LONG_PRESS_MS = 3000; // How many milliseconds to hold key for a long press + + // This merged an enum used by the ExpressLRS code, with meshtastic canned message values + // Key names are kept simple, to allow user customizaton + typedef enum { + UP = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP, + DOWN = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN, + LEFT = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT, + RIGHT = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT, + OK = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT, + CANCEL = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL, + NO_PRESS = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE + } KeyType; + + typedef enum { SHORT, LONG } PressLength; + + // From ExpressLRS + int keyInProcess; + uint32_t keyDownStart; + bool isLongPressed; + const uint16_t joyAdcValues[N_JOY_ADC_VALUES] = {JOYSTICK_ADC_VALS}; + uint16_t fuzzValues[N_JOY_ADC_VALUES]; + void calcFuzzValues(); + int readKey(); + void update(int *keyValue, bool *keyLongPressed); + + // Meshtastic code + void determineAction(KeyType key, PressLength length); + void sendKey(KeyType key); + inline bool inCannedMessageMenu() { return cannedMessageModule->shouldDraw(); } + int32_t runOnce() override; + + // Simplified Meshtastic actions, for easier remapping by user + void toggleGPS(); + void sendAdhocPing(); + void shutdown(); + void click(); + + bool alerting = false; // Is the screen showing an alert frame? Feedback for GPS toggle / adhoc ping actions + uint32_t alertingSinceMs = 0; // When did screen begin showing an alert frame? Used to auto-dismiss + + public: + ExpressLRSFiveWay(); +}; + +extern ExpressLRSFiveWay *expressLRSFiveWayInput; + +#endif \ No newline at end of file diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index a1c9f4a03..eca0e8ac9 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -1,5 +1,6 @@ #include "configuration.h" #if !MESHTASTIC_EXCLUDE_INPUTBROKER +#include "input/ExpressLRSFiveWay.h" #include "input/InputBroker.h" #include "input/RotaryEncoderInterruptImpl1.h" #include "input/ScanAndSelect.h" @@ -176,6 +177,9 @@ void setupModules() trackballInterruptImpl1 = new TrackballInterruptImpl1(); trackballInterruptImpl1->init(); #endif +#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE + expressLRSFiveWayInput = new ExpressLRSFiveWay(); +#endif #if HAS_SCREEN && !MESHTASTIC_EXCLUDE_CANNEDMESSAGES cannedMessageModule = new CannedMessageModule(); #endif diff --git a/variants/radiomaster_900_bandit_nano/variant.h b/variants/radiomaster_900_bandit_nano/variant.h index 39c1bc06f..1b6bba126 100644 --- a/variants/radiomaster_900_bandit_nano/variant.h +++ b/variants/radiomaster_900_bandit_nano/variant.h @@ -41,14 +41,11 @@ /* Five way button when using ADC. - 2.632V, 2.177V, 1.598V, 1.055V, 0V - - Possible ADC Values: - { UP, DOWN, LEFT, RIGHT, ENTER, IDLE } - 3227, 0 ,1961, 2668, 1290, 4095 + https://github.com/ExpressLRS/targets/blob/f3215b5ec891108db1a13523e4163950cfcadaac/TX/Radiomaster%20Bandit.json#L41 */ -#define BUTTON_PIN 39 -#define BUTTON_NEED_PULLUP +#define INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE +#define PIN_JOYSTICK 39 +#define JOYSTICK_ADC_VALS /*UP*/ 3227, /*DOWN*/ 0, /*LEFT*/ 1961, /*RIGHT*/ 2668, /*OK*/ 1290, /*IDLE*/ 4095 #define DISPLAY_FLIP_SCREEN From 9dad62e3c49fbf810cff24b5fb5757847aec0585 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 17 Aug 2024 05:52:36 -0500 Subject: [PATCH 155/305] Set time-only admin command (#4479) --- src/mesh/generated/meshtastic/admin.pb.h | 5 +++++ src/modules/AdminModule.cpp | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index b4971991a..99b8fd8c3 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -166,6 +166,9 @@ typedef struct _meshtastic_AdminMessage { meshtastic_Position set_fixed_position; /* Clear fixed position coordinates and then set position.fixed_position = false */ bool remove_fixed_position; + /* Set time only on the node + Convenience method to set the time on the node (as Net quality) without any other position data */ + uint32_t set_time_only; /* Begins an edit transaction for config, module config, owner, and channel settings changes This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) */ bool begin_edit_settings; @@ -261,6 +264,7 @@ extern "C" { #define meshtastic_AdminMessage_remove_favorite_node_tag 40 #define meshtastic_AdminMessage_set_fixed_position_tag 41 #define meshtastic_AdminMessage_remove_fixed_position_tag 42 +#define meshtastic_AdminMessage_set_time_only_tag 43 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 #define meshtastic_AdminMessage_factory_reset_device_tag 94 @@ -307,6 +311,7 @@ X(a, STATIC, ONEOF, UINT32, (payload_variant,set_favorite_node,set_favori X(a, STATIC, ONEOF, UINT32, (payload_variant,remove_favorite_node,remove_favorite_node), 40) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed_position), 41) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ +X(a, STATIC, ONEOF, FIXED32, (payload_variant,set_time_only,set_time_only), 43) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index fe426f8f5..0c39a4304 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -3,6 +3,7 @@ #include "MeshService.h" #include "NodeDB.h" #include "PowerFSM.h" +#include "RTC.h" #include #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH #include "BleOta.h" @@ -279,6 +280,15 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } break; } + case meshtastic_AdminMessage_set_time_only_tag: { + LOG_INFO("Client is receiving a set_time_only command.\n"); + struct timeval tv; + tv.tv_sec = r->set_time_only; + tv.tv_usec = 0; + + perhapsSetRTC(RTCQualityFromNet, &tv, false); + break; + } case meshtastic_AdminMessage_enter_dfu_mode_request_tag: { LOG_INFO("Client is requesting to enter DFU mode.\n"); #if defined(ARCH_NRF52) || defined(ARCH_RP2040) From a577dd4142ab7becd0addd1b87a6a545f8b0af0f Mon Sep 17 00:00:00 2001 From: Matt Ranostaj Date: Sat, 17 Aug 2024 19:37:05 +0800 Subject: [PATCH 156/305] define PERIPHERAL_WARMUP_MS for heltec_capsule_sensor_v3 (#4473) * delay is at least needed for the GXHTC3 module to be detected --- variants/heltec_capsule_sensor_v3/variant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/heltec_capsule_sensor_v3/variant.h b/variants/heltec_capsule_sensor_v3/variant.h index d77571c27..415de0559 100644 --- a/variants/heltec_capsule_sensor_v3/variant.h +++ b/variants/heltec_capsule_sensor_v3/variant.h @@ -47,6 +47,7 @@ #define SENSOR_POWER_CTRL_PIN 21 #define SENSOR_POWER_ON 1 +#define PERIPHERAL_WARMUP_MS 100 #define SENSOR_GPS_CONFLICT #define ESP32S3_WAKE_TYPE ESP_EXT1_WAKEUP_ANY_HIGH \ No newline at end of file From f86dde3c405641f8a756494be50303ec78492b9c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 17 Aug 2024 08:41:12 -0500 Subject: [PATCH 157/305] AdminModule session_passkey (#4478) * Protobuf * Adds session_passkey for remote admin changes --- src/modules/AdminModule.cpp | 84 +++++++++++++++++++++++++++++++------ src/modules/AdminModule.h | 9 ++++ 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 0c39a4304..721220cf1 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -70,25 +70,24 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta return handled; } meshtastic_Channel *ch = &channels.getByIndex(mp.channel); - // Could tighten this up further by tracking the last poblic_key we went an AdminMessage request to + // Could tighten this up further by tracking the last public_key we went an AdminMessage request to // and only allowing responses from that remote. - if (!((mp.from == 0 && !config.security.is_managed) || - r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || - r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag || + if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) || (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) { LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); return handled; } LOG_INFO("Handling admin payload %i\n", r->which_payload_variant); + + // all of the get and set messages, including those for other modules, flow through here first. + // any message that changes state, we want to check the passkey for + if (mp.from != 0 && !messageIsRequest(r) && !messageIsResponse(r)) { + if (!checkPassKey(r)) { + LOG_WARN("Admin message without session_key!\n"); + return handled; + } + } switch (r->which_payload_variant) { /** @@ -319,6 +318,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta AdminMessageHandleResult handleResult = MeshModule::handleAdminMessageForAllModules(mp, r, &res); if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) { + setPassKey(&res); myReply = allocDataProtobuf(res); } else if (mp.decoded.want_response) { LOG_DEBUG("We did not responded to a request that wanted a respond. req.variant=%d\n", r->which_payload_variant); @@ -638,6 +638,7 @@ void AdminModule::handleGetOwner(const meshtastic_MeshPacket &req) res.get_owner_response = owner; res.which_payload_variant = meshtastic_AdminMessage_get_owner_response_tag; + setPassKey(&res); myReply = allocDataProtobuf(res); } } @@ -694,6 +695,7 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 // and useful for users to know current provisioning) hideSecret(r.get_radio_response.preferences.wifi_password); // r.get_config_response.which_payloadVariant = Config_ModuleConfig_telemetry_tag; res.which_payload_variant = meshtastic_AdminMessage_get_config_response_tag; + setPassKey(&res); myReply = allocDataProtobuf(res); } } @@ -779,6 +781,7 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const // and useful for users to know current provisioning) hideSecret(r.get_radio_response.preferences.wifi_password); // r.get_config_response.which_payloadVariant = Config_ModuleConfig_telemetry_tag; res.which_payload_variant = meshtastic_AdminMessage_get_module_config_response_tag; + setPassKey(&res); myReply = allocDataProtobuf(res); } } @@ -802,6 +805,7 @@ void AdminModule::handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &r nodePin.pin = moduleConfig.remote_hardware.available_pins[i]; r.get_node_remote_hardware_pins_response.node_remote_hardware_pins[i + 12] = nodePin; } + setPassKey(&r); myReply = allocDataProtobuf(r); } @@ -810,6 +814,7 @@ void AdminModule::handleGetDeviceMetadata(const meshtastic_MeshPacket &req) meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default; r.get_device_metadata_response = getDeviceMetadata(); r.which_payload_variant = meshtastic_AdminMessage_get_device_metadata_response_tag; + setPassKey(&r); myReply = allocDataProtobuf(r); } @@ -873,6 +878,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r r.get_device_connection_status_response = conn; r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag; + setPassKey(&r); myReply = allocDataProtobuf(r); } @@ -883,6 +889,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default; r.get_channel_response = channels.getByIndex(channelIndex); r.which_payload_variant = meshtastic_AdminMessage_get_channel_response_tag; + setPassKey(&r); myReply = allocDataProtobuf(r); } } @@ -937,4 +944,57 @@ AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_AP { // restrict to the admin channel for rx // boundChannel = Channels::adminChannel; +} + +void AdminModule::setPassKey(meshtastic_AdminMessage *res) +{ + if (millis() / 1000 > session_time + 150) { + for (int i = 0; i < 8; i++) { + session_passkey[i] = random(); + } + session_time = millis() / 1000; + } + memcpy(res->session_passkey.bytes, session_passkey, 8); + res->session_passkey.size = 8; + // if halfway to session_expire, regenerate session_passkey, reset the timeout + // set the key in the packet +} + +bool AdminModule::checkPassKey(meshtastic_AdminMessage *res) +{ // check that the key in the packet is still valid + return (session_time + 300 < millis() / 1000 && res->session_passkey.size == 8 && + memcmp(res->session_passkey.bytes, session_passkey, 8) == 0); +} + +bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r) +{ + if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || + r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag) + return true; + else + return false; +} + +bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r) +{ + if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_owner_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_config_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_module_config_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag) + return true; + else + return false; } \ No newline at end of file diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index a5ffeb7d6..61c54d1b1 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -25,6 +25,9 @@ class AdminModule : public ProtobufModule, public Obser private: bool hasOpenEditTransaction = false; + uint8_t session_passkey[8] = {0}; + uint session_time = 0; + void saveChanges(int saveWhat, bool shouldReboot = true); /** @@ -48,6 +51,12 @@ class AdminModule : public ProtobufModule, public Obser void handleSetChannel(); void handleSetHamMode(const meshtastic_HamParameters &req); void reboot(int32_t seconds); + + void setPassKey(meshtastic_AdminMessage *res); + bool checkPassKey(meshtastic_AdminMessage *res); + + bool messageIsResponse(meshtastic_AdminMessage *r); + bool messageIsRequest(meshtastic_AdminMessage *r); }; extern AdminModule *adminModule; \ No newline at end of file From 0b010b4fd813d177ff20e1822b2463bcf956e127 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 17 Aug 2024 11:06:00 -0500 Subject: [PATCH 158/305] Get STM32 building again by disabling admin module --- src/configuration.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configuration.h b/src/configuration.h index c9a5d7fb0..2e0efffd4 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -285,6 +285,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_INPUTBROKER 1 #define MESHTASTIC_EXCLUDE_SERIAL 1 #define MESHTASTIC_EXCLUDE_POWERSTRESS 1 +#define MESHTASTIC_EXCLUDE_ADMIN 1 #endif // // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled) From 5ff1078c8c150eb61f1f611ec1118aa6b833e44c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 17 Aug 2024 11:51:53 -0500 Subject: [PATCH 159/305] Move NeighborInfo interval default to 6 hours (double NodeInfo) (#4483) * Move NeighborInfo in line with NodeInfo * Set default to 6 hours and cap minimum at 2 hours --- src/mesh/Default.h | 2 ++ src/modules/AdminModule.cpp | 4 ++++ src/modules/NeighborInfoModule.cpp | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mesh/Default.h b/src/mesh/Default.h index 3c95544da..bafa60898 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -13,7 +13,9 @@ #define default_min_wake_secs 10 #define default_screen_on_secs IF_ROUTER(1, 60 * 10) #define default_node_info_broadcast_secs 3 * 60 * 60 +#define default_neighbor_info_broadcast_secs 6 * 60 * 60 #define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour +#define min_neighbor_info_broadcast_secs 2 * 60 * 60 #define default_mqtt_address "mqtt.meshtastic.org" #define default_mqtt_username "meshdev" diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 25450992b..cf2247031 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -557,6 +557,10 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) case meshtastic_ModuleConfig_neighbor_info_tag: LOG_INFO("Setting module config: Neighbor Info\n"); moduleConfig.has_neighbor_info = true; + if (moduleConfig.neighbor_info.update_interval < min_neighbor_info_broadcast_secs) { + LOG_DEBUG("Tried to set update_interval too low, setting to %d\n", default_neighbor_info_broadcast_secs); + moduleConfig.neighbor_info.update_interval = default_neighbor_info_broadcast_secs; + } moduleConfig.neighbor_info = c.payload_variant.neighbor_info; break; case meshtastic_ModuleConfig_detection_sensor_tag: diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index fb1249029..8284c52d9 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -120,8 +120,7 @@ int32_t NeighborInfoModule::runOnce() if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { sendNeighborInfo(NODENUM_BROADCAST, false); } - return Default::getConfiguredOrDefaultMsScaled(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs, - numOnlineNodes); + return Default::getConfiguredOrDefault(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs); } /* From e37acae40583cac5cb55bdd3210db1b51950cd89 Mon Sep 17 00:00:00 2001 From: Mictronics Date: Sat, 17 Aug 2024 22:09:13 +0200 Subject: [PATCH 160/305] Fix RF switching logic on rp2040-lora board. (#4486) * Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28 * Merge PR #420 * Fixed double and missing Default class. * Use correct format specifier and fixed typo. * Removed duplicate code. * Fix error: #if with no expression * Fix warning: extra tokens at end of #endif directive. * Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board. --------- Co-authored-by: Ben Meadors --- variants/rp2040-lora/variant.h | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/variants/rp2040-lora/variant.h b/variants/rp2040-lora/variant.h index 1f42c4db7..d0bbc0ec3 100644 --- a/variants/rp2040-lora/variant.h +++ b/variants/rp2040-lora/variant.h @@ -15,7 +15,7 @@ // rxd = 9 #define EXT_NOTIFY_OUT 22 -#define BUTTON_PIN 17 +#undef BUTTON_PIN // Pin 17 used for antenna switching via DIO4 #define LED_PIN PIN_LED @@ -32,23 +32,28 @@ // https://www.waveshare.com/rp2040-lora.htm // https://www.waveshare.com/img/devkit/RP2040-LoRa-HF/RP2040-LoRa-HF-details-11.jpg -#define LORA_SCK 14 // 10 -#define LORA_MISO 24 // 12 -#define LORA_MOSI 15 // 11 -#define LORA_CS 13 // 3 +#define LORA_SCK 14 // GPIO14 +#define LORA_MISO 24 // GPIO24 +#define LORA_MOSI 15 // GPIO15 +#define LORA_CS 13 // GPIO13 -#define LORA_DIO0 RADIOLIB_NC -#define LORA_RESET 23 // 15 -#define LORA_DIO1 16 // 20 -#define LORA_DIO2 18 // 2 -#define LORA_DIO3 RADIOLIB_NC -#define LORA_DIO4 17 +#define LORA_DIO0 RADIOLIB_NC // No GPIO connection +#define LORA_RESET 23 // GPIO23 +#define LORA_BUSY 18 // GPIO18 +#define LORA_DIO1 16 // GPIO16 +#define LORA_DIO2 RADIOLIB_NC // Antenna switching, no GPIO connection +#define LORA_DIO3 RADIOLIB_NC // No GPIO connection +#define LORA_DIO4 17 // GPIO17 + +// On rp2040-lora board the antenna switch is wired and works with complementary-pin control logic. +// See PE4259 datasheet page 4 #ifdef USE_SX1262 #define SX126X_CS LORA_CS #define SX126X_DIO1 LORA_DIO1 -#define SX126X_BUSY LORA_DIO2 +#define SX126X_BUSY LORA_BUSY #define SX126X_RESET LORA_RESET -#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL +#define SX126X_RXEN LORA_DIO4 // Antenna switch !CTRL via GPIO17 // #define SX126X_DIO3_TCXO_VOLTAGE 1.8 #endif \ No newline at end of file From 33ced7e87aad4e7a79c3096c5cf2167dc8b7ec1f Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sun, 18 Aug 2024 00:34:32 +0200 Subject: [PATCH 161/305] Add two-way traceroute result with SNR per hop (#4485) * Add two-way traceroute result with SNR per hop * Update protos --- src/mesh/generated/meshtastic/mesh.pb.h | 31 +++- .../generated/meshtastic/module_config.pb.h | 2 +- src/mesh/generated/meshtastic/portnums.pb.h | 2 +- src/modules/TraceRouteModule.cpp | 136 +++++++++++++----- src/modules/TraceRouteModule.h | 6 +- 5 files changed, 131 insertions(+), 46 deletions(-) diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 1f0621f9a..f32f865db 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -187,6 +187,8 @@ typedef enum _meshtastic_HardwareModel { /* RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module SSD1306 OLED and No GPS */ meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74, + /* Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. */ + meshtastic_HardwareModel_ME25LS01_4Y10TD = 75, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -502,11 +504,20 @@ typedef struct _meshtastic_User { meshtastic_User_public_key_t public_key; } meshtastic_User; -/* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */ +/* A message used in a traceroute */ typedef struct _meshtastic_RouteDiscovery { - /* The list of nodenums this packet has visited so far */ + /* The list of nodenums this packet has visited so far to the destination. */ pb_size_t route_count; uint32_t route[8]; + /* The list of SNRs (in dB, scaled by 4) in the route towards the destination. */ + pb_size_t snr_towards_count; + int8_t snr_towards[8]; + /* The list of nodenums the packet has visited on the way back from the destination. */ + pb_size_t route_back_count; + uint32_t route_back[8]; + /* The list of SNRs (in dB, scaled by 4) in the route back from the destination. */ + pb_size_t snr_back_count; + int8_t snr_back[8]; } meshtastic_RouteDiscovery; /* A Routing control Data packet handled by the routing module */ @@ -1054,7 +1065,7 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_Position_init_default {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} -#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} +#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}} #define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} #define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0} @@ -1079,7 +1090,7 @@ extern "C" { #define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}} #define meshtastic_Position_init_zero {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} -#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} +#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}} #define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} #define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0} @@ -1136,6 +1147,9 @@ extern "C" { #define meshtastic_User_role_tag 7 #define meshtastic_User_public_key_tag 8 #define meshtastic_RouteDiscovery_route_tag 1 +#define meshtastic_RouteDiscovery_snr_towards_tag 2 +#define meshtastic_RouteDiscovery_route_back_tag 3 +#define meshtastic_RouteDiscovery_snr_back_tag 4 #define meshtastic_Routing_route_request_tag 1 #define meshtastic_Routing_route_reply_tag 2 #define meshtastic_Routing_error_reason_tag 3 @@ -1298,7 +1312,10 @@ X(a, STATIC, SINGULAR, BYTES, public_key, 8) #define meshtastic_User_DEFAULT NULL #define meshtastic_RouteDiscovery_FIELDLIST(X, a) \ -X(a, STATIC, REPEATED, FIXED32, route, 1) +X(a, STATIC, REPEATED, FIXED32, route, 1) \ +X(a, STATIC, REPEATED, INT32, snr_towards, 2) \ +X(a, STATIC, REPEATED, FIXED32, route_back, 3) \ +X(a, STATIC, REPEATED, INT32, snr_back, 4) #define meshtastic_RouteDiscovery_CALLBACK NULL #define meshtastic_RouteDiscovery_DEFAULT NULL @@ -1612,8 +1629,8 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_NodeRemoteHardwarePin_size 29 #define meshtastic_Position_size 144 #define meshtastic_QueueStatus_size 23 -#define meshtastic_RouteDiscovery_size 40 -#define meshtastic_Routing_size 42 +#define meshtastic_RouteDiscovery_size 256 +#define meshtastic_Routing_size 259 #define meshtastic_ToRadio_size 504 #define meshtastic_User_size 113 #define meshtastic_Waypoint_size 165 diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index 7fd57fe00..2e1985660 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -341,7 +341,7 @@ typedef struct _meshtastic_ModuleConfig_CannedMessageConfig { /* Enable/disable CannedMessageModule. */ bool enabled; /* Input event origin accepted by the canned message module. - Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any" */ + Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any" */ char allow_input_source[16]; /* CannedMessageModule also sends a bell character with the messages. ExternalNotificationModule can benefit from this feature. */ diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h index 6cc82352a..b9e537ddf 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.h +++ b/src/mesh/generated/meshtastic/portnums.pb.h @@ -113,7 +113,7 @@ typedef enum _meshtastic_PortNum { ENCODING: Protobuf (?) */ meshtastic_PortNum_SIMULATOR_APP = 69, /* Provides a traceroute functionality to show the route a packet towards - a certain destination would take on the mesh. + a certain destination would take on the mesh. Contains a RouteDiscovery message as payload. ENCODING: Protobuf */ meshtastic_PortNum_TRACEROUTE_APP = 70, /* Aggregates edge info for the network by sending out a list of each node's neighbors diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index f390aafcd..dd3d0b4f9 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -5,71 +5,141 @@ TraceRouteModule *traceRouteModule; bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r) { - // Only handle a response - if (mp.decoded.request_id) { - printRoute(r, mp.to, mp.from); - } - + // We only alter the packet in alterReceivedProtobuf() return false; // let it be handled by RoutingModule } void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r) { auto &incoming = p.decoded; - // Only append IDs for the request (one way) - if (!incoming.request_id) { - // Insert unknown hops if necessary - insertUnknownHops(p, r); - // Don't add ourselves if we are the destination (the reply will have our NodeNum already) - if (p.to != nodeDB->getNodeNum()) { - appendMyID(r); - printRoute(r, p.from, NODENUM_BROADCAST); - } - // Set updated route to the payload of the to be flooded packet - p.decoded.payload.size = - pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r); - } + // Insert unknown hops if necessary + insertUnknownHops(p, r, !incoming.request_id); + + // Append ID and SNR. For the last hop (p.to == nodeDB->getNodeNum()), we only need to append the SNR + appendMyIDandSNR(r, p.rx_snr, !incoming.request_id, p.to == nodeDB->getNodeNum()); + if (!incoming.request_id) + printRoute(r, p.from, p.to, true); + else + printRoute(r, p.to, p.from, false); + + // Set updated route to the payload of the to be flooded packet + p.decoded.payload.size = + pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r); } -void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r) +void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination) { + pb_size_t *route_count; + uint32_t *route; + pb_size_t *snr_count; + int8_t *snr_list; + + // Pick the correct route array and SNR list + if (isTowardsDestination) { + route_count = &r->route_count; + route = r->route; + snr_count = &r->snr_towards_count; + snr_list = r->snr_towards; + } else { + route_count = &r->route_back_count; + route = r->route_back; + snr_count = &r->snr_back_count; + snr_list = r->snr_back; + } + // Only insert unknown hops if hop_start is valid if (p.hop_start != 0 && p.hop_limit <= p.hop_start) { uint8_t hopsTaken = p.hop_start - p.hop_limit; - int8_t diff = hopsTaken - r->route_count; + int8_t diff = hopsTaken - *route_count; for (uint8_t i = 0; i < diff; i++) { - if (r->route_count < sizeof(r->route) / sizeof(r->route[0])) { - r->route[r->route_count] = NODENUM_BROADCAST; // This will represent an unknown hop - r->route_count += 1; + if (*route_count < sizeof(*route) / sizeof(route[0])) { + route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop + *route_count += 1; + } + } + // Add unknown SNR values if necessary + diff = *route_count - *snr_count; + for (uint8_t i = 0; i < diff; i++) { + if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) { + snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR + *snr_count += 1; } } } } -void TraceRouteModule::appendMyID(meshtastic_RouteDiscovery *updated) +void TraceRouteModule::appendMyIDandSNR(meshtastic_RouteDiscovery *updated, float snr, bool isTowardsDestination, bool SNRonly) { + pb_size_t *route_count; + uint32_t *route; + pb_size_t *snr_count; + int8_t *snr_list; + + // Pick the correct route array and SNR list + if (isTowardsDestination) { + route_count = &updated->route_count; + route = updated->route; + snr_count = &updated->snr_towards_count; + snr_list = updated->snr_towards; + } else { + route_count = &updated->route_back_count; + route = updated->route_back; + snr_count = &updated->snr_back_count; + snr_list = updated->snr_back; + } + + if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) { + snr_list[*snr_count] = (int8_t)(snr * 4); // Convert SNR to 1 byte + *snr_count += 1; + } + if (SNRonly) + return; + // Length of route array can normally not be exceeded due to the max. hop_limit of 7 - if (updated->route_count < sizeof(updated->route) / sizeof(updated->route[0])) { - updated->route[updated->route_count] = myNodeInfo.my_node_num; - updated->route_count += 1; + if (*route_count < sizeof(*route) / sizeof(route[0])) { + route[*route_count] = myNodeInfo.my_node_num; + *route_count += 1; } else { LOG_WARN("Route exceeded maximum hop limit, are you bridging networks?\n"); } } -void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest) +void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination) { #ifdef DEBUG_PORT LOG_INFO("Route traced:\n"); LOG_INFO("0x%x --> ", origin); for (uint8_t i = 0; i < r->route_count; i++) { - LOG_INFO("0x%x --> ", r->route[i]); + if (i < r->snr_towards_count && r->snr_towards[i] != INT8_MIN) + LOG_INFO("0x%x (%.2fdB) --> ", r->route[i], (float)r->snr_towards[i] / 4); + else + LOG_INFO("0x%x (?dB) --> ", r->route[i]); } - if (dest != NODENUM_BROADCAST) - LOG_INFO("0x%x\n", dest); - else + // If we are the destination, or it has already reached the destination, print it + if (dest == nodeDB->getNodeNum() || !isTowardsDestination) { + if (r->snr_towards_count > 0 && r->snr_towards[r->snr_towards_count - 1] != INT8_MIN) + LOG_INFO("0x%x (%.2fdB)\n", dest, (float)r->snr_towards[r->snr_towards_count - 1] / 4); + else + LOG_INFO("0x%x (?dB)\n", dest); + } else LOG_INFO("...\n"); + + // If there's a route back (or we are the destination as then the route is complete), print it + if (r->route_back_count > 0 || origin == nodeDB->getNodeNum()) { + if (r->snr_towards_count > 0 && origin == nodeDB->getNodeNum()) + LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[r->snr_back_count - 1] / 4, origin); + else + LOG_INFO("..."); + + for (int8_t i = r->route_back_count - 1; i >= 0; i--) { + if (i < r->snr_back_count && r->snr_back[i] != INT8_MIN) + LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[i] / 4, r->route_back[i]); + else + LOG_INFO("(?dB) 0x%x <-- ", r->route_back[i]); + } + LOG_INFO("0x%x\n", dest); + } #endif } @@ -86,8 +156,6 @@ meshtastic_MeshPacket *TraceRouteModule::allocReply() pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_RouteDiscovery_msg, &scratch); updated = &scratch; - printRoute(updated, req.from, req.to); - // Create a MeshPacket with this payload and set it as the reply meshtastic_MeshPacket *reply = allocDataProtobuf(*updated); diff --git a/src/modules/TraceRouteModule.h b/src/modules/TraceRouteModule.h index 18a5ac0cb..fe69300de 100644 --- a/src/modules/TraceRouteModule.h +++ b/src/modules/TraceRouteModule.h @@ -20,15 +20,15 @@ class TraceRouteModule : public ProtobufModule private: // Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit - void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r); + void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination); // Call to add your ID to the route array of a RouteDiscovery message - void appendMyID(meshtastic_RouteDiscovery *r); + void appendMyIDandSNR(meshtastic_RouteDiscovery *r, float snr, bool isTowardsDestination, bool SNRonly); /* Call to print the route array of a RouteDiscovery message. Set origin to where the request came from. Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */ - void printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest); + void printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination); }; extern TraceRouteModule *traceRouteModule; \ No newline at end of file From 1bbc273ba6cac076fa2c97a30178ef2bc12e682b Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 17 Aug 2024 17:35:05 -0500 Subject: [PATCH 162/305] Don't reject network time updates unintentionally (#4489) --- src/modules/PositionModule.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 1756e8508..5acb5e9d1 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -140,7 +140,8 @@ void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUp bool PositionModule::hasQualityTimesource() { - bool setFromPhoneOrNtpToday = (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); + bool setFromPhoneOrNtpToday = + lastSetFromPhoneNtpOrGps == 0 ? false : (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); bool hasGpsOrRtc = (gps && gps->isConnected()) || (rtc_found.address != ScanI2C::ADDRESS_NONE.address); return hasGpsOrRtc || setFromPhoneOrNtpToday; } From a8999d77590408d1f5f71941ab17d4ce7331e47a Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Mon, 19 Aug 2024 00:22:16 +1200 Subject: [PATCH 163/305] Don't manually clear runAsap flag (#4496) --- src/ButtonThread.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 1b101044f..5351fa049 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -224,7 +224,6 @@ int32_t ButtonThread::runOnce() btnEvent = BUTTON_EVENT_NONE; } - runASAP = false; return 50; } From 23e3e6db929e2c379a902d17a08f3d3cf664cad5 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 07:23:56 -0500 Subject: [PATCH 164/305] Add 4 bytes of random nonce to PKI (#4493) --- src/mesh/CryptoEngine.cpp | 23 ++++++++++++++++------- src/mesh/CryptoEngine.h | 2 +- src/mesh/Router.cpp | 8 ++++---- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index e83236eab..fbda4a678 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -41,7 +41,11 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ uint8_t *bytesOut) { uint8_t *auth; + uint32_t *extraNonce; auth = bytesOut + numBytes; + extraNonce = (uint32_t *)(auth + 8); + *extraNonce = random(); + LOG_INFO("Random nonce value: %d\n", *extraNonce); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode); if (node->num < 1 || node->user.public_key.size == 0) { LOG_DEBUG("Node %d or their public_key not found\n", toNode); @@ -50,10 +54,10 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ if (!crypto->setDHKey(toNode)) { return false; } - initNonce(fromNode, packetNum); + initNonce(fromNode, packetNum, *extraNonce); // Calculate the shared secret with the destination node and encrypt - printBytes("Attempting encrypt using nonce: ", nonce, 16); + printBytes("Attempting encrypt using nonce: ", nonce, 13); printBytes("Attempting encrypt using shared_key: ", shared_key, 32); aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); return true; @@ -68,7 +72,10 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) { uint8_t *auth; // set to last 8 bytes of text? - auth = bytes + numBytes - 8; + uint32_t *extraNonce; + auth = bytes + numBytes - 12; + extraNonce = (uint32_t *)(auth + 8); + LOG_INFO("Random nonce value: %d\n", *extraNonce); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode); if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) { @@ -80,10 +87,10 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size if (!crypto->setDHKey(fromNode)) { return false; } - initNonce(fromNode, packetNum); - printBytes("Attempting decrypt using nonce: ", nonce, 16); + initNonce(fromNode, packetNum, *extraNonce); + printBytes("Attempting decrypt using nonce: ", nonce, 13); printBytes("Attempting decrypt using shared_key: ", shared_key, 32); - return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); + return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 12, nullptr, 0, auth, bytesOut); } void CryptoEngine::setDHPrivateKey(uint8_t *_private_key) @@ -232,13 +239,15 @@ void CryptoEngine::encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numByte /** * Init our 128 bit nonce for a new packet */ -void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId) +void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce) { memset(nonce, 0, sizeof(nonce)); // use memcpy to avoid breaking strict-aliasing memcpy(nonce, &packetId, sizeof(uint64_t)); memcpy(nonce + sizeof(uint64_t), &fromNode, sizeof(uint32_t)); + if (extraNonce) + memcpy(nonce + sizeof(uint32_t), &extraNonce, sizeof(uint32_t)); } #ifndef HAS_CUSTOM_CRYPTO_ENGINE CryptoEngine *crypto = new CryptoEngine; diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 5ca9db7c1..64382f6a7 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -88,7 +88,7 @@ class CryptoEngine * a 32 bit sending node number (stored in little endian order) * a 32 bit block counter (starts at zero) */ - void initNonce(uint32_t fromNode, uint64_t packetId); + void initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce = 0); }; extern CryptoEngine *crypto; \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index f8655b1e3..bdd8c4e6c 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -322,13 +322,13 @@ bool perhapsDecode(meshtastic_MeshPacket *p) // Attempt PKI decryption first if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && p->to != NODENUM_BROADCAST && nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && - nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 8) { + nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 12) { LOG_DEBUG("Attempting PKI decryption\n"); if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) { LOG_INFO("PKI Decryption worked!\n"); memset(&p->decoded, 0, sizeof(p->decoded)); - rawSize -= 8; + rawSize -= 12; if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) && p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) { decrypted = true; @@ -470,7 +470,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); - if (numbytes + 8 > MAX_RHPACKETLEN) + if (numbytes + 12 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { @@ -479,7 +479,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) return meshtastic_Routing_Error_PKI_FAILED; } crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); - numbytes += 8; + numbytes += 12; memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; p->pki_encrypted = true; From 7129cee944d78387f65a223bb69024253cdc20bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Cort=C3=AAs?= <593860+mrfyda@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:38:54 +0100 Subject: [PATCH 165/305] feature: default to fuzzy GPS location on the Default Channel (#4467) * feature: default to fuzzy GPS location on the Default Channel * Default to 13 * 13 default --------- Co-authored-by: Ben Meadors --- src/mesh/Channels.cpp | 4 ++-- src/modules/PositionModule.cpp | 5 +++-- userPrefs.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 6e721d9b0..bba7571d2 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -97,7 +97,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) channelSettings.psk.bytes[0] = defaultpskIndex; channelSettings.psk.size = 1; strncpy(channelSettings.name, "", sizeof(channelSettings.name)); - channelSettings.module_settings.position_precision = 32; // default to sending location on the primary channel + channelSettings.module_settings.position_precision = 13; // default to sending location on the primary channel channelSettings.has_module_settings = true; ch.has_settings = true; @@ -363,4 +363,4 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) int16_t Channels::setActiveByIndex(ChannelIndex channelIndex) { return setCrypto(channelIndex); -} \ No newline at end of file +} diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 5acb5e9d1..f534baf67 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -298,7 +298,8 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha if (channels.getByIndex(channel).settings.has_module_settings) { precision = channels.getByIndex(channel).settings.module_settings.position_precision; } else if (channels.getByIndex(channel).role == meshtastic_Channel_Role_PRIMARY) { - precision = 32; + // backwards compatibility for Primary channels created before position_precision was set by default + precision = 13; } else { precision = 0; } @@ -470,4 +471,4 @@ void PositionModule::handleNewPosition() } } -#endif \ No newline at end of file +#endif diff --git a/userPrefs.h b/userPrefs.h index 3ebbefcaf..52209deaa 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -17,7 +17,7 @@ } */ // #define CHANNEL_0_NAME_USERPREFS "DEFCONnect" -// #define CHANNEL_0_PRECISION_USERPREFS 13 +// #define CHANNEL_0_PRECISION_USERPREFS 14 // #define CONFIG_OWNER_LONG_NAME_USERPREFS "My Long Name" // #define CONFIG_OWNER_SHORT_NAME_USERPREFS "MLN" From e3e36e23f9bec8e737847ed3da1caddbb05b8ee3 Mon Sep 17 00:00:00 2001 From: Andre K Date: Sun, 18 Aug 2024 11:13:53 -0300 Subject: [PATCH 166/305] add admin getter for `SECURITY_CONFIG` (#4499) --- src/modules/AdminModule.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 727445090..f1c397927 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -690,6 +690,11 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 res.get_config_response.which_payload_variant = meshtastic_Config_bluetooth_tag; res.get_config_response.payload_variant.bluetooth = config.bluetooth; break; + case meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG: + LOG_INFO("Getting config: Security\n"); + res.get_config_response.which_payload_variant = meshtastic_Config_security_tag; + res.get_config_response.payload_variant.bluetooth = config.security; + break; } // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. // So even if we internally use 0 to represent 'use default' we still need to send the value we are From 7a65c8838d1f1c92893bb134d0ae4eea28d678f1 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sun, 18 Aug 2024 16:14:23 +0200 Subject: [PATCH 167/305] Fall back to default modem preset if requested bandwidth is too large (#4497) --- src/mesh/RadioInterface.cpp | 147 ++++++++++++++++++++---------------- src/mesh/RadioInterface.h | 1 + 2 files changed, 85 insertions(+), 63 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 104ddbd1d..f0048dd3d 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -412,72 +412,93 @@ void RadioInterface::applyModemConfig() // Set up default configuration // No Sync Words in LORA mode meshtastic_Config_LoRaConfig &loraConfig = config.lora; - if (loraConfig.use_preset) { + bool validConfig = false; // We need to check for a valid configuration + while (!validConfig) { + if (loraConfig.use_preset) { - switch (loraConfig.modem_preset) { - case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: - bw = (myRegion->wideLora) ? 812.5 : 500; - cr = 5; - sf = 7; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST: - bw = (myRegion->wideLora) ? 812.5 : 250; - cr = 5; - sf = 7; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW: - bw = (myRegion->wideLora) ? 812.5 : 250; - cr = 5; - sf = 8; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST: - bw = (myRegion->wideLora) ? 812.5 : 250; - cr = 5; - sf = 9; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW: - bw = (myRegion->wideLora) ? 812.5 : 250; - cr = 5; - sf = 10; - break; - default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal. - bw = (myRegion->wideLora) ? 812.5 : 250; - cr = 5; - sf = 11; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE: - bw = (myRegion->wideLora) ? 406.25 : 125; - cr = 8; - sf = 11; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW: - bw = (myRegion->wideLora) ? 406.25 : 125; - cr = 8; - sf = 12; - break; - case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW: - bw = (myRegion->wideLora) ? 203.125 : 62.5; - cr = 8; - sf = 12; - break; + switch (loraConfig.modem_preset) { + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: + bw = (myRegion->wideLora) ? 812.5 : 500; + cr = 5; + sf = 7; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST: + bw = (myRegion->wideLora) ? 812.5 : 250; + cr = 5; + sf = 7; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW: + bw = (myRegion->wideLora) ? 812.5 : 250; + cr = 5; + sf = 8; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST: + bw = (myRegion->wideLora) ? 812.5 : 250; + cr = 5; + sf = 9; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW: + bw = (myRegion->wideLora) ? 812.5 : 250; + cr = 5; + sf = 10; + break; + default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal. + bw = (myRegion->wideLora) ? 812.5 : 250; + cr = 5; + sf = 11; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE: + bw = (myRegion->wideLora) ? 406.25 : 125; + cr = 8; + sf = 11; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW: + bw = (myRegion->wideLora) ? 406.25 : 125; + cr = 8; + sf = 12; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW: + bw = (myRegion->wideLora) ? 203.125 : 62.5; + cr = 8; + sf = 12; + break; + } + } else { + sf = loraConfig.spread_factor; + cr = loraConfig.coding_rate; + bw = loraConfig.bandwidth; + + if (bw == 31) // This parameter is not an integer + bw = 31.25; + if (bw == 62) // Fix for 62.5Khz bandwidth + bw = 62.5; + if (bw == 200) + bw = 203.125; + if (bw == 400) + bw = 406.25; + if (bw == 800) + bw = 812.5; + if (bw == 1600) + bw = 1625.0; } - } else { - sf = loraConfig.spread_factor; - cr = loraConfig.coding_rate; - bw = loraConfig.bandwidth; - if (bw == 31) // This parameter is not an integer - bw = 31.25; - if (bw == 62) // Fix for 62.5Khz bandwidth - bw = 62.5; - if (bw == 200) - bw = 203.125; - if (bw == 400) - bw = 406.25; - if (bw == 800) - bw = 812.5; - if (bw == 1600) - bw = 1625.0; + if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) { + static const char *err_string = + "Regional frequency range is smaller than bandwidth. Falling back to default preset.\n"; + LOG_ERROR(err_string); + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + + meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); + cn->level = meshtastic_LogRecord_Level_ERROR; + sprintf(cn->message, err_string); + service->sendClientNotification(cn); + + // Set to default modem preset + loraConfig.use_preset = true; + loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; + } else { + validConfig = true; + } } power = loraConfig.tx_power; diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 1f2ec9bab..f1016e3d8 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -5,6 +5,7 @@ #include "Observer.h" #include "PointerQueue.h" #include "airtime.h" +#include "error.h" #define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission From a85df199a5ae273910ffbf0e8f72fe073c4b61b8 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sun, 18 Aug 2024 19:15:50 +0200 Subject: [PATCH 168/305] Only accept PKI messages for MQTT downlink if we know transmitter and receiver (#4498) --- src/mqtt/MQTT.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index d3a2bb92c..22f68bac8 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -164,10 +164,14 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) // PKI messages get accepted even if we can't decrypt if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && - strcmp(e.channel_id, "PKI") == 0) - router->enqueueReceivedMessage(p); - // ignore messages if we don't have the channel key - else if (router && perhapsDecode(p)) + strcmp(e.channel_id, "PKI") == 0) { + meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p)); + meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); + // Only accept PKI messages if we have both the sender and receiver in our nodeDB, as then it's likely + // they discovered each other via a channel we have downlink enabled for + if (tx && tx->has_user && rx && rx->has_user) + router->enqueueReceivedMessage(p); + } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key router->enqueueReceivedMessage(p); else packetPool.release(p); From 22e129e7160913c1b1dae433f751fd75799f89fb Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 13:00:52 -0500 Subject: [PATCH 169/305] bluetooth != security; security = security --- src/modules/AdminModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index f1c397927..3a611d205 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -693,7 +693,7 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 case meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG: LOG_INFO("Getting config: Security\n"); res.get_config_response.which_payload_variant = meshtastic_Config_security_tag; - res.get_config_response.payload_variant.bluetooth = config.security; + res.get_config_response.payload_variant.security = config.security; break; } // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. From bfbc4bf93a89228f2852f61b2a0139396a86447d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 14:11:17 -0500 Subject: [PATCH 170/305] Set the private_key in crypto when changed by admin --- src/modules/AdminModule.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 3a611d205..e7dff60ce 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -536,6 +536,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.security = c.payload_variant.security; owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + crypto->setDHPrivateKey(config.security.private_key.bytes); if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled && config.security.serial_enabled == c.payload_variant.security.serial_enabled) requiresReboot = false; From f439081674e695bba40bab21ca77c7d05ecc3ccb Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 14:11:39 -0500 Subject: [PATCH 171/305] Don't segfault on PKI from unknown nodes --- src/mesh/ReliableRouter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 0c9180eb5..4c8c1e1e7 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -111,7 +111,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && - (nodeDB->getMeshNode(p->from) != nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { + (nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { // This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, p->hop_limit); From ecb4fb72dbca2c8dd555c3c9e8b423dfda17fc2e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 15:51:43 -0500 Subject: [PATCH 172/305] Don't break EXCLUDE_PKI --- src/modules/AdminModule.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index e7dff60ce..604541cd5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -536,7 +536,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.security = c.payload_variant.security; owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); +#if !MESHTASTIC_EXCLUDE_PKI crypto->setDHPrivateKey(config.security.private_key.bytes); +#endif if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled && config.security.serial_enabled == c.payload_variant.security.serial_enabled) requiresReboot = false; From 94d5ee9fe6f523a67e59d2197a73cdbf5eb48456 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 22:22:21 -0500 Subject: [PATCH 173/305] Deal with adminModule session_time of 0 --- src/modules/AdminModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 604541cd5..599e389df 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -960,7 +960,7 @@ AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_AP void AdminModule::setPassKey(meshtastic_AdminMessage *res) { - if (millis() / 1000 > session_time + 150) { + if (session_time == 0 || millis() / 1000 > session_time + 150) { for (int i = 0; i < 8; i++) { session_passkey[i] = random(); } From 273beef148a59f485268e73c6aa4ff3fab5bd5e9 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 18 Aug 2024 22:25:08 -0500 Subject: [PATCH 174/305] Re-set the extra-nonce value --- src/mesh/CryptoEngine.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index fbda4a678..eef9f74b1 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -42,9 +42,10 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ { uint8_t *auth; uint32_t *extraNonce; + long extraNonceTmp = random(); auth = bytesOut + numBytes; extraNonce = (uint32_t *)(auth + 8); - *extraNonce = random(); + *extraNonce = extraNonceTmp; LOG_INFO("Random nonce value: %d\n", *extraNonce); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode); if (node->num < 1 || node->user.public_key.size == 0) { @@ -59,7 +60,9 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_ // Calculate the shared secret with the destination node and encrypt printBytes("Attempting encrypt using nonce: ", nonce, 13); printBytes("Attempting encrypt using shared_key: ", shared_key, 32); - aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); + aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, + auth); // this can write up to 15 bytes longer than numbytes past bytesOut + *extraNonce = extraNonceTmp; return true; } From 14146d6ff57134a1328c3ea55293b0dcf00263d6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 19 Aug 2024 07:04:48 -0500 Subject: [PATCH 175/305] Ms! --- src/modules/NeighborInfoModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 8284c52d9..2de4374e6 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -120,7 +120,7 @@ int32_t NeighborInfoModule::runOnce() if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { sendNeighborInfo(NODENUM_BROADCAST, false); } - return Default::getConfiguredOrDefault(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs); + return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs); } /* From e65e79c6c9f4fcd3e74e21b02b4cfd967c913c93 Mon Sep 17 00:00:00 2001 From: Talie5in Date: Mon, 19 Aug 2024 21:35:28 +0930 Subject: [PATCH 176/305] We need Millseconds not... rapid fire NeighbourInfo! (#4504) From 6de3ca4301c32ec1f2c5094357597bf5ae5646e7 Mon Sep 17 00:00:00 2001 From: Mictronics Date: Mon, 19 Aug 2024 14:09:09 +0200 Subject: [PATCH 177/305] Fix deprecated macros. (#4505) * Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28 * Merge PR #420 * Fixed double and missing Default class. * Use correct format specifier and fixed typo. * Removed duplicate code. * Fix error: #if with no expression * Fix warning: extra tokens at end of #endif directive. * Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board. * Fix deprecated macros. --------- Co-authored-by: Ben Meadors --- src/modules/Telemetry/Sensor/OPT3001Sensor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp index d0e38bf88..338bd9e2c 100644 --- a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp +++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp @@ -25,10 +25,10 @@ void OPT3001Sensor::setup() { OPT3001_Config newConfig; - newConfig.RangeNumber = B1100; - newConfig.ConvertionTime = B0; - newConfig.Latch = B1; - newConfig.ModeOfConversionOperation = B11; + newConfig.RangeNumber = 0b1100; + newConfig.ConvertionTime = 0b0; + newConfig.Latch = 0b1; + newConfig.ModeOfConversionOperation = 0b11; OPT3001_ErrorCode errorConfig = opt3001.writeConfig(newConfig); if (errorConfig != NO_ERROR) { From ab9268cba93be45fbff8401aa953ff56459d18fb Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 19 Aug 2024 11:12:42 -0500 Subject: [PATCH 178/305] Admin session key debugging messages --- src/modules/AdminModule.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 599e389df..81d595d29 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -4,6 +4,7 @@ #include "NodeDB.h" #include "PowerFSM.h" #include "RTC.h" +#include "meshUtils.h" #include #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH #include "BleOta.h" @@ -968,13 +969,16 @@ void AdminModule::setPassKey(meshtastic_AdminMessage *res) } memcpy(res->session_passkey.bytes, session_passkey, 8); res->session_passkey.size = 8; + printBytes("Setting admin key to ", res->session_passkey.bytes, 8); // if halfway to session_expire, regenerate session_passkey, reset the timeout // set the key in the packet } bool AdminModule::checkPassKey(meshtastic_AdminMessage *res) { // check that the key in the packet is still valid - return (session_time + 300 < millis() / 1000 && res->session_passkey.size == 8 && + printBytes("Incoming session key: ", res->session_passkey.bytes, 8); + printBytes("Expected session key: ", session_passkey, 8); + return (session_time + 300 > millis() / 1000 && res->session_passkey.size == 8 && memcmp(res->session_passkey.bytes, session_passkey, 8) == 0); } From 9b4ad68f437565ddfe05286809d7ae9d6f070b95 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 19 Aug 2024 18:39:26 -0500 Subject: [PATCH 179/305] Add simulator back as a separate step --- .github/workflows/test_simulator.yml | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/test_simulator.yml diff --git a/.github/workflows/test_simulator.yml b/.github/workflows/test_simulator.yml new file mode 100644 index 000000000..9c33008c1 --- /dev/null +++ b/.github/workflows/test_simulator.yml @@ -0,0 +1,47 @@ +name: Test Simulator + +on: + schedule: + - cron: "0 0 * * *" # Run every day at midnight + workflow_dispatch: {} + +jobs: + test-simulator: + runs-on: ubuntu-latest + steps: + - name: Install libbluetooth + shell: bash + run: | + sudo apt-get update --fix-missing + sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Upgrade python tools + shell: bash + run: | + python -m pip install --upgrade pip + pip install -U platformio adafruit-nrfutil + pip install -U meshtastic --pre + + - name: Upgrade platformio + shell: bash + run: | + pio upgrade + + - name: Build Native + run: bin/build-native.sh + + # We now run integration test before other build steps (to quickly see runtime failures) + - name: Build for native + run: platformio run -e native + + - name: Integration test + run: | + .pio/build/native/program + & sleep 20 # 5 seconds was not enough + echo "Simulator started, launching python test..." + python3 -c 'from meshtastic.test import testSimulator; testSimulator()' From bd21a0455bab978a4c729311d991452a4a24eee1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 19 Aug 2024 19:19:16 -0500 Subject: [PATCH 180/305] & --- .github/workflows/test_simulator.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test_simulator.yml b/.github/workflows/test_simulator.yml index 9c33008c1..9dbcf0554 100644 --- a/.github/workflows/test_simulator.yml +++ b/.github/workflows/test_simulator.yml @@ -41,7 +41,6 @@ jobs: - name: Integration test run: | - .pio/build/native/program - & sleep 20 # 5 seconds was not enough + .pio/build/native/program & sleep 10 # 5 seconds was not enough echo "Simulator started, launching python test..." python3 -c 'from meshtastic.test import testSimulator; testSimulator()' From ef5279e85e7cb572bafb19d01af8dd0783b580a9 Mon Sep 17 00:00:00 2001 From: Mictronics Date: Tue, 20 Aug 2024 13:04:39 +0200 Subject: [PATCH 181/305] Set RP2040 in dormant mode when deep sleep is triggered. (#4510) * Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28 * Merge PR #420 * Fixed double and missing Default class. * Use correct format specifier and fixed typo. * Removed duplicate code. * Fix error: #if with no expression * Fix warning: extra tokens at end of #endif directive. * Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board. * Fix deprecated macros. * Set RP2040 in dormant mode when deep sleep is triggered. --------- Co-authored-by: Ben Meadors --- src/platform/rp2040/main-rp2040.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/platform/rp2040/main-rp2040.cpp b/src/platform/rp2040/main-rp2040.cpp index af3aeadc3..6306f34c1 100644 --- a/src/platform/rp2040/main-rp2040.cpp +++ b/src/platform/rp2040/main-rp2040.cpp @@ -1,4 +1,5 @@ #include "configuration.h" +#include "hardware/xosc.h" #include #include #include @@ -12,7 +13,11 @@ void setBluetoothEnable(bool enable) void cpuDeepSleep(uint32_t msecs) { - // not needed + /* Disable both PLL to avoid power dissipation */ + pll_deinit(pll_sys); + pll_deinit(pll_usb); + /* Set RP2040 in dormant mode. Will not wake up. */ + xosc_dormant(); } void updateBatteryLevel(uint8_t level) From 3b2c37c47fcebb2fe041c25c53e1fe4a0e181a9d Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 20 Aug 2024 19:19:02 +0800 Subject: [PATCH 182/305] Remove heltec-specific gps code from main.cpp (#4508) After the recent GPS power work we have an clear set of definitions for turning GPS on and off. Rather than manipulating specific heltec tracker-related pins in main setu, the relevant power management code in the GPS module will turn things on/off later as needed. --- src/main.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b6af60d2c..f4fb24fa9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -294,21 +294,11 @@ void setup() digitalWrite(VEXT_ENABLE, 0); // turn on the display power #endif -#if defined(VGNSS_CTRL_V03) - pinMode(VGNSS_CTRL_V03, OUTPUT); - digitalWrite(VGNSS_CTRL_V03, LOW); -#endif - #if defined(VTFT_CTRL_V03) pinMode(VTFT_CTRL_V03, OUTPUT); digitalWrite(VTFT_CTRL_V03, LOW); #endif -#if defined(VGNSS_CTRL) - pinMode(VGNSS_CTRL, OUTPUT); - digitalWrite(VGNSS_CTRL, LOW); -#endif - #if defined(VTFT_CTRL) pinMode(VTFT_CTRL, OUTPUT); digitalWrite(VTFT_CTRL, LOW); @@ -1121,4 +1111,4 @@ void loop() } // if (didWake) LOG_DEBUG("wake!\n"); } -#endif \ No newline at end of file +#endif From 058e9769d602d8919fcd1a02153e7231e939c066 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Tue, 20 Aug 2024 23:19:29 +1200 Subject: [PATCH 183/305] Add heartbeat LED for HT-VME290 and HT-VME213 (#4511) * Add heartbeat LED for HT-VME290 and HT-VME213 Not populated on original board, however revisions are now shipping which do have the LED * Update outdated commenting * Trunk strikes again --- variants/heltec_vision_master_e213/pins_arduino.h | 4 ++-- variants/heltec_vision_master_e213/variant.h | 1 + variants/heltec_vision_master_e290/pins_arduino.h | 4 ++-- variants/heltec_vision_master_e290/variant.h | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/variants/heltec_vision_master_e213/pins_arduino.h b/variants/heltec_vision_master_e213/pins_arduino.h index ce35348fd..56f5ef157 100644 --- a/variants/heltec_vision_master_e213/pins_arduino.h +++ b/variants/heltec_vision_master_e213/pins_arduino.h @@ -3,8 +3,8 @@ #include -static const uint8_t LED_BUILTIN = -1; // Board has no built-in LED, despite what schematic shows -#define BUILTIN_LED LED_BUILTIN // backward compatibility +static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant +#define BUILTIN_LED LED_BUILTIN // Backward compatibility #define LED_BUILTIN LED_BUILTIN static const uint8_t TX = 43; diff --git a/variants/heltec_vision_master_e213/variant.h b/variants/heltec_vision_master_e213/variant.h index 0771b3517..386df6fcf 100644 --- a/variants/heltec_vision_master_e213/variant.h +++ b/variants/heltec_vision_master_e213/variant.h @@ -1,3 +1,4 @@ +#define LED_PIN 45 // LED is not populated on earliest board variant #define BUTTON_PIN 0 #define BUTTON_PIN_SECONDARY 21 // Second built-in button #define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input diff --git a/variants/heltec_vision_master_e290/pins_arduino.h b/variants/heltec_vision_master_e290/pins_arduino.h index 77cf3176a..56f5ef157 100644 --- a/variants/heltec_vision_master_e290/pins_arduino.h +++ b/variants/heltec_vision_master_e290/pins_arduino.h @@ -3,8 +3,8 @@ #include -static const uint8_t LED_BUILTIN = -1; -#define BUILTIN_LED LED_BUILTIN // backward compatibility +static const uint8_t LED_BUILTIN = 45; // LED is not populated on earliest board variant +#define BUILTIN_LED LED_BUILTIN // Backward compatibility #define LED_BUILTIN LED_BUILTIN static const uint8_t TX = 43; diff --git a/variants/heltec_vision_master_e290/variant.h b/variants/heltec_vision_master_e290/variant.h index 72a82cfdb..299186549 100644 --- a/variants/heltec_vision_master_e290/variant.h +++ b/variants/heltec_vision_master_e290/variant.h @@ -1,3 +1,4 @@ +#define LED_PIN 45 // LED is not populated on earliest board variant #define BUTTON_PIN 0 #define BUTTON_PIN_SECONDARY 21 // Second built-in button #define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input From 2472c7cdc73dc2c763d8fed6f970b2ac464d417c Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 20 Aug 2024 19:20:01 +0800 Subject: [PATCH 184/305] JP frequency - 20mW limit, change freqs to avoid duty cycle (#4446) Thanks to user Goyath on Discord, we discovered that in Japan the 250mW radio level requires licensing, and 20mW is the practical limit. We also discovered that a duty cycle of 10% is needed on most frequencies. CH 24-38 920.5-923.5 20mW no airtime restrictions CH 39-61 923.5-928.1 20mW 10% airtime --- src/mesh/RadioInterface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index f0048dd3d..eacd49644 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -53,8 +53,10 @@ const RegionInfo regions[] = { /* https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf + https://www.arib.or.jp/english/html/overview/doc/5-STD-T108v1_5-E1.pdf + https://qiita.com/ammo0613/items/d952154f1195b64dc29f */ - RDEF(JP, 920.8f, 927.8f, 100, 0, 16, true, false, false), + RDEF(JP, 920.5f, 923.5f, 100, 0, 13, true, false, false), /* https://www.iot.org.au/wp/wp-content/uploads/2016/12/IoTSpectrumFactSheet.pdf @@ -615,4 +617,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} \ No newline at end of file +} From 2043ad3bd00dbd57e6ae7dea739a3cad3c892c00 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Tue, 20 Aug 2024 13:38:16 +0200 Subject: [PATCH 185/305] bin: remove unused imports from readprops.py (#3907) Co-authored-by: Ben Meadors --- bin/readprops.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/bin/readprops.py b/bin/readprops.py index ffa361541..4b730658a 100644 --- a/bin/readprops.py +++ b/bin/readprops.py @@ -1,9 +1,5 @@ - - -import subprocess import configparser -import traceback -import sys +import subprocess def readProps(prefsLoc): @@ -11,27 +7,36 @@ def readProps(prefsLoc): config = configparser.RawConfigParser() config.read(prefsLoc) - version = dict(config.items('VERSION')) - verObj = dict(short = "{}.{}.{}".format(version["major"], version["minor"], version["build"]), - long = "unset") + version = dict(config.items("VERSION")) + verObj = dict( + short="{}.{}.{}".format(version["major"], version["minor"], version["build"]), + long="unset", + ) # Try to find current build SHA if if the workspace is clean. This could fail if git is not installed try: - sha = subprocess.check_output( - ['git', 'rev-parse', '--short', 'HEAD']).decode("utf-8").strip() - isDirty = subprocess.check_output( - ['git', 'diff', 'HEAD']).decode("utf-8").strip() + sha = ( + subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]) + .decode("utf-8") + .strip() + ) + isDirty = ( + subprocess.check_output(["git", "diff", "HEAD"]).decode("utf-8").strip() + ) suffix = sha # if isDirty: # # short for 'dirty', we want to keep our verstrings source for protobuf reasons # suffix = sha + "-d" - verObj['long'] = "{}.{}.{}.{}".format( - version["major"], version["minor"], version["build"], suffix) + verObj["long"] = "{}.{}.{}.{}".format( + version["major"], version["minor"], version["build"], suffix + ) except: # print("Unexpected error:", sys.exc_info()[0]) # traceback.print_exc() - verObj['long'] = verObj['short'] + verObj["long"] = verObj["short"] # print("firmware version " + verStr) return verObj + + # print("path is" + ','.join(sys.path)) From 929b3e4f8809b5f9707465abd9402125a86ef4ea Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 20 Aug 2024 06:49:54 -0500 Subject: [PATCH 186/305] Add platformio tests --- .github/workflows/test_simulator.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/test_simulator.yml b/.github/workflows/test_simulator.yml index 9dbcf0554..ec0171a27 100644 --- a/.github/workflows/test_simulator.yml +++ b/.github/workflows/test_simulator.yml @@ -44,3 +44,14 @@ jobs: .pio/build/native/program & sleep 10 # 5 seconds was not enough echo "Simulator started, launching python test..." python3 -c 'from meshtastic.test import testSimulator; testSimulator()' + + - name: PlatformIO Tests + run: platformio test -e native --junit-output-path reports/testreport.xml + + - name: Test Report + uses: dorny/test-reporter@v1.9.1 + if: success() || failure() # run this step even if previous step failed + with: + name: PlatformIO Tests + path: reports/testreport.xml + reporter: java-junit From ee9e46ec929d8ae3bf368315f2b8aeff94daef3b Mon Sep 17 00:00:00 2001 From: Nestpebble <116762865+Nestpebble@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:54:18 +0100 Subject: [PATCH 187/305] Make it possible to define TCXO and XTAL radio modules within one variant (#4492) * Update main.cpp Add in TCXO_OPTIONAL variable for tcxoVoltage and a double-check for working in both modes. * Update SX126xInterface.cpp Make a change to the tcxoVoltage setting so that TCXO_OPTIONAL works if defined. * Update variant.h Added define for TCXO_OPTIONAL and the tcxoVoltage variable. Added detail on the compatible boards. --------- Co-authored-by: Ben Meadors --- src/main.cpp | 40 ++++++++++++++++++- src/mesh/SX126xInterface.cpp | 4 +- .../diy/nrf52_promicro_diy_tcxo/variant.h | 31 ++++++++------ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f4fb24fa9..48dec89e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,6 +112,10 @@ AccelerometerThread *accelerometerThread = nullptr; AudioThread *audioThread = nullptr; #endif +#if defined(TCXO_OPTIONAL) +float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this here so it can be changed further down. +#endif + using namespace concurrency; // We always create a screen object, but we only init it if we find the hardware @@ -890,7 +894,7 @@ void setup() } #endif -#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) +#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) if (!rIf) { rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { @@ -904,6 +908,40 @@ void setup() } #endif +#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL) + if (!rIf) { + // Try using the specified TCXO voltage + rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); + if (!rIf->init()) { + LOG_WARN("Failed to find SX1262 radio with TCXO using DIO3 reference voltage at %f V\n", tcxoVoltage); + delete rIf; + rIf = NULL; + tcxoVoltage = 0; // if it fails, set the TCXO voltage to zero for the next attempt + } else { + LOG_INFO("SX1262 Radio init succeeded, using "); + LOG_WARN("SX1262 Radio with TCXO"); + LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); + radioType = SX1262_RADIO; + } + } + + if (!rIf) { + // If specified TCXO voltage fails, attempt to use DIO3 as a reference instea + rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); + if (!rIf->init()) { + LOG_WARN("Failed to find SX1262 radio with XTAL using DIO3 reference voltage at %f V\n", tcxoVoltage); + delete rIf; + rIf = NULL; + tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if it fails, set the TCXO voltage back for the next radio search + } else { + LOG_INFO("SX1262 Radio init succeeded, using "); + LOG_WARN("SX1262 Radio with XTAL"); + LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); + radioType = SX1262_RADIO; + } + } +#endif + #if defined(USE_SX1268) if (!rIf) { rIf = new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index b564ba287..39ffb0ac9 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -40,7 +40,7 @@ template bool SX126xInterface::init() 0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per // https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.h#L471C26-L471C104 // (DIO3 is free to be used as an IRQ) -#else +#elif !defined(TCXO_OPTIONAL) float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // (DIO3 is not free to be used as an IRQ) #endif @@ -345,4 +345,4 @@ template bool SX126xInterface::sleep() #endif return true; -} \ No newline at end of file +} diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index b09d3bdb4..2e506d055 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -128,22 +128,29 @@ NRF52 PRO MICRO PIN ASSIGNMENT #define SX126X_RXEN (0 + 17) // P0.17 #define SX126X_TXEN RADIOLIB_NC // Assuming that DIO2 is connected to TXEN pin. If not, TXEN must be connected. +// #define SX126X_MAX_POWER 8 set this if using a high-power board! + /* -On the SX1262, DIO3 sets the voltage for an external TCXO, if one is present. If one is not present, then this should not be used. +On the SX1262, DIO3 sets the voltage for an external TCXO, if one is present. If one is not present, use TCXO_OPTIONAL to try both settings. -Ebyte -e22-900mm22s has no TCXO -e22-900m22s has TCXO -e220-900mm22s has no TCXO, works with/without this definition, looks like DIO3 not connected at all - -AI-thinker -RA-01SH does not have TCXO - -Waveshare -Core1262 has TCXO +| Mfr | Module | TCXO | RF Switch | Notes | +| ---------- | ---------------- | ---- | --------- | -------------------------------------------- | +| Ebyte | E22-900M22S | Yes | Ext | | +| Ebyte | E22-900MM22S | No | Ext | | +| Ebyte | E22-900M30S | Yes | Ext | | +| Ebyte | E22-900M33S | Yes | Ext | MAX_POWER must be set to 8 for this | +| Ebyte | E220-900M22S | No | Ext | LLCC68, looks like DIO3 not connected at all | +| AI-Thinker | RA-01SH | No | Int | | +| Heltec | HT-RA62 | Yes | Int | | +| NiceRF | Lora1262 | yes | Int | | +| Waveshare | Core1262-HF | yes | Ext | | +| Waveshare | LoRa Node Module | yes | Int | | */ + #define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL +extern float tcxoVoltage; // make this available everywhere #ifdef __cplusplus } @@ -153,4 +160,4 @@ Core1262 has TCXO * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif \ No newline at end of file +#endif From d404a493362df9325d1dc1a1d6a79deadad2867c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 20 Aug 2024 07:08:42 -0500 Subject: [PATCH 188/305] Trunk --- src/main.cpp | 48 +++++++++---------- .../diy/nrf52_promicro_diy_tcxo/variant.h | 5 +- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 48dec89e7..d38b4e669 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -911,34 +911,34 @@ void setup() #if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL) if (!rIf) { // Try using the specified TCXO voltage - rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); - if (!rIf->init()) { - LOG_WARN("Failed to find SX1262 radio with TCXO using DIO3 reference voltage at %f V\n", tcxoVoltage); - delete rIf; - rIf = NULL; - tcxoVoltage = 0; // if it fails, set the TCXO voltage to zero for the next attempt - } else { - LOG_INFO("SX1262 Radio init succeeded, using "); - LOG_WARN("SX1262 Radio with TCXO"); - LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); - radioType = SX1262_RADIO; - } + rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); + if (!rIf->init()) { + LOG_WARN("Failed to find SX1262 radio with TCXO using DIO3 reference voltage at %f V\n", tcxoVoltage); + delete rIf; + rIf = NULL; + tcxoVoltage = 0; // if it fails, set the TCXO voltage to zero for the next attempt + } else { + LOG_INFO("SX1262 Radio init succeeded, using "); + LOG_WARN("SX1262 Radio with TCXO"); + LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); + radioType = SX1262_RADIO; + } } if (!rIf) { // If specified TCXO voltage fails, attempt to use DIO3 as a reference instea - rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); - if (!rIf->init()) { - LOG_WARN("Failed to find SX1262 radio with XTAL using DIO3 reference voltage at %f V\n", tcxoVoltage); - delete rIf; - rIf = NULL; - tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if it fails, set the TCXO voltage back for the next radio search - } else { - LOG_INFO("SX1262 Radio init succeeded, using "); - LOG_WARN("SX1262 Radio with XTAL"); - LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); - radioType = SX1262_RADIO; - } + rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); + if (!rIf->init()) { + LOG_WARN("Failed to find SX1262 radio with XTAL using DIO3 reference voltage at %f V\n", tcxoVoltage); + delete rIf; + rIf = NULL; + tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if it fails, set the TCXO voltage back for the next radio search + } else { + LOG_INFO("SX1262 Radio init succeeded, using "); + LOG_WARN("SX1262 Radio with XTAL"); + LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); + radioType = SX1262_RADIO; + } } #endif diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index 2e506d055..05d4a088c 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -131,7 +131,8 @@ NRF52 PRO MICRO PIN ASSIGNMENT // #define SX126X_MAX_POWER 8 set this if using a high-power board! /* -On the SX1262, DIO3 sets the voltage for an external TCXO, if one is present. If one is not present, use TCXO_OPTIONAL to try both settings. +On the SX1262, DIO3 sets the voltage for an external TCXO, if one is present. If one is not present, use TCXO_OPTIONAL to try both +settings. | Mfr | Module | TCXO | RF Switch | Notes | | ---------- | ---------------- | ---- | --------- | -------------------------------------------- | @@ -149,7 +150,7 @@ On the SX1262, DIO3 sets the voltage for an external TCXO, if one is present. If */ #define SX126X_DIO3_TCXO_VOLTAGE 1.8 -#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL extern float tcxoVoltage; // make this available everywhere #ifdef __cplusplus From 2d9126f87378fc08a6c048e63d692a932a370e03 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 20 Aug 2024 07:17:39 -0500 Subject: [PATCH 189/305] Try cwd --- .github/workflows/test_simulator.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_simulator.yml b/.github/workflows/test_simulator.yml index ec0171a27..1d20657a0 100644 --- a/.github/workflows/test_simulator.yml +++ b/.github/workflows/test_simulator.yml @@ -46,12 +46,12 @@ jobs: python3 -c 'from meshtastic.test import testSimulator; testSimulator()' - name: PlatformIO Tests - run: platformio test -e native --junit-output-path reports/testreport.xml + run: platformio test -e native --junit-output-path testreport.xml - name: Test Report uses: dorny/test-reporter@v1.9.1 if: success() || failure() # run this step even if previous step failed with: name: PlatformIO Tests - path: reports/testreport.xml + path: testreport.xml reporter: java-junit From 314009a10fa0d0afa4480ff9c0068901a1560c1f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 20 Aug 2024 07:35:47 -0500 Subject: [PATCH 190/305] Version 2.5 bump --- version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.properties b/version.properties index 0450e7054..95d3d2538 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 -minor = 4 -build = 3 +minor = 5 +build = 0 From 6ee30043c31fe7353a53c8cb35dd8fbaec8866ed Mon Sep 17 00:00:00 2001 From: Mictronics Date: Tue, 20 Aug 2024 20:36:10 +0200 Subject: [PATCH 191/305] Fix array out of bounds read. (#4514) * Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28 * Merge PR #420 * Fixed double and missing Default class. * Use correct format specifier and fixed typo. * Removed duplicate code. * Fix error: #if with no expression * Fix warning: extra tokens at end of #endif directive. * Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board. * Fix deprecated macros. * Set RP2040 in dormant mode when deep sleep is triggered. * Fix array out of bounds read. --------- Co-authored-by: Ben Meadors --- src/modules/AdminModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 81d595d29..9d3d5f53f 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -350,7 +350,7 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp, if (devicestate.node_remote_hardware_pins[i].node_num == 0 || !devicestate.node_remote_hardware_pins[i].has_pin) { continue; } - for (uint8_t j = 0; j < sizeof(r->get_module_config_response.payload_variant.remote_hardware.available_pins); j++) { + for (uint8_t j = 0; j < r->get_module_config_response.payload_variant.remote_hardware.available_pins_count; j++) { auto availablePin = r->get_module_config_response.payload_variant.remote_hardware.available_pins[j]; if (i < devicestate.node_remote_hardware_pins_count) { devicestate.node_remote_hardware_pins[i].node_num = mp.from; From ab7de7f6a0be5e5130801ffadfd2166d734aaa6d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 20 Aug 2024 13:36:24 -0500 Subject: [PATCH 192/305] Add handling for sessionkey config (#4513) * Add handling for sessionkey config * Protos --------- Co-authored-by: Ben Meadors --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 8 +++++--- src/mesh/generated/meshtastic/config.pb.cpp | 3 +++ src/mesh/generated/meshtastic/config.pb.h | 22 ++++++++++++++++++++- src/modules/AdminModule.cpp | 4 ++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/protobufs b/protobufs index 4eb4f4251..56a435507 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 4eb4f425170f08839abc6ececd13e8db30094ad5 +Subproject commit 56a4355070f3371213d48f3a8cac1ddaf0d553fe diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index 99b8fd8c3..c1ff7ebd4 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -32,7 +32,9 @@ typedef enum _meshtastic_AdminMessage_ConfigType { /* TODO: REPLACE */ meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6, /* TODO: REPLACE */ - meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG = 7 + meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG = 7, + /* */ + meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG = 8 } meshtastic_AdminMessage_ConfigType; /* TODO: REPLACE */ @@ -204,8 +206,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG -#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG -#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG+1)) +#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG +#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG+1)) #define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG #define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp index c6274aed4..92c3313bd 100644 --- a/src/mesh/generated/meshtastic/config.pb.cpp +++ b/src/mesh/generated/meshtastic/config.pb.cpp @@ -36,6 +36,9 @@ PB_BIND(meshtastic_Config_BluetoothConfig, meshtastic_Config_BluetoothConfig, AU PB_BIND(meshtastic_Config_SecurityConfig, meshtastic_Config_SecurityConfig, AUTO) +PB_BIND(meshtastic_Config_SessionkeyConfig, meshtastic_Config_SessionkeyConfig, AUTO) + + diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index dbb0deb00..2f4c00fb0 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -554,6 +554,11 @@ typedef struct _meshtastic_Config_SecurityConfig { bool admin_channel_enabled; } meshtastic_Config_SecurityConfig; +/* Blank config request, strictly for getting the session key */ +typedef struct _meshtastic_Config_SessionkeyConfig { + char dummy_field; +} meshtastic_Config_SessionkeyConfig; + typedef struct _meshtastic_Config { pb_size_t which_payload_variant; union { @@ -565,6 +570,7 @@ typedef struct _meshtastic_Config { meshtastic_Config_LoRaConfig lora; meshtastic_Config_BluetoothConfig bluetooth; meshtastic_Config_SecurityConfig security; + meshtastic_Config_SessionkeyConfig sessionkey; } payload_variant; } meshtastic_Config; @@ -649,6 +655,7 @@ extern "C" { + /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} #define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -660,6 +667,7 @@ extern "C" { #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} #define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} +#define meshtastic_Config_SessionkeyConfig_init_default {0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} @@ -670,6 +678,7 @@ extern "C" { #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} #define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} +#define meshtastic_Config_SessionkeyConfig_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Config_DeviceConfig_role_tag 1 @@ -766,6 +775,7 @@ extern "C" { #define meshtastic_Config_lora_tag 6 #define meshtastic_Config_bluetooth_tag 7 #define meshtastic_Config_security_tag 8 +#define meshtastic_Config_sessionkey_tag 9 /* Struct field encoding specification for nanopb */ #define meshtastic_Config_FIELDLIST(X, a) \ @@ -776,7 +786,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,network,payload_variant.netw X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.display), 5) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.sessionkey), 9) #define meshtastic_Config_CALLBACK NULL #define meshtastic_Config_DEFAULT NULL #define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -787,6 +798,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.sec #define meshtastic_Config_payload_variant_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig #define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig +#define meshtastic_Config_payload_variant_sessionkey_MSGTYPE meshtastic_Config_SessionkeyConfig #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ @@ -911,6 +923,11 @@ X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8) #define meshtastic_Config_SecurityConfig_CALLBACK NULL #define meshtastic_Config_SecurityConfig_DEFAULT NULL +#define meshtastic_Config_SessionkeyConfig_FIELDLIST(X, a) \ + +#define meshtastic_Config_SessionkeyConfig_CALLBACK NULL +#define meshtastic_Config_SessionkeyConfig_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_Config_msg; extern const pb_msgdesc_t meshtastic_Config_DeviceConfig_msg; extern const pb_msgdesc_t meshtastic_Config_PositionConfig_msg; @@ -921,6 +938,7 @@ extern const pb_msgdesc_t meshtastic_Config_DisplayConfig_msg; extern const pb_msgdesc_t meshtastic_Config_LoRaConfig_msg; extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg; +extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_Config_fields &meshtastic_Config_msg @@ -933,6 +951,7 @@ extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg; #define meshtastic_Config_LoRaConfig_fields &meshtastic_Config_LoRaConfig_msg #define meshtastic_Config_BluetoothConfig_fields &meshtastic_Config_BluetoothConfig_msg #define meshtastic_Config_SecurityConfig_fields &meshtastic_Config_SecurityConfig_msg +#define meshtastic_Config_SessionkeyConfig_fields &meshtastic_Config_SessionkeyConfig_msg /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size @@ -945,6 +964,7 @@ extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg; #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 #define meshtastic_Config_SecurityConfig_size 112 +#define meshtastic_Config_SessionkeyConfig_size 0 #define meshtastic_Config_size 199 #ifdef __cplusplus diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 9d3d5f53f..d64aea5d8 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -699,6 +699,10 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 res.get_config_response.which_payload_variant = meshtastic_Config_security_tag; res.get_config_response.payload_variant.security = config.security; break; + case meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG: + LOG_INFO("Getting config: Sessionkey\n"); + res.get_config_response.which_payload_variant = meshtastic_Config_sessionkey_tag; + break; } // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. // So even if we internally use 0 to represent 'use default' we still need to send the value we are From 48e0fd7ed03405de78020b5c773b5306a3918226 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 20 Aug 2024 15:38:39 -0700 Subject: [PATCH 193/305] fix #4448 (by seeing there is actually no problem) (#4517) Print directory names when listing directories --- src/FSCommon.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index f45319c0b..d6a542808 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -249,6 +249,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) file.close(); } #else + LOG_DEBUG(" %s (directory)\n", file.name()); listDir(file.name(), levels - 1, del); file.close(); #endif @@ -275,7 +276,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) file.close(); } #else - LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size()); + LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size()); file.close(); #endif } From d556ae762c208cc0e30cc410b6ed761bf439949c Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Wed, 21 Aug 2024 13:04:03 +0200 Subject: [PATCH 194/305] Update ScanI2CTwoWire.cpp (#4520) --- src/detect/ScanI2CTwoWire.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index b831b0e71..1183d0ddc 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -315,7 +315,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) case SHT31_4x_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); - if (registerValue == 0x11a2 || registerValue == 0x11da) { + if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) { type = SHT4X; LOG_INFO("SHT4X sensor found\n"); } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { @@ -404,4 +404,4 @@ size_t ScanI2CTwoWire::countDevices() const { return foundDevices.size(); } -#endif \ No newline at end of file +#endif From 6ddee795d6c8742e317759ce330a8a91bd37b8ff Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 21 Aug 2024 17:24:56 -0500 Subject: [PATCH 195/305] Poetry --- .github/actions/setup-base/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 61466655d..929c1df38 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -35,6 +35,7 @@ runs: python -m pip install --upgrade pip pip install -U --no-build-isolation --no-cache-dir "setuptools<72" pip install -U platformio adafruit-nrfutil --no-build-isolation + pip install -U poetry --no-build-isolation pip install -U meshtastic --pre --no-build-isolation - name: Upgrade platformio From d017fc7a5d392eb35b197c104a33ab2c533bc6ea Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 21 Aug 2024 16:53:12 -0700 Subject: [PATCH 196/305] for #4154 use internal pull-ups to power ADC_Ctrl * Currently only on heltec tracker, but could use ADC_USE_PULLUP on other boards that could benefit * Thanks @todd-herbert and @StevenCellist for the instructions ;-) * Remove nasty Heltec_wireless #ifdefs that got somehow added to Power.cpp, instead use proper variant defs * Cleanup adc enable/disable code a bit for less copy-paste cruft --- src/Power.cpp | 60 ++++++++++----------- variants/heltec_wireless_paper/variant.h | 3 +- variants/heltec_wireless_paper_v1/variant.h | 3 +- variants/heltec_wireless_tracker/variant.h | 6 +-- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index d63c43137..61a6c987d 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -136,6 +136,30 @@ using namespace meshtastic; */ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor +static void adcEnable() +{ +#ifdef ADC_CTRL // enable adc voltage divider when we need to read +#ifdef ADC_USE_PULLUP + pinMode(ADC_CTRL, INPUT_PULLUP); +#else + pinMode(ADC_CTRL, OUTPUT); + digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED); +#endif + delay(10); +#endif +} + +static void adcDisable() +{ +#ifdef ADC_CTRL // disable adc voltage divider when we need to read +#ifdef ADC_USE_PULLUP + pinMode(ADC_CTRL, INPUT_PULLDOWN); +#else + digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED); +#endif +#endif +} + /** * A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input */ @@ -226,25 +250,19 @@ class AnalogBatteryLevel : public HasBatteryLevel uint32_t raw = 0; float scaled = 0; + adcEnable(); #ifdef ARCH_ESP32 // ADC block for espressif platforms raw = espAdcRead(); scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs); scaled *= operativeAdcMultiplier; -#else // block for all other platforms -#ifdef ADC_CTRL // enable adc voltage divider when we need to read - pinMode(ADC_CTRL, OUTPUT); - digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED); - delay(10); -#endif +#else // block for all other platforms for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) { raw += analogRead(BATTERY_PIN); } raw = raw / BATTERY_SENSE_SAMPLES; scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw; -#ifdef ADC_CTRL // disable adc voltage divider when we need to read - digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED); -#endif #endif + adcDisable(); if (!initial_read_done) { // Flush the smoothing filter with an ADC reading, if the reading is plausibly correct @@ -275,11 +293,6 @@ class AnalogBatteryLevel : public HasBatteryLevel uint8_t raw_c = 0; // raw reading counter #ifndef BAT_MEASURE_ADC_UNIT // ADC1 -#ifdef ADC_CTRL // enable adc voltage divider when we need to read - pinMode(ADC_CTRL, OUTPUT); - digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED); - delay(10); -#endif for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) { int val_ = adc1_get_raw(adc_channel); if (val_ >= 0) { // save only valid readings @@ -288,18 +301,7 @@ class AnalogBatteryLevel : public HasBatteryLevel } // delayMicroseconds(100); } -#ifdef ADC_CTRL // disable adc voltage divider when we need to read - digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED); -#endif -#else // ADC2 -#ifdef ADC_CTRL -#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) - pinMode(ADC_CTRL, OUTPUT); - digitalWrite(ADC_CTRL, LOW); // ACTIVE LOW - delay(10); -#endif -#endif // End ADC_CTRL - +#else // ADC2 #ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 // ADC2 wifi bug workaround not required, breaks compile // On ESP32S3, ADC2 can take turns with Wifi (?) @@ -334,12 +336,6 @@ class AnalogBatteryLevel : public HasBatteryLevel } #endif // BAT_MEASURE_ADC_UNIT -#ifdef ADC_CTRL -#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) - digitalWrite(ADC_CTRL, HIGH); -#endif -#endif // End ADC_CTRL - #endif // End BAT_MEASURE_ADC_UNIT return (raw / (raw_c < 1 ? 1 : raw_c)); } diff --git a/variants/heltec_wireless_paper/variant.h b/variants/heltec_wireless_paper/variant.h index a7bd460f7..36ab80445 100644 --- a/variants/heltec_wireless_paper/variant.h +++ b/variants/heltec_wireless_paper/variant.h @@ -29,6 +29,7 @@ #define BAT_MEASURE_ADC_UNIT 2 // Use ADC2 #define ADC_ATTENUATION ADC_ATTEN_DB_12 // Voltage divider output is quite high #define HAS_32768HZ +#define ADC_CTRL_ENABLED LOW // LoRa #define USE_SX1262 @@ -49,4 +50,4 @@ #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 \ No newline at end of file diff --git a/variants/heltec_wireless_paper_v1/variant.h b/variants/heltec_wireless_paper_v1/variant.h index a7bd460f7..36ab80445 100644 --- a/variants/heltec_wireless_paper_v1/variant.h +++ b/variants/heltec_wireless_paper_v1/variant.h @@ -29,6 +29,7 @@ #define BAT_MEASURE_ADC_UNIT 2 // Use ADC2 #define ADC_ATTENUATION ADC_ATTEN_DB_12 // Voltage divider output is quite high #define HAS_32768HZ +#define ADC_CTRL_ENABLED LOW // LoRa #define USE_SX1262 @@ -49,4 +50,4 @@ #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 \ No newline at end of file diff --git a/variants/heltec_wireless_tracker/variant.h b/variants/heltec_wireless_tracker/variant.h index 685c9f079..a2ca095d8 100644 --- a/variants/heltec_wireless_tracker/variant.h +++ b/variants/heltec_wireless_tracker/variant.h @@ -38,8 +38,8 @@ #define ADC_CHANNEL ADC1_GPIO1_CHANNEL #define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider #define ADC_MULTIPLIER 4.9 * 1.045 -#define ADC_CTRL 2 // active HIGH, powers the voltage divider. Only on 1.1 -#define ADC_CTRL_ENABLED HIGH +#define ADC_CTRL 2 // active HIGH, powers the voltage divider. Only on 1.1 +#define ADC_USE_PULLUP // Use internal pullup/pulldown instead of actively driving the output #undef GPS_RX_PIN #undef GPS_TX_PIN @@ -72,4 +72,4 @@ #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 \ No newline at end of file From 1e655052fc66be1876d2d4e87b5a99da407f08e6 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Thu, 22 Aug 2024 14:00:19 +0200 Subject: [PATCH 197/305] Fixes for ME25LS01_4Y10TD and ESP32-PICO (#4522) * Update platformio.ini * Update variant.h * Update architecture.h * Update variant.h --- src/platform/nrf52/architecture.h | 4 ++-- variants/ME25LS01-4Y10TD_e-ink/variant.h | 4 ++-- variants/esp32-s3-pico/platformio.ini | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 895525f5a..834ff6f0c 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -65,8 +65,8 @@ #define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110 #elif defined(TRACKER_T1000_E) #define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E -#elif defined(ME25LS01) -#define HW_VENDOR meshtastic_HardwareModel_ME25LS01 +#elif defined(ME25LS01_4Y10TD) +#define HW_VENDOR meshtastic_HardwareModel_ME25LS01_4Y10TD #elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #else diff --git a/variants/ME25LS01-4Y10TD_e-ink/variant.h b/variants/ME25LS01-4Y10TD_e-ink/variant.h index 9006d5a63..60996d468 100644 --- a/variants/ME25LS01-4Y10TD_e-ink/variant.h +++ b/variants/ME25LS01-4Y10TD_e-ink/variant.h @@ -110,8 +110,8 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_EINK_BUSY (0 + 19) // EPD_BUSY #define PIN_EINK_DC (0 + 24) // EPD_D/C #define PIN_EINK_RES (0 + 23) // EPD_RESET -#define PIN_EINK_SCLK (0 + 9) // EPD_SCLK -#define PIN_EINK_MOSI (0 + 10) // EPD_MOSI +#define PIN_EINK_SCLK PIN_SPI1_SCK +#define PIN_EINK_MOSI PIN_SPI1_MOSI // supported modules list #define USE_LR1110 diff --git a/variants/esp32-s3-pico/platformio.ini b/variants/esp32-s3-pico/platformio.ini index ff77c30e0..916f623bd 100644 --- a/variants/esp32-s3-pico/platformio.ini +++ b/variants/esp32-s3-pico/platformio.ini @@ -11,7 +11,7 @@ board_upload.require_upload_port = yes ;upload_port = /dev/ttyACM0 -build_flags = ${esp32_base.build_flags} +build_flags = ${esp32s3_base.build_flags} -DESP32_S3_PICO ;-DPRIVATE_HW -Ivariants/esp32-s3-pico @@ -22,4 +22,4 @@ build_flags = ${esp32_base.build_flags} lib_deps = ${esp32s3_base.lib_deps} zinggjm/GxEPD2@^1.5.3 - adafruit/Adafruit NeoPixel @ ^1.12.0 \ No newline at end of file + adafruit/Adafruit NeoPixel @ ^1.12.0 From 734f36589dd54f60e9a07e3dbbb368716642235d Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Thu, 22 Aug 2024 17:44:49 +0200 Subject: [PATCH 198/305] Update variant.h (#4534) --- variants/wio-e5/variant.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/variants/wio-e5/variant.h b/variants/wio-e5/variant.h index b4345a530..ac92915bb 100644 --- a/variants/wio-e5/variant.h +++ b/variants/wio-e5/variant.h @@ -15,4 +15,7 @@ Do not expect a working Meshtastic device with this target. #define USE_STM32WLx #define MAX_NUM_NODES 10 -#endif \ No newline at end of file +#define LED_PIN PB5 +#define LED_STATE_ON 1 + +#endif From 7fb9b094d522fefb41c8e13ee8de8cf526b8b227 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 08:59:46 -0700 Subject: [PATCH 199/305] Remove redundant ST7735_BL variant defs. No need for _V05 and _V03 definitions - I think there was a slight misunderstanding on how variant files are supposed to _decrease_ #ifdef code in the cpp files. --- src/graphics/TFTDisplay.cpp | 32 ++++++------------- src/main.cpp | 6 +--- src/sleep.cpp | 1 - variants/heltec_wireless_tracker/variant.h | 2 +- .../heltec_wireless_tracker_V1_0/variant.h | 4 +-- variants/tracksenger/internal/variant.h | 2 +- variants/tracksenger/lcd/variant.h | 4 +-- variants/tracksenger/oled/variant.h | 2 +- 8 files changed, 18 insertions(+), 35 deletions(-) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 8ea90c523..73ad4d130 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -95,14 +95,8 @@ class LGFX : public lgfx::LGFX_Device { auto cfg = _light_instance.config(); // Gets a structure for backlight settings. -#ifdef ST7735_BL_V03 - cfg.pin_bl = ST7735_BL_V03; -#elif defined(ST7735_BL_V05) - cfg.pin_bl = ST7735_BL_V05; -#else cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected -#endif - cfg.invert = true; // true to invert the brightness of the backlight + cfg.invert = true; // true to invert the brightness of the backlight // cfg.freq = 44100; // PWM frequency of backlight // cfg.pwm_channel = 1; // PWM channel number to use @@ -581,11 +575,9 @@ void TFTDisplay::sendCommand(uint8_t com) display(true); if (settingsMap[displayBacklight] > 0) digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL_V03) - digitalWrite(ST7735_BL_V03, TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL_V05) - pinMode(ST7735_BL_V05, OUTPUT); - digitalWrite(ST7735_BL_V05, TFT_BACKLIGHT_ON); +#elif defined(ST7735_BL) + pinMode(ST7735_BL, OUTPUT); + digitalWrite(ST7735_BL, TFT_BACKLIGHT_ON); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) tft->wakeup(); tft->powerSaveOff(); @@ -614,11 +606,9 @@ void TFTDisplay::sendCommand(uint8_t com) tft->clear(); if (settingsMap[displayBacklight] > 0) digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL_V03) - digitalWrite(ST7735_BL_V03, !TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL_V05) - pinMode(ST7735_BL_V05, OUTPUT); - digitalWrite(ST7735_BL_V05, !TFT_BACKLIGHT_ON); +#elif defined(ST7735_BL) + pinMode(ST7735_BL, OUTPUT); + digitalWrite(ST7735_BL, !TFT_BACKLIGHT_ON); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) tft->sleep(); tft->powerSaveOn(); @@ -720,11 +710,9 @@ bool TFTDisplay::connect() LOG_INFO("Power to TFT Backlight\n"); #endif -#ifdef ST7735_BL_V03 - digitalWrite(ST7735_BL_V03, TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL_V05) - pinMode(ST7735_BL_V05, OUTPUT); - digitalWrite(ST7735_BL_V05, TFT_BACKLIGHT_ON); +#ifdef ST7735_BL + pinMode(ST7735_BL, OUTPUT); + digitalWrite(ST7735_BL, TFT_BACKLIGHT_ON); #endif #ifdef UNPHONE unphone.backlight(true); // using unPhone library diff --git a/src/main.cpp b/src/main.cpp index d38b4e669..968ee6053 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -280,15 +280,11 @@ void setup() #if defined(VEXT_ENABLE_V03) pinMode(VEXT_ENABLE_V03, OUTPUT); - pinMode(ST7735_BL_V03, OUTPUT); digitalWrite(VEXT_ENABLE_V03, 0); // turn on the display power and antenna boost - digitalWrite(ST7735_BL_V03, 1); // display backligth on LOG_DEBUG("HELTEC Detect Tracker V1.0\n"); #elif defined(VEXT_ENABLE_V05) pinMode(VEXT_ENABLE_V05, OUTPUT); - pinMode(ST7735_BL_V05, OUTPUT); digitalWrite(VEXT_ENABLE_V05, 1); // turn on the lora antenna boost - digitalWrite(ST7735_BL_V05, 1); // turn on display backligth LOG_DEBUG("HELTEC Detect Tracker V1.1\n"); #elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE) pinMode(VEXT_ENABLE, OUTPUT); @@ -1149,4 +1145,4 @@ void loop() } // if (didWake) LOG_DEBUG("wake!\n"); } -#endif +#endif \ No newline at end of file diff --git a/src/sleep.cpp b/src/sleep.cpp index bf50d8ffa..693d02a29 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -250,7 +250,6 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) digitalWrite(VEXT_ENABLE_V03, 1); // turn off the display power #elif defined(VEXT_ENABLE_V05) digitalWrite(VEXT_ENABLE_V05, 0); // turn off the lora amplifier power - digitalWrite(ST7735_BL_V05, 0); // turn off the display power #elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE) digitalWrite(VEXT_ENABLE, !VEXT_ON_VALUE); // turn on the display power #elif defined(VEXT_ENABLE) diff --git a/variants/heltec_wireless_tracker/variant.h b/variants/heltec_wireless_tracker/variant.h index a2ca095d8..f10612601 100644 --- a/variants/heltec_wireless_tracker/variant.h +++ b/variants/heltec_wireless_tracker/variant.h @@ -15,7 +15,7 @@ #define ST7735_RESET 39 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL_V05 21 /* V1.1 PCB marking */ +#define ST7735_BL 21 /* V1.1 PCB marking */ #define ST7735_SPI_HOST SPI3_HOST #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 diff --git a/variants/heltec_wireless_tracker_V1_0/variant.h b/variants/heltec_wireless_tracker_V1_0/variant.h index 23987adf0..e97219163 100644 --- a/variants/heltec_wireless_tracker_V1_0/variant.h +++ b/variants/heltec_wireless_tracker_V1_0/variant.h @@ -15,7 +15,7 @@ #define ST7735_RESET 39 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL_V03 45 +#define ST7735_BL 45 #define ST7735_SPI_HOST SPI3_HOST #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 @@ -69,4 +69,4 @@ #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 \ No newline at end of file diff --git a/variants/tracksenger/internal/variant.h b/variants/tracksenger/internal/variant.h index 929c38793..75d81752b 100644 --- a/variants/tracksenger/internal/variant.h +++ b/variants/tracksenger/internal/variant.h @@ -17,7 +17,7 @@ #define ST7735_RESET 39 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL_V05 21 /* V1.1 PCB marking */ +#define ST7735_BL 21 /* V1.1 PCB marking */ #define ST7735_SPI_HOST SPI3_HOST #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 diff --git a/variants/tracksenger/lcd/variant.h b/variants/tracksenger/lcd/variant.h index 3f952361b..7e814de68 100644 --- a/variants/tracksenger/lcd/variant.h +++ b/variants/tracksenger/lcd/variant.h @@ -16,7 +16,7 @@ #define ST7789_CS 38 #define ST7789_RS 40 #define ST7789_BL 21 -// P#define ST7735_BL_V05 21 /* V1.1 PCB marking */ +// P#define ST7735_BL 21 /* V1.1 PCB marking */ #define ST7789_RESET -1 #define ST7789_MISO -1 @@ -41,7 +41,7 @@ // #define ST7735_RESET 39 // #define ST7735_MISO -1 // #define ST7735_BUSY -1 -#define ST7735_BL_V05 21 /* V1.1 PCB marking */ +#define ST7735_BL 21 /* V1.1 PCB marking */ // #define ST7735_SPI_HOST SPI3_HOST // #define SPI_FREQUENCY 40000000 // #define SPI_READ_FREQUENCY 16000000 diff --git a/variants/tracksenger/oled/variant.h b/variants/tracksenger/oled/variant.h index 99f12bd23..10bd7358b 100644 --- a/variants/tracksenger/oled/variant.h +++ b/variants/tracksenger/oled/variant.h @@ -19,7 +19,7 @@ // #define ST7735_RESET 39 // #define ST7735_MISO -1 // #define ST7735_BUSY -1 -#define ST7735_BL_V05 21 /* V1.1 PCB marking */ +#define ST7735_BL 21 /* V1.1 PCB marking */ // #define ST7735_SPI_HOST SPI3_HOST // #define SPI_FREQUENCY 40000000 // #define SPI_READ_FREQUENCY 16000000 From 3ae8aadaf0214d86c20842b3e67eed4bb0056ed1 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 09:15:59 -0700 Subject: [PATCH 200/305] Merge the three redundant backlight enables into the single TFT_BL flag --- src/graphics/TFTDisplay.cpp | 30 +++++++------------ variants/chatter2/variant.h | 2 +- variants/heltec_wireless_tracker/variant.h | 2 +- .../heltec_wireless_tracker_V1_0/variant.h | 2 +- variants/lora_relay_v1/variant.h | 4 +-- variants/lora_relay_v2/variant.h | 4 +-- variants/tracksenger/internal/variant.h | 4 +-- variants/tracksenger/lcd/variant.h | 6 ++-- variants/tracksenger/oled/variant.h | 2 +- 9 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 73ad4d130..d20733e90 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -21,10 +21,6 @@ extern SX1509 gpioExtender; #if defined(ST7735S) #include // Graphics and font library for ST7735 driver chip -#if defined(ST7735_BACKLIGHT_EN) && !defined(TFT_BL) -#define TFT_BL ST7735_BACKLIGHT_EN -#endif - #ifndef TFT_INVERT #define TFT_INVERT true #endif @@ -91,18 +87,20 @@ class LGFX : public lgfx::LGFX_Device _panel_instance.config(cfg); } +#ifdef TFT_BL // Set the backlight control { auto cfg = _light_instance.config(); // Gets a structure for backlight settings. - cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected - cfg.invert = true; // true to invert the brightness of the backlight + cfg.pin_bl = TFT_BL; // Pin number to which the backlight is connected + cfg.invert = true; // true to invert the brightness of the backlight // cfg.freq = 44100; // PWM frequency of backlight // cfg.pwm_channel = 1; // PWM channel number to use _light_instance.config(cfg); _panel_instance.setLight(&_light_instance); // Set the backlight on the panel. } +#endif setPanel(&_panel_instance); } @@ -575,14 +573,12 @@ void TFTDisplay::sendCommand(uint8_t com) display(true); if (settingsMap[displayBacklight] > 0) digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL) - pinMode(ST7735_BL, OUTPUT); - digitalWrite(ST7735_BL, TFT_BACKLIGHT_ON); +#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) tft->wakeup(); tft->powerSaveOff(); -#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) - digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); #endif #ifdef VTFT_CTRL_V03 @@ -606,14 +602,12 @@ void TFTDisplay::sendCommand(uint8_t com) tft->clear(); if (settingsMap[displayBacklight] > 0) digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON); -#elif defined(ST7735_BL) - pinMode(ST7735_BL, OUTPUT); - digitalWrite(ST7735_BL, !TFT_BACKLIGHT_ON); +#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) tft->sleep(); tft->powerSaveOn(); -#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) - digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); #endif #ifdef VTFT_CTRL_V03 @@ -710,10 +704,6 @@ bool TFTDisplay::connect() LOG_INFO("Power to TFT Backlight\n"); #endif -#ifdef ST7735_BL - pinMode(ST7735_BL, OUTPUT); - digitalWrite(ST7735_BL, TFT_BACKLIGHT_ON); -#endif #ifdef UNPHONE unphone.backlight(true); // using unPhone library LOG_INFO("Power to TFT Backlight\n"); diff --git a/variants/chatter2/variant.h b/variants/chatter2/variant.h index 70438e52a..b7f946970 100644 --- a/variants/chatter2/variant.h +++ b/variants/chatter2/variant.h @@ -54,7 +54,7 @@ #define ST7735_RESET 15 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL 32 +#define TFT_BL 32 #define ST7735_SPI_HOST HSPI_HOST // SPI2_HOST for S3, auto may work too #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 diff --git a/variants/heltec_wireless_tracker/variant.h b/variants/heltec_wireless_tracker/variant.h index f10612601..519c76d8f 100644 --- a/variants/heltec_wireless_tracker/variant.h +++ b/variants/heltec_wireless_tracker/variant.h @@ -15,7 +15,7 @@ #define ST7735_RESET 39 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL 21 /* V1.1 PCB marking */ +#define TFT_BL 21 /* V1.1 PCB marking */ #define ST7735_SPI_HOST SPI3_HOST #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 diff --git a/variants/heltec_wireless_tracker_V1_0/variant.h b/variants/heltec_wireless_tracker_V1_0/variant.h index e97219163..b638dec53 100644 --- a/variants/heltec_wireless_tracker_V1_0/variant.h +++ b/variants/heltec_wireless_tracker_V1_0/variant.h @@ -15,7 +15,7 @@ #define ST7735_RESET 39 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL 45 +#define TFT_BL 45 #define ST7735_SPI_HOST SPI3_HOST #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 diff --git a/variants/lora_relay_v1/variant.h b/variants/lora_relay_v1/variant.h index 54bc87b68..6efd711c6 100644 --- a/variants/lora_relay_v1/variant.h +++ b/variants/lora_relay_v1/variant.h @@ -144,7 +144,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define ST7735_RESET (11) // Output #define ST7735_CS (12) -#define ST7735_BACKLIGHT_EN (13) +#define TFT_BL (13) #define ST7735_RS (9) // #define LORA_DISABLE_SENDING // The board can brownout during lora TX if you don't have a battery connected. Disable sending @@ -158,4 +158,4 @@ static const uint8_t SCK = PIN_SPI_SCK; * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif +#endif \ No newline at end of file diff --git a/variants/lora_relay_v2/variant.h b/variants/lora_relay_v2/variant.h index 6ef7ad7d6..f18f81034 100644 --- a/variants/lora_relay_v2/variant.h +++ b/variants/lora_relay_v2/variant.h @@ -166,7 +166,7 @@ static const uint8_t SCK = PIN_SPI_SCK; // ST7565 SPI #define ST7735_RESET (11) // Output #define ST7735_CS (12) -#define ST7735_BACKLIGHT_EN (13) +#define TFT_BL (13) #define ST7735_RS (9) #define ST7735_SDA (39) // actually spi MOSI #define ST7735_SCK (37) // actually spi clk @@ -185,4 +185,4 @@ static const uint8_t SCK = PIN_SPI_SCK; * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif +#endif \ No newline at end of file diff --git a/variants/tracksenger/internal/variant.h b/variants/tracksenger/internal/variant.h index 75d81752b..bd4a51a2d 100644 --- a/variants/tracksenger/internal/variant.h +++ b/variants/tracksenger/internal/variant.h @@ -17,7 +17,7 @@ #define ST7735_RESET 39 #define ST7735_MISO -1 #define ST7735_BUSY -1 -#define ST7735_BL 21 /* V1.1 PCB marking */ +#define TFT_BL 21 /* V1.1 PCB marking */ #define ST7735_SPI_HOST SPI3_HOST #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 @@ -88,4 +88,4 @@ { \ 26, 37, 17, 16, 15, 7 \ } -// #end keyboard +// #end keyboard \ No newline at end of file diff --git a/variants/tracksenger/lcd/variant.h b/variants/tracksenger/lcd/variant.h index 7e814de68..17b012ae7 100644 --- a/variants/tracksenger/lcd/variant.h +++ b/variants/tracksenger/lcd/variant.h @@ -16,7 +16,7 @@ #define ST7789_CS 38 #define ST7789_RS 40 #define ST7789_BL 21 -// P#define ST7735_BL 21 /* V1.1 PCB marking */ +// P#define TFT_BL 21 /* V1.1 PCB marking */ #define ST7789_RESET -1 #define ST7789_MISO -1 @@ -41,7 +41,7 @@ // #define ST7735_RESET 39 // #define ST7735_MISO -1 // #define ST7735_BUSY -1 -#define ST7735_BL 21 /* V1.1 PCB marking */ +#define TFT_BL 21 /* V1.1 PCB marking */ // #define ST7735_SPI_HOST SPI3_HOST // #define SPI_FREQUENCY 40000000 // #define SPI_READ_FREQUENCY 16000000 @@ -112,4 +112,4 @@ { \ 26, 37, 17, 16, 15, 7 \ } -// #end keyboard +// #end keyboard \ No newline at end of file diff --git a/variants/tracksenger/oled/variant.h b/variants/tracksenger/oled/variant.h index 10bd7358b..e6e28e459 100644 --- a/variants/tracksenger/oled/variant.h +++ b/variants/tracksenger/oled/variant.h @@ -19,7 +19,7 @@ // #define ST7735_RESET 39 // #define ST7735_MISO -1 // #define ST7735_BUSY -1 -#define ST7735_BL 21 /* V1.1 PCB marking */ +#define TFT_BL 21 /* V1.1 PCB marking */ // #define ST7735_SPI_HOST SPI3_HOST // #define SPI_FREQUENCY 40000000 // #define SPI_READ_FREQUENCY 16000000 From 5ccb6df142a3136be813f0c0e98f2c0a72b779ca Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 09:28:41 -0700 Subject: [PATCH 201/305] Remove all sorts of redundant VEXT_ENABLE ifdefs --- src/graphics/TFTDisplay.cpp | 7 ------- src/main.cpp | 18 +----------------- src/sleep.cpp | 8 +------- variants/heltec_wireless_tracker/variant.h | 3 ++- .../heltec_wireless_tracker_V1_0/variant.h | 5 +++-- variants/tracksenger/internal/variant.h | 3 ++- variants/tracksenger/lcd/variant.h | 3 ++- variants/tracksenger/oled/variant.h | 5 +++-- 8 files changed, 14 insertions(+), 38 deletions(-) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index d20733e90..0e203a9d6 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -581,10 +581,6 @@ void TFTDisplay::sendCommand(uint8_t com) tft->powerSaveOff(); #endif -#ifdef VTFT_CTRL_V03 - digitalWrite(VTFT_CTRL_V03, LOW); -#endif - #ifdef VTFT_CTRL digitalWrite(VTFT_CTRL, LOW); #endif @@ -610,9 +606,6 @@ void TFTDisplay::sendCommand(uint8_t com) tft->powerSaveOn(); #endif -#ifdef VTFT_CTRL_V03 - digitalWrite(VTFT_CTRL_V03, HIGH); -#endif #ifdef VTFT_CTRL digitalWrite(VTFT_CTRL, HIGH); #endif diff --git a/src/main.cpp b/src/main.cpp index 968ee6053..7520667dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -278,25 +278,9 @@ void setup() digitalWrite(LORA_TCXO_GPIO, HIGH); #endif -#if defined(VEXT_ENABLE_V03) - pinMode(VEXT_ENABLE_V03, OUTPUT); - digitalWrite(VEXT_ENABLE_V03, 0); // turn on the display power and antenna boost - LOG_DEBUG("HELTEC Detect Tracker V1.0\n"); -#elif defined(VEXT_ENABLE_V05) - pinMode(VEXT_ENABLE_V05, OUTPUT); - digitalWrite(VEXT_ENABLE_V05, 1); // turn on the lora antenna boost - LOG_DEBUG("HELTEC Detect Tracker V1.1\n"); -#elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE) +#if defined(VEXT_ENABLE) pinMode(VEXT_ENABLE, OUTPUT); digitalWrite(VEXT_ENABLE, VEXT_ON_VALUE); // turn on the display power -#elif defined(VEXT_ENABLE) - pinMode(VEXT_ENABLE, OUTPUT); - digitalWrite(VEXT_ENABLE, 0); // turn on the display power -#endif - -#if defined(VTFT_CTRL_V03) - pinMode(VTFT_CTRL_V03, OUTPUT); - digitalWrite(VTFT_CTRL_V03, LOW); #endif #if defined(VTFT_CTRL) diff --git a/src/sleep.cpp b/src/sleep.cpp index 693d02a29..27e81ce54 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -246,14 +246,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power #endif -#if defined(VEXT_ENABLE_V03) - digitalWrite(VEXT_ENABLE_V03, 1); // turn off the display power -#elif defined(VEXT_ENABLE_V05) - digitalWrite(VEXT_ENABLE_V05, 0); // turn off the lora amplifier power -#elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE) +#if defined(VEXT_ENABLE) digitalWrite(VEXT_ENABLE, !VEXT_ON_VALUE); // turn on the display power -#elif defined(VEXT_ENABLE) - digitalWrite(VEXT_ENABLE, 1); // turn off the display power #endif #ifdef ARCH_ESP32 diff --git a/variants/heltec_wireless_tracker/variant.h b/variants/heltec_wireless_tracker/variant.h index 519c76d8f..46e922eb5 100644 --- a/variants/heltec_wireless_tracker/variant.h +++ b/variants/heltec_wireless_tracker/variant.h @@ -31,7 +31,8 @@ // GPS UC6580: GPS V_DET(8), VDD_IO(7), DCDC_IN(21), pulls up RESETN(17), D_SEL(33) and BOOT_MODE(34) through 10kR // GPS LNA SW7125DE: VCC(4), pulls up SHDN(5) through 10kR // LED: VDD, LEDA (through diode) -#define VEXT_ENABLE_V05 3 // active HIGH - powers the GPS, GPS LNA and OLED VDD/anode +#define VEXT_ENABLE 3 // active HIGH - powers the GPS, GPS LNA and OLED VDD/anode +#define VEXT_ON_VALUE HIGH #define BUTTON_PIN 0 #define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage diff --git a/variants/heltec_wireless_tracker_V1_0/variant.h b/variants/heltec_wireless_tracker_V1_0/variant.h index b638dec53..36c3dd261 100644 --- a/variants/heltec_wireless_tracker_V1_0/variant.h +++ b/variants/heltec_wireless_tracker_V1_0/variant.h @@ -24,11 +24,12 @@ #define TFT_WIDTH DISPLAY_HEIGHT #define TFT_OFFSET_X 26 #define TFT_OFFSET_Y -1 -#define VTFT_CTRL_V03 46 // Heltec Tracker needs this pulled low for TFT +#define VTFT_CTRL 46 // Heltec Tracker needs this pulled low for TFT #define SCREEN_TRANSITION_FRAMERATE 3 // fps #define DISPLAY_FORCE_SMALL_FONTS -#define VEXT_ENABLE_V03 Vext // active low, powers the oled display and the lora antenna boost +#define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost +#define VEXT_ON_VALUE LOW #define BUTTON_PIN 0 #define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage diff --git a/variants/tracksenger/internal/variant.h b/variants/tracksenger/internal/variant.h index bd4a51a2d..57ead848d 100644 --- a/variants/tracksenger/internal/variant.h +++ b/variants/tracksenger/internal/variant.h @@ -29,7 +29,8 @@ #define SCREEN_TRANSITION_FRAMERATE 3 // fps #define DISPLAY_FORCE_SMALL_FONTS -#define VEXT_ENABLE_V05 3 // active HIGH, powers the lora antenna boost +#define VEXT_ENABLE 3 // active HIGH, powers the lora antenna boost +#define VEXT_ON_VALUE HIGH #define BUTTON_PIN 0 #define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage diff --git a/variants/tracksenger/lcd/variant.h b/variants/tracksenger/lcd/variant.h index 17b012ae7..c89bf141c 100644 --- a/variants/tracksenger/lcd/variant.h +++ b/variants/tracksenger/lcd/variant.h @@ -53,7 +53,8 @@ #define SCREEN_TRANSITION_FRAMERATE 3 // fps // #define DISPLAY_FORCE_SMALL_FONTS -#define VEXT_ENABLE_V05 3 // active HIGH, powers the lora antenna boost +#define VEXT_ENABLE 3 // active HIGH, powers the lora antenna boost +#define VEXT_ON_VALUE HIGH #define BUTTON_PIN 0 #define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage diff --git a/variants/tracksenger/oled/variant.h b/variants/tracksenger/oled/variant.h index e6e28e459..70f0f3209 100644 --- a/variants/tracksenger/oled/variant.h +++ b/variants/tracksenger/oled/variant.h @@ -31,7 +31,8 @@ #define SCREEN_TRANSITION_FRAMERATE 3 // fps // #define DISPLAY_FORCE_SMALL_FONTS -#define VEXT_ENABLE_V05 3 // active HIGH, powers the lora antenna boost +#define VEXT_ENABLE 3 // active HIGH, powers the lora antenna boost +#define VEXT_ON_VALUE HIGH #define BUTTON_PIN 0 #define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage @@ -90,4 +91,4 @@ { \ 26, 37, 17, 16, 15, 7 \ } -// #end keyboard +// #end keyboard \ No newline at end of file From 2dda640d27c6c1373515f8a6fef895fdee2c669f Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 09:33:43 -0700 Subject: [PATCH 202/305] Remove unneeded VGNSS_CTRL_V03 --- variants/heltec_wireless_tracker_V1_0/variant.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/variants/heltec_wireless_tracker_V1_0/variant.h b/variants/heltec_wireless_tracker_V1_0/variant.h index 36c3dd261..876ff1146 100644 --- a/variants/heltec_wireless_tracker_V1_0/variant.h +++ b/variants/heltec_wireless_tracker_V1_0/variant.h @@ -44,8 +44,7 @@ #define PIN_GPS_RESET 35 #define PIN_GPS_PPS 36 -#define VGNSS_CTRL_V03 37 // Heltec Tracker needs this pulled low for GPS -#define PIN_GPS_EN VGNSS_CTRL_V03 +#define PIN_GPS_EN 37 // Heltec Tracker needs this pulled low for GPS #define GPS_EN_ACTIVE LOW #define GPS_RESET_MODE LOW From 5570b6bbc6542642735f774b95af56451ca87dda Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 10:15:23 -0700 Subject: [PATCH 203/305] change GPS to use virtual GPIOs (for #4154) --- src/GpioLogic.cpp | 6 ++++++ src/GpioLogic.h | 2 +- src/gps/GPS.cpp | 27 +++++++++++++++++---------- src/gps/GPS.h | 11 +++++++++-- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/GpioLogic.cpp b/src/GpioLogic.cpp index d164615a7..c23c40a7f 100644 --- a/src/GpioLogic.cpp +++ b/src/GpioLogic.cpp @@ -10,6 +10,12 @@ void GpioVirtPin::set(bool value) } } +void GpioHwPin::set(bool value) +{ + pinMode(num, OUTPUT); + digitalWrite(num, value); +} + GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {} void GpioTransformer::set(bool value) diff --git a/src/GpioLogic.h b/src/GpioLogic.h index 27e85d55b..c5a3cefa9 100644 --- a/src/GpioLogic.h +++ b/src/GpioLogic.h @@ -29,7 +29,7 @@ class GpioHwPin : public GpioPin public: explicit GpioHwPin(uint32_t num) : num(num) {} - void set(bool value) { digitalWrite(num, value); } + void set(bool value); }; class GpioTransformer; diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 7d7de8700..f7db1367a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -2,6 +2,7 @@ #if !MESHTASTIC_EXCLUDE_GPS #include "Default.h" #include "GPS.h" +#include "GpioLogic.h" #include "NodeDB.h" #include "PowerMon.h" #include "RTC.h" @@ -875,16 +876,8 @@ void GPS::writePinEN(bool on) if (HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1)) return; - // Abort: if pin unset - if (!en_gpio) - return; - - // Determine new value for the pin - bool val = GPS_EN_ACTIVE ? on : !on; - // Write and log - pinMode(en_gpio, OUTPUT); - digitalWrite(en_gpio, val); + enablePin->set(on); #ifdef GPS_EXTRAVERBOSE LOG_DEBUG("Pin EN %s\n", val == HIGH ? "HIGH" : "LOW"); #endif @@ -1421,7 +1414,21 @@ GPS *GPS::createGps() GPS *new_gps = new GPS; new_gps->rx_gpio = _rx_gpio; new_gps->tx_gpio = _tx_gpio; - new_gps->en_gpio = _en_gpio; + + if (_en_gpio) { + GpioPin *p = new GpioHwPin(_en_gpio); + + if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware + auto virtPin = new GpioVirtPin(); + new GpioNotTransformer( + virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio + p = virtPin; + } + new_gps->enablePin = p; + } else { + // Just use a simulated pin + new_gps->enablePin = new GpioVirtPin(); + } #ifdef PIN_GPS_PPS // pulse per second diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 96171cba5..494bddae8 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -3,6 +3,7 @@ #if !MESHTASTIC_EXCLUDE_GPS #include "GPSStatus.h" +#include "GpioLogic.h" #include "Observer.h" #include "TinyGPS++.h" #include "concurrency/OSThread.h" @@ -73,7 +74,6 @@ class GPS : private concurrency::OSThread uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; uint32_t rx_gpio = 0; uint32_t tx_gpio = 0; - uint32_t en_gpio = 0; int speedSelect = 0; int probeTries = 2; @@ -152,6 +152,13 @@ class GPS : private concurrency::OSThread meshtastic_Position p = meshtastic_Position_init_default; + /** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced + * implementations. Those boards will set this public variable to a custom implementation. + * + * Normally set by GPS::createGPS() + */ + GpioPin *enablePin; + GPS() : concurrency::OSThread("GPS") {} virtual ~GPS(); @@ -303,4 +310,4 @@ class GPS : private concurrency::OSThread }; extern GPS *gps; -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file From db6e591c078deb5829922d7f6d2254b00cd6be6d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 10:38:19 -0700 Subject: [PATCH 204/305] For #4154 - change TFT driver to use virtual GPIO for backlight enable --- src/graphics/TFTDisplay.cpp | 30 ++++++++++++++++++------------ src/graphics/TFTDisplay.h | 7 +++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 0e203a9d6..21b9c2bd1 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -516,6 +516,21 @@ extern unPhone unphone; TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) { LOG_DEBUG("TFTDisplay!\n"); + +#ifdef TFT_BL + GpioPin *p = new GpioHwPin(TFT_BL); + + if (!TFT_BACKLIGHT_ON) { // Need to invert the pin before hardware + auto virtPin = new GpioVirtPin(); + new GpioNotTransformer( + virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio + p = virtPin; + } +#else + GpioPin *p = new GpioVirtPin(); // Just simulate a pin +#endif + backlightEnable = p; + #if ARCH_PORTDUINO if (settingsMap[displayRotate]) { setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]); @@ -569,13 +584,11 @@ void TFTDisplay::sendCommand(uint8_t com) // handle display on/off directly switch (com) { case DISPLAYON: { + backlightEnable->set(true); #if ARCH_PORTDUINO display(true); if (settingsMap[displayBacklight] > 0) digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON); -#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) - pinMode(TFT_BL, OUTPUT); - digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) tft->wakeup(); tft->powerSaveOff(); @@ -594,13 +607,11 @@ void TFTDisplay::sendCommand(uint8_t com) break; } case DISPLAYOFF: { + backlightEnable->set(false); #if ARCH_PORTDUINO tft->clear(); if (settingsMap[displayBacklight] > 0) digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON); -#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) - pinMode(TFT_BL, OUTPUT); - digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) tft->sleep(); tft->powerSaveOn(); @@ -689,13 +700,8 @@ bool TFTDisplay::connect() tft = new LGFX; #endif -#ifdef TFT_BL - pinMode(TFT_BL, OUTPUT); - digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); - // pinMode(PIN_3V3_EN, OUTPUT); - // digitalWrite(PIN_3V3_EN, HIGH); + backlightEnable->set(true); LOG_INFO("Power to TFT Backlight\n"); -#endif #ifdef UNPHONE unphone.backlight(true); // using unPhone library diff --git a/src/graphics/TFTDisplay.h b/src/graphics/TFTDisplay.h index 42aa3abff..595984fbc 100644 --- a/src/graphics/TFTDisplay.h +++ b/src/graphics/TFTDisplay.h @@ -1,5 +1,6 @@ #pragma once +#include #include /** @@ -39,6 +40,12 @@ class TFTDisplay : public OLEDDisplay */ void setDetected(uint8_t detected); + /** + * This is normally managed entirely by TFTDisplay, but some rare applications (heltec tracker) might need to replace the + * default GPIO behavior with something a bit more complex. + */ + GpioPin *backlightEnable; + protected: // the header size of the buffer used, e.g. for the SPI command header virtual int getBufferOffset(void) override { return 0; } From 2a7cf9d3873ee4a8d485c8688d96cd084b08b31a Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 10:40:12 -0700 Subject: [PATCH 205/305] Remove redundant defintions of ST7789_BACKLIGHT_EN --- src/graphics/TFTDisplay.cpp | 4 ---- variants/heltec_mesh_node_t114/variant.h | 2 +- variants/picomputer-s3/variant.h | 2 +- variants/t-deck/variant.h | 2 +- variants/t-watch-s3/variant.h | 4 +--- variants/tracksenger/lcd/variant.h | 2 +- variants/wiphone/variant.h | 2 +- 7 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 21b9c2bd1..7a0a8c3eb 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -123,10 +123,6 @@ static void rak14014_tpIntHandle(void) #elif defined(ST7789_CS) #include // Graphics and font library for ST7735 driver chip -#if defined(ST7789_BACKLIGHT_EN) && !defined(TFT_BL) -#define TFT_BL ST7789_BACKLIGHT_EN -#endif - class LGFX : public lgfx::LGFX_Device { lgfx::Panel_ST7789 _panel_instance; diff --git a/variants/heltec_mesh_node_t114/variant.h b/variants/heltec_mesh_node_t114/variant.h index f7a268148..e8c305990 100644 --- a/variants/heltec_mesh_node_t114/variant.h +++ b/variants/heltec_mesh_node_t114/variant.h @@ -49,7 +49,7 @@ extern "C" { // #define ST7789_BL (32+6) #define TFT_BACKLIGHT_ON LOW #define ST7789_SPI_HOST SPI1_HOST -// #define ST7789_BACKLIGHT_EN (32+6) +// #define TFT_BL (32+6) #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 135 diff --git a/variants/picomputer-s3/variant.h b/variants/picomputer-s3/variant.h index fc746c599..ff8faa6f4 100644 --- a/variants/picomputer-s3/variant.h +++ b/variants/picomputer-s3/variant.h @@ -37,7 +37,7 @@ #define ST7789_MISO -1 #define ST7789_BUSY -1 #define ST7789_SPI_HOST SPI3_HOST -#define ST7789_BACKLIGHT_EN 5 +#define TFT_BL 5 #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 320 diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h index 7efa00c82..9860d608f 100644 --- a/variants/t-deck/variant.h +++ b/variants/t-deck/variant.h @@ -8,7 +8,7 @@ #define ST7789_BUSY -1 #define ST7789_BL 42 #define ST7789_SPI_HOST SPI2_HOST -#define ST7789_BACKLIGHT_EN 42 +#define TFT_BL 42 #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 320 diff --git a/variants/t-watch-s3/variant.h b/variants/t-watch-s3/variant.h index ad7e6b56b..9f939d859 100644 --- a/variants/t-watch-s3/variant.h +++ b/variants/t-watch-s3/variant.h @@ -8,7 +8,7 @@ #define ST7789_BUSY -1 #define ST7789_BL 45 #define ST7789_SPI_HOST SPI3_HOST -#define ST7789_BACKLIGHT_EN 45 +#define TFT_BL 45 #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 240 @@ -30,8 +30,6 @@ #define I2C_SDA1 39 // Used for capacitive touch #define I2C_SCL1 40 // Used for capacitive touch -#define TFT_BL ST7789_BACKLIGHT_EN - #define HAS_I2S #define DAC_I2S_BCK 48 #define DAC_I2S_WS 15 diff --git a/variants/tracksenger/lcd/variant.h b/variants/tracksenger/lcd/variant.h index c89bf141c..ecf4e854e 100644 --- a/variants/tracksenger/lcd/variant.h +++ b/variants/tracksenger/lcd/variant.h @@ -22,7 +22,7 @@ #define ST7789_MISO -1 #define ST7789_BUSY -1 #define ST7789_SPI_HOST SPI3_HOST -#define ST7789_BACKLIGHT_EN 21 +#define TFT_BL 21 #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 320 diff --git a/variants/wiphone/variant.h b/variants/wiphone/variant.h index b2b3ade78..052dc5ea8 100644 --- a/variants/wiphone/variant.h +++ b/variants/wiphone/variant.h @@ -40,7 +40,7 @@ #define ST7789_MISO 19 #define ST7789_BUSY -1 #define ST7789_SPI_HOST SPI3_HOST -#define ST7789_BACKLIGHT_EN -1 // EXTENDER_PIN(9) +#define TFT_BL -1 // EXTENDER_PIN(9) #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 240 From f77c5f6a5bb42ea5d28cb2db54417d2694acc7c9 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 10:50:44 -0700 Subject: [PATCH 206/305] Don't create backlight instances when the variant hasn't specified a pin --- src/graphics/TFTDisplay.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 7a0a8c3eb..f6bd6513a 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -192,6 +192,7 @@ class LGFX : public lgfx::LGFX_Device _panel_instance.config(cfg); } +#ifdef ST7789_BL // Set the backlight control. (delete if not necessary) { auto cfg = _light_instance.config(); // Gets a structure for backlight settings. @@ -203,6 +204,7 @@ class LGFX : public lgfx::LGFX_Device _light_instance.config(cfg); _panel_instance.setLight(&_light_instance); // Set the backlight on the panel. } +#endif #if HAS_TOUCHSCREEN // Configure settings for touch screen control. @@ -312,6 +314,7 @@ class LGFX : public lgfx::LGFX_Device _panel_instance.config(cfg); } +#ifdef TFT_BL // Set the backlight control { auto cfg = _light_instance.config(); // Gets a structure for backlight settings. @@ -324,6 +327,7 @@ class LGFX : public lgfx::LGFX_Device _light_instance.config(cfg); _panel_instance.setLight(&_light_instance); // Set the backlight on the panel. } +#endif setPanel(&_panel_instance); } From 5c5cbb23cfaae33693f2f7b505b827a493a60811 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 10:51:15 -0700 Subject: [PATCH 207/305] wiphone isn't setting a valid backlight enable pin Therefore don't just randomly be writing to a GPIO numbered -1 Instead just don't try to control the backlight NOTE: I don't have a 'wiphone' to test with, but I saw this via inspection while cleaning up some other stuff. --- variants/wiphone/variant.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/variants/wiphone/variant.h b/variants/wiphone/variant.h index 052dc5ea8..cfa5667bb 100644 --- a/variants/wiphone/variant.h +++ b/variants/wiphone/variant.h @@ -34,13 +34,17 @@ #define ST7789_SCK 18 #define ST7789_CS 5 #define ST7789_RS 26 -#define ST7789_BL -1 // EXTENDER_PIN(9) +// I don't have a 'wiphone' but this I think should not be defined this way (don't set TFT_BL if we don't have a hw way to control +// it) +// #define ST7789_BL -1 // EXTENDER_PIN(9) #define ST7789_RESET -1 #define ST7789_MISO 19 #define ST7789_BUSY -1 #define ST7789_SPI_HOST SPI3_HOST -#define TFT_BL -1 // EXTENDER_PIN(9) +// I don't have a 'wiphone' but this I think should not be defined this way (don't set TFT_BL if we don't have a hw way to control +// it) +// #define TFT_BL -1 // EXTENDER_PIN(9) #define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 16000000 #define TFT_HEIGHT 240 From e6163a59cd0521a19bf2d1db63e9f6792964d359 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 22 Aug 2024 11:07:06 -0700 Subject: [PATCH 208/305] Make specifying VEXT_ON_VALUE manatory if using VEXT_ENABLE --- variants/heltec_wireless_paper/variant.h | 1 + variants/heltec_wireless_paper_v1/variant.h | 1 + variants/heltec_wsl_v3/variant.h | 3 ++- variants/tlora_v1/variant.h | 5 +++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/variants/heltec_wireless_paper/variant.h b/variants/heltec_wireless_paper/variant.h index 36ab80445..520dcec9b 100644 --- a/variants/heltec_wireless_paper/variant.h +++ b/variants/heltec_wireless_paper/variant.h @@ -22,6 +22,7 @@ // Power #define VEXT_ENABLE 45 // Active low, powers the E-Ink display +#define VEXT_ON_VALUE LOW #define ADC_CTRL 19 #define BATTERY_PIN 20 #define ADC_CHANNEL ADC2_GPIO20_CHANNEL diff --git a/variants/heltec_wireless_paper_v1/variant.h b/variants/heltec_wireless_paper_v1/variant.h index 36ab80445..520dcec9b 100644 --- a/variants/heltec_wireless_paper_v1/variant.h +++ b/variants/heltec_wireless_paper_v1/variant.h @@ -22,6 +22,7 @@ // Power #define VEXT_ENABLE 45 // Active low, powers the E-Ink display +#define VEXT_ON_VALUE LOW #define ADC_CTRL 19 #define BATTERY_PIN 20 #define ADC_CHANNEL ADC2_GPIO20_CHANNEL diff --git a/variants/heltec_wsl_v3/variant.h b/variants/heltec_wsl_v3/variant.h index 75cea538d..c103b9172 100644 --- a/variants/heltec_wsl_v3/variant.h +++ b/variants/heltec_wsl_v3/variant.h @@ -4,6 +4,7 @@ #define LED_PIN LED #define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost +#define VEXT_ON_VALUE LOW #define BUTTON_PIN 0 #define ADC_CTRL 37 @@ -32,4 +33,4 @@ #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 \ No newline at end of file diff --git a/variants/tlora_v1/variant.h b/variants/tlora_v1/variant.h index 08fefa809..83e2c193e 100644 --- a/variants/tlora_v1/variant.h +++ b/variants/tlora_v1/variant.h @@ -4,8 +4,9 @@ #define RESET_OLED 16 // If defined, this pin will be used to reset the display controller #define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost -#define LED_PIN 2 // If defined we will blink this LED -#define BUTTON_PIN 0 // If defined, this will be used for user button presses +#define VEXT_ON_VALUE LOW +#define LED_PIN 2 // If defined we will blink this LED +#define BUTTON_PIN 0 // If defined, this will be used for user button presses #define BUTTON_NEED_PULLUP #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module. From ff500bc5a934d2fcc07aa06ef85dcc8468cb4c11 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 22 Aug 2024 20:57:03 -0500 Subject: [PATCH 209/305] Save nodedb after favoriting (or removing) (#4537) --- src/modules/AdminModule.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index d64aea5d8..ef60a4bf2 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -238,6 +238,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node); if (node != NULL) { node->is_favorite = true; + saveChanges(SEGMENT_DEVICESTATE, false); } break; } @@ -246,6 +247,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node); if (node != NULL) { node->is_favorite = false; + saveChanges(SEGMENT_DEVICESTATE, false); } break; } From 601ae29fe917f90da0679ae6455b8e8e51ae4b6e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 23 Aug 2024 06:25:40 -0500 Subject: [PATCH 210/305] Adds has_x bools to position packet. (#4540) --- src/modules/PositionModule.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index f534baf67..2a0c95a9b 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -187,16 +187,23 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.longitude_i = localPosition.longitude_i; } p.precision_bits = precision; + p.has_latitude_i = true; + p.has_longitude_i = true; p.time = getValidTime(RTCQualityNTP) > 0 ? getValidTime(RTCQualityNTP) : localPosition.time; if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) { - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) { p.altitude = localPosition.altitude; - else + p.has_altitude = true; + } else { p.altitude_hae = localPosition.altitude_hae; + p.has_altitude_hae = true; + } - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION) { p.altitude_geoidal_separation = localPosition.altitude_geoidal_separation; + p.has_altitude_geoidal_separation = true; + } } if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_DOP) { @@ -216,11 +223,15 @@ meshtastic_MeshPacket *PositionModule::allocReply() if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SEQ_NO) p.seq_number = localPosition.seq_number; - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING) { p.ground_track = localPosition.ground_track; + p.has_ground_track = true; + } - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED) { p.ground_speed = localPosition.ground_speed; + p.has_ground_speed = true; + } // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS or NTP to include the time, so that devices @@ -471,4 +482,4 @@ void PositionModule::handleNewPosition() } } -#endif +#endif \ No newline at end of file From 00ea9182a48887741e0e22aa1971100d455f1344 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 23 Aug 2024 06:26:19 -0500 Subject: [PATCH 211/305] Fix copyPasta in NodeDB (#4538) --- src/mesh/NodeDB.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 1caaaf39b..34d3e4ce9 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -556,13 +556,8 @@ void NodeDB::cleanupMeshDB() for (int i = 0; i < numMeshNodes; i++) { if (meshNodes->at(i).has_user) { if (meshNodes->at(i).user.public_key.size > 0) { - for (int j = 0; j < numMeshNodes; j++) { - if (meshNodes->at(i).user.public_key.bytes[j] != 0) { - break; - } - if (j == 31) { - meshNodes->at(i).user.public_key.size = 0; - } + if (memfll(meshNodes->at(i).user.public_key.bytes, 0, meshNodes->at(i).user.public_key.size)) { + meshNodes->at(i).user.public_key.size = 0; } } meshNodes->at(newPos++) = meshNodes->at(i); From 0850ad6c8d87be756008001393cdc426a677dcc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Gjels=C3=B8?= <36234524+gjelsoe@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:28:23 +0200 Subject: [PATCH 212/305] Initial support for RadioMaster Bandit. (#4523) * Initial support for RadioMaster Bandit. * Different lighting can be made for Button 1 & 2 on the Bandit. Changes to AmbientLighting will turn off af shutdown(). * Trunk * Trunk again. --- platformio.ini | 1 + src/AmbientLightingThread.h | 59 ++++++++- src/mesh/RF95Interface.cpp | 26 +++- src/platform/esp32/architecture.h | 2 + .../radiomaster_900_bandit/platformio.ini | 14 ++ variants/radiomaster_900_bandit/variant.h | 121 ++++++++++++++++++ 6 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 variants/radiomaster_900_bandit/platformio.ini create mode 100644 variants/radiomaster_900_bandit/variant.h diff --git a/platformio.ini b/platformio.ini index 5ad7d60a2..4de1ec39f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,6 +34,7 @@ default_envs = tbeam ;default_envs = wio-e5 ;default_envs = radiomaster_900_bandit_nano ;default_envs = radiomaster_900_bandit_micro +;default_envs = radiomaster_900_bandit ;default_envs = heltec_capsule_sensor_v3 ;default_envs = heltec_vision_master_t190 ;default_envs = heltec_vision_master_e213 diff --git a/src/AmbientLightingThread.h b/src/AmbientLightingThread.h index 6b3360b1f..fdd4b53fa 100644 --- a/src/AmbientLightingThread.h +++ b/src/AmbientLightingThread.h @@ -1,3 +1,4 @@ +#include "Observer.h" #include "configuration.h" #ifdef HAS_NCP5623 @@ -22,10 +23,18 @@ class AmbientLightingThread : public concurrency::OSThread public: explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread") { + notifyDeepSleepObserver.observe(¬ifyDeepSleep); // Let us know when shutdown() is issued. + +// Enables Ambient Lighting by default if conditions are meet. +#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) +#ifdef ENABLE_AMBIENTLIGHTING + moduleConfig.ambient_lighting.led_state = true; +#endif +#endif // Uncomment to test module // moduleConfig.ambient_lighting.led_state = true; // moduleConfig.ambient_lighting.current = 10; - // // Default to a color based on our node number + // Default to a color based on our node number // moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16; // moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8; // moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF; @@ -82,9 +91,46 @@ class AmbientLightingThread : public concurrency::OSThread return disable(); } + // When shutdown() is issued, setLightingOff will be called. + CallbackObserver notifyDeepSleepObserver = + CallbackObserver(this, &AmbientLightingThread::setLightingOff); + private: ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE; + // Turn RGB lighting off, is used in junction to shutdown() + int setLightingOff(void *unused) + { +#ifdef HAS_NCP5623 + rgb.setCurrent(0); + rgb.setRed(0); + rgb.setGreen(0); + rgb.setBlue(0); + LOG_INFO("Turn Off NCP5623 Ambient lighting.\n"); +#endif +#ifdef HAS_NEOPIXEL + pixels.clear(); + pixels.show(); + LOG_INFO("Turn Off NeoPixel Ambient lighting.\n"); +#endif +#ifdef RGBLED_CA + analogWrite(RGBLED_RED, 255 - 0); + analogWrite(RGBLED_GREEN, 255 - 0); + analogWrite(RGBLED_BLUE, 255 - 0); + LOG_INFO("Turn Off Ambient lighting RGB Common Anode.\n"); +#elif defined(RGBLED_RED) + analogWrite(RGBLED_RED, 0); + analogWrite(RGBLED_GREEN, 0); + analogWrite(RGBLED_BLUE, 0); + LOG_INFO("Turn Off Ambient lighting RGB Common Cathode.\n"); +#endif +#ifdef UNPHONE + unphone.rgb(0, 0, 0); + LOG_INFO("Turn Off unPhone Ambient lighting.\n"); +#endif + return 0; + } + void setLighting() { #ifdef HAS_NCP5623 @@ -100,6 +146,17 @@ class AmbientLightingThread : public concurrency::OSThread pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue), 0, NEOPIXEL_COUNT); + +// RadioMaster Bandit has addressable LED at the two buttons +// this allow us to set different lighting for them in variant.h file. +#ifdef RADIOMASTER_900_BANDIT +#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX) + pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1); +#endif +#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX) + pixels.fill(BUTTON2_COLOR, BUTTON1_COLOR_INDEX, 1); +#endif +#endif pixels.show(); LOG_DEBUG("Initializing NeoPixel Ambient lighting w/ brightness(current)=%d, red=%d, green=%d, blue=%d\n", moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index b6083e7f9..25df3258f 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -16,7 +16,7 @@ // In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING // if you set power to something higher than 17 or 20 you might fry your board. -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) // Structure to hold DAC and DB values typedef struct { uint8_t dac; @@ -40,12 +40,23 @@ DACDB getDACandDB(uint8_t dbm) static const struct { uint8_t dbm; DACDB values; - } dbmToDACDB[] = { + } +#ifdef RADIOMASTER_900_BANDIT_NANO + dbmToDACDB[] = { {20, {168, 2}}, // 100mW {24, {148, 6}}, // 250mW {27, {128, 9}}, // 500mW {30, {90, 12}} // 1000mW }; +#endif +#ifdef RADIOMASTER_900_BANDIT + dbmToDACDB[] = { + {20, {165, 2}}, // 100mW + {24, {155, 6}}, // 250mW + {27, {142, 9}}, // 500mW + {30, {110, 10}} // 1000mW + }; +#endif const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]); // Find the interval dbm falls within and interpolate @@ -56,7 +67,12 @@ DACDB getDACandDB(uint8_t dbm) } // Return a default value if no match is found and default to 100mW +#ifdef RADIOMASTER_900_BANDIT_NANO DACDB defaultValue = {168, 2}; +#endif +#ifdef RADIOMASTER_900_BANDIT + DACDB defaultValue = {165, 2}; +#endif return defaultValue; } #endif @@ -95,7 +111,7 @@ bool RF95Interface::init() { RadioLibInterface::init(); -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) // DAC and DB values based on dBm using interpolation DACDB dacDbValues = getDACandDB(power); int8_t powerDAC = dacDbValues.dac; @@ -117,7 +133,7 @@ bool RF95Interface::init() // enable PA #ifdef RF95_PA_EN #if defined(RF95_PA_DAC_EN) -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) // Use calculated DAC value dacWrite(RF95_PA_EN, powerDAC); #else @@ -163,7 +179,7 @@ bool RF95Interface::init() LOG_INFO("Frequency set to %f\n", getFreq()); LOG_INFO("Bandwidth set to %f\n", bw); LOG_INFO("Power output set to %d\n", power); -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) LOG_INFO("DAC output set to %d\n", powerDAC); #endif diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index b6def5b01..3761235a0 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -152,6 +152,8 @@ #define HW_VENDOR meshtastic_HardwareModel_WIPHONE #elif defined(RADIOMASTER_900_BANDIT_NANO) #define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO +#elif defined(RADIOMASTER_900_BANDIT) +#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT #elif defined(HELTEC_CAPSULE_SENSOR_V3) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 #elif defined(HELTEC_VISION_MASTER_T190) diff --git a/variants/radiomaster_900_bandit/platformio.ini b/variants/radiomaster_900_bandit/platformio.ini new file mode 100644 index 000000000..4ff8a6ea2 --- /dev/null +++ b/variants/radiomaster_900_bandit/platformio.ini @@ -0,0 +1,14 @@ +[env:radiomaster_900_bandit] +extends = esp32_base +board = esp32doit-devkit-v1 +build_flags = + ${esp32_base.build_flags} + -DRADIOMASTER_900_BANDIT + -DVTABLES_IN_FLASH=1 + -DCONFIG_DISABLE_HAL_LOCKS=1 + -O2 + -Ivariants/radiomaster_900_bandit +board_build.f_cpu = 240000000L +upload_protocol = esptool +lib_deps = + ${esp32_base.lib_deps} \ No newline at end of file diff --git a/variants/radiomaster_900_bandit/variant.h b/variants/radiomaster_900_bandit/variant.h new file mode 100644 index 000000000..0499970f5 --- /dev/null +++ b/variants/radiomaster_900_bandit/variant.h @@ -0,0 +1,121 @@ +/* + Initial settings and work by https://github.com/gjelsoe + Unit provided by Radio Master RC + https://radiomasterrc.com/products/bandit-expresslrs-rf-module with 1.29" OLED display CH1115 driver +*/ + +/* + On this model then screen is NOT upside down, don't flip it for the user. +*/ +#undef DISPLAY_FLIP_SCREEN + +/* + I2C SDA and SCL. + 0x18 - STK8XXX Accelerometer, Not supported yet. + 0x3C - SH1115 Display Driver +*/ +#define I2C_SDA 14 +#define I2C_SCL 12 + +/* + No GPS - but free pins are available. +*/ +#define HAS_GPS 0 +#undef GPS_RX_PIN +#undef GPS_TX_PIN + +/* + Pin connections from ESP32-D0WDQ6 to SX1276. +*/ +#define LORA_DIO0 22 +#define LORA_DIO1 21 +#define LORA_SCK 18 +#define LORA_MISO 19 +#define LORA_MOSI 23 +#define LORA_CS 4 +#define LORA_RESET 5 +#define LORA_TXEN 33 + +/* + This unit has a FAN built-in. + FAN is active at 250mW on it's ExpressLRS Firmware. + This FAN has TACHO signal on Pin 27 for use with PWM. +*/ +#define RF95_FAN_EN 2 + +/* + LED PIN setup and it has a NeoPixel LED. + It's possible to setup colors for Button 1 and 2, + look at BUTTON1_COLOR, BUTTON1_COLOR_INDEX, BUTTON2_COLOR and BUTTON2_COLOR_INDEX + this is done here for now. +*/ +#define HAS_NEOPIXEL // Enable the use of neopixels +#define NEOPIXEL_COUNT 6 // How many neopixels are connected +#define NEOPIXEL_DATA 15 // GPIO pin used to send data to the neopixels +#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // Type of neopixels in use +#define ENABLE_AMBIENTLIGHTING // Turn on Ambient Lighting +// #define BUTTON1_COLOR 0xFF0000 // Background light for Button 1 in HEX RGB Color (RadioMaster Bandit only). +// #define BUTTON1_COLOR_INDEX 0 // NeoPixel Index ID for Button 1 +// #define BUTTON2_COLOR 0x0000FF // Background light for Button 2 in HEX RGB Color (RadioMaster Bandit only). +// #define BUTTON2_COLOR_INDEX 1 // NeoPixel Index ID for Button 2 + +/* + It has 1 x five-way and 2 x normal buttons. + + Button GPIO RGB Index + --------------------------- + Five-way 39 - + Button 1 34 0 + Button 2 35 1 + + Five way button when using ADC. + 2.632V, 2.177V, 1.598V, 1.055V, 0V + + ADC Values: + { UP, DOWN, LEFT, RIGHT, ENTER, IDLE } + 3227, 0 ,1961, 2668, 1290, 4095 + + Five way button when using ADC. + https://github.com/ExpressLRS/targets/blob/f3215b5ec891108db1a13523e4163950cfcadaac/TX/Radiomaster%20Bandit.json#L41 + +*/ +#define INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE +#define PIN_JOYSTICK 39 +#define JOYSTICK_ADC_VALS /*UP*/ 3227, /*DOWN*/ 0, /*LEFT*/ 1961, /*RIGHT*/ 2668, /*OK*/ 1290, /*IDLE*/ 4095 + +/* + Normal Button Pin setup. +*/ +#define BUTTON_PIN 34 +#define BUTTON_NEED_PULLUP + +/* + No External notification. +*/ +#undef EXT_NOTIFY_OUT + +/* + Remapping PIN Names. + Note, that this unit uses RFO +*/ +#define USE_RF95 +#define USE_RF95_RFO +#define RF95_CS LORA_CS +#define RF95_DIO1 LORA_DIO1 +#define RF95_TXEN LORA_TXEN +#define RF95_RESET LORA_RESET +#define RF95_MAX_POWER 10 + +/* + This module has Skyworks SKY66122 controlled by dacWrite + power ranging from 100mW to 1000mW. + + Mapping of PA_LEVEL to Power output: GPIO26/dacWrite + 168 -> 100mW + 155 -> 250mW + 142 -> 500mW + 110 -> 1000mW +*/ +#define RF95_PA_EN 26 +#define RF95_PA_DAC_EN +#define RF95_PA_LEVEL 110 \ No newline at end of file From f99b81acf771279cfeea09bc350db979d6c89592 Mon Sep 17 00:00:00 2001 From: Ian McEwen Date: Fri, 23 Aug 2024 05:03:29 -0700 Subject: [PATCH 213/305] Use the '+' wildcard for MQTT rather than '#', to subscribe only to topics one nesting level deep (#4528) --- src/mqtt/MQTT.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 22f68bac8..2f7e82e3d 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -376,12 +376,12 @@ void MQTT::sendSubscriptions() const auto &ch = channels.getByIndex(i); if (ch.settings.downlink_enabled) { hasDownlink = true; - std::string topic = cryptTopic + channels.getGlobalId(i) + "/#"; + std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 if (moduleConfig.mqtt.json_enabled == true) { - std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/#"; + std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? } @@ -390,7 +390,7 @@ void MQTT::sendSubscriptions() } #if !MESHTASTIC_EXCLUDE_PKI if (hasDownlink) { - std::string topic = cryptTopic + "PKI/#"; + std::string topic = cryptTopic + "PKI/+"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); } @@ -674,4 +674,4 @@ bool MQTT::isValidJsonEnvelope(JSONObject &json) (json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type (json.find("payload") != json.end()); // should have a payload -} \ No newline at end of file +} From 7abc194ef509f03a3f018b44e51bb0208cac4634 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 23 Aug 2024 07:04:34 -0500 Subject: [PATCH 214/305] Found more places to set explicit has_optional on position (#4542) --- src/mesh/TypeConversions.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index d8ee6afc7..bcff0b817 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -16,8 +16,14 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo if (lite->has_position) { info.has_position = true; + if (lite->position.latitude_i > 0) + info.position.has_latitude_i = true; info.position.latitude_i = lite->position.latitude_i; + if (lite->position.longitude_i > 0) + info.position.has_longitude_i = true; info.position.longitude_i = lite->position.longitude_i; + if (lite->position.altitude > 0) + info.position.has_altitude = true; info.position.altitude = lite->position.altitude; info.position.location_source = lite->position.location_source; info.position.time = lite->position.time; @@ -48,8 +54,14 @@ meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Positi meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite) { meshtastic_Position position = meshtastic_Position_init_default; + if (lite.latitude_i > 0) + position.has_latitude_i = true; position.latitude_i = lite.latitude_i; + if (lite.longitude_i > 0) + position.has_longitude_i = true; position.longitude_i = lite.longitude_i; + if (lite.altitude > 0) + position.has_altitude = true; position.altitude = lite.altitude; position.location_source = lite.location_source; position.time = lite.time; From 2a279c7f3dcabab5beb13f9a376948760725f8d7 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 23 Aug 2024 07:07:28 -0500 Subject: [PATCH 215/305] Dum dum zero comparision --- src/mesh/TypeConversions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index bcff0b817..513728ca5 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -16,13 +16,13 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo if (lite->has_position) { info.has_position = true; - if (lite->position.latitude_i > 0) + if (lite->position.latitude_i != 0) info.position.has_latitude_i = true; info.position.latitude_i = lite->position.latitude_i; - if (lite->position.longitude_i > 0) + if (lite->position.longitude_i != 0) info.position.has_longitude_i = true; info.position.longitude_i = lite->position.longitude_i; - if (lite->position.altitude > 0) + if (lite->position.altitude != 0) info.position.has_altitude = true; info.position.altitude = lite->position.altitude; info.position.location_source = lite->position.location_source; @@ -54,13 +54,13 @@ meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Positi meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite) { meshtastic_Position position = meshtastic_Position_init_default; - if (lite.latitude_i > 0) + if (lite.latitude_i != 0) position.has_latitude_i = true; position.latitude_i = lite.latitude_i; - if (lite.longitude_i > 0) + if (lite.longitude_i != 0) position.has_longitude_i = true; position.longitude_i = lite.longitude_i; - if (lite.altitude > 0) + if (lite.altitude != 0) position.has_altitude = true; position.altitude = lite.altitude; position.location_source = lite.location_source; From aa54335e214fbc55de01a694accfeac2a427422a Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 23 Aug 2024 18:18:36 -0700 Subject: [PATCH 216/305] remove deprecated serial/bt logging options and unify in the new (#4516) security option. Per discussion in https://github.com/meshtastic/firmware/issues/4375 no need to preserve the old options when changing to this new simpler single boolean because they were newish, rarely used and only for 'advanced' developers. --- protobufs | 2 +- src/RedirectablePrint.cpp | 2 +- src/mesh/NodeDB.cpp | 1 - src/mesh/generated/meshtastic/config.pb.h | 37 ++++++------------- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 6 files changed, 15 insertions(+), 31 deletions(-) diff --git a/protobufs b/protobufs index 56a435507..183ba970a 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 56a4355070f3371213d48f3a8cac1ddaf0d553fe +Subproject commit 183ba970a7a71de7a81541b74c413543523417d2 diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 96cf851e4..6eb6f8319 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -213,7 +213,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) { #if !MESHTASTIC_EXCLUDE_BLUETOOTH - if (config.security.bluetooth_logging_enabled && !pauseBluetoothLogging) { + if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) { bool isBleConnected = false; #ifdef ARCH_ESP32 isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected(); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 34d3e4ce9..0504cc273 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -127,7 +127,6 @@ NodeDB::NodeDB() if (!config.has_security) { config.has_security = true; config.security.serial_enabled = config.device.serial_enabled; - config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; config.security.is_managed = config.device.is_managed; } #if !(MESHTASTIC_EXCLUDE_PKI) diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 2f4c00fb0..d79654856 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -284,10 +284,6 @@ typedef struct _meshtastic_Config_DeviceConfig { /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI Moved to SecurityConfig */ bool serial_enabled; - /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Set this to true to leave the debug log outputting even when API is active. - Moved to SecurityConfig */ - bool debug_log_enabled; /* For boards without a hard wired button, this is the pin number that will be used Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */ uint32_t button_gpio; @@ -523,9 +519,6 @@ typedef struct _meshtastic_Config_BluetoothConfig { meshtastic_Config_BluetoothConfig_PairingMode mode; /* Specified PIN for PairingMode.FixedPin */ uint32_t fixed_pin; - /* Enables device (serial style logs) over Bluetooth - Moved to SecurityConfig */ - bool device_logging_enabled; } meshtastic_Config_BluetoothConfig; typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_public_key_t; @@ -546,10 +539,8 @@ typedef struct _meshtastic_Config_SecurityConfig { /* Serial Console over the Stream API." */ bool serial_enabled; /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Output live debug logging over serial. */ + Output live debug logging over serial or bluetooth is set to true. */ bool debug_log_api_enabled; - /* Enables device (serial style logs) over Bluetooth */ - bool bluetooth_logging_enabled; /* Allow incoming device control over the insecure legacy admin channel. */ bool admin_channel_enabled; } meshtastic_Config_SecurityConfig; @@ -658,32 +649,31 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} -#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} +#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} #define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0} #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} -#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} -#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} +#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_default {0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} -#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} +#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} #define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0} #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} -#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} -#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} +#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Config_DeviceConfig_role_tag 1 #define meshtastic_Config_DeviceConfig_serial_enabled_tag 2 -#define meshtastic_Config_DeviceConfig_debug_log_enabled_tag 3 #define meshtastic_Config_DeviceConfig_button_gpio_tag 4 #define meshtastic_Config_DeviceConfig_buzzer_gpio_tag 5 #define meshtastic_Config_DeviceConfig_rebroadcast_mode_tag 6 @@ -758,14 +748,12 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_enabled_tag 1 #define meshtastic_Config_BluetoothConfig_mode_tag 2 #define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3 -#define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4 #define meshtastic_Config_SecurityConfig_public_key_tag 1 #define meshtastic_Config_SecurityConfig_private_key_tag 2 #define meshtastic_Config_SecurityConfig_admin_key_tag 3 #define meshtastic_Config_SecurityConfig_is_managed_tag 4 #define meshtastic_Config_SecurityConfig_serial_enabled_tag 5 #define meshtastic_Config_SecurityConfig_debug_log_api_enabled_tag 6 -#define meshtastic_Config_SecurityConfig_bluetooth_logging_enabled_tag 7 #define meshtastic_Config_SecurityConfig_admin_channel_enabled_tag 8 #define meshtastic_Config_device_tag 1 #define meshtastic_Config_position_tag 2 @@ -803,7 +791,6 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.s #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ X(a, STATIC, SINGULAR, BOOL, serial_enabled, 2) \ -X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 3) \ X(a, STATIC, SINGULAR, UINT32, button_gpio, 4) \ X(a, STATIC, SINGULAR, UINT32, buzzer_gpio, 5) \ X(a, STATIC, SINGULAR, UENUM, rebroadcast_mode, 6) \ @@ -906,8 +893,7 @@ X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104) #define meshtastic_Config_BluetoothConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \ X(a, STATIC, SINGULAR, UENUM, mode, 2) \ -X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) \ -X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4) +X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) #define meshtastic_Config_BluetoothConfig_CALLBACK NULL #define meshtastic_Config_BluetoothConfig_DEFAULT NULL @@ -918,7 +904,6 @@ X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ -X(a, STATIC, SINGULAR, BOOL, bluetooth_logging_enabled, 7) \ X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8) #define meshtastic_Config_SecurityConfig_CALLBACK NULL #define meshtastic_Config_SecurityConfig_DEFAULT NULL @@ -955,15 +940,15 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size -#define meshtastic_Config_BluetoothConfig_size 12 -#define meshtastic_Config_DeviceConfig_size 100 +#define meshtastic_Config_BluetoothConfig_size 10 +#define meshtastic_Config_DeviceConfig_size 98 #define meshtastic_Config_DisplayConfig_size 30 #define meshtastic_Config_LoRaConfig_size 82 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 -#define meshtastic_Config_SecurityConfig_size 112 +#define meshtastic_Config_SecurityConfig_size 110 #define meshtastic_Config_SessionkeyConfig_size 0 #define meshtastic_Config_size 199 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 343e5f48a..976e0e135 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -358,7 +358,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 183 -#define meshtastic_OEMStore_size 3502 +#define meshtastic_OEMStore_size 3496 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 96 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index c612b24ab..38529fc14 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -187,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 669 +#define meshtastic_LocalConfig_size 663 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus From 5514aab0076352f9dcc610ea1ce65ce3d3823619 Mon Sep 17 00:00:00 2001 From: Nestpebble <116762865+Nestpebble@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:24:23 +0100 Subject: [PATCH 217/305] add a .yml to setup a Gitpod instance quickly (#4551) * Create .gitpod.yml * Update .gitpod.yml --- .gitpod.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitpod.yml diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..0af235a3a --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,2 @@ +tasks: + - init: pip install platformio && pip install --upgrade pip From 8847945734f190e844e866197bf372957b3a85fb Mon Sep 17 00:00:00 2001 From: John Hollowell Date: Fri, 23 Aug 2024 21:25:16 -0400 Subject: [PATCH 218/305] Add devcontainer (#4491) devcontainers can be used by IDEs/editors like VS Code to create a standardized development environment in a container --- .devcontainer/Dockerfile | 24 ++++++++++++++++++++++++ .devcontainer/devcontainer.json | 28 ++++++++++++++++++++++++++++ .devcontainer/setup.sh | 3 +++ 3 files changed, 55 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/setup.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..82f4d91bf --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12 + +# [Optional] Uncomment this section to install additional packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends \ + ca-certificates \ + g++ \ + git \ + libbluetooth-dev \ + libgpiod-dev \ + liborcania-dev \ + libssl-dev \ + libulfius-dev \ + libyaml-cpp-dev \ + pkg-config \ + python3 \ + python3-pip \ + python3-venv \ + python3-wheel \ + wget \ + zip \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN pip3 install --no-cache-dir -U platformio==6.1.15 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..e45fd5d93 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +{ + "name": "Meshtastic Firmware Dev", + "build": { + "dockerfile": "Dockerfile" + }, + "features": { + "ghcr.io/devcontainers/features/python:1": { + "installTools": true, + "version": "latest" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "platformio.platformio-ide", + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ 4403 ], + + // Run commands to prepare the container for use + "postCreateCommand": ".devcontainer/setup.sh", +} diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 000000000..866a4a417 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +git submodule update --init \ No newline at end of file From 23844389ac22a4f80ab96e91783f984835250c4a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:44:14 -0500 Subject: [PATCH 219/305] [create-pull-request] automated change (#4544) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 183ba970a..52cfa2c1c 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 183ba970a7a71de7a81541b74c413543523417d2 +Subproject commit 52cfa2c1c2cd5a1a714e1338d551d967f674fca8 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index f32f865db..d612d74be 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -189,6 +189,13 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74, /* Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. */ meshtastic_HardwareModel_ME25LS01_4Y10TD = 75, + /* RP2040_FEATHER_RFM95 + Adafruit Feather RP2040 with RFM95 LoRa Radio RFM95 with SX1272, SSD1306 OLED + https://www.adafruit.com/product/5714 + https://www.adafruit.com/product/326 + https://www.adafruit.com/product/938 + ^^^ short A0 to switch to I2C address 0x3C */ + meshtastic_HardwareModel_RP2040_FEATHER_RFM95 = 76, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From d6dac1737acba65fcd48e9d30d2311aaf210f5f4 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 24 Aug 2024 12:19:31 -0500 Subject: [PATCH 220/305] Userlite mem comparison (#4552) --- src/mesh/NodeDB.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0504cc273..3af6c6a45 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1027,9 +1027,10 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde #endif // Both of info->user and p start as filled with zero so I think this is okay - bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); + auto lite = TypeConversions::ConvertToUserLite(p); + bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex); - info->user = TypeConversions::ConvertToUserLite(p); + info->user = lite; if (info->user.public_key.size == 32) { printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32); } From d0fd17134e9fdc51d838694a9a51e9143ae628a3 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 26 Aug 2024 07:48:07 -0500 Subject: [PATCH 221/305] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/config.pb.h | 11 ++++++----- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/protobufs b/protobufs index 52cfa2c1c..431291e67 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 52cfa2c1c2cd5a1a714e1338d551d967f674fca8 +Subproject commit 431291e673c1c7592ee64cb972d7845589f60138 diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index d79654856..eb03ddc58 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -532,7 +532,8 @@ typedef struct _meshtastic_Config_SecurityConfig { Used to create a shared key with a remote device. */ meshtastic_Config_SecurityConfig_private_key_t private_key; /* The public key authorized to send admin messages to this node. */ - meshtastic_Config_SecurityConfig_admin_key_t admin_key; + pb_size_t admin_key_count; + meshtastic_Config_SecurityConfig_admin_key_t admin_key[1]; /* If true, device is considered to be "managed" by a mesh administrator via admin messages Device is managed by a mesh administrator. */ bool is_managed; @@ -657,7 +658,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} -#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_default {0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -668,7 +669,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} -#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ @@ -900,7 +901,7 @@ X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) #define meshtastic_Config_SecurityConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BYTES, public_key, 1) \ X(a, STATIC, SINGULAR, BYTES, private_key, 2) \ -X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ +X(a, STATIC, REPEATED, BYTES, admin_key, 3) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ @@ -948,7 +949,7 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 -#define meshtastic_Config_SecurityConfig_size 110 +#define meshtastic_Config_SecurityConfig_size 111 #define meshtastic_Config_SessionkeyConfig_size 0 #define meshtastic_Config_size 199 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 976e0e135..692402210 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -358,7 +358,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 183 -#define meshtastic_OEMStore_size 3496 +#define meshtastic_OEMStore_size 3497 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 96 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 38529fc14..91a23dc4f 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -187,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 663 +#define meshtastic_LocalConfig_size 664 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus From 777ae2b99c13bbe59f94fd23f9fcdcbab9674419 Mon Sep 17 00:00:00 2001 From: John Milton Date: Mon, 26 Aug 2024 11:28:08 -0400 Subject: [PATCH 222/305] Add support for Adafruit Feather RP2040 with RFM95. (#4451) * Add support for Adafruit Feather RP2040 with RFM95. * Update mesh.pb.h dropping this change from the file generated by the protobuf * Update mesh.pb.h remove these reverting changes * Update mesh.pb.h oops, missed a comma --- src/platform/rp2040/architecture.h | 2 + variants/feather_rp2040_rfm95/platformio.ini | 16 +++++ variants/feather_rp2040_rfm95/variant.h | 61 ++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 variants/feather_rp2040_rfm95/platformio.ini create mode 100644 variants/feather_rp2040_rfm95/variant.h diff --git a/src/platform/rp2040/architecture.h b/src/platform/rp2040/architecture.h index d7d7214c0..3f75735d3 100644 --- a/src/platform/rp2040/architecture.h +++ b/src/platform/rp2040/architecture.h @@ -29,4 +29,6 @@ #define HW_VENDOR meshtastic_HardwareModel_SENSELORA_RP2040 #elif defined(RP2040_LORA) #define HW_VENDOR meshtastic_HardwareModel_RP2040_LORA +#elif defined(RP2040_FEATHER_RFM95) +#define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95 #endif \ No newline at end of file diff --git a/variants/feather_rp2040_rfm95/platformio.ini b/variants/feather_rp2040_rfm95/platformio.ini new file mode 100644 index 000000000..a28ad7655 --- /dev/null +++ b/variants/feather_rp2040_rfm95/platformio.ini @@ -0,0 +1,16 @@ +[env:feather_rp2040_rfm95] +extends = rp2040_base +board = adafruit_feather +upload_protocol = picotool + +# add our variants files to the include and src paths +build_flags = ${rp2040_base.build_flags} + -DRP2040_FEATHER_RFM95 + -Ivariants/feather_rp2040_rfm95 + -DDEBUG_RP2040_PORT=Serial + -DHW_SPI1_DEVICE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" +lib_deps = + ${rp2040_base.lib_deps} +debug_build_flags = ${rp2040_base.build_flags} +debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/feather_rp2040_rfm95/variant.h b/variants/feather_rp2040_rfm95/variant.h new file mode 100644 index 000000000..e9e178202 --- /dev/null +++ b/variants/feather_rp2040_rfm95/variant.h @@ -0,0 +1,61 @@ +// #define RADIOLIB_CUSTOM_ARDUINO 1 +// #define RADIOLIB_TONE_UNSUPPORTED 1 +// #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED 1 + +#define ARDUINO_ARCH_AVR + +// #define USE_SSD1306 + +// #define USE_SH1106 1 + +// default I2C pins: +// SDA = 4 +// SCL = 5 + +// Recommended pins for SerialModule: +// txd = 8 +// rxd = 9 + +#define EXT_NOTIFY_OUT 22 +#define BUTTON_PIN 7 +// #define BUTTON_NEED_PULLUP + +#define LED_PIN PIN_LED + +// #define BATTERY_PIN 26 +// ratio of voltage divider = 3.0 (R17=200k, R18=100k) +// #define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic + +#define USE_RF95 // RFM95/SX127x + +#undef LORA_SCK +#undef LORA_MISO +#undef LORA_MOSI +#undef LORA_CS + +// https://www.adafruit.com/product/5714 +// https://learn.adafruit.com/feather-rp2040-rfm95 +// https://learn.adafruit.com/assets/120283 +// https://learn.adafruit.com/assets/120813 +#define LORA_SCK 14 // 10 12P +#define LORA_MISO 8 // 12 10P +#define LORA_MOSI 15 // 11 11P +#define LORA_CS 16 // 3 13P + +#define LORA_RESET 17 // 15 14P + +#define LORA_DIO0 21 // ?? 6P +#define LORA_DIO1 22 // 20 7P +#define LORA_DIO2 23 // 2 8P +#define LORA_DIO3 19 // ?? 3P +#define LORA_DIO4 20 // ?? 4P +#define LORA_DIO5 18 // ?? 15P + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +// #define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif \ No newline at end of file From 5824a8f4c1eaae2c6b9b62bb227387b32d488d3a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 26 Aug 2024 12:29:44 -0500 Subject: [PATCH 223/305] Deal with admin_key being repeated (#4558) --- src/mesh/NodeDB.cpp | 6 +++--- src/modules/AdminModule.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 3af6c6a45..fa736e08a 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -292,10 +292,10 @@ void NodeDB::installDefaultConfig() config.lora.ignore_mqtt = false; #endif #ifdef ADMIN_KEY_USERPREFS - memcpy(config.security.admin_key.bytes, admin_key_userprefs, 32); - config.security.admin_key.size = 32; + memcpy(config.security.admin_key[0].bytes, admin_key_userprefs, 32); + config.security.admin_key[0].size = 32; #else - config.security.admin_key.size = 0; + config.security.admin_key[0].size = 0; #endif config.security.public_key.size = 0; config.security.private_key.size = 0; diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index ef60a4bf2..b63eca396 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -75,7 +75,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta // and only allowing responses from that remote. if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) || (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || - (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) { + (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key[0].bytes, 32) == 0))) { LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); return handled; } From b9a8683a4bfe9c453313554f8de1e85acb789383 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 26 Aug 2024 15:48:47 -0500 Subject: [PATCH 224/305] Mask out random bits when doing queue ordering (#4561) * Mask out random bits when doing queue ordering * Parenthesis --- src/mesh/MeshPacketQueue.cpp | 7 ++++--- src/mesh/MeshTypes.h | 5 +++-- src/mesh/Router.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 24756fd2a..f1c6c4ff3 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -20,8 +20,9 @@ bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_Mes // If priorities differ, use that // for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but // no big deal) - return (p1p != p2p) ? (p1p < p2p) // prefer bigger priorities - : (p1->id >= p2->id); // prefer smaller packet ids + return (p1p != p2p) + ? (p1p < p2p) // prefer bigger priorities + : ((p1->id & ID_COUNTER_MASK) >= (p2->id & ID_COUNTER_MASK)); // Mask to counter portion, prefer smaller packet ids } MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {} @@ -127,4 +128,4 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); return true; -} +} \ No newline at end of file diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index c0919bf5d..1c9099c39 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -14,8 +14,9 @@ typedef uint32_t PacketId; // A packet sequence number 1 // Reserved to only deliver packets over high speed (non-lora) transports, such as MQTT or BLE mesh (not yet implemented) #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 -#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -#define ERRNO_DISABLED 34 // the interface is disabled +#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER +#define ERRNO_DISABLED 34 // the interface is disabled +#define ID_COUNTER_MASK (UINT32_MAX >> 22) // mask to select the counter portion of the ID /* * Source of a received message diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index bdd8c4e6c..61b1bbfb6 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -109,7 +109,7 @@ PacketId generatePacketId() rollingPacketId++; - rollingPacketId &= UINT32_MAX >> 22; // Mask out the top 22 bits + rollingPacketId &= ID_COUNTER_MASK; // Mask out the top 22 bits PacketId id = rollingPacketId | random(UINT32_MAX & 0x7fffffff) << 10; // top 22 bits LOG_DEBUG("Partially randomized packet id %u\n", id); return id; From ada61ae17840da40b5f3b257a7683b822d1a1446 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 27 Aug 2024 14:49:58 -0500 Subject: [PATCH 225/305] Don't compare nodeDB macaddr to owner.macaddr, because in rare cases that may be unset. (#4562) Co-authored-by: Ben Meadors --- src/mesh/NodeDB.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index fa736e08a..8409eaff6 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -620,7 +620,7 @@ void NodeDB::pickNewNodeNum() meshtastic_NodeInfoLite *found; while ((nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED) || - ((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr)) != 0)) { + ((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0)) { NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate); nodeNum = candidate; From ab62590aa98049e6be46dbac648571863cb2f1fa Mon Sep 17 00:00:00 2001 From: Power Li Date: Wed, 28 Aug 2024 05:26:02 +0800 Subject: [PATCH 226/305] set current time to system time in portduino build (#4556) * set current time to system time in portduino build * fix includes order --------- Co-authored-by: Jonathan Bennett --- src/platform/portduino/PortduinoGlue.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 3532d2e79..cce893c0b 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -1,5 +1,6 @@ #include "CryptoEngine.h" #include "PortduinoGPIO.h" +#include "RTC.h" #include "SPIChip.h" #include "mesh/RF95Interface.h" #include "sleep.h" @@ -370,6 +371,12 @@ void portduinoSetup() exit(EXIT_FAILURE); } } + + struct timeval tv; + tv.tv_sec = time(NULL); + tv.tv_usec = 0; + perhapsSetRTC(RTCQualityNTP, &tv); + return; } From 50d778d281d58bb78094e4b0f45a3f7bf139b6f4 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 27 Aug 2024 18:24:14 -0500 Subject: [PATCH 227/305] Add RAK4631 hex to firmware release --- bin/build-nrf52.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index 060d06cfd..9d0b3dfdd 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -46,3 +46,8 @@ else cp bin/device-update.* $OUTDIR cp bin/*.uf2 $OUTDIR fi + +if (echo $1 | grep -q "rak4631"); then + echo "Copying hex file" + cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex +fi \ No newline at end of file From 0ee9d375b3c204586474dea98d0123986176c615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 28 Aug 2024 12:43:19 +0200 Subject: [PATCH 228/305] fix #4390 (#4571) --- src/serialization/MeshPacketSerializer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index a42bb867a..e00dde024 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -76,6 +76,13 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction); msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust); msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull); + } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { + msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard); + msgPayload["pm25"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_standard); + msgPayload["pm100"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm100_standard); + msgPayload["pm10_e"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_environmental); + msgPayload["pm25_e"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_environmental); + msgPayload["pm100_e"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm100_environmental); } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage); msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current); From ef9ecec341f162e5bd6cecc48cc97eea59055c95 Mon Sep 17 00:00:00 2001 From: "FW\\AM5" Date: Wed, 28 Aug 2024 13:10:19 +0200 Subject: [PATCH 229/305] Support for Polish OLED characters Added support for Polish OLED characters. - Custom FONT_SMALL ArialMT_Plain_10_PL - Automatic selection between Polish and Ukrainian/Russian characters mapping depending on the -D OLED_{LANG_NAME} flage --- .vscode/extensions.json | 7 +- platformio.ini | 3 +- src/graphics/Screen.cpp | 2 +- src/graphics/Screen.h | 50 +++ src/graphics/ScreenFonts.h | 8 + src/graphics/fonts/OLEDDisplayFontsPL.cpp | 440 ++++++++++++++++++++++ src/graphics/fonts/OLEDDisplayFontsPL.h | 11 + 7 files changed, 516 insertions(+), 5 deletions(-) create mode 100644 src/graphics/fonts/OLEDDisplayFontsPL.cpp create mode 100644 src/graphics/fonts/OLEDDisplayFontsPL.h diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b50c95349..080e70d08 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,8 +2,9 @@ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "ms-vscode.cpptools", - "platformio.platformio-ide", - "trunk.io" + "platformio.platformio-ide" ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] } diff --git a/platformio.ini b/platformio.ini index 4de1ec39f..5c3c4e421 100644 --- a/platformio.ini +++ b/platformio.ini @@ -81,6 +81,7 @@ build_flags = -Wno-missing-field-initializers -DRADIOLIB_EXCLUDE_APRS -DRADIOLIB_EXCLUDE_LORAWAN -DMESHTASTIC_EXCLUDE_DROPZONE=1 + ;-D OLED_PL monitor_speed = 115200 monitor_filters = direct @@ -157,4 +158,4 @@ lib_deps = mprograms/QMC5883LCompass@^1.2.0 - https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee + https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3a4db6ee0..7fe5964d5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -123,7 +123,7 @@ static bool heartbeat = false; /// Check if the display can render a string (detect special chars; emoji) static bool haveGlyphs(const char *str) { -#if defined(OLED_UA) || defined(OLED_RU) +#if defined(OLED_PL) ||defined(OLED_UA) || defined(OLED_RU) // Don't want to make any assumptions about custom language support return true; #endif diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 93e5f2ef7..7db580272 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -322,6 +322,53 @@ class Screen : public concurrency::OSThread uint8_t last = LASTCHAR; // get last char LASTCHAR = ch; +#if defined(OLED_PL) + + switch (last) { // conversion depending on first UTF8-character + case 0xC2: { + SKIPREST = false; + return (uint8_t)ch; + } + case 0xC3: { + + if (ch == 147) + return (uint8_t)(ch); // Ó + else + if (ch == 179) + return (uint8_t)(148); // ó + else + return (uint8_t)(ch | 0xC0); + break; + + } + + case 0xC4: { + SKIPREST = false; + return (uint8_t)(ch); + } + + case 0xC5: { + SKIPREST = false; + if (ch == 132) + return (uint8_t)(136); // ń + else + if (ch == 186) + return (uint8_t)(137); // ź + else + return (uint8_t)(ch); + break; + } + } + + // We want to strip out prefix chars for two-byte char formats + if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) + return (uint8_t)0; + + +#endif + +#if defined(OLED_UA) || defined(OLED_RU) + switch (last) { // conversion depending on first UTF8-character case 0xC2: { SKIPREST = false; @@ -376,6 +423,9 @@ class Screen : public concurrency::OSThread if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1) return (uint8_t)0; +#endif + + // If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the // rest of it if (SKIPREST) diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 8a48d053e..3e4c220a0 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -1,5 +1,9 @@ #pragma once +#ifdef OLED_PL +#include "graphics/fonts/OLEDDisplayFontsPL.h" +#endif + #ifdef OLED_RU #include "graphics/fonts/OLEDDisplayFontsRU.h" #endif @@ -15,6 +19,9 @@ #define FONT_MEDIUM ArialMT_Plain_24 // Height: 28 #define FONT_LARGE ArialMT_Plain_24 // Height: 28 #else +#ifdef OLED_PL +#define FONT_SMALL ArialMT_Plain_10_PL +#else #ifdef OLED_RU #define FONT_SMALL ArialMT_Plain_10_RU #else @@ -24,6 +31,7 @@ #define FONT_SMALL ArialMT_Plain_10 // Height: 13 #endif #endif +#endif #define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 #define FONT_LARGE ArialMT_Plain_24 // Height: 28 #endif diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.cpp b/src/graphics/fonts/OLEDDisplayFontsPL.cpp new file mode 100644 index 000000000..1322b1772 --- /dev/null +++ b/src/graphics/fonts/OLEDDisplayFontsPL.cpp @@ -0,0 +1,440 @@ +#include "OLEDDisplayFontsPL.h" + +// Font generated or edited with the glyphEditor +const uint8_t ArialMT_Plain_10_PL[] PROGMEM = { +0x0A, // Width: 10 +0x0D, // Height: 13 +0x20, // First char: 32 +0xE0, // Number of chars: 224 + +// Jump Table: +0xFF, 0xFF, 0x00, 0x03, // 32:65535 +0x00, 0x00, 0x04, 0x03, // 33 +0x00, 0x04, 0x05, 0x04, // 34 +0x00, 0x09, 0x09, 0x06, // 35 +0x00, 0x12, 0x0A, 0x06, // 36 +0x00, 0x1C, 0x10, 0x09, // 37 +0x00, 0x2C, 0x0E, 0x08, // 38 +0x00, 0x3A, 0x01, 0x02, // 39 +0x00, 0x3B, 0x06, 0x04, // 40 +0x00, 0x41, 0x06, 0x04, // 41 +0x00, 0x47, 0x05, 0x04, // 42 +0x00, 0x4C, 0x09, 0x06, // 43 +0x00, 0x55, 0x04, 0x03, // 44 +0x00, 0x59, 0x03, 0x03, // 45 +0x00, 0x5C, 0x04, 0x03, // 46 +0x00, 0x60, 0x05, 0x04, // 47 +0x00, 0x65, 0x0A, 0x06, // 48 +0x00, 0x6F, 0x08, 0x05, // 49 +0x00, 0x77, 0x0A, 0x06, // 50 +0x00, 0x81, 0x0A, 0x06, // 51 +0x00, 0x8B, 0x0B, 0x07, // 52 +0x00, 0x96, 0x0A, 0x06, // 53 +0x00, 0xA0, 0x0A, 0x06, // 54 +0x00, 0xAA, 0x09, 0x06, // 55 +0x00, 0xB3, 0x0A, 0x06, // 56 +0x00, 0xBD, 0x0A, 0x06, // 57 +0x00, 0xC7, 0x04, 0x03, // 58 +0x00, 0xCB, 0x04, 0x03, // 59 +0x00, 0xCF, 0x0A, 0x06, // 60 +0x00, 0xD9, 0x09, 0x06, // 61 +0x00, 0xE2, 0x09, 0x06, // 62 +0x00, 0xEB, 0x0B, 0x07, // 63 +0x00, 0xF6, 0x14, 0x0B, // 64 +0x01, 0x0A, 0x0E, 0x08, // 65 +0x01, 0x18, 0x0C, 0x07, // 66 +0x01, 0x24, 0x0C, 0x07, // 67 +0x01, 0x30, 0x0B, 0x07, // 68 +0x01, 0x3B, 0x0C, 0x07, // 69 +0x01, 0x47, 0x09, 0x06, // 70 +0x01, 0x50, 0x0D, 0x08, // 71 +0x01, 0x5D, 0x0C, 0x07, // 72 +0x01, 0x69, 0x04, 0x03, // 73 +0x01, 0x6D, 0x08, 0x05, // 74 +0x01, 0x75, 0x0E, 0x08, // 75 +0x01, 0x83, 0x0C, 0x07, // 76 +0x01, 0x8F, 0x10, 0x09, // 77 +0x01, 0x9F, 0x0C, 0x07, // 78 +0x01, 0xAB, 0x0E, 0x08, // 79 +0x01, 0xB9, 0x0B, 0x07, // 80 +0x01, 0xC4, 0x0E, 0x08, // 81 +0x01, 0xD2, 0x0C, 0x07, // 82 +0x01, 0xDE, 0x0C, 0x07, // 83 +0x01, 0xEA, 0x0B, 0x07, // 84 +0x01, 0xF5, 0x0C, 0x07, // 85 +0x02, 0x01, 0x0D, 0x08, // 86 +0x02, 0x0E, 0x11, 0x0A, // 87 +0x02, 0x1F, 0x0E, 0x08, // 88 +0x02, 0x2D, 0x0D, 0x08, // 89 +0x02, 0x3A, 0x0C, 0x07, // 90 +0x02, 0x46, 0x06, 0x04, // 91 +0x02, 0x4C, 0x06, 0x04, // 92 +0x02, 0x52, 0x04, 0x03, // 93 +0x02, 0x56, 0x09, 0x06, // 94 +0x02, 0x5F, 0x0C, 0x07, // 95 +0x02, 0x6B, 0x03, 0x03, // 96 +0x02, 0x6E, 0x0A, 0x06, // 97 +0x02, 0x78, 0x0A, 0x06, // 98 +0x02, 0x82, 0x0A, 0x06, // 99 +0x02, 0x8C, 0x0A, 0x06, // 100 +0x02, 0x96, 0x0A, 0x06, // 101 +0x02, 0xA0, 0x05, 0x04, // 102 +0x02, 0xA5, 0x0A, 0x06, // 103 +0x02, 0xAF, 0x0A, 0x06, // 104 +0x02, 0xB9, 0x04, 0x03, // 105 +0x02, 0xBD, 0x04, 0x03, // 106 +0x02, 0xC1, 0x08, 0x05, // 107 +0x02, 0xC9, 0x04, 0x03, // 108 +0x02, 0xCD, 0x10, 0x09, // 109 +0x02, 0xDD, 0x0A, 0x06, // 110 +0x02, 0xE7, 0x0A, 0x06, // 111 +0x02, 0xF1, 0x0A, 0x06, // 112 +0x02, 0xFB, 0x0A, 0x06, // 113 +0x03, 0x05, 0x05, 0x04, // 114 +0x03, 0x0A, 0x08, 0x05, // 115 +0x03, 0x12, 0x06, 0x04, // 116 +0x03, 0x18, 0x0A, 0x06, // 117 +0x03, 0x22, 0x09, 0x06, // 118 +0x03, 0x2B, 0x0E, 0x08, // 119 +0x03, 0x39, 0x0A, 0x06, // 120 +0x03, 0x43, 0x09, 0x06, // 121 +0x03, 0x4C, 0x0A, 0x06, // 122 +0x03, 0x56, 0x06, 0x04, // 123 +0x03, 0x5C, 0x04, 0x03, // 124 +0x03, 0x60, 0x05, 0x04, // 125 +0x03, 0x65, 0x09, 0x06, // 126 +0xFF, 0xFF, 0x00, 0x0A, // 127 +0xFF, 0xFF, 0x00, 0x0A, // 128 +0x03, 0x6E, 0x0C, 0x07, // 129 +0x03, 0x7A, 0x05, 0x04, // 130 +0x03, 0x7F, 0x0C, 0x07, // 131 +0x03, 0x8B, 0x0E, 0x08, // 132 +0x03, 0x99, 0x0C, 0x07, // 133 +0x03, 0xA5, 0x0C, 0x07, // 134 +0x03, 0xB1, 0x0A, 0x06, // 135 +0x03, 0xBB, 0x0A, 0x06, // 136 +0x03, 0xC5, 0x0A, 0x06, // 137 +0xFF, 0xFF, 0x00, 0x0A, // 138 +0xFF, 0xFF, 0x00, 0x0A, // 139 +0xFF, 0xFF, 0x00, 0x0A, // 140 +0xFF, 0xFF, 0x00, 0x0A, // 141 +0xFF, 0xFF, 0x00, 0x0A, // 142 +0xFF, 0xFF, 0x00, 0x0A, // 143 +0xFF, 0xFF, 0x00, 0x0A, // 144 +0xFF, 0xFF, 0x00, 0x0A, // 145 +0xFF, 0xFF, 0x00, 0x0A, // 146 +0x03, 0xCF, 0x0E, 0x08, // 147 +0x03, 0xDD, 0x0A, 0x06, // 148 +0xFF, 0xFF, 0x00, 0x0A, // 149 +0xFF, 0xFF, 0x00, 0x0A, // 150 +0xFF, 0xFF, 0x00, 0x0A, // 151 +0x03, 0xE7, 0x0C, 0x07, // 152 +0x03, 0xF3, 0x0C, 0x07, // 153 +0x03, 0xFF, 0x0C, 0x07, // 154 +0x04, 0x0B, 0x08, 0x05, // 155 +0xFF, 0xFF, 0x00, 0x0A, // 156 +0xFF, 0xFF, 0x00, 0x0A, // 157 +0xFF, 0xFF, 0x00, 0x0A, // 158 +0xFF, 0xFF, 0x00, 0x0A, // 159 +0xFF, 0xFF, 0x00, 0x0A, // 160 +0x04, 0x13, 0x04, 0x03, // 161 +0x04, 0x17, 0x0A, 0x06, // 162 +0x04, 0x21, 0x0C, 0x07, // 163 +0x04, 0x2D, 0x0A, 0x06, // 164 +0x04, 0x37, 0x0A, 0x06, // 165 +0x04, 0x41, 0x04, 0x03, // 166 +0x04, 0x45, 0x0A, 0x06, // 167 +0x04, 0x4F, 0x05, 0x04, // 168 +0x04, 0x54, 0x0D, 0x08, // 169 +0x04, 0x61, 0x07, 0x05, // 170 +0x04, 0x68, 0x0A, 0x06, // 171 +0x04, 0x72, 0x09, 0x06, // 172 +0x04, 0x7B, 0x03, 0x03, // 173 +0x04, 0x7E, 0x0D, 0x08, // 174 +0x04, 0x8B, 0x0B, 0x07, // 175 +0x04, 0x96, 0x07, 0x05, // 176 +0x04, 0x9D, 0x0A, 0x06, // 177 +0x04, 0xA7, 0x05, 0x04, // 178 +0x04, 0xAC, 0x05, 0x04, // 179 +0x04, 0xB1, 0x05, 0x04, // 180 +0x04, 0xB6, 0x0A, 0x06, // 181 +0x04, 0xC0, 0x09, 0x06, // 182 +0x04, 0xC9, 0x03, 0x03, // 183 +0x04, 0xCC, 0x06, 0x04, // 184 +0x04, 0xD2, 0x0C, 0x07, // 185 +0x04, 0xDE, 0x07, 0x05, // 186 +0x04, 0xE5, 0x0C, 0x07, // 187 +0x04, 0xF1, 0x0A, 0x06, // 188 +0x04, 0xFB, 0x10, 0x09, // 189 +0x05, 0x0B, 0x10, 0x09, // 190 +0x05, 0x1B, 0x0A, 0x06, // 191 +0x05, 0x25, 0x0E, 0x08, // 192 +0x05, 0x33, 0x0E, 0x08, // 193 +0x05, 0x41, 0x0E, 0x08, // 194 +0x05, 0x4F, 0x0E, 0x08, // 195 +0x05, 0x5D, 0x0E, 0x08, // 196 +0x05, 0x6B, 0x0E, 0x08, // 197 +0x05, 0x79, 0x12, 0x0A, // 198 +0x05, 0x8B, 0x0C, 0x07, // 199 +0x05, 0x97, 0x0C, 0x07, // 200 +0x05, 0xA3, 0x0C, 0x07, // 201 +0x05, 0xAF, 0x0C, 0x07, // 202 +0x05, 0xBB, 0x0C, 0x07, // 203 +0x05, 0xC7, 0x05, 0x04, // 204 +0x05, 0xCC, 0x04, 0x03, // 205 +0x05, 0xD0, 0x04, 0x03, // 206 +0x05, 0xD4, 0x05, 0x04, // 207 +0x05, 0xD9, 0x0B, 0x07, // 208 +0x05, 0xE4, 0x0C, 0x07, // 209 +0x05, 0xF0, 0x0E, 0x08, // 210 +0x05, 0xFE, 0x0E, 0x08, // 211 +0x06, 0x0C, 0x0E, 0x08, // 212 +0x06, 0x1A, 0x0E, 0x08, // 213 +0x06, 0x28, 0x0E, 0x08, // 214 +0x06, 0x36, 0x0A, 0x06, // 215 +0x06, 0x40, 0x0D, 0x08, // 216 +0x06, 0x4D, 0x0C, 0x07, // 217 +0x06, 0x59, 0x0C, 0x07, // 218 +0x06, 0x65, 0x0C, 0x07, // 219 +0x06, 0x71, 0x0C, 0x07, // 220 +0x06, 0x7D, 0x0D, 0x08, // 221 +0x06, 0x8A, 0x0B, 0x07, // 222 +0x06, 0x95, 0x0C, 0x07, // 223 +0x06, 0xA1, 0x0A, 0x06, // 224 +0x06, 0xAB, 0x0A, 0x06, // 225 +0x06, 0xB5, 0x0A, 0x06, // 226 +0x06, 0xBF, 0x0A, 0x06, // 227 +0x06, 0xC9, 0x0A, 0x06, // 228 +0x06, 0xD3, 0x0A, 0x06, // 229 +0x06, 0xDD, 0x10, 0x09, // 230 +0x06, 0xED, 0x0A, 0x06, // 231 +0x06, 0xF7, 0x0A, 0x06, // 232 +0x07, 0x01, 0x0A, 0x06, // 233 +0x07, 0x0B, 0x0A, 0x06, // 234 +0x07, 0x15, 0x0A, 0x06, // 235 +0x07, 0x1F, 0x05, 0x04, // 236 +0x07, 0x24, 0x04, 0x03, // 237 +0x07, 0x28, 0x05, 0x04, // 238 +0x07, 0x2D, 0x05, 0x04, // 239 +0x07, 0x32, 0x0A, 0x06, // 240 +0x07, 0x3C, 0x0A, 0x06, // 241 +0x07, 0x46, 0x0A, 0x06, // 242 +0x07, 0x50, 0x0A, 0x06, // 243 +0x07, 0x5A, 0x0A, 0x06, // 244 +0x07, 0x64, 0x0A, 0x06, // 245 +0x07, 0x6E, 0x0A, 0x06, // 246 +0x07, 0x78, 0x09, 0x06, // 247 +0x07, 0x81, 0x0A, 0x06, // 248 +0x07, 0x8B, 0x0A, 0x06, // 249 +0x07, 0x95, 0x0A, 0x06, // 250 +0x07, 0x9F, 0x0A, 0x06, // 251 +0x07, 0xA9, 0x0A, 0x06, // 252 +0x07, 0xB3, 0x09, 0x06, // 253 +0x07, 0xBC, 0x0A, 0x06, // 254 +0x07, 0xC6, 0x09, 0x06, // 255 +// Font Data: +0x00, 0x00, 0xF8, 0x02, // 33 +0x38, 0x00, 0x00, 0x00, 0x38, // 34 +0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 +0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 +0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 +0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 +0x38, // 39 +0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 +0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 +0x28, 0x00, 0x18, 0x00, 0x28, // 42 +0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 +0x00, 0x00, 0x00, 0x06, // 44 +0x80, 0x00, 0x80, // 45 +0x00, 0x00, 0x00, 0x02, // 46 +0x00, 0x03, 0xE0, 0x00, 0x18, // 47 +0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 +0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 +0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 +0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 +0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 +0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 +0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 +0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 +0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 +0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 +0x00, 0x00, 0x20, 0x02, // 58 +0x00, 0x00, 0x20, 0x06, // 59 +0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 +0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 +0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 +0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 +0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 +0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 +0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 +0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 +0x00, 0x00, 0xF8, 0x03, // 73 +0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 +0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 +0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 +0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 +0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 +0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 +0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 +0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 +0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 +0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 +0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 +0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 +0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 +0x08, 0x08, 0xF8, 0x0F, // 93 +0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 +0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 +0x08, 0x00, 0x10, // 96 +0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 +0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 +0x20, 0x00, 0xF0, 0x03, 0x28, // 102 +0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 +0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 +0x00, 0x00, 0xE8, 0x03, // 105 +0x00, 0x08, 0xE8, 0x07, // 106 +0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 +0x00, 0x00, 0xF8, 0x03, // 108 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 +0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 +0x00, 0x00, 0xE0, 0x03, 0x20, // 114 +0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 +0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 +0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 +0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 +0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 +0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 +0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 +0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 +0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 +0x00, 0x00, 0xF8, 0x0F, // 124 +0x08, 0x08, 0x78, 0x0F, 0x80, // 125 +0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 +0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129 +0x40, 0x00, 0xF8, 0x03, 0x20, // 130 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x05, 0x00, 0x0A, // 132 +0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x07, 0x00, 0x08, // 133 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136 +0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x06, 0x48, 0x0A, // 152 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x06, 0x00, 0x08, // 153 +0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154 +0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155 +0x00, 0x00, 0xA0, 0x0F, // 161 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 +0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 +0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 +0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 +0x00, 0x00, 0x38, 0x0F, // 166 +0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 +0x08, 0x00, 0x00, 0x00, 0x08, // 168 +0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 +0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 +0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 +0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 +0x80, 0x00, 0x80, // 173 +0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 +0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 +0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 +0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 +0x48, 0x00, 0x68, 0x00, 0x58, // 178 +0x48, 0x00, 0x58, 0x00, 0x68, // 179 +0x00, 0x00, 0x10, 0x00, 0x08, // 180 +0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 +0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 +0x00, 0x00, 0x40, // 183 +0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184 +0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185 +0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 +0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187 +0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 188 +0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 +0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 +0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193 +0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194 +0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195 +0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196 +0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197 +0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199 +0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201 +0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202 +0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203 +0x00, 0x00, 0xF9, 0x03, 0x02, // 204 +0x02, 0x00, 0xF9, 0x03, // 205 +0x01, 0x00, 0xFA, 0x03, // 206 +0x02, 0x00, 0xF8, 0x03, 0x02, // 207 +0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208 +0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212 +0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213 +0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214 +0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215 +0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216 +0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220 +0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221 +0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222 +0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223 +0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226 +0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228 +0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229 +0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231 +0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235 +0x00, 0x00, 0xE4, 0x03, 0x08, // 236 +0x08, 0x00, 0xE4, 0x03, // 237 +0x08, 0x00, 0xE4, 0x03, 0x08, // 238 +0x08, 0x00, 0xE0, 0x03, 0x08, // 239 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240 +0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241 +0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244 +0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246 +0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247 +0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248 +0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249 +0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250 +0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251 +0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252 +0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253 +0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254 +0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255 +}; \ No newline at end of file diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.h b/src/graphics/fonts/OLEDDisplayFontsPL.h new file mode 100644 index 000000000..59dd92c41 --- /dev/null +++ b/src/graphics/fonts/OLEDDisplayFontsPL.h @@ -0,0 +1,11 @@ +#ifndef OLEDDISPLAYFONTSPL_h +#define OLEDDISPLAYFONTSPL_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#define PROGMEM +#endif + +extern const uint8_t ArialMT_Plain_10_PL[] PROGMEM; +#endif \ No newline at end of file From b8609ff1308ee07814079511127c5cd23f028c22 Mon Sep 17 00:00:00 2001 From: "FW\\AM5" Date: Wed, 28 Aug 2024 13:10:19 +0200 Subject: [PATCH 230/305] Support for Polish OLED characters Added support for Polish OLED characters. - Custom FONT_SMALL ArialMT_Plain_10_PL - Automatic selection between Polish and Ukrainian/Russian characters mapping depending on the -D OLED_{LANG_NAME} flage --- .vscode/extensions.json | 7 +- platformio.ini | 3 +- src/graphics/Screen.cpp | 2 +- src/graphics/Screen.h | 50 +++ src/graphics/ScreenFonts.h | 8 + src/graphics/fonts/OLEDDisplayFontsPL.cpp | 440 ++++++++++++++++++++++ src/graphics/fonts/OLEDDisplayFontsPL.h | 11 + 7 files changed, 516 insertions(+), 5 deletions(-) create mode 100644 src/graphics/fonts/OLEDDisplayFontsPL.cpp create mode 100644 src/graphics/fonts/OLEDDisplayFontsPL.h diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b50c95349..080e70d08 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,8 +2,9 @@ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "ms-vscode.cpptools", - "platformio.platformio-ide", - "trunk.io" + "platformio.platformio-ide" ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] } diff --git a/platformio.ini b/platformio.ini index 4de1ec39f..5c3c4e421 100644 --- a/platformio.ini +++ b/platformio.ini @@ -81,6 +81,7 @@ build_flags = -Wno-missing-field-initializers -DRADIOLIB_EXCLUDE_APRS -DRADIOLIB_EXCLUDE_LORAWAN -DMESHTASTIC_EXCLUDE_DROPZONE=1 + ;-D OLED_PL monitor_speed = 115200 monitor_filters = direct @@ -157,4 +158,4 @@ lib_deps = mprograms/QMC5883LCompass@^1.2.0 - https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee + https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3a4db6ee0..7fe5964d5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -123,7 +123,7 @@ static bool heartbeat = false; /// Check if the display can render a string (detect special chars; emoji) static bool haveGlyphs(const char *str) { -#if defined(OLED_UA) || defined(OLED_RU) +#if defined(OLED_PL) ||defined(OLED_UA) || defined(OLED_RU) // Don't want to make any assumptions about custom language support return true; #endif diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 93e5f2ef7..7db580272 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -322,6 +322,53 @@ class Screen : public concurrency::OSThread uint8_t last = LASTCHAR; // get last char LASTCHAR = ch; +#if defined(OLED_PL) + + switch (last) { // conversion depending on first UTF8-character + case 0xC2: { + SKIPREST = false; + return (uint8_t)ch; + } + case 0xC3: { + + if (ch == 147) + return (uint8_t)(ch); // Ó + else + if (ch == 179) + return (uint8_t)(148); // ó + else + return (uint8_t)(ch | 0xC0); + break; + + } + + case 0xC4: { + SKIPREST = false; + return (uint8_t)(ch); + } + + case 0xC5: { + SKIPREST = false; + if (ch == 132) + return (uint8_t)(136); // ń + else + if (ch == 186) + return (uint8_t)(137); // ź + else + return (uint8_t)(ch); + break; + } + } + + // We want to strip out prefix chars for two-byte char formats + if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) + return (uint8_t)0; + + +#endif + +#if defined(OLED_UA) || defined(OLED_RU) + switch (last) { // conversion depending on first UTF8-character case 0xC2: { SKIPREST = false; @@ -376,6 +423,9 @@ class Screen : public concurrency::OSThread if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1) return (uint8_t)0; +#endif + + // If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the // rest of it if (SKIPREST) diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 8a48d053e..3e4c220a0 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -1,5 +1,9 @@ #pragma once +#ifdef OLED_PL +#include "graphics/fonts/OLEDDisplayFontsPL.h" +#endif + #ifdef OLED_RU #include "graphics/fonts/OLEDDisplayFontsRU.h" #endif @@ -15,6 +19,9 @@ #define FONT_MEDIUM ArialMT_Plain_24 // Height: 28 #define FONT_LARGE ArialMT_Plain_24 // Height: 28 #else +#ifdef OLED_PL +#define FONT_SMALL ArialMT_Plain_10_PL +#else #ifdef OLED_RU #define FONT_SMALL ArialMT_Plain_10_RU #else @@ -24,6 +31,7 @@ #define FONT_SMALL ArialMT_Plain_10 // Height: 13 #endif #endif +#endif #define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 #define FONT_LARGE ArialMT_Plain_24 // Height: 28 #endif diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.cpp b/src/graphics/fonts/OLEDDisplayFontsPL.cpp new file mode 100644 index 000000000..1322b1772 --- /dev/null +++ b/src/graphics/fonts/OLEDDisplayFontsPL.cpp @@ -0,0 +1,440 @@ +#include "OLEDDisplayFontsPL.h" + +// Font generated or edited with the glyphEditor +const uint8_t ArialMT_Plain_10_PL[] PROGMEM = { +0x0A, // Width: 10 +0x0D, // Height: 13 +0x20, // First char: 32 +0xE0, // Number of chars: 224 + +// Jump Table: +0xFF, 0xFF, 0x00, 0x03, // 32:65535 +0x00, 0x00, 0x04, 0x03, // 33 +0x00, 0x04, 0x05, 0x04, // 34 +0x00, 0x09, 0x09, 0x06, // 35 +0x00, 0x12, 0x0A, 0x06, // 36 +0x00, 0x1C, 0x10, 0x09, // 37 +0x00, 0x2C, 0x0E, 0x08, // 38 +0x00, 0x3A, 0x01, 0x02, // 39 +0x00, 0x3B, 0x06, 0x04, // 40 +0x00, 0x41, 0x06, 0x04, // 41 +0x00, 0x47, 0x05, 0x04, // 42 +0x00, 0x4C, 0x09, 0x06, // 43 +0x00, 0x55, 0x04, 0x03, // 44 +0x00, 0x59, 0x03, 0x03, // 45 +0x00, 0x5C, 0x04, 0x03, // 46 +0x00, 0x60, 0x05, 0x04, // 47 +0x00, 0x65, 0x0A, 0x06, // 48 +0x00, 0x6F, 0x08, 0x05, // 49 +0x00, 0x77, 0x0A, 0x06, // 50 +0x00, 0x81, 0x0A, 0x06, // 51 +0x00, 0x8B, 0x0B, 0x07, // 52 +0x00, 0x96, 0x0A, 0x06, // 53 +0x00, 0xA0, 0x0A, 0x06, // 54 +0x00, 0xAA, 0x09, 0x06, // 55 +0x00, 0xB3, 0x0A, 0x06, // 56 +0x00, 0xBD, 0x0A, 0x06, // 57 +0x00, 0xC7, 0x04, 0x03, // 58 +0x00, 0xCB, 0x04, 0x03, // 59 +0x00, 0xCF, 0x0A, 0x06, // 60 +0x00, 0xD9, 0x09, 0x06, // 61 +0x00, 0xE2, 0x09, 0x06, // 62 +0x00, 0xEB, 0x0B, 0x07, // 63 +0x00, 0xF6, 0x14, 0x0B, // 64 +0x01, 0x0A, 0x0E, 0x08, // 65 +0x01, 0x18, 0x0C, 0x07, // 66 +0x01, 0x24, 0x0C, 0x07, // 67 +0x01, 0x30, 0x0B, 0x07, // 68 +0x01, 0x3B, 0x0C, 0x07, // 69 +0x01, 0x47, 0x09, 0x06, // 70 +0x01, 0x50, 0x0D, 0x08, // 71 +0x01, 0x5D, 0x0C, 0x07, // 72 +0x01, 0x69, 0x04, 0x03, // 73 +0x01, 0x6D, 0x08, 0x05, // 74 +0x01, 0x75, 0x0E, 0x08, // 75 +0x01, 0x83, 0x0C, 0x07, // 76 +0x01, 0x8F, 0x10, 0x09, // 77 +0x01, 0x9F, 0x0C, 0x07, // 78 +0x01, 0xAB, 0x0E, 0x08, // 79 +0x01, 0xB9, 0x0B, 0x07, // 80 +0x01, 0xC4, 0x0E, 0x08, // 81 +0x01, 0xD2, 0x0C, 0x07, // 82 +0x01, 0xDE, 0x0C, 0x07, // 83 +0x01, 0xEA, 0x0B, 0x07, // 84 +0x01, 0xF5, 0x0C, 0x07, // 85 +0x02, 0x01, 0x0D, 0x08, // 86 +0x02, 0x0E, 0x11, 0x0A, // 87 +0x02, 0x1F, 0x0E, 0x08, // 88 +0x02, 0x2D, 0x0D, 0x08, // 89 +0x02, 0x3A, 0x0C, 0x07, // 90 +0x02, 0x46, 0x06, 0x04, // 91 +0x02, 0x4C, 0x06, 0x04, // 92 +0x02, 0x52, 0x04, 0x03, // 93 +0x02, 0x56, 0x09, 0x06, // 94 +0x02, 0x5F, 0x0C, 0x07, // 95 +0x02, 0x6B, 0x03, 0x03, // 96 +0x02, 0x6E, 0x0A, 0x06, // 97 +0x02, 0x78, 0x0A, 0x06, // 98 +0x02, 0x82, 0x0A, 0x06, // 99 +0x02, 0x8C, 0x0A, 0x06, // 100 +0x02, 0x96, 0x0A, 0x06, // 101 +0x02, 0xA0, 0x05, 0x04, // 102 +0x02, 0xA5, 0x0A, 0x06, // 103 +0x02, 0xAF, 0x0A, 0x06, // 104 +0x02, 0xB9, 0x04, 0x03, // 105 +0x02, 0xBD, 0x04, 0x03, // 106 +0x02, 0xC1, 0x08, 0x05, // 107 +0x02, 0xC9, 0x04, 0x03, // 108 +0x02, 0xCD, 0x10, 0x09, // 109 +0x02, 0xDD, 0x0A, 0x06, // 110 +0x02, 0xE7, 0x0A, 0x06, // 111 +0x02, 0xF1, 0x0A, 0x06, // 112 +0x02, 0xFB, 0x0A, 0x06, // 113 +0x03, 0x05, 0x05, 0x04, // 114 +0x03, 0x0A, 0x08, 0x05, // 115 +0x03, 0x12, 0x06, 0x04, // 116 +0x03, 0x18, 0x0A, 0x06, // 117 +0x03, 0x22, 0x09, 0x06, // 118 +0x03, 0x2B, 0x0E, 0x08, // 119 +0x03, 0x39, 0x0A, 0x06, // 120 +0x03, 0x43, 0x09, 0x06, // 121 +0x03, 0x4C, 0x0A, 0x06, // 122 +0x03, 0x56, 0x06, 0x04, // 123 +0x03, 0x5C, 0x04, 0x03, // 124 +0x03, 0x60, 0x05, 0x04, // 125 +0x03, 0x65, 0x09, 0x06, // 126 +0xFF, 0xFF, 0x00, 0x0A, // 127 +0xFF, 0xFF, 0x00, 0x0A, // 128 +0x03, 0x6E, 0x0C, 0x07, // 129 +0x03, 0x7A, 0x05, 0x04, // 130 +0x03, 0x7F, 0x0C, 0x07, // 131 +0x03, 0x8B, 0x0E, 0x08, // 132 +0x03, 0x99, 0x0C, 0x07, // 133 +0x03, 0xA5, 0x0C, 0x07, // 134 +0x03, 0xB1, 0x0A, 0x06, // 135 +0x03, 0xBB, 0x0A, 0x06, // 136 +0x03, 0xC5, 0x0A, 0x06, // 137 +0xFF, 0xFF, 0x00, 0x0A, // 138 +0xFF, 0xFF, 0x00, 0x0A, // 139 +0xFF, 0xFF, 0x00, 0x0A, // 140 +0xFF, 0xFF, 0x00, 0x0A, // 141 +0xFF, 0xFF, 0x00, 0x0A, // 142 +0xFF, 0xFF, 0x00, 0x0A, // 143 +0xFF, 0xFF, 0x00, 0x0A, // 144 +0xFF, 0xFF, 0x00, 0x0A, // 145 +0xFF, 0xFF, 0x00, 0x0A, // 146 +0x03, 0xCF, 0x0E, 0x08, // 147 +0x03, 0xDD, 0x0A, 0x06, // 148 +0xFF, 0xFF, 0x00, 0x0A, // 149 +0xFF, 0xFF, 0x00, 0x0A, // 150 +0xFF, 0xFF, 0x00, 0x0A, // 151 +0x03, 0xE7, 0x0C, 0x07, // 152 +0x03, 0xF3, 0x0C, 0x07, // 153 +0x03, 0xFF, 0x0C, 0x07, // 154 +0x04, 0x0B, 0x08, 0x05, // 155 +0xFF, 0xFF, 0x00, 0x0A, // 156 +0xFF, 0xFF, 0x00, 0x0A, // 157 +0xFF, 0xFF, 0x00, 0x0A, // 158 +0xFF, 0xFF, 0x00, 0x0A, // 159 +0xFF, 0xFF, 0x00, 0x0A, // 160 +0x04, 0x13, 0x04, 0x03, // 161 +0x04, 0x17, 0x0A, 0x06, // 162 +0x04, 0x21, 0x0C, 0x07, // 163 +0x04, 0x2D, 0x0A, 0x06, // 164 +0x04, 0x37, 0x0A, 0x06, // 165 +0x04, 0x41, 0x04, 0x03, // 166 +0x04, 0x45, 0x0A, 0x06, // 167 +0x04, 0x4F, 0x05, 0x04, // 168 +0x04, 0x54, 0x0D, 0x08, // 169 +0x04, 0x61, 0x07, 0x05, // 170 +0x04, 0x68, 0x0A, 0x06, // 171 +0x04, 0x72, 0x09, 0x06, // 172 +0x04, 0x7B, 0x03, 0x03, // 173 +0x04, 0x7E, 0x0D, 0x08, // 174 +0x04, 0x8B, 0x0B, 0x07, // 175 +0x04, 0x96, 0x07, 0x05, // 176 +0x04, 0x9D, 0x0A, 0x06, // 177 +0x04, 0xA7, 0x05, 0x04, // 178 +0x04, 0xAC, 0x05, 0x04, // 179 +0x04, 0xB1, 0x05, 0x04, // 180 +0x04, 0xB6, 0x0A, 0x06, // 181 +0x04, 0xC0, 0x09, 0x06, // 182 +0x04, 0xC9, 0x03, 0x03, // 183 +0x04, 0xCC, 0x06, 0x04, // 184 +0x04, 0xD2, 0x0C, 0x07, // 185 +0x04, 0xDE, 0x07, 0x05, // 186 +0x04, 0xE5, 0x0C, 0x07, // 187 +0x04, 0xF1, 0x0A, 0x06, // 188 +0x04, 0xFB, 0x10, 0x09, // 189 +0x05, 0x0B, 0x10, 0x09, // 190 +0x05, 0x1B, 0x0A, 0x06, // 191 +0x05, 0x25, 0x0E, 0x08, // 192 +0x05, 0x33, 0x0E, 0x08, // 193 +0x05, 0x41, 0x0E, 0x08, // 194 +0x05, 0x4F, 0x0E, 0x08, // 195 +0x05, 0x5D, 0x0E, 0x08, // 196 +0x05, 0x6B, 0x0E, 0x08, // 197 +0x05, 0x79, 0x12, 0x0A, // 198 +0x05, 0x8B, 0x0C, 0x07, // 199 +0x05, 0x97, 0x0C, 0x07, // 200 +0x05, 0xA3, 0x0C, 0x07, // 201 +0x05, 0xAF, 0x0C, 0x07, // 202 +0x05, 0xBB, 0x0C, 0x07, // 203 +0x05, 0xC7, 0x05, 0x04, // 204 +0x05, 0xCC, 0x04, 0x03, // 205 +0x05, 0xD0, 0x04, 0x03, // 206 +0x05, 0xD4, 0x05, 0x04, // 207 +0x05, 0xD9, 0x0B, 0x07, // 208 +0x05, 0xE4, 0x0C, 0x07, // 209 +0x05, 0xF0, 0x0E, 0x08, // 210 +0x05, 0xFE, 0x0E, 0x08, // 211 +0x06, 0x0C, 0x0E, 0x08, // 212 +0x06, 0x1A, 0x0E, 0x08, // 213 +0x06, 0x28, 0x0E, 0x08, // 214 +0x06, 0x36, 0x0A, 0x06, // 215 +0x06, 0x40, 0x0D, 0x08, // 216 +0x06, 0x4D, 0x0C, 0x07, // 217 +0x06, 0x59, 0x0C, 0x07, // 218 +0x06, 0x65, 0x0C, 0x07, // 219 +0x06, 0x71, 0x0C, 0x07, // 220 +0x06, 0x7D, 0x0D, 0x08, // 221 +0x06, 0x8A, 0x0B, 0x07, // 222 +0x06, 0x95, 0x0C, 0x07, // 223 +0x06, 0xA1, 0x0A, 0x06, // 224 +0x06, 0xAB, 0x0A, 0x06, // 225 +0x06, 0xB5, 0x0A, 0x06, // 226 +0x06, 0xBF, 0x0A, 0x06, // 227 +0x06, 0xC9, 0x0A, 0x06, // 228 +0x06, 0xD3, 0x0A, 0x06, // 229 +0x06, 0xDD, 0x10, 0x09, // 230 +0x06, 0xED, 0x0A, 0x06, // 231 +0x06, 0xF7, 0x0A, 0x06, // 232 +0x07, 0x01, 0x0A, 0x06, // 233 +0x07, 0x0B, 0x0A, 0x06, // 234 +0x07, 0x15, 0x0A, 0x06, // 235 +0x07, 0x1F, 0x05, 0x04, // 236 +0x07, 0x24, 0x04, 0x03, // 237 +0x07, 0x28, 0x05, 0x04, // 238 +0x07, 0x2D, 0x05, 0x04, // 239 +0x07, 0x32, 0x0A, 0x06, // 240 +0x07, 0x3C, 0x0A, 0x06, // 241 +0x07, 0x46, 0x0A, 0x06, // 242 +0x07, 0x50, 0x0A, 0x06, // 243 +0x07, 0x5A, 0x0A, 0x06, // 244 +0x07, 0x64, 0x0A, 0x06, // 245 +0x07, 0x6E, 0x0A, 0x06, // 246 +0x07, 0x78, 0x09, 0x06, // 247 +0x07, 0x81, 0x0A, 0x06, // 248 +0x07, 0x8B, 0x0A, 0x06, // 249 +0x07, 0x95, 0x0A, 0x06, // 250 +0x07, 0x9F, 0x0A, 0x06, // 251 +0x07, 0xA9, 0x0A, 0x06, // 252 +0x07, 0xB3, 0x09, 0x06, // 253 +0x07, 0xBC, 0x0A, 0x06, // 254 +0x07, 0xC6, 0x09, 0x06, // 255 +// Font Data: +0x00, 0x00, 0xF8, 0x02, // 33 +0x38, 0x00, 0x00, 0x00, 0x38, // 34 +0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 +0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 +0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 +0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 +0x38, // 39 +0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 +0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 +0x28, 0x00, 0x18, 0x00, 0x28, // 42 +0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 +0x00, 0x00, 0x00, 0x06, // 44 +0x80, 0x00, 0x80, // 45 +0x00, 0x00, 0x00, 0x02, // 46 +0x00, 0x03, 0xE0, 0x00, 0x18, // 47 +0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 +0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 +0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 +0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 +0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 +0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 +0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 +0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 +0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 +0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 +0x00, 0x00, 0x20, 0x02, // 58 +0x00, 0x00, 0x20, 0x06, // 59 +0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 +0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 +0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 +0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 +0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 +0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 +0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 +0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 +0x00, 0x00, 0xF8, 0x03, // 73 +0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 +0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 +0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 +0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 +0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 +0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 +0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 +0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 +0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 +0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 +0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 +0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 +0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 +0x08, 0x08, 0xF8, 0x0F, // 93 +0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 +0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 +0x08, 0x00, 0x10, // 96 +0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 +0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 +0x20, 0x00, 0xF0, 0x03, 0x28, // 102 +0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 +0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 +0x00, 0x00, 0xE8, 0x03, // 105 +0x00, 0x08, 0xE8, 0x07, // 106 +0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 +0x00, 0x00, 0xF8, 0x03, // 108 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 +0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 +0x00, 0x00, 0xE0, 0x03, 0x20, // 114 +0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 +0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 +0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 +0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 +0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 +0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 +0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 +0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 +0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 +0x00, 0x00, 0xF8, 0x0F, // 124 +0x08, 0x08, 0x78, 0x0F, 0x80, // 125 +0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 +0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129 +0x40, 0x00, 0xF8, 0x03, 0x20, // 130 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x05, 0x00, 0x0A, // 132 +0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x07, 0x00, 0x08, // 133 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136 +0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x06, 0x48, 0x0A, // 152 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x06, 0x00, 0x08, // 153 +0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154 +0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155 +0x00, 0x00, 0xA0, 0x0F, // 161 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 +0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 +0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 +0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 +0x00, 0x00, 0x38, 0x0F, // 166 +0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 +0x08, 0x00, 0x00, 0x00, 0x08, // 168 +0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 +0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 +0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 +0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 +0x80, 0x00, 0x80, // 173 +0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 +0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 +0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 +0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 +0x48, 0x00, 0x68, 0x00, 0x58, // 178 +0x48, 0x00, 0x58, 0x00, 0x68, // 179 +0x00, 0x00, 0x10, 0x00, 0x08, // 180 +0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 +0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 +0x00, 0x00, 0x40, // 183 +0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184 +0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185 +0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 +0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187 +0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 188 +0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 +0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 +0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193 +0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194 +0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195 +0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196 +0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197 +0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199 +0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201 +0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202 +0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203 +0x00, 0x00, 0xF9, 0x03, 0x02, // 204 +0x02, 0x00, 0xF9, 0x03, // 205 +0x01, 0x00, 0xFA, 0x03, // 206 +0x02, 0x00, 0xF8, 0x03, 0x02, // 207 +0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208 +0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212 +0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213 +0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214 +0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215 +0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216 +0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220 +0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221 +0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222 +0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223 +0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226 +0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228 +0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229 +0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231 +0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235 +0x00, 0x00, 0xE4, 0x03, 0x08, // 236 +0x08, 0x00, 0xE4, 0x03, // 237 +0x08, 0x00, 0xE4, 0x03, 0x08, // 238 +0x08, 0x00, 0xE0, 0x03, 0x08, // 239 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240 +0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241 +0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244 +0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246 +0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247 +0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248 +0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249 +0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250 +0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251 +0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252 +0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253 +0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254 +0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255 +}; \ No newline at end of file diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.h b/src/graphics/fonts/OLEDDisplayFontsPL.h new file mode 100644 index 000000000..59dd92c41 --- /dev/null +++ b/src/graphics/fonts/OLEDDisplayFontsPL.h @@ -0,0 +1,11 @@ +#ifndef OLEDDISPLAYFONTSPL_h +#define OLEDDISPLAYFONTSPL_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#define PROGMEM +#endif + +extern const uint8_t ArialMT_Plain_10_PL[] PROGMEM; +#endif \ No newline at end of file From d1e64c74dee780f2b3f4200a5aa8b92f6e258812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Cort=C3=AAs?= Date: Mon, 26 Aug 2024 17:15:42 +0100 Subject: [PATCH 231/305] Fix devcontainer Dockerfile build --- .devcontainer/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 82f4d91bf..c21564491 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -12,6 +12,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ libssl-dev \ libulfius-dev \ libyaml-cpp-dev \ + pipx \ pkg-config \ python3 \ python3-pip \ @@ -21,4 +22,4 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ zip \ && apt-get clean && rm -rf /var/lib/apt/lists/* -RUN pip3 install --no-cache-dir -U platformio==6.1.15 \ No newline at end of file +RUN pipx install platformio==6.1.15 \ No newline at end of file From f27281d3fa2dd4e5d51db22300230e1b04c68a7a Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 28 Aug 2024 06:43:30 -0500 Subject: [PATCH 232/305] Fix super tiny T1114 tft font size and fork repo to fix compiler warnings (#4573) --- src/graphics/Screen.cpp | 13 ++++++++----- src/graphics/ScreenFonts.h | 3 ++- src/graphics/images.h | 4 ++-- variants/heltec_mesh_node_t114/platformio.ini | 2 +- variants/heltec_vision_master_t190/platformio.ini | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3a4db6ee0..2690d0b70 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1093,7 +1093,8 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStat { char usersString[20]; snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x, y + 3, 8, 8, imgUser); #else @@ -2414,7 +2415,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 #ifdef ARCH_ESP32 if (millis() - storeForwardModule->lastHeartbeat > (storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL1); @@ -2425,7 +2427,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 imgQuestion); #endif } else { -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8, imgSFL1); @@ -2439,8 +2442,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 #endif } else { // TODO: Raspberry Pi supports more than just the one screen size -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \ - ARCH_PORTDUINO) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgInfoL1); diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 8a48d053e..41e739fa1 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -8,7 +8,8 @@ #include "graphics/fonts/OLEDDisplayFontsUA.h" #endif -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) // The screen is bigger so use bigger fonts #define FONT_SMALL ArialMT_Plain_16 // Height: 19 diff --git a/src/graphics/images.h b/src/graphics/images.h index d4c738610..ab3767a89 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -20,8 +20,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03 0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f}; #endif -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \ - ARCH_PORTDUINO) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff}; const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f}; diff --git a/variants/heltec_mesh_node_t114/platformio.ini b/variants/heltec_mesh_node_t114/platformio.ini index 99bdf77a7..c2a458f78 100644 --- a/variants/heltec_mesh_node_t114/platformio.ini +++ b/variants/heltec_mesh_node_t114/platformio.ini @@ -12,4 +12,4 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_node lib_deps = ${nrf52840_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/Bei-Ji-Quan/st7789#b8e7e076714b670764139289d3829b0beff67edb \ No newline at end of file + https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b \ No newline at end of file diff --git a/variants/heltec_vision_master_t190/platformio.ini b/variants/heltec_vision_master_t190/platformio.ini index 38a3169e8..fd0001439 100644 --- a/variants/heltec_vision_master_t190/platformio.ini +++ b/variants/heltec_vision_master_t190/platformio.ini @@ -9,5 +9,5 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/Bei-Ji-Quan/st7789#b8e7e076714b670764139289d3829b0beff67edb + https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b upload_speed = 921600 \ No newline at end of file From 5b3579af52bec586743f87922c047d739cb7562d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 28 Aug 2024 13:51:44 +0200 Subject: [PATCH 233/305] trunk upgrade (#4574) --- .trunk/trunk.yaml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 2d9f60899..b20a1ec95 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,36 +1,36 @@ version: 0.1 cli: - version: 1.22.2 + version: 1.22.3 plugins: sources: - id: trunk - ref: v1.5.0 + ref: v1.6.2 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.76.3 + - trufflehog@3.81.9 - yamllint@1.35.1 - - bandit@1.7.8 - - checkov@3.2.95 + - bandit@1.7.9 + - checkov@3.2.238 - terrascan@1.19.1 - - trivy@0.51.1 + - trivy@0.54.1 #- trufflehog@3.63.2-rc0 - - taplo@0.8.1 - - ruff@0.4.4 + - taplo@0.9.3 + - ruff@0.6.2 - isort@5.13.2 - - markdownlint@0.40.0 - - oxipng@9.1.1 + - markdownlint@0.41.0 + - oxipng@9.1.2 - svgo@3.3.2 - - actionlint@1.7.0 - - flake8@7.0.0 + - actionlint@1.7.1 + - flake8@7.1.1 - hadolint@2.12.0 - shfmt@3.6.0 - shellcheck@0.10.0 - - black@24.4.2 + - black@24.8.0 - git-diff-check - - gitleaks@8.18.2 + - gitleaks@8.18.4 - clang-format@16.0.3 - - prettier@3.2.5 + - prettier@3.3.3 ignore: - linters: [ALL] paths: From a34170654c083bce032c5ec9b7bd796d709ae085 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 28 Aug 2024 07:24:41 -0500 Subject: [PATCH 234/305] Fix T1000-E default to turn on buzzer for Ext. Notification (#4575) --- src/mesh/NodeDB.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 8409eaff6..004f27a0d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -395,6 +395,12 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.has_store_forward = true; moduleConfig.has_telemetry = true; moduleConfig.has_external_notification = true; +#if defined(PIN_BUZZER) + moduleConfig.external_notification.enabled = true; + moduleConfig.external_notification.output_buzzer = PIN_BUZZER; + moduleConfig.external_notification.alert_message_buzzer = true; + moduleConfig.external_notification.nag_timeout = 60; +#endif #if defined(RAK4630) || defined(RAK11310) // Default to RAK led pin 2 (blue) moduleConfig.external_notification.enabled = true; From 50f06840d756d3d34f68237956d47b5d13ed1688 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 28 Aug 2024 07:54:50 -0500 Subject: [PATCH 235/305] Add button secondary and enable scan-select on T190 (#4577) --- variants/heltec_vision_master_t190/variant.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/heltec_vision_master_t190/variant.h b/variants/heltec_vision_master_t190/variant.h index 726f6d864..1da3f9971 100644 --- a/variants/heltec_vision_master_t190/variant.h +++ b/variants/heltec_vision_master_t190/variant.h @@ -1,4 +1,6 @@ #define BUTTON_PIN 0 +#define BUTTON_PIN_SECONDARY 21 // Second built-in button +#define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input // I2C #define I2C_SDA SDA From 06175737ccb25ea69688023ffd02c05d4fdd8291 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 22 Aug 2024 20:57:03 -0500 Subject: [PATCH 236/305] Save nodedb after favoriting (or removing) (#4537) --- src/modules/AdminModule.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index d64aea5d8..ef60a4bf2 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -238,6 +238,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node); if (node != NULL) { node->is_favorite = true; + saveChanges(SEGMENT_DEVICESTATE, false); } break; } @@ -246,6 +247,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node); if (node != NULL) { node->is_favorite = false; + saveChanges(SEGMENT_DEVICESTATE, false); } break; } From 710fdbd4e530d244efc86841e2432afa6058da0e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 23 Aug 2024 06:25:40 -0500 Subject: [PATCH 237/305] Adds has_x bools to position packet. (#4540) --- src/modules/PositionModule.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index f534baf67..2a0c95a9b 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -187,16 +187,23 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.longitude_i = localPosition.longitude_i; } p.precision_bits = precision; + p.has_latitude_i = true; + p.has_longitude_i = true; p.time = getValidTime(RTCQualityNTP) > 0 ? getValidTime(RTCQualityNTP) : localPosition.time; if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) { - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) { p.altitude = localPosition.altitude; - else + p.has_altitude = true; + } else { p.altitude_hae = localPosition.altitude_hae; + p.has_altitude_hae = true; + } - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION) { p.altitude_geoidal_separation = localPosition.altitude_geoidal_separation; + p.has_altitude_geoidal_separation = true; + } } if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_DOP) { @@ -216,11 +223,15 @@ meshtastic_MeshPacket *PositionModule::allocReply() if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SEQ_NO) p.seq_number = localPosition.seq_number; - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING) { p.ground_track = localPosition.ground_track; + p.has_ground_track = true; + } - if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED) + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED) { p.ground_speed = localPosition.ground_speed; + p.has_ground_speed = true; + } // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS or NTP to include the time, so that devices @@ -471,4 +482,4 @@ void PositionModule::handleNewPosition() } } -#endif +#endif \ No newline at end of file From 9b2ef971c219e2c40336584d6592cf6f874a0311 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 23 Aug 2024 06:26:19 -0500 Subject: [PATCH 238/305] Fix copyPasta in NodeDB (#4538) --- src/mesh/NodeDB.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 1caaaf39b..34d3e4ce9 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -556,13 +556,8 @@ void NodeDB::cleanupMeshDB() for (int i = 0; i < numMeshNodes; i++) { if (meshNodes->at(i).has_user) { if (meshNodes->at(i).user.public_key.size > 0) { - for (int j = 0; j < numMeshNodes; j++) { - if (meshNodes->at(i).user.public_key.bytes[j] != 0) { - break; - } - if (j == 31) { - meshNodes->at(i).user.public_key.size = 0; - } + if (memfll(meshNodes->at(i).user.public_key.bytes, 0, meshNodes->at(i).user.public_key.size)) { + meshNodes->at(i).user.public_key.size = 0; } } meshNodes->at(newPos++) = meshNodes->at(i); From de41a054b0a399123c141d08e043fe8567a502c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Gjels=C3=B8?= <36234524+gjelsoe@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:28:23 +0200 Subject: [PATCH 239/305] Initial support for RadioMaster Bandit. (#4523) * Initial support for RadioMaster Bandit. * Different lighting can be made for Button 1 & 2 on the Bandit. Changes to AmbientLighting will turn off af shutdown(). * Trunk * Trunk again. --- platformio.ini | 1 + src/AmbientLightingThread.h | 59 ++++++++- src/mesh/RF95Interface.cpp | 26 +++- src/platform/esp32/architecture.h | 2 + .../radiomaster_900_bandit/platformio.ini | 14 ++ variants/radiomaster_900_bandit/variant.h | 121 ++++++++++++++++++ 6 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 variants/radiomaster_900_bandit/platformio.ini create mode 100644 variants/radiomaster_900_bandit/variant.h diff --git a/platformio.ini b/platformio.ini index 5ad7d60a2..4de1ec39f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,6 +34,7 @@ default_envs = tbeam ;default_envs = wio-e5 ;default_envs = radiomaster_900_bandit_nano ;default_envs = radiomaster_900_bandit_micro +;default_envs = radiomaster_900_bandit ;default_envs = heltec_capsule_sensor_v3 ;default_envs = heltec_vision_master_t190 ;default_envs = heltec_vision_master_e213 diff --git a/src/AmbientLightingThread.h b/src/AmbientLightingThread.h index 6b3360b1f..fdd4b53fa 100644 --- a/src/AmbientLightingThread.h +++ b/src/AmbientLightingThread.h @@ -1,3 +1,4 @@ +#include "Observer.h" #include "configuration.h" #ifdef HAS_NCP5623 @@ -22,10 +23,18 @@ class AmbientLightingThread : public concurrency::OSThread public: explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread") { + notifyDeepSleepObserver.observe(¬ifyDeepSleep); // Let us know when shutdown() is issued. + +// Enables Ambient Lighting by default if conditions are meet. +#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) +#ifdef ENABLE_AMBIENTLIGHTING + moduleConfig.ambient_lighting.led_state = true; +#endif +#endif // Uncomment to test module // moduleConfig.ambient_lighting.led_state = true; // moduleConfig.ambient_lighting.current = 10; - // // Default to a color based on our node number + // Default to a color based on our node number // moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16; // moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8; // moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF; @@ -82,9 +91,46 @@ class AmbientLightingThread : public concurrency::OSThread return disable(); } + // When shutdown() is issued, setLightingOff will be called. + CallbackObserver notifyDeepSleepObserver = + CallbackObserver(this, &AmbientLightingThread::setLightingOff); + private: ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE; + // Turn RGB lighting off, is used in junction to shutdown() + int setLightingOff(void *unused) + { +#ifdef HAS_NCP5623 + rgb.setCurrent(0); + rgb.setRed(0); + rgb.setGreen(0); + rgb.setBlue(0); + LOG_INFO("Turn Off NCP5623 Ambient lighting.\n"); +#endif +#ifdef HAS_NEOPIXEL + pixels.clear(); + pixels.show(); + LOG_INFO("Turn Off NeoPixel Ambient lighting.\n"); +#endif +#ifdef RGBLED_CA + analogWrite(RGBLED_RED, 255 - 0); + analogWrite(RGBLED_GREEN, 255 - 0); + analogWrite(RGBLED_BLUE, 255 - 0); + LOG_INFO("Turn Off Ambient lighting RGB Common Anode.\n"); +#elif defined(RGBLED_RED) + analogWrite(RGBLED_RED, 0); + analogWrite(RGBLED_GREEN, 0); + analogWrite(RGBLED_BLUE, 0); + LOG_INFO("Turn Off Ambient lighting RGB Common Cathode.\n"); +#endif +#ifdef UNPHONE + unphone.rgb(0, 0, 0); + LOG_INFO("Turn Off unPhone Ambient lighting.\n"); +#endif + return 0; + } + void setLighting() { #ifdef HAS_NCP5623 @@ -100,6 +146,17 @@ class AmbientLightingThread : public concurrency::OSThread pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue), 0, NEOPIXEL_COUNT); + +// RadioMaster Bandit has addressable LED at the two buttons +// this allow us to set different lighting for them in variant.h file. +#ifdef RADIOMASTER_900_BANDIT +#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX) + pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1); +#endif +#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX) + pixels.fill(BUTTON2_COLOR, BUTTON1_COLOR_INDEX, 1); +#endif +#endif pixels.show(); LOG_DEBUG("Initializing NeoPixel Ambient lighting w/ brightness(current)=%d, red=%d, green=%d, blue=%d\n", moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index b6083e7f9..25df3258f 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -16,7 +16,7 @@ // In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING // if you set power to something higher than 17 or 20 you might fry your board. -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) // Structure to hold DAC and DB values typedef struct { uint8_t dac; @@ -40,12 +40,23 @@ DACDB getDACandDB(uint8_t dbm) static const struct { uint8_t dbm; DACDB values; - } dbmToDACDB[] = { + } +#ifdef RADIOMASTER_900_BANDIT_NANO + dbmToDACDB[] = { {20, {168, 2}}, // 100mW {24, {148, 6}}, // 250mW {27, {128, 9}}, // 500mW {30, {90, 12}} // 1000mW }; +#endif +#ifdef RADIOMASTER_900_BANDIT + dbmToDACDB[] = { + {20, {165, 2}}, // 100mW + {24, {155, 6}}, // 250mW + {27, {142, 9}}, // 500mW + {30, {110, 10}} // 1000mW + }; +#endif const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]); // Find the interval dbm falls within and interpolate @@ -56,7 +67,12 @@ DACDB getDACandDB(uint8_t dbm) } // Return a default value if no match is found and default to 100mW +#ifdef RADIOMASTER_900_BANDIT_NANO DACDB defaultValue = {168, 2}; +#endif +#ifdef RADIOMASTER_900_BANDIT + DACDB defaultValue = {165, 2}; +#endif return defaultValue; } #endif @@ -95,7 +111,7 @@ bool RF95Interface::init() { RadioLibInterface::init(); -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) // DAC and DB values based on dBm using interpolation DACDB dacDbValues = getDACandDB(power); int8_t powerDAC = dacDbValues.dac; @@ -117,7 +133,7 @@ bool RF95Interface::init() // enable PA #ifdef RF95_PA_EN #if defined(RF95_PA_DAC_EN) -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) // Use calculated DAC value dacWrite(RF95_PA_EN, powerDAC); #else @@ -163,7 +179,7 @@ bool RF95Interface::init() LOG_INFO("Frequency set to %f\n", getFreq()); LOG_INFO("Bandwidth set to %f\n", bw); LOG_INFO("Power output set to %d\n", power); -#ifdef RADIOMASTER_900_BANDIT_NANO +#if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) LOG_INFO("DAC output set to %d\n", powerDAC); #endif diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index b6def5b01..3761235a0 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -152,6 +152,8 @@ #define HW_VENDOR meshtastic_HardwareModel_WIPHONE #elif defined(RADIOMASTER_900_BANDIT_NANO) #define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO +#elif defined(RADIOMASTER_900_BANDIT) +#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT #elif defined(HELTEC_CAPSULE_SENSOR_V3) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 #elif defined(HELTEC_VISION_MASTER_T190) diff --git a/variants/radiomaster_900_bandit/platformio.ini b/variants/radiomaster_900_bandit/platformio.ini new file mode 100644 index 000000000..4ff8a6ea2 --- /dev/null +++ b/variants/radiomaster_900_bandit/platformio.ini @@ -0,0 +1,14 @@ +[env:radiomaster_900_bandit] +extends = esp32_base +board = esp32doit-devkit-v1 +build_flags = + ${esp32_base.build_flags} + -DRADIOMASTER_900_BANDIT + -DVTABLES_IN_FLASH=1 + -DCONFIG_DISABLE_HAL_LOCKS=1 + -O2 + -Ivariants/radiomaster_900_bandit +board_build.f_cpu = 240000000L +upload_protocol = esptool +lib_deps = + ${esp32_base.lib_deps} \ No newline at end of file diff --git a/variants/radiomaster_900_bandit/variant.h b/variants/radiomaster_900_bandit/variant.h new file mode 100644 index 000000000..0499970f5 --- /dev/null +++ b/variants/radiomaster_900_bandit/variant.h @@ -0,0 +1,121 @@ +/* + Initial settings and work by https://github.com/gjelsoe + Unit provided by Radio Master RC + https://radiomasterrc.com/products/bandit-expresslrs-rf-module with 1.29" OLED display CH1115 driver +*/ + +/* + On this model then screen is NOT upside down, don't flip it for the user. +*/ +#undef DISPLAY_FLIP_SCREEN + +/* + I2C SDA and SCL. + 0x18 - STK8XXX Accelerometer, Not supported yet. + 0x3C - SH1115 Display Driver +*/ +#define I2C_SDA 14 +#define I2C_SCL 12 + +/* + No GPS - but free pins are available. +*/ +#define HAS_GPS 0 +#undef GPS_RX_PIN +#undef GPS_TX_PIN + +/* + Pin connections from ESP32-D0WDQ6 to SX1276. +*/ +#define LORA_DIO0 22 +#define LORA_DIO1 21 +#define LORA_SCK 18 +#define LORA_MISO 19 +#define LORA_MOSI 23 +#define LORA_CS 4 +#define LORA_RESET 5 +#define LORA_TXEN 33 + +/* + This unit has a FAN built-in. + FAN is active at 250mW on it's ExpressLRS Firmware. + This FAN has TACHO signal on Pin 27 for use with PWM. +*/ +#define RF95_FAN_EN 2 + +/* + LED PIN setup and it has a NeoPixel LED. + It's possible to setup colors for Button 1 and 2, + look at BUTTON1_COLOR, BUTTON1_COLOR_INDEX, BUTTON2_COLOR and BUTTON2_COLOR_INDEX + this is done here for now. +*/ +#define HAS_NEOPIXEL // Enable the use of neopixels +#define NEOPIXEL_COUNT 6 // How many neopixels are connected +#define NEOPIXEL_DATA 15 // GPIO pin used to send data to the neopixels +#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // Type of neopixels in use +#define ENABLE_AMBIENTLIGHTING // Turn on Ambient Lighting +// #define BUTTON1_COLOR 0xFF0000 // Background light for Button 1 in HEX RGB Color (RadioMaster Bandit only). +// #define BUTTON1_COLOR_INDEX 0 // NeoPixel Index ID for Button 1 +// #define BUTTON2_COLOR 0x0000FF // Background light for Button 2 in HEX RGB Color (RadioMaster Bandit only). +// #define BUTTON2_COLOR_INDEX 1 // NeoPixel Index ID for Button 2 + +/* + It has 1 x five-way and 2 x normal buttons. + + Button GPIO RGB Index + --------------------------- + Five-way 39 - + Button 1 34 0 + Button 2 35 1 + + Five way button when using ADC. + 2.632V, 2.177V, 1.598V, 1.055V, 0V + + ADC Values: + { UP, DOWN, LEFT, RIGHT, ENTER, IDLE } + 3227, 0 ,1961, 2668, 1290, 4095 + + Five way button when using ADC. + https://github.com/ExpressLRS/targets/blob/f3215b5ec891108db1a13523e4163950cfcadaac/TX/Radiomaster%20Bandit.json#L41 + +*/ +#define INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE +#define PIN_JOYSTICK 39 +#define JOYSTICK_ADC_VALS /*UP*/ 3227, /*DOWN*/ 0, /*LEFT*/ 1961, /*RIGHT*/ 2668, /*OK*/ 1290, /*IDLE*/ 4095 + +/* + Normal Button Pin setup. +*/ +#define BUTTON_PIN 34 +#define BUTTON_NEED_PULLUP + +/* + No External notification. +*/ +#undef EXT_NOTIFY_OUT + +/* + Remapping PIN Names. + Note, that this unit uses RFO +*/ +#define USE_RF95 +#define USE_RF95_RFO +#define RF95_CS LORA_CS +#define RF95_DIO1 LORA_DIO1 +#define RF95_TXEN LORA_TXEN +#define RF95_RESET LORA_RESET +#define RF95_MAX_POWER 10 + +/* + This module has Skyworks SKY66122 controlled by dacWrite + power ranging from 100mW to 1000mW. + + Mapping of PA_LEVEL to Power output: GPIO26/dacWrite + 168 -> 100mW + 155 -> 250mW + 142 -> 500mW + 110 -> 1000mW +*/ +#define RF95_PA_EN 26 +#define RF95_PA_DAC_EN +#define RF95_PA_LEVEL 110 \ No newline at end of file From fe9a80a4e0a9b88fd04229c2b4e18e1cf8033080 Mon Sep 17 00:00:00 2001 From: Ian McEwen Date: Fri, 23 Aug 2024 05:03:29 -0700 Subject: [PATCH 240/305] Use the '+' wildcard for MQTT rather than '#', to subscribe only to topics one nesting level deep (#4528) --- src/mqtt/MQTT.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 22f68bac8..2f7e82e3d 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -376,12 +376,12 @@ void MQTT::sendSubscriptions() const auto &ch = channels.getByIndex(i); if (ch.settings.downlink_enabled) { hasDownlink = true; - std::string topic = cryptTopic + channels.getGlobalId(i) + "/#"; + std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 if (moduleConfig.mqtt.json_enabled == true) { - std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/#"; + std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? } @@ -390,7 +390,7 @@ void MQTT::sendSubscriptions() } #if !MESHTASTIC_EXCLUDE_PKI if (hasDownlink) { - std::string topic = cryptTopic + "PKI/#"; + std::string topic = cryptTopic + "PKI/+"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); } @@ -674,4 +674,4 @@ bool MQTT::isValidJsonEnvelope(JSONObject &json) (json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type (json.find("payload") != json.end()); // should have a payload -} \ No newline at end of file +} From 9de0b7cfac56a0e8ceea13e48112d8ddd88a8555 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 23 Aug 2024 07:04:34 -0500 Subject: [PATCH 241/305] Found more places to set explicit has_optional on position (#4542) --- src/mesh/TypeConversions.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index d8ee6afc7..bcff0b817 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -16,8 +16,14 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo if (lite->has_position) { info.has_position = true; + if (lite->position.latitude_i > 0) + info.position.has_latitude_i = true; info.position.latitude_i = lite->position.latitude_i; + if (lite->position.longitude_i > 0) + info.position.has_longitude_i = true; info.position.longitude_i = lite->position.longitude_i; + if (lite->position.altitude > 0) + info.position.has_altitude = true; info.position.altitude = lite->position.altitude; info.position.location_source = lite->position.location_source; info.position.time = lite->position.time; @@ -48,8 +54,14 @@ meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Positi meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite) { meshtastic_Position position = meshtastic_Position_init_default; + if (lite.latitude_i > 0) + position.has_latitude_i = true; position.latitude_i = lite.latitude_i; + if (lite.longitude_i > 0) + position.has_longitude_i = true; position.longitude_i = lite.longitude_i; + if (lite.altitude > 0) + position.has_altitude = true; position.altitude = lite.altitude; position.location_source = lite.location_source; position.time = lite.time; From b285aa5bd608e57e5cbd8398402805eca8c27dd0 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 23 Aug 2024 07:07:28 -0500 Subject: [PATCH 242/305] Dum dum zero comparision --- src/mesh/TypeConversions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index bcff0b817..513728ca5 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -16,13 +16,13 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo if (lite->has_position) { info.has_position = true; - if (lite->position.latitude_i > 0) + if (lite->position.latitude_i != 0) info.position.has_latitude_i = true; info.position.latitude_i = lite->position.latitude_i; - if (lite->position.longitude_i > 0) + if (lite->position.longitude_i != 0) info.position.has_longitude_i = true; info.position.longitude_i = lite->position.longitude_i; - if (lite->position.altitude > 0) + if (lite->position.altitude != 0) info.position.has_altitude = true; info.position.altitude = lite->position.altitude; info.position.location_source = lite->position.location_source; @@ -54,13 +54,13 @@ meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Positi meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite) { meshtastic_Position position = meshtastic_Position_init_default; - if (lite.latitude_i > 0) + if (lite.latitude_i != 0) position.has_latitude_i = true; position.latitude_i = lite.latitude_i; - if (lite.longitude_i > 0) + if (lite.longitude_i != 0) position.has_longitude_i = true; position.longitude_i = lite.longitude_i; - if (lite.altitude > 0) + if (lite.altitude != 0) position.has_altitude = true; position.altitude = lite.altitude; position.location_source = lite.location_source; From 5ce5b7b08bfadb9312e2e9f95d10b54d6dff7b4c Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 23 Aug 2024 08:03:16 -0700 Subject: [PATCH 243/305] Older variant.h files (IMO sloppily) don't define VEXT_ON_VALUE But in an attempt to avoid updating lots of files, make it default to LOW --- src/configuration.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/configuration.h b/src/configuration.h index 2e0efffd4..047dbd727 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -177,6 +177,11 @@ along with this program. If not, see . /* Step #1: offer chance for variant-specific defines */ #include "variant.h" +#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE) +// Older variant.h files might not be defining this value, so stay with the old default +#define VEXT_ON_VALUE LOW +#endif + #ifndef GPS_BAUDRATE #define GPS_BAUDRATE 9600 #endif From cdafa87cefac890ae2398885cc9052b6aaa0e4d7 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 28 Aug 2024 09:51:00 -0700 Subject: [PATCH 244/305] add lateInitVariant() as a concept. see below for docs (from src/extra_variants/README.md) This directory tree is designed to solve two problems. - The ESP32 arduino/platformio project doesn't support the nice "if initVariant() is found, call that after init" behavior of the nrf52 builds (they use initVariant() internally). - Over the years a lot of 'board specific' init code has been added to init() in main.cpp. It would be great to have a general/clean mechanism to allow developers to specify board specific/unique code in a clean fashion without mucking in main. So we are borrowing the initVariant() ideas here (by using weak gcc references). You can now define lateInitVariant() if your board needs it. If you'd like a board specific variant to be run, add the variant.cpp file to an appropriately named subdirectory and check for \_VARIANT_boardname in the cpp file (so that your code is only built for your board). You'll need to define \_VARIANT_boardname in your corresponding variant.h file. See existing boards for examples. This approach has no added runtime cost. --- src/main.cpp | 7 ++++ src/platform/extra_variants/README.md | 15 ++++++++ .../heltec_wireless_tracker/variant.cpp | 34 +++++++++++++++++++ variants/heltec_wireless_tracker/variant.h | 4 ++- 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/platform/extra_variants/README.md create mode 100644 src/platform/extra_variants/heltec_wireless_tracker/variant.cpp diff --git a/src/main.cpp b/src/main.cpp index 7520667dd..8f64960bb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -224,6 +224,11 @@ __attribute__((weak, noinline)) bool loopCanSleep() return true; } +// Weak empty variant initialization function. +// May be redefined by variant files. +void lateInitVariant() __attribute__((weak)); +void lateInitVariant() {} + /** * Print info as a structured log message (for automated log processing) */ @@ -1003,6 +1008,8 @@ void setup() } } + lateInitVariant(); // Do board specific init (see extra_variants/README.md for documentation) + #if !MESHTASTIC_EXCLUDE_MQTT mqttInit(); #endif diff --git a/src/platform/extra_variants/README.md b/src/platform/extra_variants/README.md new file mode 100644 index 000000000..e558502f0 --- /dev/null +++ b/src/platform/extra_variants/README.md @@ -0,0 +1,15 @@ +# About extra_variants + +This directory tree is designed to solve two problems. + +- The ESP32 arduino/platformio project doesn't support the nice "if initVariant() is found, call that after init" behavior of the nrf52 builds (they use initVariant() internally). +- Over the years a lot of 'board specific' init code has been added to init() in main.cpp. It would be great to have a general/clean mechanism to allow developers to specify board specific/unique code in a clean fashion without mucking in main. + +So we are borrowing the initVariant() ideas here (by using weak gcc references). You can now define lateInitVariant() if your board needs it. + +If you'd like a board specific variant to be run, add the variant.cpp file to an appropriately named +subdirectory and check for \_VARIANT_boardname in the cpp file (so that your code is only built for your board). +You'll need to define \_VARIANT_boardname in your corresponding variant.h file. +See existing boards for examples. + +This approach has no added runtime cost. diff --git a/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp b/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp new file mode 100644 index 000000000..84264ef58 --- /dev/null +++ b/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp @@ -0,0 +1,34 @@ +#include "configuration.h" + +#ifdef _VARIANT_HELTEC_WIRELESS_TRACKER + +#include "GPS.h" +#include "GpioLogic.h" +#include "graphics/TFTDisplay.h" + +// Heltec tracker specific init +void lateInitVariant() +{ + // LOG_DEBUG("Heltec tracker initVariant\n"); +#ifdef VEXT_ENABLE + GpioPin *hwEnable = new GpioHwPin(VEXT_ENABLE); + GpioVirtPin *virtGpsEnable = gps ? gps->enablePin : new GpioVirtPin(); + + // On this board we are actually using the backlightEnable signal to already be controlling a physical enable to the + // display controller. But we'd _ALSO_ like to have that signal drive a virtual GPIO. So nest it as needed. + GpioVirtPin *virtScreenEnable = new GpioVirtPin(); + if (TFTDisplay::backlightEnable) { + GpioPin *physScreenEnable = TFTDisplay::backlightEnable; + GpioPin *splitter = new GpioSplitter(virtScreenEnable, physScreenEnable); + TFTDisplay::backlightEnable = splitter; + + // Assume screen is initially powered + splitter->set(true); + } + + // If either the GPS or the screen is on, turn on the external power regulator + new GpioBinaryTransformer(virtGpsEnable, virtScreenEnable, hwEnable, GpioBinaryTransformer::Or); +#endif +} + +#endif \ No newline at end of file diff --git a/variants/heltec_wireless_tracker/variant.h b/variants/heltec_wireless_tracker/variant.h index 46e922eb5..79fa0e801 100644 --- a/variants/heltec_wireless_tracker/variant.h +++ b/variants/heltec_wireless_tracker/variant.h @@ -1,5 +1,6 @@ #define LED_PIN 18 +#define _VARIANT_HELTEC_WIRELESS_TRACKER #define HELTEC_TRACKER_V1_X // I2C @@ -31,7 +32,8 @@ // GPS UC6580: GPS V_DET(8), VDD_IO(7), DCDC_IN(21), pulls up RESETN(17), D_SEL(33) and BOOT_MODE(34) through 10kR // GPS LNA SW7125DE: VCC(4), pulls up SHDN(5) through 10kR // LED: VDD, LEDA (through diode) -#define VEXT_ENABLE 3 // active HIGH - powers the GPS, GPS LNA and OLED VDD/anode + +#define VEXT_ENABLE 3 // active HIGH - powers the GPS, GPS LNA and OLED #define VEXT_ON_VALUE HIGH #define BUTTON_PIN 0 From 8a9cc727a8d73b9b9e0b1754d483954151abcb40 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 28 Aug 2024 09:59:42 -0700 Subject: [PATCH 245/305] for #4154 use a binary gpio transformer to manage vext on heltec-tracker (saves power) --- src/GpioLogic.cpp | 16 +++++++++++++++- src/GpioLogic.h | 30 +++++++++++++++++++++++------- src/gps/GPS.cpp | 11 +++++------ src/gps/GPS.h | 2 +- src/graphics/TFTDisplay.cpp | 4 ++++ src/graphics/TFTDisplay.h | 4 +++- 6 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/GpioLogic.cpp b/src/GpioLogic.cpp index c23c40a7f..cbe26fc60 100644 --- a/src/GpioLogic.cpp +++ b/src/GpioLogic.cpp @@ -12,6 +12,7 @@ void GpioVirtPin::set(bool value) void GpioHwPin::set(bool value) { + // if (num == 3) LOG_DEBUG("Setting pin %d to %d\n", num, value); pinMode(num, OUTPUT); digitalWrite(num, value); } @@ -23,7 +24,7 @@ void GpioTransformer::set(bool value) outPin->set(value); } -GpioNotTransformer::GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin) +GpioUnaryTransformer::GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin) { assert(!inPin->dependentPin); // We only allow one dependent pin inPin->dependentPin = this; @@ -33,6 +34,18 @@ GpioNotTransformer::GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : Gp // update(); } +/** + * Update the output pin based on the current state of the input pin. + */ +void GpioUnaryTransformer::update() +{ + auto p = inPin->get(); + if (p == GpioVirtPin::PinState::Unset) + return; // Not yet fully initialized + + set(p); +} + /** * Update the output pin based on the current state of the input pin. */ @@ -75,6 +88,7 @@ void GpioBinaryTransformer::update() newValue = (GpioVirtPin::PinState)(p1 && p2); break; case Or: + // LOG_DEBUG("Doing GPIO OR\n"); newValue = (GpioVirtPin::PinState)(p1 || p2); break; case Xor: diff --git a/src/GpioLogic.h b/src/GpioLogic.h index c5a3cefa9..947d49625 100644 --- a/src/GpioLogic.h +++ b/src/GpioLogic.h @@ -42,7 +42,7 @@ class GpioBinaryTransformer; class GpioVirtPin : public GpioPin { friend class GpioBinaryTransformer; - friend class GpioNotTransformer; + friend class GpioUnaryTransformer; public: enum PinState { On = true, Off = false, Unset = 2 }; @@ -79,12 +79,31 @@ class GpioTransformer }; /** - * A transformer that performs a unary NOT operation from an input. + * A transformer that just drives a hw pin based on a virtual pin. */ -class GpioNotTransformer : public GpioTransformer +class GpioUnaryTransformer : public GpioTransformer { public: - GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin); + GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin); + + protected: + friend class GpioVirtPin; + + /** + * Update the output pin based on the current state of the input pin. + */ + virtual void update(); + + GpioVirtPin *inPin; +}; + +/** + * A transformer that performs a unary NOT operation from an input. + */ +class GpioNotTransformer : public GpioUnaryTransformer +{ + public: + GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioUnaryTransformer(inPin, outPin) {} protected: friend class GpioVirtPin; @@ -93,9 +112,6 @@ class GpioNotTransformer : public GpioTransformer * Update the output pin based on the current state of the input pin. */ void update(); - - private: - GpioVirtPin *inPin; }; /** diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index f7db1367a..12ef34c52 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1415,19 +1415,18 @@ GPS *GPS::createGps() new_gps->rx_gpio = _rx_gpio; new_gps->tx_gpio = _tx_gpio; + GpioVirtPin *virtPin = new GpioVirtPin(); + new_gps->enablePin = virtPin; // Always at least populate a virtual pin if (_en_gpio) { GpioPin *p = new GpioHwPin(_en_gpio); if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware - auto virtPin = new GpioVirtPin(); new GpioNotTransformer( virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio - p = virtPin; + } else { + new GpioUnaryTransformer( + virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio } - new_gps->enablePin = p; - } else { - // Just use a simulated pin - new_gps->enablePin = new GpioVirtPin(); } #ifdef PIN_GPS_PPS diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 494bddae8..befa4eef0 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -157,7 +157,7 @@ class GPS : private concurrency::OSThread * * Normally set by GPS::createGPS() */ - GpioPin *enablePin; + GpioVirtPin *enablePin; GPS() : concurrency::OSThread("GPS") {} diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index f6bd6513a..1dc4569db 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -25,6 +25,8 @@ extern SX1509 gpioExtender; #define TFT_INVERT true #endif +GpioPin *TFTDisplay::backlightEnable; + class LGFX : public lgfx::LGFX_Device { lgfx::Panel_ST7735S _panel_instance; @@ -584,6 +586,7 @@ void TFTDisplay::sendCommand(uint8_t com) // handle display on/off directly switch (com) { case DISPLAYON: { + // LOG_DEBUG("Display on\n"); backlightEnable->set(true); #if ARCH_PORTDUINO display(true); @@ -607,6 +610,7 @@ void TFTDisplay::sendCommand(uint8_t com) break; } case DISPLAYOFF: { + // LOG_DEBUG("Display off\n"); backlightEnable->set(false); #if ARCH_PORTDUINO tft->clear(); diff --git a/src/graphics/TFTDisplay.h b/src/graphics/TFTDisplay.h index 595984fbc..38cd53ebb 100644 --- a/src/graphics/TFTDisplay.h +++ b/src/graphics/TFTDisplay.h @@ -43,8 +43,10 @@ class TFTDisplay : public OLEDDisplay /** * This is normally managed entirely by TFTDisplay, but some rare applications (heltec tracker) might need to replace the * default GPIO behavior with something a bit more complex. + * + * We (cruftily) make it static so that variant.cpp can access it without needing a ptr to the TFTDisplay instance. */ - GpioPin *backlightEnable; + static GpioPin *backlightEnable; protected: // the header size of the buffer used, e.g. for the SPI command header From 9631a1be3875169a7e9b802e4d06e1c881ef6783 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 23 Aug 2024 18:18:36 -0700 Subject: [PATCH 246/305] remove deprecated serial/bt logging options and unify in the new (#4516) security option. Per discussion in https://github.com/meshtastic/firmware/issues/4375 no need to preserve the old options when changing to this new simpler single boolean because they were newish, rarely used and only for 'advanced' developers. --- protobufs | 2 +- src/RedirectablePrint.cpp | 2 +- src/mesh/NodeDB.cpp | 1 - src/mesh/generated/meshtastic/config.pb.h | 37 ++++++------------- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 6 files changed, 15 insertions(+), 31 deletions(-) diff --git a/protobufs b/protobufs index 56a435507..183ba970a 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 56a4355070f3371213d48f3a8cac1ddaf0d553fe +Subproject commit 183ba970a7a71de7a81541b74c413543523417d2 diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 96cf851e4..6eb6f8319 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -213,7 +213,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) { #if !MESHTASTIC_EXCLUDE_BLUETOOTH - if (config.security.bluetooth_logging_enabled && !pauseBluetoothLogging) { + if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) { bool isBleConnected = false; #ifdef ARCH_ESP32 isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected(); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 34d3e4ce9..0504cc273 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -127,7 +127,6 @@ NodeDB::NodeDB() if (!config.has_security) { config.has_security = true; config.security.serial_enabled = config.device.serial_enabled; - config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; config.security.is_managed = config.device.is_managed; } #if !(MESHTASTIC_EXCLUDE_PKI) diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 2f4c00fb0..d79654856 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -284,10 +284,6 @@ typedef struct _meshtastic_Config_DeviceConfig { /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI Moved to SecurityConfig */ bool serial_enabled; - /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Set this to true to leave the debug log outputting even when API is active. - Moved to SecurityConfig */ - bool debug_log_enabled; /* For boards without a hard wired button, this is the pin number that will be used Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */ uint32_t button_gpio; @@ -523,9 +519,6 @@ typedef struct _meshtastic_Config_BluetoothConfig { meshtastic_Config_BluetoothConfig_PairingMode mode; /* Specified PIN for PairingMode.FixedPin */ uint32_t fixed_pin; - /* Enables device (serial style logs) over Bluetooth - Moved to SecurityConfig */ - bool device_logging_enabled; } meshtastic_Config_BluetoothConfig; typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_public_key_t; @@ -546,10 +539,8 @@ typedef struct _meshtastic_Config_SecurityConfig { /* Serial Console over the Stream API." */ bool serial_enabled; /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Output live debug logging over serial. */ + Output live debug logging over serial or bluetooth is set to true. */ bool debug_log_api_enabled; - /* Enables device (serial style logs) over Bluetooth */ - bool bluetooth_logging_enabled; /* Allow incoming device control over the insecure legacy admin channel. */ bool admin_channel_enabled; } meshtastic_Config_SecurityConfig; @@ -658,32 +649,31 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} -#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} +#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} #define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0} #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} -#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} -#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} +#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_default {0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} -#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} +#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} #define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0} #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} -#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} -#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} +#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Config_DeviceConfig_role_tag 1 #define meshtastic_Config_DeviceConfig_serial_enabled_tag 2 -#define meshtastic_Config_DeviceConfig_debug_log_enabled_tag 3 #define meshtastic_Config_DeviceConfig_button_gpio_tag 4 #define meshtastic_Config_DeviceConfig_buzzer_gpio_tag 5 #define meshtastic_Config_DeviceConfig_rebroadcast_mode_tag 6 @@ -758,14 +748,12 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_enabled_tag 1 #define meshtastic_Config_BluetoothConfig_mode_tag 2 #define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3 -#define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4 #define meshtastic_Config_SecurityConfig_public_key_tag 1 #define meshtastic_Config_SecurityConfig_private_key_tag 2 #define meshtastic_Config_SecurityConfig_admin_key_tag 3 #define meshtastic_Config_SecurityConfig_is_managed_tag 4 #define meshtastic_Config_SecurityConfig_serial_enabled_tag 5 #define meshtastic_Config_SecurityConfig_debug_log_api_enabled_tag 6 -#define meshtastic_Config_SecurityConfig_bluetooth_logging_enabled_tag 7 #define meshtastic_Config_SecurityConfig_admin_channel_enabled_tag 8 #define meshtastic_Config_device_tag 1 #define meshtastic_Config_position_tag 2 @@ -803,7 +791,6 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.s #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ X(a, STATIC, SINGULAR, BOOL, serial_enabled, 2) \ -X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 3) \ X(a, STATIC, SINGULAR, UINT32, button_gpio, 4) \ X(a, STATIC, SINGULAR, UINT32, buzzer_gpio, 5) \ X(a, STATIC, SINGULAR, UENUM, rebroadcast_mode, 6) \ @@ -906,8 +893,7 @@ X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104) #define meshtastic_Config_BluetoothConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \ X(a, STATIC, SINGULAR, UENUM, mode, 2) \ -X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) \ -X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4) +X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) #define meshtastic_Config_BluetoothConfig_CALLBACK NULL #define meshtastic_Config_BluetoothConfig_DEFAULT NULL @@ -918,7 +904,6 @@ X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ -X(a, STATIC, SINGULAR, BOOL, bluetooth_logging_enabled, 7) \ X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8) #define meshtastic_Config_SecurityConfig_CALLBACK NULL #define meshtastic_Config_SecurityConfig_DEFAULT NULL @@ -955,15 +940,15 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size -#define meshtastic_Config_BluetoothConfig_size 12 -#define meshtastic_Config_DeviceConfig_size 100 +#define meshtastic_Config_BluetoothConfig_size 10 +#define meshtastic_Config_DeviceConfig_size 98 #define meshtastic_Config_DisplayConfig_size 30 #define meshtastic_Config_LoRaConfig_size 82 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 -#define meshtastic_Config_SecurityConfig_size 112 +#define meshtastic_Config_SecurityConfig_size 110 #define meshtastic_Config_SessionkeyConfig_size 0 #define meshtastic_Config_size 199 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 343e5f48a..976e0e135 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -358,7 +358,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 183 -#define meshtastic_OEMStore_size 3502 +#define meshtastic_OEMStore_size 3496 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 96 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index c612b24ab..38529fc14 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -187,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 669 +#define meshtastic_LocalConfig_size 663 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus From eddb72705f4b540970dbc141539b1e97edf0f816 Mon Sep 17 00:00:00 2001 From: Nestpebble <116762865+Nestpebble@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:24:23 +0100 Subject: [PATCH 247/305] add a .yml to setup a Gitpod instance quickly (#4551) * Create .gitpod.yml * Update .gitpod.yml --- .gitpod.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitpod.yml diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..0af235a3a --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,2 @@ +tasks: + - init: pip install platformio && pip install --upgrade pip From 17b2a83b44fdf5e4788ec16d8de1aee389a3cf08 Mon Sep 17 00:00:00 2001 From: John Hollowell Date: Fri, 23 Aug 2024 21:25:16 -0400 Subject: [PATCH 248/305] Add devcontainer (#4491) devcontainers can be used by IDEs/editors like VS Code to create a standardized development environment in a container --- .devcontainer/Dockerfile | 24 ++++++++++++++++++++++++ .devcontainer/devcontainer.json | 28 ++++++++++++++++++++++++++++ .devcontainer/setup.sh | 3 +++ 3 files changed, 55 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/setup.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..82f4d91bf --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12 + +# [Optional] Uncomment this section to install additional packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends \ + ca-certificates \ + g++ \ + git \ + libbluetooth-dev \ + libgpiod-dev \ + liborcania-dev \ + libssl-dev \ + libulfius-dev \ + libyaml-cpp-dev \ + pkg-config \ + python3 \ + python3-pip \ + python3-venv \ + python3-wheel \ + wget \ + zip \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN pip3 install --no-cache-dir -U platformio==6.1.15 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..e45fd5d93 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +{ + "name": "Meshtastic Firmware Dev", + "build": { + "dockerfile": "Dockerfile" + }, + "features": { + "ghcr.io/devcontainers/features/python:1": { + "installTools": true, + "version": "latest" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "platformio.platformio-ide", + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ 4403 ], + + // Run commands to prepare the container for use + "postCreateCommand": ".devcontainer/setup.sh", +} diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 000000000..866a4a417 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +git submodule update --init \ No newline at end of file From 059d5582d15c5922e437ffd0a6a772fc50fbfdf8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:44:14 -0500 Subject: [PATCH 249/305] [create-pull-request] automated change (#4544) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 183ba970a..52cfa2c1c 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 183ba970a7a71de7a81541b74c413543523417d2 +Subproject commit 52cfa2c1c2cd5a1a714e1338d551d967f674fca8 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index f32f865db..d612d74be 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -189,6 +189,13 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74, /* Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. */ meshtastic_HardwareModel_ME25LS01_4Y10TD = 75, + /* RP2040_FEATHER_RFM95 + Adafruit Feather RP2040 with RFM95 LoRa Radio RFM95 with SX1272, SSD1306 OLED + https://www.adafruit.com/product/5714 + https://www.adafruit.com/product/326 + https://www.adafruit.com/product/938 + ^^^ short A0 to switch to I2C address 0x3C */ + meshtastic_HardwareModel_RP2040_FEATHER_RFM95 = 76, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From c11a66030f5c35d32d26c0e750d52b851c071984 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 24 Aug 2024 12:19:31 -0500 Subject: [PATCH 250/305] Userlite mem comparison (#4552) --- src/mesh/NodeDB.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0504cc273..3af6c6a45 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1027,9 +1027,10 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde #endif // Both of info->user and p start as filled with zero so I think this is okay - bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); + auto lite = TypeConversions::ConvertToUserLite(p); + bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex); - info->user = TypeConversions::ConvertToUserLite(p); + info->user = lite; if (info->user.public_key.size == 32) { printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32); } From 927a35ef51c5e644432def8581757231c7a526f1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 26 Aug 2024 07:48:07 -0500 Subject: [PATCH 251/305] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/config.pb.h | 11 ++++++----- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/protobufs b/protobufs index 52cfa2c1c..431291e67 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 52cfa2c1c2cd5a1a714e1338d551d967f674fca8 +Subproject commit 431291e673c1c7592ee64cb972d7845589f60138 diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index d79654856..eb03ddc58 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -532,7 +532,8 @@ typedef struct _meshtastic_Config_SecurityConfig { Used to create a shared key with a remote device. */ meshtastic_Config_SecurityConfig_private_key_t private_key; /* The public key authorized to send admin messages to this node. */ - meshtastic_Config_SecurityConfig_admin_key_t admin_key; + pb_size_t admin_key_count; + meshtastic_Config_SecurityConfig_admin_key_t admin_key[1]; /* If true, device is considered to be "managed" by a mesh administrator via admin messages Device is managed by a mesh administrator. */ bool is_managed; @@ -657,7 +658,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} -#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_default {0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -668,7 +669,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} -#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0} #define meshtastic_Config_SessionkeyConfig_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ @@ -900,7 +901,7 @@ X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) #define meshtastic_Config_SecurityConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BYTES, public_key, 1) \ X(a, STATIC, SINGULAR, BYTES, private_key, 2) \ -X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ +X(a, STATIC, REPEATED, BYTES, admin_key, 3) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ @@ -948,7 +949,7 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 -#define meshtastic_Config_SecurityConfig_size 110 +#define meshtastic_Config_SecurityConfig_size 111 #define meshtastic_Config_SessionkeyConfig_size 0 #define meshtastic_Config_size 199 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 976e0e135..692402210 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -358,7 +358,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 #define meshtastic_NodeInfoLite_size 183 -#define meshtastic_OEMStore_size 3496 +#define meshtastic_OEMStore_size 3497 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 96 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 38529fc14..91a23dc4f 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -187,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 663 +#define meshtastic_LocalConfig_size 664 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus From 1fe80e0f304986d4fe1a9a8e8faf642b6735e0c9 Mon Sep 17 00:00:00 2001 From: John Milton Date: Mon, 26 Aug 2024 11:28:08 -0400 Subject: [PATCH 252/305] Add support for Adafruit Feather RP2040 with RFM95. (#4451) * Add support for Adafruit Feather RP2040 with RFM95. * Update mesh.pb.h dropping this change from the file generated by the protobuf * Update mesh.pb.h remove these reverting changes * Update mesh.pb.h oops, missed a comma --- src/platform/rp2040/architecture.h | 2 + variants/feather_rp2040_rfm95/platformio.ini | 16 +++++ variants/feather_rp2040_rfm95/variant.h | 61 ++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 variants/feather_rp2040_rfm95/platformio.ini create mode 100644 variants/feather_rp2040_rfm95/variant.h diff --git a/src/platform/rp2040/architecture.h b/src/platform/rp2040/architecture.h index d7d7214c0..3f75735d3 100644 --- a/src/platform/rp2040/architecture.h +++ b/src/platform/rp2040/architecture.h @@ -29,4 +29,6 @@ #define HW_VENDOR meshtastic_HardwareModel_SENSELORA_RP2040 #elif defined(RP2040_LORA) #define HW_VENDOR meshtastic_HardwareModel_RP2040_LORA +#elif defined(RP2040_FEATHER_RFM95) +#define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95 #endif \ No newline at end of file diff --git a/variants/feather_rp2040_rfm95/platformio.ini b/variants/feather_rp2040_rfm95/platformio.ini new file mode 100644 index 000000000..a28ad7655 --- /dev/null +++ b/variants/feather_rp2040_rfm95/platformio.ini @@ -0,0 +1,16 @@ +[env:feather_rp2040_rfm95] +extends = rp2040_base +board = adafruit_feather +upload_protocol = picotool + +# add our variants files to the include and src paths +build_flags = ${rp2040_base.build_flags} + -DRP2040_FEATHER_RFM95 + -Ivariants/feather_rp2040_rfm95 + -DDEBUG_RP2040_PORT=Serial + -DHW_SPI1_DEVICE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" +lib_deps = + ${rp2040_base.lib_deps} +debug_build_flags = ${rp2040_base.build_flags} +debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/feather_rp2040_rfm95/variant.h b/variants/feather_rp2040_rfm95/variant.h new file mode 100644 index 000000000..e9e178202 --- /dev/null +++ b/variants/feather_rp2040_rfm95/variant.h @@ -0,0 +1,61 @@ +// #define RADIOLIB_CUSTOM_ARDUINO 1 +// #define RADIOLIB_TONE_UNSUPPORTED 1 +// #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED 1 + +#define ARDUINO_ARCH_AVR + +// #define USE_SSD1306 + +// #define USE_SH1106 1 + +// default I2C pins: +// SDA = 4 +// SCL = 5 + +// Recommended pins for SerialModule: +// txd = 8 +// rxd = 9 + +#define EXT_NOTIFY_OUT 22 +#define BUTTON_PIN 7 +// #define BUTTON_NEED_PULLUP + +#define LED_PIN PIN_LED + +// #define BATTERY_PIN 26 +// ratio of voltage divider = 3.0 (R17=200k, R18=100k) +// #define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic + +#define USE_RF95 // RFM95/SX127x + +#undef LORA_SCK +#undef LORA_MISO +#undef LORA_MOSI +#undef LORA_CS + +// https://www.adafruit.com/product/5714 +// https://learn.adafruit.com/feather-rp2040-rfm95 +// https://learn.adafruit.com/assets/120283 +// https://learn.adafruit.com/assets/120813 +#define LORA_SCK 14 // 10 12P +#define LORA_MISO 8 // 12 10P +#define LORA_MOSI 15 // 11 11P +#define LORA_CS 16 // 3 13P + +#define LORA_RESET 17 // 15 14P + +#define LORA_DIO0 21 // ?? 6P +#define LORA_DIO1 22 // 20 7P +#define LORA_DIO2 23 // 2 8P +#define LORA_DIO3 19 // ?? 3P +#define LORA_DIO4 20 // ?? 4P +#define LORA_DIO5 18 // ?? 15P + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +// #define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif \ No newline at end of file From 574124aee55dfc79d5e40053ec12887d83e48c02 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 26 Aug 2024 12:29:44 -0500 Subject: [PATCH 253/305] Deal with admin_key being repeated (#4558) --- src/mesh/NodeDB.cpp | 6 +++--- src/modules/AdminModule.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 3af6c6a45..fa736e08a 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -292,10 +292,10 @@ void NodeDB::installDefaultConfig() config.lora.ignore_mqtt = false; #endif #ifdef ADMIN_KEY_USERPREFS - memcpy(config.security.admin_key.bytes, admin_key_userprefs, 32); - config.security.admin_key.size = 32; + memcpy(config.security.admin_key[0].bytes, admin_key_userprefs, 32); + config.security.admin_key[0].size = 32; #else - config.security.admin_key.size = 0; + config.security.admin_key[0].size = 0; #endif config.security.public_key.size = 0; config.security.private_key.size = 0; diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index ef60a4bf2..b63eca396 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -75,7 +75,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta // and only allowing responses from that remote. if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) || (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || - (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) { + (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key[0].bytes, 32) == 0))) { LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); return handled; } From 3c4d964334788e7c335268c746ed0c9eb6947d32 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 26 Aug 2024 15:48:47 -0500 Subject: [PATCH 254/305] Mask out random bits when doing queue ordering (#4561) * Mask out random bits when doing queue ordering * Parenthesis --- src/mesh/MeshPacketQueue.cpp | 7 ++++--- src/mesh/MeshTypes.h | 5 +++-- src/mesh/Router.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 24756fd2a..f1c6c4ff3 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -20,8 +20,9 @@ bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_Mes // If priorities differ, use that // for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but // no big deal) - return (p1p != p2p) ? (p1p < p2p) // prefer bigger priorities - : (p1->id >= p2->id); // prefer smaller packet ids + return (p1p != p2p) + ? (p1p < p2p) // prefer bigger priorities + : ((p1->id & ID_COUNTER_MASK) >= (p2->id & ID_COUNTER_MASK)); // Mask to counter portion, prefer smaller packet ids } MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {} @@ -127,4 +128,4 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); return true; -} +} \ No newline at end of file diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index c0919bf5d..1c9099c39 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -14,8 +14,9 @@ typedef uint32_t PacketId; // A packet sequence number 1 // Reserved to only deliver packets over high speed (non-lora) transports, such as MQTT or BLE mesh (not yet implemented) #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 -#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -#define ERRNO_DISABLED 34 // the interface is disabled +#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER +#define ERRNO_DISABLED 34 // the interface is disabled +#define ID_COUNTER_MASK (UINT32_MAX >> 22) // mask to select the counter portion of the ID /* * Source of a received message diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index bdd8c4e6c..61b1bbfb6 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -109,7 +109,7 @@ PacketId generatePacketId() rollingPacketId++; - rollingPacketId &= UINT32_MAX >> 22; // Mask out the top 22 bits + rollingPacketId &= ID_COUNTER_MASK; // Mask out the top 22 bits PacketId id = rollingPacketId | random(UINT32_MAX & 0x7fffffff) << 10; // top 22 bits LOG_DEBUG("Partially randomized packet id %u\n", id); return id; From e3ce3a3a4f478ebaf266a1335e6e40f4cbb93f42 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 27 Aug 2024 14:49:58 -0500 Subject: [PATCH 255/305] Don't compare nodeDB macaddr to owner.macaddr, because in rare cases that may be unset. (#4562) Co-authored-by: Ben Meadors --- src/mesh/NodeDB.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index fa736e08a..8409eaff6 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -620,7 +620,7 @@ void NodeDB::pickNewNodeNum() meshtastic_NodeInfoLite *found; while ((nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED) || - ((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr)) != 0)) { + ((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0)) { NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate); nodeNum = candidate; From cc93df27a5835ebe90649d1351c451d0fdffd789 Mon Sep 17 00:00:00 2001 From: Power Li Date: Wed, 28 Aug 2024 05:26:02 +0800 Subject: [PATCH 256/305] set current time to system time in portduino build (#4556) * set current time to system time in portduino build * fix includes order --------- Co-authored-by: Jonathan Bennett --- src/platform/portduino/PortduinoGlue.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 3532d2e79..cce893c0b 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -1,5 +1,6 @@ #include "CryptoEngine.h" #include "PortduinoGPIO.h" +#include "RTC.h" #include "SPIChip.h" #include "mesh/RF95Interface.h" #include "sleep.h" @@ -370,6 +371,12 @@ void portduinoSetup() exit(EXIT_FAILURE); } } + + struct timeval tv; + tv.tv_sec = time(NULL); + tv.tv_usec = 0; + perhapsSetRTC(RTCQualityNTP, &tv); + return; } From 72c82c1c08b83986fe9afdf5b55fa10d9933e308 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 27 Aug 2024 18:24:14 -0500 Subject: [PATCH 257/305] Add RAK4631 hex to firmware release --- bin/build-nrf52.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index 060d06cfd..9d0b3dfdd 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -46,3 +46,8 @@ else cp bin/device-update.* $OUTDIR cp bin/*.uf2 $OUTDIR fi + +if (echo $1 | grep -q "rak4631"); then + echo "Copying hex file" + cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex +fi \ No newline at end of file From 94c3bb4a560b0e3e3aa9e4b1c81fa0b6fc0db8af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 28 Aug 2024 12:43:19 +0200 Subject: [PATCH 258/305] fix #4390 (#4571) --- src/serialization/MeshPacketSerializer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index a42bb867a..e00dde024 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -76,6 +76,13 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction); msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust); msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull); + } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { + msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard); + msgPayload["pm25"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_standard); + msgPayload["pm100"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm100_standard); + msgPayload["pm10_e"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_environmental); + msgPayload["pm25_e"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_environmental); + msgPayload["pm100_e"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm100_environmental); } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage); msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current); From 545d32fcec222a86c487950c21ecbcbf37634d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Cort=C3=AAs?= Date: Mon, 26 Aug 2024 17:15:42 +0100 Subject: [PATCH 259/305] Fix devcontainer Dockerfile build --- .devcontainer/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 82f4d91bf..c21564491 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -12,6 +12,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ libssl-dev \ libulfius-dev \ libyaml-cpp-dev \ + pipx \ pkg-config \ python3 \ python3-pip \ @@ -21,4 +22,4 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ zip \ && apt-get clean && rm -rf /var/lib/apt/lists/* -RUN pip3 install --no-cache-dir -U platformio==6.1.15 \ No newline at end of file +RUN pipx install platformio==6.1.15 \ No newline at end of file From 3ad0af5ce8afa3009009acaf4961c5677377da46 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 28 Aug 2024 06:43:30 -0500 Subject: [PATCH 260/305] Fix super tiny T1114 tft font size and fork repo to fix compiler warnings (#4573) --- src/graphics/Screen.cpp | 13 ++++++++----- src/graphics/ScreenFonts.h | 3 ++- src/graphics/images.h | 4 ++-- variants/heltec_mesh_node_t114/platformio.ini | 2 +- variants/heltec_vision_master_t190/platformio.ini | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3a4db6ee0..2690d0b70 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1093,7 +1093,8 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStat { char usersString[20]; snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x, y + 3, 8, 8, imgUser); #else @@ -2414,7 +2415,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 #ifdef ARCH_ESP32 if (millis() - storeForwardModule->lastHeartbeat > (storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL1); @@ -2425,7 +2427,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 imgQuestion); #endif } else { -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8, imgSFL1); @@ -2439,8 +2442,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 #endif } else { // TODO: Raspberry Pi supports more than just the one screen size -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \ - ARCH_PORTDUINO) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgInfoL1); diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 8a48d053e..41e739fa1 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -8,7 +8,8 @@ #include "graphics/fonts/OLEDDisplayFontsUA.h" #endif -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) // The screen is bigger so use bigger fonts #define FONT_SMALL ArialMT_Plain_16 // Height: 19 diff --git a/src/graphics/images.h b/src/graphics/images.h index d4c738610..ab3767a89 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -20,8 +20,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03 0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f}; #endif -#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \ - ARCH_PORTDUINO) && \ +#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \ + defined(HX8357_CS) || ARCH_PORTDUINO) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff}; const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f}; diff --git a/variants/heltec_mesh_node_t114/platformio.ini b/variants/heltec_mesh_node_t114/platformio.ini index 99bdf77a7..c2a458f78 100644 --- a/variants/heltec_mesh_node_t114/platformio.ini +++ b/variants/heltec_mesh_node_t114/platformio.ini @@ -12,4 +12,4 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_node lib_deps = ${nrf52840_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/Bei-Ji-Quan/st7789#b8e7e076714b670764139289d3829b0beff67edb \ No newline at end of file + https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b \ No newline at end of file diff --git a/variants/heltec_vision_master_t190/platformio.ini b/variants/heltec_vision_master_t190/platformio.ini index 38a3169e8..fd0001439 100644 --- a/variants/heltec_vision_master_t190/platformio.ini +++ b/variants/heltec_vision_master_t190/platformio.ini @@ -9,5 +9,5 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/Bei-Ji-Quan/st7789#b8e7e076714b670764139289d3829b0beff67edb + https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b upload_speed = 921600 \ No newline at end of file From ad931799c9d434d6861869cf4e3fa83740346913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 28 Aug 2024 13:51:44 +0200 Subject: [PATCH 261/305] trunk upgrade (#4574) --- .trunk/trunk.yaml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 2d9f60899..b20a1ec95 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,36 +1,36 @@ version: 0.1 cli: - version: 1.22.2 + version: 1.22.3 plugins: sources: - id: trunk - ref: v1.5.0 + ref: v1.6.2 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.76.3 + - trufflehog@3.81.9 - yamllint@1.35.1 - - bandit@1.7.8 - - checkov@3.2.95 + - bandit@1.7.9 + - checkov@3.2.238 - terrascan@1.19.1 - - trivy@0.51.1 + - trivy@0.54.1 #- trufflehog@3.63.2-rc0 - - taplo@0.8.1 - - ruff@0.4.4 + - taplo@0.9.3 + - ruff@0.6.2 - isort@5.13.2 - - markdownlint@0.40.0 - - oxipng@9.1.1 + - markdownlint@0.41.0 + - oxipng@9.1.2 - svgo@3.3.2 - - actionlint@1.7.0 - - flake8@7.0.0 + - actionlint@1.7.1 + - flake8@7.1.1 - hadolint@2.12.0 - shfmt@3.6.0 - shellcheck@0.10.0 - - black@24.4.2 + - black@24.8.0 - git-diff-check - - gitleaks@8.18.2 + - gitleaks@8.18.4 - clang-format@16.0.3 - - prettier@3.2.5 + - prettier@3.3.3 ignore: - linters: [ALL] paths: From f5633bf0c5cff3bfc2c83d1ae293cf23664d8ff1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 28 Aug 2024 07:24:41 -0500 Subject: [PATCH 262/305] Fix T1000-E default to turn on buzzer for Ext. Notification (#4575) --- src/mesh/NodeDB.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 8409eaff6..004f27a0d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -395,6 +395,12 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.has_store_forward = true; moduleConfig.has_telemetry = true; moduleConfig.has_external_notification = true; +#if defined(PIN_BUZZER) + moduleConfig.external_notification.enabled = true; + moduleConfig.external_notification.output_buzzer = PIN_BUZZER; + moduleConfig.external_notification.alert_message_buzzer = true; + moduleConfig.external_notification.nag_timeout = 60; +#endif #if defined(RAK4630) || defined(RAK11310) // Default to RAK led pin 2 (blue) moduleConfig.external_notification.enabled = true; From a1bf0d8519263a2663db0374211d370cacee0127 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 28 Aug 2024 07:54:50 -0500 Subject: [PATCH 263/305] Add button secondary and enable scan-select on T190 (#4577) --- variants/heltec_vision_master_t190/variant.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/heltec_vision_master_t190/variant.h b/variants/heltec_vision_master_t190/variant.h index 726f6d864..1da3f9971 100644 --- a/variants/heltec_vision_master_t190/variant.h +++ b/variants/heltec_vision_master_t190/variant.h @@ -1,4 +1,6 @@ #define BUTTON_PIN 0 +#define BUTTON_PIN_SECONDARY 21 // Second built-in button +#define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input // I2C #define I2C_SDA SDA From dc9f6e1360a8f2e7d8b84886482a9e4322877c75 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 28 Aug 2024 10:44:46 -0700 Subject: [PATCH 264/305] fix CI warnings (and change CI comment to be correct) --- .github/workflows/build_native.yml | 2 +- src/gps/GPS.h | 2 +- src/graphics/TFTDisplay.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml index 3e8b4c001..51bef0c13 100644 --- a/.github/workflows/build_native.yml +++ b/.github/workflows/build_native.yml @@ -10,7 +10,7 @@ jobs: build-native: runs-on: ubuntu-latest steps: - - name: Install libbluetooth + - name: Install libs needed for native build shell: bash run: | sudo apt-get update --fix-missing diff --git a/src/gps/GPS.h b/src/gps/GPS.h index befa4eef0..c0e9fb8b6 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -157,7 +157,7 @@ class GPS : private concurrency::OSThread * * Normally set by GPS::createGPS() */ - GpioVirtPin *enablePin; + GpioVirtPin *enablePin = NULL; GPS() : concurrency::OSThread("GPS") {} diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 1dc4569db..2849dd9a9 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -25,8 +25,6 @@ extern SX1509 gpioExtender; #define TFT_INVERT true #endif -GpioPin *TFTDisplay::backlightEnable; - class LGFX : public lgfx::LGFX_Device { lgfx::Panel_ST7735S _panel_instance; @@ -515,6 +513,8 @@ static LGFX *tft = nullptr; extern unPhone unphone; #endif +GpioPin *TFTDisplay::backlightEnable = NULL; + TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) { LOG_DEBUG("TFTDisplay!\n"); From 92eae39a1b83d5f667dbcc3c9f9b7798cf41010e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 29 Aug 2024 05:39:30 -0500 Subject: [PATCH 265/305] Move Time set from system to main (#4583) --- src/main.cpp | 6 ++++++ src/platform/portduino/PortduinoGlue.cpp | 7 ------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d38b4e669..b73d9803b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -257,6 +257,12 @@ void setup() #ifdef DEBUG_PORT consoleInit(); // Set serial baud rate and init our mesh console +#endif +#if ARCH_PORTDUINO + struct timeval tv; + tv.tv_sec = time(NULL); + tv.tv_usec = 0; + perhapsSetRTC(RTCQualityNTP, &tv); #endif powerMonInit(); diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index cce893c0b..dc143c661 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -1,6 +1,5 @@ #include "CryptoEngine.h" #include "PortduinoGPIO.h" -#include "RTC.h" #include "SPIChip.h" #include "mesh/RF95Interface.h" #include "sleep.h" @@ -8,7 +7,6 @@ #include #include -#include #include "PortduinoGlue.h" #include "linux/gpio/LinuxGPIOPin.h" @@ -372,11 +370,6 @@ void portduinoSetup() } } - struct timeval tv; - tv.tv_sec = time(NULL); - tv.tv_usec = 0; - perhapsSetRTC(RTCQualityNTP, &tv); - return; } From c02bbad9f386b757aa567a71492cd5153de7d407 Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:46:33 +1000 Subject: [PATCH 266/305] Update nightly.yml --- .github/workflows/nightly.yml | 37 ++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e249823a7..67256c21e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,19 +1,20 @@ name: Nightly -on: - schedule: - - cron: 0 8 * * 1-5 - workflow_dispatch: {} - -jobs: - trunk_check: - name: Trunk Check Upload - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Trunk Check - uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b - with: - trunk-token: ${{ secrets.TRUNK_TOKEN }} +on: workflow_dispatch +# on: +# schedule: +# - cron: 0 8 * * 1-5 +# workflow_dispatch: {} +# +# jobs: +# trunk_check: +# name: Trunk Check Upload +# runs-on: ubuntu-latest +# +# steps: +# - name: Checkout +# uses: actions/checkout@v4 +# +# - name: Trunk Check +# uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b +# with: +# trunk-token: ${{ secrets.TRUNK_TOKEN }} From fc1e60ac584dcceeb3acf38cd6d1af1977a5a7e7 Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 17 Aug 2024 16:19:39 +1000 Subject: [PATCH 267/305] Initial upload --- .vscode/extensions.json | 7 ++- platformio.ini | 1 + src/detect/ScanI2C.h | 1 + src/detect/ScanI2CTwoWire.cpp | 15 ++++- src/main.cpp | 1 + .../Telemetry/EnvironmentTelemetry.cpp | 23 +++++++- src/modules/Telemetry/Sensor/BMP3XXSensor.cpp | 58 +++++++++++++++++++ src/modules/Telemetry/Sensor/BMP3XXSensor.h | 31 ++++++++++ 8 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 src/modules/Telemetry/Sensor/BMP3XXSensor.cpp create mode 100644 src/modules/Telemetry/Sensor/BMP3XXSensor.h diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b50c95349..080e70d08 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,8 +2,9 @@ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "ms-vscode.cpptools", - "platformio.platformio-ide", - "trunk.io" + "platformio.platformio-ide" ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] } diff --git a/platformio.ini b/platformio.ini index 4de1ec39f..414232f61 100644 --- a/platformio.ini +++ b/platformio.ini @@ -130,6 +130,7 @@ lib_deps = adafruit/Adafruit BMP280 Library@^2.6.8 adafruit/Adafruit BMP085 Library@^1.2.4 adafruit/Adafruit BME280 Library@^2.2.2 + adafruit/Adafruit BMP3XX Library@^2.1.5 adafruit/Adafruit MCP9808 Library@^2.0.0 adafruit/Adafruit INA260 Library@^1.5.0 adafruit/Adafruit INA219@^1.2.0 diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 711e8bee5..0a5b360de 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -24,6 +24,7 @@ class ScanI2C BME_280, BMP_280, BMP_085, + BMP_3XX, INA260, INA219, INA3221, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 1183d0ddc..76ca42b7f 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -267,8 +267,19 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) type = BMP_085; break; default: - LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address); - type = BMP_280; + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // GET_ID + switch(registerValue) { + case 0x50: // BMP-388 should be 0x50 + LOG_INFO("BMP-388 sensor found at address 0x%x\n", (uint8_t)addr.address); + type = BMP_3XX; + break; + case 0x58: // BMP-280 should be 0x58 + default: + LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address); + type = BMP_280; + break; + } + break; } break; #ifndef HAS_NCP5623 diff --git a/src/main.cpp b/src/main.cpp index b73d9803b..b7d25e764 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -562,6 +562,7 @@ void setup() SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 4755a5be5..51b49a0bc 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -23,6 +23,7 @@ #include "Sensor/BME680Sensor.h" #include "Sensor/BMP085Sensor.h" #include "Sensor/BMP280Sensor.h" +#include "Sensor/BMP3XXSensor.h" #include "Sensor/DFRobotLarkSensor.h" #include "Sensor/LPS22HBSensor.h" #include "Sensor/MCP9808Sensor.h" @@ -40,6 +41,7 @@ BMP085Sensor bmp085Sensor; BMP280Sensor bmp280Sensor; BME280Sensor bme280Sensor; +BMP3XXSensor bmp3xxSensor; BME680Sensor bme680Sensor; MCP9808Sensor mcp9808Sensor; SHTC3Sensor shtc3Sensor; @@ -107,6 +109,8 @@ int32_t EnvironmentTelemetryModule::runOnce() result = bmp280Sensor.runOnce(); if (bme280Sensor.hasSensor()) result = bme280Sensor.runOnce(); + if (bmp3xxSensor.hasSensor()) + result = bmp3xxSensor.runOnce(); if (bme680Sensor.hasSensor()) result = bme680Sensor.runOnce(); if (mcp9808Sensor.hasSensor()) @@ -327,6 +331,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && bme280Sensor.getMetrics(m); hasSensor = true; } + if (bmp3xxSensor.hasSensor()) { + valid = valid && bmp3xxSensor.getMetrics(m); + hasSensor = true; + } if (bme680Sensor.hasSensor()) { valid = valid && bme680Sensor.getMetrics(m); hasSensor = true; @@ -372,15 +380,21 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m hasSensor = true; } if (aht10Sensor.hasSensor()) { - if (!bmp280Sensor.hasSensor()) { + if (!bmp280Sensor.hasSensor() && !bmp3xxSensor.hasSensor()) { valid = valid && aht10Sensor.getMetrics(m); hasSensor = true; - } else { + } else if (bmp280Sensor.hasSensor()) { // prefer bmp280 temp if both sensors are present, fetch only humidity meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n"); aht10Sensor.getMetrics(&m_ahtx); m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; + } else { + // prefer bmp3xx temp if both sensors are present, fetch only humidity + meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; + LOG_INFO("AHTX0+BMP3XX module detected: using temp from BMP3XX and humy from AHTX0\n"); + aht10Sensor.getMetrics(&m_ahtx); + m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; } } @@ -508,6 +522,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (bmp3xxSensor.hasSensor()) { + result = bmp3xxSensor.handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } if (bme680Sensor.hasSensor()) { result = bme680Sensor.handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp new file mode 100644 index 000000000..7aec83818 --- /dev/null +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -0,0 +1,58 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "BMP3XXSensor.h" +#include "TelemetrySensor.h" +#include +#include + +BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX"){} + +int32_t BMP3XXSensor::runOnce() +{ + LOG_INFO("Init sensor: %s\n", sensorName); + if (!hasSensor()) + { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + status = bmp3xx.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); + + // set up oversampling and filter initialization + bmp3xx.setTemperatureOversampling(BMP3_OVERSAMPLING_4X); + bmp3xx.setPressureOversampling(BMP3_OVERSAMPLING_8X); + bmp3xx.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3); + bmp3xx.setOutputDataRate(BMP3_ODR_25_HZ); + + // take a couple of initial readings to settle the sensor filters + for (int i = 0; i < 3; i++) + { + bmp3xx.performReading(); + } + return initI2CSensor(); +} + +bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) +{ + if ((int)measurement->which_variant == meshtastic_Telemetry_environment_metrics_tag) + { + bmp3xx.performReading(); + measurement->variant.environment_metrics.temperature = bmp3xx.readTemperature(); + measurement->variant.environment_metrics.barometric_pressure = bmp3xx.readPressure() / 100.0F; + LOG_DEBUG("BMP3XXSensor::getMetrics id: %i temp: %.1f press %.1f\n", measurement->which_variant, measurement->variant.environment_metrics.temperature, measurement->variant.environment_metrics.barometric_pressure); + } + else + { + LOG_DEBUG("BMP3XXSensor::getMetrics id: %i\n", measurement->which_variant); + } + return true; +} + +float BMP3XXSensor::getAltitudeAMSL() +{ + return bmp3xx.readAltitude(SEAL_LEVEL_HPA); +} + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.h b/src/modules/Telemetry/Sensor/BMP3XXSensor.h new file mode 100644 index 000000000..262d581e9 --- /dev/null +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.h @@ -0,0 +1,31 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#ifndef _BMP3XX_SENSOR_H +#define _BMP3XX_SENSOR_H + +#define SEAL_LEVEL_HPA 1013.2f + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class BMP3XXSensor : public TelemetrySensor +{ +protected: + Adafruit_BMP3XX bmp3xx; + float pressureHPa = 0.0f; + float temperatureCelcius = 0.0f; + float altitudeAmslMetres = 0.0f; + +public: + BMP3XXSensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual float getAltitudeAMSL(); +}; + +#endif + +#endif \ No newline at end of file From 47e1580a62fc0dbd4429a9c3ca9b4e3fc0fe6e8c Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 17 Aug 2024 23:01:43 +1000 Subject: [PATCH 268/305] Integration test --- src/modules/Telemetry/EnvironmentTelemetry.cpp | 2 +- src/modules/Telemetry/Sensor/BMP3XXSensor.cpp | 4 ++++ src/modules/Telemetry/Sensor/BMP3XXSensor.h | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 51b49a0bc..89e3b9d5f 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -41,7 +41,6 @@ BMP085Sensor bmp085Sensor; BMP280Sensor bmp280Sensor; BME280Sensor bme280Sensor; -BMP3XXSensor bmp3xxSensor; BME680Sensor bme680Sensor; MCP9808Sensor mcp9808Sensor; SHTC3Sensor shtc3Sensor; @@ -56,6 +55,7 @@ AHT10Sensor aht10Sensor; MLX90632Sensor mlx90632Sensor; DFRobotLarkSensor dfRobotLarkSensor; NAU7802Sensor nau7802Sensor; +extern BMP3XXSensor bmp3xxSensor; #ifdef T1000X_SENSOR_EN T1000xSensor t1000xSensor; #endif diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp index 7aec83818..6f0f9fc10 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -10,6 +10,10 @@ BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX"){} +BMP3XXSensor bmp3xxSensor; + +void BMP3XXSensor::setup(){}; + int32_t BMP3XXSensor::runOnce() { LOG_INFO("Init sensor: %s\n", sensorName); diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.h b/src/modules/Telemetry/Sensor/BMP3XXSensor.h index 262d581e9..514ee1762 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.h +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.h @@ -21,11 +21,14 @@ protected: public: BMP3XXSensor(); + virtual void setup() override; virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual float getAltitudeAMSL(); }; +extern BMP3XXSensor bmp3xxSensor; + #endif #endif \ No newline at end of file From 6d2011c17241c7213814672ccec6b5c2d2f55ff6 Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 17 Aug 2024 23:25:55 +1000 Subject: [PATCH 269/305] Revert "Update nightly.yml" This reverts commit 44b975386d042b1810d5f3e1f2796af3ba7c118a. --- .github/workflows/nightly.yml | 37 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 67256c21e..e249823a7 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,20 +1,19 @@ name: Nightly -on: workflow_dispatch -# on: -# schedule: -# - cron: 0 8 * * 1-5 -# workflow_dispatch: {} -# -# jobs: -# trunk_check: -# name: Trunk Check Upload -# runs-on: ubuntu-latest -# -# steps: -# - name: Checkout -# uses: actions/checkout@v4 -# -# - name: Trunk Check -# uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b -# with: -# trunk-token: ${{ secrets.TRUNK_TOKEN }} +on: + schedule: + - cron: 0 8 * * 1-5 + workflow_dispatch: {} + +jobs: + trunk_check: + name: Trunk Check Upload + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Trunk Check + uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b + with: + trunk-token: ${{ secrets.TRUNK_TOKEN }} From 28d0cef42760b81825e905b890a52aa977cf2646 Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 17 Aug 2024 23:31:37 +1000 Subject: [PATCH 270/305] Undo inadvertent changes to extensions.json --- .vscode/extensions.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 080e70d08..783791f0b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,9 +2,8 @@ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "platformio.platformio-ide" + "ms-vscode.cpptools", + "platformio.platformio-ide", + "trunk.io" ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} +} \ No newline at end of file From db870dc17db36cee31bfbe60ea5f006e9b4bc42f Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 17 Aug 2024 23:35:27 +1000 Subject: [PATCH 271/305] Update extensions.json --- .vscode/extensions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 783791f0b..b50c95349 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,8 +2,8 @@ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "ms-vscode.cpptools", + "ms-vscode.cpptools", "platformio.platformio-ide", "trunk.io" ], -} \ No newline at end of file +} From 171512d2f65fe9819a341d68cbd263e10764549b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 29 Aug 2024 11:42:27 -0500 Subject: [PATCH 272/305] Fixed buzzer --- src/mesh/NodeDB.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 004f27a0d..bf8596423 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -398,6 +398,7 @@ void NodeDB::installDefaultModuleConfig() #if defined(PIN_BUZZER) moduleConfig.external_notification.enabled = true; moduleConfig.external_notification.output_buzzer = PIN_BUZZER; + moduleConfig.external_notification.use_pwm = true; moduleConfig.external_notification.alert_message_buzzer = true; moduleConfig.external_notification.nag_timeout = 60; #endif @@ -410,6 +411,7 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.output_ms = 1000; moduleConfig.external_notification.nag_timeout = 60; #endif + #ifdef HAS_I2S // Don't worry about the other settings for T-Watch, we'll also use the DRV2056 behavior for notifications moduleConfig.external_notification.enabled = true; From 50631f96fc02e1d842de407c62e665a9a747b1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 29 Aug 2024 21:51:06 +0200 Subject: [PATCH 273/305] trunk fmt --- src/graphics/Screen.cpp | 2 +- src/graphics/Screen.h | 19 +- src/graphics/fonts/OLEDDisplayFontsPL.cpp | 868 +++++++++++----------- 3 files changed, 442 insertions(+), 447 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index f8abe030d..04fe73e44 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -123,7 +123,7 @@ static bool heartbeat = false; /// Check if the display can render a string (detect special chars; emoji) static bool haveGlyphs(const char *str) { -#if defined(OLED_PL) ||defined(OLED_UA) || defined(OLED_RU) +#if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU) // Don't want to make any assumptions about custom language support return true; #endif diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 7db580272..1b6f541be 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -322,7 +322,7 @@ class Screen : public concurrency::OSThread uint8_t last = LASTCHAR; // get last char LASTCHAR = ch; -#if defined(OLED_PL) +#if defined(OLED_PL) switch (last) { // conversion depending on first UTF8-character case 0xC2: { @@ -333,28 +333,25 @@ class Screen : public concurrency::OSThread if (ch == 147) return (uint8_t)(ch); // Ó - else - if (ch == 179) + else if (ch == 179) return (uint8_t)(148); // ó - else + else return (uint8_t)(ch | 0xC0); break; - } - case 0xC4: { + case 0xC4: { SKIPREST = false; return (uint8_t)(ch); } - + case 0xC5: { SKIPREST = false; if (ch == 132) return (uint8_t)(136); // ń - else - if (ch == 186) + else if (ch == 186) return (uint8_t)(137); // ź - else + else return (uint8_t)(ch); break; } @@ -364,7 +361,6 @@ class Screen : public concurrency::OSThread if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) return (uint8_t)0; - #endif #if defined(OLED_UA) || defined(OLED_RU) @@ -425,7 +421,6 @@ class Screen : public concurrency::OSThread #endif - // If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the // rest of it if (SKIPREST) diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.cpp b/src/graphics/fonts/OLEDDisplayFontsPL.cpp index 1322b1772..03fdab5fa 100644 --- a/src/graphics/fonts/OLEDDisplayFontsPL.cpp +++ b/src/graphics/fonts/OLEDDisplayFontsPL.cpp @@ -2,439 +2,439 @@ // Font generated or edited with the glyphEditor const uint8_t ArialMT_Plain_10_PL[] PROGMEM = { -0x0A, // Width: 10 -0x0D, // Height: 13 -0x20, // First char: 32 -0xE0, // Number of chars: 224 + 0x0A, // Width: 10 + 0x0D, // Height: 13 + 0x20, // First char: 32 + 0xE0, // Number of chars: 224 -// Jump Table: -0xFF, 0xFF, 0x00, 0x03, // 32:65535 -0x00, 0x00, 0x04, 0x03, // 33 -0x00, 0x04, 0x05, 0x04, // 34 -0x00, 0x09, 0x09, 0x06, // 35 -0x00, 0x12, 0x0A, 0x06, // 36 -0x00, 0x1C, 0x10, 0x09, // 37 -0x00, 0x2C, 0x0E, 0x08, // 38 -0x00, 0x3A, 0x01, 0x02, // 39 -0x00, 0x3B, 0x06, 0x04, // 40 -0x00, 0x41, 0x06, 0x04, // 41 -0x00, 0x47, 0x05, 0x04, // 42 -0x00, 0x4C, 0x09, 0x06, // 43 -0x00, 0x55, 0x04, 0x03, // 44 -0x00, 0x59, 0x03, 0x03, // 45 -0x00, 0x5C, 0x04, 0x03, // 46 -0x00, 0x60, 0x05, 0x04, // 47 -0x00, 0x65, 0x0A, 0x06, // 48 -0x00, 0x6F, 0x08, 0x05, // 49 -0x00, 0x77, 0x0A, 0x06, // 50 -0x00, 0x81, 0x0A, 0x06, // 51 -0x00, 0x8B, 0x0B, 0x07, // 52 -0x00, 0x96, 0x0A, 0x06, // 53 -0x00, 0xA0, 0x0A, 0x06, // 54 -0x00, 0xAA, 0x09, 0x06, // 55 -0x00, 0xB3, 0x0A, 0x06, // 56 -0x00, 0xBD, 0x0A, 0x06, // 57 -0x00, 0xC7, 0x04, 0x03, // 58 -0x00, 0xCB, 0x04, 0x03, // 59 -0x00, 0xCF, 0x0A, 0x06, // 60 -0x00, 0xD9, 0x09, 0x06, // 61 -0x00, 0xE2, 0x09, 0x06, // 62 -0x00, 0xEB, 0x0B, 0x07, // 63 -0x00, 0xF6, 0x14, 0x0B, // 64 -0x01, 0x0A, 0x0E, 0x08, // 65 -0x01, 0x18, 0x0C, 0x07, // 66 -0x01, 0x24, 0x0C, 0x07, // 67 -0x01, 0x30, 0x0B, 0x07, // 68 -0x01, 0x3B, 0x0C, 0x07, // 69 -0x01, 0x47, 0x09, 0x06, // 70 -0x01, 0x50, 0x0D, 0x08, // 71 -0x01, 0x5D, 0x0C, 0x07, // 72 -0x01, 0x69, 0x04, 0x03, // 73 -0x01, 0x6D, 0x08, 0x05, // 74 -0x01, 0x75, 0x0E, 0x08, // 75 -0x01, 0x83, 0x0C, 0x07, // 76 -0x01, 0x8F, 0x10, 0x09, // 77 -0x01, 0x9F, 0x0C, 0x07, // 78 -0x01, 0xAB, 0x0E, 0x08, // 79 -0x01, 0xB9, 0x0B, 0x07, // 80 -0x01, 0xC4, 0x0E, 0x08, // 81 -0x01, 0xD2, 0x0C, 0x07, // 82 -0x01, 0xDE, 0x0C, 0x07, // 83 -0x01, 0xEA, 0x0B, 0x07, // 84 -0x01, 0xF5, 0x0C, 0x07, // 85 -0x02, 0x01, 0x0D, 0x08, // 86 -0x02, 0x0E, 0x11, 0x0A, // 87 -0x02, 0x1F, 0x0E, 0x08, // 88 -0x02, 0x2D, 0x0D, 0x08, // 89 -0x02, 0x3A, 0x0C, 0x07, // 90 -0x02, 0x46, 0x06, 0x04, // 91 -0x02, 0x4C, 0x06, 0x04, // 92 -0x02, 0x52, 0x04, 0x03, // 93 -0x02, 0x56, 0x09, 0x06, // 94 -0x02, 0x5F, 0x0C, 0x07, // 95 -0x02, 0x6B, 0x03, 0x03, // 96 -0x02, 0x6E, 0x0A, 0x06, // 97 -0x02, 0x78, 0x0A, 0x06, // 98 -0x02, 0x82, 0x0A, 0x06, // 99 -0x02, 0x8C, 0x0A, 0x06, // 100 -0x02, 0x96, 0x0A, 0x06, // 101 -0x02, 0xA0, 0x05, 0x04, // 102 -0x02, 0xA5, 0x0A, 0x06, // 103 -0x02, 0xAF, 0x0A, 0x06, // 104 -0x02, 0xB9, 0x04, 0x03, // 105 -0x02, 0xBD, 0x04, 0x03, // 106 -0x02, 0xC1, 0x08, 0x05, // 107 -0x02, 0xC9, 0x04, 0x03, // 108 -0x02, 0xCD, 0x10, 0x09, // 109 -0x02, 0xDD, 0x0A, 0x06, // 110 -0x02, 0xE7, 0x0A, 0x06, // 111 -0x02, 0xF1, 0x0A, 0x06, // 112 -0x02, 0xFB, 0x0A, 0x06, // 113 -0x03, 0x05, 0x05, 0x04, // 114 -0x03, 0x0A, 0x08, 0x05, // 115 -0x03, 0x12, 0x06, 0x04, // 116 -0x03, 0x18, 0x0A, 0x06, // 117 -0x03, 0x22, 0x09, 0x06, // 118 -0x03, 0x2B, 0x0E, 0x08, // 119 -0x03, 0x39, 0x0A, 0x06, // 120 -0x03, 0x43, 0x09, 0x06, // 121 -0x03, 0x4C, 0x0A, 0x06, // 122 -0x03, 0x56, 0x06, 0x04, // 123 -0x03, 0x5C, 0x04, 0x03, // 124 -0x03, 0x60, 0x05, 0x04, // 125 -0x03, 0x65, 0x09, 0x06, // 126 -0xFF, 0xFF, 0x00, 0x0A, // 127 -0xFF, 0xFF, 0x00, 0x0A, // 128 -0x03, 0x6E, 0x0C, 0x07, // 129 -0x03, 0x7A, 0x05, 0x04, // 130 -0x03, 0x7F, 0x0C, 0x07, // 131 -0x03, 0x8B, 0x0E, 0x08, // 132 -0x03, 0x99, 0x0C, 0x07, // 133 -0x03, 0xA5, 0x0C, 0x07, // 134 -0x03, 0xB1, 0x0A, 0x06, // 135 -0x03, 0xBB, 0x0A, 0x06, // 136 -0x03, 0xC5, 0x0A, 0x06, // 137 -0xFF, 0xFF, 0x00, 0x0A, // 138 -0xFF, 0xFF, 0x00, 0x0A, // 139 -0xFF, 0xFF, 0x00, 0x0A, // 140 -0xFF, 0xFF, 0x00, 0x0A, // 141 -0xFF, 0xFF, 0x00, 0x0A, // 142 -0xFF, 0xFF, 0x00, 0x0A, // 143 -0xFF, 0xFF, 0x00, 0x0A, // 144 -0xFF, 0xFF, 0x00, 0x0A, // 145 -0xFF, 0xFF, 0x00, 0x0A, // 146 -0x03, 0xCF, 0x0E, 0x08, // 147 -0x03, 0xDD, 0x0A, 0x06, // 148 -0xFF, 0xFF, 0x00, 0x0A, // 149 -0xFF, 0xFF, 0x00, 0x0A, // 150 -0xFF, 0xFF, 0x00, 0x0A, // 151 -0x03, 0xE7, 0x0C, 0x07, // 152 -0x03, 0xF3, 0x0C, 0x07, // 153 -0x03, 0xFF, 0x0C, 0x07, // 154 -0x04, 0x0B, 0x08, 0x05, // 155 -0xFF, 0xFF, 0x00, 0x0A, // 156 -0xFF, 0xFF, 0x00, 0x0A, // 157 -0xFF, 0xFF, 0x00, 0x0A, // 158 -0xFF, 0xFF, 0x00, 0x0A, // 159 -0xFF, 0xFF, 0x00, 0x0A, // 160 -0x04, 0x13, 0x04, 0x03, // 161 -0x04, 0x17, 0x0A, 0x06, // 162 -0x04, 0x21, 0x0C, 0x07, // 163 -0x04, 0x2D, 0x0A, 0x06, // 164 -0x04, 0x37, 0x0A, 0x06, // 165 -0x04, 0x41, 0x04, 0x03, // 166 -0x04, 0x45, 0x0A, 0x06, // 167 -0x04, 0x4F, 0x05, 0x04, // 168 -0x04, 0x54, 0x0D, 0x08, // 169 -0x04, 0x61, 0x07, 0x05, // 170 -0x04, 0x68, 0x0A, 0x06, // 171 -0x04, 0x72, 0x09, 0x06, // 172 -0x04, 0x7B, 0x03, 0x03, // 173 -0x04, 0x7E, 0x0D, 0x08, // 174 -0x04, 0x8B, 0x0B, 0x07, // 175 -0x04, 0x96, 0x07, 0x05, // 176 -0x04, 0x9D, 0x0A, 0x06, // 177 -0x04, 0xA7, 0x05, 0x04, // 178 -0x04, 0xAC, 0x05, 0x04, // 179 -0x04, 0xB1, 0x05, 0x04, // 180 -0x04, 0xB6, 0x0A, 0x06, // 181 -0x04, 0xC0, 0x09, 0x06, // 182 -0x04, 0xC9, 0x03, 0x03, // 183 -0x04, 0xCC, 0x06, 0x04, // 184 -0x04, 0xD2, 0x0C, 0x07, // 185 -0x04, 0xDE, 0x07, 0x05, // 186 -0x04, 0xE5, 0x0C, 0x07, // 187 -0x04, 0xF1, 0x0A, 0x06, // 188 -0x04, 0xFB, 0x10, 0x09, // 189 -0x05, 0x0B, 0x10, 0x09, // 190 -0x05, 0x1B, 0x0A, 0x06, // 191 -0x05, 0x25, 0x0E, 0x08, // 192 -0x05, 0x33, 0x0E, 0x08, // 193 -0x05, 0x41, 0x0E, 0x08, // 194 -0x05, 0x4F, 0x0E, 0x08, // 195 -0x05, 0x5D, 0x0E, 0x08, // 196 -0x05, 0x6B, 0x0E, 0x08, // 197 -0x05, 0x79, 0x12, 0x0A, // 198 -0x05, 0x8B, 0x0C, 0x07, // 199 -0x05, 0x97, 0x0C, 0x07, // 200 -0x05, 0xA3, 0x0C, 0x07, // 201 -0x05, 0xAF, 0x0C, 0x07, // 202 -0x05, 0xBB, 0x0C, 0x07, // 203 -0x05, 0xC7, 0x05, 0x04, // 204 -0x05, 0xCC, 0x04, 0x03, // 205 -0x05, 0xD0, 0x04, 0x03, // 206 -0x05, 0xD4, 0x05, 0x04, // 207 -0x05, 0xD9, 0x0B, 0x07, // 208 -0x05, 0xE4, 0x0C, 0x07, // 209 -0x05, 0xF0, 0x0E, 0x08, // 210 -0x05, 0xFE, 0x0E, 0x08, // 211 -0x06, 0x0C, 0x0E, 0x08, // 212 -0x06, 0x1A, 0x0E, 0x08, // 213 -0x06, 0x28, 0x0E, 0x08, // 214 -0x06, 0x36, 0x0A, 0x06, // 215 -0x06, 0x40, 0x0D, 0x08, // 216 -0x06, 0x4D, 0x0C, 0x07, // 217 -0x06, 0x59, 0x0C, 0x07, // 218 -0x06, 0x65, 0x0C, 0x07, // 219 -0x06, 0x71, 0x0C, 0x07, // 220 -0x06, 0x7D, 0x0D, 0x08, // 221 -0x06, 0x8A, 0x0B, 0x07, // 222 -0x06, 0x95, 0x0C, 0x07, // 223 -0x06, 0xA1, 0x0A, 0x06, // 224 -0x06, 0xAB, 0x0A, 0x06, // 225 -0x06, 0xB5, 0x0A, 0x06, // 226 -0x06, 0xBF, 0x0A, 0x06, // 227 -0x06, 0xC9, 0x0A, 0x06, // 228 -0x06, 0xD3, 0x0A, 0x06, // 229 -0x06, 0xDD, 0x10, 0x09, // 230 -0x06, 0xED, 0x0A, 0x06, // 231 -0x06, 0xF7, 0x0A, 0x06, // 232 -0x07, 0x01, 0x0A, 0x06, // 233 -0x07, 0x0B, 0x0A, 0x06, // 234 -0x07, 0x15, 0x0A, 0x06, // 235 -0x07, 0x1F, 0x05, 0x04, // 236 -0x07, 0x24, 0x04, 0x03, // 237 -0x07, 0x28, 0x05, 0x04, // 238 -0x07, 0x2D, 0x05, 0x04, // 239 -0x07, 0x32, 0x0A, 0x06, // 240 -0x07, 0x3C, 0x0A, 0x06, // 241 -0x07, 0x46, 0x0A, 0x06, // 242 -0x07, 0x50, 0x0A, 0x06, // 243 -0x07, 0x5A, 0x0A, 0x06, // 244 -0x07, 0x64, 0x0A, 0x06, // 245 -0x07, 0x6E, 0x0A, 0x06, // 246 -0x07, 0x78, 0x09, 0x06, // 247 -0x07, 0x81, 0x0A, 0x06, // 248 -0x07, 0x8B, 0x0A, 0x06, // 249 -0x07, 0x95, 0x0A, 0x06, // 250 -0x07, 0x9F, 0x0A, 0x06, // 251 -0x07, 0xA9, 0x0A, 0x06, // 252 -0x07, 0xB3, 0x09, 0x06, // 253 -0x07, 0xBC, 0x0A, 0x06, // 254 -0x07, 0xC6, 0x09, 0x06, // 255 -// Font Data: -0x00, 0x00, 0xF8, 0x02, // 33 -0x38, 0x00, 0x00, 0x00, 0x38, // 34 -0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 -0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 -0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 -0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 -0x38, // 39 -0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 -0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 -0x28, 0x00, 0x18, 0x00, 0x28, // 42 -0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 -0x00, 0x00, 0x00, 0x06, // 44 -0x80, 0x00, 0x80, // 45 -0x00, 0x00, 0x00, 0x02, // 46 -0x00, 0x03, 0xE0, 0x00, 0x18, // 47 -0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 -0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 -0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 -0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 -0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 -0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 -0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 -0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 -0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 -0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 -0x00, 0x00, 0x20, 0x02, // 58 -0x00, 0x00, 0x20, 0x06, // 59 -0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 -0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 -0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 -0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 -0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 -0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 -0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 -0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 -0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 -0x00, 0x00, 0xF8, 0x03, // 73 -0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 -0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 -0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 -0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 -0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 -0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 -0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 -0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 -0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 -0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 -0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 -0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 -0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 -0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 -0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 -0x08, 0x08, 0xF8, 0x0F, // 93 -0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 -0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 -0x08, 0x00, 0x10, // 96 -0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 -0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 -0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 -0x20, 0x00, 0xF0, 0x03, 0x28, // 102 -0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 -0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 -0x00, 0x00, 0xE8, 0x03, // 105 -0x00, 0x08, 0xE8, 0x07, // 106 -0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 -0x00, 0x00, 0xF8, 0x03, // 108 -0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 -0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 -0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 -0x00, 0x00, 0xE0, 0x03, 0x20, // 114 -0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 -0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 -0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 -0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 -0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 -0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 -0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 -0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 -0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 -0x00, 0x00, 0xF8, 0x0F, // 124 -0x08, 0x08, 0x78, 0x0F, 0x80, // 125 -0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 -0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129 -0x40, 0x00, 0xF8, 0x03, 0x20, // 130 -0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131 -0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x05, 0x00, 0x0A, // 132 -0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x07, 0x00, 0x08, // 133 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135 -0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136 -0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x06, 0x48, 0x0A, // 152 -0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x06, 0x00, 0x08, // 153 -0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154 -0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155 -0x00, 0x00, 0xA0, 0x0F, // 161 -0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 -0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 -0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 -0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 -0x00, 0x00, 0x38, 0x0F, // 166 -0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 -0x08, 0x00, 0x00, 0x00, 0x08, // 168 -0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 -0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 -0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 -0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 -0x80, 0x00, 0x80, // 173 -0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 -0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 -0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 -0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 -0x48, 0x00, 0x68, 0x00, 0x58, // 178 -0x48, 0x00, 0x58, 0x00, 0x68, // 179 -0x00, 0x00, 0x10, 0x00, 0x08, // 180 -0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 -0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 -0x00, 0x00, 0x40, // 183 -0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184 -0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185 -0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 -0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187 -0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 188 -0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 -0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 -0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 -0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192 -0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193 -0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194 -0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195 -0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196 -0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197 -0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199 -0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200 -0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201 -0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202 -0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203 -0x00, 0x00, 0xF9, 0x03, 0x02, // 204 -0x02, 0x00, 0xF9, 0x03, // 205 -0x01, 0x00, 0xFA, 0x03, // 206 -0x02, 0x00, 0xF8, 0x03, 0x02, // 207 -0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208 -0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211 -0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212 -0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213 -0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214 -0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215 -0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216 -0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217 -0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218 -0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219 -0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220 -0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221 -0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222 -0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223 -0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224 -0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225 -0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226 -0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227 -0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228 -0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229 -0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230 -0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231 -0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232 -0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233 -0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234 -0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235 -0x00, 0x00, 0xE4, 0x03, 0x08, // 236 -0x08, 0x00, 0xE4, 0x03, // 237 -0x08, 0x00, 0xE4, 0x03, 0x08, // 238 -0x08, 0x00, 0xE0, 0x03, 0x08, // 239 -0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240 -0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241 -0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242 -0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243 -0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244 -0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245 -0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246 -0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247 -0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248 -0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249 -0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250 -0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251 -0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252 -0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253 -0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254 -0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255 + // Jump Table: + 0xFF, 0xFF, 0x00, 0x03, // 32:65535 + 0x00, 0x00, 0x04, 0x03, // 33 + 0x00, 0x04, 0x05, 0x04, // 34 + 0x00, 0x09, 0x09, 0x06, // 35 + 0x00, 0x12, 0x0A, 0x06, // 36 + 0x00, 0x1C, 0x10, 0x09, // 37 + 0x00, 0x2C, 0x0E, 0x08, // 38 + 0x00, 0x3A, 0x01, 0x02, // 39 + 0x00, 0x3B, 0x06, 0x04, // 40 + 0x00, 0x41, 0x06, 0x04, // 41 + 0x00, 0x47, 0x05, 0x04, // 42 + 0x00, 0x4C, 0x09, 0x06, // 43 + 0x00, 0x55, 0x04, 0x03, // 44 + 0x00, 0x59, 0x03, 0x03, // 45 + 0x00, 0x5C, 0x04, 0x03, // 46 + 0x00, 0x60, 0x05, 0x04, // 47 + 0x00, 0x65, 0x0A, 0x06, // 48 + 0x00, 0x6F, 0x08, 0x05, // 49 + 0x00, 0x77, 0x0A, 0x06, // 50 + 0x00, 0x81, 0x0A, 0x06, // 51 + 0x00, 0x8B, 0x0B, 0x07, // 52 + 0x00, 0x96, 0x0A, 0x06, // 53 + 0x00, 0xA0, 0x0A, 0x06, // 54 + 0x00, 0xAA, 0x09, 0x06, // 55 + 0x00, 0xB3, 0x0A, 0x06, // 56 + 0x00, 0xBD, 0x0A, 0x06, // 57 + 0x00, 0xC7, 0x04, 0x03, // 58 + 0x00, 0xCB, 0x04, 0x03, // 59 + 0x00, 0xCF, 0x0A, 0x06, // 60 + 0x00, 0xD9, 0x09, 0x06, // 61 + 0x00, 0xE2, 0x09, 0x06, // 62 + 0x00, 0xEB, 0x0B, 0x07, // 63 + 0x00, 0xF6, 0x14, 0x0B, // 64 + 0x01, 0x0A, 0x0E, 0x08, // 65 + 0x01, 0x18, 0x0C, 0x07, // 66 + 0x01, 0x24, 0x0C, 0x07, // 67 + 0x01, 0x30, 0x0B, 0x07, // 68 + 0x01, 0x3B, 0x0C, 0x07, // 69 + 0x01, 0x47, 0x09, 0x06, // 70 + 0x01, 0x50, 0x0D, 0x08, // 71 + 0x01, 0x5D, 0x0C, 0x07, // 72 + 0x01, 0x69, 0x04, 0x03, // 73 + 0x01, 0x6D, 0x08, 0x05, // 74 + 0x01, 0x75, 0x0E, 0x08, // 75 + 0x01, 0x83, 0x0C, 0x07, // 76 + 0x01, 0x8F, 0x10, 0x09, // 77 + 0x01, 0x9F, 0x0C, 0x07, // 78 + 0x01, 0xAB, 0x0E, 0x08, // 79 + 0x01, 0xB9, 0x0B, 0x07, // 80 + 0x01, 0xC4, 0x0E, 0x08, // 81 + 0x01, 0xD2, 0x0C, 0x07, // 82 + 0x01, 0xDE, 0x0C, 0x07, // 83 + 0x01, 0xEA, 0x0B, 0x07, // 84 + 0x01, 0xF5, 0x0C, 0x07, // 85 + 0x02, 0x01, 0x0D, 0x08, // 86 + 0x02, 0x0E, 0x11, 0x0A, // 87 + 0x02, 0x1F, 0x0E, 0x08, // 88 + 0x02, 0x2D, 0x0D, 0x08, // 89 + 0x02, 0x3A, 0x0C, 0x07, // 90 + 0x02, 0x46, 0x06, 0x04, // 91 + 0x02, 0x4C, 0x06, 0x04, // 92 + 0x02, 0x52, 0x04, 0x03, // 93 + 0x02, 0x56, 0x09, 0x06, // 94 + 0x02, 0x5F, 0x0C, 0x07, // 95 + 0x02, 0x6B, 0x03, 0x03, // 96 + 0x02, 0x6E, 0x0A, 0x06, // 97 + 0x02, 0x78, 0x0A, 0x06, // 98 + 0x02, 0x82, 0x0A, 0x06, // 99 + 0x02, 0x8C, 0x0A, 0x06, // 100 + 0x02, 0x96, 0x0A, 0x06, // 101 + 0x02, 0xA0, 0x05, 0x04, // 102 + 0x02, 0xA5, 0x0A, 0x06, // 103 + 0x02, 0xAF, 0x0A, 0x06, // 104 + 0x02, 0xB9, 0x04, 0x03, // 105 + 0x02, 0xBD, 0x04, 0x03, // 106 + 0x02, 0xC1, 0x08, 0x05, // 107 + 0x02, 0xC9, 0x04, 0x03, // 108 + 0x02, 0xCD, 0x10, 0x09, // 109 + 0x02, 0xDD, 0x0A, 0x06, // 110 + 0x02, 0xE7, 0x0A, 0x06, // 111 + 0x02, 0xF1, 0x0A, 0x06, // 112 + 0x02, 0xFB, 0x0A, 0x06, // 113 + 0x03, 0x05, 0x05, 0x04, // 114 + 0x03, 0x0A, 0x08, 0x05, // 115 + 0x03, 0x12, 0x06, 0x04, // 116 + 0x03, 0x18, 0x0A, 0x06, // 117 + 0x03, 0x22, 0x09, 0x06, // 118 + 0x03, 0x2B, 0x0E, 0x08, // 119 + 0x03, 0x39, 0x0A, 0x06, // 120 + 0x03, 0x43, 0x09, 0x06, // 121 + 0x03, 0x4C, 0x0A, 0x06, // 122 + 0x03, 0x56, 0x06, 0x04, // 123 + 0x03, 0x5C, 0x04, 0x03, // 124 + 0x03, 0x60, 0x05, 0x04, // 125 + 0x03, 0x65, 0x09, 0x06, // 126 + 0xFF, 0xFF, 0x00, 0x0A, // 127 + 0xFF, 0xFF, 0x00, 0x0A, // 128 + 0x03, 0x6E, 0x0C, 0x07, // 129 + 0x03, 0x7A, 0x05, 0x04, // 130 + 0x03, 0x7F, 0x0C, 0x07, // 131 + 0x03, 0x8B, 0x0E, 0x08, // 132 + 0x03, 0x99, 0x0C, 0x07, // 133 + 0x03, 0xA5, 0x0C, 0x07, // 134 + 0x03, 0xB1, 0x0A, 0x06, // 135 + 0x03, 0xBB, 0x0A, 0x06, // 136 + 0x03, 0xC5, 0x0A, 0x06, // 137 + 0xFF, 0xFF, 0x00, 0x0A, // 138 + 0xFF, 0xFF, 0x00, 0x0A, // 139 + 0xFF, 0xFF, 0x00, 0x0A, // 140 + 0xFF, 0xFF, 0x00, 0x0A, // 141 + 0xFF, 0xFF, 0x00, 0x0A, // 142 + 0xFF, 0xFF, 0x00, 0x0A, // 143 + 0xFF, 0xFF, 0x00, 0x0A, // 144 + 0xFF, 0xFF, 0x00, 0x0A, // 145 + 0xFF, 0xFF, 0x00, 0x0A, // 146 + 0x03, 0xCF, 0x0E, 0x08, // 147 + 0x03, 0xDD, 0x0A, 0x06, // 148 + 0xFF, 0xFF, 0x00, 0x0A, // 149 + 0xFF, 0xFF, 0x00, 0x0A, // 150 + 0xFF, 0xFF, 0x00, 0x0A, // 151 + 0x03, 0xE7, 0x0C, 0x07, // 152 + 0x03, 0xF3, 0x0C, 0x07, // 153 + 0x03, 0xFF, 0x0C, 0x07, // 154 + 0x04, 0x0B, 0x08, 0x05, // 155 + 0xFF, 0xFF, 0x00, 0x0A, // 156 + 0xFF, 0xFF, 0x00, 0x0A, // 157 + 0xFF, 0xFF, 0x00, 0x0A, // 158 + 0xFF, 0xFF, 0x00, 0x0A, // 159 + 0xFF, 0xFF, 0x00, 0x0A, // 160 + 0x04, 0x13, 0x04, 0x03, // 161 + 0x04, 0x17, 0x0A, 0x06, // 162 + 0x04, 0x21, 0x0C, 0x07, // 163 + 0x04, 0x2D, 0x0A, 0x06, // 164 + 0x04, 0x37, 0x0A, 0x06, // 165 + 0x04, 0x41, 0x04, 0x03, // 166 + 0x04, 0x45, 0x0A, 0x06, // 167 + 0x04, 0x4F, 0x05, 0x04, // 168 + 0x04, 0x54, 0x0D, 0x08, // 169 + 0x04, 0x61, 0x07, 0x05, // 170 + 0x04, 0x68, 0x0A, 0x06, // 171 + 0x04, 0x72, 0x09, 0x06, // 172 + 0x04, 0x7B, 0x03, 0x03, // 173 + 0x04, 0x7E, 0x0D, 0x08, // 174 + 0x04, 0x8B, 0x0B, 0x07, // 175 + 0x04, 0x96, 0x07, 0x05, // 176 + 0x04, 0x9D, 0x0A, 0x06, // 177 + 0x04, 0xA7, 0x05, 0x04, // 178 + 0x04, 0xAC, 0x05, 0x04, // 179 + 0x04, 0xB1, 0x05, 0x04, // 180 + 0x04, 0xB6, 0x0A, 0x06, // 181 + 0x04, 0xC0, 0x09, 0x06, // 182 + 0x04, 0xC9, 0x03, 0x03, // 183 + 0x04, 0xCC, 0x06, 0x04, // 184 + 0x04, 0xD2, 0x0C, 0x07, // 185 + 0x04, 0xDE, 0x07, 0x05, // 186 + 0x04, 0xE5, 0x0C, 0x07, // 187 + 0x04, 0xF1, 0x0A, 0x06, // 188 + 0x04, 0xFB, 0x10, 0x09, // 189 + 0x05, 0x0B, 0x10, 0x09, // 190 + 0x05, 0x1B, 0x0A, 0x06, // 191 + 0x05, 0x25, 0x0E, 0x08, // 192 + 0x05, 0x33, 0x0E, 0x08, // 193 + 0x05, 0x41, 0x0E, 0x08, // 194 + 0x05, 0x4F, 0x0E, 0x08, // 195 + 0x05, 0x5D, 0x0E, 0x08, // 196 + 0x05, 0x6B, 0x0E, 0x08, // 197 + 0x05, 0x79, 0x12, 0x0A, // 198 + 0x05, 0x8B, 0x0C, 0x07, // 199 + 0x05, 0x97, 0x0C, 0x07, // 200 + 0x05, 0xA3, 0x0C, 0x07, // 201 + 0x05, 0xAF, 0x0C, 0x07, // 202 + 0x05, 0xBB, 0x0C, 0x07, // 203 + 0x05, 0xC7, 0x05, 0x04, // 204 + 0x05, 0xCC, 0x04, 0x03, // 205 + 0x05, 0xD0, 0x04, 0x03, // 206 + 0x05, 0xD4, 0x05, 0x04, // 207 + 0x05, 0xD9, 0x0B, 0x07, // 208 + 0x05, 0xE4, 0x0C, 0x07, // 209 + 0x05, 0xF0, 0x0E, 0x08, // 210 + 0x05, 0xFE, 0x0E, 0x08, // 211 + 0x06, 0x0C, 0x0E, 0x08, // 212 + 0x06, 0x1A, 0x0E, 0x08, // 213 + 0x06, 0x28, 0x0E, 0x08, // 214 + 0x06, 0x36, 0x0A, 0x06, // 215 + 0x06, 0x40, 0x0D, 0x08, // 216 + 0x06, 0x4D, 0x0C, 0x07, // 217 + 0x06, 0x59, 0x0C, 0x07, // 218 + 0x06, 0x65, 0x0C, 0x07, // 219 + 0x06, 0x71, 0x0C, 0x07, // 220 + 0x06, 0x7D, 0x0D, 0x08, // 221 + 0x06, 0x8A, 0x0B, 0x07, // 222 + 0x06, 0x95, 0x0C, 0x07, // 223 + 0x06, 0xA1, 0x0A, 0x06, // 224 + 0x06, 0xAB, 0x0A, 0x06, // 225 + 0x06, 0xB5, 0x0A, 0x06, // 226 + 0x06, 0xBF, 0x0A, 0x06, // 227 + 0x06, 0xC9, 0x0A, 0x06, // 228 + 0x06, 0xD3, 0x0A, 0x06, // 229 + 0x06, 0xDD, 0x10, 0x09, // 230 + 0x06, 0xED, 0x0A, 0x06, // 231 + 0x06, 0xF7, 0x0A, 0x06, // 232 + 0x07, 0x01, 0x0A, 0x06, // 233 + 0x07, 0x0B, 0x0A, 0x06, // 234 + 0x07, 0x15, 0x0A, 0x06, // 235 + 0x07, 0x1F, 0x05, 0x04, // 236 + 0x07, 0x24, 0x04, 0x03, // 237 + 0x07, 0x28, 0x05, 0x04, // 238 + 0x07, 0x2D, 0x05, 0x04, // 239 + 0x07, 0x32, 0x0A, 0x06, // 240 + 0x07, 0x3C, 0x0A, 0x06, // 241 + 0x07, 0x46, 0x0A, 0x06, // 242 + 0x07, 0x50, 0x0A, 0x06, // 243 + 0x07, 0x5A, 0x0A, 0x06, // 244 + 0x07, 0x64, 0x0A, 0x06, // 245 + 0x07, 0x6E, 0x0A, 0x06, // 246 + 0x07, 0x78, 0x09, 0x06, // 247 + 0x07, 0x81, 0x0A, 0x06, // 248 + 0x07, 0x8B, 0x0A, 0x06, // 249 + 0x07, 0x95, 0x0A, 0x06, // 250 + 0x07, 0x9F, 0x0A, 0x06, // 251 + 0x07, 0xA9, 0x0A, 0x06, // 252 + 0x07, 0xB3, 0x09, 0x06, // 253 + 0x07, 0xBC, 0x0A, 0x06, // 254 + 0x07, 0xC6, 0x09, 0x06, // 255 + // Font Data: + 0x00, 0x00, 0xF8, 0x02, // 33 + 0x38, 0x00, 0x00, 0x00, 0x38, // 34 + 0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 + 0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 + 0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 + 0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 + 0x38, // 39 + 0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 + 0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 + 0x28, 0x00, 0x18, 0x00, 0x28, // 42 + 0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 + 0x00, 0x00, 0x00, 0x06, // 44 + 0x80, 0x00, 0x80, // 45 + 0x00, 0x00, 0x00, 0x02, // 46 + 0x00, 0x03, 0xE0, 0x00, 0x18, // 47 + 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 + 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 + 0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 + 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 + 0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 + 0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 + 0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 + 0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 + 0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 + 0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 + 0x00, 0x00, 0x20, 0x02, // 58 + 0x00, 0x00, 0x20, 0x06, // 59 + 0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 + 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 + 0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 + 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 + 0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 + 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 + 0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 + 0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 + 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 + 0x00, 0x00, 0xF8, 0x03, // 73 + 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 + 0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 + 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 + 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 + 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 + 0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 + 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 + 0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 + 0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 + 0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 + 0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 + 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 + 0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 + 0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 + 0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 + 0x08, 0x08, 0xF8, 0x0F, // 93 + 0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 + 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 + 0x08, 0x00, 0x10, // 96 + 0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 + 0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 + 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 + 0x20, 0x00, 0xF0, 0x03, 0x28, // 102 + 0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 + 0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 + 0x00, 0x00, 0xE8, 0x03, // 105 + 0x00, 0x08, 0xE8, 0x07, // 106 + 0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 + 0x00, 0x00, 0xF8, 0x03, // 108 + 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 + 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 + 0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 + 0x00, 0x00, 0xE0, 0x03, 0x20, // 114 + 0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 + 0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 + 0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 + 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 + 0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 + 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 + 0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 + 0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 + 0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 + 0x00, 0x00, 0xF8, 0x0F, // 124 + 0x08, 0x08, 0x78, 0x0F, 0x80, // 125 + 0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 + 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129 + 0x40, 0x00, 0xF8, 0x03, 0x20, // 130 + 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131 + 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x05, 0x00, 0x0A, // 132 + 0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x07, 0x00, 0x08, // 133 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135 + 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136 + 0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x06, 0x48, 0x0A, // 152 + 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x06, 0x00, 0x08, // 153 + 0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154 + 0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155 + 0x00, 0x00, 0xA0, 0x0F, // 161 + 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 + 0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 + 0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 + 0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 + 0x00, 0x00, 0x38, 0x0F, // 166 + 0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 + 0x08, 0x00, 0x00, 0x00, 0x08, // 168 + 0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 + 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 + 0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 + 0x80, 0x00, 0x80, // 173 + 0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 + 0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 + 0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 + 0x48, 0x00, 0x68, 0x00, 0x58, // 178 + 0x48, 0x00, 0x58, 0x00, 0x68, // 179 + 0x00, 0x00, 0x10, 0x00, 0x08, // 180 + 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 + 0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 + 0x00, 0x00, 0x40, // 183 + 0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184 + 0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185 + 0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 + 0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187 + 0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 188 + 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 + 0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 + 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 + 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192 + 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193 + 0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194 + 0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195 + 0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196 + 0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197 + 0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199 + 0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200 + 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201 + 0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202 + 0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203 + 0x00, 0x00, 0xF9, 0x03, 0x02, // 204 + 0x02, 0x00, 0xF9, 0x03, // 205 + 0x01, 0x00, 0xFA, 0x03, // 206 + 0x02, 0x00, 0xF8, 0x03, 0x02, // 207 + 0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208 + 0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211 + 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212 + 0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213 + 0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214 + 0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215 + 0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216 + 0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217 + 0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218 + 0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219 + 0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220 + 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221 + 0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222 + 0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223 + 0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224 + 0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225 + 0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226 + 0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227 + 0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228 + 0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229 + 0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230 + 0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231 + 0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232 + 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233 + 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234 + 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235 + 0x00, 0x00, 0xE4, 0x03, 0x08, // 236 + 0x08, 0x00, 0xE4, 0x03, // 237 + 0x08, 0x00, 0xE4, 0x03, 0x08, // 238 + 0x08, 0x00, 0xE0, 0x03, 0x08, // 239 + 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240 + 0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241 + 0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242 + 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243 + 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244 + 0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245 + 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246 + 0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247 + 0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248 + 0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249 + 0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250 + 0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251 + 0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252 + 0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253 + 0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254 + 0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255 }; \ No newline at end of file From 22454c95c7711a11632e9fb9483f6de8a0d36d10 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Thu, 29 Aug 2024 23:17:44 +0200 Subject: [PATCH 274/305] [BOARD] Add Minewsemi MS24SF1 nRF52840 SX1262 Module (SoftDevice 7.3.0) (#4584) * Update architecture.h * Add files via upload * Add files via upload * Update variant.h * Update variant.h * Update variant.cpp * Update variant.cpp * Update variant.cpp --- boards/ms24sf1.json | 58 +++++++++++++ src/platform/nrf52/architecture.h | 2 + variants/MS24SF1/platformio.ini | 15 ++++ variants/MS24SF1/variant.cpp | 30 +++++++ variants/MS24SF1/variant.h | 139 ++++++++++++++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 boards/ms24sf1.json create mode 100644 variants/MS24SF1/platformio.ini create mode 100644 variants/MS24SF1/variant.cpp create mode 100644 variants/MS24SF1/variant.h diff --git a/boards/ms24sf1.json b/boards/ms24sf1.json new file mode 100644 index 000000000..4e28507da --- /dev/null +++ b/boards/ms24sf1.json @@ -0,0 +1,58 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v7.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "MS24SF1-BOOT", + "mcu": "nrf52840", + "variant": "MINEWSEMI_MS24SF1_SX1262", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "7.3.0", + "sd_fwid": "0x0123" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": ["arduino"], + "name": "MINEWSEMI_MS24SF1_SX1262", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink", + "cmsis-dap", + "blackmagic" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://en.minewsemi.com/lora-module/nrf52840-sx1262-ms24sf1", + "vendor": "Minesemi" +} diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 834ff6f0c..5a04dd6a7 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -67,6 +67,8 @@ #define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E #elif defined(ME25LS01_4Y10TD) #define HW_VENDOR meshtastic_HardwareModel_ME25LS01_4Y10TD +#elif defined(MS24SF1) +#define HW_VENDOR meshtastic_HardwareModel_MS24SF1 #elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #else diff --git a/variants/MS24SF1/platformio.ini b/variants/MS24SF1/platformio.ini new file mode 100644 index 000000000..5cbd078d0 --- /dev/null +++ b/variants/MS24SF1/platformio.ini @@ -0,0 +1,15 @@ +[env:ms24sf1] +extends = nrf52840_base +board = ms24sf1 +board_level = extra +; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e +build_flags = ${nrf52840_base.build_flags} -Ivariants/MS24SF1 -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. +board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/MS24SF1> +lib_deps = + ${nrf52840_base.lib_deps} +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +upload_protocol = nrfutil +upload_port = /dev/ttyACM1 diff --git a/variants/MS24SF1/variant.cpp b/variants/MS24SF1/variant.cpp new file mode 100644 index 000000000..d1d79c8ce --- /dev/null +++ b/variants/MS24SF1/variant.cpp @@ -0,0 +1,30 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() {} diff --git a/variants/MS24SF1/variant.h b/variants/MS24SF1/variant.h new file mode 100644 index 000000000..d26dcebc2 --- /dev/null +++ b/variants/MS24SF1/variant.h @@ -0,0 +1,139 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_MINEWSEMI_MS24SF1_ +#define _VARIANT_MINEWSEMI_MS24SF1_ + +#define ME25LS01 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// Use the native nrf52 usb power detection +#define NRF_APM + +#define PIN_3V3_EN (32 + 5) //-1 +#define PIN_3V3_ACC_EN -1 + +#define PIN_LED1 (-1) + +#define LED_PIN PIN_LED1 +#define LED_BUILTIN -1 + +#define LED_BLUE -1 +#define LED_STATE_ON 1 // State when LED is lit + +#define BUTTON_PIN (-1) +#define BUTTON_NEED_PULLUP + +#define HAS_WIRE 1 + +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (0 + 29) // P0.15 +#define PIN_WIRE_SCL (0 + 30) // P0.17 + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (-1) // P0.14 +#define PIN_SERIAL1_TX (-1) // P0.13 + +#define PIN_SERIAL2_RX (-1) // P0.17 +#define PIN_SERIAL2_TX (-1) // P0.16 + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 1 // 2 + +#define PIN_SPI_MISO (0 + 17) // MISO P0.17 +#define PIN_SPI_MOSI (0 + 20) // MOSI P0.20 +#define PIN_SPI_SCK (0 + 21) // SCK P0.21 + +// #define PIN_SPI1_MISO (-1) // +// #define PIN_SPI1_MOSI (10) // EPD_MOSI P0.10 +// #define PIN_SPI1_SCK (9) // EPD_SCLK P0.09 + +static const uint8_t SS = (0 + 22); // LORA_CS P0.22 +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// MINEWSEMI nRF52840+SX1262 MS24SF1 (NRF82540 with integrated SX1262) +#define USE_SX1262 +#define SX126X_CS (0 + 22) // LORA_CS P0.22 +#define SX126X_DIO1 (0 + 16) // DIO1 P0.16 +#define SX126X_BUSY (0 + 19) // LORA_BUSY P0.19 +#define SX126X_RESET (0 + 12) // LORA_RESET P0.12 +#define SX126X_TXEN (32 + 4) // TXEN P1.04 +#define SX126X_RXEN (32 + 2) // RXEN P1.02 + +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH + +#define HAS_GPS 0 + +#define PIN_GPS_EN -1 +#define GPS_EN_ACTIVE HIGH +#define PIN_GPS_RESET -1 +#define GPS_VRTC_EN -1 +#define GPS_SLEEP_INT -1 +#define GPS_RTC_INT -1 +#define GPS_RESETB_OUT -1 + +#define BATTERY_PIN -1 +#define ADC_MULTIPLIER (2.0F) + +#define ADC_RESOLUTION 14 +#define BATTERY_SENSE_RESOLUTION_BITS 12 + +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 + +// Buzzer +// #define PIN_BUZZER (0 + 25) + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif // _VARIANT_MINEWSEMI_MS24SF1_ From 5bc17a99112e6695dce63e95be48fb4f2f886361 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 29 Aug 2024 16:28:03 -0500 Subject: [PATCH 275/305] Key regen and MQTT fix (#4585) * Add public key regen * Properly label and handle PKI MQTT packets * Extra debug message to indicate PKI_UNKNOWN_PUBKEY * Ternary! * Don't call non-existant function on stm32 * Actually fix STM32 compilation --- src/mesh/CryptoEngine.cpp | 25 +++++++++++++++++++++++++ src/mesh/CryptoEngine.h | 3 +++ src/mesh/NodeDB.cpp | 26 ++++++++++++++++++-------- src/mesh/ReliableRouter.cpp | 2 +- src/modules/AdminModule.cpp | 9 +++++++++ src/mqtt/MQTT.cpp | 22 ++++++---------------- 6 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index eef9f74b1..a5322a65a 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -11,6 +11,7 @@ #include #include #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + /** * Create a public/private key pair with Curve25519. * @@ -24,6 +25,30 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) memcpy(pubKey, public_key, sizeof(public_key)); memcpy(privKey, private_key, sizeof(private_key)); } + +/** + * regenerate a public key with Curve25519. + * + * @param pubKey The destination for the public key. + * @param privKey The source for the private key. + */ +bool CryptoEngine::regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey) +{ + if (!memfll(privKey, 0, sizeof(private_key))) { + Curve25519::eval(pubKey, privKey, 0); + if (Curve25519::isWeakPoint(pubKey)) { + LOG_ERROR("PKI key generation failed. Specified private key results in a weak\n"); + memset(pubKey, 0, 32); + return false; + } + memcpy(private_key, privKey, sizeof(private_key)); + memcpy(public_key, pubKey, sizeof(public_key)); + } else { + LOG_WARN("X25519 key generation failed due to blank private key\n"); + return false; + } + return true; +} #endif void CryptoEngine::clearKeys() { diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 64382f6a7..4c2fc19d9 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -21,6 +21,7 @@ struct CryptoKey { */ #define MAX_BLOCKSIZE 256 +#define TEST_CURVE25519_FIELD_OPS // Exposes Curve25519::isWeakPoint() for testing keys class CryptoEngine { @@ -33,6 +34,8 @@ class CryptoEngine #if !(MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); + virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey); + #endif void clearKeys(); void setDHPrivateKey(uint8_t *_private_key); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index bf8596423..ba7671dc5 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -139,14 +139,24 @@ NodeDB::NodeDB() crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) - LOG_INFO("Generating new PKI keys\n"); - crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); - config.security.public_key.size = 32; - config.security.private_key.size = 32; - - printBytes("New Pubkey", config.security.public_key.bytes, 32); - owner.public_key.size = 32; - memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); + bool keygenSuccess = false; + if (config.security.private_key.size == 32) { + LOG_INFO("Calculating PKI Public Key\n"); + if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { + keygenSuccess = true; + } + } else { + LOG_INFO("Generating new PKI keys\n"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + keygenSuccess = true; + } + if (keygenSuccess) { + config.security.public_key.size = 32; + config.security.private_key.size = 32; + printBytes("New Pubkey", config.security.public_key.bytes, 32); + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); + } #else LOG_INFO("No PKI keys set, and generation disabled!\n"); #endif diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 4c8c1e1e7..1f2c01473 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -112,7 +112,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && (nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { - // This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY + LOG_INFO("This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY\n"); sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, p->hop_limit); } else { diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index b63eca396..c9b875bd1 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -537,6 +537,15 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) case meshtastic_Config_security_tag: LOG_INFO("Setting config: Security\n"); config.security = c.payload_variant.security; +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI) + // We check for a potentially valid private key, and a blank public key, and regen the public key if needed. + if (config.security.private_key.size == 32 && !memfll(config.security.private_key.bytes, 0, 32) && + (config.security.public_key.size == 0 || memfll(config.security.public_key.bytes, 0, 32))) { + if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { + config.security.public_key.size = 32; + } + } +#endif owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); #if !MESHTASTIC_EXCLUDE_PKI diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 2f7e82e3d..797fc7dc3 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -167,9 +167,9 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) strcmp(e.channel_id, "PKI") == 0) { meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p)); meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to); - // Only accept PKI messages if we have both the sender and receiver in our nodeDB, as then it's likely - // they discovered each other via a channel we have downlink enabled for - if (tx && tx->has_user && rx && rx->has_user) + // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's + // likely they discovered each other via a channel we have downlink enabled for + if (p->to == nodeDB->getNodeNum() || (tx && tx->has_user && rx && rx->has_user)) router->enqueueReceivedMessage(p); } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key router->enqueueReceivedMessage(p); @@ -527,7 +527,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & } if (ch.settings.uplink_enabled || mp.pki_encrypted) { - const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel + const char *channelId = mp.pki_encrypted ? "PKI" : channels.getGlobalId(chIndex); meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed(); env->channel_id = (char *)channelId; @@ -546,12 +546,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); - std::string topic; - if (mp.pki_encrypted) { - topic = cryptTopic + "PKI/" + owner.id; - } else { - topic = cryptTopic + channelId + "/" + owner.id; - } + std::string topic = cryptTopic + channelId + "/" + owner.id; LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes); publish(topic.c_str(), bytes, numBytes, false); @@ -561,12 +556,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); if (jsonString.length() != 0) { - std::string topicJson; - if (mp.pki_encrypted) { - topicJson = jsonTopic + "PKI/" + owner.id; - } else { - topicJson = jsonTopic + channelId + "/" + owner.id; - } + std::string topicJson = jsonTopic + channelId + "/" + owner.id; LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str()); publish(topicJson.c_str(), jsonString.c_str(), false); From 79925406d690464b69821e7c582a99b0ce2a6ca0 Mon Sep 17 00:00:00 2001 From: S5NC <145265251+S5NC@users.noreply.github.com> Date: Fri, 30 Aug 2024 03:18:43 +0100 Subject: [PATCH 276/305] Update variant.h --- variants/heltec_wireless_tracker_V1_0/variant.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/variants/heltec_wireless_tracker_V1_0/variant.h b/variants/heltec_wireless_tracker_V1_0/variant.h index 23987adf0..7549d6964 100644 --- a/variants/heltec_wireless_tracker_V1_0/variant.h +++ b/variants/heltec_wireless_tracker_V1_0/variant.h @@ -43,8 +43,7 @@ #define PIN_GPS_RESET 35 #define PIN_GPS_PPS 36 -#define VGNSS_CTRL_V03 37 // Heltec Tracker needs this pulled low for GPS -#define PIN_GPS_EN VGNSS_CTRL_V03 +#define PIN_GPS_EN 37 // Heltec Tracker needs this pulled low for GPS #define GPS_EN_ACTIVE LOW #define GPS_RESET_MODE LOW From dd933e6babd1d8b5f4f12c8dd9520493e79eaca1 Mon Sep 17 00:00:00 2001 From: S5NC <145265251+S5NC@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:51:46 +0100 Subject: [PATCH 277/305] Add bluetooth capability marker to some ESP32S3 boards (#4587) * Update ESP32-S3-WROOM-1-N4.json * Update CDEBYTE_EoRa-S3.json * Update tlora-t3s3-v1.json --- boards/CDEBYTE_EoRa-S3.json | 2 +- boards/ESP32-S3-WROOM-1-N4.json | 2 +- boards/tlora-t3s3-v1.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/boards/CDEBYTE_EoRa-S3.json b/boards/CDEBYTE_EoRa-S3.json index 9ecee3c9f..afaabc5a7 100644 --- a/boards/CDEBYTE_EoRa-S3.json +++ b/boards/CDEBYTE_EoRa-S3.json @@ -19,7 +19,7 @@ "mcu": "esp32s3", "variant": "CDEBYTE_EoRa-S3" }, - "connectivity": ["wifi"], + "connectivity": ["wifi", "bluetooth"], "debug": { "openocd_target": "esp32s3.cfg" }, diff --git a/boards/ESP32-S3-WROOM-1-N4.json b/boards/ESP32-S3-WROOM-1-N4.json index 3620a711d..160926b21 100644 --- a/boards/ESP32-S3-WROOM-1-N4.json +++ b/boards/ESP32-S3-WROOM-1-N4.json @@ -19,7 +19,7 @@ "mcu": "esp32s3", "variant": "ESP32-S3-WROOM-1-N4" }, - "connectivity": ["wifi"], + "connectivity": ["wifi", "bluetooth"], "debug": { "default_tool": "esp-builtin", "onboard_tools": ["esp-builtin"], diff --git a/boards/tlora-t3s3-v1.json b/boards/tlora-t3s3-v1.json index c5a68981b..0bfd17afc 100644 --- a/boards/tlora-t3s3-v1.json +++ b/boards/tlora-t3s3-v1.json @@ -19,7 +19,7 @@ "mcu": "esp32s3", "variant": "tlora-t3s3-v1" }, - "connectivity": ["wifi"], + "connectivity": ["wifi", "bluetooth"], "debug": { "openocd_target": "esp32s3.cfg" }, From 6a24566efb0789789ea46159ed6825957abfc7a7 Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:53:06 +0200 Subject: [PATCH 278/305] Lilygo T3S3 E-Paper support (#4569) * t3s3 e-paper support * remove GPS autodetect (which leads to crashes during startup when no GPS present) * update EINK defines * keep definitions for external GPS connector but disable GPS auto scan by default --- src/graphics/EInkDisplay2.cpp | 2 +- src/graphics/EInkDisplay2.h | 2 +- src/mesh/NodeDB.cpp | 2 +- src/platform/esp32/architecture.h | 2 + variants/tlora_t3s3_epaper/pins_arduino.h | 26 +++++++++ variants/tlora_t3s3_epaper/platformio.ini | 23 ++++++++ variants/tlora_t3s3_epaper/variant.h | 69 +++++++++++++++++++++++ 7 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 variants/tlora_t3s3_epaper/pins_arduino.h create mode 100644 variants/tlora_t3s3_epaper/platformio.ini create mode 100644 variants/tlora_t3s3_epaper/variant.h diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 4b845bd51..c4cb10f82 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -157,7 +157,7 @@ bool EInkDisplay::connect() } #elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \ - defined(HELTEC_VISION_MASTER_E290) + defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) { // Start HSPI hspi = new SPIClass(HSPI); diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h index 26091b2cd..af631150e 100644 --- a/src/graphics/EInkDisplay2.h +++ b/src/graphics/EInkDisplay2.h @@ -68,7 +68,7 @@ class EInkDisplay : public OLEDDisplay // If display uses HSPI #if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \ - defined(HELTEC_VISION_MASTER_E290) + defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) SPIClass *hspi = NULL; #endif diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ba7671dc5..fa3667f32 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -317,7 +317,7 @@ void NodeDB::installDefaultConfig() #else config.device.disable_triple_click = true; #endif -#if !HAS_GPS || defined(T_DECK) +#if !HAS_GPS || defined(T_DECK) || defined(TLORA_T3S3_EPAPER) config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; #elif !defined(GPS_RX_PIN) if (config.position.rx_gpio == 0) diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 3761235a0..f86b342ce 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -122,6 +122,8 @@ #define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_PAPER #elif defined(TLORA_T3S3_V1) #define HW_VENDOR meshtastic_HardwareModel_TLORA_T3_S3 +#elif defined(TLORA_T3S3_EPAPER) +#define HW_VENDOR meshtastic_HardwareModel_TLORA_T3_S3 #elif defined(CDEBYTE_EORA_S3) #define HW_VENDOR meshtastic_HardwareModel_CDEBYTE_EORA_S3 #elif defined(BETAFPV_2400_TX) diff --git a/variants/tlora_t3s3_epaper/pins_arduino.h b/variants/tlora_t3s3_epaper/pins_arduino.h new file mode 100644 index 000000000..ca44959c8 --- /dev/null +++ b/variants/tlora_t3s3_epaper/pins_arduino.h @@ -0,0 +1,26 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// The default Wire will be mapped to PMU and RTC +static const uint8_t SDA = 18; +static const uint8_t SCL = 12; // t3s3 e-Paper has no pin 17 as t3s3 v1, so use another free pin next to it + +// Default SPI will be mapped to Radio +static const uint8_t SS = 7; +static const uint8_t MOSI = 6; +static const uint8_t MISO = 3; +static const uint8_t SCK = 5; + +#define SPI_MOSI (11) +#define SPI_SCK (14) +#define SPI_MISO (2) +#define SPI_CS (13) + +#define SDCARD_CS SPI_CS + +#endif /* Pins_Arduino_h */ diff --git a/variants/tlora_t3s3_epaper/platformio.ini b/variants/tlora_t3s3_epaper/platformio.ini new file mode 100644 index 000000000..ceb4fbaf5 --- /dev/null +++ b/variants/tlora_t3s3_epaper/platformio.ini @@ -0,0 +1,23 @@ +[env:tlora-t3s3-epaper] +extends = esp32s3_base +board = tlora-t3s3-v1 +board_check = true +upload_protocol = esptool + +build_flags = + ${esp32_base.build_flags} -D TLORA_T3S3_EPAPER -I variants/tlora_t3s3_epaper + -DGPS_POWER_TOGGLE + -DEINK_DISPLAY_MODEL=GxEPD2_213_BN + -DEINK_WIDTH=250 + -DEINK_HEIGHT=122 + -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted + -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates + -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates + -DEINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear + ;-DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated + ;-DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. + +lib_deps = + ${esp32s3_base.lib_deps} + https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d diff --git a/variants/tlora_t3s3_epaper/variant.h b/variants/tlora_t3s3_epaper/variant.h new file mode 100644 index 000000000..461ce0c31 --- /dev/null +++ b/variants/tlora_t3s3_epaper/variant.h @@ -0,0 +1,69 @@ +#define HAS_SDCARD +#define SDCARD_USE_SPI1 + +// Display (E-Ink) +#define USE_EINK +#define PIN_EINK_CS 15 +#define PIN_EINK_BUSY 48 +#define PIN_EINK_DC 16 +#define PIN_EINK_RES 47 +#define PIN_EINK_SCLK 14 +#define PIN_EINK_MOSI 11 + +#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to +// measure battery voltage ratio of voltage divider = 2.0 (assumption) +#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage. +#define ADC_CHANNEL ADC1_GPIO1_CHANNEL + +#define I2C_SDA SDA +#define I2C_SCL SCL + +// external qwiic connector +#define GPS_RX_PIN 44 +#define GPS_TX_PIN 43 + +#define LED_PIN 37 +#define BUTTON_PIN 0 +#define BUTTON_NEED_PULLUP + +// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and +// we will probe at runtime for RF95 and if not found then probe for SX1262 +#define USE_RF95 // RFM95/SX127x +#define USE_SX1262 +#define USE_SX1280 + +#define LORA_SCK 5 +#define LORA_MISO 3 +#define LORA_MOSI 6 +#define LORA_CS 7 +#define LORA_RESET 8 + +// per SX1276_Receive_Interrupt/utilities.h +#define LORA_DIO0 9 +#define LORA_DIO1 33 // TCXO_EN ? +#define LORA_DIO2 34 +#define LORA_RXEN 21 +#define LORA_TXEN 10 + +// per SX1262_Receive_Interrupt/utilities.h +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 33 +#define SX126X_BUSY 34 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif + +// per SX128x_Receive_Interrupt/utilities.h +#ifdef USE_SX1280 +#define SX128X_CS LORA_CS +#define SX128X_DIO1 9 +#define SX128X_DIO2 33 +#define SX128X_DIO3 34 +#define SX128X_BUSY 36 +#define SX128X_RESET LORA_RESET +#define SX128X_RXEN 21 +#define SX128X_TXEN 10 +#define SX128X_MAX_POWER 3 +#endif From 2b0113ae82f2dc5cde82e5c00921d41d10ac141d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 30 Aug 2024 06:02:48 -0500 Subject: [PATCH 279/305] Consider an admin timestamp to be higher quality than from net (#4589) --- src/modules/AdminModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index c9b875bd1..bfe3a9ba5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -288,7 +288,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta tv.tv_sec = r->set_time_only; tv.tv_usec = 0; - perhapsSetRTC(RTCQualityFromNet, &tv, false); + perhapsSetRTC(RTCQualityNTP, &tv, false); break; } case meshtastic_AdminMessage_enter_dfu_mode_request_tag: { @@ -1028,4 +1028,4 @@ bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r) return true; else return false; -} \ No newline at end of file +} From 8144dcbc2537a0ec1fd1851dedf6166d9815292f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:46:51 -0500 Subject: [PATCH 280/305] [create-pull-request] automated change (#4591) Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 5 +++++ src/mesh/generated/meshtastic/telemetry.pb.h | 8 +++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/protobufs b/protobufs index 431291e67..28492e88e 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 431291e673c1c7592ee64cb972d7845589f60138 +Subproject commit 28492e88e515aabf5c886dd23631518d6dee82d7 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index d612d74be..a711da806 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -196,6 +196,9 @@ typedef enum _meshtastic_HardwareModel { https://www.adafruit.com/product/938 ^^^ short A0 to switch to I2C address 0x3C */ meshtastic_HardwareModel_RP2040_FEATHER_RFM95 = 76, + /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ */ + meshtastic_HardwareModel_M5STACK_COREBASIC = 77, + meshtastic_HardwareModel_M5STACK_CORE2 = 78, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -347,6 +350,8 @@ typedef enum _meshtastic_MeshPacket_Priority { /* If priority is unset but the message is marked as want_ack, assume it is important and use a slightly higher priority */ meshtastic_MeshPacket_Priority_RELIABLE = 70, + /* Higher priority for specific message types (portnums) to distinguish between other reliable packets. */ + meshtastic_MeshPacket_Priority_HIGH = 100, /* Ack/naks are sent with very high priority to ensure that retransmission stops as soon as possible */ meshtastic_MeshPacket_Priority_ACK = 120, diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 2d3eb407a..cedc2867e 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -69,7 +69,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* ICM-20948 9-Axis digital motion processor */ meshtastic_TelemetrySensorType_ICM20948 = 27, /* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */ - meshtastic_TelemetrySensorType_MAX17048 = 28 + meshtastic_TelemetrySensorType_MAX17048 = 28, + /* Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor */ + meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -265,8 +267,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_MAX17048 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_MAX17048+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_CUSTOM_SENSOR +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_CUSTOM_SENSOR+1)) From eb071ec80d9cbd8cfc339ddfadd730076781da66 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:54:44 +0200 Subject: [PATCH 281/305] Set high priority for text messages (#4592) --- src/mesh/MeshPacketQueue.cpp | 15 +++++++++------ src/mesh/MeshTypes.h | 5 ++++- src/mesh/Router.cpp | 2 ++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index f1c6c4ff3..8e5eedc87 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -40,19 +40,22 @@ void fixPriority(meshtastic_MeshPacket *p) // We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that // and fix it if (p->priority == meshtastic_MeshPacket_Priority_UNSET) { - // if acks give high priority // if a reliable message give a bit higher default priority - p->priority = (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP) - ? meshtastic_MeshPacket_Priority_ACK - : (p->want_ack ? meshtastic_MeshPacket_Priority_RELIABLE : meshtastic_MeshPacket_Priority_DEFAULT); + p->priority = (p->want_ack ? meshtastic_MeshPacket_Priority_RELIABLE : meshtastic_MeshPacket_Priority_DEFAULT); + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + // if acks/naks give very high priority + if (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP) + p->priority = meshtastic_MeshPacket_Priority_ACK; + // if text give high priority + else if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) + p->priority = meshtastic_MeshPacket_Priority_HIGH; + } } } /** enqueue a packet, return false if full */ bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p) { - fixPriority(p); - // no space - try to replace a lower priority packet in the queue if (queue.size() >= maxLen) { return replaceLowerPriorityPacket(p); diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 1c9099c39..90cfd5897 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -48,4 +48,7 @@ extern Allocator &packetPool; * Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on * the local node. If from is zero this function returns our node number instead */ -NodeNum getFrom(const meshtastic_MeshPacket *p); \ No newline at end of file +NodeNum getFrom(const meshtastic_MeshPacket *p); + +/* Some clients might not properly set priority, therefore we fix it here. */ +void fixPriority(meshtastic_MeshPacket *p); \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 61b1bbfb6..804761f4e 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -252,6 +252,8 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) return meshtastic_Routing_Error_BAD_REQUEST; } + fixPriority(p); // Before encryption, fix the priority if it's unset + // If the packet is not yet encrypted, do so now if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it From 7475cc301e0f7270002b5ed6baea97f24bfc69ac Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 30 Aug 2024 15:37:39 -0500 Subject: [PATCH 282/305] GPS_POWER_TOGGLE on T114 --- variants/heltec_mesh_node_t114/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/heltec_mesh_node_t114/platformio.ini b/variants/heltec_mesh_node_t114/platformio.ini index c2a458f78..1009ecce5 100644 --- a/variants/heltec_mesh_node_t114/platformio.ini +++ b/variants/heltec_mesh_node_t114/platformio.ini @@ -7,6 +7,7 @@ debug_tool = jlink # add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling. build_flags = ${nrf52840_base.build_flags} -Ivariants/heltec_mesh_node_t114 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_node_t114> lib_deps = From 33eb073535c2bece2353b9fd76d0dda5d120ac05 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 30 Aug 2024 19:02:48 -0500 Subject: [PATCH 283/305] Ignore (from)Net time on positions with an unknown or fixed location source (#4593) * Ignore (from)Net time on positions with an unknown or fixed location source * Dunk a trunk --- src/modules/PositionModule.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 2a0c95a9b..7c08835bc 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -129,6 +129,10 @@ void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUp LOG_DEBUG("Ignoring time from mesh because we have a GPS, RTC, or Phone/NTP time source in the past day\n"); return; } + if (!isLocal && p.location_source < meshtastic_Position_LocSource_LOC_INTERNAL) { + LOG_DEBUG("Ignoring time from mesh because it has a unknown or manual source\n"); + return; + } struct timeval tv; uint32_t secs = p.time; @@ -191,6 +195,10 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.has_longitude_i = true; p.time = getValidTime(RTCQualityNTP) > 0 ? getValidTime(RTCQualityNTP) : localPosition.time; + if (config.position.fixed_position) { + p.location_source = meshtastic_Position_LocSource_LOC_MANUAL; + } + if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) { if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL) { p.altitude = localPosition.altitude; From 644e213b13d0066b8c8b034188d0cfdc099252eb Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:15:33 +1000 Subject: [PATCH 284/305] Added a singleton wrapper for bmp3xx --- .../Telemetry/EnvironmentTelemetry.cpp | 2 +- src/modules/Telemetry/Sensor/BMP3XXSensor.cpp | 72 ++++++++++++++----- src/modules/Telemetry/Sensor/BMP3XXSensor.h | 50 +++++++++---- 3 files changed, 91 insertions(+), 33 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 89e3b9d5f..31cb2f838 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -55,7 +55,7 @@ AHT10Sensor aht10Sensor; MLX90632Sensor mlx90632Sensor; DFRobotLarkSensor dfRobotLarkSensor; NAU7802Sensor nau7802Sensor; -extern BMP3XXSensor bmp3xxSensor; +BMP3XXSensor bmp3xxSensor; #ifdef T1000X_SENSOR_EN T1000xSensor t1000xSensor; #endif diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp index 6f0f9fc10..feb08d8ff 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -2,17 +2,11 @@ #if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR -#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "BMP3XXSensor.h" -#include "TelemetrySensor.h" -#include -#include BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX"){} -BMP3XXSensor bmp3xxSensor; - -void BMP3XXSensor::setup(){}; +void BMP3XXSensor::setup() {} int32_t BMP3XXSensor::runOnce() { @@ -22,30 +16,44 @@ int32_t BMP3XXSensor::runOnce() return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } - status = bmp3xx.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); + // Get a singleton instance and initialise the bmp3xx + if (bmp3xx == nullptr) { + bmp3xx = BMP3XXSingleton::GetInstance(); + } + status = bmp3xx->begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); // set up oversampling and filter initialization - bmp3xx.setTemperatureOversampling(BMP3_OVERSAMPLING_4X); - bmp3xx.setPressureOversampling(BMP3_OVERSAMPLING_8X); - bmp3xx.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3); - bmp3xx.setOutputDataRate(BMP3_ODR_25_HZ); + bmp3xx->setTemperatureOversampling(BMP3_OVERSAMPLING_4X); + bmp3xx->setPressureOversampling(BMP3_OVERSAMPLING_8X); + bmp3xx->setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3); + bmp3xx->setOutputDataRate(BMP3_ODR_25_HZ); // take a couple of initial readings to settle the sensor filters for (int i = 0; i < 3; i++) { - bmp3xx.performReading(); + bmp3xx->performReading(); } return initI2CSensor(); } bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) { + if (bmp3xx == nullptr) { + bmp3xx = BMP3XXSingleton::GetInstance(); + } if ((int)measurement->which_variant == meshtastic_Telemetry_environment_metrics_tag) { - bmp3xx.performReading(); - measurement->variant.environment_metrics.temperature = bmp3xx.readTemperature(); - measurement->variant.environment_metrics.barometric_pressure = bmp3xx.readPressure() / 100.0F; - LOG_DEBUG("BMP3XXSensor::getMetrics id: %i temp: %.1f press %.1f\n", measurement->which_variant, measurement->variant.environment_metrics.temperature, measurement->variant.environment_metrics.barometric_pressure); + bmp3xx->performReading(); + + measurement->variant.environment_metrics.has_temperature = true; + measurement->variant.environment_metrics.has_barometric_pressure = true; + measurement->variant.environment_metrics.has_relative_humidity = false; + + measurement->variant.environment_metrics.temperature = static_cast(bmp3xx->temperature); + measurement->variant.environment_metrics.barometric_pressure = static_cast(bmp3xx->pressure) / 100.0F; + measurement->variant.environment_metrics.relative_humidity = 0.0f; + + LOG_DEBUG("BMP3XXSensor::getMetrics id: %i temp: %.1f press %.1f\n", measurement->which_variant, measurement->variant.environment_metrics.temperature, measurement->variant.environment_metrics.barometric_pressure); } else { @@ -54,9 +62,35 @@ bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) return true; } -float BMP3XXSensor::getAltitudeAMSL() +// Get a singleton wrapper for an Adafruit_bmp3xx +BMP3XXSingleton *BMP3XXSingleton::GetInstance() { - return bmp3xx.readAltitude(SEAL_LEVEL_HPA); + std::lock_guard lock(mutex_); + if (pinstance_ == nullptr) + { + pinstance_ = new BMP3XXSingleton(); + } + return pinstance_; +} + +BMP3XXSingleton::BMP3XXSingleton(){} + +BMP3XXSingleton::~BMP3XXSingleton(){} + +BMP3XXSingleton* BMP3XXSingleton::pinstance_{nullptr}; + +std::mutex BMP3XXSingleton::mutex_; + +bool BMP3XXSingleton::performReading() +{ + bool result = Adafruit_BMP3XX::performReading(); + if (result) { + double atmospheric = this->pressure / 100.0; + altitudeAmslMetres = 44330.0 * (1.0 - pow(atmospheric / SEAL_LEVEL_HPA, 0.1903)); + } else { + altitudeAmslMetres = 0.0; + } + return result; } #endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.h b/src/modules/Telemetry/Sensor/BMP3XXSensor.h index 514ee1762..04e8f9783 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.h +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.h @@ -7,28 +7,52 @@ #define SEAL_LEVEL_HPA 1013.2f -#include "../mesh/generated/meshtastic/telemetry.pb.h" -#include "TelemetrySensor.h" +#include +#include #include +#include "TelemetrySensor.h" + +// Singleton wrapper for the Adafruit_BMP3XX class +class BMP3XXSingleton : public Adafruit_BMP3XX +{ +private: + static BMP3XXSingleton * pinstance_; + static std::mutex mutex_; + +protected: + BMP3XXSingleton(); + ~BMP3XXSingleton(); + +public: + // Create a singleton instance (with lock for thread safety) + static BMP3XXSingleton *GetInstance(); + + // Singletons should not be cloneable. + BMP3XXSingleton(BMP3XXSingleton &other) = delete; + + // Singletons should not be assignable. + void operator=(const BMP3XXSingleton &) = delete; + + // Performs a full reading of all sensors in the BMP3XX. Assigns + // the internal temperature, pressure and altitudeAmsl variables + bool performReading(); + + // Altitude in metres above mean sea level, assigned after calling performReading() + double altitudeAmslMetres = 0.0f; +}; class BMP3XXSensor : public TelemetrySensor { protected: - Adafruit_BMP3XX bmp3xx; - float pressureHPa = 0.0f; - float temperatureCelcius = 0.0f; - float altitudeAmslMetres = 0.0f; + BMP3XXSingleton *bmp3xx = nullptr; + virtual void setup() override; public: - BMP3XXSensor(); - virtual void setup() override; - virtual int32_t runOnce() override; - virtual bool getMetrics(meshtastic_Telemetry *measurement) override; - virtual float getAltitudeAMSL(); + BMP3XXSensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; }; -extern BMP3XXSensor bmp3xxSensor; - #endif #endif \ No newline at end of file From eb1f80f520bcc25ac75dad674cd74a97e176963e Mon Sep 17 00:00:00 2001 From: David <2941290+dhskinner@users.noreply.github.com> Date: Sat, 31 Aug 2024 19:40:54 +1000 Subject: [PATCH 285/305] Fix build issue with mutex --- src/modules/Telemetry/Sensor/BMP3XXSensor.cpp | 11 ++++------- src/modules/Telemetry/Sensor/BMP3XXSensor.h | 6 ++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp index feb08d8ff..d948cfe38 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -65,21 +65,18 @@ bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) // Get a singleton wrapper for an Adafruit_bmp3xx BMP3XXSingleton *BMP3XXSingleton::GetInstance() { - std::lock_guard lock(mutex_); - if (pinstance_ == nullptr) + if (pinstance == nullptr) { - pinstance_ = new BMP3XXSingleton(); + pinstance = new BMP3XXSingleton(); } - return pinstance_; + return pinstance; } BMP3XXSingleton::BMP3XXSingleton(){} BMP3XXSingleton::~BMP3XXSingleton(){} -BMP3XXSingleton* BMP3XXSingleton::pinstance_{nullptr}; - -std::mutex BMP3XXSingleton::mutex_; +BMP3XXSingleton* BMP3XXSingleton::pinstance{nullptr}; bool BMP3XXSingleton::performReading() { diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.h b/src/modules/Telemetry/Sensor/BMP3XXSensor.h index 04e8f9783..77b434caa 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.h +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.h @@ -7,7 +7,6 @@ #define SEAL_LEVEL_HPA 1013.2f -#include #include #include #include "TelemetrySensor.h" @@ -16,15 +15,14 @@ class BMP3XXSingleton : public Adafruit_BMP3XX { private: - static BMP3XXSingleton * pinstance_; - static std::mutex mutex_; + static BMP3XXSingleton * pinstance; protected: BMP3XXSingleton(); ~BMP3XXSingleton(); public: - // Create a singleton instance (with lock for thread safety) + // Create a singleton instance (not thread safe) static BMP3XXSingleton *GetInstance(); // Singletons should not be cloneable. From b71e12c5e51bc6f57ffa23c35f2310605b7c78c1 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 31 Aug 2024 15:04:35 -0500 Subject: [PATCH 286/305] Phone admin work (#4599) * Don't throw an error on the sessionkey admin tag * Throw an error on packet sent to 0 --- src/mesh/PhoneAPI.cpp | 3 +++ src/mesh/Router.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 0a9bb5b10..0ca89b1ef 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -259,6 +259,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.config.which_payload_variant = meshtastic_Config_security_tag; fromRadioScratch.config.payload_variant.security = config.security; break; + case meshtastic_Config_sessionkey_tag: + fromRadioScratch.config.which_payload_variant = meshtastic_Config_sessionkey_tag; + break; default: LOG_ERROR("Unknown config type %d\n", config_state); } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 804761f4e..d8e578db1 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -165,6 +165,9 @@ meshtastic_QueueStatus Router::getQueueStatus() ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) { + if (p->to == 0) { + LOG_ERROR("Packet received with to: of 0!\n"); + } // No need to deliver externally if the destination is the local node if (p->to == nodeDB->getNodeNum()) { printPacket("Enqueued local", p); From e45a358de07ce438556dc23127e315c9a0bcd7bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 09:20:10 -0500 Subject: [PATCH 287/305] [create-pull-request] automated change (#4594) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 28492e88e..5f7c91adb 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 28492e88e515aabf5c886dd23631518d6dee82d7 +Subproject commit 5f7c91adb97187e0cb2140de7057344d93444bd1 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index a711da806..9d7ff74a1 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -350,6 +350,9 @@ typedef enum _meshtastic_MeshPacket_Priority { /* If priority is unset but the message is marked as want_ack, assume it is important and use a slightly higher priority */ meshtastic_MeshPacket_Priority_RELIABLE = 70, + /* If priority is unset but the packet is a response to a request, we want it to get there relatively quickly. + Furthermore, responses stop relaying packets directed to a node early. */ + meshtastic_MeshPacket_Priority_RESPONSE = 80, /* Higher priority for specific message types (portnums) to distinguish between other reliable packets. */ meshtastic_MeshPacket_Priority_HIGH = 100, /* Ack/naks are sent with very high priority to ensure that retransmission From 56223710b502727d7a8da18c551dbbc3859c9c12 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:29:53 +0200 Subject: [PATCH 288/305] More priorities for different types of MeshPackets (#4606) --- src/mesh/MeshPacketQueue.cpp | 14 +++++++++++--- src/modules/NeighborInfoModule.cpp | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 8e5eedc87..6581b1ce4 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -44,11 +44,19 @@ void fixPriority(meshtastic_MeshPacket *p) p->priority = (p->want_ack ? meshtastic_MeshPacket_Priority_RELIABLE : meshtastic_MeshPacket_Priority_DEFAULT); if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { // if acks/naks give very high priority - if (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP) + if (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP) { p->priority = meshtastic_MeshPacket_Priority_ACK; - // if text give high priority - else if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) + // if text or admin, give high priority + } else if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP || + p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) { p->priority = meshtastic_MeshPacket_Priority_HIGH; + // if it is a response, give higher priority to let it arrive early and stop the request being relayed + } else if (p->decoded.request_id != 0) { + p->priority = meshtastic_MeshPacket_Priority_RESPONSE; + // Also if we want a response, give a bit higher priority + } else if (p->decoded.want_response) { + p->priority = meshtastic_MeshPacket_Priority_RELIABLE; + } } } } diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 2de4374e6..218fb8801 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -107,6 +107,7 @@ void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies) // because we want to get neighbors for the next cycle p->to = dest; p->decoded.want_response = wantReplies; + p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; printNeighborInfo("SENDING", &neighborInfo); service->sendToMesh(p, RX_SRC_LOCAL, true); } From 7d2f3a34253c0b74e0c542d56939b5e3188f234f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 1 Sep 2024 11:29:34 -0500 Subject: [PATCH 289/305] Hello world for MeshTestic (#4607) --- .gitmodules | 3 +++ meshtestic | 1 + 2 files changed, 4 insertions(+) create mode 160000 meshtestic diff --git a/.gitmodules b/.gitmodules index e6f376a0b..7c54ad513 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "protobufs"] path = protobufs url = https://github.com/meshtastic/protobufs.git +[submodule "meshtestic"] + path = meshtestic + url = https://github.com/meshtastic/meshTestic diff --git a/meshtestic b/meshtestic new file mode 160000 index 000000000..31ee3d90c --- /dev/null +++ b/meshtestic @@ -0,0 +1 @@ +Subproject commit 31ee3d90c8bef61e835c3271be2c7cda8c4a5cc2 From 24501c30c0b64081fe8e7eb36ed4ef236942125e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 1 Sep 2024 11:33:41 -0500 Subject: [PATCH 290/305] Update and rename test_simulator.yml to tests.yml --- .../{test_simulator.yml => tests.yml} | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) rename .github/workflows/{test_simulator.yml => tests.yml} (64%) diff --git a/.github/workflows/test_simulator.yml b/.github/workflows/tests.yml similarity index 64% rename from .github/workflows/test_simulator.yml rename to .github/workflows/tests.yml index 1d20657a0..f58b38ac9 100644 --- a/.github/workflows/test_simulator.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: Test Simulator +name: End to end tests on: schedule: @@ -55,3 +55,37 @@ jobs: name: PlatformIO Tests path: testreport.xml reporter: java-junit + + hardware-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Upgrade python tools + shell: bash + run: | + python -m pip install --upgrade pip + pip install -U --no-build-isolation --no-cache-dir "setuptools<72" + pip install -U platformio adafruit-nrfutil --no-build-isolation + pip install -U poetry --no-build-isolation + pip install -U meshtastic --pre --no-build-isolation + + - name: Upgrade platformio + shell: bash + run: | + pio upgrade + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: latest + + - name: Install Dependencies + run: pnpm install + + - name: Setup devices + run: pnpm run setup + + - name: Execute end to end tests on connected hardware + run: pnpm run test From cd16b7b00a090ab2ef90dcafa408b96547b45ebe Mon Sep 17 00:00:00 2001 From: Riley Nielsen Date: Sun, 1 Sep 2024 18:26:08 -0700 Subject: [PATCH 291/305] fix display of degree symbol (+ other symbols) --- src/graphics/Screen.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 1b6f541be..bb5d947e3 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -309,7 +309,7 @@ class Screen : public concurrency::OSThread static char customFontTableLookup(const uint8_t ch) { // UTF-8 to font table index converter - // Code form http://playground.arduino.cc/Main/Utf8ascii + // Code from http://playground.arduino.cc/Main/Utf8ascii static uint8_t LASTCHAR; static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters @@ -322,13 +322,19 @@ class Screen : public concurrency::OSThread uint8_t last = LASTCHAR; // get last char LASTCHAR = ch; -#if defined(OLED_PL) - - switch (last) { // conversion depending on first UTF8-character + switch(last) { case 0xC2: { SKIPREST = false; return (uint8_t)ch; } + } + + // We want to strip out prefix chars for two-byte char formats + if (ch == 0xC2) + return (uint8_t)0; + +#if defined(OLED_PL) + case 0xC3: { if (ch == 147) @@ -365,11 +371,6 @@ class Screen : public concurrency::OSThread #if defined(OLED_UA) || defined(OLED_RU) - switch (last) { // conversion depending on first UTF8-character - case 0xC2: { - SKIPREST = false; - return (uint8_t)ch; - } case 0xC3: { SKIPREST = false; return (uint8_t)(ch | 0xC0); From 234e652a0753e56bdace676ebe28e44f9600cb27 Mon Sep 17 00:00:00 2001 From: Riley Nielsen Date: Sun, 1 Sep 2024 20:40:56 -0700 Subject: [PATCH 292/305] Fix switch --- src/graphics/Screen.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index bb5d947e3..d5a2879cd 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -335,6 +335,7 @@ class Screen : public concurrency::OSThread #if defined(OLED_PL) + switch(last) { case 0xC3: { if (ch == 147) @@ -371,6 +372,7 @@ class Screen : public concurrency::OSThread #if defined(OLED_UA) || defined(OLED_RU) + switch(last) { case 0xC3: { SKIPREST = false; return (uint8_t)(ch | 0xC0); From 719faf4f970eaddf9feb97ed36110a507a0ad805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 2 Sep 2024 10:16:32 +0200 Subject: [PATCH 293/305] trunk fmt --- src/graphics/Screen.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index d5a2879cd..021b11bda 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -322,7 +322,7 @@ class Screen : public concurrency::OSThread uint8_t last = LASTCHAR; // get last char LASTCHAR = ch; - switch(last) { + switch (last) { case 0xC2: { SKIPREST = false; return (uint8_t)ch; @@ -335,7 +335,7 @@ class Screen : public concurrency::OSThread #if defined(OLED_PL) - switch(last) { + switch (last) { case 0xC3: { if (ch == 147) @@ -372,7 +372,7 @@ class Screen : public concurrency::OSThread #if defined(OLED_UA) || defined(OLED_RU) - switch(last) { + switch (last) { case 0xC3: { SKIPREST = false; return (uint8_t)(ch | 0xC0); From e543b61dd8850e300c193167cb45bcb95da0856b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 2 Sep 2024 10:23:31 +0200 Subject: [PATCH 294/305] trunk fmt --- src/detect/ScanI2CTwoWire.cpp | 4 +-- src/modules/Telemetry/Sensor/BMP3XXSensor.cpp | 28 ++++++++----------- src/modules/Telemetry/Sensor/BMP3XXSensor.h | 16 +++++------ 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 76ca42b7f..21e7ca8ac 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -268,7 +268,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; default: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // GET_ID - switch(registerValue) { + switch (registerValue) { case 0x50: // BMP-388 should be 0x50 LOG_INFO("BMP-388 sensor found at address 0x%x\n", (uint8_t)addr.address); type = BMP_3XX; @@ -279,7 +279,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) type = BMP_280; break; } - break; + break; } break; #ifndef HAS_NCP5623 diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp index d948cfe38..399610613 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -4,15 +4,14 @@ #include "BMP3XXSensor.h" -BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX"){} +BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX") {} void BMP3XXSensor::setup() {} int32_t BMP3XXSensor::runOnce() { LOG_INFO("Init sensor: %s\n", sensorName); - if (!hasSensor()) - { + if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -29,8 +28,7 @@ int32_t BMP3XXSensor::runOnce() bmp3xx->setOutputDataRate(BMP3_ODR_25_HZ); // take a couple of initial readings to settle the sensor filters - for (int i = 0; i < 3; i++) - { + for (int i = 0; i < 3; i++) { bmp3xx->performReading(); } return initI2CSensor(); @@ -41,8 +39,7 @@ bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) if (bmp3xx == nullptr) { bmp3xx = BMP3XXSingleton::GetInstance(); } - if ((int)measurement->which_variant == meshtastic_Telemetry_environment_metrics_tag) - { + if ((int)measurement->which_variant == meshtastic_Telemetry_environment_metrics_tag) { bmp3xx->performReading(); measurement->variant.environment_metrics.has_temperature = true; @@ -53,10 +50,10 @@ bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.barometric_pressure = static_cast(bmp3xx->pressure) / 100.0F; measurement->variant.environment_metrics.relative_humidity = 0.0f; - LOG_DEBUG("BMP3XXSensor::getMetrics id: %i temp: %.1f press %.1f\n", measurement->which_variant, measurement->variant.environment_metrics.temperature, measurement->variant.environment_metrics.barometric_pressure); - } - else - { + LOG_DEBUG("BMP3XXSensor::getMetrics id: %i temp: %.1f press %.1f\n", measurement->which_variant, + measurement->variant.environment_metrics.temperature, + measurement->variant.environment_metrics.barometric_pressure); + } else { LOG_DEBUG("BMP3XXSensor::getMetrics id: %i\n", measurement->which_variant); } return true; @@ -65,18 +62,17 @@ bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) // Get a singleton wrapper for an Adafruit_bmp3xx BMP3XXSingleton *BMP3XXSingleton::GetInstance() { - if (pinstance == nullptr) - { + if (pinstance == nullptr) { pinstance = new BMP3XXSingleton(); } return pinstance; } -BMP3XXSingleton::BMP3XXSingleton(){} +BMP3XXSingleton::BMP3XXSingleton() {} -BMP3XXSingleton::~BMP3XXSingleton(){} +BMP3XXSingleton::~BMP3XXSingleton() {} -BMP3XXSingleton* BMP3XXSingleton::pinstance{nullptr}; +BMP3XXSingleton *BMP3XXSingleton::pinstance{nullptr}; bool BMP3XXSingleton::performReading() { diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.h b/src/modules/Telemetry/Sensor/BMP3XXSensor.h index 77b434caa..79939c8d8 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.h +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.h @@ -7,21 +7,21 @@ #define SEAL_LEVEL_HPA 1013.2f -#include -#include #include "TelemetrySensor.h" +#include +#include // Singleton wrapper for the Adafruit_BMP3XX class class BMP3XXSingleton : public Adafruit_BMP3XX { -private: - static BMP3XXSingleton * pinstance; + private: + static BMP3XXSingleton *pinstance; -protected: + protected: BMP3XXSingleton(); ~BMP3XXSingleton(); -public: + public: // Create a singleton instance (not thread safe) static BMP3XXSingleton *GetInstance(); @@ -41,11 +41,11 @@ public: class BMP3XXSensor : public TelemetrySensor { -protected: + protected: BMP3XXSingleton *bmp3xx = nullptr; virtual void setup() override; -public: + public: BMP3XXSensor(); virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; From 2f0c19ebeac0054cb8b8f417b9f2952d18532fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 2 Sep 2024 15:06:06 +0200 Subject: [PATCH 295/305] - use setRfSwitchTable - ditch Godmode - fixes Signedness Error in Loop. - add V3 factory erase for 7.3.0 softdevice --- ...astic_nRF52_factory_erase_v3_S140_7.3.0.uf2 | Bin 0 -> 122368 bytes src/mesh/LR11x0Interface.cpp | 14 +++++++------- src/meshUtils.cpp | 2 +- variants/tracker-t1000-e/platformio.ini | 2 +- variants/tracker-t1000-e/variant.h | 1 - 5 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 bin/Meshtastic_nRF52_factory_erase_v3_S140_7.3.0.uf2 diff --git a/bin/Meshtastic_nRF52_factory_erase_v3_S140_7.3.0.uf2 b/bin/Meshtastic_nRF52_factory_erase_v3_S140_7.3.0.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..73537a7358bf4e217e1218ceeb4ce7d317cdfc13 GIT binary patch literal 122368 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 v_xzx8xj)eEf~HJYRP+zn@z1U!k9wBmfPRg(; bool LR11x0Interface::init() // FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option if (res == RADIOLIB_ERR_NONE) res = lora.setRegulatorDCDC(); -#ifdef TRACKER_T1000_E -#ifdef LR11X0_DIO_RF_SWITCH_CONFIG - res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG); -#else - res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0); -#endif -#endif +// #ifdef TRACKER_T1000_E +// #ifdef LR11X0_DIO_RF_SWITCH_CONFIG +// res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG); +// #else +// res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0); +// #endif +// #endif if (res == RADIOLIB_ERR_NONE) { if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate res = lora.setRxBoostedGainMode(true); diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index 99fcd2a57..c6f2c69b4 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -68,7 +68,7 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes) bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) { - for (int i = 0; i < numbytes; i++) { + for (uint8_t i = 0; i < numbytes; i++) { if (mem[i] != find) return false; } diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini index 1db57ca29..dfc72f3f0 100644 --- a/variants/tracker-t1000-e/platformio.ini +++ b/variants/tracker-t1000-e/platformio.ini @@ -4,7 +4,7 @@ extends = nrf52840_base board = tracker-t1000-e ; board_level = extra ; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e -build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -DRADIOLIB_GODMODE +build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index 63c2a76dc..470edd08c 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -102,7 +102,6 @@ extern "C" { #define LR11X0_DIO3_TCXO_VOLTAGE 1.6 #define LR11X0_DIO_AS_RF_SWITCH -#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 #define HAS_GPS 1 #define GNSS_AIROHA From b526a3ad53b6584a702e56769be1ecf03210ba5a Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 2 Sep 2024 17:51:02 -0500 Subject: [PATCH 296/305] Own node should be favorited and have zero hops away (#4618) --- src/mesh/PhoneAPI.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 0ca89b1ef..30af9d7b0 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -194,6 +194,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) auto us = nodeDB->readNextMeshNode(readIndex); if (us) { nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us); + nodeInfoForPhone.hops_away = 0; + nodeInfoForPhone.is_favorite = true; fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag; fromRadioScratch.node_info = nodeInfoForPhone; // Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS From cdea602181b283c87c55908999f7242054edc723 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 3 Sep 2024 09:08:02 +0800 Subject: [PATCH 297/305] Remove unused define (#4620) Neither RF95_DIO2 nor LORA_DIO2 are found anywhere in the code. --- src/RF95Configuration.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/RF95Configuration.h b/src/RF95Configuration.h index 66b2dcf86..5c525814b 100644 --- a/src/RF95Configuration.h +++ b/src/RF95Configuration.h @@ -3,5 +3,4 @@ #define RF95_RESET LORA_RESET #define RF95_IRQ LORA_DIO0 // on SX1262 version this is a no connect DIO0 #define RF95_DIO1 LORA_DIO1 // Note: not really used for RF95, but used for pure SX127x -#define RF95_DIO2 LORA_DIO2 // Note: not really used for RF95 -#endif \ No newline at end of file +#endif From 3bb1cb8f1de3eb56e4dff01c0592651c50155064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 09:25:33 +0200 Subject: [PATCH 298/305] Update Erase tool for legacy softdevices to V3 --- bin/Meshtastic_nRF52_factory_erase_v2.uf2 | Bin 127488 -> 0 bytes ...astic_nRF52_factory_erase_v3_S140_6.1.0.uf2 | Bin 0 -> 126976 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 bin/Meshtastic_nRF52_factory_erase_v2.uf2 create mode 100644 bin/Meshtastic_nRF52_factory_erase_v3_S140_6.1.0.uf2 diff --git a/bin/Meshtastic_nRF52_factory_erase_v2.uf2 b/bin/Meshtastic_nRF52_factory_erase_v2.uf2 deleted file mode 100644 index 8a83bc8ecff5b9cc00839b16f338deaf071f275b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127488 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
5H9oZFl=>o9U)+aRT>E-+rTFPk&cG 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 diff --git a/bin/Meshtastic_nRF52_factory_erase_v3_S140_6.1.0.uf2 b/bin/Meshtastic_nRF52_factory_erase_v3_S140_6.1.0.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..e44d9da2a7934c1230927695ce899dc928faa452 GIT binary patch literal 126976 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 literal 0 HcmV?d00001 From 85986459310bf55a18dbbd999b5c6dfc4805ced8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 2 Sep 2024 15:06:06 +0200 Subject: [PATCH 299/305] - use setRfSwitchTable - ditch Godmode - fixes Signedness Error in Loop. - add V3 factory erase for 7.3.0 softdevice --- ...astic_nRF52_factory_erase_v3_S140_7.3.0.uf2 | Bin 0 -> 122368 bytes src/mesh/LR11x0Interface.cpp | 14 +++++++------- src/meshUtils.cpp | 2 +- variants/tracker-t1000-e/platformio.ini | 2 +- variants/tracker-t1000-e/variant.h | 1 - 5 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 bin/Meshtastic_nRF52_factory_erase_v3_S140_7.3.0.uf2 diff --git a/bin/Meshtastic_nRF52_factory_erase_v3_S140_7.3.0.uf2 b/bin/Meshtastic_nRF52_factory_erase_v3_S140_7.3.0.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..73537a7358bf4e217e1218ceeb4ce7d317cdfc13 GIT binary patch literal 122368 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 v_xzx8xj)eEf~HJYRP+zn@z1U!k9wBmfPRg(; bool LR11x0Interface::init() // FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option if (res == RADIOLIB_ERR_NONE) res = lora.setRegulatorDCDC(); -#ifdef TRACKER_T1000_E -#ifdef LR11X0_DIO_RF_SWITCH_CONFIG - res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG); -#else - res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0); -#endif -#endif +// #ifdef TRACKER_T1000_E +// #ifdef LR11X0_DIO_RF_SWITCH_CONFIG +// res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG); +// #else +// res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0); +// #endif +// #endif if (res == RADIOLIB_ERR_NONE) { if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate res = lora.setRxBoostedGainMode(true); diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index 99fcd2a57..c6f2c69b4 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -68,7 +68,7 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes) bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) { - for (int i = 0; i < numbytes; i++) { + for (uint8_t i = 0; i < numbytes; i++) { if (mem[i] != find) return false; } diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini index 1db57ca29..dfc72f3f0 100644 --- a/variants/tracker-t1000-e/platformio.ini +++ b/variants/tracker-t1000-e/platformio.ini @@ -4,7 +4,7 @@ extends = nrf52840_base board = tracker-t1000-e ; board_level = extra ; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e -build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -DRADIOLIB_GODMODE +build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index 63c2a76dc..470edd08c 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -102,7 +102,6 @@ extern "C" { #define LR11X0_DIO3_TCXO_VOLTAGE 1.6 #define LR11X0_DIO_AS_RF_SWITCH -#define LR11X0_DIO_RF_SWITCH_CONFIG 0x0f, 0x0, 0x09, 0x0B, 0x0A, 0x0, 0x4, 0x0 #define HAS_GPS 1 #define GNSS_AIROHA From 190c7ecdd4c0701233d9a649502542647ad91e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 09:25:33 +0200 Subject: [PATCH 300/305] Update Erase tool for legacy softdevices to V3 --- bin/Meshtastic_nRF52_factory_erase_v2.uf2 | Bin 127488 -> 0 bytes ...astic_nRF52_factory_erase_v3_S140_6.1.0.uf2 | Bin 0 -> 126976 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 bin/Meshtastic_nRF52_factory_erase_v2.uf2 create mode 100644 bin/Meshtastic_nRF52_factory_erase_v3_S140_6.1.0.uf2 diff --git a/bin/Meshtastic_nRF52_factory_erase_v2.uf2 b/bin/Meshtastic_nRF52_factory_erase_v2.uf2 deleted file mode 100644 index 8a83bc8ecff5b9cc00839b16f338deaf071f275b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127488 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
5H9oZFl=>o9U)+aRT>E-+rTFPk&cG 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
Q01$vUkfbqhs9=nV;fNT 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 diff --git a/bin/Meshtastic_nRF52_factory_erase_v3_S140_6.1.0.uf2 b/bin/Meshtastic_nRF52_factory_erase_v3_S140_6.1.0.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..e44d9da2a7934c1230927695ce899dc928faa452 GIT binary patch literal 126976 zcmd?Sdt6l2-ao$f+=uIMQ4vsw8Bk=rjABJEe0zsGTEePnhY1RA-|g8wQx)d+j~ol%41I`@O#3zXV=0 zvoCAy%X)v-=f2juhn?+n?~>oYO7w(~EEypTe(@*ZlN9^L0YVh|nD1re5q$oHPYynL z_&kQsum6^7%jfd0L$^Oe`5XDq{CI=UJ>K{)m++?~a1H+(xdle|yIqI>3JHJOvxo8T`dK$O znYXVxhu{2E(xPipE;bg#A5x@;G$|zj03Q z;4_}9|E41#UH4iMFfL;!vSem9_g)tXlyj#AVm@eD#I=E}Otx1oCN4SWZP%5{a#yhB z@S*oDV@|&-?LGZwCxmW=ydv3VU?;J&*az76QcDlXtM6s2t&vtT^Pi4e+eyW)@Q*vl z3Qkb$*l(M~a;YTtb52>``~&~D99weEP78PX^RQi(<88Y-6+VYk_S<4x4ztU{?_%#` z$6H&%@3#8#IsGSbwK_*|`)NU8uCX+nrq5QK6K>sQIpC>xTF~f575vD!6)DuLEmu2DUx264LWySe$ zQlY3ARrvo=!e8Zu|5{r}bsYNP4)n2S9f#4Ir*ewwc?UzPADpPDR&>g>A=Sz*Mzg0= zu2Fl?o&>c2=vbT*%FVNd=WexVfOhr?#vm*A@0uhczJ!ej-W2WgLlo^LXF{qJ)yh|b z&_6>qO|X(_dmTit718bt|DuCrOUUhVoDmedy_Q!`&$X53_*nN@wxO>oH9Jzy2pSUG zvV+YD*H|rV$TnZTpfAA|vaL8L#k#;cjPmQ98b!z^Vp`K(P=_n((jybQSJjd7GaZbP z6EybL)T~2yvCBj{^Z^|vc8@Cj|0LnB_QL<+Itc~Z=cFxFx++aA_PGNB-Bv7o7ya)| z;g|id9)0jj^hMea73_%)V%}5_j^OK4L5#j-s!fWnP>p|AtX5fJv@H`94@Bf{x2!&) zGx~R#eTdtR|)`hkvz^C&?kEj5dSqL{5bz{WRqb+C4hM z5>{Iw<9`~IO6W|n+;JhYOgGuT%c$PQs@s{H8N;otc8`odGuV(ajx*ZmJr|r{jJVq> zvehJVAbP*?xSx~zhSu&HRruQ^{HZoQtpCjk4CjKxen{IxTU~vq>W4t+-i%6Dr|x{- zPxYE^@=(_8>|e7YZ+=h+d$2j{d7NjA%8K{9WPyPqYN0JePw7Xxj;hM9`b183I5Aaz z{i8F3m-+tHd5w?De?>>hHYWZS4pfv}f~}Hz1sltafmWEo6AkD|o6AmRAw$-=d(+(Q zhm>8>n!Ma)kz={K+@~W$xPJNj;XzSWQY%r*_ZPBD*x=KQKSiT%N`y6|w}VByXjMSO_$JKNkWlQrDx zYyB>(X1R178MFX7IFb+lu-jV);F~Jy$z*?)nWEmCOR3~f3OwaILn>C+5Y=81X>jPO znfOR92;bv5-9GhR+WU78)gO|3wx4y6D*SUL{C&LeC$X0t+xv88V#?q(vZ<_c4}Zpo z{{a2wM+Y<6%!btS%_>&h%aUlu@JWZ(Qp}dJ6IfMOv^EX;Lnd0ba7fMC4pD7_ z?q#|&8%iQ4IacK|@iOipzArfvSkT1_(+U-QzCu5(!?E86%RygZ(QrRtOQ9>Z$hi#` zhAXz5aWGS)(rWfR>^$5%V*|Ur-nUC3S5)|R`N@xWDCaSG3UCTWLt;*K_?va@_iHca zF$oVv?#TXA_U{BktDK)C)AY+O2Ve3FB>a@3dF(dJj}8(`_{&8G+xc9`tsD67I{PnA z!r#{mf6a>V)-p?Mi;it@UA>}mg#_ud&#h?BdnEgWhM>ni=FSFRCj7lcHRA8mB4S{S zB<6UB#t0d&W(+gfQ{ckqa?c8Svp#QGc!2dC*OeZ*Ys@3hS#&3IwBO|^`AN#VgebG7 zU)eJu*&;+`(z54rwNK40!*)tPQ!_^-GBAC>U;^TI#0oba9Y@uJqHm?wizq|$Pt z?VFD975S0Ccm=~PiG0a%O7JVYgFOTePx${S>dg6Cp49cbqQ`T{Ov2w)1pIe=EktH9 ziJIHrwY2j8UG%Q&D$gYN@GlL>%x`wuWsR<@S5FD%_H((W9Q77u*EFJJzXrnj@brXg zUt$@yGHgd@K)?~#WnSjeeYYNzaIdyKPlo=RDNNDPKJ1g6Buvqh8H68KMC+<}M*V4* z%%{SVacMoV^OZ%udqvGsN6HtmaUvDG?jo^IIefa@^XW5&w7)M|e+M^Mz%@wdiHu95 z3jfC>{QbS~UpjHPzs=ASPk)L^F=>d;)8;9 z{_WN`oO;`LxlC?|^`NWkGUE!4S5#R9#k^A;ej(c|igL9huzgn^6VJ@uU^&*|KU>57 zUQ7A$?}V64if5eV-;QrO6tgQVYr0>n{YD6@C%Lfx%q14zu1Fc^g0b^6iWD(SIWg#2 z^z3qX?|k5y#BZ>l5hBg73ub@Op*4i+nRs2)7aeNz7lNOT=qHHUY*gN#FX~`XBz6bY z4b!K*%RO&*mqzbn|PeJ+=xPJ3!) zSWvEUO6(oBi#TPy0uE=v@e_SF=BNRUwIP*00$HeYKwVwW{{miRBa`tQeQR2zp z^pn`S-{_62QigluZ&Rc6RKC^pxps9jzo5pwT{11D$2&ofx~@5aiQOyfBnnWmTb+;d z5#%YSdV6P1wfwJN&CDe;pXP?CLE=`U3jfC?`~$u4fBNJwuTF4@yjp&6gjeU~{-5${ z3wJ%IPCjvsE{6GZCMaV~x1sKTyJkC)a^K*`Bn8!O*M#ez&RL9)PQe8bLcXHkr;qp%6_kpX6 zXI#40=PkN67WyLLCy^z>WV5oL@L}LelgX(L-&Vo zDd8XFg}yq_kx%CFYg9%a*2YjZb1$L=X2WAEZUt<320FIR6budXWBa&k8YZ*NauOP5Sik~kn5qC7 zccjamxRL6Zc9x~_q{Qyjx+|iN8C&!hQOArP)-laYVojGulbX`X?}3&nh%)?4pUAn? z*zX0skaM||PATV!&rxnIfsG1@^|Q_=fL$)?nJOkP(29DdD&SSY3X@Q>6px9{rG`2Hcq=j1ysm)M`;(VupA+Mx-!u7t5tZ}P9Elz?9J^h>PT(~o{> zul;VsUYjhnJE&-~NJU=_Q<3TuFDg22zeYu%p>Rq=i$OzsM`-A`>@{yKV3>-o*((wq z4cjXc9m#H|RCEjUL*=Vipa&*(71?irCiquT6C73eKPllK>V-e-c0H2_3II=i2D+fc zQ5%K-#8Fq}{=ae5|Ba(a`rt45$-)!Y=wO(e4E25r59FHe=n;+Z|0X}7lyEaLNY->O zt{qkQ|5?I+j2Hf!E>Iik-xtZ0^-eP7p^NB=Pcs*X<263~{@;kZQ{C`0L~5oPNL1t= zAN~Qv?@4m3Vags76Y$GMbS4M6P6gOCYR}LF%`^g0{wBq7~d_3K9(P~`E068g|{F<$?^?jU?V2+C+t4e z#{|8d8D0LrrzHHxdf}gRxv(HIqFqWZD`2^`T{g-ZY^)IJW{?;+SF`jq%(Y$I7-Yf)8$}cb}Wq{m2pR z(ExR(*@!HK`rcM&u=#pwbr&%-Cj121N({@yTAvQlT7PxWm~JM^@GFN5w&8o6A1NKj zO7|!b?@{Kt2XnJx-m8}XK6$Oi=S2!+C*Zkq7a{IMpXwp8#+uO;|No1G|2QxF|56u~ zdrSzmr$q*HdRt9yaSkbe&bA_VkBIo1?myKTZ98&lo{kT4aA=>=+vGVCJ*?@zuP*x4 zZ-gi7j|<)Q$vmOnT)EA}y1cH4@`vga zJ6}*FM&~Q2{3kCz;K61Q;`85gO2h~kjc9GNYiUGyHY@RtT>_8T(oYWmu2=j%Ks-9K zHt-Yr8~Dj9Kd8y`_=p_nme)X$>+EK5o;BSqwcgj;-uES90$=LU6gTYOb@8793I8y> z@v!|*OsBr4e0PCpd*9B@5qZD~=wV0B+{8)kP58NprnCZ)Cg(RD=1lO~C}Z`6O~*T8 zGMRW|g=W*la&0-qhncLm4RebOj`G@99GYtCsbx%t70GQ{%4ympKJ%rE_V5=S6SmO^ z-gi!l#y7&>+qVn<-FjRQ%4GZ+Speb@@rdjsj^Bv03(6SdZKCuzF8CFJ=Gu{2_Th0s z_aWi?V2vhrKVGL~vz;Wy8|hL@V)yd84e&BQR$Gkk+}dUE3&txmoW{Cc>x9CfrZK-wvhpP!zR}~kM>2RC-s_e_mOm{I`c1AjdxWcvD*T_8 z@DKOGU&{};r>yYi>Ctu3uTh>pg(p;Nnl1A5M9R~3i8%DCF2eS+Q&*)H>-?jZ zdX19!V!Ko;+f$41$Lvx)!nfGhLQnj_z8>F)>=Cw0PRjM}y??o*jn&rsQcEaTVF~6K0~5biNYejJ zlr}GN)aL&uXyXOYhWg_DSreoh|a6TxbREtKA1^gl86u zGFil!S{9CTEbd&0J12HOT=%tzYo>Vp+iLH?Tj};|Ak&6=_2OHdI*?4?N~Wt0t?-Kv z8(sOo&q(-3df^{j=4TAr16=b%%4e}-D#+w<6=NZ1L-*;~@h?Y+sHTgkzI#9;qB?=o zil~kUs^fs_m=RPHyA$guJtcO}tE0%~0NKRun;?_7U68P~j9~jcu+{99kR3!;hx_Ua zKz7n^AnPY08(eqB=^djO&ON2J=s$A&S!Ad$c%z=Vxr~jQkdsx+{=EFHEKRwxqNHIW zi_9W&TJST+;p@@gK8OErPMaSzxLIwQZ8XCxuTR0+-lw&5Y>aWZ9*OEFy+$BV8y(Hmb^9}*cntJ-2!R!uyzh|6^P!?qkCMmi?(FBWK$3Wy|&&($Flqj z*5o2K2ome_ep-V*%c#QtSqcA%Uied+{Zmhik*x+~WYhNI#P(X#{cIgLy8+7tM^``eNM;v}=S4W2|nBiaXw5=}poNbqlW@o)jwZSNA z+e=s9>A8AFox1vd+Zmg07vnq5I??*Ma~PEh%QMjLf~bDCTvomS`W=0$=REb|>)?yl zQR>=e`CJG!k4Kc5kgtVUGqqmV+P)T|^*d52;%mAmpe!-t9HBO>mVa)L)&ni$7$NeU zQHB2+3I9o6_*Z(`Nn2;i{Yp^SX&<9n-@z`0c6>SdnePM+c6=@J>tV&uV#lNRnEDT; z1k*M*N90zfOqVk3v&qZJz-gICKQ?&lE}|DtbdIkI5_Pn*wc6?s@Cuq~n%Esu_Xk_9 zZ7XEQHjDx)LKNqDOCa<*e^IZyE&cYi+eN+b(SfWluD*SntXU>F85UjBlmwT$fLUiLU)-*#=NYn2&fC*8oG5X zbeFKQpnanXf4hXg-V1-mn8}8`JZ`JI_de)XZ#aA_Y3o*6W^sO1XkXoW%QVROILP@} zQO>W2_Ezm_`Ik8=1)c)d<-mH+MT!K~)+h=gkp1qf&(=N1&0>KBbTuP%wM^E4I~j4O z#O}hn@11$J&vCbX_}1wETa@oquG8^dbez|49>N%QX<>Qc&xMNkhWZezPj0-`k5yI} zc(NcA96*7L;1X6{@d$USgJEXa&gZDMDqjsOE02HdEr0b5#W*UUqHUkYYT*YPiRwbyAK$ycB|N|_i6jl+-yc{ zy~k+WQSSjAZz;+6^kN5?(qcOr% ze~`-fqa4jDpEV-gA?;P(`{UX0eM7zZF4e@&GX^m}&~DEw+*^1FRN!k3u>$|x=bfj) z$0e;`zs1t#>(L5kLMzy;jXb2PejnD}Ze$A*eV|7>a7n)75$#}9;a@D_Z}7sOq7u|c zF@0Cmj)KA8-xp|$UBl7oX*05AHY?%SD%PO>8AyejyiquM`j>S-gXcY%%JC(VpN8IV zr>Axz#^?>p@h?O@qA{S^T_d?E-J@Cvp7cOxI~xKzeJw~ z&z=vuNTZVMaF@a-rW~}PCA@!;k_~?g9_gDHLlhf>cO6yumq_>DWa_# zkxTvMxsp#lnQWN{dA>;Ow|3ZI-=oLII1LyJL}_H|uW00+BItNlvc=$`keMEgkJop)~_0fBWkzist5kvgkO=DIO;0dD&|FshS(O&pd{lC|)i$)K91blk4gf}P9c01}Y zjKu8XDR=epJ%HBL`+IolS5&XRmux2R1K!6D_WlHUK{2SPWyH2d49HE(({`r!IclFx z{k*6l-DSIYmom>?m^q4huUP(Z(!0h!yTaf@xyJ$-a{7sZB3&%jTGRb!k$Zq|*wZ7& z(UIT(fpaYX73WynPicsjr(9!H;s2b3e~cIYbS#v^u43;e?=QU2c0Zw?*M-+?J8Umld@&MC%N??)VHZ%l;o^b&ZOh?N{I{dc7H8XJxzd(PuhRPO z!pa=Q>=o9z)^*{<;cZz}%P%@V>R|rzp>s*rPTQue{m$z2SJGY)?FdU>WqNhmHqpMA z-#4o8e_q0WvKRhz#03*yi2g%usTjof6EuJ3g7?pbCxG@gQrpb1;h4l zghzKA7pB=`c#=XjJ;iMk*+keKRM&%bI*0mpeD@Ik!J#)S@suAxo>iq3(|E+oWb;yR zk69v@s)F_5ge*(EK;L#$;s1h!{}eC$6Eq9hxgyesA>-Xw{c97u6|mxfc0MuAgw1vz zI|E3520BRW?yY?f{`D__WlkS6Wv$4-kQ$^R0mf;L$LV8 zZx<5D*a|gD{5koSpNHONasD29 z`=u32>1~gA|51g1sf2&57yeBA>l|(M3aUTNc$Itm`tFj`i4`#iH@w)%XSHB^b$@;h+zBy!uy=*_a z{O`F`0=;VAv0}aGpKB7RUT?4&S7a|=n%kyI;-{3*vJ*;ld9-A?y*>LxhjK}StLKW+ z6%s;BNqm3tzjEGnb@ec=F@Gl0lXy~sYux;1mqw0}s8`~x2m4E|9PQG}-|Z|3Y~?Q% z-<2D)d@6gaL-q(pAKbuy*Tw(WN%&9o!XMGN%s*yn5kE89F08;<4I?}~Eg@(fzngYt z8^p-jhse|`XfWEUa!Fh^_cgvB;3D#f>25BTO&PJ^r;BCxi&=d?U}WEns5#XE$hn#k z%)~3o=2ZA^LY{~AT7F2fm>+TyGk#27!I=Ep0-(?Pw9OE&a=Vxt@-gO~nz2n$@x7R@ zam2YWN6B{$wlAl2@U1<{MrmhF_lmkw9KSHQV+B2a633T0PivI?*}?W~dgODv!S;x= z9ixz)gS(wOa+Un~L8I+eXFF>B&)^LV|MmGF>m~f-yzn>LwmRn^4~FJ|X!mFlN&R4e zdU0lWboRd%BL_qS4ICML$N{m_{EWfgRx#7!RmV*u_CYEeFJ^&Y&KHfr2F@*N{0|pH z`mQ4{OIaX;y=Mn#&d-|eWyk_qfcp{txdDT1_+F{SVjc*}OLy7m$>+$clkUUZqL}xZ znAbO4V@9#Upr!V~%Hb@K!QLl4wK9I zH2iA!cf#G}2x5#Fo!*(&u@eHW=mppa~)IUq~eBv8%o;S;`yNWYameNo~jg#UXHXGOeUE9x0P)GFW&Oyb8D$K-5muz_|e;p2O!XrFB+^bDF`T#E17 zBAOe#zi7JcN9UVqM%zXqH&Zit!Q~mL2LxOGMjLQ+qphucD}E- z8R4T*i?L?P6V5xP)=|k%`4Ej46TZL&Ex^M^=1S3GF{@bO4QmF%L_hKupeLGVxTbq1 zD1$MDI#i&K#O^=V8upUOm_r14497bX{xsMd=iv(xwMNWZwkvzwMdYp6ZdmZ~V%Oon zQNn+^7ygQhmz{AQE`HFmtPN4~^)#y9?yD@D*>126N7dcb*K>$+ayn<%VDDc<3H>2_ z#>;>g;iJ6JI&T;G_#v`Y!fL9A>pvkzlZWs2s>Oh4GBJJW(9QhZ5q>}qpAo&%K>sz| zkJ$B5)H5a9q^y#zCHGuwaUIaR**ryA@lWTq^1IbG;2CS&*636UZgSNbex_2ewV^Gp?net znz`RQMw|c?xAnh|7%|NWtW3$GHh7K{AEvW&4fdXfHTeU2Zj`egalQmkjQC=$8uV~4 za{uxges42YZL!>e_;57x{uXi1SyB-nPJthYcQQs*g~quleN$Tb!lbK{7!7}VpbWW$ zFJaG!J;%UH>Fd)rEX?SZE!Xg$4{S((A?*d63&pvk1MAXD($-=x1bZJ0j4J&9CgDHR z3xBE?Kj5-nqrn4Yo;=`}YJJ7rvp*p!O*0C! z;ZIl&f5Jl?WR^_IDNO9n_GF(ugzPhnJL!j9nVyI-3jcL68&CFMVoqQ}t$$sfm@N<_ zKHaFozf8j4?1ldr5uHtMEr*3aLl#s$9$AqSDngJI7>umI^vqCX1%_8dR=~$MnK6!s zG@ZCbzhx4zN%CN`>Mef}n_Ia65u2NUO+2uf<&_t>_kffa_{IT>% zu4k2#QjV1CIoR9nqBIi%UsNCBN5)KIw3OjFz&jRf8S0hOI|JFyTIM32TCD$f)b}5$ ze@d)B`K`q6N8!z$@vHjhsV0E>e>f2O{iwo!lZ5{)FZ>nYzh^x;7Z2li4n9B9xjpfr zJ{+2lL31(qChEDV7Ckr5aKnC^5|7_T!!H}4@*+y~+bqmI3ct-!JGH*Qvim}>|1);=a=6wvbC1#M+{d=;$dtGHeCRiwaI5rMhvqF7z^ z5+T$i*{2)rqYD3W3IEw%`0MquGIgy^4=qbY=gb=H^?`m)V!we0N29Z(`&U(AR6V1c z%F!`1Cqy4iT_5$q%;JVUFn<+2Ft7LRSdnlr%J!7A72cQaeek|uuSE2??CUey-WNSC zZ(!@7=#AOY*W?VSs_A-R6;=w!uCn~Y;a45*$Hd7T)s2kl&9=w+G0G|@wG%&T_&Rre zLquMB1GNpSoDkoJq9oD9hXDQf$-g-A(lfaxok10LJcH@|aA0w_-Kw9Xz z8~N`#`){*^{~Ry;HBIo7p!Kne2i8(xdVX5+B~x6l=(4NaF$_k^`gf_BQ0qoPok`HA zcU!tKmxio9zgZ)em34lolU0pp3IDe~tkr;(6_PBPYv+Q(SXmWp0gTQ25_1GIdU}?$ zBs7sIB@kNUt~`{{{aCb~RrJ{JCj6$p`x@Tvd2;4eM=Z{kyD>*>RPzrBNdvvPEp*KY zU6XpTdXXOI(%h#1r@#_G+Q(7cB$&q2)d7H6e zc}_;R{jSQ?{6qej)g*byIJ)frEfW4WdEu`|4#cSr1(R))T86gE9Ydn+w_Jxdr#YA< z^-M+b!Ec59G9_kaF2lMFpIq_v`5c&;az3!AE@^#ZTjsM5q{Jd-Y+%P>-sNNN{We?9 z$AYeY87p1amP5~FS~YxRe`3UB>kjK#2U97|%KSH54n3~J@g3I5R&L}t;oa`(9YV4? zX9BxAQib_uLs=#Fvc>nby2rk>%3?lw&=v*HVjNqU8jG1_g_5P?U$b1YMpULgJzmqPC3v0(Nz{sz*p?OSs?nAIy;Z{hW-t85VMhK8w7#RS*>bk+ zpzR#mNKG!GwWJnB8~XY6*dII>5r>w};EDE}bLzMGsj>J34r=zHur>_RhLI zrCP$UmW{b7?2f56I@fqx1DfK7{kx9;zbxT@3*LCx{~PT~TeyFSj@Q~U(z-od2;{cB z-rP*FlTa&lQ)JQ)(K*!3%|3bs-{OwVqMBldGuB4iUCHlpNoR@J&dfr0Z_5$VG9SEH z_qKNN4vWvpdhmDki4gYpf+F1q^S&xj=J*lRHq#@J;#o2xmG@&qa314>7Sc2B-u@%> zxHopvv%exf`z`2WL_aRG!R^z#X&SfmXl~oU@HD4L&wPeZ`W5J3m#d0#)i2RdSPu=-$Ggf^% zT2Ndn=jB8e`_UC2nUa&oj^~(IHAiRjmraf2lH#(wUr}_QnJ}3RAv_;DXie(vtRJwoQKbA&Wh9mq<&C)&S_RG>8TZATL zE#Z75H;%o(!GCnk|4}XBKhF#QP}pv73`~WbwKtfbCHzB!(cui%X%L!~RyAKaK#y$^ z&y(1n9f=VMY!}uFqkLR$3rl;zR$O0UL2uMWIp8%yyBg8fe;oiqDkgdsu3FjuFv>a8q?xO?rXwCpFL1KS$9P45TwJIfSD^uyVWM_7PW`E5u)V#s{e~Vv?s{a3q zg#Uam{Qv**%l|We@#@h3#xI!kumalG6Cgk2&$>-AhdMpHK-Q3-s#+v_YQb{GK)Q5{ zVqthb;m^D$l}msRhsv94yoB)0E?tAfLzpt~C>OOeDMz6^2Qh41kMf)xQ2r6usKS4{ zg#T?`_><~`JzF1_ah?|9?{M`w&2MYq33TgaT^eOZ_tQ_3DD&*FrzT{*k`=z({K2xU zWy|wZ>6)Mm(f0BJnzd7I=dlhayf5qwR*sw?)9+`DKiVMgN|K#uWa6&2b(^|RhpTj_ z7(-3Zee63u#kLTruT|0vX@x_d%CE@Jq*5LDuTWw+6`gMA;D>v0N$%`Ox9VC;G`}F2;5ocbS$} z*=M}tpk?TJc$#N7_Sh#Sv?!+RKY?elj4J$JmGHma3x8;SkMBNtu*aS_-}=tUiLuRR z(MItW@~V%I_2?C?e0PujcQrk_rNAj`sa2ouB>nGn?DY!bqv@^W-md*d6x>OMQ#Ix2i6 zgZ6_zFnRJF-wJ!m-a-aoDNASFTURfy3;a}G8~6`pS7<;C8~E|7n1SysG6Cn=^Vpkh zGwkxCd93^k%qHIzCWlq_RQB>6%g-%eixJ0)p33}VLf9s~;dLQ$If+^;OkASoXAcJ4 z8MyF`l?p@B<$oKYeWsGAV;yPCS|Ky4#-4$-o{x1bd`b=9LEcaj_;@>31r}E?EO{L~ z=^1?1;zN0qmJkNdoXQE)n9_RKt_`wT?%vmjS{pTVosbu9Sn%;;*WtfI!avCie;Zow ztG?$k#xi-L3UfZ3X$-pK2UgozqE)=L1=xlO%<8rHRr7&<{#|;XQ&ykOapNKd-IwD8 z-Td7c7q-f-xVa(a2S>)GJIP3^wv?g$$-?|pDw~<~`*NsUr`x)w`UW|)?=g!v3z@S@ zEXKBlW<}4wm3IqzLgyej+Q`H|K$5$YRs}44UASwOuGx1n)|{L5x^VZbjP3=it~fP| zcJ}OjlKQJE+{uwr+3H3ov{G0V;yqsaGjzxFrMMz#Ah1- zvPgdFY3x&9y>W32R@zA7CbH9vau->uU{4DAvQn%kNLP`3J_O-m%J=9L4nsL|T5Lyf7Zqrgc=!0{h zv)sUc*Y*Eh68;u1{8!|JHj*rt0(v+tLQz+hRMKkLAi=^2?%r+7P=Gi>2+ZY0?`onmY=s?YXHPPvZm7 zEkPmoG{7H#-WM)XiQ?|_Ilc|%55iIzkqT*D+FxZ=F}6FzlH)z)YHU(DdNqk&|8v>% zzgbp+=QcIaHzj6yj(l|a|7#@tlfCc{L*$&cq0*~eO3*G83(61ZIjog;gfxYuCmBlW zON1##aCE*6h}x4$^hqJ%?u`OTvau@u3*-zi70ezLf4N_UJl;;V`V@(xSjqT11b1&w zf3H)|%K1e@SDfS=X^)}LgR37r4F66OF_zTJF$**?$`>gX{@tL&n!}P+@;&vxVdmlatduTbW9^fMRhFYg{*6{>SFCpp(04oCp4WhM(=$9HR1= z;cvni-adx{88^RMC3h(ZDa33OMAzId(j1OHfu0y&Y13p?t*c(WVdZ-84|-11gy@}g z)~qu#{NR8_tOH$c0gnG!=^{Om85vjIt1_+*wj-Z_@{W|&tF0d`L-#0ZhnHc z%v@^{`nL@2CbbzoM)%Kq_9-^?Ox&bSoa zSW{S;VvR+~%|M`o!`ks`GkXY@`Y!CB;}kvYlUAHZT;IU(waE6*YB*Ft?Lrgiuzn!v z5am>7)L(u@j(p*y^|nGo_{2%$7WCN?nsoH(o|L5fFnvh05j{tu4bz|;v=NDCxNCIf zfA5y?r$j&8{}W@G(|R@mvfkah9T7U3XY=d+7mfaRuc!acQ2~H<2C0GLzN( znLamS2Q!D7ths*W!scSG@sRxZsMXnIKOndR|!x8X#mV!=G^} zdIDCO5gQ!nQ}h^C%7HTG=~}AbyZbQyR7oTH^jUOG+H_)&cSS4YxMp}3EKxphkoImH z{YH+EE{WlbN9ZuUP-ZHwmpL}zSHaQQz-8ik)Uz_)FEBOC66xm(=%>rMpLlY-E9oW9l;(yXu5NS18>6z_~S2^X>2u?9o=EyGe zF~xHFg$#e(l^Dyl^%YS5a1=8=e9lGS=tnr(R}j)RK272z|8yO3QXO5!Z$z0EuBKph z{_vHzH+}`XO~$|NddnG*wpk!{rd(#kn6h$EMW==dN3DL06?vUgb_K}8FphhB{Qb}teU8XO~wx*Gk~^R zD9WC9+*2B}Qi;rfpoMFB2HBaP3 zrg7Xm4a&>^iq&+geD+-abgHbKOY!IO3gN4mi6v+q3&9zBKs8q!Ife9n{_V7;8yl>k z%}Y3%&n>{#zxxwom2DlPtN-tn@Lz6KRd{bAGxu7qN>BOw;%y1e}XyvFy}8v_u?`8qm2Ki|EzO=!3N7& zE-pRN77AK9MKdovS7!MA1GL6hjwoDn6RydjE>`&EcVhc?KDA|Dsmw@+nkz29VE$QeCJZYe)R9(@#dqS6ag% zLF7gmzY4j;r?{Fz#-wODU${n#*FxD`D{>idg#ggeA%{k!qo%KJMBInp={o%POZd}44O;D*i2@y9YFw_Cull|D|7rk6MhV?0MHy*Z>)T>w@Zj7aVy7%sd~{xzmA{HQHqY z#+J6^y(J^d@wo$^3_SC%KmIs=9>+h#hnY5klTZ7nBd>IM+Kcces>ii8-nx>+9p|!G z$^|}iGSfcA+4~p12%nESc)ZV&mHGzh9EsgMy6k_n8(|1p?1leSQ8T;T$LM#Wr7$0d zT|TYOVJrQqG-*2186ckq-)d5{j(0N|mvja-$4pnYg%y-qkj*jwb@=MDSc18L{@Bmx zi%`GZ;bWy&G=iIdhaBL);8>}H>D0)sV5`K@B&gc+N(004*`!!pS05r?H^eZHeE_S# zGJ*HEF$r>kiLf`4vMg96(f!7->PBKpahTGAZ70-gdhV++bu2Sj!Ucs}>a+Vo3gBnkP8aHYF#NO;jrevoD?v3Xrysk=Nuqaqc*bx$Qr?l@WL&%p+FWt{iUK7YjPcu(xy`~$`t_F0kPc?- z&*CCP`5-$5DMNF9j`YJG*klReh@rYs(X~*82u(#iP;KhlSuh(bf>92BK*(y4a)~hO ze;SDsxGI#@Ftop4c2dvu8TCIQalUfCIET^yOZ>j%#nb3B<~I>|BA|!dz<<}_Un}8% zC*FA&f9i?J5U95$EQAkX^4&Q8uIg{A)B+pz;K`G+@QwsEjUyR@0IK^r{WN4pPP?R#kdZ2NVHS)whOZdsv z1xn;`+zMX8lr#LZZl=n1@O{*1(}uQ`3XHiFSH86XD^1D$tq=8=^c`pnw4QZ_Aji1O zofx~V4R?lblHs3&kGY)l$MLl|PLC?vd_?a9!@ul)4jhHyOWZ|HxyWnQ^gII@qGSt2 zpW&#&zh1&W(+mGr^pnz!L{Iw*)!Kxn>38e{&z{JR7p?CfJ=S-S5IbTKPDV>-prw^< zd%-anKE+MffuWY8(3}UmPs2^}*nK@7yH99VgLh+G80Ox*gwzT)#2oUt7KuI0(LHKSGW^Z%6HXu8aV+k*w?NsZ^&lJP{zOD}totMIcJcY~Ui5t^ z52Ejs-!yyick63xcO5Qoe7+tMsNtaN^5ycz9eJmOAYHxd^3@2s6xt2@cOCz4knmrE zHy-By2a)+7i_s8dmG&^Hkh#XnW?5s>vsSx%j|j}fiD!Jc4UJLo%+1hINx=iZcuoaL zjU#6HCbdDn2jlL8HVS^o3in4YzRtW6ZOb5!n3mdQtI12gD$|pwli@pFSS#GFgWQg) zS+Q1_M%D^u9f>>55z{|8y3+IpUVrmm@4FQNXMh zEB2(~ZnQ3~b;fbBlrf!lOkKE92sE1;nDU|C&xV*P29n`8w|HVKGDjGUDh-t3Jn)=8 z)b5;scyuhg`-Nj2{#aqqY)1b^)JiqP=$o9fIL^_SchFWHE{{IALe-_0`5T`XDWP<9 z&Hwqfg#X=M_|uiSbs`1q00q3}91lF25ml=~%!YFOe*v2Y2chj{25LHaS}Z%sNaJsG zwV-n%U%m}_WmM)u-(K(*^7_n~%=DM)C&GuH&&j8!ayK=|rW1a>ozVw~aX4@h^9D<= zW73Qy%>27E-C(P-B=Otq->rUaVYUsEx~yRIHMTca#Ku8RWWJFNJV|yo;W4iqW{Ui% zm}Z>E7wK2GYLobxMU>wsU7k>~S(vY{K~0&_BVEjfLzc@*IVeG2^8(c^mJHHod(TN_ z*9ObG4*wZVj#1VBnR7a9S#cb`pDVT_PLj1+(G@7#(nL>Nz5vpHrf!mv zwjhrf{aMFfKris=c5Q>dZ;O*&A()FZE#qqP46Ea#t0&a~%?{T_VZY$kDQy47NRV%$Zw zPBX1fXZ#wdefQio8f?MZ0;3B5cP0Gq^};`aJuC9?dzM;~@u8}L$jkFR`t~EMW8$VD zejdzcFq6q-5+r`pt>60p=vPPgknetVq!|165Q-R`^NX{8kk-7Yx-k~iWdjFjz5FR= z4U2cwh^u}4T)Q)mmV6-mmsW$w*D)60cKyy5=v=Sg)KdE)iBBt{y?@=sSXQx5xp$~{ z`XHTOl<;pBH48f2kMO&Sb}VA%MH~)3{9y-~t$~yoRrtRr;eVeO{&X(oQgA}Coh`p? zXWEbIYOl%AJr|X=bT*Fw8CrPnsw`#2!K|U){sBc5ZQCc&$^jT#L-@ywf)77%SOZJ% z0LE>r$~Ow*2*zFOwqjI>J-r0qe)BgjQa?9Qw^_e?DN;gux$C|m#Qkc9s- zFZ>yqRy)-DwkT1pA}=-_g2j%!ZHaSI3m&wgWArD(sa_=mrv;BCQB=2r4S&0C#|t!i z5K()p$cHxzx1o36u0t07hu;e3a~V8AOp(ZscPLAbcc@EOT#?HuqP)S!h!Obqje;Ju z5e0b>s^2I?n3Whs^cKdO#M|D2|L_RL3=sZn`&$_I84GVRd4l@*kq;!yx8vUSma9M1 zKC(JH?+p4p*0IA|Z0Fi9dY&0iH!afUM?c|hXZRaGJ1M%Z~COXOGSAXrBOX-6i>S7K>vlB zys2R!{q`#~mQ<6iUXHz5PODT6H7UpBZFEtm*#gt$+)a zSOX-CEosck9wYkx=-JE2yjauyKX19eM_Yr>uj%fprJMro3mxs!MVbBsaDbVj#vR$8 zc>#U29p~DE_30F!i`V4HyB8!Hp!OoXy?nK6IA<7h%(*Z>LoCf1zUY-R946)r6U_5? zEoZo}fZ(5dbiKu( zPx2Gpimm}hiq`~ipQ`E=y- zNxnZy4bvC@v7n|PKDoGF4quX#wd-FHP`IJMm>wB0H&S-)~X0 zO)c=Z<`gKW3W!KOV?Qn^>>5OjNCh&{j8j3k_aj<7uW)Ap@|5HgtVM^i3zi=G0T~9; z)rZ^aM*O`F|7HpQ-+SR7B>GDK4h#KL_(R>jH@T@_6uH@m0wX)W3!^ysHX$)q-L|tp zb2&Yj=JYcBkHQ(}PBHfLFX5j~iX-*^Y{GATlUa=U2XQM9Me=n_PYZ9@)+l8XVlCeO zu=g3{Vm>XO z;(A~IbcCVAV|ZQ%x4ZzcME^U~@{@ zO&;u!v+Xvmdvsl0X?5f4jWuZPW1Xq0i4j-B-f1K=7IW`fEGO^8%$awxZ(81Il%M`S z;`PQiEv`1?b(KxE&5NjR{On4A>{ZJqR_0O!O>PTu$*%N9OzJP`pKrYjqk2PK+HNHu zF*wz>t5MVGLzMiO!Cc&}ys=_@bz_xfLfg{>*fz%IOb;c54a8Mve)et zsi+F?b+mH<;lk|@WjF5Mb@;bR_-Emjhw(>GI9O!So>-6eTpGRr z!gsKC*s68!mY&c~>Ms?l8^<^*Qz#0l@;#k$d9t;-@ioh|(Q3)D=R-qd+`Sr*NH2oW*g>v^_|qe!F3dv z?@vK5CXpvGD*W;y^t;4?#?p^cGA>!dp7#3KIwZ9l=zaZBwk{TykV$XghrwB_A?y!nq*jy|Xo- zAv%7v)h0;~8E+pNmLxL%>7jNTF=iKClN}BH-juixIjFj3+V5L!*ZTcKURPK)biT0N z_6U4R=aB;}$($P&e7x9o_?Kp_2cLYtABS(KWf^)59>c?_6&7o$)N05-<4qK4Du~ zN?t~uXJ3O&1->Foh3?;|f7kW@V-o%kRaz8xs+PyN22i<}BJ7dhuJhh2j zK4hn(F3a}4TfH_(r|_xvZSbwctl~BqzX@@~|8e-!T!6oA7h-kW(iwh@O5VxJ<(-a@ z3L%C(eua<{{7T!!;x?IqNRIt)X=k1ZE~r2Zu-vYjP-$tsywzSEz9Eg$j||i^F=x9l zRqtE9U5F<2sh>KF@5EMqJZ2_l+EC7B-OWc(x|8;$b0knD5|V){8m+O**go64d|o;gfvJ)f&$T@=_HkvL z9pmy0oq)=sBM@eU#03p1h%6n5FA*_p?)pwQ%b2i86i5ScsgPh-(nL}_8?J(%A=wSC@Bs@V?^dd&m9*W83_R&^KpYP-u< z+e{aIic`35I%fFhyOQ~I*GUPQ1H2Ih-dNgRvNIccq5&mLC~=!Bq^cjo-xvSSWc(iv zz@JKs4>8f!ZFK!1kEFhV2(Ci+BtH0kCaK$f0G%$_t-#r65euqC~5fyEBNR*<7YP9jrtBb2BU=4 zWwhqvolF&5nVub;J&A|3TfoMU2j_^UXTL0?bKV+CF_JYA^<2 zRZ8wo=IuMNTI_)@STYS;K6sn^Qr=&}_rLd^TZ(-cHmdt+d*HVt`ZMnSck3hnpUe0^ z5`aG~OWVP3F&0DHOxi92X=4Ov9KZ9Eg7oaD(o@j4rlY{qN+oG<%VE{j_RyUo^#Ad$ zHi7a9wFW7gYj!|_jaccYTH5Zx@n~akCWD%;BKzMbUB2&z;H{J3^Iu#>#9O+aYWCmj z{Eq5t_p*t>WjEgK#12tNXo?)V(d_;0pF67JH~RIxYM+a=Dni?H5>Blz*wKQQX)zJD z1sz>*+*GU&BX7{>wBb2rj+USE)*aiLsb1N-gT8-%{q(^fazNMr(Mb>f_Wu_bm8*Al zkP0ItJk7{f#Pdr^E6pK}C5V}65;vEzm_uO_jb;C139(cC%T_HZ0WG%@@jK6u=KZ*a zK@H1yq+`bZV{M1I;H~NKxPIvgYECbGL!%N|)!{Z%^sp?f7029xWY#a&M?KPoC8aM* z$#m{k?=J*>gr6^H|#)7c-qw8{kcUy>q0G%@8Q^9$2JS{PPeCe&-uUq zyL~ar&yZwWwAcJfm?R*%mc z(g@u;HE7EnKY{)NVUVeNvKMXm#`~TwYMaL1x0vn;_D5W@R71kiil6FaF8Io?L;H`p z+_Rph@;7TS-80y4cg=>pce^iP40^63tCTf7=sfiF_|ikr9SO{#Hd|DZXk@Z3o1;Ur z(o3>ds9wdK|2>Ce6&0j4h*4CnHb2;CDGf2)Lo>pLC=Aay6;=1e=UK?8bu|g4%5~hU zaZPAbOffBo6pN4vR#~aqphm`W-%It=e|_-(TE>4N-q)}HHL2>2YK)h%Jlm+;u*S?E zJzF!y!o8YM8b9<^bHZt^a-aF_7EYmP$yTUaa@o@j85XYPh~kVZlb?{MUZ-5!<#zSl zWKmZtHzt%uW^aH^$3Dyh`V4Uf*e0T9o2qj@FR5<~Z{f&2 zotPz-zDD8>eBA1dI9=1~p!aN0(^{5Zt^MGT3lSryhvZe8kqt&z|*WoB=|Ng~Xly#^jl;!iyqT*WF zOAewR&ol2MUZj`~{ekH3jz1t@m+j8`tfu<@l36$|ZY#$Tz590ocTefAca4EhW0!4mDgJLFDI@q`D*rn41KDsVlX1Q# zWN5W{1W(_&zkd4Q|E-Mwqj+Jz{A*9WWrl2XV#A~HO62v?w0z3WnokTrI~EJZ(ha$j zOE0TQrY`rBh~<~W&*ab9iMYfg+N(=X1`Vzcsmv^iMwGFTRwiUy zPGWX~qZso>bbPq;2h9HA7`r*hSxwh9VR}RV{q({A|H}CPIRJl+p`nq+!ohkD_)b+g zI;E!37!W=A6&j6!j>qGup7s&_K2>9F)QsnM5xpkm+e<9NRHO<(-!!ET`PzDspVy_t z{GSE*PO;DOVSm-3>?7YTEZ#SFq&L{LGHB^2=**Om?~?Q(Zx zj^{59x;BZfLL_1bV#m^F^Z9zwfBia0nWromkq%D zi~hf{|NklD|5zaYKKksrMxQ}>lWIXbn&XRlcw%%J)~64sRVU~gF_}kb484b*4$I3C z86_!wXfg5w!D4SmgaCDc6&kvDaq-hu_z#O0M}wDK;3_(v0+Jzr4ru|hJ>9jfS%+r0kD1AXM*A>&Uwy?*^q^fbRWi&^zs>TOBJkN4Kc zAu6}m{cJrjTMFG=8_K5^=3~r9CwyeRjpsfkNgZVuR&2vqx&dST`^w(2C+xm+_p2R* zm<)*NqbI3pr-ovlmDhbQ^83>GE~nJPzy&m#=lTwUnWG=zP9%v&bWKAk9&rF#e4}yV ze$;|>8Xw>pY)SE0nIezs$fG~ve#9fpW8j}qh|hE>3YaQe($^UM(egiFyoWyP7xa0- zV;rJ|9==33HaQ--YUppsErcVv9LFmP?iGj-&2xwQEB5rg`rz-B@n3uc{)d3ROe3*& z+Vl~nUk2G*P7GhRYdEjs2RqX3xA`RhkG>H&asL#|(o6D1*3!dD80W|U(p=JpTsoTS z!x#9ePHhrMYO}+bPq9kry0G#s;l~xXA@axM?Dx$V%HFaLc1#@?)A;sE@B^8JSwnu# z8F%XM=1fO&0p$*_yRK)KU9ML@UvG~uU~zUs?j}2~(ow#pd=!W1A(UUM;itanjPh~Y zR&X0)5`FTHi@~o%%)$I2aGW^J^PTJOh)xmD2ZN6k@Ly?YD7=3A>4X1K8UM%e#(w#y zQuKuj)Jgu-MUsMf8cV|`_mCq>7%Ed{+^!m0ruONHt)iq0)5yvhT zjyrtO^dOD&P0vS(-iTYBn$cq%ik4|nWAernGFR6xVOq*UXG(f&#fqaVD&s3^Fr%#H zCARSLw8Y(JO-*WjI)4}P3m%flBkGni=8&YgC0EkTq`d&z8%VTlGzU2pnH$Y2hluRI4?lcy`nZo_?@KZbP{i~ERsGJL#s5Ho}SU%WLLyfxZk$TRr(it?DBuPBdQ=dCOb)mc*VG9Rq0DBz9`L_ z!NSV7VD8A#Xk`l*4G%v#(iW^>QWMJxFCUE==QwT8%zG)3(Q}=O_5+e4%OPoVxK5@u zDeHiw9h6})53KcHCuIDe48Y&E_^=c^;;cYH3zJpZ|*eV+BnF`cc4d@hjX)ZVtl@fd3&8Y9s_ z*Q4i0#M&os&n61=E>$gx#4!FfDMSl@!f2CNZA(E!aj$y99fg-q4EsP*7(b9yB_!44 zB!;`30(65d<8a%7=p{Kb9JD1-U0t)7Np@Z=oOjG(LQ&1qOfL-LH^|<+Amhu(ltw*zOp4e2 zOea=^!M3MsaR)h7EvCVYzU^_pw%GlQ-rn{MCwdjcu*6AY!1>Ga4wQ`~EjI4kRjXEo zt`3@iXF?TOSHrd1vbd{>>2IS4natlj1Nwb1qcwWucPxE-?mie!Mcbh`jyB*KWt$6{~KlepANvENlEJD zu6(BsIRP75XSOTw&4gaspe#Q&OnRK*C+AOy_cNmWtTekORjE0#7Mgg=%?x7nZj%O& zUybabTiL`DtC6WOX5vXDY5jBgaY;Mzl+x7td3lwTIG*a`GvzdT1X}&au?8>cGw2#+ z#dfUv_qy{t7P7-nJV!0RY(;z1T$hsCe5Cc&@`sh$6Mq4x9F{aADj&k{`lxmirKvwG z-IY`V3xY&JbLcnYj>YAvtm(u;+CrQ^CM|~rxnkZi>80#r(wZlZN$VBIqzx&@q}MWr ziAy6M?6gT=*8Em;zUINs0VV$@W&EEB!2iXr2Rq9<8bFD^buv!n=wS{rDwChWFKAP9 zi`vv|QCoywXp2aGxNS(*{I((Hd2Pdst!=~S7PcRl$0Ye;{!G{|4K4waCa?RvG~Hq_ zQM6B#SqgJ+-^bVS>Z@x?5q+;|PWDO2G7%pyKe=M7q|lM%laHTTp;}i}qpnbHQR`6~ zB89{u3PkKC8ac@8-Yub;`^}`D{CT=EvBkQGv=7B@N#x^y`xUjkQL^A&Z8^pFkZE;X05rl z@uSuOh5soT|7Qd6C&;ADBvU=#04wT?i*l<-zaut#L_)qvoK}9L6}7szmEDb%9TaOK zdTn8lyG5H1WAkf`LG8AOi1vlS#Ix{2y!vY0h>d4?0J z(`g$)6oes&Lx0(}w^bc+C$QJuap|cGsqpghP?zJFq)|FvamA132b!E+^@=L2eS+%mpZr+_=l+CP##UCV~w@7eA!m*lZi%opjc5&5! z!vD04|8oKOPhJgOJM!lE4{QDgdn}yaQUHzmFvb5U_~FpSX>U?1$hnJ#f_ z{5VsTK5)jj+om#3u4C2Zqp35%};j}qCvB`SO+3TPd^5(|AL9!80Iy(QN9N-Ust45xLh_LX=6 zC6@P=$o7?3Me7)D64hlW88M*nKO^J+KLPktN`394)Mud7kzPvO;-l18lu{89DA9sZ z8zM%Q?UR~O>b$QMvT~QH(R!D5`Z907(z5DXDW<8a22tS7p-+J*KZA^<3fdgKiMXa7 z_dVW0E*6oZrnV2-Il?u=GQHrelxLrjM^czkhoqP%4@uf5@3O9+d%yLyxvyDI&pmBT zEKV%Czxe*5*NR^&I$eCaC~@9hbJx#1B+Z_7R3aJJVnVXu-J?h8htM-kqCP%uHrB4J zdz=%sJ<1&>*c-OTX@v1@c#dt7#t6G3Te%)jWumr6k9$Dj|GkX=Ujy(D@&4limF}uu ztxgP&J4a&_h0fa=wu5|<(9CQcnOA@j7KK#XjPd@v-)bIygCF>Mzngl2P2zUjap^$D z_Z(^6V1qV@M1P);(M0^vHYguaC72`dydRM^-*UZGp|)b9a_ZFu+aJ9^EyPW8m0Or3 zM2t#T`z*vB_uigY5Hp6h7ydqlGUfn7`xUfLL4DJ-{b1m48Z(F>8Zf<5xdqr417%~6 zdqdB|%B5EqZl8StHe>V=@X93IiN5!M!vCy{|Iz^b{jK?X7pkOf#&r8W>0L9~$2331-MwB%Wo{OW^m&$Fm$T*pFxO^m;$llc9k5B8tu z@cZzNMa_QR8Bq8)$@tp>@b{1Jh_y`W^Se|IHkF905n(OML478>X`gHa&9H*ebV)J7 z-|Fwkx9JW_%ZcBsN_*7kqhTZ0%4A(pFkI`)vw{Tf>Qj2|%Bfi!&CCawe_i$_dUWyD zHv_$J9F6Tpy-+%20A=E%tTOo2+ACp31nGPuYe~|Gn3&1}#<194w&o-dNK4*zSBxVB3 z=vmC5h_@yPOa-MBlgw<4xBkWT7?4m*B!&X#qXo1Dj#YY20FPqY2K%vC;KRb=m}hxt z2AsLzn#3I1NB>k>KCQ?7AtLYm#X0wO+)as^vkk&-bEM-qkieGF$o+QWn7aMM-98kr zbDPh5w}p(tJi`*N`w!CV@Cy^eIR~?adDZLAkm&5uM#ni9eFsbLJM0d?I5gu7_s)sq z>gp3Qt{glcXV}@0ZHX6X1kwS8|Bo{MT|= z!)pz<6&$<2!ali|RyDRM0cDK^6MD<8K-m`>GV(JlrYq-4OvxJSbzkB$u0_@tT&X^) zPyKD&&Q#R6%|4>H#+Pj)@^SYGoG#uhs%n?)Dqj!QQ*7vyI_su9?+Wz{EDA&KRu3_Vj@Lt~`9hXpGsuIuvyMj%(C80dEA<>|xXb%&v&N@urnyv>1JF6I zH!FSL3o%QW!}lkF8+|7FJea*#5NtUr?KK^hJ|;({J=(-2B$-K3%}89lVadHqPA-Wr zA8M_%8Znb$7v5nEe9uILZCJ}Bg<%DAZV&zceGiiqj=iae{vS~IpO^7p9)SOMphg7f zFu8~Rcl2msMYH0Bp8Xd!CN0i<{t2lM&JQUFu^<-v8Aaw7vmG-%r-Sl>@(ac76IiRl zsF>_=VjSM%b~7;aH{XRR)mEaLT@2bwoQ`M+>2dY_gD*ZS!ID^r7Nz*Cw8(~S4aNXR zF!~S3Hj|>&mYr@^Ts@E-f!2YHFSfV*kx5rMW@GhHynT=qWPG-6O)k+N!Eek&&DeAB zP!wX8vizr{D-V{DB(he+bKAJJEaKK*r1EbiCILK=hs}hI;V0D&DEu$T_&*x&XOTWE|PM!dh9O;tE(wqUJ^ z6Qe(3#Q%frfE@Cx(qD@@L+v_VyPp+Tc#+eu_Zl;<33g`N8SyP&S<)$IY%BOki##oI zO~2J?Ys+^rYZ&pB4rT)LNr$;mG@{>I4tLtQkTvy6Rbi8L$ zZ6g1=q@9_yn~c`f$76-*kxLOwvURz{BPRV3@9|3!(aFNw)&YfovyA@>0r)c>uJ!Bq z=xofk$@V5~Dn17aWozf+e~y-B*ki>!#P>|WKHJNPHQwdY^qD$D{E})_{185jpKm7~ zMw|(6{bpqDTCP2zQ&Royfby{!#6Z`=*K&rGobgNJCoHb3?M``v?j@2d81YVemkCM2d&L~0&{f6@uBZhd_OWBAnqo}8IHM5cTBj?(;97v?ni)=Gn5qA`82~6T|xbjL5y-TIJ$iaxe z;mI!d;F_6_){wEv(@{+ehtctg2Wr9`#S4u5ENB!fu53fuUp)TbTAg0pi`M=xT>llW z&-PtE=nSwAR`}`wDf;cF&-{;zGX5*@#+30*sqbu;X{et+(?`QU^wRK~-_bA|G~5Fk z{stu9?Zt&=iY`Bzyy%^Uv>)ZNjTC5vo(>x`@txx01;p^2lMySCe@R}$M=8#T>w1`t zGoj5f)+m<$7d$1b1g3bt@VjTBc>WpBGWQyuH}Zr3T^f3?fB1LT(|5TY*RMfe^wohi z|KpO3|H=UT(F>pTC2afaYc5}Q(Y#L~9-T|ZcAaAy-z0FNNlIRHPDnx)t<~mp)V2CKtLsy5wUD%Et`I`AHd(#x+MTj|`)nt})b_BQ zD_mBAnaGG~9xC?_TWLIzr8`OLGG|Y76KdG({XO5*w?DkIh}z@mgG*Rn^z?3O_a|8o zUW%Al2K!6}{#(*5x8Cmrw%((zwcaIi>zz}=ic*){dbM{!Cm>TWx%&dz_vu!;x@a`k z4SL-}d#<%*SqiLK8q9Cxe{G9gVM;DXoU#7<>4SfZjQ{EY{3BP`7XG+!jd|EH)}Tbp z^%bCt9BGeAWb&f-7Cu@tVV@rPc#xs*mW7G$6;E_96CbTXyE>Ekx1~uM%^x|*Nx5g_ z4cwDbuNSJVr(BdP;=mP6&?dI{73LRQ=|FP+S|N%hRA&s?-6`Kni+ zJcelhM;zp)Lk`SQwr-_-ht;Kj?qsTpt;{XEeD(g^^ZR=Lb@V8BjrKPc{PVjae7S~? zbfMK6YZ6CMS7^We^uhlp8UL5?#(w#yzEdI|wXs_mac#$0>7{b2e;LI2MUG#zk~_Qp zv)#6}i`h!AT?%aGpv8Z+t9l`w|FaVoTvZpdk;Yqp6;{J))bpRLHwM?!*8Y`_zS{jM zNcKYd9M`S4puiSFKR?fByBuZ~B5R|nN@cjJ9t#Tv0$PTJyqajnJcx8UQ&`r!Yw zjQ`64_*1`qeiI{R{ug}gy|kVBJK9ofY!vWG_+Q}jN^c!Qe^&>^M+tm#!EbI)^+IaR zccWL?>Y-A4my`17()Niv(I@a=g|E-MgB%1X(d7BhTy;b`h z-|l8^d#6}G-%tm;+r#u?NZW8mRQT#w^sx~yxu|{M*W13H=UPU51pI*rq~&B(9Z&Vl zcfGyNY3Oy*_!C6@9-8M*crFoKITQTxpL(4I0}KCOW&G)+2fzN8d)+!<@F2AJBv6-9 z^$XDORu66UiI`AEwKx$o(34O7kMc&uH?WqQYVk3@;}xnUVjyoCipPJ{;zWF|w~pZ7 z)j|2~N++HJc>;g0#r;~W0Xe4r->nQ0Z}-(r#M55CJ{VB)FUt6@3BdolkK_pYZ&okl^s7v@S_+|}&-+;ouRmOj90RDbC2y@6*6cIn}rD4JEXh`L60`PsJYdz4>cNQ&(v+Ebe2?tx1 zj1_V9PC`EB z*&=JS{Q*f~Daob(y>3^X*L|T5>jeKsbZU$$$J;AuD9nEQ>0|$0k?~)LH}>m)7L;R> zuhY$Hl_^GPR00gb5p#(VKj`?+?dZE5wB5*Yz}82}|Ln;jVn{ECZS3)L-*qii*1?ER z(z{)6CH{>khlrQZ3Q$RZ>UVWy8~0x>V5B_a3{uKfE8)=MxA$|9`1>^I^=c|Qa;Z5_Tdd@m7kD0(>#(l7r zv@9b(7AFnG_yGEG<2%xYh4wVz+=8PED$SasBm2lPUH|F-eM9{i>rvMjE9h-t9p@Gd zx&3QbT;5q}jV-P)=5|)Blw$K(^bhI(_0kahU+eq-H^~I6akWHk#}!gE&Oh(_UoJ)A zc&QX+i7LRXBinH6xdm^#nlP4ILdSH=iLM=ctUuK~hmG2gp*Xh&$D6V3#r6%hF*t|J z)w*nK{TTkf{l9h@|Mda*mx3NoV3V*R=33U7OF>7-n1>xSwu99X#{YwCK-}I5{Br3I z<2y-e{)l8eUS+4+`B6O9W?PuaKO=w{=#(%tqJ z9Nl7UDP3(hJiT0+WHdZoY+s0@$;O3GXWF;nXo_*$)6w=89Az3cuPer;)+N~l8#8z5w3DF|4va#(Q;|#nt5}nuOUIwS*g!Y zjWJ?GE?Z{x{g;pFSk^y?4YR?+-~sfyEwFkA6#iFb{Qn++|6GTfzYC+ZIT-s#9{^Dq z)x|Q}llXTq6XelO1I5{BC_OG2H*xaNis~05LDHmZp3Nu#v@28Lc|4qjK9|8D}=BMXPpTUR^VSIKb z;y}yevonpb1HH6;{G&5nsHRa|_S0_JESZiM3Fkx07F$m771<}?CLORYS zV7&I>##^V~W=R&WeCr@hM>*zPKdW&MP$qVi~hgy|G8!SUk$(?-q0H&u9oai2Mt4fGqg+g zv;5~SqcC2`7|)1r_t3mLw4J5xCT(kJyZaNi5**WZm$tdIElHv6D%#!+c4O_(83tjQ zdD$)n9}H_vonL8IJLnjL(L}_cdYUP=B)59Opn?iBEILE0Gij)XXVx;};hxBX za|^TrvvHX@B5y@~H1ZEDJ|>S z6c;Q0pvpdSDe`XGB2Su$R(a-yxr~^Dxg%7QPV0fDRw?y^@~ex|_zUxlJk{9H+%t)I zM~~bl-_{*rNrW8!+Rda!I!*dz7;6g24=o6#@|Y27)mn$**O@(vT_yV!zFDC3h}7q2 zPtQ5%VTle{Qr@Dxw+I_sAlvbDz8B>~#P}2FvH!Q&Y%Pr{iOBi(lWij6ZC_kNX9cwYV@0bXMAi(}?%A-q9_T)|V1giX%DueC$1CYp zMjYLp*{jFLAH&N1O|-`EoqAvUP143W;)SGZE%@-;GX%^X9pQDydNp>Gp2^%QIWw#@ z0>CVLT%T5e(IF0$j#)#*d%7``l=>H6q4n=@hV<6|&w(}n2Rf861cA7(`+sQLNo^sA z>#Q_p&9^QpAGBTfx0{p?{B62VUeJcpcAoN0No`(TY#*wZ?2joJlpie&nn8Um;evus z6%^13r@IWo=D7Q<5l17Sr$-A4*c2aig#n56@Hiw|z3yCimj4AyGw9my?{~F9W@)?a zm)pSSp?pi@gio1%R#L2;GGhw(kd#dUA9n513oVEU?&xZiLXBAIwJEq2fd#}$JOh(y zuTa;(RVu0MMr&pfYr|!G>vqu z{OfCD<#dzFw`=vAOU*0DxNVMXiV(Y=(J?DotEV4(gm;k zUp+O*$Yvxx?zg(=-QMVW-zC@cF6!Cbdwkhfr>j8&KA^G555zO6O^rl6*LB8q9+*@9 z^zk6}RA*6rPWglSbg2HK{6g0-C)h{Jc5Lpbf|A-%)1$4)g1)zxa%*GCj1)`S!1Dig z%lN+@fdBP=l}7HRsXjz6?Tvfy+2`w{^?n<+&(}li{nlLP>!0<0+fnzw>z(PXY3Nz7 z{Ld~O=Katf#nYaTefq|yOMu(8wx@OoT9#0rYmG%TxGkO-SZlHl`N&h8IK(0A5%n=y zk5GRv)g|lqg$WM+=%ff2qY-zd&^elM zdp77%QX2=l59a-ppW_*9`I9}Nm-f$jMhnr`=s07aACnS~9}k)Seo9Z@cU_P39y;D4 z>-m!Xe)`jUQVjghV5~jtvm9cT%XU#uO_qn+gPHsM{lQF6+_nDTkl*zOmEe*eVQbPJ zVR?O&h0(=YBYmy>hI*pAvo4~youj?Yu)NWz{in_gF029V$DcmZ33=9;aj13;1bc(z7@3_sVs@LVVGG7AEG!I{18)cZ=zNhieAL@ z1II6sqSF#7`2?eOWoA_a~j5bOzScwjEIHP`Y5#41M z(HpFct{FA}y$8J))2q?xb5Z_^tqTH=MAPvwHa5*&V%c)>J>x_MP&f03Xt_MF$s{7crS@ zeSV~6^N2%l2PaZ~rBB}LEgI7qTu{77E1a9V%pBy?4u>&XaLr@1@tq>fAL*d|O3LSc z9tSDqqMRm@1%`la*LYt_!V7MUr?LY1)UZ6w#|8w&hL$D)VpzHU4 zaYhwH3gfNOR(bYF_5952H_fAY?J?RXB!(6Tvc$c}uJ^i?UZTf263@?v-MPN=d-yer zttmzQ7JR%|AN*Mv|F;A1XT;Ku2FTnKko{6@2i5Idw+Tu4zQXo&{) ziC=r@8LvCtW5u_NJx{sznwds2`VEIskngq*cbkRZl06eJ>hqm2xwmb2Lz3n*8<|wj z@$3SctJviFX#Uiq5V{_sAAkDDze2`;a{&H%Ii*&v(I{Ykog})pwuGQHk7%G?GMb?e zvD`8>h_TY#eI5C~bewikd>L_$^qq_9jm^**RBurILHpif?eOU<;WtEPC*dl*Ft18F z;eef_sNYv@r4d7w7~Kt%&RkNCXOTgUB(W9h%IN%!>3+9VxfO02|~KS}+{5t85IuITy9<$IPpI5G%2 zp$pIT9JV#s{5oz6J%?v4L7D#i>4U#g#{ZoF{N+)Y9+z>?W5~yJzHeqFLvXO|-N@12TcDwvq7OxLlmFGQg&9E6iH)xrHA;Uu>f;= z?GL0$gFWoLX-cdq|Fun9{`iN*)2oGLb#pDnc}&tX=$i6qovBZ%XyEwaKtJsuex zv~1nn59i$eFG=yd*InLKy8GV3dkc1AWN93)Zqaah;LIe!Y7G4hamYTCOcOCb>;U+v zqOjeT&TRNhQl!%uAtUzT_w+&5D8XXk)^T(wq~Ct}$iJHNwg2zojs5ojP>kuY6D~| zy4aMASYJup7W^OovJ`s2>((RYPbk{|8BPPQtA`yKiKs70!C8`HlqEH9bOEjc!kTE3 z#8`>It>HFA0v~oAxN0NiDH?SwzM#5Aqe&H)=H=$3*5@L|CrM|<9g=M0b&aLNyzUpJ zSj#ElBolFB2(-s3#4{gkxkKPQYWI6rpBVMtRqa#t!A#4GL;i?yhNm&_>lFv}2-)uJ zx8UQ&`rxmT@!t}FKh3tg($(&G5mwW;Js-LfG4GekX_v~opUP~v*Gqh|54rc{RnbCc zn-dd6yinbc(vT*KkD!cSLZ7n_X!Y;^h)?Bsn1mblkaaWfwA!)Zet5w{IG+>Xo zz)mZUQ+>zdoJlJ7#7GfN#h_NU|1*3}=6qOrte{>Oeg9qarR}n36ICI=ZOGV*KKZd`r{0GbUzaM}< z>!T72njrrls1yd8FrX5HGave>6ilgvGX!VebHy;k6pJkpn+{tFHj23hn*v)LVnoI! zv30;bX$WG8$3oX*TUx%$Yrq_mjbVwiEBok;Yv zo{E0_=p`~fIFH>Gn@h{7`;^m6glE{unzXndT$(duu}=uNZ``%}u9LEi{yT>|)=6gV zR^Pp&uGKl9@DGvk-xh#>AnnVgX~uH&)Q?^Y`Y-fHG&S)${ci>R{{nBicF+BZMBHn8 zqCjJa5|SN@aZY1++;MN>Aje|`QN9?hU)%nS*r9Sy;SlzIy*}TvpuneXphJX$`E-n- z^iUfYb>79LnXbHLd(e0N2`86!tO45O&LYGCRKy&Xbc2xl0s9o}L$OcAUW@%0>|?Mu zVjqir2KI^Ao3PhmpN)MA_5%w4P#OOZ0`SiR9a2Gu>DZ6KekS%t?C-)p1A8m>ldzwM z{jJyw*qg9lgnc&h@|BF3oEXIx&LKxdeYue)`yd;WGX^0`SM$%AK?9)Vntsd6c`|kM=OB zLC)T_xHva2x!e6eT}LuZ9*ha==iwXHkdJ9~rV2go*E?=PFYG0ceAS{Z)M^YVNEPM~I(u}M zKyjJOr`z>>GM*@cdsadL4d1B_F}B!Rnqj?7K*)vD-b` zqu^)b{gA`brh1&SD($&-;rr+cp=Z2wZgNwLWAU&rYl4uCd26eJy;D+5-G(){blu%rv)fri^0B@$6w;pF{Vx}l z%P^0LKLI%_sWs_CW|Y&}0LihuBFSLwc0c0L%+Oev3Kdq9kqUMGP1d(-h+c)b{(Cy9 z$BE)d@uXNThYooZx+)SrKMlsBz3xtL811c3qnZQ033!@p$n~%s`{hXo5n-HK5vqKZ zjG$ z{ov_C^3$@Yu0JlBM$n#LVLLI>EPR#LgYd)oZM5*|+|?E7u-bFru`$YixjYV@8}2?t zUH(+kpf9NzM58a0`gy=nz=p{NKEEHe(>1k!)Op?i@M>yr$9|o6TxtTWg;|o$INb0; zeP#je3!rv$_~;0<1z3~x;JveEM6ZzYlmD9kTvl^K zS*dmiUv5`o4LeC;o0yFEu(Bz|Fn==(FY{jxLUf#2O z5^thsrp4wedC_$SJjV%Pn1B0#@ON{#GeHPTQqU1tyI#=o2|{oZ#UWIltF_<2bNO`s zAe$Tu%;~jj&=$aE%T;4l$NEj*Ns2WzCIJ~e#X+AkqtlsV=&Y2VI%*5U4S>v_44=DMMmhs;mfd7Wvy>@n@54#sxL`=}}U*wJw zl)Ox_W``;tv9^=H$W>;`Wy1KAE@oo0OUeJsMX{k6X=*Y4i@dPy?vK2YmVNd}OR>G( z{fn1~rP9teVrzF_#PQ?4<14Oq_Ye5CP-@5A5M&6He#4mt9L>d<C7eiP*@Ltm!PjoyzWoCd)$$ow58JIYyOQLun29@dOU9q zr9rPfFsT+E?0)>|WB$xJj&k|VpqFxhTvc4u*tjvTW>qg%_G)1hhoN-T>urN{U zp!M`)`1|^Q2g&%;qz8Wd6ZtuqN%y|Fvyt`DYNPcfYmrq`Wc--YN-@7!xZ>jGYxg)V zF;VvmtE|h-cUU#`_rd#4y>%F|o5B>uTje=?v+4@!XtaHjnpjWq!G;+E<#pRzHKC7Q zQv6>c#!F`;Mbnu}tR5>nbF(y@cz61N_+{9qr=uTXjLjx3S}hSHjHCrPx4?D`FUh6) zA8{f0mwXZ5mf2RBhw!@r3uhn-kP{YaD6yrrShi*}FNWD{L7%>DFx1WtC}jOSpoJ9BMy~%rKu` zB3a{Ud;cO^=lz~yLegeBzfJ%Ow}bu?Gjg}5rz@9-?J_q_;|XnBZB{ov9UBT9 z*rThFAM}!KIsavRb&cKABZhmlk-#fk;~~yE~WtMs6x_t~RsB%2E4X+w%CL7LH+b;GeNu94I^e9T)h9 z({*yc4JiDF$oT)C0Q_GyF`cYP(P}gqv$#-TK;=dX_Y9^uKyEaU8$~DE-YYw|+QgS_ zRCZR`WZo$Q@Az=@;Y&Q4UVOnjw=My`iyPTVle52$2j}pmuFqO^pojAhI``Z6HE*8XgXKru$`(a1SK_%MX^EPoV=r} za`?urt!z+I_FFB%O#LUtJ(?e54@P~gj#`*BdrX&$V(E_oK(Eo%sx1mW+D?*a86{SWEL*_Y zH{{YRH3vHaY|MVj1bytkI2r%C0Q?(3&+t85ErU!+%Dh>U!ay+oOT-5{*j;GhTR61S zl$Is6VKc&}&r8^>B_rQ82RTD#gihzgO&$I-N@vObyCp^DhFs?6UtIp9D=t$p(fPN# zaLii31BeQ^b+3%8|N9-i-;K4*WRmi>nQ_7nY~mhZGQu9%X8z4FJHfa=ID4&`wn)TT zlB=zalo$&qF@I7ZZ?!5Ed+X1~vf>CySHNt*if=x>uGku5l~;Vgi?g>HYrB$!xv>?9 z(GxGs!HNq-{hhJN)-l$fT=B3xL+sVCJR^m>W8X1{I8%hA?7L#C&5_nK&P0@+WlhSC zw0`Ge8);ADm}`Vk6t@~R#r4*dAD3jk%lf&Cu2b2v8hy~(Zxcs|oKiC}bnK-1&=F)T zG6|g8YUXf#x)s0gXT*Pclk$Uw82+_79b#SRsVMZ@PapYDkn!J#H}>2ANr;Aq@w%Q# z*3rHrPj^53!dTV0#2&FZmF-|mkVW+!T`aU~j_o+-(tS*IfS82&`;2R;pTE`R#xtJlRe4JiDF%J@S>uF3x<%$*Lx?5pT3MaA2& z8>X@-eTt}33ET1O0fX-_|$%}`dH zmCkQUD&>-ALT^m(G1*P}O)bHML*HCc#bq%WiVBL@n7o&zRIMtR#+^cTAP3?!?BlA` z>3UJkY0|lhpsLL5po&Lap&oWZD9`BL0ar1oJr46WgF|(^7CdB}5|qX@62qqs<=Vq2 z$-tw`=tUQ|jZ~!ZgDna=)+~M_srk~xe>nrCopS2B?rkgQsHR?x+8MMV(q}XFS03mi z|HEYbKMBBJrdd!g&F=A>hi;yECr3)K8L^#)eb|ic$>AK?iT2}19FM)^&At04t%ZyY z=i_(-zpq}|O0tGxoPVr7q$I5B3?fCY<@KUY^KpGLwDsOrK0@i(+nN(WoXpK^5^3U+ zxNS@Y3%qhr&n$id@Or;{Z)q*G?#_a3AjqTCcp?%gElesEOj`|o^6<&ENVGRe z*5m$7?HN4CII%cQ(dx|zmzev<^R1_i<9D>ON`=HEW+#s3i*kaGg|!B;Z=)CaP|k{a zk{q2K8&6mFQ~MjG#{KLv>W$-dAN^JdZ#4bByE|_m4`ILdN`3Ix$@m{Y z2|xbJF+;Y(vOaGgWN&@_2HwVVdVP_yN-rwdWO(;U5P3D74|zWc9snXXbTCGfxW)#0 z8pC$&|A}1K3BuacA>QE1=vXGLjMo>jr;{x*8O z^jLZFnnQk`4K7t>ZrUeKJR!2x)w!^qxgUS};GZPpe=q?5ha+O0WHb;ulT;L4mn=Bc zEa(=JtuK1(A&UO@E_#+ylTB}TG5RZrruRrlaMeb$@|0dkDr!()K4D38xyb0_hz!88 z3}n9veTMs#>f`$PX82@N`MtS}ag1{wsQplc=}MCgQp->|WW|4B?RSRbmI6laFEU?T|roHJo$7&!2S9${C(~JWEuZM0r*dCkMq$xI*WDcg+_Hl zA9@E-dIy2t!9i7X%$6MHba2%=&^Hhh_l}@eCg>N~4qorCOMixq(2A`C+f{7c*n%so zAFVWRK_+IBwwAx;H0lf)efiSUsF$sNR9R8o62#5PNk1)Pp9$mHA+1bEo$E$v`^34jSy^e}&DnnWJm|DF2Ujv_%;{6= zNhcWnbqSx^{=|+Oub$ezaL097Z}K$M1y=zLmdW(j7Vf+kNCa}yoj$x|$!IpXQg-tuul?bO!DrTHFL2_f^(NIF{u^g}eC zZe1&7>C}jHHK6cMk?}tgfPb)^Nl!;S#EmL*eY(Knc!V%WR4G+XMQdE}Ibi}K`fW3h zJiQ+DiXN6MU|qg`oyu@r(VE5P^ON`k;Gba*y>I~~IZ?$a44V4w@zp}|{LuOlmoGd? zMpT%O)K|s-97{&VXVjYG;n&qPQOx~UbI8ai$8KjPe&+fi_P#`;u1v}z$=S#xqAi!Q zv~2q4Ks@={70nLj=0^0~Xg?&BU(1jBYv{((aW;4u=u@d1=8g+)Amd=I*kn`Sr$^R=F~R?ZXo$Il;7NzDI@ zV-I7T?vqWq=R7eFIbTckLbxzob&sT&6K;(;(Fk8#mZ;kY4y8c|H)@d z^{MnK<2l!#RC)E=Ynoky`F+54q+p#tbZD4$q6(`GPsCc;rp3TF7XChc(O~`~l!zZ1 zY#k=d6-W~GLvQZT*lh_`Yq6j&qWR;qj9Xm#`4gZKOuPdcXd5(8l{)%3@>KU)fSaK) zuK!+zoVDi!4(-D>^Oe&rF3e|ajZw-i*k)Szc_!7WW)Qssb3YB% z9`_49S&6LEAaHiQb<+L}to!AB-dmF9amL0ohy)_lhQqU?yyjicl9DX1nGMcGLiPM= zYxT-_^*Bh`!rl7$i2V{@JpZ?vW#(IW#c8=8xxs7PR}Ig9HCmqg5|gdNN{Z(*#${%O zqk82jwdzFhO$w)6a+!BU-6YIW4My#Z^8?|Zt{@y~>VFcxa>*q^v6}yD2ilNZuqyNh z-_-;QqJ;=5wDKeD+vZ2t(H`ut-kWfJ1bxnxdpbgO0}B6C8UHT=@Q+iEIetbJc{~Us zK4#IxRhQA$_zz7ZdgO|4PX~>Na;GZRG@LfOOQdhl_Yu7E=eU&Wb9CWma5ucJa`Dm|2Toz=7&{tcF=g%=$7a>|q zomRAwk1&>8rk-723{Gly5uF>j(lO{jzoe#Cr9;l~F2olUPj!sF(Cp;$M0+W<4@X!f8Uj};EZImDKXQ8eMG?rt&6A_Q23|G z_FHg0ozDo9p$T*=I@e9X}1hVDig92S3Y-xsr{y+9N6%eY7%y&hx66{m4Ua7fhq(Oc;ge$e@4TkzfGnBn(VM@ZGTlF4=$_djzf_>nAe z=^u03O+((f;+?n#v7?O08ODemo@Le4i#(w4H^}&Z9e_V5*In+T1kJatf`=pm)S1-1 zwD;WW-7Fshtxak61AdQymp4ly$%yx;m(TWi^Xz%}by)WVJ`?+&yqY@9(3@P>IymB< zh9@js%ap( zRy+oqd59`F;z--A z?&-M*BMcyh0LBI+K~WjN3q%y=LZd;&OWaMOS!Y0W@B#!h(cP1E)LgtIyCx(#k%_Jw zV{RrY8ne0^GHzlvoBbcNGb+X#3As6I&nB5slmW!~-|BW`4Evw_&vVXm{{P@9o{#FT zuCA`Bx9Y2}s=oSau5dIkZ0|zwg*pqD%K8V97mW|uetDXQl$8;zSgfa~g}_2v6wtYB zr<%GK?G)KZTeI#N;EPtU-VYTG-;rP4-fmxACJw)C@AoAeecvzI_@FBTYd8_i@e7(X z#;3jpUd^a|(P~9{>U5Gd!wv0mX7)c5GSP;6=(8HkChWPM!vS7JB<8UcOeTXhYZ^{x zteJF@rfkqW56NZ%Nu_y@O&jp+@9|7?Yw`b4GX6)y@P~Zmq}q{IQ%%KdPaZ4W|5wYV zjE#RO>|XV|8FA+0h1lM^dIZJ?DWGc@D`4fQ@F1=?gIa;VToZofVX_~x%p$+?u<5%z zofu)y{AWVsNwj*L<9yYCYi8_foo3uN*bUibN9|I*S-PuC<%?!0rUrm?+H62JzZ<3wrLAm!C7+E4J7sXQkgqV zlu~5I9u4UC()X!d`A@oKPL*}_>C#)O{g0OM|1=DL0!@q+UBCCwsHY{bXwafGE$;O< zO1ZL&?$)4upYgqh5-;*?S&I-lD#w~X?G=74g>FJ<1nh;fX>my5@Wr`n4*8>^$)%sK zsg-&F+R_I<#L6+An@3BX{X@dy#MH+9jn@&yU=E(@np~_!eGcwvC5{fg+!AE!mo# zriA7WU1gZ`)kWBl`b?@@pLR22nY4#>n(>QJ^A>v;^cl&UwGA6QWQ+DiZR5;RDpfFM z_8HBV(6?!gn~g7!;l-}9DYq2qHReFju;1d&-+akk-*oIju_wFM=%~*0I%C*#6L-Y~Y&^No(8k z?$M?6DFy-y z8#o)Vvp4QhsvdA|x3!2-H?cc`DgT%U9;NSyvh8S=hlBUg(l*!qpqd zY5aN><16Sy4O&9BF~8fL^amqFYP=iuOsY3>ovQEo>!IEh&Ct_GfJRWXn-^w6wipdt z#F@3x4)6k!AdL$O+dq42h~>mLwC!gKL+DJ^sQeKcH)(8yoeFg$*}~+gpVg;E)F!TE zauREw_RwC0@$KotDs(?~edhnOW&G);_mKVvWNV4H$Dl1ZTt_{cMlyo<{$f{RjgMS< z)2@^5U$!eQ=9*O7iUqEbNl7$g54D+tp7_%}YK*udTRHH2P3tZr*Z8_KQ+VC0(0rG8 zJdtZqIl7xPQvdvWI~g6*nh#AcDjyEMHVb<<)YA3xo|%GrGY$RoRkfp0P&pctIM+<+ z8GCK5z+lHV@!i)m5b3CdU$-XWO&ZePqjWF`S=ma*2f0=BB#MM8;=6S9u%9QK1E)Xw zhN8-@x8ok`Qbm{6;3^qU6lS|wiE+h2NDTcL{=W17IWqpo!thtN&cwPnEzd-hHN{cc znvI<~G$iJlN}#fr&6O=stt=aQ`tma*ZKe>BN}t=WYS0RU`51L$Y`%U?KE{OdllrSv z8bjT<^jo{q!J_Op*{8L|IzIG|Q_Mv#{fj$Pj{QcbQ{_;-a4~V9<6WmCVO!Ebj257g zcPGg_;6xo#Lb6LM7K%NpZJOT|V^5K)vED;5PdLXq_8(F>=m>uYd^#hMG%!h-*~B@R zT&{tx_%o;zw8pG%uiL}$k6?Uo#lt$M=3l@)P60a)nDtl*2Sa_m*Sdw2(ZRZCC6N|x`A~l zFqeE#P&KFp#zAYV!XKOv?5OWj2>M)hvjHI(>-nDSxm(~i%emj$^MMV#sUN#O{C|v$ zKi%{Y!e1${e_|w@SM_KdEc9!6;phcT4)Oi68*?s=%vQ|rGAc);N#hvk&d`85X76DsUZ>Uy_nj!knH0)%kxNM?dyc?zn{Une z#bk6R){kP_?X$9Qa#)VbvTVnO2QWIUplgmxBAe2d9!|tuf^dFu%Hj-RZ;2^+#|D*q zxNXSrk*7x7^S7dt>V6D=-~NA`jQ`0n{4-ieYPsb!ms*u-;aZHz3|;j-WT~_+$+D2) ze{+S_`Z91fN3aw2*((0*NmL{DWSy~kh%?^_Gs#mSqWT8FAORXu3UtMex?#7(+UfAhToeRB5S0I1mQJY`) z5)F10IT@H5&N~oUrNl~Z+#!;lA>=j7ySl*91B0+WnS}LRtmZ<~_Xk&bwM)&N*n+)X z+m}JA>CAvtlVHdF!HViLYNgP2V#fxDvQ>IEu#;*L^1_c@O4&_rh^4Ooc5P{_yU)^&a!5WY2gQJ)>&^CG^z8@BR|-wEUhS4mhJ$34H{OS zRskE_&S?e(sR6wP`Zo|Wh4}VhkMA3(gS7|lxx-OU@CA2NB{fl-;J9{T zr$BC6Wn5_-Y{tG^z5_c*p^;>z5(ZDZ!G7!d;GZYse+s`i)c;$szDIn?f$fr?fR&v# z3zuTSS-oq6+igth0PFyMG}vZiQ)6+pGDu7A7}_M=H(rJM6gZ$pdA}9B1821EhmHl+ z31|*Z9(NLZ`I!ldmRwup{&BVr4_fDpmP3O}zlK)WeWY?B*75?>7T^)AJ07{f%}KZH zv}z#RasGL*B)TtfrrMXwxQGsLnCX3qyiI zoCR%C3F`&opUjcFPF!G~KZM>%*)q&VMs~RKU>zi|Zr27TQ+@hCXOy1FRk&d3vLCxX z_~*;`pAN%c4-HPW$u<^zmJnF^KT?jp{w2~MG<=#mqjY5W3)o3JL{MYomucC*AyK6} zQHFCqqS_Xt<}87XaPchD$hOxSU%pIcKVW>oFaoF)r4c^OG8mRU3E8xv{=il>)x%M@ zaQ+hLh^1MXvI(=VDcf#$s3hFoJjL26eb^wzT|7qz|#HY zJ2vnZlh2NO;;67P4&FMq=^f;d?)J;Sn$bkHQ0N;BVr<`_H0@k-)I;lNxMO@lMmHk! ztj?#7*0^-HR{OtO#{Wzh{>%jC?oGx~E?OSO{a4(+cMKwv0y3of)ZA(xQIj;^KiHpX z3LY03{^yA1-9{NI^bdYI3ln$cZysCB7c)m~j2N z_JFXM69x*Cps&!?F^TFcEVPuXEPt^qTztv4uBl@2S=;PDIyrcVTC`VQsT=Yvp*Crs z4_cZK_h(UUDXAQ0l6pFx!rG+Iau#ySH^^Oh-RN)EecJy78UM3k_~S%wn<)wZpdG7<yJ#x$HOV2eY2?QfWe|DHbh7s&XZ z3&WogB3)&0VhM?O&+8>fI0!0AvMUPnysu=M{7%2ybw{9S=ESL z7?G$m?>R@JXSu7Y1*b^vuvu+t?*P?)JY%vSht)@%+3Fv}Fy1FLk+39fDjss2^AA!e z{e!S-{G^)oZcwld6O!U5P_EDAa{hSb9-Ly!rD12WL-A~bQFDrne&C$SD%lMGtqD43 z=REtM&-O>uzF`>I42s%tYxVyVW&B&i@W;qs`wM7u!xsDAUHgV&Z)HTcVn14GDMDsc zm~k~VqC4^iQ{7j}%XlH1p!8eszj*7y^pJ3djBcyP3XzB*H zH`kVPmLryvfnl&%uPK^(sOaKDSg{V$69=HTrXid_Emx8O-dk(@KS{>_^Dz7)TJm9q zgO-gxNN36D)uY!+gk`!&7jMful#0xT~QY=oqEg5DZ*Mjv0%SBm-e?A z5ltFDt70bQ38Srayq|PUwpYt!&n18K7z121lly^(%cp4>0$kQh#W?oZqo<}Y`>pH4 z{|jaOzrZgJjsI9^O~)ohxq*&iP`1MR7-77RG0+8m5O$uX?KCP}%HvF`PS7{6GhRC0 zh26iwoDSL|%d}V@h{25OZ7A)tLFFA9_i(AEyxGG*mf*&sj{No&=LF#{gs!r^>P!}> zu3oUCv1d}%N0#&|HR=QKAAm1MsZlS$=d}a8k1H<<sPR| z5>vrH6!}jT=fOTfq8X`A{8f|6+Hb+f59@<}k&J&^82-Dct+q|Kb**nt9mIMQjIZo7 zi>w(uy7aa|Z60Ql2&_9y8_<(e4eHIn`tF{Op=rwrA7k}x?4QVJrXj;+6UYm}j*~rP z3)#;!()Csr`j(NV4~j5OdzNwRu#(I{PBxWEO|`Z(?Yx*w`b$ z;V@A`zQivMwSR`k-u+?5Kzfm65ub0vJb-MIFW7MmV=ubz0(P>m?+uEv_T z!CofyG`f>12K@-mqs5pnqBA_lG{2$!PW={q{IEXw8)f{z3d5i7_rh6jHz6vQ&&`;3 zUJWln8S_vX%4q~{iV;d990%gN(kIT^z04uz4P#SjQ{2i(OYBPB9oS*aV`ivChf@h^ zd<+SAevBuyxcj6tr71Y#apy*~=!jane()kv!Raw{tVh=Ph=FY*&UMfe&>tuh4P2RM zQa&dtaoXrDg}+J0|LZXPH;ZiaQ0KiO(asTB?P_tsywzgmJmUMxUMAjq?+NdmIR~A~ zjM~YHPHq^wM2vN1E5JXMUzW?e-_tJlE9N#Tmz zsbX9%>zZY(@R}5~Nza~O>QY|1-w@Td z)6XlT5Q`as@T(clsXkUqpB+w^>HYd4#z%_Puu?~&&iRS<*Iq)Zy$sWI**-Msc++Jv zRC|1iX1=H=WnzJr!}rQW^Q_p*_Gr7W(f*#Fl-*MJn`QjJ3BzCGoG;GM&VU^pgO{6W z5S5I+dA>MRTVybx9GQX)CoRhh$DZ*K%mFa4qu126<`U*na(DP675n`>t39L5(X>MU zU;BYy%Q9VBUrHD2*R~;qJ>`U*Z7nQJ`(f$%>N%%D)H0ikzDa!9$>3|lP5R~iFI zB%jfai8=>qkUN4D`7X|fcP8BF?`g24mn&0 z`9bAe({7K!>chn89$KFhoU}#{cggLY)_0|g)_2$u6?C`O_`g`j|JyM9_07+Sis(C> zB!%!4`FrOn-9==IA;W0^*VH!;x@_kPoXll=j2_lgNpZ37n!(Us3kzH5Uz%z8CQ9W< z%QzWxI;=25gcWD!EKxC*rfg`(4sNUmZkFl~NvSOX2X417;QTop(!iNqgqeXgoPYIP zhwn{x78z-rNr+=aEB=Zp6SF~V?NDbd&U|mc+}|3XWoB(jj&C*g1=p6)5Y9=zD}T$f zoaQ?Pza|-JAa^W{gasgW9R>ZA+8In0tpf zlf}3>&xzXT=Rw5xd0?a&{fR0?oH{d2oH`ph8Ji&08w1)eB)hmg7woiuN^$zKoBdYh zBgVFF#kaUuec7$}*-NNV3C`(0Ch8S`kb4*Vix*rjTFxDtFHVnIW)Qq8l~Mc)Rx;+y zEE7NCi^N~dC=)*&{f@s!yWaWU<+hqTPQ8Dt@&6JTf4bBclK&uDnRoL`-7}V(YxeYr(m%7`-ay7osT&J&Jg|mZl+bdA!HrJtgJzq4%VB zsC;m9e4c*e;&hRX!gnrV?dKvi))cf?pt*v7k;iulz8C%;?Jc6AcYaH?|H(4`{|LjM z2u0}m878LPK2+mm3OZZ{QN=tjwJq2MM{SrzOxU9xCqJLq;WCLTMQsUf$6B=K1Sf6H zTGtHGprWnUq;k7yy`g1~dO~WO=@XSx2TP&!>Fwx;wb85_t$kdao4%`bqpuj3EX9bp z@vb4PpFypY?p89j&Hrnj@><{(uad*}qiFv_LKyyKBDpgPSW%14FBr}K9*zSSWsl9(l!-a)yNQ+1!w4ReArFvX{6gA5=jC<4V(I|hlW8mH4(m-yBA*Bv-^No0Vxmp)rQNs zwaxgw)2CyMG6=lwmcsuY8Gj7mZt(wq&D%GhF9sG*pQkOHwpWeR$_v{k?4frNwDpcz z-9BayZNIDA)A!KUJ<>z(RJ1jZEo|@EO~X<>y?kMH`$xNR$E|K3xCfj-xw_rEJC@ET z7pO2c$F5>69ivl!T7Wx>Q94GWOXp#v4yl7)CApZrcweifTt-V}OV&xHdAi=H!^#s4 zxzD);^?0*!r1R8mHR9K`MVLRREeWM1ah(FSo2E%qJc+#CQuvq3_+yZK1Aj%qaTjds zM`E7D`54ASrZyl@@1N*1vK9UJKBTvv_`@|7{D1#PB}~r zj_DKD7JRNx*r)#@?8r^;-i%em4XjvNuL@Y$qe7ojf?6Wu%En$MvGK=ZT58%?*6;V0 z&x$==CjL3igqa?bIAbbpGx?cTd}&JRkU-rI(my7231mG!pe*81gD z6@2w9VQdaBSSyy-)UD!YRo26=%zJfRm~S2C`dV! zmUnnL%Jrbn3%$umFN_(1wB}dWK9ZBsix!{7XY_ueH=$8EBgPCL&KFD!%Rui_5~>gs zG@7bZ9p=*#VHj3{HOv9* z0J@gBJqHg$tb?XI&01g2&qfbnT_o|c?9qC%qP`rpgAiqOK4BAdN|N`kX3QG{wwG+` z*-pdztLuY5J$D^tM}?jc{tvH@f435a$|R#)zOb~O+ED!Q)R&G`Zr!$Ec!h~_?+D=IRI(}4cyC;7!Y;J_gG>DtpOd~!IGKHayZQU zK&Tlc0u%|-f(C-3K+zx_Clc0r%#hx4ZLuZ%7g9d|c10{fN2PJ}% zK*^vXpgTZAK|Cl0lnP1%4Fjcv?gZTh$^Z=qjR0kWMuJ9xMuW0I_b#iO36J{x?1w8P zEKSy>OUtbl5-%PI;Lv;5l9Bwv71b5Xt7@w6<_Q{gWgWfdQ`75H`GwZ1rIicr<`=?~ zLPqjSs~`xbFG#OXucy$H^E6dER>Kb(*;shy^u^dOefXWYNE$q zKX!fa$DT@B;1}_mL+$^^?6#NxTFWbFYoAiRkoKXg$aL%U{~KjxZyUerLxbtIYUP0g z2OEUTorM<^WqH$bhUhydKeG0L0%!f|*0T5&a#pB466%H(Nglah_KSWi`|CfH{lx*v zw?8)8K;T@l6_WqhXa7_3X;^=Beej3ACdK~}eryPT`nxNS9;M~L&)?1(;s4{|$>XX2 z@^gvH;1_-SlKy4*o9{UC-bwgl?!3H2Ya-WiBHK?r{_`;po5)|MFIx8JT_!T|8Ot~K zeQ7Fs>cGk&d*aPAe)#I-H;JX@XIrMJ{_eAzuiW$H$Ly#B=6AN9US(P-nknz5{N_=R ziOeXQJ>@>iEBKJhpe}UN>#x_{?6@25dbmMy=rH;?If7AgEQ`0#*?u`AY<@n#@#4%w`NctuEvmD zJTc=cj~H=XjH_&tgEvgXfG`7|(!YGfp}c`Q9i+MTUlTrq%CZ07{bULB!T)D6{`AoM z5dNV=q(Rwcmm&}IepGg%;WzCA4@LYx*4YvR7YJBt6m19L(o&;S4c literal 0 HcmV?d00001 From 7dad2286e25b01ed01325ce53eb8dece5932fa54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 09:58:27 +0200 Subject: [PATCH 301/305] trunk fmt --- src/mesh/LR11x0Interface.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index ecd222c48..0201282db 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -100,13 +100,6 @@ template bool LR11x0Interface::init() // FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option if (res == RADIOLIB_ERR_NONE) res = lora.setRegulatorDCDC(); -// #ifdef TRACKER_T1000_E -// #ifdef LR11X0_DIO_RF_SWITCH_CONFIG -// res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG); -// #else -// res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0); -// #endif -// #endif if (res == RADIOLIB_ERR_NONE) { if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate res = lora.setRxBoostedGainMode(true); From 543e7f334227397b44e6fc5bb47e5dce1b7b396c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 12:03:06 +0200 Subject: [PATCH 302/305] add generic bootloaders by @markbirss --- ...stic_6.1.0_bootloader-0.9.2_s140_6.1.1.hex | 11744 +++++++++++++++ ...stic_6.1.0_bootloader-0.9.2_s140_6.1.1.zip | Bin 0 -> 190874 bytes ...stic_7.3.0_bootloader-0.9.2_s140_7.3.0.hex | 11851 ++++++++++++++++ ...stic_7.3.0_bootloader-0.9.2_s140_7.3.0.zip | Bin 0 -> 192586 bytes ...Meshtastic_6.1.0_bootloader-0.9.2_nosd.uf2 | Bin 0 -> 74752 bytes ...Meshtastic_7.3.0_bootloader-0.9.2_nosd.uf2 | Bin 0 -> 74752 bytes 6 files changed, 23595 insertions(+) create mode 100644 bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.hex create mode 100644 bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.zip create mode 100644 bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.hex create mode 100644 bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.zip create mode 100644 bin/generic/update-Meshtastic_6.1.0_bootloader-0.9.2_nosd.uf2 create mode 100644 bin/generic/update-Meshtastic_7.3.0_bootloader-0.9.2_nosd.uf2 diff --git a/bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.hex b/bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.hex new file mode 100644 index 000000000..eebabf484 --- /dev/null +++ b/bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.hex @@ -0,0 +1,11744 @@ +:04000003F000B2CD8A +:020000040000FA +:1000000000040020810A000015070000610A0000BA +:100010001F07000029070000330700000000000050 +:10002000000000000000000000000000A50A000021 +:100030003D070000000000004707000051070000D6 +:100040005B070000650700006F07000079070000EC +:10005000830700008D07000097070000A10700003C +:10006000AB070000B5070000BF070000C90700008C +:10007000D3070000DD070000E7070000F1070000DC +:10008000FB070000050800000F0800001908000029 +:10009000230800002D080000370800004108000078 +:1000A0004B080000550800005F08000069080000C8 +:1000B000730800007D080000870800009108000018 +:1000C0009B080000A5080000AF080000B908000068 +:1000D000C3080000CD080000D7080000E1080000B8 +:1000E000EB080000F5080000FF0800000909000007 +:1000F000130900001D090000270900003109000054 +:100100003B0900001FB500F003F88DE80F001FBD8C +:1001100000F0ACBC40F6FC7108684FF01022401CA7 +:1001200008D00868401C09D00868401C04D0086842 +:1001300000F037BA9069F5E79069F9E7704770B554 +:100140000B46010B184400F6FF70040B4FF0805073 +:100150000022090303692403406943431D1B104621 +:1001600000F048FA29462046BDE8704000F042BA47 +:10017000F0B54FF6FF734FF4B4751A466E1E11E0DA +:10018000A94201D3344600E00C46091B30F8027B3B +:10019000641E3B441A44F9D19CB204EB134394B25D +:1001A00004EB12420029EBD198B200EB134002EBB2 +:1001B000124140EA0140F0BDF34992B00446D1E952 +:1001C0000001CDE91001FF224021684600F0F4FB58 +:1001D00094E80F008DE80F00684610A902E004C8FB +:1001E00041F8042D8842FAD110216846FFF7C0FF7C +:1001F0001090AA208DF8440000F099F9FFF78AFFCB +:1002000040F6FC7420684FF01025401C0FD0206889 +:1002100010226946803000F078F92068401C08D030 +:100220002068082210A900F070F900F061F9A869AF +:10023000EEE7A869F5E74FF080500369406940F6A2 +:10024000FC71434308684FF01022401C06D0086838 +:1002500000F58050834203D2092070479069F7E788 +:100260000868401C04D00868401C03D00020704778 +:100270009069F9E70420704770B504460068C34DE3 +:10028000072876D2DFE800F033041929631E250021 +:10029000D4E9026564682946304600F062F92A46CE +:1002A0002146304600F031F9AA002146304600F0E0 +:1002B00057FB002800D0032070BD00F009FC4FF46C +:1002C000805007E0201D00F040F90028F4D100F034 +:1002D000FFFB60682860002070BD241D94E80700C3 +:1002E000920000F03DFB0028F6D00E2070BDFFF715 +:1002F000A2FF0028FAD1D4E901034FF0805100EBAE +:10030000830208694D69684382420ED840F6F8704E +:1003100005684FF010226D1C09D0056805EB8305B8 +:100320000B6949694B439D4203D9092070BD55694A +:10033000F4E70168491C03D00068401C02D003E0C8 +:100340005069FAE70F2070BD2046FFF735FFFFF731 +:1003500072FF0028F7D1201D00F0F7F80028F2D135 +:1003600060680028F0D100F0E2F8FFF7D3FE00F05B +:10037000BFF8072070BD10B50C46182802D0012028 +:10038000086010BD2068FFF777FF206010BD41684E +:10039000054609B1012700E0002740F6F8742068FF +:1003A0004FF01026401C2BD02068AA68920000F065 +:1003B000D7FA38B3A86881002068401C27D020688D +:1003C000FFF7BDFED7B12068401C22D026684FF051 +:1003D0008050AC686D68016942695143A9420DD9EA +:1003E000016940694143A14208D92146304600F0E5 +:1003F000B8F822462946304600F087F800F078F831 +:100400007069D2E700F093F8FFF784FEF6E77069B1 +:10041000D6E77669DBE740F6FC7420684FF01026DB +:10042000401C23D02068401C0CD02068401C1FD0EA +:100430002568206805F18005401C1BD027683879A5 +:10044000AA2819D040F6F8700168491C42D001680A +:10045000491C45D00168491C3ED001680968491C07 +:100460003ED00168491C39D000683EE0B069DAE747 +:10047000B569DEE7B769E2E710212846FFF778FEA5 +:100480003968814222D12068401C05D0D4F8001080 +:1004900001F18002C03107E0B169F9E730B108CA63 +:1004A00051F8040D984201D1012000E000208A4259 +:1004B000F4D158B1286810B1042803D0FEE72846CB +:1004C000FFF765FF3149686808600EE0FFF722FE1C +:1004D00000F00EF87169BBE77169BFE7706904E06D +:1004E0004FF480500168491C01D000F0CBFAFEE7C0 +:1004F000BFF34F8F26480168264A01F4E06111439B +:100500000160BFF34F8F00BFFDE72DE9F0411746B3 +:100510000D460646002406E03046296800F054F8EF +:10052000641C2D1D361DBC42F6D3BDE8F08140F69B +:10053000FC700168491C04D0D0F800004FF48051D1 +:10054000FDE54FF010208069F8E74FF080510A690F +:10055000496900684A43824201D810207047002050 +:10056000704770B50C4605464FF4806608E0284693 +:1005700000F017F8B44205D3A4F5806405F5805562 +:10058000002CF4D170BD0000F40A0000000000202F +:100590000CED00E00400FA05144801680029FCD0C5 +:1005A0007047134A0221116010490B68002BFCD0E0 +:1005B0000F4B1B1D186008680028FCD0002010603D +:1005C00008680028FCD07047094B10B501221A605A +:1005D000064A1468002CFCD0016010680028FCD08A +:1005E0000020186010680028FCD010BD00E4014015 +:1005F00004E5014070B50C46054600F073F810B9EB +:1006000000F07EF828B121462846BDE8704000F091 +:1006100007B821462846BDE8704000F037B8000012 +:100620007FB5002200920192029203920A0B000B06 +:100630006946012302440AE0440900F01F0651F80C +:10064000245003FA06F6354341F82450401C8242F8 +:10065000F2D80D490868009A10430860081D016827 +:10066000019A1143016000F03DF800280AD00649C4 +:1006700010310868029A10430860091D0868039A3F +:10068000104308607FBD00000006004030B50F4CED +:10069000002200BF04EB0213D3F800582DB9D3F8A1 +:1006A000045815B9D3F808581DB1521C082AF1D3C3 +:1006B00030BD082AFCD204EB0212C2F80008C3F8CD +:1006C00004180220C3F8080830BD000000E0014013 +:1006D0004FF08050D0F83001082801D0002070473A +:1006E000012070474FF08050D0F83011062905D016 +:1006F000D0F83001401C01D0002070470120704725 +:100700004FF08050D0F830010A2801D00020704707 +:100710000120704708208F490968095808471020B0 +:100720008C4909680958084714208A4909680958FA +:100730000847182087490968095808473020854923 +:100740000968095808473820824909680958084744 +:100750003C20804909680958084740207D490968BC +:100760000958084744207B49096809580847482028 +:1007700078490968095808474C207649096809589A +:10078000084750207349096809580847542071499F +:1007900009680958084758206E49096809580847E8 +:1007A0005C206C4909680958084760206949096854 +:1007B00009580847642067490968095808476820AC +:1007C00064490968095808476C2062490968095852 +:1007D000084770205F4909680958084774205D4937 +:1007E00009680958084778205A490968095808478C +:1007F0007C205849096809580847802055490968EC +:10080000095808478420534909680958084788202F +:1008100050490968095808478C204E490968095809 +:10082000084790204B4909680958084794204949CE +:10083000096809580847982046490968095808472F +:100840009C204449096809580847A0204149096883 +:1008500009580847A4203F49096809580847A820B3 +:100860003C49096809580847AC203A4909680958C1 +:100870000847B0203749096809580847B420354966 +:10088000096809580847B8203249096809580847D3 +:10089000BC203049096809580847C0202D4909681B +:1008A00009580847C4202B49096809580847C82037 +:1008B0002849096809580847CC2026490968095879 +:1008C0000847D0202349096809580847D4202149FE +:1008D000096809580847D8201E4909680958084777 +:1008E000DC201C49096809580847E02019490968B3 +:1008F00009580847E4201749096809580847E820BB +:100900001449096809580847EC2012490968095830 +:100910000847F0200F49096809580847F4200D4995 +:10092000096809580847F8200A490968095808471A +:10093000FC2008490968095808475FF48070054998 +:10094000096809580847000003480449024A034B54 +:100950007047000000000020000B0000000B0000AA +:1009600040EA010310B59B070FD1042A0DD310C82C +:1009700008C9121F9C42F8D020BA19BA884201D97E +:10098000012010BD4FF0FF3010BD1AB1D30703D0C6 +:10099000521C07E0002010BD10F8013B11F8014B7C +:1009A0001B1B07D110F8013B11F8014B1B1B01D198 +:1009B000921EF1D1184610BD02F0FF0343EA032254 +:1009C00042EA024200F005B87047704770474FF0A6 +:1009D00000020429C0F0128010F0030C00F01B800C +:1009E000CCF1040CBCF1020F18BF00F8012BA8BF1A +:1009F00020F8022BA1EB0C0100F00DB85FEAC17CDE +:100A000024BF00F8012B00F8012B48BF00F8012B90 +:100A100070474FF0000200B51346944696462039C1 +:100A200022BFA0E80C50A0E80C50B1F12001BFF4A7 +:100A3000F7AF090728BFA0E80C5048BF0CC05DF80D +:100A400004EB890028BF40F8042B08BF704748BF5B +:100A500020F8022B11F0804F18BF00F8012B7047CF +:100A6000014B1B68DB6818470000002009480A4951 +:100A70007047FFF7FBFFFFF745FB00BD20BFFDE719 +:100A8000064B1847064A1060016881F308884068E1 +:100A900000470000000B0000000B000017040000DE +:100AA000000000201EF0040F0CBFEFF30881EFF3ED +:100AB0000981886902380078182803D100E0000015 +:100AC000074A1047074A12682C3212681047000084 +:100AD00000B5054B1B68054A9B58984700BD0000B0 +:100AE0007703000000000020F00A0000040000006E +:100AF000001000000000000000FFFFFF0090D00386 +:1010000080130020B157020069C00000175702008A +:1010100069C0000069C0000069C000000000000055 +:101020000000000000000000000000000D58020059 +:1010300069C000000000000069C0000069C0000035 +:10104000755802007B58020069C0000069C00000AA +:1010500069C0000069C0000069C0000069C00000EC +:101060008158020069C0000069C000008758020072 +:1010700069C000008D580200935802009958020080 +:1010800069C0000069C0000069C0000069C00000BC +:1010900069C0000069C0000069C0000069C00000AC +:1010A00069C000009F58020069C0000069C00000CC +:1010B00069C0000069C0000069C0000069C000008C +:1010C000A558020069C0000069C0000069C00000A6 +:1010D00069C0000069C0000069C0000069C000006C +:1010E00069C0000069C0000069C0000069C000005C +:1010F00069C0000069C0000069C0000069C000004C +:1011000069C0000069C0000000F002F824F03FFB55 +:101110000AA090E8000C82448344AAF10107DA4552 +:1011200001D124F034FBAFF2090EBAE80F0013F03E +:10113000010F18BFFB1A43F001031847584C020077 +:10114000784C02000A444FF0000C10F8013B13F0F9 +:10115000070408BF10F8014B1D1108BF10F8015B10 +:10116000641E05D010F8016B641E01F8016BF9D103 +:1011700013F0080F1EBF10F8014BAD1C0C1B09D15A +:101180006D1E58BF01F801CBFAD505E014F8016BCC +:1011900001F8016B6D1EF9D59142D6D3704700005E +:1011A0000023002400250026103A28BF78C1FBD870 +:1011B000520728BF30C148BF0B6070471FB500F011 +:1011C0003DF88DE80F001FBD1EF0040F0CBFEFF3BC +:1011D0000880EFF30980014A10470000ABBF000010 +:1011E000F0B44046494652465B460FB402A0013077 +:1011F00001B50648004700BF01BC86460FBC8046CB +:10120000894692469B46F0BC7047000009110000D9 +:101210008269034981614FF001001044704700006A +:101220002512000001B41EB400B514F0CBFE01B4C9 +:101230000198864601BC01B01EBD000024F0A4BA8E +:1012400070B51A4C054609202070A01C00F0D1F89A +:101250005920A08029462046BDE8704008F0CEB84D +:1012600008F0D7B870B50C461149097829B1A0F13A +:1012700060015E2908D3012013E0602804D06928AA +:1012800002D043F201000CE020CC0A4E94E80E009C +:1012900006EB8000A0F58050241FD0F8806E284611 +:1012A000B047206070BD012070470000080000209A +:1012B00018000020F05802003249884201D2012073 +:1012C00070470020704770B50446A0F500002E4E10 +:1012D000B0F1786F02D23444A4F500042948844266 +:1012E00001D2012500E0002500F043F848B125B9FE +:1012F000B44204D32548006808E0012070BD0020F6 +:1013000070BD002DF9D1B442F9D321488442F6D200 +:10131000F3E710B50446A0F50000B0F1786F03D2F2 +:1013200019480444A4F5000400F023F84FF080416C +:1013300030B11648006804E08C4204D2012003E07A +:1013400013488442F8D2002080F0010010BD10B58F +:1013500020B1FFF7DEFF08B1012010BD002010BD55 +:1013600010B520B1FFF7AFFF08B1012010BD00207C +:1013700010BD084808490068884201D10120704723 +:101380000020704700600200000000201C000020C8 +:101390000800002054000020BEBAFECA10B5044662 +:1013A0000021012000F03DF800210B2000F039F869 +:1013B0000421192000F035F804210D2000F031F847 +:1013C00004210E2000F02DF804210F2000F029F850 +:1013D0000421C84300F025F80621162000F021F86A +:1013E0000621152000F01DF82046FFF729FF0020F8 +:1013F00010BDB62101807047FFF732BF114870471A +:1014000010487047104A10B514680F4B0F4A083344 +:101410001A60FFF727FF0C48001D046010BD7047DD +:1014200070474907090E002804DB00F1E02080F82E +:101430000014704700F00F0000F1E02080F8141D48 +:101440007047000003F9004210050240010000014E +:10145000FE48002101604160018170472DE9F7439A +:10146000044692B091464068FFF771FF40B1606852 +:10147000FFF776FF20B9607800F00300022801D062 +:10148000012000E00020F14E30724846FFF71BFFBC +:1014900018B1102015B0BDE8F0834946012001F0D5 +:1014A0008EFE0028F6D101258DF842504FF4C05031 +:1014B000ADF84000002210A9284606F009FC0028DB +:1014C000E8D18DF842504FF428504FF00008ADF8A5 +:1014D000400047461C216846CDF81C8024F0EFF8F8 +:1014E0009DF81C0008AA20F00F00401C20F0F0001E +:1014F00010308DF81C0020788DF81D0061789DF863 +:101500001E0061F3420040F001008DF81E009DF8BE +:1015100000000AA940F002008DF800002089ADF813 +:101520003000ADF83270608907AFADF834000B972A +:10153000606810AC0E900A94684606F0BCF900286A +:10154000A8D1BDF8200030808DF8425042F601202D +:10155000ADF840009DF81E0008AA20F00600801C8F +:1015600020F001008DF81E000220ADF83000ADF82B +:10157000340013A80E900AA9684606F09CF90028CA +:1015800088D1BDF820007080311D484600F033F945 +:10159000002887D18DF8425042F6A620ADF84000D1 +:1015A0001C216846CDF81C8024F089F89DF81C00A9 +:1015B000ADF8345020F00F00401C20F0F000103047 +:1015C0008DF81C009DF81D0008AA20F0FF008DF882 +:1015D0001D009DF81E000AA920F0060040F0010041 +:1015E000801C8DF81E009DF800008DF8445040F0DE +:1015F00002008DF80000CDE90A4711A80E90ADF861 +:101600003050684606F057F9002899D1BDF82000FF +:10161000F08000203EE73EB504460820ADF800000B +:101620002046FFF750FE08B110203EBD21460120A4 +:1016300001F0C5FD0028F8D12088ADF804006088CD +:10164000ADF80600A088ADF80800E088ADF80A0003 +:101650007E4801AB6A468088002106F035FDBDF862 +:1016600000100829E1D003203EBD1FB5044600202C +:1016700002900820ADF80800CDF80CD02046FFF706 +:1016800022FE10B1102004B010BD704802AA81885B +:101690004FF6FF7006F05AFF0028F4D1BDF808108D +:1016A000082901D00320EEE7BDF800102180BDF825 +:1016B00002106180BDF80410A180BDF80610E18021 +:1016C000E1E701B582B00220ADF800005F4802AB4F +:1016D0006A464088002106F0F7FCBDF80010022998 +:1016E00000D003200EBD1CB5002100910221ADF8F1 +:1016F00000100190FFF70DFE08B110201CBD5348EB +:101700006A4641884FF6FF7006F020FFBDF80010D2 +:101710000229F3D003201CBDFEB54C4C06461546ED +:10172000207A0F46C00705D00846FFF7CCFD18B158 +:101730001020FEBD0F20FEBDF82D01D90C20FEBDEE +:101740003046FFF7C0FD18BB208801A905F0B8FDA1 +:101750000028F4D130788DF80500208801A906F022 +:1017600091FC0028EBD100909DF800009DF8051039 +:1017700040F002008DF80000090703D040F0080097 +:101780008DF800002088694606F019FC0028D6D1A3 +:10179000ADF8085020883B4602AA002106F094FCD0 +:1017A000BDF80810A942CAD00320FEBD7CB505468D +:1017B0000020009001900888ADF800000C462846F3 +:1017C0000195FFF7C4FD18B92046FFF7A2FD08B147 +:1017D00010207CBD15B1BDF8000050B11B486A4611 +:1017E00001884FF6FF7006F0B1FEBDF800102180B1 +:1017F0007CBD0C207CBD30B593B0044600200D4666 +:101800000090142101A823F05AFF1C2108A823F0FE +:1018100056FF9DF80000CDF808D020F00F00401CC6 +:1018200020F0F00010308DF800009DF8010020F04D +:10183000FF008DF801009DF8200040F002008DF8B7 +:10184000200001208DF8460002E000002002002068 +:1018500042F60420ADF8440011A801902088ADF8AC +:101860003C006088ADF83E00A088ADF84000E088FC +:10187000ADF842009DF8020006AA20F00600801C88 +:1018800020F001008DF802000820ADF80C00ADF842 +:1018900010000FA8059001A908A806F00CF8002870 +:1018A00003D1BDF818002880002013B030BD00001F +:1018B000F0B5007B059F1E4614460D46012800D05A +:1018C000FFDF0C2030803A203880002C08D0287AA6 +:1018D000032806D0287B012800D0FFDF1720608175 +:1018E000F0BDA889FBE72DE9F04786B0144691F8D2 +:1018F0000C900E9A0D46B9F1010F0BD01021007B10 +:101900002E8A8846052807D0062833D0FFDF06B088 +:10191000BDE8F0870221F2E7E8890C2100EB4000E6 +:1019200001EB4000188033201080002CEFD0E889B4 +:10193000608100271AE00096688808F1020301AA76 +:10194000696900F084FF06EB0800801C07EB470183 +:1019500086B204EB4102BDF8040090810DF106014E +:1019600040460E3212F0D3FD7F1CBFB26089B842F0 +:10197000E1D8CCE734201080E889B9F1010F11D00B +:10198000122148430E301880002CC0D0E8896081B5 +:101990004846B9F1010F00D00220207300270DF155 +:1019A000040A1FE00621ECE70096688808F10203AC +:1019B00001AA696900F04BFF06EB0800801C86B2A3 +:1019C000B9F1010F12D007EBC70004EB4000BDF8DE +:1019D0000410C18110220AF10201103023F0CEFD63 +:1019E0007F1CBFB26089B842DED890E707EB4701A1 +:1019F00004EB4102BDF80400D0810AF10201404627 +:101A0000103212F084FDEBE72DE9F0470E4688B066 +:101A100090F80CC096F80C80378AF5890C20109944 +:101A200002F10C044FF0000ABCF1030F08D0BCF126 +:101A3000040F3ED0BCF1070F7DD0FFDF08B067E791 +:101A400005EB850C00EB4C00188031200880002A43 +:101A5000F4D0A8F1060000F0FF09558125E0182117 +:101A600001A823F02CFE00977088434601AA7169F3 +:101A700000F0EDFEBDF804002080BDF80600E08017 +:101A8000BDF808002081A21C0DF10A01484612F0A1 +:101A90003EFDB9F1000F00D018B184F804A0A4F8FD +:101AA00002A007EB080087B20A346D1EADB2D6D291 +:101AB000C4E705EB850C00EB4C0018803220088051 +:101AC000002ABBD0A8F1050000F0FF09558137E0DE +:101AD00000977088434601AA716900F0B8FE9DF82E +:101AE0000600BDF80410E1802179420860F300018E +:101AF00062F34101820862F38201C20862F3C3010A +:101B0000020962F30411420962F34511820962F38A +:101B100086112171C0096071BDF80700208122463D +:101B20000DF10901484612F0F2FC18B184F802A048 +:101B3000A4F800A000E007E007EB080087B20A3431 +:101B40006D1EADB2C4D279E7A8F1020084B205FBE4 +:101B500008F000F10E0CA3F800C035230B80002A1A +:101B6000A6D055819481009783B270880E32716936 +:101B700000F06DFE62E72DE9F84F1E460A9D0C4607 +:101B800081462AB1607A00F58070D080E0891081AA +:101B900099F80C000C274FF000084FF00E0A0D28A2 +:101BA00073D2DFE800F09E070E1C28303846556AD5 +:101BB00073737300214648460095FFF779FEBDE830 +:101BC000F88F207B9146082802D0032800D0FFDF41 +:101BD000378030200AE000BFA9F80A80EFE7207BB9 +:101BE0009146042800D0FFDF378031202880B9F1EA +:101BF000000FF1D1E3E7207B9146042800D0FFDFFE +:101C000037803220F2E7207B9146022800D0FFDFA8 +:101C100037803320EAE7207B1746022800D0FFDF19 +:101C20003420A6F800A02880002FC8D0A7F80A808A +:101C3000C5E7207B1746042800D0FFDF3520A6F833 +:101C400000A02880002FBAD04046A7F80A8012E0F2 +:101C5000207B1746052802D0062800D0FFDF102081 +:101C6000308036202880002FA9D0E0897881A7F81D +:101C70000E80B9F80E00B881A1E7207B91460728B5 +:101C800000D0FFDF37803720B0E72AE04FF01200A6 +:101C900018804FF038001700288090D0E0897881B4 +:101CA000A7F80E80A7F8108099F80C000A2805D034 +:101CB0000B2809D00C280DD0FFDF80E7207B0A28F5 +:101CC00000D0FFDF01200AE0207B0B2800D0FFDFDF +:101CD000042004E0207B0C2800D0FFDF05203873AF +:101CE0006DE7FFDF6BE770B50C46054601F0ABFB17 +:101CF00020B10078222804D2082070BD43F20200EF +:101D000070BD0521284610F075FE206008B1002046 +:101D100070BD032070BD30B44880087820F00F00FB +:101D2000C01C20F0F000903001F8080B1DCA81E8BB +:101D30001D0030BC07F0E3BB2DE9FF4784B000274E +:101D40008246029707989046894612300AF0DCF9DD +:101D5000401D20F00306079828B907A95046FFF751 +:101D6000C2FF002854D1B9F1000F05D00798017BBC +:101D700019BB052504681BE098F80000092803D06A +:101D80000D2812D0FFDF46E0079903254868B0B35D +:101D9000497B42887143914239D98AB2B3B2011D5D +:101DA00010F09BFC0446078002E0079C04250834E1 +:101DB0000CB1208810B1032D29D02CE00798012107 +:101DC00012300AF0D3F9ADF80C00024602AB2946F6 +:101DD000504608F000FA070001D1A01C02900798B5 +:101DE0003A461230C8F80400A8F802A003A94046F9 +:101DF000029B0AF0C8F9D8B10A2817D200E006E021 +:101E0000DFE800F007091414100B0D14141213204E +:101E100014E6002012E6112010E608200EE643F238 +:101E200003000BE6072009E60D2007E6032005E680 +:101E3000BDF80C002346CDE900702A4650460799AC +:101E400000F015FD57B9032D08D10798B3B2417BB7 +:101E5000406871438AB2011D10F053FCB9F1000FC4 +:101E6000D7D0079981F80C90D3E72DE9FE4F914622 +:101E70001A881C468A468046FAB102AB494608F0E9 +:101E8000AAF9050019D04046A61C278810F0F6FED6 +:101E90003246072629463B46009610F004FB208870 +:101EA0002346CDE900504A465146404600F0DFFC4B +:101EB000002020800120BDE8FE8F0020FBE710B548 +:101EC00086B01C46AAB104238DF800301388ADF803 +:101ED00008305288ADF80A208A788DF80E200988DB +:101EE000ADF80C1000236A462146FFF725FF06B027 +:101EF00010BD1020FBE770B50D46052110F07AFDEE +:101F0000040000D1FFDF294604F11200BDE8704053 +:101F10000AF015B92DE9F8430D468046002607F072 +:101F2000EBFA04462878102878D2DFE800F0773BF7 +:101F30003453313112313131083131313131287975 +:101F4000001FC0B2022801D0102810D114BBFFDF3F +:101F500035E004B9FFDF0521404610F04BFD007B62 +:101F6000032806D004280BD0072828D0FFDF072637 +:101F700055E02879801FC0B2022820D050B1F6E782 +:101F80002879401FC0B2022819D0102817D0EEE7D8 +:101F900004B9FFDF13E004B9FFDF287901280ED16F +:101FA000172137E00521404610F024FD070000D13D +:101FB000FFDF07F1120140460AF09EF82CB12A46D5 +:101FC00021464046FFF7A7FE29E01321404602F0D4 +:101FD000F7FC24E004B9FFDF0521404610F00AFDBC +:101FE000060000D1FFDF694606F112000AF08EF804 +:101FF000060000D0FFDFA988172901D2172200E0D0 +:102000000A46BDF80000824202D9014602E005E01E +:102010001729C5D3404600F03AFCD0E7FFDF304631 +:10202000BDE8F883401D20F0030219B102FB01F066 +:10203000001D00E000201044704713B5009848B11F +:102040000024684610F0F3FA002C02D1F74A0099F8 +:1020500011601CBD01240020F4E72DE9F0470C4677 +:1020600015462421204623F02AFB05B9FFDFA87876 +:1020700060732888DFF8B4A3401D20F00301AF7817 +:102080008946DAF8000010F0F0FA060000D1FFDF10 +:102090004FF000082660A6F8008077B109FB07F131 +:1020A000091D0AD0DAF8000010F0DFFA060000D1AE +:1020B000FFDF6660C6F8008001E0C4F8048029886C +:1020C00004F11200BDE8F0470AF008B82DE9F04726 +:1020D000804601F112000D4681460AF015F8401DB8 +:1020E000D24F20F003026E7B14462968386810F046 +:1020F000E7FA3EB104FB06F2121D03D069683868A6 +:1021000010F0DEFA052010F01DFC0446052010F04A +:1021100021FC201A012802D1386810F09BFA4946A8 +:102120004046BDE8F04709F0EEBF70B50546052111 +:1021300010F060FC040000D1FFDF04F1120128461A +:10214000BDE8704009F0D8BF2DE9F04F91B04FF0D5 +:10215000000BADF834B0ADF804B047880C46054626 +:1021600092460521384610F045FC060000D1FFDFFD +:1021700024B1A780A4F806B0A4F808B029780922F1 +:102180000B20B2EB111F7DD12A7A04F11001382700 +:102190004FF00C084FF001090391102A73D2DFE8C9 +:1021A00002F072F2F1F07F08D2888D9F3DDBF3EEF2 +:1021B000B6B6307B022800D0FFDFA88908EBC0014B +:1021C000ADF804103021ADF83410002C25D060811A +:1021D000B5F80E9000271DE004EBC708317C88F8A5 +:1021E0000E10F189A8F80C10CDF80090688804232F +:1021F00004AA296900F02BFBBDF81010A8F81010F4 +:1022000009F10400BDF812107F1C1FFA80F9A8F82C +:102210001210BFB26089B842DED80DE1307B0228CF +:1022200000D0FFDFE98908EBC100ADF804003020E1 +:10223000ADF83400287B0A90001FC0B20F90002C2C +:10224000EBD06181B5F81090002725E0CDF8009023 +:102250006888696903AA0A9B00F0F9FA0A9804EBF6 +:10226000C70848441FFA80F908F10C0204A90F9826 +:1022700012F04DF918B188F80EB0A8F80CB0BDF8FE +:102280000C1001E0D4E0CFE0A8F81010BDF80E105B +:102290007F1CA8F81210BFB26089B842D6D8CBE034 +:1022A0000DA8009001AB224629463046FFF71BFBE4 +:1022B000C2E0307B082805D0FFDF03E0307B082830 +:1022C00000D0FFDFE8891030ADF804003620ADF80B +:1022D0003400002C3FD0A9896181F189A18127E0D8 +:1022E000307B092800D0FFDFA88900F10C01ADF890 +:1022F00004103721ADF83410002C2CD06081E8890F +:102300000090AB89688804F10C02296956E0E889DD +:102310003921103080B2ADF80400ADF83410002C33 +:1023200074D0A9896181287A0E280AD002212173EC +:10233000E989E181288A0090EB8968886969039AB4 +:102340003CE00121F3E70DA8009001AB22462946AD +:102350003046FFF759FB6FE0307B0A2800D0FFDFE3 +:102360001220ADF80400ADF834704CB3A989618136 +:10237000A4F810B0A4F80EB084F80C905CE020E053 +:1023800002E031E039E042E0307B0B2800D0FFDF93 +:10239000288AADF834701230ADF8040084B10421FD +:1023A0002173A9896181E989E181298A2182688A69 +:1023B00000902B8A688804F11202696900F047FADC +:1023C0003AE0307B0C2800D0FFDF1220ADF804008B +:1023D000ADF834703CB305212173A4F80AB0A4F819 +:1023E0000EB0A4F810B027E00DA8009001AB224673 +:1023F00029463046FFF75CFA1EE00DA8009001ABBD +:10240000224629463046FFF7B6FB15E034E03B2173 +:10241000ADF80400ADF8341074B3A4F80690A4F835 +:1024200008B084F80AB007E0FFDF05E010000020E4 +:10243000297A012917D0FFDFBDF80400AAF80000AF +:102440006CB1BDF834002080BDF804006080BDF898 +:102450003400392803D03C2801D086F80CB011B0E4 +:102460000020BDE8F08F3C21ADF80400ADF8341039 +:1024700014B1697AA172DFE7AAF80000EFE72DE94D +:10248000F84356880F46804615460521304610F021 +:10249000B1FA040000D1FFDF123400943B464146FC +:1024A00030466A6809F0A3FFBAE570B50D4605210C +:1024B00010F0A0FA040000D1FFDF294604F1120059 +:1024C000BDE8704009F02DBE70B50D46052110F035 +:1024D00091FA040000D1FFDF294604F11200BDE8A3 +:1024E000704009F04BBE70B50546052110F082FA28 +:1024F000040000D1FFDF04F1080321462846BDE8AF +:1025000070400422B1E470B50546052110F072FA5E +:10251000040000D1FFDF214628462368BDE8704053 +:102520000522A2E470B50646052110F063FA040006 +:1025300000D1FFDF04F1120009F0E6FD401D20F09C +:10254000030511E0011D008803224318214630468F +:10255000FFF78BFC00280BD0607BABB2684382B2E4 +:102560006068011D10F003F9606841880029E9D115 +:1025700070BD70B50E46054606F0BEFF040000D1E2 +:10258000FFDF0120207266726580207820F00F0046 +:10259000C01C20F0F00030302070BDE8704006F024 +:1025A000AEBF2DE9F0438BB00D461446814606A917 +:1025B000FFF799FB002814D14FF6FF7601274FF45F +:1025C00020588CB103208DF800001020ADF81000C9 +:1025D00007A8059007AA204604A911F0B7FF78B113 +:1025E00007200BB0BDE8F0830820ADF808508DF847 +:1025F0000E708DF80000ADF80A60ADF80C800CE0AC +:102600000698A17801742188C1818DF80E70ADF80B +:102610000850ADF80C80ADF80A606A4602214846C1 +:10262000069BFFF789FBDCE708B501228DF8022045 +:1026300042F60202ADF800200A4603236946FFF77E +:102640003EFC08BD08B501228DF8022042F60302C7 +:10265000ADF800200A4604236946FFF730FC08BDA8 +:1026600000B587B079B102228DF800200A88ADF854 +:1026700008204988ADF80A1000236A460521FFF7B3 +:102680005BFB07B000BD1020FBE709B1072316E490 +:102690000720704770B588B00D461446064606A957 +:1026A000FFF721FB00280ED17CB10620ADF80850C1 +:1026B0008DF80000ADF80A40069B6A460821DC81CF +:1026C0003046FFF739FB08B070BD05208DF80000DB +:1026D000ADF80850F0E700B587B059B107238DF881 +:1026E0000030ADF80820039100236A460921FFF766 +:1026F00023FBC6E71020C4E770B588B00C46064639 +:10270000002506A9FFF7EFFA0028DCD10698012181 +:10271000123009F02BFD9CB12178062921D2DFE887 +:1027200001F0200505160318801E80B2C01EE28845 +:1027300080B20AB1A3681BB1824203D90C20C2E760 +:102740001020C0E7042904D0A08850B901E0062079 +:10275000B9E7012913D0022905D004291CD0052985 +:102760002AD00720AFE709208DF800006088ADF877 +:102770000800E088ADF80A00A068039023E00A2072 +:102780008DF800006088ADF80800E088ADF80A0018 +:10279000A0680A25039016E00B208DF800006088E1 +:1027A000ADF80800A088ADF80A00E088ADF80C008C +:1027B000A0680B25049006E00C208DF800006078DE +:1027C0008DF808000C256A4629463046069BFFF71F +:1027D000B3FA78E700B587B00D228DF80020ADF888 +:1027E000081000236A461946FFF7A6FA49E700B524 +:1027F00087B071B102228DF800200A88ADF8082058 +:102800004988ADF80A1000236A460621FFF794FABA +:1028100037E7102035E770B586B0064601200D4633 +:10282000ADF808108DF80000014600236A463046D6 +:10283000FFF782FA040008D12946304605F05EFC15 +:102840000021304605F078FC204606B070BDF8B592 +:102850001C4615460E46069F10F0FEF92346FF1D46 +:10286000BCB231462A4600940FF0E9FDF8BD30B401 +:102870001146DDE902423CB1032903D0002330BCFC +:1028800008F034BB0123FAE71A8030BC704770B5FA +:102890000C460546FFF72FFB2146284605F03DFC78 +:1028A0002846BDE87040012105F046BC4FF0E0220B +:1028B0004FF400400021C2F88001BFF34F8FBFF3F7 +:1028C0006F8F1748016001601649900208607047D9 +:1028D000134900B500220A600A60124B4FF0607283 +:1028E0001A60002808BF00BD0F4A104BDFF840C037 +:1028F00001280CD002281CBFFFDF00BD03200860A8 +:102900001A604FF4000000BFCCF8000000BD0220A8 +:1029100008601A604FF04070F6E700B5FFDF00BDB9 +:1029200000F5004008F50140A002002014F5004029 +:1029300004F5014070B50B2000F0BDF9082000F04F +:10294000BAF900210B2000F0D4F90021082000F092 +:10295000D0F9F44C01256560A5600020C4F8400161 +:10296000C4F84401C4F848010B2000F0B5F9082070 +:1029700000F0B2F90B2000F091F9256070BD10B5A0 +:102980000B2000F098F9082000F095F9E5480121A6 +:1029900041608160E4490A68002AFCD10021C0F846 +:1029A0004011C0F84411C0F848110B2000F094F910 +:1029B000BDE81040082000F08FB910B50B2000F0E2 +:1029C0008BF9BDE81040082000F086B900B530B1A1 +:1029D000012806D0022806D0FFDF002000BDD34822 +:1029E00000BDD34800BDD248001D00BD70B5D1491F +:1029F0004FF000400860D04DC00BC5F80803CF4829 +:102A000000240460C5F840410820C43500F053F9A3 +:102A1000C5F83C41CA48047070BD08B5C14A0021E0 +:102A200028B1012811D002281CD0FFDF08BD4FF4C7 +:102A30008030C2F80803C2F84803BB483C3001604C +:102A4000C2F84011BDE80840D0E74FF40030C2F8AA +:102A50000803C2F84803B44840300160C2F844118A +:102A6000B3480CE04FF48020C2F80803C2F84803D2 +:102A7000AD4844300160C2F84811AD48001D0068FF +:102A8000009008BD70B516460D460446022800D9D0 +:102A9000FFDF0022A348012304F110018B4000EB6B +:102AA0008401C1F8405526B1C1F84021C0F8043373 +:102AB00003E0C0F80833C1F84021C0F8443370BDCA +:102AC0002DE9F0411D46144630B1012833D00228CB +:102AD00038D0FFDFBDE8F081891E002221F07F4160 +:102AE0001046FFF7CFFF012D23D00020944D924FC9 +:102AF000012668703E61914900203C39086002203F +:102B0000091D08608D490420303908608B483D3428 +:102B1000046008206C6000F0DFF83004C7F804039C +:102B2000082000F0BBF88349F007091F08602E70E9 +:102B3000D0E70120DAE7012B02D00022012005E0D6 +:102B40000122FBE7012B04D000220220BDE8F04166 +:102B500098E70122F9E774480068704770B500F003 +:102B6000D8F8704C0546D4F840010026012809D158 +:102B7000D4F80803C00305D54FF48030C4F8080327 +:102B8000C4F84061D4F8440101280CD1D4F80803FA +:102B9000800308D54FF40030C4F80803C4F844613A +:102BA000012012F0A9FCD4F8480101280CD1D4F876 +:102BB0000803400308D54FF48020C4F80803C4F884 +:102BC0004861022012F098FC5E48056070BD70B547 +:102BD00000F09FF85A4D0446287850B1FFF706FFE1 +:102BE000687818B10020687012F086FC55480460BF +:102BF00070BD0320F8E74FF0E0214FF40010C1F85A +:102C000000027047152000F067B84B4901200861A9 +:102C1000082000F061B848494FF47C10C1F808035F +:102C20000020024601EB8003C3F84025C3F8402191 +:102C3000401CC0B20628F5D37047410A43F609523A +:102C40005143C0F3080010FB02F000F5807001EB67 +:102C50005020704710B5430B48F2376463431B0C98 +:102C60005C020C60384C03FB0400384B4CF2F72438 +:102C700043435B0D13FB04F404EB402000F580702C +:102C80004012107008681844086010BD2C48406855 +:102C9000704729490120C1F800027047002809DB6C +:102CA00000F01F02012191404009800000F1E02066 +:102CB000C0F80011704700280DDB00F01F02012151 +:102CC00091404009800000F1E020C0F88011BFF37E +:102CD0004F8FBFF36F8F7047002809DB00F01F0292 +:102CE000012191404009800000F1E020C0F88012ED +:102CF00070474907090E002804DB00F1E02080F846 +:102D00000014704700F00F0000F1E02080F8141D5F +:102D100070470C48001F00680A4A0D49121D1160D7 +:102D20007047000000B0004004B500404081004002 +:102D300044B1004008F5014000800040408500405B +:102D40003400002014050240F7C2FFFF6F0C0100A1 +:102D5000010000010A4810B5046809490948083112 +:102D6000086012F05DFC0648001D046010BD0649B5 +:102D7000002008604FF0E0210220C1F88002704777 +:102D80001005024001000001FC1F004010B50D209D +:102D900000F077F8C4B26FF0040000F072F8C0B22F +:102DA000844200D0FFDF3E490120086010BD70B5AD +:102DB0000D2000F048F83B4C0020C4F8000101252C +:102DC000C4F804530D2000F04FF825604FF0E021C7 +:102DD0006014C1F8000170BD10B50D2000F033F88B +:102DE0003048012141600021C0F80011BDE81040C9 +:102DF0000D2000F039B82C4810B504682A492B483A +:102E0000083108602749D1F80001012804D0FFDF0C +:102E10002548001D046010BD2148001D00680022E7 +:102E2000C0B2C1F8002113F047F8F1E710B51D4812 +:102E3000D0F800110029FBD0FFF7DDFFBDE81040FE +:102E40000D2000F011B800280DDB00F01F02012159 +:102E500091404009800000F1E020C0F88011BFF3EC +:102E60004F8FBFF36F8F7047002809DB00F01F0200 +:102E7000012191404009800000F1E020C0F880125B +:102E80007047002804DB00F1E02090F8000405E022 +:102E900000F00F0000F1E02090F8140D4009704799 +:102EA00004D5004000D000401005024001000001A0 +:102EB0004FF0E0214FF00070C1F8800101F5C071C2 +:102EC000BFF34F8FBFF36F8FC1F80001384B8022E3 +:102ED00083F8002441F8800C704700B502460420B6 +:102EE000344903E001EBC0031B792BB1401EC0B293 +:102EF000F8D2FFDFFF2000BD41F8302001EBC00118 +:102F000000224A718A7101220A7100BD294A0021FA +:102F100002EBC0000171704710B50446042800D3CD +:102F2000FFDF244800EBC4042079012800D0FFDF34 +:102F30006079A179401CC0B2814200D060714FF02D +:102F4000E0214FF00070C1F8000210BD2DE9F04102 +:102F500019480568184919480831086014480426BA +:102F600090F80004134F4009154C042818D0FFDFD7 +:102F700016E0217807EBC1000279012A08D14279D5 +:102F800083799A4204D04279827157F831008047A0 +:102F90002078401CC0B22070042801D3002020708B +:102FA000761EF6B2E5D20448001D0560BDE8F0814A +:102FB00019E000E0D80500201005024001000001E2 +:102FC000500000200548064A0168914201D10021C5 +:102FD000016004490120086070470000540000208F +:102FE000BEBAFECA40E5014070B50C46054609F080 +:102FF0009BFB21462846BDE870400AF080BC704724 +:103000002CFFFFFFDBE5B15100600200B600FFFFBF +:103010008C00000069915B00935FFEEDA0843C731F +:10302000F87462145E06C0CB72F2136030B5F84DCE +:103030000446062CA9780ED2DFE804F0030E0E0E2B +:103040000509FFDF08E0022906D0FFDF04E00329BD +:1030500002D0FFDF00E0FFDFAC7030BD30B50446CA +:103060001038EB4D07280CD2DFE800F0040C060CFA +:103070000C0C0C00FFDF05E0287E112802D0FFDFDA +:1030800000E0FFDF2C7630BD2DE9F04111F0C8FBE8 +:10309000044612F0A1FD201AC5B206200FF052FC22 +:1030A000044606200FF056FC211AD94C207E122827 +:1030B00018D000200F1807200FF044FC0646072008 +:1030C0000FF048FC301A3918207E13280CD000204D +:1030D0000144A078042809D000200844281AC0B26E +:1030E000BDE8F0810120E5E70120F1E70120F4E7E8 +:1030F000C74810B590F825004108C54800F12600E2 +:1031000005D00DF018FBBDE8104006F00BB80DF02F +:10311000F3FAF8E730B50446A1F120000D460A287D +:103120004AD2DFE800F005070C1C2328353A3F445B +:10313000FFDF42E0207820283FD1FFDF3DE0B448A8 +:103140008178052939D0007E122836D020782428AD +:1031500033D0252831D023282FD0FFDF2DE0207851 +:1031600022282AD0232828D8FFDF26E0207822280A +:1031700023D0FFDF21E0207822281ED024281CD075 +:1031800026281AD0272818D0292816D0FFDF14E0C7 +:103190002078252811D0FFDF0FE0207825280CD0DB +:1031A000FFDF0AE02078252807D0FFDF05E0207840 +:1031B000282802D0FFDF00E0FFDF257030BD1FB5FB +:1031C00004466A46002001F03CFEB4B1BDF802207E +:1031D0004FF6FF700621824201D1ADF80210BDF812 +:1031E0000420824201D1ADF80410BDF808108142DC +:1031F00003D14FF44860ADF8080068460EF014F9AA +:1032000005F090FF04B010BD70B514460D4606469B +:10321000FEF759F858B90DB1A54201D90C2070BD7F +:10322000002408E056F82400FEF74DF808B11020FD +:1032300070BD641CE4B2AC42F4D3002070BD2DE933 +:10324000F04105461F4690460E4600240068FEF7F2 +:1032500087F830B9A98828680844401EFEF780F82E +:1032600008B110203CE728680028A88802D0B8429E +:1032700002D850E00028F5D0092031E72968085D20 +:10328000B8B1671CCA5D152A2ED03CDC152A3AD28B +:10329000DFE802F03912222228282A2A313139396E +:1032A00039393939393939392200085D30BB641C64 +:1032B000A4B2A242F9D833E00228DDD1A01C085CF8 +:1032C00088F80000072801D2400701D40A2007E748 +:1032D000307840F0010015E0C143C90707E001283C +:1032E00007D010E00620FBE60107A1F1805100297C +:1032F000F5D01846F4E63078810701D50B20EFE6CB +:1033000040F0020030702868005D384484B2A8881C +:10331000A04202D2B0E74FF4485382B2A242ADD8E5 +:103320000020DDE610B5027843F2022354080122A2 +:10333000022C12D003DC3CB1012C16D106E0032C88 +:1033400010D07F2C11D112E0002011E080790324ED +:10335000B4EB901F09D10A700BE08079B2EB901F9B +:1033600003D1F8E780798009F5D0184610BDFF2019 +:103370000870002010BD08B500208DF8000024481A +:1033800090F82E1049B190F82F0002280ED0032893 +:103390000ED0FFDF9DF8000008BD1D4869462530AE +:1033A00001F09EFD0028F5D0FFDFF3E7032000E0E9 +:1033B00001208DF80000EDE738B50C46054669465A +:1033C00001F08EFD00280DD19DF80010207861F3EA +:1033D0004700207055F8010FC4F80100A888A4F830 +:1033E0000500002038BD38B51378A8B1022813D0E5 +:1033F000FF281AD007A46D46246800944C7905EB89 +:103400009414247864F347031370032809D00FE061 +:10341000EC0100200302FF0123F0FE0313700228D9 +:10342000F3D1D8B240F0010005E043F0FE00107087 +:10343000107820F0010010700868C2F80100888838 +:10344000A2F8050038BD02210FF0D4BA38B50C46F9 +:103450000978222901D2082038BDADF800008DF886 +:10346000022068460DF0A9F905F05CFE050003D1C5 +:1034700021212046FFF74EFE284638BD1CB500200E +:103480008DF80000CDF80100ADF80500FB4890F87C +:103490002E00022801D0012000E000208DF8070056 +:1034A00068460DF0FAFA002800D0FFDF1CBD0022AC +:1034B0000A80437892B263F3451222F040020A80F8 +:1034C00000780C282BD2DFE800F02A06090E11162E +:1034D000191C1F220C2742F0110009E042F01D00C8 +:1034E00008800020704742F0110012E042F0100006 +:1034F00040F00200F4E742F01000F1E742F0010072 +:10350000EEE742F0010004E042F00200E8E742F09A +:10351000020040F00400E3E742F00400E0E7072087 +:1035200070472DE9FF478AB00025BDF82C60824620 +:103530001C4691468DF81C50700703D56068FDF756 +:10354000C2FE68B9CD4F4FF0010897F82E0058B170 +:1035500097F82F00022807D16068FDF701FF18B126 +:1035600010200EB0BDE8F087300702D5A089802872 +:103570003ED8700705D4B9F1000F02D097F82400A7 +:10358000A0B3E07DC0F300108DF81B00627D072022 +:10359000032162B3012A2DD0022AE2D0042AE0D10D +:1035A0008DF81710F00628D4A27D07202AB3012A2F +:1035B00023D0022A24D0042AD3D18DF8191000BFB9 +:1035C0008DF81590606810B307A9FFF7ABFE0028CF +:1035D000C7D19DF81C00FF2816D0606850F8011F65 +:1035E000CDF80F108088ADF8130014E000E001E082 +:1035F0000720B6E78DF81780D4E78DF81980DFE74C +:1036000002208DF81900DBE743F20220A9E7CDF88C +:103610000F50ADF81350E07B40B9207C30B9607C8E +:1036200020B9A07C10B9E07CC00601D0062098E744 +:103630008DF800A0BDF82C00ADF80200A068019044 +:10364000A068029004F10F0001F03EFC8DF80C0020 +:10365000FFF791FE8DF80D009DF81C008DF80E000F +:103660008DF816508DF81850E07D08A900F00F0075 +:103670008DF81A0068460EF015F805F053FD70E756 +:10368000F0B58FB000258DF830508DF814508DF8BE +:10369000345006468DF828500195029503950495FF +:1036A00019B10FC901AC84E80F00744CA07805284B +:1036B00001D004280CD101986168884200D120B95A +:1036C0000398E168884203D110B108200FB0F0BD23 +:1036D000207DC00601D51F2700E0FF273B460DAA2D +:1036E00005A903A8FFF7ABFD0028EFD1A08AC10709 +:1036F00002D0C00600D4EE273B460AAA0CA901A8B6 +:10370000FFF79DFD0028E1D19DF81400C00701D00E +:103710000A20DBE7A08A410708D4A17D31B19DF8DA +:103720002810890702D043F20120CFE79DF8281026 +:10373000C90709D0400707D4208818B144F2506166 +:10374000884201D90720C1E78DF818508DF819601B +:10375000BDF80800ADF81A000198079006A80DF012 +:10376000BBFF05F0DFFC0028B0D18DF820508DF8AC +:103770002160BDF81000ADF822000398099008A858 +:103780000DF0C9FF05F0CEFC00289FD101AD241D2E +:1037900095E80F0084E80F00002097E770B586B029 +:1037A0000D46040005D0FDF7DBFD20B1102006B06A +:1037B00070BD0820FBE72078C107A98802D0FF2947 +:1037C00002D303E01F2901D20920F0E7800763D468 +:1037D000FFF75AFC38B12178C1F3C100012804D0A9 +:1037E000032802D005E01320E1E7244890F82400E4 +:1037F000C8B1C8074FF001064FF0000502D08DF8A0 +:103800000F6001E08DF80F50FFF7B5FD8DF8000057 +:1038100020786946C0F3C1008DF8010060788DF80A +:103820000250C20801D00720C1E730B3C20701D05F +:103830008DF80260820705D59DF8022042F0020251 +:103840008DF80220400705D59DF8020040F00400E5 +:103850008DF80200002022780B18C2F38002DA7083 +:1038600001EB40026388D380401CA388C0B253811F +:103870000228F0D3207A78B905E001E0EC010020BD +:103880008DF80260E6E7607A30B9A07A20B9E07A74 +:1038900010B9207BC00601D0062088E704F108009B +:1038A00001F012FB8DF80E0068460DF0BFFA05F02E +:1038B00039FC002889D18DF810608DF81150E0880E +:1038C000ADF81200ADF8145004A80DF002FB05F09D +:1038D00029FC002888D12078C00701D0152000E0FD +:1038E0001320FFF7BBFB002061E72DE9FF47022013 +:1038F000FB4E8DF804000027708EADF80600B84628 +:1039000043F202094CE001A80EF0DBFF050006D0EF +:10391000708EA8B3A6F83280ADF806803EE0039C16 +:10392000A07F01072DD504F124000090A28EBDF8E0 +:103930000800214604F1360301F05FFC050005D0C4 +:103940004D452AD0112D3CD0FFDF3AE0A07F20F07A +:103950000801E07F420862F3C711A177810861F393 +:103960000000E07794F8210000F01F0084F82000A8 +:103970002078282826D129212046FFF7CBFB21E0FB +:1039800014E040070AD5BDF8080004F10E0101F06B +:10399000B1FA05000DD04D4510D100257F1CFFB2B6 +:1039A00002200EF0CFFF401CB842ACD8052D11D03C +:1039B00008E0A07F20F00400A07703E0112D00D0E4 +:1039C000FFDF0025BDF806007086052D04D02846CF +:1039D00004B0C7E5A6F832800020F9E770B50646C6 +:1039E000FFF731FD054605F087FD040000D1FFDF3C +:1039F0006680207820F00F00801C20F0F00020303E +:103A000020700320207295F83E006072BDE870407F +:103A100005F075BD2DE9F04786B0040000D1FFDF49 +:103A20002078AF4D20F00F00801C20F0F0007030A7 +:103A3000207060680178491F1B2933D2DFE801F04C +:103A4000FE32323255FD320EFDFD42FC323232780A +:103A5000FCFCFBFA3232FCFCF9F8FC00C68830466C +:103A6000FFF7F1FC0546304607F03EF9E0B160682B +:103A7000007A85F83E0021212846FFF74BFB3046AF +:103A8000FEF753FB304603F05BFE3146012012F097 +:103A9000D3FCA87F20F01000A877FFF726FF0028AE +:103AA00000D0FFDF06B05DE5207820F0F000203088 +:103AB00020700320207266806068007A607205F0D2 +:103AC0001EFDD8E7C5882846FFF7BDFC00B9FFDF1B +:103AD00060680079012800D0FFDF6068017A06B0D5 +:103AE0002846BDE8F04707F0DEBCC6883046FFF741 +:103AF000AAFC050000D1FFDF05F001FD606831463A +:103B00000089288160684089688160688089A8810F +:103B1000012012F091FC0020A875A87F00F003009E +:103B20000228BFD1FFF7E1FE0028BBD0FFDFB9E7D5 +:103B300000790228B6D000B1FFDF05F0E0FC66682E +:103B4000B6F806A0307A361D012806D0687E814678 +:103B500005F054FA070003D101E0E878F7E7FFDF4A +:103B60000022022150460EF03CFF040000D1FFDF8E +:103B700022212046FFF7CEFA3079012800D002201A +:103B8000A17F804668F30101A177308B2081708B83 +:103B90006081B08BA08184F822908DF80880B8688D +:103BA0000090F86801906A46032150460EF019FF14 +:103BB00000B9FFDFB888ADF81000B8788DF81200B2 +:103BC00004AA052150460EF00CFF00B9FFDFB888AB +:103BD000ADF80C00F8788DF80E0003AA04215046C9 +:103BE0000EF0FFFE00B9FFDF062106F1120001F022 +:103BF0009FF940B37079800700D5FFDF7179E07DD0 +:103C000061F34700E075D6F80600A0617089A083D3 +:103C1000062106F10C0001F08BF9F0B195F82500B2 +:103C20004108607861F347006070D5F8260006E02F +:103C30003EE036E06DE055E04AE02CE040E0C4F8BC +:103C40000200688D12E0E07D20F0FE00801CE0752F +:103C5000D6F81200A061F08AD9E7607820F0FE0063 +:103C6000801C6070F068C4F80200308AE080B8F10F +:103C7000010F04D0B8F1020F05D0FFDF12E70320D7 +:103C8000FFF7D4F90EE7287E122800D0FFDF1120BD +:103C9000FFF7E4F906E706B02046BDE8F04701F07B +:103CA00035BD05F02CFC15F8300F40F0020005E0A2 +:103CB00005F025FC15F8300F40F004002870F1E6FF +:103CC000287E132809D01528D8D11620FFF7C6F969 +:103CD00006B0BDE8F04705F012BC1420F6E700007E +:103CE000EC010020A978052909D00429C6D105F0E6 +:103CF00006FC022006B0BDE8F047FFF797B900794F +:103D00000028BBD0E87801F0C6F805F0F8FB0320E6 +:103D1000F0E7287E122802D1687E01F0BCF811205D +:103D2000D4E72DE9F047054600784FF00008000978 +:103D3000DFF8C0A891460C464646012875D00228F7 +:103D400074D007280AD00A2871D0FFDFA9F80060D4 +:103D500014B1A4F800806680002003E4696801279C +:103D600004F108000A784FF0020C4FF6FF73172A8F +:103D70007ED00EDC142A32D006DC052A68D0092A4F +:103D800010D0102A75D120E0152A73D0162AF9D147 +:103D9000F8E0183A082A6CD2DFE802F0F36B6B0AFD +:103DA000CAF2DFF1C8884FF01208102621468DE1D3 +:103DB0004FF01C080A26BCB38888A0806868807908 +:103DC00020726868C0796072C0E74FF01B08142643 +:103DD00054B30320207268688088A080B6E70A790F +:103DE0003C2AB3D00D1D4FF010082C26E4B1698891 +:103DF000A180298B6182298B2182698BA182A98B69 +:103E0000E1826B790246A91D1846FFF7ECFA297981 +:103E1000002001290CD084F80FC0FF212176E06139 +:103E200020626062A06291E70FE02EE151E18CE137 +:103E3000E77320760AF1040090E80E00DAF810002B +:103E4000C4E90930C4E9071280E7A9F8006083E7F4 +:103E50002C264FF01D08002CF7D00546A380887B48 +:103E60002A880F1D60F300022A80887B400802E048 +:103E70009DE007E1BEE060F341022A80887B800874 +:103E800060F382022A80887BB91CC00860F3C302F9 +:103E90002A80B87A0011401C60F3041202F07F00FF +:103EA00028807878AA1CFFF79EFA387D05F1090270 +:103EB00007F11501FFF797FA387B01F048F82874ED +:103EC000787B01F044F86874F87EA874787AE87416 +:103ED000387F2875B87B6875388AE882DAF81C0064 +:103EE000A861B87A524697F808A0C0F34111012999 +:103EF00004D0108C504503D2824609E0FFDF10E069 +:103F0000022903D0288820F0600009E0504504D140 +:103F1000288820F06000403002E0288840F06000EF +:103F20002880A4F824A0524607F11D01A86996E054 +:103F300011264FF02008002C87D0A380686804F178 +:103F40000A02007920726868007B607269688B1DC4 +:103F500048791946FFF747FAF8E60A264FF0210894 +:103F6000002CE9D08888A080686880792072686811 +:103F7000C07960729AF8301021F004019FE065E08A +:103F80004CE06FE00B264FF02208002CD4D0C888FC +:103F9000A0806868007920726868007A00F0D7FF16 +:103FA00060726868407A00F0D2FFA072CEE61C26EC +:103FB0004FF02608002CBFD0A3806868407960725B +:103FC0006868007AA0720AF1040090E80E00DAF83E +:103FD0001000C4E90530C4E90312686800793C2880 +:103FE00003D0432803D0FFDFB0E62772AEE684F8A3 +:103FF00008C0ABE610264FF02408002C9CD088881F +:10400000A0806868807920816868807A60816868AB +:104010000089A08168688089E08197E610264FF0CA +:104020002308002C88D08888A0806868C0882081F8 +:1040300068680089608168684089A08168688089B3 +:10404000E0819AF8301021F0020138E030264FF07C +:104050002508002C85D0A38069682822496821F0B2 +:104060008DFA73E614264FF01B08002C8ED0A38027 +:10407000686800790128BAD02772DAE90710C4E924 +:10408000031063E64A46214660E0287A012803D0FF +:10409000022817D0FFDF59E610264FF01F08002C2A +:1040A00089D06888A080A8892081E8896081288AD1 +:1040B000A081688AE0819AF8301021F001018AF825 +:1040C000301043E64FF012081026688800F01DFFFC +:1040D0003CE6287AC8B3012838D0022836D0032815 +:1040E00001D0FFDF32E609264FF01108002C85D001 +:1040F0006F883846FFF7A7F990F822A0A780687A62 +:104100002072042138460EF087FC052138460EF057 +:1041100083FC002138460EF07FFC012138460EF06A +:104120007BFC032138460EF077FC022138460EF066 +:1041300073FC062138460EF06FFC072138460EF05E +:104140006BFC504600F0A7FE00E6FFE72846BDE8FE +:10415000F04701F065BC70B5012803D0052800D0F8 +:10416000FFDF70BD8DB22846FFF76DF9040000D166 +:10417000FFDF20782128F4D005F0BEF980B1017866 +:1041800021F00F01891C21F0F00110310170022192 +:10419000017245800020A075BDE8704005F0AFB900 +:1041A00021462846BDE870401322FFF74FB92DE99C +:1041B000F04116460C00804600D1FFDF307820F039 +:1041C0000F00801C20F0F0001030307020780128A3 +:1041D00004D0022818D0FFDFBDE8F0814046FFF789 +:1041E00032F9050000D1FFDF0320A87505F087F93B +:1041F00094E80F00083686E80F00FE4810F8301FDC +:1042000041F001010170E7E74046FFF71CF90500A6 +:1042100000D1FFDFA1884FF6FF700027814202D155 +:10422000E288824203D0814201D1E08840B105F0AA +:1042300066F994E80F00083686E80F00AF75CBE703 +:10424000A87D0128C8D178230022414612F04AF8FF +:104250000220A875C0E738B505460C460846FDF7AC +:1042600032F818BB203D062D4AD2DFE805F0031BCB +:10427000373C42300021052012F0B4F808B111207B +:1042800038BDA01C0DF023F904F04CFF050038D117 +:10429000002208231146052012F024F8052830D00A +:1042A000FFDF2EE06068FDF752F808B1102038BD3E +:1042B000618820886A460DF0C5FB04F033FF0500D5 +:1042C0001FD16068E8B1BDF80010018019E0A07846 +:1042D00000F0010120880DF0E6FB0EE0206801F0FF +:1042E0004BFE05460DE0207800F001000CF0EDF9E2 +:1042F00003E0618820880DF020FB04F013FFF0E755 +:104300000725284638BD70B505460C460846FDF71A +:1043100000F808B1102070BD202D07D0212D0DD040 +:10432000222D0BD0252D09D0072070BD2088A11C7F +:104330000CF0A0FABDE8704004F0F4BE062070BD99 +:10434000AC482530704708B53421AA4821F0B7F9A8 +:104350000120FEF76BFE1120FEF780FEA54968469E +:10436000263105F05FF8A3489DF8002010F8251FBE +:1043700062F3470121F001010170002141724FF405 +:104380006171A0F8071002218172FEF7B1FE00B141 +:10439000FFDFFDF75DF801F084F908BD10B50C46AC +:1043A0004021204621F069F9A07F20F00300A0778A +:1043B000202020700020A07584F8230010BD7047D5 +:1043C0002DE9FC410746FCF77EFF10B11020BDE847 +:1043D000FC81884E06F12501D6F825000090B6F83C +:1043E0002950ADF8045096F82B408DF80640384619 +:1043F000FEF7E2FF0028EAD1FEF77AFE0028E6D0B9 +:10440000009946F8251FB580B471E0E710B5044661 +:10441000FCF77FFF08B1102010BD76487549224691 +:1044200090F8250026314008FEF7DDFF002010BD82 +:104430003EB504460D460846FCF76BFF08B1102058 +:104440003EBD14B143F204003EBD6A4880780528A1 +:1044500003D0042801D008203EBD694602A80AF016 +:10446000AEFA2A4669469DF80800FEF7BCFF002018 +:104470003EBDFEB50D4604004FF0000711D00822E6 +:10448000FEF7C2FE002811D1002608E054F82600ED +:104490006946FEF747FF002808D1761CF6B2AE4207 +:1044A000F4D30CF059F810B143F20320FEBD514E85 +:1044B00086F824700CB300271BE000BF54F82700D7 +:1044C00002A9FEF72FFF00B1FFDF9DF808008DF86D +:1044D000000054F8270050F8011FCDF80110808823 +:1044E000ADF8050068460CF05CF800B1FFDF7F1CFA +:1044F000FFB2AF42E2D386F824500020FEBD2DE982 +:10450000F0478AB01546894604001ED00F4608229F +:104510002946FEF779FE002811D1002613E000BFDE +:1045200054F826006946103000F0DAFC002806D165 +:104530003FB157F82600FCF7C6FE10B110200AB0B4 +:104540000BE4761CF6B2AE42EAD30026A5F10108D0 +:104550001CE000BF06F1010A0AF0FF0712E000BFED +:1045600054F82600017C4A0854F827100B7CB2EB63 +:10457000530F05D106221130113120F0D3FF58B16D +:104580007F1CFFB2AF42EBD30AF0FF064645E1DBEA +:104590004E4624B1012003E043F20520CFE700207E +:1045A0000CF024F810B90CF02DF810B143F20420EF +:1045B000C5E774B300270DF1170828E054F8270069 +:1045C0006946103000F08CFC00B1FFDF54F8270082 +:1045D000102250F8111FCDF801108088ADF80500A9 +:1045E00054F827100DF1070020F0C8FFAEB156F8BF +:1045F000271001E0EC0100201022404620F0BEFF11 +:1046000068460BF0B3FF00B1FFDF7F1CFFB2AF4283 +:10461000D4D3FEF733FF002091E7404601F0A0FC21 +:10462000EEE730B585B00446FCF74DFE18B960687A +:10463000FCF796FE10B1102005B030BD60884AF23C +:10464000B811884206D82078F84D28B1012806D044 +:10465000022804D00720EFE7FEF74AFD18E0607853 +:10466000022804D0032802D043F20220E4E785F8B0 +:104670002F00C1B200200090ADF8040002292CD018 +:10468000032927D0FFDF68460CF055F804F04AFDF7 +:104690000028D1D1606801F056FC207858B1012083 +:1046A0008DF800000DF1010001F05AFC68460DF094 +:1046B0005EFA00B1FFDF207885F82E00FEF7DEFEFF +:1046C000608860B1A88580B20BF088FF00B1FFDF81 +:1046D0000020B1E78DF80500D5E74020FAE74FF458 +:1046E0006170EFE710B50446FCF713FE20B960686F +:1046F00038B1FCF72CFE08B1102010BD606801F045 +:104700002FFCCA4830F82C1F6180C1786170807816 +:104710002070002010BD2DE9F84314468946064656 +:10472000FCF7F7FDA0B94846FCF71AFE80B9204611 +:10473000FCF716FE60B9BD4DA878012800D13CB148 +:104740003178FF2906D049B143F20400BDE8F8836F +:104750001020FBE7012801D00420F7E7CCB305289F +:1047600011D004280FD069462046FEF7A0FE00288D +:10477000ECD1217D49B1012909D0022909D00329B1 +:1047800009D00720E2E70820E0E7024604E0012222 +:1047900002E0022200E00322804623461746002062 +:1047A0000099FEF7BEFE0028D0D1A0892880A07B0A +:1047B000E875BDF80000A882AF75BDF800100907C4 +:1047C00001D5A18931B1A1892980C00704D0032076 +:1047D00003E006E08021F7E70220FEF727FC86F8D9 +:1047E00000804946BDE8F8430020FEF749BF7CB58C +:1047F0008E4C05460E46A078022803D0032801D02F +:1048000008207CBD15B143F204007CBD07200EF0EA +:10481000A1F810B9A078032806D0FEF735FC28B11E +:10482000A078032804D009E012207CBD13207CBDB1 +:10483000304600F013FB0028F9D1E670FEF79BFD2F +:1048400009F0FAFF01208DF800008DF801008DF8C5 +:1048500002502088ADF80400E07D8DF8060068461F +:104860000DF02EF804F05EFC0028E0D1A0780328BB +:1048700004D00420FEF7DAFB00207CBDE07800F0D5 +:10488000FDFA0520F6E71CB510B143F204001CBD8B +:10489000664CA078042803D0052801D008201CBD50 +:1048A00000208DF8000001218DF801108DF8020024 +:1048B00068460DF005F804F035FC0028EFD1A0782B +:1048C000052805D05FF00200FEF7B0FB00201CBDFC +:1048D000E07800F0E0FA0320F6E72DE9FC4180469D +:1048E0000E4603250846FCF73BFD002866D14046EE +:1048F000FEF7A9FD040004D02078222804D2082065 +:1049000065E543F2020062E5A07F00F003073EB1D7 +:10491000012F0CD000203146FEF751FC0500EFD1ED +:10492000012F06D0022F1AD0FFDF28464FE50120C5 +:10493000F1E7A07D3146022801D011B107E0112036 +:1049400045E56846FCF791FE0028D9D16946404606 +:1049500006F06CFD0500E8D10120A075E5E7A07D1B +:10496000032804D1314890F83000C00701D02EB39D +:104970000EE026B1A07F40071ED4002100E00121F7 +:10498000404606F073FD0500CFD1A075002ECCD0B7 +:104990003146404600F0AEFA05461128C5D1A07F49 +:1049A0004107C2D4316844F80E1F7168616040F05D +:1049B000040020740025B8E71125B6E7102006E5AD +:1049C00070B50C460546FEF73EFD010005D02246B7 +:1049D0002846BDE87040FEF739BD43F2020070BDC5 +:1049E00010B5012807D1114B9B78012B00D011B1D4 +:1049F00043F2040010BD0BF023FEBDE8104004F0AC +:104A000091BB012300F051BA00231A46194600F069 +:104A10004CBA70B506460C460846FCF754FC18B96B +:104A20002068FCF776FC18B1102070BDEC01002066 +:104A3000F84D2A7E112A04D0132A00D33EB1082053 +:104A4000F3E721463046FEF7A9FE60B1EDE7092005 +:104A5000132A0DD0142A0BD0A188FF29E5D31520E5 +:104A6000FEF7FCFA0020D4E90012C5E90712DCE7E2 +:104A7000A1881F29D9D31320F2E71CB5E548007E91 +:104A8000132801D208201CBD00208DF800006846C4 +:104A90000CF01FFA04F046FB0028F4D11120FEF7B9 +:104AA000DDFA00201CBD2DE9F04FDFF868A3814638 +:104AB00091B09AF818009B4615460C46132803D36C +:104AC000FFF7DBFF00281FD12046FCF7FCFBE8BB0B +:104AD0002846FCF7F8FBC8BB20784FF00107C00759 +:104AE0004FF0000102D08DF83A7001E08DF83A10D5 +:104AF00020788846C0F3C1008DF8000060788DF8FA +:104B00000910C10803D0072011B0BDE8F08FB0B381 +:104B1000C10701D08DF80970810705D59DF80910EE +:104B200041F002018DF80910400705D59DF80900F4 +:104B300040F004008DF809009DF80900810703D5B5 +:104B400040F001008DF80900002000E015E06E46FD +:104B500006EB400162884A81401CA288C0B20A82EA +:104B60000328F5D32078C0F3C100012825D00328FD +:104B700023D04846FCF7A7FB28B11020C4E7FFE785 +:104B80008DF80970D8E799F80000400808D001288E +:104B900009D0022807D0032805D043F20220B3E74A +:104BA0008DF8028001E08DF80270484650F8011F30 +:104BB000CDF803108088ADF80700FEF7DCFB8DF818 +:104BC00001000021424606EB41002B88C3826B881E +:104BD0008383AB884384EB880385491CC285C9B2B3 +:104BE00082860329EFD3E088ADF83C0068460CF0DC +:104BF000B5FA002887D19AF818005546112801D037 +:104C0000082081E706200DF0A5FE38B12078C0F31A +:104C1000C100012804D0032802D006E0122073E767 +:104C200095F8240000283FF46EAFFEF72DFA022815 +:104C300001D2132068E7584600F010F900289DD1F2 +:104C400085F819B068460CF0C9FB04F06BFA040053 +:104C500094D1687E00F012F91220FEF7FFF9204689 +:104C600052E770B56B4D287E122801D00820DCE693 +:104C70000CF0B7FB04F056FA040005D1687E00F092 +:104C80000AF91120FEF7EAF92046CEE670B506468D +:104C900015460C460846FCF73CFB18B92846FCF7BD +:104CA00038FB08B11020C0E62A46214630460CF0F9 +:104CB000A9FE04F037FA0028F5D121787F29F2D136 +:104CC0000520B2E67CB505460C460846FCF7FBFA23 +:104CD00008B110207CBD2846FEF7B5FB20B1007856 +:104CE000222804D208207CBD43F202007CBD494842 +:104CF00090F83000400701D511207CBD2078C00815 +:104D000002D16078C00801D007207CBDADF800500A +:104D100020788DF8020060788DF803000220ADF84D +:104D2000040068460BF0B6FF04F0FCF97CBD70B5DA +:104D300086B014460D460646FEF785FB28B100787E +:104D4000222805D2082006B06FE643F20200FAE7F7 +:104D50002846FCF705FB20B944B12046FCF7F7FADA +:104D600008B11020EFE700202060A080294890F8CB +:104D70003000800701D51120E5E703A930460BF08C +:104D8000CCFD10B104F0CEF9DDE7ADF80060BDF860 +:104D90001400ADF80200BDF81600ADF80400BDF82F +:104DA0001000BDF81210ADF80600ADF808107DB186 +:104DB000298809B1ADF80610698809B1ADF802106B +:104DC000A98809B1ADF80810E98809B1ADF8041057 +:104DD000DCB1BDF80610814201D9081A2080BDF867 +:104DE0000210BDF81400814201D9081A6080BDF894 +:104DF0000800BDF80410BDF816200144BDF81200EB +:104E00001044814201D9081AA08068460BF044FE84 +:104E1000B8E70000EC0100201CB554490968CDE951 +:104E2000001068460CF09CF904F07CF91CBD1CB520 +:104E300000200090019068460CF092F904F072F99D +:104E40001CBD10800888508048889080C8881081D8 +:104E50008888D080002050819081704710B504462A +:104E600004F0CCF830B1407830B1204604F0EBFBD0 +:104E7000002010BD052010BD122010BD10B504F09B +:104E8000BDF8040000D1FFDF607800B9FFDF607873 +:104E9000401E607010BD10B504F0B0F8040000D1E1 +:104EA000FFDF6078401C607010BD1CB5ADF80000DD +:104EB0008DF802308DF803108DF8042068460CF050 +:104EC00064FD04F02FF91CBD0CB529A2D2E9001233 +:104ED000CDE900120079694601EB501000780CBD55 +:104EE0000278520804D0012A02D043F2022070470F +:104EF000FEF718BA1FB56A46FFF7A3FF68460CF025 +:104F0000A3FA04F00FF904B010BD70B50C0006460A +:104F10000DD0FEF798FA050000D1FFDFA6802889A2 +:104F20002081288960816889A081A889E0817CE549 +:104F300010B500231A4603E0845C2343521CD2B20E +:104F40008A42F9D30BB1002010BD012010BD00B57D +:104F500040B1012805D0022803D0032804D0FFDF88 +:104F6000002000BDFF2000BD042000BD645A0200E7 +:104F7000070605040302010010B50446FCF7A3F977 +:104F800008B1102010BD2078C0F30210042807D803 +:104F90006078072804D3A178102901D8814201D272 +:104FA000072010BDE078410706D421794A0703D4D1 +:104FB000000701D4080701D5062010BD002010BD50 +:104FC00010B513785C08837F64F3C7138377137875 +:104FD0009C08C37F64F30003C3771078C309487843 +:104FE00063F34100487013781C090B7864F347138E +:104FF0000B701378DB0863F3000048705078487139 +:1050000010BD10B5C4780B7864F300030B70C4783E +:10501000640864F341030B70C478A40864F382034A +:105020000B70C478E40864F3C3030B700379117840 +:1050300063F30001117003795B0863F341011170A0 +:1050400003799B0863F3820111700079C00860F353 +:10505000C301117010BD70B514460D46064604F02C +:105060004BFA80B10178182221F00F01891C21F040 +:10507000F001A03100F8081B214620F0C4FABDE879 +:10508000704004F03CBA29463046BDE87040132217 +:10509000FEF7DCB92DE9F047064608A8894690E8F6 +:1050A00030041F4690461421284620F008FB0021BA +:1050B000CAF80010B8F1000F03D0B9F1000F03D106 +:1050C00014E03878C00711D02068FCF722F9C0BB83 +:1050D000B8F1000F07D12068123028602068143022 +:1050E00068602068A8602168CAF8001038788007D6 +:1050F00024D56068FCF72BF918BBB9F1000F21D05B +:10510000FFF71EF90168C6F868118188A6F86C11CE +:10511000807986F86E0101F0F8FCF94FEF60626863 +:1051200062B196F8680106F2691140081032FEF784 +:105130005AF910223946606820F020FA0020BDE8B4 +:10514000F08706E0606820B1E8606068C6F8640136 +:10515000F4E71020F3E730B5054608780C4620F058 +:105160000F00401C20F0F001103121700020607011 +:1051700095F8230030B104280FD0052811D0062857 +:1051800014D0FFDF20780121B1EB101F04D295F875 +:10519000200000F01F00607030BD21F0F0002030D2 +:1051A00002E021F0F00030302070EBE721F0F00059 +:1051B0004030F9E7F0B591B0022715460C46064697 +:1051C0003A46ADF80870092103AB05F004F80490E5 +:1051D000002810D004208DF804008DF80170E03410 +:1051E000099605948DF818500AA968460FF0F2F850 +:1051F00000B1FFDF012011B0F0BD10B588B00C4642 +:105200000A99ADF80000C3B11868CDF802005868DB +:10521000CDF80600ADF80A20102203A820F0AEF960 +:1052200068460CF081F903F07DFF002803D1A17FCF +:1052300041F01001A17708B010BD0020CDF80200A8 +:10524000E6E72DE9F84F0646808A0D4680B2824691 +:10525000FEF7F9F804463078DFF8A48200274FF013 +:105260000209A8F120080F2870D2DFE800F06FF2E1 +:105270003708387D8CC8F1F0EFF35FF3F300A07FBF +:1052800000F00300022809D05FF0000080F0010167 +:1052900050460DF0AFFB050003D101E00120F5E71A +:1052A000FFDF98F85C10C90702D0D8F860000BE067 +:1052B000032105F11D0010F0E0FDD5F81D00914916 +:1052C000B0FBF1F201FB1200C5F81D0070686867C1 +:1052D000B068A8672078252800D0FFDFCAE0A07F4B +:1052E00000F00300022809D05FF0000080F0010107 +:1052F00050460DF07FFB060003D101E00120F5E7E9 +:10530000FFDF3078810702D52178252904D040F0CD +:1053100001003070BDE8F88F85F80090307F28716B +:1053200006F11D002D36C5E90206F3E7A07F00F067 +:105330000300022808D0002080F0010150460DF043 +:1053400059FB040004D102E00120F5E7A7E1FFDFEB +:105350002078C10604D5072028703D346C60D9E759 +:1053600040F008002070D5E7E07F000700D5FFDFA0 +:10537000307CB28800F0010301B05046BDE8F04F28 +:10538000092105F0B3BD04B9FFDF716821B1102216 +:1053900004F1240020F0F2F828212046FDF7BAFE9F +:1053A000A07F00F0030002280ED104F124000023A6 +:1053B00000901A4621465046FFF71FFF112807D0DC +:1053C00029212046FDF7A6FE307A84F82000A1E7C7 +:1053D000A07F000700D5FFDF14F81E0F40F0080083 +:1053E0002070E782A761E761C109607861F341003D +:1053F000014660F382016170307AE0708AE7A07F35 +:1054000000F00300022809D05FF0000080F00101E5 +:1054100050460DF0EFFA040003D101E00120F5E75A +:10542000FFDF022104F1850010F027FD0420287021 +:1054300004F5B4706860B4F88500288230481038EC +:105440007C346C61C5E9028064E703E024E15BE041 +:105450002DE015E0A07F00F00300022807D0002017 +:1054600080F0010150460DF0C5FA18B901E00120A5 +:10547000F6E7FFDF324621465046BDE8F84FEAE541 +:1054800004B9FFDF20782128A1D93079012803D180 +:10549000E07F40F00800E077324621465046FFF7B3 +:1054A000DAFD2046BDE8F84F2321FDF733BE3279FF +:1054B000AA8005F108030921504604F08CFEE8603B +:1054C00010B10520287025E7A07F00F00300022816 +:1054D00008D0002080F0010150460DF08BFA040046 +:1054E00003D101E00120F5E7FFDF04F162010223AF +:1054F0001022081F0DF005F907703179417009E796 +:105500004C02002040420F00A07F00F00300022860 +:1055100008D0002080F0010150460DF06BFA050024 +:1055200003D101E00120F5E7FFDF95F8840000F0EA +:10553000030001287AD1A07F00F00307E07F10F07C +:10554000010602D0022F04D133E095F8A000C00775 +:105550002BD0D5F8601121B395F88320087C62F335 +:1055600087000874A17FCA09D5F8601162F3410071 +:105570000874D5F8601166F300000874AEB1D5F870 +:105580006001102204F1240188351FF0F7FF287E06 +:1055900040F001002876287820F0010005F88809FD +:1055A00000E016B1022F04D02DE095F88800C00766 +:1055B00027D0D5F85C1121B395F88320087C62F3DD +:1055C00087000874A17FCA09D5F85C1162F3410015 +:1055D0000874D5F85C1166F3000008748EB1D5F834 +:1055E0005C01102204F1240188351FF0C7FF2878E0 +:1055F00040F0010005F8180B287820F0010005F8AC +:10560000A009022F44D0002000EB400005EBC000B1 +:1056100090F88800800709D595F87C00D5F86421BA +:10562000400805F17D011032FDF7DDFE8DF8009098 +:1056300095F884006A4600F003008DF8010095F8A3 +:1056400088108DF8021095F8A0008DF8030021460F +:10565000504601F043FA2078252805D0212807D0AC +:10566000FFDF2078222803D922212046FDF752FDB2 +:10567000A07F00F0030002280CD0002080F0010180 +:1056800050460DF0C9F900283FF44FAEFFDF41E668 +:105690000120B9E70120F1E7706847703AE6FFDFC3 +:1056A00038E670B5FE4C002584F85C5025660EF097 +:1056B0005EFE04F11001204603F0DAFE84F830505B +:1056C00070BD70B50D46FDF7BEFE040000D1FFDFD2 +:1056D0004FF4B87128461FF0F2FF04F1240028614E +:1056E000A07F00F00300022808D0012105F1E000AE +:1056F0000EF03EFE002800D0FFDF70BD0221F5E76E +:105700000A46014602F1E0000EF052BE70B50546B1 +:10571000406886B001780A2906D00D2933D00E29B9 +:105720002FD0FFDF06B070BD86883046FDF78BFEB8 +:10573000040000D1FFDF20782128F3D028281BD1D6 +:10574000686802210E3001F0BEF9A8B1686808212E +:10575000801D01F0B8F978B104F1240130460CF055 +:10576000B1F803F0DFFC00B1FFDF06B02046BDE872 +:1057700070402921FDF7CEBC06B0BDE8704003F0B3 +:10578000BEBE012101726868C6883046FDF75BFE27 +:10579000040000D1FFDFA07F00F00301022902D145 +:1057A00020F01000A077207821280AD06868017ABC +:1057B00009B1007980B1A07F00F00300022862D017 +:1057C000FFDFA07F00F003000228ABD1FEF78DF8C9 +:1057D0000028A7D0FFDFA5E703F091FEA17F080610 +:1057E0002BD5E07FC00705D094F8200000F01F0003 +:1057F000102820D05FF0050084F8230020782928A5 +:105800001DD02428DDD13146042010F015FE2221C0 +:105810002046FDF77FFCA07F00F00300022830D077 +:105820005FF0000080F0010130460DF0F5F800282F +:10583000C7D0FFDFC5E70620DEE70420DCE701F084 +:105840000300022808D0002080F0010130460DF04E +:10585000D1F8050003D101E00120F5E7FFDF2521A4 +:105860002046FDF757FC03208DF80000694605F13E +:10587000E0000EF094FD0228A3D00028A1D0FFDFA5 +:105880009FE70120CEE703F03AFE9AE72DE9F043C7 +:1058900087B09946164688460746FDF7D4FD0400B2 +:1058A0004BD02078222848D3232846D0E07F000719 +:1058B00043D4A07F00F00300022809D05FF000006D +:1058C00080F0010138460DF095F8050002D00CE09B +:1058D0000120F5E7A07F00F00300022805D0012198 +:1058E000002238460DF07DF805466946284601F04D +:1058F0001CF9009800B9FFDF45B10098E03505615B +:105900002078222806D0242804D007E0009900201F +:10591000086103E025212046FDF7FCFB00980121EA +:1059200041704762868001A9C0E902890EF052FDEC +:10593000022802D0002800D0FFDF07B0BDE8F083C6 +:1059400070B586B00546FDF77EFD017822291ED987 +:10595000807F00F00300022808D0002080F00101C1 +:1059600028460DF047F804002FD101E00120F5E7AB +:10597000FFDF2AE0B4F85E0004F1620630440178EB +:10598000427829B121462846FFF714FCB0B9C9E690 +:10599000ADF804200921284602AB04F01CFC03905A +:1059A0000028F4D005208DF80000694604F1E000DD +:1059B0000EF0F5FC022801D000B1FFDF0223102217 +:1059C000314604F15E000CF0D2FEB4F8600000280D +:1059D000D0D1A7E610B586B00446FDF734FD0178B6 +:1059E00022291BD9807F00F00300022808D0002064 +:1059F00080F0010120460CF0FDFF040003D101E01E +:105A00000120F5E7FFDF06208DF80000694604F16C +:105A1000E0000EF0C4FC002800D0FFDF06B010BD8F +:105A20002DE9F05F05460C460027007890460109F5 +:105A30003E4604F1080BBA4602297DD0072902D060 +:105A40000A2909D146E0686801780A2905D00D299C +:105A500030D00E292ED0FFDFBBE114271C26002CEE +:105A60006BD08088A080FDF7EEFC5FEA000900D1D2 +:105A7000FFDF99F817005A46400809F11801FDF7B1 +:105A8000B2FC6868C0892082696851F8060FC4F8C2 +:105A900012004868C4F81600A07E20F0060001E05D +:105AA0002C02002040F00100A07699F81E0040F082 +:105AB00020014DE01A270A26002CD1D0C088A080F2 +:105AC000FDF7C1FC050000D1FFDF59462846FFF76E +:105AD00042FB7EE10CB1A88BA080287A0B287DD0F8 +:105AE00006DC01287BD0022808D0032804D135E049 +:105AF0000D2875D00E2874D0FFDF6AE11E27092615 +:105B0000002CADD0A088FDF79EFC5FEA000900D113 +:105B1000FFDF287B00F003000128207A1BD020F053 +:105B200001002072297B890861F341002072297BE2 +:105B3000C90861F3820001E041E1F2E02072297BB3 +:105B4000090961F3C300207299F81E0040F040017A +:105B500089F81E103DE140F00100E2E713270D2611 +:105B6000002CAAD0A088FDF76EFC8146807F00F053 +:105B70000300022808D0002080F00101A0880CF06A +:105B800039FF050003D101E00120F5E7FFDF99F8B7 +:105B90001E0000F00302022A50D0686F817801F0E5 +:105BA00003010129217A4BD021F001012172837870 +:105BB0009B0863F3410121728378DB0863F3820160 +:105BC000217283781B0963F3C3012172037863F3A5 +:105BD00006112172437863F3C71103E061E0A9E085 +:105BE00090E0A1E0217284F809A0C178A172022A94 +:105BF00029D00279E17A62F30001E1720279520858 +:105C000062F34101E1720279920862F38201E1726A +:105C10000279D20862F3C301E1724279217B62F317 +:105C2000000121734279520862F3410121734279E4 +:105C3000920862F382012173407928E0A86FADE7F2 +:105C400041F00101B2E74279E17A62F30001E172C9 +:105C50004279520862F34101E1724279920862F39B +:105C60008201E1724279D20862F3C301E1720279E2 +:105C7000217B62F3000121730279520862F3410132 +:105C800021730279920862F3820121730079C008BE +:105C900060F3C301217399F80000232831D926212C +:105CA00040E018271026E4B3A088FDF7CCFB83461C +:105CB000807F00F00300022809D0002080F001015D +:105CC000A0880CF097FE5FEA000903D101E00120F3 +:105CD000F4E7FFDFE868A06099F8000040F00401F5 +:105CE00089F8001099F80100800708D50120207379 +:105CF0009BF8000023286CD92721584651E084F8EE +:105D00000CA066E015270F265CB1A088FDF79BFB71 +:105D1000814606225946E86808F0CBFA0120A073B4 +:105D2000A0E041E048463CE016270926E4B3287B82 +:105D300020724EE0287B19270E26ACB3C4F808A0C9 +:105D4000A4F80CA0012807D0022805D0032805D00C +:105D5000042803D0FFDF0DE0207207E0697B0428F0 +:105D600001F00F0141F0800121721ED0607A20F015 +:105D700003006072A088FDF766FB054600782128C5 +:105D800027D0232800D0FFDFA87F00F003000228DF +:105D900013D0002080F00101A0880CF03DFE2221EC +:105DA0002846FDF7B7F914E004E0607A20F003001C +:105DB000401CDEE7A8F8006010E00120EAE70CB123 +:105DC0006888A080287A68B301280AD002284FD0BA +:105DD000FFDFA8F800600CB1278066800020BDE8D6 +:105DE000F09F15270F26002CE4D0A088FDF72BFB91 +:105DF000807F00F00300022808D0002080F001011D +:105E0000A0880CF0F7FD050003D101E00120F5E7C3 +:105E1000FFDFD5F81D000622594608F04AFA84F83B +:105E20000EA0D6E717270926002CC3D0A088FDF7BF +:105E30000AFB8146807F00F00300022808D0002082 +:105E400080F00101A0880CF0D5FD050003D101E030 +:105E50000120F5E7FFDF6878800701D5022000E028 +:105E60000120207299F800002328B2D9272159E790 +:105E700019270E26002C9DD0A088FDF7E4FA5FEAD2 +:105E8000000900D1FFDFC4F808A0A4F80CA084F832 +:105E900008A0A07A40F00300A07299F81E10C9096A +:105EA00061F38200A07299F81F2099F81E1012EA7F +:105EB000D11F05D099F8201001F01F0110292BD017 +:105EC00020F00800A07299F81F10607A61F3C300F7 +:105ED0006072697A01F003010129A2D140F0040047 +:105EE000607299F81E0000F003000228E87A16D0CC +:105EF000217B60F300012173AA7A607B62F30000CA +:105F00006073EA7A520862F341012173A97A490861 +:105F100061F3410060735CE740F00800D2E7617B09 +:105F200060F300016173AA7A207B62F300002073A2 +:105F3000EA7A520862F341016173A97A490861F370 +:105F40004100207345E710B5FE4C30B101461022E8 +:105F500004F120001FF012FB012084F8300010BD76 +:105F600010B5044600F0D1FDF64920461022BDE8E8 +:105F7000104020311FF002BB70B5F24D06004FF00B +:105F8000000413D0FBF79FF908B110240CE00621A0 +:105F9000304608F075F9411C05D028665FF0010015 +:105FA00085F85C0000E00724204670BD0020F7E77C +:105FB000007810F00F0204D0012A05D0022A0CD17B +:105FC00010E0000909D10AE00009012807D00228E1 +:105FD00005D0032803D0042801D00720704708709B +:105FE000002070470620704705282AD2DFE800F01D +:105FF00003070F171F00087820F0FF001EE0087845 +:1060000020F00F00401C20F0F000103016E008785F +:1060100020F00F00401C20F0F00020300EE0087847 +:1060200020F00F00401C20F0F000303006E008782F +:1060300020F00F00401C20F0F000403008700020DD +:106040007047072070472DE9F041804688B00D4623 +:1060500000270846FBF784F9A8B94046FDF7F3F995 +:10606000040003D02078222815D104E043F2020076 +:1060700008B0BDE8F08145B9A07F410603D500F026 +:106080000300022801D01020F2E7A07FC10601D44E +:10609000010702D50DB10820EAE7E17F090701D524 +:1060A0000D20E5E700F00300022805D125B12846C0 +:1060B000FEF762FF0700DBD1A07F00F0030002289B +:1060C00008D0002080F0010140460CF093FC06004F +:1060D00002D00FE00120F5E7A07F00F003000228C6 +:1060E0000ED0002080F00101002240460CF079FC27 +:1060F000060007D0A07F00F00300022804D009E0CA +:106100000120EFE70420B3E725B12A4631462046B7 +:10611000FEF756FF6946304600F007FD009800B9CB +:10612000FFDF0099022006F1E0024870C1F82480E8 +:106130004A6100220A81A27F02F00302022A1CD0D7 +:1061400001200871287800F00102087E62F3010046 +:1061500008762A78520862F3820008762A78920834 +:1061600062F3C30008762A78D20862F30410087636 +:1061700024212046FCF7CEFF33E035B30871301DF3 +:1061800088613078400908777078C0F3400048771C +:10619000287800F00102887F62F301008877A27FEF +:1061A000D20962F382008877E27F62F3C3008877C6 +:1061B000727862F304108877A878C87701F1210219 +:1061C00028462031FEF71DFF03E00320087105205B +:1061D000087625212046FCF79DFFA07F20F0400097 +:1061E000A07701A900980EF0F5F8022801D000B1BF +:1061F000FFDF38463CE72DE9FF4F534A0D4699B083 +:106200009A4607CA0AAB002783E807001998FDF7EA +:106210001AF9060006D03078262806D008201DB0CE +:10622000BDE8F08F43F20200F9E7B07F00F0030908 +:10623000B9F1020F0AD05DB91B98FEF79DFE002848 +:10624000EDD1B07F00F00300022801D11B9890BB74 +:10625000B07F00F00300022808D0002080F0010188 +:1062600019980CF0C7FB040003D101E00120F5E709 +:10627000FFDF852D28D007DCF5B1812D1ED0822DC2 +:106280001ED0832D08D11DE0862D1FD0882D1FD054 +:10629000892D1FD08A2D1FD00F2020710F281DD0CF +:1062A00003F02AF9E0B101208DF83C00201D109088 +:1062B0002079B8B15BE111E00020EEE70120ECE7C6 +:1062C0000220EAE70320E8E70520E6E70620E4E706 +:1062D0000820E2E70920E0E70A20DEE707209EE742 +:1062E00011209CE7B9F1020F03D0A56F03D1A06F75 +:1062F00002E0656FFAE7606F804632D04FF0010030 +:1063000001904FF002000090214630461B9AFEF7A4 +:1063100057FE1B98007800F00101A87861F3010096 +:10632000A870B17FC90961F38200A870F17F61F3A1 +:10633000C300A870617861F30410A8702078400948 +:10634000287003E02C0200206C5A02006078C0F331 +:10635000400068701B988078E87000206871287190 +:1063600003E00220019001200090A87898F8021024 +:10637000C0F3C000C1F3C00108405FEA000B2DD09C +:106380005046FAF7A0FF78BBDAF80C00FAF79BFF4B +:1063900050BBDAF81C00FAF796FF28BBDAF80C00BD +:1063A000A060DAF81C00E060607898F8012042EA0A +:1063B000500100BF61F34100607098F80210C0B254 +:1063C00000EA111161F3000060700020207700994D +:1063D00006F11700022908D0012107E0607898F83B +:1063E000012002EA5001E5E732E0002104EB8101DF +:1063F00048610199701C022901D0012100E00021AF +:1064000004EB81014861A87800F00300012857D10E +:1064100098F8020000F00300012851D1B9F1020FF1 +:1064200004D02A1D691D1B98FEF7EBFD287998F80A +:10643000041008408DF83400697998F8052011405F +:106440008DF8381008433BD05046FAF73CFF08B1AE +:106450001020E4E60AF110010491B9F1020F17D0FF +:106460000846002104F18C03CDE9000304F5AE7267 +:1064700002920DAB5A462046FEF70CFE0028E8D1EA +:10648000B9F1020F08D0504608D14FF0010107E0E2 +:1064900050464FF00101E5E70498F5E74FF00001A1 +:1064A00004F1A403CDE9000304F5B072029281F077 +:1064B00001010EAB5A462046FEF7ECFD0028C8D17C +:1064C0006078800734D4A87898F80210C0F3800070 +:1064D000C1F3800108432BD0297898F800000AAA5C +:1064E000B9F1020F06D032F811204300DA4002F071 +:1064F00003070AE032F810204B00DA4012F00307DD +:1065000005D0012F0BD0022F0BD0032F07D0BBF1EA +:10651000000F0DD0012906D0042904D008E002277D +:10652000F5E70127F3E7012801D0042800D104276B +:10653000B07F40F08000B077F17F6BF30001F1771E +:106540006078800706D50320A071BBF1000F0ED143 +:10655000002028E00220022F18D0012F18D0042F8D +:1065600029D00020A071B07F20F08000B0772521D5 +:106570003046FCF7CFFD0FA904F1E0000DF00FFF4E +:1065800010B1022800D0FFDF002048E6A071DFE74D +:10659000A0710D2104F120001FF091F8207840F047 +:1065A0000200207001208DF85C0017AA314619986E +:1065B00000F094FADBE70120A071D8E72DE9F04361 +:1065C00087B09046894604460025FCF73CFF06004C +:1065D00006D03078272806D0082007B0BDE8F08321 +:1065E00043F20200F9E7B07F00F00300022809D06F +:1065F0005FF0000080F0010120460CF0FBF9040080 +:1066000003D101E00120F5E7FFDFA7795FEA090088 +:1066100005D0012821D0B9F1020F26D110E0B8F140 +:10662000000F22D1012F05D0022F05D0032F05D056 +:10663000FFDF2DE00C252BE0012529E0022527E0D6 +:106640004046FAF740FEB0B9032F0ED11022414662 +:1066500004F11D001EF092FF1AE0012F02D0022F5C +:1066600003D104E0B8F1000F12D00720B5E740468F +:10667000FAF729FE08B11020AFE7102104F11D0040 +:106680001EF0FBFF0621404607F0FAFDC4F81D008E +:106690002078252140F0020020703046FCF73AFDBA +:1066A0002078C10713D020F00100207002208DF85F +:1066B000000004F11D0002908DF804506946C330BB +:1066C0000DF06DFE022803D010B1FFDF00E025774A +:1066D000002082E730B587B00D460446FCF7B3FED4 +:1066E000A0B1807F00F00300022812D05FF000000C +:1066F00080F0010120460CF07DF904000ED0284600 +:10670000FAF7E1FD38B1102007B030BD43F20200C6 +:10671000FAE70120ECE72078400701D40820F3E7EE +:10672000294604F13D00202205461EF027FF20786F +:1067300040F01000207001070FD520F008002070F5 +:1067400007208DF80000694604F1E00001950DF086 +:1067500026FE022801D000B1FFDF0020D4E770B58B +:106760000D460646FCF76FFE18B10178272921D1A6 +:1067700002E043F2020070BD807F00F003000228B7 +:1067800008D0002080F0010130460CF033F90400FD +:1067900003D101E00120F5E7FFDFA079022809D14C +:1067A0006078C00706D02A4621463046FEF702FD33 +:1067B00010B10FE0082070BDB4F860000E280BD2B5 +:1067C00004F1620102231022081F0BF09AFF01213D +:1067D00001704570002070BD112070BD70B5064677 +:1067E00014460D460846FAF76EFD18B92046FAF72A +:1067F00090FD08B1102070BDA6F57F40FF380ED087 +:106800003046FCF720FE38B1417822464B08811C07 +:106810001846FCF7E8FD07E043F2020070BD204691 +:10682000FDF7F4FD0028F9D11021E01D0FF025FB44 +:10683000E21D294604F1170000F087F9002070BD21 +:106840002DE9F04104468AB01546884600270846DF +:10685000FAF786FD18B92846FAF782FD10B1102024 +:106860000AB006E42046FCF7EEFD060003D03078BF +:1068700027281AD102E043F20200F1E7B07F00F0CE +:106880000300022808D0002080F0010120460CF00F +:10689000B1F8040003D101E00120F5E7FFDF207823 +:1068A000400702D56078800701D40820D8E7B07F80 +:1068B00000F00300022803D0A06F03D1A16F02E013 +:1068C000606FFAE7616F407800B19DB1487810B110 +:1068D000B8F1000F0ED0ADB1EA1D06A8E16800F0D6 +:1068E00034F9102206A905F117001EF01BFE18B19D +:1068F000042707E00720B3E71022E91D04F12D006B +:106900001EF03CFEB8F1000F06D0102208F107017E +:1069100004F11D001EF032FE2078252140F0020017 +:1069200020703046FCF7F6FB2078C10715D020F028 +:106930000100207002208DF8000004F11D0002907B +:10694000103003908DF804706946B3300DF027FDC8 +:10695000022803D010B1FFDF00E0277700207FE797 +:10696000F8B515460E460746FCF76DFD040004D049 +:106970002078222804D00820F8BD43F20200F8BD98 +:10698000A07F00F00300022802D043F20500F8BD0A +:106990003046FAF798FC18B92846FAF794FC08B183 +:1069A0001020F8BD00953288B31C21463846FEF70A +:1069B00024FC112815D00028F3D1297C4A08A17F96 +:1069C00062F3C711A177297CE27F61F30002E277CD +:1069D000297C890884F82010A17F21F04001A1774B +:1069E000F8BDA17F0907FBD4D6F80200C4F8360031 +:1069F000D6F80600C4F83A003088A086102229464E +:106A000004F124001EF0BAFD287C4108E07F61F308 +:106A10004100E077297C61F38200E077287C8008E0 +:106A200084F82100A07F40F00800A0770020D3E781 +:106A300070B50D4606460BB1072070BDFCF703FD8F +:106A4000040007D02078222802D3A07F800604D437 +:106A5000082070BD43F2020070BDADB1294630463A +:106A60000AF030FF02F05EFB297C4A08A17F62F346 +:106A7000C711A177297CE27F61F30002E277297CCC +:106A8000890884F8201004E030460AF03EFF02F046 +:106A900049FBA17F21F02001A17770BD70B50D46A3 +:106AA000FCF7D1FC040005D02846FAF732FC20B1EF +:106AB000102070BD43F2020070BD29462046FEF74B +:106AC0004AFB002070BD04E010F8012B0AB1002041 +:106AD0007047491E89B2F7D20120704770B515463C +:106AE000064602F009FD040000D1FFDF207820F007 +:106AF0000F00801C20F0F000203020706680286895 +:106B0000A060BDE8704002F0FABC10B5134C94F8D8 +:106B10003000002808D104F12001A1F110000DF08F +:106B200080FC012084F8300010BD10B190F8B9202D +:106B30002AB10A4890F8350018B1002003E0B830B7 +:106B400001E0064834300860704708B50023009320 +:106B500013460A460CF049F908BD00002C0200203B +:106B600018B18178012938D101E0102070470188DF +:106B700042F60112881A914231D018DC42F6010225 +:106B8000A1EB020091422AD00CDC41B3B1F5C05F09 +:106B900025D06FF4C050081821D0A0F57060FF38E0 +:106BA0001BD11CE001281AD002280AD117E0B0F549 +:106BB000807F14D008DC012811D002280FD00328D0 +:106BC0000DD0FF2809D10AE0B0F5817F07D0A0F5EC +:106BD0008070033803D0012801D0002070470F20B7 +:106BE00070470A281FD008DC0A2818D2DFE800F016 +:106BF000191B1F1F171F231D1F21102815D008DC6C +:106C00000B2812D00C2810D00D2816D00F2806D132 +:106C10000DE011280BD084280BD087280FD003203B +:106C200070470020704705207047072070470F20ED +:106C3000704704207047062070470C20704743F2CD +:106C40000200704738B50C46050041D06946FFF791 +:106C5000AFF9002819D19DF80010607861F30200A7 +:106C600060706946681CFFF7A3F900280DD19DF8F4 +:106C70000010607861F3C5006070A978C1F341012C +:106C8000012903D0022905D0072038BD217821F041 +:106C9000200102E0217841F020012170410704D059 +:106CA000A978C90861F386106070607810F0380F19 +:106CB00007D0A978090961F3C710607010F0380F88 +:106CC00002D16078400603D5207840F04000207063 +:106CD000002038BD70B50446002008801546606865 +:106CE000FFF7B0FF002816D12089A189884211D86A +:106CF00060688078C0070AD0B1F5007F0AD840F2FA +:106D00000120B1FBF0F200FB1210288007E0B1F582 +:106D1000FF7F01D90C2070BD01F2012129800020E4 +:106D200070BD10B50478137864F300031370047811 +:106D3000640864F3410313700478A40864F38203C5 +:106D400013700478E40864F3C3031370047824090F +:106D500064F3041313700478640964F34513137027 +:106D60000078800960F38613137031B10878C10789 +:106D700001D1800701D5012000E0002060F3C71396 +:106D8000137010BD4278530702D002F0070306E0EB +:106D900012F0380F02D0C2F3C20300E001234A7898 +:106DA00063F302024A70407810F0380F02D0C0F34B +:106DB000C20005E0430702D000F0070000E0012018 +:106DC00060F3C5024A7070472DE9F04F95B00D0091 +:106DD000824612D0122128461EF04FFC4FF6FF7B50 +:106DE00005AA0121584607F066F8002426463746D2 +:106DF0004FF420586FF4205973E0102015B0BDE80F +:106E0000F08F00BF9DF81E0001280AD1BDF81C10AC +:106E100041450BD011EB09000AD001280CD0022803 +:106E20000CD0042C0ED0052C0FD10DE0012400E075 +:106E30000224BDF81A6008E0032406E00424BDF82B +:106E40001A7002E0052400E00624BDF81A1051452E +:106E500047D12C74BEB34FF0000810AA4FF0070AB8 +:106E6000CDE90282CDE900A80DF13C091023CDF84F +:106E7000109042463146584607F0D0F808BBBDF89E +:106E80003C002A46C0B210A90DF041FBC8B9AE8142 +:106E9000CFB1CDE900A80DF1080C0AAE40468CE850 +:106EA0004102132300223946584607F0B7F840B98B +:106EB000BDF83C00F11CC01EC0B22A1D0DF027FB1E +:106EC00010B103209AE70AE0BDF82900E881062CFA +:106ED00005D19DF81E00A872BDF81C002881002075 +:106EE0008CE705A806F0F3FF00288BD0FFF779FEAA +:106EF00084E72DE9F0471C46DDE90978DDF82090AC +:106F000015460E00824600D1FFDF0CB1208818B173 +:106F1000D5B11120BDE8F087022D01D0012100E09C +:106F2000002106F1140005F0B5FEA8F800000246A5 +:106F30003B462946504603F04EF9C9F8000008B90F +:106F4000A41C3C600020E5E71320E3E7F0B41446FE +:106F5000DDE904528DB1002314B1022C09D101E006 +:106F6000012306E00D7CEE0703D025F00105012387 +:106F70000D742146F0BC03F0B9BF1A80F0BC704715 +:106F80002DE9FE4F91461A881C468A468046FAB182 +:106F900002AB494603F01FF9050019D04046A61C74 +:106FA00027880BF06BFE3246072629463B460096A3 +:106FB0000BF079FA20882346CDE900504A46514625 +:106FC0004046FFF7C3FF002020800120BDE8FE8F70 +:106FD0000020FBE72DE9F04786B082460EA89046D8 +:106FE00090E8B000894604AA05A903A88DE8070027 +:106FF0001E462A4621465046FFF77BFF039901B102 +:1070000001213970002818D1F94904F1140204ABA8 +:107010000860039805998DE80700424649465046A6 +:1070200006F0EFF9A8B1092811D2DFE800F0050851 +:107030000510100A0C0C0E00002006B06AE71120A3 +:10704000FBE70720F9E70820F7E70D20F5E7032025 +:10705000F3E7BDF810100398CDE9000133462A4646 +:1070600021465046FFF772FFE6E72DE9F04389B06D +:107070000D46DDE9108781461C461646142103A8FB +:107080001EF01DFB012002218DF810108DF80C0060 +:107090008DF81170ADF8146064B1A278D20709D0F0 +:1070A0008DF81600E088ADF81A00A088ADF8180039 +:1070B000A068079008A80095CDE90110424603A9F1 +:1070C00048466B68FFF786FF09B0BDE8F083F0B56E +:1070D0008BB000240646069407940727089405A859 +:1070E0000994019400970294CDE903400D461023C2 +:1070F0002246304606F092FF78B90AA806A9019404 +:1071000000970294CDE90310BDF8143000222946FF +:10711000304606F059FD002801D0FFF762FD0BB0A4 +:10712000F0BD06F0F9BB2DE9FC410C468046002677 +:1071300002F0E2F9054620780D287DD2DFE800F064 +:10714000BC0713B325BD49496383AF959B00A8488D +:10715000006820B1417841F010014170ADE0404637 +:1071600002F0FAF9A9E0042140460BF043FC0700C5 +:1071700000D1FFDF07F11401404605F01FFDA5BB5C +:1071800013214046FDF71CFC97E0042140460BF01C +:1071900031FC070000D1FFDFE088ADF800000020DF +:1071A000B8819DF80000010704D5C00602D5A0886B +:1071B000B88105E09DF8010040067ED5A088F881E1 +:1071C00005B9FFDF22462946404601F0BDFC0226F4 +:1071D00073E0E188ADF800109DF8011009060FD5A5 +:1071E000072803D006280AD00AE024E004214046FC +:1071F0000BF000FC060000D1FFDFA088F081022622 +:10720000CDB9FFDF17E0042140460BF0F3FB070088 +:1072100000D1FFDF07F1140006F0B5FB90F0010F7D +:1072200002D1E079000648D5387C022640F0020001 +:10723000387405B9FFDF00E03EE0224629464046AB +:1072400001F082FC39E0042140460BF0D3FB017CC5 +:10725000002D01F00206C1F340016171017C21F0B3 +:1072600002010174E7D1FFDFE5E702260121404674 +:1072700002F0A4F921E0042140460BF0BBFB0546D7 +:10728000606800902089ADF80400012269464046FC +:1072900002F0B5F9287C20F0020028740DE0002DE2 +:1072A000C9D1FFDFC7E7022600214046FBF70CF9F2 +:1072B000002DC0D1FFDFBEE7FFDF3046BDE8FC8117 +:1072C0003EB50C0009D001466B4601AA002006F02D +:1072D00027FF20B1FFF785FC3EBD10203EBD0020FA +:1072E0002080A0709DF8050002A900F00700FEF7BD +:1072F0007BFE50B99DF8080020709DF8050002A99A +:10730000C0F3C200FEF770FE08B103203EBD9DF839 +:10731000080060709DF80500C109A07861F30410B1 +:10732000A0709DF80510890961F3C300A0709DF855 +:107330000410890601D5022100E0012161F3420019 +:107340009DF8001061F30000A07000203EBD70B5F4 +:10735000144606460D4651EA040005D075B10846AC +:10736000F9F7F5FF78B901E0072070BD29463046EE +:1073700006F037FF10B1BDE8704032E454B120464A +:10738000F9F7E5FF08B1102070BD21463046BDE891 +:10739000704095E7002070BD2DE9FC5F0C469046DB +:1073A0000546002701780822007A3E46B2EB111FFD +:1073B0007ED104F10A0100910A31821E4FF0020AC7 +:1073C00004F1080B0191092A73D2DFE802F0ECDF27 +:1073D00005F427277AA9CD00688804210BF00AFB61 +:1073E000060000D1FFDFB08920B152270726C2E096 +:1073F0009002002051271026002C7DD06888A080A4 +:107400000120A071A88900220099FFF7A0FF0028A1 +:1074100073D1A8892081288AE081D1E0B5F8129043 +:10742000072824D1E87B000621D5512709F1140053 +:1074300086B2002CE1D0A88900220099FFF787FFCF +:1074400000285AD16888A08084F806A0A8892081E5 +:107450000120A073288A2082A4F81290A88A0090A4 +:1074600068884B46A969019A01F04BFBA8E05027B8 +:1074700009F1120086B2002C3ED0A889002259469C +:10748000FFF765FF002838D16888A080A889E080D0 +:10749000287A072813D002202073288AE081E87B0D +:1074A000C0096073A4F81090A88A0090688801E071 +:1074B00083E080E04B4604F11202A969D4E7012081 +:1074C000EAE7B5F81290512709F1140086B2002CB2 +:1074D00066D0688804210BF08DFA83466888A08006 +:1074E000A88900220099FFF732FF00286ED184F8A6 +:1074F00006A0A889208101E052E067E00420A07383 +:10750000288A2082A4F81290A88A009068884B46A6 +:10751000A969019A01F0F5FAA989ABF80E104FE0BC +:107520006888FBF790FF0746688804210BF062FA31 +:10753000064607B9FFDF06B9FFDF687BC00702D048 +:107540005127142601E0502712264CB36888A080EA +:10755000502F06D084F806A0287B594601F0E1FAA6 +:107560002EE0287BA11DF9E7FE49A88949898142BF +:1075700005D1542706269CB16888A08020E05327B7 +:107580000BE06888A080A889E08019E06888042161 +:107590000BF030FA00B9FFDF55270826002CF0D198 +:1075A000A8F8006011E056270726002CF8D068885C +:1075B000A080002013E0FFDF02E0012808D0FFDFF9 +:1075C000A8F800600CB1278066800020BDE8FC9F11 +:1075D00057270726002CE3D06888A080687AA0711E +:1075E000EEE7401D20F0030009B14143091D01EB06 +:1075F0004000704713B5DB4A00201071009848B175 +:10760000002468460BF013F8002C02D1D64A0099EA +:1076100011601CBD01240020F4E770B50D4606463C +:1076200086B014465C2128461EF049F804B9FFDFF5 +:10763000A0786874A2782188284601F09CFA00207E +:10764000A881E881228805F11401304605F09BFAF3 +:107650006A460121304606F02EFC19E09DF8030031 +:10766000000715D5BDF806103046FFF730FD9DF830 +:107670000300BDF8061040F010008DF80300BDF8BF +:107680000300ADF81400FF233046059A06F074FDA0 +:10769000684606F01CFC0028E0D006B070BD10B5AE +:1076A0000C4601F1140005F0A5FA0146627C204663 +:1076B000BDE8104001F094BA30B50446A94891B035 +:1076C0004FF6FF75C18905AA284606F0F4FB30E0A5 +:1076D0009DF81E00A0422AD001282AD1BDF81C0026 +:1076E000B0F5205F03D042F60101884221D100208D +:1076F00002AB0AAA0CA9019083E8070007200090BA +:10770000BDF81A1010230022284606F087FC38B96D +:10771000BDF828000BAAC0B20CA90CF0F8FE10B1FD +:10772000032011B030BD9DF82E00A04201D10020F1 +:10773000F7E705A806F0CBFB0028C9D00520F0E745 +:1077400070B5054604210BF055F9040000D1FFDFA8 +:1077500004F114010C46284605F030FA214628466B +:10776000BDE8704005F031BA70B58AB00C460646E7 +:10777000FBF769FE050014D02878222827D30CB126 +:10778000A08890B101208DF80C0003208DF8100026 +:1077900000208DF8110054B1A088ADF818002068C1 +:1077A00007E043F202000AB070BD0920FBE7ADF824 +:1077B00018000590042130460BF01CF9040000D19C +:1077C000FFDF04F1140005F02CFA000701D40820B3 +:1077D000E9E701F091FE60B108A802210094CDE92B +:1077E000011095F8232003A930466368FFF7F2FBE8 +:1077F000D9E71120D7E72DE9F04FB2F802A0834670 +:1078000089B0154689465046FBF71DFE0746042100 +:1078100050460BF0EFF80026044605964FF002089C +:107820000696ADF81C6007B9FFDF04B9FFDF4146DB +:10783000504603F0C6FE50B907AA06A905A88DE870 +:1078400007004246214650466368FFF752FB454811 +:1078500007AB0660DDE9051204F11400CDF80090D5 +:10786000CDE90320CDE9013197F823205946504650 +:107870006B6805F01FFA06000AD0022E04D0032E12 +:1078800014D0042E00D0FFDF09B03046BDE8F08FE1 +:10789000BDF81C000028F7D00599CDE9001042463C +:1078A000214650466368FFF751FBEDE7687840F0EA +:1078B00008006870E8E72DE9F04F99B004464FF0F2 +:1078C00000082848ADF81C80ADF82080ADF8248071 +:1078D000A0F80880ADF81480ADF81880ADF82C80C1 +:1078E000ADF82880007916460D464746012808D095 +:1078F000022806D0032804D0042802D0082019B09A +:10790000C4E72046F9F7DFFC80BB2846F9F7DBFC2B +:1079100060BB6068F9F724FD40BB606848B16089CE +:107920002189884202D8B1F5007F01D90C20E6E711 +:1079300080460EAA06A92846FFF7CCF90028DED11A +:1079400068688078C0F34100022808D19DF81900CA +:1079500010F0380F03D02869F9F7F9FC30B905A900 +:10796000206904E0900200201400002020E0FFF7CE +:1079700069F90028C3D1206948B1607880079DF873 +:10798000150000F0380001D5F0B300E0E0BB9DF831 +:10799000140080060ED59DF8150010F0380F03D0A6 +:1079A0006068F9F7D4FC18B96068F9F7D9FC08B138 +:1079B0001020A4E70AA96069FFF744F900289ED1C6 +:1079C000606940B19DF8290000F0070101293CD110 +:1079D00010F0380F39D00BA9A069FFF733F9002850 +:1079E0008DD19DF8280080062FD49DF82C008006AC +:1079F0002BD4A06950B19DF82D0000F0070101299A +:107A000023D110F0380F00E01FE01ED0E06818B15D +:107A10000078D0B11C2818D20FAA611C2046FFF7AD +:107A200080F90121384661F30F2082468DF852100B +:107A3000B94642F603000F46ADF850000DF13F0283 +:107A400018A928680CF082FD08B1072057E79DF8B7 +:107A5000600015A9CDF80090C01CCDE9019100F09F +:107A6000FF0B00230BF20122514614A806F066F921 +:107A7000F0BBBDF854000C90FD492A89286900929A +:107A8000CDE901016B89BDF838202868069906F018 +:107A900055F901007ED120784FF0020AC10601D4C9 +:107AA00080062BD5ADF80C90606950B90AA906A8DC +:107AB000FFF768F99DF8290020F00700401C8DF8B9 +:107AC00029009DF8280008A940F0C8008DF828007A +:107AD0008DF8527042F60210ADF8500003AACDF8AE +:107AE00000A0CDE90121002340F2032214A800E008 +:107AF0001EE00A9906F022F901004BD1DC484D4600 +:107B000008385B460089ADF83D000FA8CDE902902A +:107B1000CDF80490CDF810904FF007090022CDF871 +:107B20000090BDF854104FF6FF7006F04DF810B1FC +:107B3000FFF757F8E3E69DF83C00000625D52946F7 +:107B4000012060F30F218DF852704FF42450ADF8EE +:107B50005000ADF80C5062789DF80C00002362F3E1 +:107B600000008DF80C006278CDF800A0520862F396 +:107B700041008DF80C0003AACDE9012540F2032253 +:107B800014A806F0DBF8010004D1606888B320690E +:107B9000A8B900E086E005A906A8FFF7F3F8607829 +:107BA000800706D49DF8150020F038008DF81500E8 +:107BB00005E09DF8140040F040008DF814008DF8A9 +:107BC000527042F60110ADF85000208940F20121B8 +:107BD000B0FBF1F201FB1202606809ABCDF8008046 +:107BE000CDE90103002314A8059906F0A7F80100C8 +:107BF00058D12078C00729D0ADF80C50A06950B9F1 +:107C00000BA906A8FFF7BEF89DF82D0020F007008D +:107C1000401C8DF82D009DF82C008DF8527040F01E +:107C200040008DF82C0042F60310ADF8500007A973 +:107C300003AACDF800A0CDE90121002340F20322E0 +:107C400014A80B9906F07AF801002BD1E06868B30C +:107C50002946012060F30F218DF8527042F604107E +:107C6000ADF85000E068002302788DF85820407885 +:107C70008DF85900E06816AA4088ADF85A00E0680F +:107C800000798DF85C00E068C088ADF85D00CDF843 +:107C90000090CDE901254FF4027214A806F04EF8C9 +:107CA000010003D00C9800F0C7FF28E670480321BC +:107CB0000838017156B100893080BDF82400708009 +:107CC000BDF82000B080BDF81C00F080002016E652 +:107CD00070B501258AB016460B46012802D002284D +:107CE00016D104E08DF80E504FF4205003E08DF8CB +:107CF0000E5042F60100ADF80C005BB10024601C90 +:107D000060F30F2404AA08A918460CF01FFC18B150 +:107D1000072048E5102046E504A99DF82020544896 +:107D2000CDE90021801E02900023214603A802F223 +:107D3000012206F003F810B1FEF753FF33E54C487B +:107D400008380EB1C1883180057100202BE5F0B5EF +:107D500093B0074601268DF83E6041F60100ADF86C +:107D60003C0012AA0FA93046FFF7B2FF002848D105 +:107D70003F4C0025083CE7B31C2102A81DF09FFCE6 +:107D80009DF808008DF83E6040F020008DF8080056 +:107D900042F60520ADF83C000E959DF83A0011958D +:107DA00020F00600801C8DF83A009DF838006A46E5 +:107DB00020F0FF008DF838009DF8390009A920F067 +:107DC000FF008DF839000420ADF82C00ADF830002C +:107DD0000EA80A9011A80D900FA80990ADF82E508A +:107DE00002A8FFF768FD00280BD1BDF800006081F4 +:107DF00000E008E0BDF80400A081401CE08125718E +:107E0000002013B0F0BD6581A581BDF84800F4E7FE +:107E10002DE9F74F1649A0B00024083917940A79C4 +:107E2000A146012A04D0022A02D0082023B02DE561 +:107E3000CA88824201D00620F8E721988A46824209 +:107E400001D10720F2E701202146ADF848004FF6A6 +:107E5000FF788DF86E0042F6020B60F30F21ADF84B +:107E60004A80ADF86CB006918DF8724002E00000D7 +:107E7000980200201CA9ADF870401391ADF8508015 +:107E800012A806F048F800252E462F460DAB072213 +:107E900012A9404606F042F878B10A285DD195B3A0 +:107EA0008EB3ADF86450ADF866609DF85E008DF855 +:107EB000144019AC012864D06BE09DF83A001FB360 +:107EC000012859D1BDF8381059451FD118A809A962 +:107ED00001940294CDE9031007200090BDF83610FC +:107EE00010230022404606F099F8B0BBBDF86000B0 +:107EF000042801D006284AD1BDF8241021988142D7 +:107F00003AD10F2092E73AE0012835D1BDF8380088 +:107F1000B0F5205F03D042F6010188422CD1BAF8B7 +:107F20000600BDF83610884201D1012700E0002785 +:107F300005B19EB1219881421ED118A809AA0194C9 +:107F40000294CDE90320072000900D461023002263 +:107F5000404606F063F800B902E02DE04E460BE023 +:107F6000BDF86000022801D0102810D1C0B217AAB5 +:107F700009A90CF0CCFA50B9BDF8369086E7052077 +:107F800054E705A917A8221D0CF0E0FA08B1032058 +:107F90004CE79DF814000023001DC2B28DF8142098 +:107FA00022980092CDE901401BA8069905F0C6FE73 +:107FB00010B902228AF80420FEF713FE36E710B546 +:107FC0000B46401E88B084B205AA00211846FEF771 +:107FD000A8FE00200DF1080C06AA05A901908CE866 +:107FE0000700072000900123002221464FF6FF7072 +:107FF00005F0EAFD0446BDF81800012800D0FFDFB7 +:108000002046FEF7EEFD08B010BDF0B5F74F044670 +:1080100087B038790E46032804D0042802D00820FF +:1080200007B0F0BD04AA03A92046FEF753FE0500E1 +:10803000F6D160688078C0F3410002280AD19DF82B +:108040000D0010F0380F05D02069F9F780F908B15C +:108050001020E5E7208905AA21698DE807006389DA +:10806000BDF810202068039905F068FE10B1FEF7F6 +:10807000B8FDD5E716B1BDF8140030800420387182 +:108080002846CDE7F8B50C0006460CD001464FF661 +:10809000FF7500236A46284606F042F828B100BF63 +:1080A000FEF79FFDF8BD1020F8BD69462046FEF79B +:1080B000C9FD0028F8D1A078314600F00103284618 +:1080C000009A06F059F8EBE730B587B01446002265 +:1080D0000DF1080C05AD01928CE82C0007220092EE +:1080E0000A46014623884FF6FF7005F06DFDBDF886 +:1080F00014102180FEF775FD07B030BD70B50D4638 +:1081000004210AF077FC040000D1FFDF294604F1C6 +:108110001400BDE8704004F07DBD70B50D4604212B +:108120000AF068FC040000D1FFDF294604F11400C6 +:10813000BDE8704004F091BD70B50D4604210AF011 +:1081400059FC040000D1FFDF294604F11400BDE80A +:10815000704004F0A9BD70B5054604210AF04AFC40 +:10816000040000D1FFDF214628462368BDE87040A7 +:108170000122FEF705BF70B5064604210AF03AFC5D +:10818000040000D1FFDF04F1140004F033FD401DB2 +:1081900020F0030511E0011D00880022431821464C +:1081A0003046FEF7EDFE00280BD0607CABB2684392 +:1081B00082B2A068011D0AF0DAFAA068418800299D +:1081C000E9D170BD70B5054604210AF013FC040026 +:1081D00000D1FFDF214628466368BDE870400222D7 +:1081E000FEF7CEBE70B50E46054601F085F90400D7 +:1081F00000D1FFDF0120207266726580207820F0B8 +:108200000F00001D20F0F00040302070BDE87040ED +:1082100001F075B910B50446012900D0FFDF2046F2 +:10822000BDE810400121FAF74FB92DE9F04F97B0A2 +:108230004FF0000A0C008346ADF814A0D04619D0C8 +:10824000E06830B1A068A8B10188ADF81410A0F8BA +:1082500000A05846FBF7F7F8070043F2020961D087 +:10826000387822285CD3042158460AF0C3FB050065 +:1082700005D103E0102017B0BDE8F08FFFDF05F156 +:10828000140004F0B7FC401D20F00306A07801287C +:1082900003D0022801D00720EDE7218807AA58461D +:1082A00005F009FE30BB07A805F011FE10BB07A8BA +:1082B00005F00DFE48B99DF82600012805D1BDF84E +:1082C0002400A0F52451023902D04FF45050D2E7D7 +:1082D000E068B0B1CDE902A00720009005AACDF872 +:1082E00004A00492A2882188BDF81430584605F0F5 +:1082F0006BFC10B1FEF775FCBDE7A168BDF814007A +:1083000008809DF81F00C00602D543F20140B2E785 +:108310000B9838B1A1780078012905D080071AD4CC +:108320000820A8E74846A6E7C007F9D002208DF844 +:108330003C00A8684FF00009A0B1697C42887143F5 +:1083400091420FD98AB2B3B2011D0AF0C6F9804634 +:10835000A0F800A006E003208DF83C00D5F80080CE +:108360004FF001099DF8200010F0380F00D1FFDF19 +:108370009DF820001E49C0F3C200084497F823105E +:1083800010F8010C884201D90F2074E72088ADF85D +:10839000400014A90095CDE90191434607220FA999 +:1083A0005846FEF717FE002891D19DF8500050B9AD +:1083B000A078012807D1687CB3B2704382B2A86864 +:1083C000011D0AF09EF9002055E770B506461546D6 +:1083D0000C460846FEF7C4FB002805D12A46214674 +:1083E0003046BDE8704073E470BD11E59002002096 +:1083F000765A020070B51E4614460D0009D044B1ED +:10840000616831B138B1FC49C988814203D0072085 +:1084100070BD102070BD2068FEF7A2FB0028F9D1C6 +:10842000324621462846BDE87040FFF744BA70B591 +:1084300015460C0006D038B1EF490989814203D0B6 +:10844000072070BD102070BD2068FEF789FB002852 +:10845000F9D129462046BDE87040D6E570B50646FC +:1084600086B00D4614461046F8F753FFD0BB60683F +:10847000F8F776FFB0BBA6F57F40FF3803D0304653 +:10848000FAF7E1FF80B128466946FEF79DFC002817 +:108490000CD19DF810100F2008293DD2DFE801F023 +:1084A00008060606060A0A0843F2020006B070BD76 +:1084B0000320FBE79DF80210012908D1BDF8001048 +:1084C000B1F5C05FF2D06FF4C052D142EED09DF84A +:1084D000061001290DD1BDF80410A1F52851062977 +:1084E00007D200E029E0DFE801F0030304030303FF +:1084F000DCE79DF80A1001290FD1BDF80810B1F58D +:10850000245FD3D0A1F60211B1F50051CED00129DC +:10851000CCD0022901D1C9E7FFDF606878B9002318 +:1085200005AA2946304605F0FBFD10B1FEF759FBC0 +:10853000BCE79DF81400800601D41020B6E76188DE +:10854000224628466368FFF7BFFDAFE72DE9F043F9 +:10855000814687B0884614461046F8F7DAFE18B10F +:10856000102007B0BDE8F083002306AA4146484624 +:1085700005F0D6FD18B100BFFEF733FBF1E79DF81B +:108580001800C00602D543F20140EAE7002507279C +:1085900005A8019500970295CDE9035062884FF632 +:1085A000FF734146484605F039FD060013D1606867 +:1085B000F8F7AFFE60B960680195CDE90250009709 +:1085C0000495238862884146484605F027FD064603 +:1085D000BDF8140020803046CEE739B1864B0A88BA +:1085E0009B899A4202D843F2030070471DE610B5FA +:1085F00086B0814C0423ADF81430638943B1A4895B +:108600008C4201D2914205D943F2030006B010BD5D +:108610000620FBE7ADF81010002100910191ADF8A4 +:10862000003002218DF8021005A9029104A90391DE +:10863000ADF812206946FFF7F8FDE7E72DE9FC47A2 +:1086400081460D460846F8F73EFE88BB4846FAF7D5 +:10865000FAFE5FEA00080AD098F80000222829D321 +:10866000042148460AF0C6F9070005D103E043F2A9 +:108670000200BDE8FC87FFDF07F1140004F0D1FA27 +:1086800006462878012803D0022804D00720F0E706 +:10869000B0070FD502E016F01C0F0BD0A8792C1DE7 +:1086A000C00709D0E08838B1A068F8F70CFE18B10F +:1086B0001020DEE70820DCE721882A780720B1F5C2 +:1086C000847F35D01EDC40F20315A1F20313A942CA +:1086D00026D00EDCB1F5807FCBD003DCF9B10129C7 +:1086E00026D1C6E7A1F58073013BC2D0012B1FD173 +:1086F00013E0012BBDD0022B1AD0032BB9D0042BD1 +:1087000016D112E0A1F20912082A11D2DFE802F014 +:108710000B04041010101004ABE7022AA9D007E0E4 +:10872000012AA6D004E0320700E0F206002AA0DA0F +:10873000CDB200F0E1FE50B198F82300CDE900057C +:10874000FA89234639464846FEF78FFC91E7112007 +:108750008FE72DE9F04F8BB01F4615460C46834638 +:108760000026FAF770FE28B10078222805D20820EA +:108770000BB081E543F20200FAE7B80801D0072008 +:10878000F6E7032F00D100274FF6FF79CCB1022D79 +:1087900073D32046F8F7E4FD30B904EB0508A8F1DF +:1087A0000100F8F7DDFD08B11020E1E7AD1EAAB227 +:1087B0002146484605F08FFD38F8021C88425CD1FE +:1087C000ADB20D49B80702D58889401C00E00120F0 +:1087D0001FFA80F8F80701D08F8900E04F4605AAFC +:1087E0004146584605F067FB4FF0070A4FF0000975 +:1087F000DCB320460BE000009002002040881028E7 +:108800003BD8361D304486B2AE4236D2A01902881B +:108810004245F3D351E000BF9DF8170002074CD545 +:1088200094B304EB0608361DB8F80230B6B2102B2C +:1088300023D89A19AA4220D8B8F8002091421CD116 +:10884000C0061CD5CDE900A90DF1080C0AAAA11992 +:1088500048468CE80700B8F800100022584605F09A +:10886000B3F920B1FEF7BDF982E726E005E0B8F8DC +:108870000200BDF82810884201D00B2078E7B8F834 +:108880000200304486B207E0FFE7C00604D5584630 +:10889000FEF71DFC002888D19DF81700BDF81A10BE +:1088A00020F010008DF81700BDF81700ADF800009B +:1088B000FF235846009A05F05FFC05A805F007FB6A +:1088C00018B9BDF81A10B942A6D9042158460AF0C1 +:1088D00091F8040000D1FFDFA2895AB1CDE900A9C7 +:1088E0004D46002321465846FEF7BFFB0028BBD16A +:1088F000A5813DE700203BE72DE9FF4F8BB01E46E9 +:1089000017000D464FF0000412D0B00802D0072027 +:108910000FB0B1E4032E00D100265DB10846F8F790 +:1089200016FD28B93888691E0844F8F710FD08B10B +:108930001020EDE7C64AB00701D5D18900E001213A +:10894000F0074FF6FF7802D0D089401E00E0404685 +:1089500086B206AA0B9805F0AEFA4FF000094FF068 +:10896000070B0DF1140A38E09DF81B00000734D501 +:10897000CDF80490CDF800B0CDF80890CDE9039A79 +:10898000434600220B9805F049FB60BB05B3BDF8D8 +:1089900014103A8821442819091D8A4230D3BDF8A1 +:1089A0001E2020F8022BBDF8142020F8022BCDE960 +:1089B00000B9CDE90290CDF810A0BDF81E10BDF8A9 +:1089C000143000220B9805F029FB08B103209FE723 +:1089D000BDF814002044001D84B206A805F077FA03 +:1089E00020B10A2806D0FEF7FCF891E7BDF81E106A +:1089F000B142B9D934B17DB13888A11C884203D2C3 +:108A00000C2085E7052083E722462946404605F0ED +:108A100062FC014628190180A41C3C80002077E7F5 +:108A200010B50446F8F775FC08B1102010BD884851 +:108A3000C0892080002010BDF0B58BB00D460646E1 +:108A4000142103A81CF03BFE01208DF80C008DF8CA +:108A5000100000208DF81100ADF814503046FAF7E0 +:108A6000F2FC48B10078222812D30421304609F0E4 +:108A7000C1FF040005D103E043F202000BB0F0BDDA +:108A8000FFDF04F11400074604F0CBF8800601D4A0 +:108A90000820F3E7207C022140F00100207409A89F +:108AA0000094CDE90110072203A930466368FEF760 +:108AB00091FA20B1217C21F001012174DEE72946E1 +:108AC0003046F9F7F2FC08A9384604F099F800B1ED +:108AD000FFDFBDF82040172C01D2172000E0204610 +:108AE000A84201D92C4602E0172C00D217242146B7 +:108AF0003046FFF712FB21463046F9F7FCF900201B +:108B0000BCE7F8B51C4615460E46069F0AF0A4F8C9 +:108B10002346FF1DBCB231462A46009409F08FFC63 +:108B2000F8BD70B50C4605460E2120461CF0A5FD8B +:108B3000002020802DB1012D01D0FFDF70BD062067 +:108B400000E00520A07170BD10B54880087813467C +:108B500020F00F00001D20F0F00080300C4608705F +:108B60001422194604F108001CF04DFD00F0C7FC6A +:108B70003748046010BD2DE9F047DFF8D890491D53 +:108B8000064621F0030117460C46D9F8000009F00B +:108B90006CFD050000D1FFDF4FF000083560A5F83F +:108BA00000802146D9F8000009F05FFD050000D1E2 +:108BB000FFDF7560A5F800807FB104FB07F1091D98 +:108BC0000BD0D9F8000009F050FD040000D1FFDF00 +:108BD000B460C4F80080BDE8F087C6F80880FAE702 +:108BE0002DE9F0411746491D21F00302194D0646B3 +:108BF00001681446286809F063FD224671682868F8 +:108C000009F05EFD3FB104FB07F2121D03D0B1680D +:108C1000286809F055FD042009F094FE044604205C +:108C200009F098FE201A012804D12868BDE8F04117 +:108C300009F010BDBDE8F08110B50C4605F007F94C +:108C400000B1FFDF2046BDE81040FDF7CABF0000BD +:108C5000900200201400002038B50C468288817BE9 +:108C600019B14189914200D90A462280C188121D5A +:108C700090B26A4608F04FFFBDF80000032800D309 +:108C80000320C1B2208801F011F838BD38B50C4678 +:108C90008288817B19B10189914200D90A462280DC +:108CA000C188121D90B26A4608F035FFBDF8000079 +:108CB000022800D30220C1B2208800F0F7FF401C38 +:108CC000C0B238BD2DE9FE4F82468B46F9481446A6 +:108CD0000BF10302D0E90010CDE9011022F00302EC +:108CE00068464FF49071009209F0A1FCF24E002CFE +:108CF00002D1F24A00999160009901440091357FB8 +:108D000005F1010504D1E8B20BF09AFB00B1FFDFD9 +:108D1000009800EB0510C01C20F0030100915CB925 +:108D2000707AB27A1044C2B200200870308C80B2DF +:108D300004F015FF00B1FFDF0098316A084400908D +:108D40002146684600F075FF80460098C01C20F060 +:108D500003000090B37AF27A717A04B1002009F02E +:108D60005CFD0099084400902146684600F0A9FF88 +:108D7000D14800273D4690F801900CE0284600F0CD +:108D80003BFF064681788088F9F74CF971786D1CB5 +:108D900000FB0177EDB24D45F0D10098C01C20F0EA +:108DA0000300009004B100203946F9F746F9009914 +:108DB000002708440090C0483D4690F801900CE020 +:108DC000284600F019FF0646C1788088FEF709FCA6 +:108DD00071786D1C00FB0177EDB24D45F0D1009824 +:108DE000C01C20F00300009004B100203946FEF7BB +:108DF00001FC00994FF0000908440090AE484D4630 +:108E000047780EE0284600F0F7FE0646807B30B13A +:108E100006F1080001F019FF727800FB02996D1C41 +:108E2000EDB2BD42EED10098C01C20F003000090CE +:108E300004B10020494601F00CFF0099084400905D +:108E40002146684600F0AFFE0098C01D20F00700E4 +:108E50000090DAF80010814204D3A0EB0B01B1F5C9 +:108E6000803F04DB4FF00408CAF8000004E0CAF8B1 +:108E70000000B8F1000F02D04046BDE8FE8F34BBC1 +:108E80008F490020009A03F083F8FBF75CFA8A48C8 +:108E900001AA00211030F8F7E1FA00B1FFDF86489F +:108EA000407FFEF754FF00B1FFDF83484FF4F671B7 +:108EB00040301CF004FC80480421403080F8E91167 +:108EC00080F8EA11062180F8EB11032101710020DE +:108ED000D3E770B5784C06464034207804EB401553 +:108EE000E078083590B9A01990F8E80100280ED074 +:108EF000A0780F2800D3FFDF202128461CF0DFFBDD +:108F0000687866F3020068700120E070284670BD42 +:108F10002DE9F04105460C460027007805219046D2 +:108F20003E46B1EB101F00D0FFDF287A50B1012878 +:108F30000ED0FFDFA8F800600CB12780668000200B +:108F4000BDE8F0810127092674B16888A08008E097 +:108F50000227142644B16888A0802869E060A88AA6 +:108F60002082287B2072E5E7A8F80060E7E730B5AB +:108F7000514C012000212070617020726072032228 +:108F8000A272E07221732174052121831F21618364 +:108F900060744CA161610A2121776077474D4FF4DD +:108FA000B06020626868C11C21F00301814200D0DA +:108FB000FFDF6868606030BD30B5404C156863689D +:108FC00010339D4202D20420136030BD3A4B5D78CD +:108FD0005A6802EB0512107051700320D0801720E0 +:108FE00090800120D0709070002090735878401CC1 +:108FF0005870606810306060002030BD70B5064663 +:109000002D480024457807E0204600F0F5FD017862 +:10901000B14204D0641CE4B2AC42F5D1002070BD72 +:10902000F7B5074608780C4610B3FFF7E7FF05468B +:10903000A7F12006202F06D0052E19D2DFE806F072 +:109040000F2B2B151A0000F0E2FD0DB1697800E03E +:109050000021401AA17880B20844FF2808D8A078DF +:1090600030B1A088022831D202E0608817282DD2C2 +:109070000720FEBD207AE0B161881729F8D3A188C6 +:109080001729F5D3A1790029F2D0E1790029EFD091 +:10909000402804D9ECE7242F18D1207A48B1618800 +:1090A0004FF6FB70814202D8A18881420ED904207C +:1090B000FEBD0BE07C5A0200AC030020180000202B +:1090C000000000206E5246357800000065B9207817 +:1090D00002AA0121FFF770FF0028E9D12078FFF7ED +:1090E0008DFF050000D1FFDF052E18D2DFE806F066 +:1090F000030B0E081100A0786870A088E8800FE0CC +:109100006088A8800CE0A078A87009E0A078E870DA +:1091100006E054F8020FA8606068E86000E0FFDF36 +:109120000020C5E71A2835D00DDC132832D2DFE83D +:1091300000F01B31203131272723252D31312931F2 +:109140003131312F0F00302802D003DC1E2821D10D +:10915000072070473A3809281CD2DFE800F0151BB9 +:109160000F1B1B1B1B1B07000020704743F2040052 +:10917000704743F202007047042070470D2070478B +:109180000F20704708207047112070471320704748 +:10919000062070470320704710B5007800F00100EA +:1091A00008F0ABFCBDE81040BCE710B5007818B182 +:1091B000012801D0072010BD08F0EFFCBDE81040E9 +:1091C000B0E710B5007800F0010008F09FFCBDE8A2 +:1091D0001040A7E70EB5017801F001018DF80010ED +:1091E000417801F001018DF801100178C1F34001CF +:1091F0008DF802104178C1F340018DF80310017819 +:1092000089088DF80410417889088DF80510817857 +:109210008DF80610C1788DF8071000798DF80800D8 +:10922000684607F095FAFFF77DFF0EBD2DE9F84F70 +:10923000DFF8F883FE4C00264FF490771FE0012002 +:1092400000F082FD0120FFF744FE05463946D8F8BC +:10925000080009F00AFA686000B9FFDF686807F0E3 +:1092600006F9B0B12846FAF7D5FB284600F072FDA2 +:1092700028B93A466968D8F8080009F021FA94F943 +:10928000E9010428DBDA022009F05CFB074600252F +:10929000A5E03A466968D8F8080009F011FAF2E743 +:1092A000B8F802104046491C89B2A8F80210B94229 +:1092B00001D3002141800221B8F8020009F09AFB95 +:1092C000002864D0B8F80200694608F088FBFFF770 +:1092D00029FF00B1FFDF9DF8000078B1B8F8020067 +:1092E00009F0CCFC5FEA000900D1FFDF484608F036 +:1092F0003AFF18B1B8F8020002F052F9B8F80200CB +:1093000009F0AAFC5FEA000900D1FFDF484608F037 +:1093100022FFE0BB0321B8F8020009F06BFB5FEA13 +:10932000000B47D1FFDF45E0DBF8100010B10078FB +:10933000FF2849D0022000F007FD0220FFF7C9FDF9 +:109340008246484609F013F8CAF8040000B9FFDF66 +:10935000DAF8040009F0DBF8002100900170B8F899 +:1093600002105046AAF8021001F01CFE484609F00F +:10937000D0F800B9FFDF504600F0ECFC18B99AF8BD +:109380000100000704D50098CBF8100012E024E09B +:10939000DBF8100038B10178491C11F0FF010170B1 +:1093A00008D1FFDF06E000221146484600F0F9FB35 +:1093B00000B9FFDF94F9EA01022805DBB8F80200E2 +:1093C00001F0B5FD0028AFD194F9E901042804DBD0 +:1093D000484609F002F900B101266D1CEDB2BD420C +:1093E00004D294F9EA010228BFF65AAF002E7FF4A6 +:1093F00022AFBDE8F84F032000F0A6BC10B58B4C9F +:10940000E06008682061AFF2DB10F9F766FD60707C +:1094100010BD87480021403801708448017085499B +:109420004160704770B505464FF080500C46D0F84B +:10943000A410491C05D1D0F8A810C9430904090C8F +:109440000BD050F8A01F01F0010129704168216084 +:109450008068A080287830B970BD062120460CF0C5 +:109460000CFD01202870607940F0C000607170BD73 +:1094700070B54FF080540D46D4F88010491C0BD1C4 +:10948000D4F88410491C07D1D4F88810491C03D1A2 +:10949000D4F88C10491C0CD0D4F880100160D4F89A +:1094A00084104160D4F888108160D4F88C10C160B9 +:1094B00002E010210CF0E1FCD4F89000401C0BD12C +:1094C000D4F89400401C07D1D4F89800401C03D174 +:1094D000D4F89C00401C09D054F8900F28606068B4 +:1094E0006860A068A860E068E86070BD2846BDE8D4 +:1094F000704010210CF0C1BC4D480079E9E470B512 +:109500004B4CE07830B3207804EB4010407A00F008 +:109510000700204490F9E801002800DCFFDF2078F4 +:10952000002504EB4010407A00F00700011991F883 +:10953000E801401E81F8E8012078401CC0B220708C +:109540000F2800D12570A078401CA0700CF08CFB77 +:10955000E57070BDFFDF70BD3EB50546032109F023 +:1095600049FA0446284609F077FB054604B9FFDFAF +:10957000206918B10078FF2800D1FFDF01AA6946F1 +:10958000284600F00EFB60B9FFDF0AE0002202A9C6 +:10959000284600F006FB00B9FFDF9DF8080000B187 +:1095A000FFDF9DF80000411E8DF80010EED220690B +:1095B0000199884201D1002020613EBD70B5054669 +:1095C000A0F57F400C46FF3800D1FFDF012C01D011 +:1095D000FFDF70BDFFF790FF040000D1FFDF2078B0 +:1095E00020F00F00401D20F0F0005030207065800A +:1095F0000020207201202073BDE870407FE72DE934 +:10960000F04116460D460746FFF776FF040000D1ED +:10961000FFDF207820F00F00401D20F0F0005030D8 +:109620002070678001202072286805E01800002063 +:10963000EC030020F81300202061A888A082267384 +:10964000BDE8F0415BE77FB5FFF7D8FC040000D12F +:10965000FFDF02A92046FFF7FFFA054603A92046CF +:10966000FFF714FB8DF800508DF80100BDF80800DD +:10967000001DADF80200BDF80C00001DADF804009F +:10968000E088ADF80600684608F01FFA002800D010 +:10969000FFDF7FBD2DE9F05FF94E8146307810B1D4 +:1096A0000820BDE8F09F4846F7F733FE08B11020C8 +:1096B000F7E7F44C207808B9FFF759FCA17A607AF3 +:1096C0004D460844C4B200F0A2FAA04207D2201AC4 +:1096D000C1B22A460020FFF76FFC0028E1D1716873 +:1096E000E848C91C002721F003017160B3463E46DB +:1096F0003D46BA463C4690F801800AE0204600F01C +:109700007BFA4178807B0E4410FB0155641CE4B267 +:109710007F1C4445F2D1C6EBC601DA4E0AEB870046 +:1097200000EB8100F17A00EB850000EB8100DBF8B3 +:1097300004105C464518012229464846FFF7C2FA44 +:10974000070012D00020FFF759FC05000BD005F1EF +:109750001300616820F00300884200D0FFDF7078BA +:10976000401E7070656038469BE7002229464846D7 +:10977000FFF7A8FA00B1FFDFD9F8000060604FF6EC +:10978000FF7060800120207000208AE72DE9F04101 +:109790000446BB4817460E46007810B10820BDE8C5 +:1097A000F0810846F7F78FFD08B11020F7E7B54DB7 +:1097B000287808B9FFF7DBFB601E1E2807D8012CAC +:1097C00022D13078FE281FD828770020E7E7A4F1BF +:1097D00020001F2805D8E0B23A463146BDE8F041E6 +:1097E0001EE4A4F140001F2805D831462046BDE8FC +:1097F000F04100F0D7BAA4F1A0001F2804D800203F +:10980000A02C03D0A12C06D00720C8E7317801F0A6 +:1098100001016977C3E731680922F82901D38B0771 +:1098200001D01046BBE76B7C03F00303012B04D18E +:109830006B8BD7339CB28C42F3D82962AFE72DE90A +:10984000F04781460E460846F7F763FD48B948469B +:10985000F7F77DFD28B909F1030020F00301494520 +:1098600002D01020BDE8F08786484FF0000A403053 +:10987000817869B14178804600EB4114083437881B +:1098800032460021204600F073FA050004D027E09C +:10989000A6F800A00520E5E7B9F1000F24D0308834 +:1098A000B84201D90C251FE0607800F00705284672 +:1098B00000F04AFA08EB0507324697F8E8014946F6 +:1098C000401C87F8E801204607F5F47700F050FACD +:1098D00005463878401E3870032000F035FA2DB167 +:1098E0000C2D01D0A6F800A02846BBE76078644E96 +:1098F00000F00701012923D002290CD0032934D01C +:10990000FFDF98F801104046491CC9B288F80110E1 +:109910000F2935D036E0616821B1000702D4608894 +:10992000FFF71AFE98F8EA014746012802D170783D +:10993000F9F7F2FA97F9EA010428E2DBFFDFE0E742 +:10994000616821B14FF49072B06808F0B9FE98F8E0 +:10995000E9014746032802D17078F9F7DDFA97F953 +:10996000E9010428CDDBFFDFCBE7C00602D5608824 +:10997000FFF7F2FD98F9EB010628C2DBFFDFC0E735 +:1099800080F801A08178491E8170617801F007019B +:1099900001EB080090F8E811491C80F8E811A3E7F2 +:1099A00070B50D460446F7F78EFC18B92846F7F750 +:1099B000B0FC08B1102070BD29462046BDE87040BB +:1099C0000AF075BD70B505460AF094FDC4B228468C +:1099D000F7F7BDFC08B1102070BD35B128782C70A8 +:1099E00018B1A04201D0072070BD2046FDF764FEEB +:1099F000052805D10AF082FD012801D0002070BDA4 +:109A00000F2070BD70B5044615460E460846F7F7A0 +:109A10005AFC18B92846F7F77CFC08B1102070BD35 +:109A2000022C03D0102C01D0092070BD2A463146EB +:109A300020460AF06CFD0028F7D0052070BD70B5F7 +:109A400014460D460646F7F73EFC38B92846F7F7A8 +:109A500060FC18B92046F7F77AFC08B1102070BDF9 +:109A60002246294630460AF071FD0028F7D007202B +:109A700070BD3EB50446F7F74CFC28B110203EBD42 +:109A800018000020AC030020684606F0C8FDFFF770 +:109A900049FB0028F3D19DF806002070BDF80800AE +:109AA0006080BDF80A00A0800020E8E770B5054698 +:109AB0000C460846F7F74BFC20B93CB12068F7F795 +:109AC00028FC08B1102070BDA08828B12146284686 +:109AD000BDE87040FDF748BE092070BD70B5054671 +:109AE0000C460846F7F7EFFB30B9681E1E2814D85D +:109AF0002046F7F7E8FB08B1102070BD042D01D90E +:109B0000072070BD05B9FFDFF84800EB850050F86D +:109B1000041C2046BDE870400847A5F120001F281E +:109B200005D821462846BDE87040FAF794BBF02DD1 +:109B300008D0F12DE4D1207808F05CF8BDE8704041 +:109B4000FFF7F0BAA068F7F7BEFB0028D4D1204693 +:109B500008F028F8F2E770B504460D460846F7F716 +:109B6000D8FB30B9601E1E2811D82846F7F7ABFB8A +:109B700008B1102070BD012C05D0022C03D0032C9D +:109B800001D0042C01D1062070BD072070BDA4F1C6 +:109B900020001F28F9D829462046BDE87040FAF772 +:109BA000B2BB08F0A7BA38B50446D148007B00F034 +:109BB0000105D9B904F034FB0DB1226800E00022A0 +:109BC000CC484178C06806F018FCCA481030C0780C +:109BD0008DF8000010B1012802D004E0012000E05F +:109BE00000208DF80000684606F093FD002D02D09D +:109BF00020682830206038BD30B5BD4D04466878F7 +:109C0000A04200D8FFDF686800EB041030BD70B5DB +:109C1000B74800252C46467807E02046FFF7ECFFC2 +:109C20004078641C2844C5B2E4B2B442F5D1284659 +:109C300070BD2DE9F0410C4607464FF0000800F0DA +:109C4000DEF80646FF2801D94FF013083868C01C1B +:109C500020F003023A6054EA080421D1A448F3B288 +:109C6000072124300CF00EFB09E0072C10D2DFE8AE +:109C700004F0060408080A0406009F4804E09F4810 +:109C800002E09F4800E09F480CF01CFB054600E006 +:109C9000FFDFA54200D0FFDF641CE4B2072CE4D351 +:109CA000386800EB06103860404678E5021D5143E5 +:109CB000452900D245210844C01CB0FBF2F0C0B2D7 +:109CC00070472DE9FC5F064689484FF000088B4637 +:109CD0004746444690F8019022E02046FFF78CFF6B +:109CE000050000D1FFDF687869463844C7B22846CE +:109CF000FEF7B2FF824601A92846FEF7C7FF0346DA +:109D0000BDF804005246001D81B2BDF80000001DE0 +:109D100080B208F0EDFE6A78641C00FB0288E4B2B1 +:109D20004C45DAD13068C01C20F003003060BBF134 +:109D3000000F00D000204246394608F0E7FE3168A7 +:109D400008443060BDE8FC9F69494031087100203B +:109D5000C870704766494031CA782AB10A7801EB69 +:109D600042110831814201D0012070470020704724 +:109D70002DE9F04106460078154600F00F0400205A +:109D80001080601E0F46052800D3FFDF57482A4683 +:109D9000183800EB8400394650F8043C3046BDE8E2 +:109DA000F041184770B50C46402802D0412806D132 +:109DB00020E0A07861780D18E178814201D9072070 +:109DC00070BD2078012801D9132070BDFF2D08D85F +:109DD0000AF026FD06460BF0FFFE301A801EA84250 +:109DE00001DA122070BD4248216881602179017337 +:109DF000002070BDBDE87040084600F02BB82DE98A +:109E0000F047DFF8EC900026344699F8090099F8FD +:109E10000A2099F801700244D5B299F80B20104439 +:109E200000F0FF0808E02046FFF7E6FE817B40785F +:109E300011FB0066641CE4B2BC42F4D199F809102D +:109E400099F80A0029442944414400B101200844FA +:109E5000304407E538B50446407800F00300012897 +:109E600003D002280BD0072038BD606858B1F7F73F +:109E700077FAD0B96068F7F76AFA20B915E0606838 +:109E8000F7F721FA88B969462046FCF791F80028CF +:109E9000EAD1607800F00300022808D19DF80000A4 +:109EA00028B16068F7F753FA08B1102038BD61890E +:109EB000F8290DD8208988420AD8607800F003027A +:109EC0000B48012A06D1D731026A89B28A4201D2EF +:109ED000092038BD94E80E0000F1100585E80E0059 +:109EE0000AB900210183002038BD00009C5A0200FD +:109EF000AC03002018000020574100001FAD0000F7 +:109F0000E92F0000334201002DE9F04107461446D5 +:109F10008846084601F022FD064608EB88001C2210 +:109F2000796802EBC0000D18688C58B1414638467C +:109F300001F01CFD014678680078C200082305F195 +:109F400020000CE0E88CA8B14146384601F015FD30 +:109F50000146786808234078C20005F1240008F023 +:109F600006FC38B1062121726681D0E90010C4E9EF +:109F7000031009E0287809280BD00520207266819B +:109F80006868E060002028702046BDE8F04101F0DC +:109F9000DBBC072020726681F4E72DE9F04116460C +:109FA0000D460746406801EB85011C2202EBC1010A +:109FB0004418204601F003FD40B10021708865F38C +:109FC0000F2160F31F4106200CF036FA09202070A3 +:109FD000324629463846BDE8F04195E72DE9F04183 +:109FE0000E46074600241C21F07816E004EB84039B +:109FF000726801EBC303D25C6AB1FFF77DFA05001A +:10A0000000D1FFDF6F802A4621463046FFF7C5FFAB +:10A010000120BDE8F081641CE4B2A042E6D8002033 +:10A02000F7E770B5064600241C21C0780AE000BF9F +:10A0300004EB8403726801EBC303D5182A782AB1B4 +:10A04000641CE4B2A042F3D8402070BD2821284609 +:10A050001BF013FB706880892881204670BD70B5A5 +:10A06000034600201C25DC780DE000BF00EB8006D5 +:10A070005A6805EBC6063244167816B1128A8A422F +:10A0800004D0401CC0B28442F0D8402070BDF0B56E +:10A09000044600201C26E5780EE000BF00EB800798 +:10A0A000636806EBC7073B441F788F4202D15B7899 +:10A0B000934204D0401CC0B28542EFD84020F0BD8E +:10A0C0000078032801D000207047012070470078F5 +:10A0D000022801D00020704701207047007807282F +:10A0E00001D000207047012070472DE9F04106465D +:10A0F00088461078F1781546884200D3FFDF2C7827 +:10A100001C27641CF078E4B2A04201D8201AC4B223 +:10A1100004EB8401706807EBC1010844017821B1A8 +:10A120004146884708B12C7073E72878A042E8D1EF +:10A13000402028706DE770B514460B880122A240BC +:10A14000134207D113430B8001230A22011D08F09B +:10A15000D8FA047070BD2DE9FF4F81B00878DDE9B1 +:10A160000E7B9A4691460E4640072CD4019808F083 +:10A1700085FD040000D1FFDF07F1040820461FFA27 +:10A1800088F107F0C4FE050000D1FFDF2046294614 +:10A190006A4608F00EF90098A0F80370A0F805A030 +:10A1A000284608F0B4F9017869F306016BF3C7118A +:10A1B000017020461FFA88F107F0ECFE00B9FFDFBE +:10A1C000019806F08CF806EB0900017F491C017725 +:10A1D00005B0BDE8F08F2DE9F84F0E469A4691463E +:10A1E0000746032108F006FC0446008DDFF8B88519 +:10A1F000002518B198F80000B0421ED1384608F08A +:10A200003DFD070000D1FFDF09F10401384689B2A6 +:10A2100007F07DFE050010D0384629466A4608F052 +:10A22000C8F8009800210A460180817006F010F9F4 +:10A230000098C01DCAF8000021E098F80000B04264 +:10A2400016D104F1260734F8341F012000FA06F96C +:10A2500011EA090F00D0FFDF2088012340EA09003E +:10A2600020800A22391D384608F066FA067006E09A +:10A27000324604F1340104F12600FFF75CFF0A21A5 +:10A2800088F800102846BDE8F88FFEB515460C4644 +:10A29000064602AB0C220621FFF79DFF002827D0BF +:10A2A0000299607812220A70801C487008224A8045 +:10A2B000A07002982988052381806988C180A988B7 +:10A2C0000181E988418100250C20CDE900050622A5 +:10A2D00021463046FFF73FFF2946002266F31F4123 +:10A2E000F02310460BF0FEFF6078801C60700120A8 +:10A2F000FEBDFEB514460D460622064602AB1146CB +:10A30000FFF769FF002812D0029B1320002118706C +:10A31000A8785870022058809C800620CDE9000162 +:10A320000246052329463046FFF715FF0120FEBDF2 +:10A330002DE9FE430C46804644E002AB0E22072185 +:10A340004046FFF748FF002841D060681C2267782C +:10A350008678BF1C06EB860102EBC1014518029806 +:10A360001421017047700A214180698A0181E98ABC +:10A370004181A9888180A9898181304601F0EEFA66 +:10A38000029905230722C8806F70042028700025D9 +:10A390000E20CDE9000521464046FFF7DCFE2946A8 +:10A3A00066F30F2168F31F41F023002206200BF013 +:10A3B00099FF6078FD49801C607062682046921C9D +:10A3C000FFF793FE606880784028B6D10120BDE891 +:10A3D000FE83FEB50D46064638E002AB0E2207218D +:10A3E0003046FFF7F8FE002835D068681C23C17896 +:10A3F00001EB810203EBC20284180298152202705D +:10A40000627842700A224280A2894281A2888281B7 +:10A41000084601F0A3FA014602988180618AC18052 +:10A42000E18A0181A088B8B10020207000210E20AF +:10A43000CDE900010523072229463046FFF78BFEB0 +:10A440006A68DB492846D21CFFF74FFE6868C0786F +:10A450004028C2D10120FEBD0620E6E72DE9FE43DB +:10A460000C46814644E0204601F093FAD0B302AB9B +:10A47000082207214846FFF7AEFE0028A7D06068F3 +:10A480001C2265780679AD1C06EB860102EBC10142 +:10A4900047180298B7F81080062101704570042112 +:10A4A0004180304601F05AFA0146029805230722FE +:10A4B000C180A0F804807D70082038700025CDE9A7 +:10A4C000000521464846FFF746FE294666F30F2160 +:10A4D00069F31F41F023002206200BF003FF607890 +:10A4E000801C60706268B3492046121DFFF7FDFDB5 +:10A4F000606801794029B6D1012068E72DE9F34F62 +:10A5000083B00E4680E0304601F043FA002875D053 +:10A5100071681C2091F8068008EB880200EBC200ED +:10A520000C184146304601F028FA0146A078C300D5 +:10A5300070684078C20004F1240008F034F907463E +:10A540008088E18B401A80B2002581B3AA46218B16 +:10A55000814200D808468146024602AB0721039893 +:10A56000FFF739FE010028D0BAF1000F03D0029A9C +:10A57000B888022510808B46E28B3968A9EB05006C +:10A580001FFA80FA0A440398009208F077FBED1D49 +:10A59000009A59465346009507F085FFE08B5044DA +:10A5A00080B2E083B988884209D1012508E0FFE73D +:10A5B000801C4FF0010A80B2C9E7002008E60025A0 +:10A5C000CDE90095238A072231460398FFF7C3FDA2 +:10A5D000E089401EE0818DB1A078401CA0707068B9 +:10A5E000F178427811FB02F1CAB2816901230E3081 +:10A5F00008F087F880F800800020E08372686E49D8 +:10A600003046921DFFF771FD7068817940297FF413 +:10A610007AAF0120DCE570B5064648680D46144661 +:10A620008179402910D104EB84011C2202EBC10185 +:10A63000084401F0E5F9002806D0686829468471CD +:10A640003046BDE8704059E770BDFEB50C46074680 +:10A65000002645E0204601F09CF9D8B360681C2232 +:10A66000417901EB810102EBC1014518688900B90C +:10A67000FFDF02AB082207213846FFF7ACFD0028B8 +:10A6800033D00299607816220A70801C487004202A +:10A6900048806068407901F061F90146029805231D +:10A6A000072281806989C1800820CDE90006214602 +:10A6B0003846FFF750FD6078801C6070A889698972 +:10A6C0000844B0F5803F00D3FFDFA88969890844BA +:10A6D000A8816E81626839492046521DFFF705FD49 +:10A6E000606841794029B5D10120FEBD30B5438C69 +:10A6F000458BC3F3C704002345B1838B641EED1A59 +:10A70000C38A6D1E1D4495FBF3F3E4B22CB100899E +:10A7100018B1A04200D8204603444FF6FF70834290 +:10A7200000D3034613800C7030BD2DE9FC41074671 +:10A7300016460D46486802EB86011C2202EBC10159 +:10A7400044186A4601A92046FFF7D0FFA089618915 +:10A7500001448AB2BDF80010914212D0081A00D507 +:10A76000002060816868407940280AD1204601F0C5 +:10A770003DF9002805D06868294646713846FFF73C +:10A7800064FFBDE8FC812DE9FE4F894680461546F1 +:10A790005088032108F02EF98346B8F802004028BB +:10A7A0000ED240200DE000002C000020C1A00000CF +:10A7B000CFA00000DDA0000001BA0000EDB900004C +:10A7C000403880B282460146584601F0E2F800283F +:10A7D0007ED00AEB8A001C22DBF8041002EBC000DA +:10A7E0000C18204601F0EBF8002877D1B8F80000EB +:10A7F000E18A88423CD8A189D1B348456ED1002670 +:10A800005146584601F0B2F8218C0F18608B48B9B8 +:10A81000B9F1020F62D3B8F804006083618A8842FC +:10A8200026D80226A9EB06001FFA80F9B888A28B69 +:10A83000801A002814DD4946814500DA084683B2B3 +:10A8400068886968029139680A44CDE9003208F0E5 +:10A8500003FADDE90121F61D009B009607F0EFFDEC +:10A86000A18B01EB090080B2A083618B884207D9DC +:10A87000688803B052465946BDE8F04F01F0DDB894 +:10A880001FD14FF009002872B8F802006881D7E99B +:10A890000001C5E90401608BA881284601F054F845 +:10A8A0005146584601F062F80146DBF804000823DF +:10A8B0000078C20004F1200007F059FF0020A083B7 +:10A8C0006083A0890AF0FF02401EA081688800E032 +:10A8D00004E003B05946BDE8F04F26E7BDE8FE8F1F +:10A8E0002DE9F041064615460F461C461846F6F778 +:10A8F000EAFC18B92068F6F70CFD08B1102013E443 +:10A900007168688C0978B0EBC10F01D313200BE498 +:10A910003946304601F02AF801467068082300786D +:10A92000C20005F1200007F0ECFED4E90012C0E9F6 +:10A9300000120020E3E710B50446032108F05AF89E +:10A940000146007800F00300022805D02046BDE84B +:10A95000104001F1140298E48A8A2046BDE81040B4 +:10A96000C7E470B50446032108F044F805460146E3 +:10A970002046FFF773FD002816D029462046FFF732 +:10A9800064FE002810D029462046FFF722FD00284B +:10A990000AD029462046FFF7CBFC002804D02946E0 +:10A9A0002046BDE87040A9E570BD2DE9F0410C4698 +:10A9B00080461EE0E178427811FB02F1CAB281695B +:10A9C00001230E3007F0D3FE077860681C22C1799E +:10A9D000491EC17107EB8701606802EBC10146188F +:10A9E0003946204600F0D5FF18B1304600F0E0FFB0 +:10A9F00020B16068C1790029DCD180E7FEF77CFDD9 +:10AA0000050000D1FFDF0A202872384600F0A6FFBB +:10AA100068813946204600F0B0FF0146606808238F +:10AA20004078C20006F1240007F0A1FED0E9001032 +:10AA3000C5E90310A5F80280284600F085FFB0782C +:10AA400000B9FFDFB078401EB07058E770B50C4613 +:10AA50000546032107F0CEFF01464068C279224433 +:10AA6000C2712846BDE870409FE72DE9FE4F82463F +:10AA7000507814460F464FF0000800284FD00128A8 +:10AA800007D0022822D0FFDF2068B8606068F86035 +:10AA900024E702AB0E2208215046FFF79CFB00285A +:10AAA000F2D00298152105230170217841700A2106 +:10AAB0004180C0F80480C0F80880A0F80C8062884B +:10AAC00082810E20CDE90008082221E0A6783046D8 +:10AAD00000F044FF054606EB86012C22786802EB65 +:10AAE000C1010822465A02AB11465046FFF773FBDC +:10AAF0000028C9D0029807210170217841700421F3 +:10AB0000418008218580C680CDE9001805230A46CA +:10AB100039465046FFF71FFB87F80880DEE6A67827 +:10AB2000022516B1022E13D0FFDF2A1D914602AB7B +:10AB300008215046FFF74FFB0028A5D002980121BD +:10AB4000022E0170217841704580868002D005E098 +:10AB50000625EAE7A188C180E1880181CDE9009856 +:10AB60000523082239465046D4E710B50446032190 +:10AB700007F040FF014600F108022046BDE8104002 +:10AB800073E72DE9F05F0C4601281DD0957992F806 +:10AB90000480567905EB85011F2202EBC10121F0EB +:10ABA000030B08EB060111FB05F14FF6FF7202EAF9 +:10ABB000C10909F1030115FB0611F94F21F0031A30 +:10ABC00040B101283DD124E06168E57891F800802A +:10ABD0004E78DFE75946786807F047FD606000B9B6 +:10ABE000FFDF594660681AF06AFDE57051467868E3 +:10ABF00007F03BFD6168486100B9FFDF60684269AA +:10AC000002EB09018161606880F80080606846702D +:10AC100017E0606852464169786807F051FD5A466E +:10AC20006168786807F04CFD032007F08BFE04464E +:10AC3000032007F08FFE201A012802D1786807F060 +:10AC400009FD0BEB0A00BDE8F09F0246002102203F +:10AC500097E773B5D24D0A202870009848B10024B8 +:10AC60004FEA0D0007F0E3FC002C01D10099696068 +:10AC70007CBD01240020F5E770B50C46154638214F +:10AC800020461AF01CFD012666700A2104F11C0002 +:10AC90001AF015FD05B9FFDF297A207861F301006C +:10ACA0002070A879002817D02A4621460020FFF7F7 +:10ACB00068FF6168402088706168C87061680871C9 +:10ACC0006168487161688871616828880881616875 +:10ACD000688848816068868170BDC878002802D085 +:10ACE000002201204DE7704770B50546002165F34D +:10ACF0001F4100200BF0A0FB0321284607F07AFE3D +:10AD0000040000D1FFDF21462846FFF767F900283D +:10AD100004D0207840F010002070012070BD2DE993 +:10AD2000FF4180460E460F0CFEF7E6FB050007D0FC +:10AD30006F800321384607F05DFE040008D106E06D +:10AD400004B03846BDE8F0411321F9F739BEFFDF02 +:10AD50005FEA080005D0B8F1060F18D0FFDFBDE8A4 +:10AD6000FF8120782A4620F0080020700020ADF8EE +:10AD7000020002208DF800004FF6FF70ADF80400CD +:10AD8000ADF8060069463846F9F711FAE7E7C6F369 +:10AD9000072101EB81021C23606803EBC202805C87 +:10ADA000042803D008280AD0FFDFD8E7012000904C +:10ADB0004FF440432A46204600F008FECFE704B097 +:10ADC0002A462046BDE8F041FFF7E7B82DE9F05FDD +:10ADD0000027B0F80A9090460C4605463E46B9F169 +:10ADE000400F01D2402001E0A9F140001FFA80FA93 +:10ADF000287AC01E08286BD2DFE800F00D04192065 +:10AE000058363C4772271026002C6CD0D5E9030138 +:10AE1000C4E902015CE070271226002C63D00A22EC +:10AE200005F10C0104F108001AF0EDFB50E0712768 +:10AE30000C26002C57D0E868A06049E07427102643 +:10AE40009CB3D5E90301C4E902016888032107F036 +:10AE5000D1FD8346FEF750FB02466888508051467C +:10AE60005846FFF751F833E075270A26ECB1A88958 +:10AE700020812DE076271426BCB105F10C0004F1E9 +:10AE8000080307C883E8070022E07727102664B18B +:10AE9000D5E90301C4E902016888032107F0AAFD8E +:10AEA00001466888FFF781FD12E01CE07327082641 +:10AEB000CCB16888032107F09DFD01460078C006EB +:10AEC00006D56888FFF78AF810B96888F8F786FD14 +:10AED000A8F800602CB12780A4F8069066806888E6 +:10AEE000A0800020AFE6A8F80060FAE72DE9FC4159 +:10AEF0000C461E4617468046032107F07BFD05469B +:10AF00000A2C0AD2DFE804F0050505050505090944 +:10AF10000907042303E0062301E0FFDF0023CDE956 +:10AF20000076224629464046FFF715F929E438B550 +:10AF30000546A0F57F40FF3830D0284607F08CFE4C +:10AF4000040000D1FFDF204607F011FA002815D0D9 +:10AF500001466A46204607F02CFA00980321B0F813 +:10AF60000540284607F046FD0546052C03D0402C39 +:10AF700005D2402404E0007A80B1002038BD403C76 +:10AF8000A4B2214600F005FD40B1686804EB8401DD +:10AF90003E2202EBC101405A0028EFD0012038BD0B +:10AFA0002C0000202DE9F04F054689B0408807F0BD +:10AFB00053FE040000D1FFDF06AA2046696800F0B6 +:10AFC000C1FC069C001F34F8031F21806388638046 +:10AFD000228881B28A4205D1042B0AD0052B1DD0CC +:10AFE000062B15D02A462046FFF7CDFB09B0BDE859 +:10AFF000F08F1646241D2A4621463046F7F73FFAC1 +:10B000000828F3D12A4621463046FCF7F4FBEDE749 +:10B010006888211D6B68FAF739FCE7E717466888EE +:10B02000032107F0E7FC4FF000088DF80480064686 +:10B03000ADF80680042FD9D36279002AD6D02079C2 +:10B040004FF6FF794FF01C0A13282CD008DC01289A +:10B0500078D0062847D0072875D0122874D106E08A +:10B06000142872D0152871D016286DD1ACE10C2FA0 +:10B070006AD1307800F00301022965D140F0080060 +:10B0800030706079B07001208DF804002089ADF82F +:10B0900008006089ADF80A00A089ADF80C00E089CD +:10B0A000ADF80E0019E0B07890429FD130780107DA +:10B0B0009CD5062F9AD120F0080030706888414650 +:10B0C00060F31F4100200BF0B7F902208DF8040057 +:10B0D000ADF808902089ADF80A0068882A4601A9D1 +:10B0E000F9F765F882E7082F80D12789B4F80A902C +:10B0F000402F01D2402001E0A7F1400080B28046FD +:10B100000146304600F045FC08B3716808EB880042 +:10B110002C2202EBC000095A4945E3D1FE4807AA98 +:10B12000D0E90210CDE9071060798DF81C0008F015 +:10B13000FF048DF81E4068883146FFF796FC2A46CA +:10B14000214639E0B6E014E03CE039E0E6E0F248C0 +:10B15000D0E90010CDE907106079ADF820708DF8C6 +:10B160001C00ADF82290688807AA3146FFF77DFCE5 +:10B170003CE7082FB6D16089B4F80880402801D296 +:10B18000402000E0403887B23946304600F001FCEC +:10B190000028A7D007EB870271680AEBC2000844B9 +:10B1A000028A42459ED1017808299BD14078617975 +:10B1B000884297D1F9B22A463046FEF7EEFE15E7EF +:10B1C0000E2F07D0CDF81C80CDF8208060798DF847 +:10B1D0001C00C8E76189E7898B46B4F80C903046BB +:10B1E000FEF73DFFABF14001402901D309204AE0C1 +:10B1F000B9F1170F01D3172F01D20B2043E04028DC +:10B200000ED000EB800271680AEBC200084401789E +:10B21000012903D1407861798842A9D00A2032E01F +:10B220003046FEF7FEFE014640282BD001EB81039D +:10B2300072680AEBC30002EB0008012288F80020C4 +:10B24000627988F80120706822894089B84200D963 +:10B250003846248A03232B72AA82EF812882A5F81C +:10B260000C906C82084600F079FB6881A8F8149075 +:10B27000A8F81870A8F80E40A8F810B0284600F0FA +:10B2800063FBB3E6042005212972A5F80A80E88152 +:10B2900001212973A049D1E90421CDE90721617970 +:10B2A0008DF81C10ADF81E00688807AA3146FFF71C +:10B2B000DCFBE3E7062FE4D3B078904215D1307879 +:10B2C000010712D520F0080030706888414660F30D +:10B2D0001F4100200BF0B0F802208DF804002089F7 +:10B2E000ADF80800ADF80A90F7E604213046FEF705 +:10B2F000CEFE04464028C4D00220830300902A4694 +:10B300002146304600F062FB4146688864F30F2115 +:10B3100060F31F4106200BF08FF867E60E2FB0D1C7 +:10B3200004213046FEF7B3FE81464028A9D04146AD +:10B33000688869F30F2160F31F4106200BF07CF849 +:10B34000208A0790E08900907068A7894089B842F8 +:10B3500000D938468346B4F80A80208905904846CB +:10B3600000F0FCFA6881079840B10220079B00902A +:10B370002A464946304600F029FB37E6B8F1170F58 +:10B380001ED3172F1CD30420287200986882EF81E7 +:10B39000A5F810B0A5F80C8009EB89020AEBC200F1 +:10B3A0007168009A0C180598A4F81480A4F818B0D5 +:10B3B000E2812082284600F0C7FA0620207015E6B8 +:10B3C00001200B230090D3E7082FA6D12189304616 +:10B3D000FEF745FE074640289FD007EB87027168BD +:10B3E0000AEBC2000844804600F0E9FA002894D134 +:10B3F0006489B8F80E002044B0F5803F05D3688812 +:10B400003A46314600F019FBF0E5002C85D0A8F84B +:10B410000E0068883A463146FFF7FDF8082028728A +:10B42000384600F09BFA6881AC8127E770B50D467D +:10B430000646032107F0DEFA040004D02078000756 +:10B4400004D5112070BD43F2020070BD2A4621468A +:10B450003046FEF71AFF18B9286860616868A06175 +:10B46000207840F008002070002070BD70B50D46B7 +:10B470000646032107F0BEFA040004D02078000736 +:10B4800004D4082070BD43F2020070BD2A46214654 +:10B490003046FEF72EFF00B9A582207820F0080084 +:10B4A0002070002070BD2DE9F04F0E4691B080460F +:10B4B000032107F09FFA0446404607F0DFFB0746EA +:10B4C0000020079008900990ADF830000A90029093 +:10B4D0000390049004B9FFDF0DF108091FBBFFDFE3 +:10B4E00021E038460BA9002206F004FE9DF82C004E +:10B4F00000F07F050A2D00D3FFDF6019017F491E90 +:10B5000001779DF82C0000060DD52A460CA907A846 +:10B51000FEF711FE02E00000AC5A020019F8051017 +:10B52000491C09F80510761EF6B2DAD204F134008F +:10B53000FA4D04F1260BDFF8E8A304F12A07069080 +:10B5400010E05846069900F06AFA064628700A2864 +:10B5500000D3FFDF5AF8261040468847E08CC05DD4 +:10B56000B04202D0208D0028EBD10A202870EC4D8B +:10B570004E4628350EE00CA907A800F050FA044604 +:10B58000375D55F8240000B9FFDF55F8242039460F +:10B5900040469047BDF81E000028ECD111B026E5CA +:10B5A00010B5032107F026FA040000D1FFDF0A21BD +:10B5B00004F11C001AF083F8207840F00400207099 +:10B5C00010BD10B50C46032107F014FA2044007F8B +:10B5D000002800D0012010BD2DE9F84F89461546FE +:10B5E0008246032107F006FA070004D02846F5F743 +:10B5F0006AFE40B903E043F20200BDE8F88F484616 +:10B60000F5F787FE08B11020F7E7786828B1698858 +:10B610000089814201D90920EFE7B9F800001C2414 +:10B6200018B1402809D2402008E03846FEF7F9FC5E +:10B630008046402819D11320DFE7403880B2804689 +:10B640000146384600F0A5F948B108EB8800796852 +:10B6500004EBC000085C012803D00820CDE70520DA +:10B66000CBE7FDF749FF06000BD008EB88007968AF +:10B6700004EBC0000C18B9F8000020B1E88910B143 +:10B6800013E01120B9E72888172802D36888172803 +:10B6900001D20720B1E7686838B12B1D2246414628 +:10B6A0003846FFF71DF90028A7D104F10C026946BE +:10B6B0002046FFF71BF8288860826888E082B9F886 +:10B6C000000030B102202070E889A080E889A0B194 +:10B6D0002BE003202070A889A08078688178402919 +:10B6E00005D180F8028039465046FEF721FE4046DB +:10B6F00000F034F9A9F8000021E07868218B408936 +:10B70000884200D908462083A6F802A0042030729F +:10B71000B9F800007081E0897082F181208B30825D +:10B72000A08AB081304600F00FF97868C1784029CE +:10B7300005D180F8038039465046FEF74AFE0020C6 +:10B740005BE770B50D460646032107F053F9040088 +:10B7500003D0402D04D2402503E043F2020070BD27 +:10B76000403DADB2294600F014F958B105EB850112 +:10B770001C22606802EBC101084400F020F918B1F6 +:10B78000082070BD052070BD2A462146304600F0D5 +:10B7900054F9002070BD2DE9F0410D461646804653 +:10B7A000032107F027F90446402D01D2402500E08F +:10B7B000403DADB28CB1294600F0EBF880B105EB0D +:10B7C00085011C22606802EBC1014718384600F071 +:10B7D000F6F838B10820BDE8F08143F20200FAE73C +:10B7E0000520F8E733463A4629462046FFF778F821 +:10B7F0000028F0D1EAB221464046FEF796FF00202D +:10B80000E9E72DE9F0410D4616468046032107F091 +:10B81000F1F80446402D01D2402500E0403DAFB292 +:10B8200024B13046F5F74FFD38B902E043F202008B +:10B83000D1E73068F5F747FD08B11020CBE739466E +:10B84000204600F0A6F860B107EB87011C22606873 +:10B8500002EBC1014518284600F0B1F818B10820E4 +:10B86000B9E70520B7E7B088A98A884201D90C203A +:10B87000B1E76168E88C4978B0EBC10F01D31320C0 +:10B88000A9E73946204600F078F8014660680823A9 +:10B890004078C20005F1240006F033FFD6E900121B +:10B8A000C0E90012FAB221464046FEF7B4FE00207D +:10B8B00091E72DE9F0470D461F469046814603214A +:10B8C00007F098F80446402D01D2402001E0A5F190 +:10B8D000400086B23CB14DB13846F5F738FD50B165 +:10B8E0001020BDE8F08743F20200FAE76068C8B1B3 +:10B8F000A0F80C8024E03146204600F04AF888B1D8 +:10B9000006EB86011C22606802EBC101451828463F +:10B9100000F055F840B10820E3E700002C000020BB +:10B92000C45A02000520DCE7A5F80880F2B22146DF +:10B930004846FEF7FAFE1FB1A88969890844388095 +:10B940000020CEE706F035BD017821F00F01491C3B +:10B9500021F0F00110310170FDF7D1BD10B50446A2 +:10B96000402800D9FFDF4034A0B210BD40684269D2 +:10B970000078484302EBC0007047C2784068037803 +:10B9800012FB03F24378406901FB032100EBC10085 +:10B990007047C2788A4209D9406801EB81011C22B4 +:10B9A00002EBC101405C08B10120704700207047E4 +:10B9B0000078062801D901207047002070470078E0 +:10B9C000062801D00120704700207047F0B401EB39 +:10B9D00081061C27446807EBC6063444049D0526EF +:10B9E0002670E3802571F0BCFEF78EBA10B5418950 +:10B9F00011B1FFF7DDFF08B1002010BD012010BD1F +:10BA000010B5C18C8278B1EBC20F04D9C18911B1D4 +:10BA1000FFF7CEFF08B1002010BD012010BD10B50A +:10BA20000C4601230A22011D06F0A1FE00782188A0 +:10BA3000012282409143218010BDF0B402EB8205C7 +:10BA40001C264C6806EBC505072363554B681C791B +:10BA5000402C03D11A71F0BCFEF700BDF0BC70475A +:10BA600010B5EFF3108000F0010472B6F948417888 +:10BA7000491C41704078012801D10AF01DF9002CC1 +:10BA800000D162B610BD70B5F24CA07848B901255E +:10BA9000A570FFF7E5FF0AF020F920B100200AF0B9 +:10BAA000EAF8002070BD4FF08040E570C0F8045304 +:10BAB000F7E770B5EFF3108000F0010572B6E54CC2 +:10BAC000607800B9FFDF6078401E6070607808B968 +:10BAD0000AF0F6F8002D00D162B670BDDD4810B551 +:10BAE000817821B10021C1708170FFF7E2FF002051 +:10BAF00010BD10B504460AF0F0F8D6498978084020 +:10BB000000D001202060002010BD10B5FFF7A8FF75 +:10BB10000AF0E3F802220123CE49540728B1CE48A7 +:10BB2000026023610320087202E00A72C4F8043341 +:10BB30000020887110BD2DE9F05FDFF8189342787E +:10BB4000817889F80420002689F80510074689F8CD +:10BB500006600078DFF804B3354620B1012811D023 +:10BB6000022811D0FFDF0AF0CAF84FF0804498B1E4 +:10BB70000AF0CCF8B0420FD130460AF0CBF80028DA +:10BB8000FAD041E00126EEE7FFF76AFF5846016868 +:10BB9000C907FCD00226E6E70120E060C4F80451A2 +:10BBA000AF490E600107D1F84412AD4AC1F34231EA +:10BBB00024321160AA49343108604FF0020AC4F8F7 +:10BBC00004A3A060A7480168C94341F3001101F133 +:10BBD0000108016841F01001016000E020BFD4F8C5 +:10BBE00004010028FAD030460AF094F80028FAD070 +:10BBF000B8F1000F04D19B48016821F010010160E9 +:10BC0000C4F808A3C4F8045199F805004E4688B159 +:10BC1000387878B90AF061F880460AF0F5F90146FB +:10BC20006FF00042B8F1000F02D0C6E9032101E035 +:10BC3000C6E90312DBF80000C00701D00AF049F89A +:10BC4000387810B13572BDE8F09F4FF01808C4F88D +:10BC50000883C4F82C510127C4F81870D4F82C01BB +:10BC60000028FBD0C4F80C51C4F810517948C01D0D +:10BC70000AF062F83570FFF748FF6761784930795C +:10BC800020310860C4F80483DDE770B5050000D1F9 +:10BC9000FFDF4FF080424FF0FF30C2F80803002171 +:10BCA000C2F80011C2F80411C2F80C11C2F8101148 +:10BCB000684C61700AF01DF810B10120A07060702E +:10BCC00066480068C00701D00AF003F82846BDE8BE +:10BCD000704030E75F48007A002800D001207047AC +:10BCE0002DE9FF5F6048D0F800805F4A5F49083265 +:10BCF00011608406D4F8080100B10120D4F82411A1 +:10BD000001B101218A46D4F81C1101B101218946F3 +:10BD1000D4F8201109B1012700E00027D4F8001160 +:10BD200001B101218B46D4F8041101B10121039125 +:10BD3000D4F80C1101B101210291D4F8101101B114 +:10BD40000121444D019129780026009120B1C4F8C9 +:10BD50000861012009F08FFFBAF1000F04D0C4F888 +:10BD60002461092009F087FFB9F1000F04D0C4F85D +:10BD70001C610A2009F07FFF27B1C4F820610B2065 +:10BD800009F079FF3348C01D09F0DEFF00B1FFDF85 +:10BD9000DFF8C4900127BBF1000F10D0C4F808737E +:10BDA000E87818B1EE70002009F065FF287A0228C3 +:10BDB00005D1032028720221C9F8001027610398D9 +:10BDC00008B1C4F80461029850B1C4F80C61287A33 +:10BDD000032800D0FFDFC9F800602F72FFF769FE6B +:10BDE000019838B1C4F81061287A012801D100F017 +:10BDF0005DF86761009838B12E70287A012801D16A +:10BE0000FFF783FEFFF755FE1248C01D09F0B2FF91 +:10BE10001549091DC1F80080BDE8FF9F0D4810B508 +:10BE2000C01D09F091FF0B4940B1012008704FF08F +:10BE3000E021C1F80002BDE8104011E6087A0128AF +:10BE400001D1FFF762FE0348BDE81040C01D09F0B4 +:10BE500091BF00003C000020340C00400C04004066 +:10BE60001805004010ED00E010050240010000013F +:10BE700070B5224CA07808B909F022FF012085078F +:10BE8000A861207A002603280AD100BFD5F80C014A +:10BE900020B9002009F03EFF0028F7D1C5F80C6159 +:10BEA00026724FF0FF30C5F8080370BD70B5134C13 +:10BEB0006079F0B1012803D0A179401E814218DADF +:10BEC00009F00BFF05460AF09FF86179012902D9B4 +:10BED000A179491CA1710DB1216900E0E168411A05 +:10BEE000022902DA11F1020F06DC0DB1206100E037 +:10BEF000E060BDE8704008E670BD00003C00002036 +:10BF000010B5202000F07FF8202000F08DF84D497A +:10BF1000202081F80004F5F771FA4B4908604B487E +:10BF2000D0F8041341F00101C0F80413D0F8041351 +:10BF300041F08071C0F80413424901201C39C1F856 +:10BF4000000110BD10B5202000F05DF83E48002132 +:10BF5000C8380160001D01603D4A481E10603B4A20 +:10BF6000C2F80803384B1960C2F80001C2F860013A +:10BF700038490860BDE81040202000F055B8344929 +:10BF80003548091F086070473149334808607047D9 +:10BF90002D48C8380160001D521E026070472C49B0 +:10BFA00001200860BFF34F8F70472DE9F041284909 +:10BFB000D0F8188028480860244CD4F800010025E7 +:10BFC000244E6F1E28B14046F5F776F940B900219E +:10BFD00011E0D4F8600198B14046F5F76DF948B129 +:10BFE000C4F80051C4F860513760BDE8F04120202A +:10BFF00000F01AB831684046BDE8F04119F08ABB3C +:10C00000FFDFBDE8F08100280DDB00F01F020121F9 +:10C0100091404009800000F1E020C0F88011BFF39A +:10C020004F8FBFF36F8F7047002809DB00F01F02AE +:10C03000012191404009800000F1E020C0F8801209 +:10C040007047000020E000E0C80602400000024007 +:10C050001805024000040240010000010F4A126866 +:10C060000D498A420CD118470C4A12680A4B9A4271 +:10C0700006D101B509F09AFFFFF781FFBDE8014045 +:10C08000074909680958084706480749054A064B01 +:10C090007047000000000000BEBAFECA5400002035 +:10C0A000040000208013002080130020F8B51D46F6 +:10C0B000DDE906470E000AD006F0E0FD2346FF1D2D +:10C0C000BCB231462A46009406F0EDF9F8BDD0190D +:10C0D0002246194619F052FA2046F8BD70B50D46B1 +:10C0E0000446102119F0C9FA258117206081A07B30 +:10C0F00040F00A00A07370BD4FF6FF720A8001463F +:10C1000002200AF099B9704700897047827BD307F3 +:10C1100001D1920703D48089088000207047052050 +:10C120007047827B920700D58181704701460020CD +:10C13000098841F6FE52114200D00120704700B537 +:10C140000346807BC00701D0052000BD59811846F9 +:10C15000FFF7ECFFC00703D0987B40F00400987312 +:10C16000987B40F001009873002000BD827B52074D +:10C1700000D509B14089704717207047827B61F371 +:10C18000C302827370472DE9FC5F0E4604460178B6 +:10C190009646012000FA01F14DF6FF5201EA02092C +:10C1A00062684FF6FF7B1188594502D10920BDE82E +:10C1B000FC9FB9F1000F05D041F6FE55294201D090 +:10C1C0000120F4E741EA090111801D0014D0002389 +:10C1D0002B7094F800C0052103221F464FF0020A7D +:10C1E000BCF10E0F76D2DFE80CF0F909252F476479 +:10C1F0006B77479193B4D1D80420D8E76168208940 +:10C200008B7B9B0767D517284AD30B89834247D37B +:10C210008989172901D3814242D185F800A0A5F868 +:10C2200001003280616888816068817B21F00201B1 +:10C230008173C6E0042028702089A5F80100608978 +:10C24000A5F803003180BCE0208A3188C01D1FFAA8 +:10C2500080F8414524D3062028702089A5F80100E4 +:10C260006089A5F80300A089A5F805000721208AA8 +:10C27000CDE90001636941E00CF0FF00082810D00F +:10C28000082028702089A5F801006089A5F803001E +:10C2900031806A1D694604F10C0008F057F910B1AD +:10C2A0005EE01020EDE730889DF8001008443080F3 +:10C2B00087E00A2028702089A5F80100328044E038 +:10C2C0000C2028702089A5F801006089A5F80300DA +:10C2D00031803AE082E064E02189338800EB41025A +:10C2E0001FFA82F843453BD3B8F1050F38D30E222D +:10C2F0002A700BEA4101CDE90010E36860882A4604 +:10C300007146FFF7D3FEA6F800805AE0402028705F +:10C3100060893188C01C1FFA80F8414520D32878F5 +:10C32000714620F03F00123028702089A5F80100E6 +:10C330006089CDE9000260882A46E368FFF7B6FE0F +:10C34000A6F80080287840063BD461682089888060 +:10C3500037E0A0893288401D1FFA80F8424501D29B +:10C3600004273DE0162028702089A5F80100608987 +:10C37000A5F80300A089CDE9000160882A4671462E +:10C380002369FFF793FEA6F80080DEE718202870E7 +:10C39000207A6870A6F800A013E061680A88920409 +:10C3A00001D405271CE0C9882289914201D00627C3 +:10C3B00016E01E21297030806068018821F4005148 +:10C3C0000180B9F1000F0BD0618878230022022090 +:10C3D00009F088FF61682078887006E033800327C1 +:10C3E0006068018821EA090101803846DFE62DE90D +:10C3F000FF4F85B01746129C0D001E461CD03078AA +:10C40000C10703D000F03F00192801D9012100E045 +:10C4100000212046FFF7AAFEA8420DD32088A0F5F0 +:10C420007F41FF3908D03078410601D4000605D598 +:10C43000082009B0BDE8F08F0720FAE700208DF84A +:10C4400000008DF8010030786B1E00F03F0C0121D8 +:10C45000A81E4FF0050A4FF002094FF0030B9AB2E5 +:10C46000BCF1200F75D2DFE80CF08B10745E74689D +:10C47000748C749C74B674BB74C974D574E274748F +:10C4800074F274F074EF74EE748B052D78D18DF81E +:10C490000090A0788DF804007088ADF8060030791F +:10C4A0008DF80100707800F03F000C2829D00ADCDC +:10C4B000A0F10200092863D2DFE800F012621562E1 +:10C4C0001A621D622000122824D004DC0E281BD022 +:10C4D0001028DBD11BE016281FD01828D6D11FE06A +:10C4E0002078800701E020784007002848DAEFE054 +:10C4F00020780007F9E72078C006F6E72078800664 +:10C50000F3E720784006F0E720780006EDE7208882 +:10C51000C005EAE720884005E7E720880005E4E752 +:10C520002088C004E1E72078800729D5032D27D192 +:10C530008DF800B0B6F8010082E0217849071FD5D8 +:10C54000062D1DD381B27078012803D0022817D19F +:10C5500002E0CAE0022000E0102004228DF8002052 +:10C5600072788DF80420801CB1FBF0F2ADF8062043 +:10C5700092B242438A4203D10397ADF80890A7E0F4 +:10C580007AE02078000777D598B282088DF800A06D +:10C59000ADF80420B0EB820F6ED10297ADF8061013 +:10C5A00096E02178C90667D5022D65D381B20620B1 +:10C5B0008DF80000707802285ED300BFB1FBF0F266 +:10C5C0008DF80400ADF8062092B242438A4253D15E +:10C5D000ADF808907BE0207880064DD5072003E079 +:10C5E000207840067FD508208DF80000A088ADF89F +:10C5F0000400ADF80620ADF8081068E020780006C9 +:10C6000071D50920ADF804208DF80000ADF80610B2 +:10C6100002975DE02188C90565D5022D63D381B2FB +:10C620000A208DF80000707804285CD3C6E72088C3 +:10C63000400558D5012D56D10B208DF80000A0885B +:10C64000ADF8040044E021E026E016E0FFE7208892 +:10C65000000548D5052D46D30C208DF80000A08894 +:10C66000ADF80400B6F803006D1FADF80850ADF842 +:10C670000600ADF80AA02AE035E02088C00432D5D3 +:10C68000012D30D10D208DF8000021E0208880049C +:10C6900029D4B6F80100E080A07B000723D5032D44 +:10C6A00021D3307800F03F001B2818D00F208DF8E0 +:10C6B0000000208840F40050A4F80000B6F8010003 +:10C6C000ADF80400ED1EADF80650ADF808B00397C4 +:10C6D00069460598F5F71EFC050008D016E00E2007 +:10C6E0008DF80000EAE7072510E008250EE0307815 +:10C6F00000F03F001B2809D01D2807D00220059913 +:10C7000009F09AFE208800F400502080A07B4007AA +:10C7100008D52046FFF70AFDC00703D1A07B20F013 +:10C720000400A073284684E61FB5022806D1012024 +:10C730008DF8000088B26946F5F7ECFB1FBD0000DC +:10C74000F8B51D46DDE906470E000AD006F096FA58 +:10C750002346FF1DBCB231462A46009405F0A3FED5 +:10C76000F8BDD0192246194618F008FF2046F8BD3A +:10C770002DE9FF4F8DB09B46DDE91B57DDF87CA00E +:10C780000C46082B05D0E06901F002F950B11020E9 +:10C79000D2E02888092140F0100028808AF8001093 +:10C7A000022617E0E16901208871E2694FF4205107 +:10C7B0009180E1698872E06942F601010181E069D6 +:10C7C000002181732888112140F0200028808AF8F8 +:10C7D0000010042638780A900A2038704FF00209B9 +:10C7E00004F118004D460C9001F095FBB04681E035 +:10C7F000BBF1100F0ED1022D0CD0A9EB0800801C4C +:10C8000080B20221CDE9001005AB52461E990D9869 +:10C81000FFF796FFBDF816101A98814203D9F74822 +:10C8200000790F9004E003D10A9808B138702FE026 +:10C830004FF00201CDE900190DF1160352461E9981 +:10C840000D98FFF77DFF1D980088401B801B83B269 +:10C85000C6F1FF00984200D203461E990BA8D9B139 +:10C860005FF00002DDF878C0CDE9032009EB060196 +:10C8700089B2CDE901C10F980090BDF816100022D1 +:10C880000D9801F0CBFB387070B1C0B2832807D08F +:10C89000BDF8160020833AE00AEB09018A19E1E7A6 +:10C8A000022011B0BDE8F08FBDF82C00811901F015 +:10C8B000FF08022D0DD09AF80120424506D1BDF89F +:10C8C0002010814207D0B8F1FF0F04D09AF8018000 +:10C8D0001FE08AF80180C94800680178052902D163 +:10C8E000BDF81610818009EB08001FFA80F905EBEE +:10C8F000080085B2DDE90C1005AB0F9A01F00EFBC4 +:10C9000028B91D980088411B4145BFF671AF022D23 +:10C9100013D0BBF1100F0CD1A9EB0800801C81B221 +:10C920000220CDE9000105AB52461E990D98FFF794 +:10C9300007FF1D980580002038700020B1E72DE921 +:10C94000F8439C46089E13460027B26B9AB3491FD2 +:10C950008CB2F18FA1F57F45FF3D05D05518AD880C +:10C960002944891D8DB200E000252919B6F83C80C4 +:10C970000831414520D82A44BCF8011022F8021B96 +:10C98000BCF8031022F8021B984622F8024B91468D +:10C9900006F062F94FF00C0C41464A462346CDF8AA +:10C9A00000C005F04CFDF587B16B00202944A41DA3 +:10C9B0002144088003E001E0092700E0832738468E +:10C9C000BDE8F88310B50B88848F9C420CD9846B2A +:10C9D000E018048844B1848824F40044A41D23444E +:10C9E0000B801060002010BD0A2010BD2DE9F0471B +:10C9F0008AB00025904689468246ADF81850072730 +:10CA00004BE0059806888088000446D4A8F80060AA +:10CA100007A8019500970295CDE903504FF40073E4 +:10CA200000223146504601F0F9FA04003CD1BDF82D +:10CA30001800ADF82000059804888188B44216D10A +:10CA40000A0414D401950295039521F4004100973E +:10CA5000049541F4804342882146504601F0B4F8E1 +:10CA600004000BD10598818841F40041818005AA1A +:10CA700008A94846FFF7A6FF0400DCD000970598F8 +:10CA800002950195039504950188BDF81C3000229C +:10CA9000504601F099F80A2C06D105AA06A9484685 +:10CAA000FFF790FF0400ACD0ADF8185004E00598F3 +:10CAB000818821F40041818005AA06A94846FFF734 +:10CAC00081FF0028F3D00A2C03D020460AB0BDE82D +:10CAD000F0870020FAE710B50C46896B86B051B19B +:10CAE0000C218DF80010A18FADF80810A16B0191F9 +:10CAF0006946FAF718FB00204FF6FF71A063E18743 +:10CB0000A08706B010BD2DE9F0410D460746896BA0 +:10CB10000020069E1446002911D0012B0FD1324669 +:10CB200029463846FFF762FF002808D1002C06D0BE +:10CB3000324629463846BDE8F04100F038BFBDE82E +:10CB4000F0812DE9FC411446DDE9087C0E46DDE963 +:10CB50000A15521DBCF800E092B2964502D2072099 +:10CB6000BDE8FC81ACF8002017222A70A5F801600E +:10CB7000A5F803300522CDE900423B462A46FFF7DF +:10CB8000DFFD0020ECE770B50C4615464821204635 +:10CB900018F095FD04F1080044F81C0F00204FF632 +:10CBA000FF71E06161842084A5841720E08494F8FB +:10CBB0002A0040F00A0084F82A0070BD4FF6FF7288 +:10CBC0000A800146032009F037BC30B585B00C4619 +:10CBD0000546FFF780FFA18E284629B101218DF877 +:10CBE00000106946FAF79FFA0020E0622063606354 +:10CBF00005B030BDB0F8400070470000580000207C +:10CC000090F84620920703D4408808800020F3E77C +:10CC10000620F1E790F846209207EDD5A0F84410E1 +:10CC2000EAE70146002009880A0700D5012011F033 +:10CC3000F00F01D040F00200CA0501D540F0040019 +:10CC40008A0501D540F008004A0501D540F01000E2 +:10CC50000905D1D540F02000CEE700B5034690F895 +:10CC60004600C00701D0062000BDA3F842101846B8 +:10CC7000FFF7D7FF10F03E0F05D093F8460040F0C5 +:10CC8000040083F8460013F8460F40F001001870C6 +:10CC9000002000BD90F84620520700D511B1B0F831 +:10CCA0004200A9E71720A7E710F8462F61F3C30257 +:10CCB0000270A1E72DE9FF4F9BB00E00DDE92B3498 +:10CCC000DDE92978289D24D02878C10703D000F019 +:10CCD0003F00192801D9012100E000212046FFF77B +:10CCE000D9FFB04215D32878410600F03F010CD49B +:10CCF0001E290CD0218811F47F6F0AD13A8842B1E5 +:10CD0000A1F57F42FF3A04D001E0122901D10006CB +:10CD100002D504201FB0C5E5F9491D984FF0000A5F +:10CD200008718DF818A08DF83CA00FAA0A60ADF824 +:10CD30001CA0ADF850A02978994601F03F02701F61 +:10CD40005B1C04F1180C4FF0060E4FF0040BCDF8ED +:10CD500058C01F2A7ED2DFE802F07D7D107D267D3F +:10CD6000AC7DF47DF37DF27DF17DF47DF07D7D7D04 +:10CD7000EF7DEE7D7D7D7D7DED0094F84610B5F86C +:10CD80000100890701D5032E02D08DF818B022E3E7 +:10CD90004FF40061ADF85010608003218DF83C1015 +:10CDA000ADF84000D8E2052EEFD1B5F801002083A0 +:10CDB000ADF81C00B5F80310618308B1884201D9B1 +:10CDC00001207FE10020A07220814FF6FF702084B7 +:10CDD000169801F0A0F8052089F8000002200290C2 +:10CDE00083460AAB1D9A16991B9801F097F890BBE1 +:10CDF0009DF82E00012804D0022089F8010010209F +:10CE000003E0012089F8010002200590002203A917 +:10CE10000BA807F09BFBE8BB9DF80C00059981422D +:10CE20003DD13A88801CA2EB0B01814237DB02998D +:10CE30000220CDE900010DF12A034A4641461B9824 +:10CE4000FFF77EFC02980BF1020B801C80B217AA40 +:10CE500003A901E0A0E228E002900BA807F076FB0E +:10CE600002999DF80C00CDE9000117AB4A464146F6 +:10CE70001B98FFF765FC9DF80C100AAB0BEB01004B +:10CE80001FFA80FB02981D9A084480B202901699FE +:10CE90001B9800E003E001F041F80028B6D0BBF198 +:10CEA000020F02D0A7F800B053E20A208DF8180054 +:10CEB0004FE200210391072EFFF467AFB5F80100A0 +:10CEC0002083ADF81C00B5F80320628300283FF4EE +:10CED00077AF90423FF674AF0120A072B5F805001D +:10CEE00020810020A073E06900F052FD78B9E1696B +:10CEF00001208871E2694FF420519180E1698872C4 +:10CF0000E06942F601010181E06900218173F01FAF +:10CF100020841E98606207206084169800F0FBFF52 +:10CF2000072089F800000120049002900020ADF84D +:10CF30002A0028E01DE2A3E13AE1EAE016E2AEE0D1 +:10CF400086E049E00298012814D0E0698079012840 +:10CF500003D1BDF82800ADF80E00049803ABCDE96D +:10CF600000B04A4641461B98FFF7EAFB0498001DB3 +:10CF700080B20490BDF82A00ADF80C00ADF80E00A8 +:10CF8000059880B202900AAB1D9A16991B9800F082 +:10CF9000C5FF28B902983988001D05908142D1D279 +:10CFA0000298012881D0E0698079012805D0BDF878 +:10CFB0002810A1F57F40FF3803D1BDF82800ADF857 +:10CFC0000E00049803ABCDE900B04A4641461B98D9 +:10CFD000FFF7B6FB0298BBE1072E02D0152E7FF4B7 +:10CFE000D4AEB5F801102183ADF81C10B5F80320BC +:10CFF000628300293FF4E4AE91423FF6E1AE0121A5 +:10D00000A1724FF0000BA4F808B084F80EB0052E02 +:10D0100007D0C0B2691DE26907F079FA00287FF4F1 +:10D0200044AF4FF6FF70208401A906AA14A8CDF8DA +:10D0300000B081E885032878214600F03F031D9A5F +:10D040001B98FFF795FB8246208BADF81C0080E112 +:10D050000120032EC3D14021ADF85010B5F80110C6 +:10D060002183ADF81C100AAAB8F1000F00D00023EC +:10D07000CDE9020304921D98CDF804800090388811 +:10D080000022401E83B21B9800F0C8FF8DF81800E4 +:10D0900090BB0B2089F80000BDF8280037E04FF066 +:10D0A000010C052E9BD18020ADF85000B5F8011081 +:10D0B0002183B5F803002084ADF81C10B0F5007F83 +:10D0C00003D907208DF8180085E140F47C422284C2 +:10D0D0000CA8B8F1000F00D00023CDE90330CDE952 +:10D0E000018C1D9800903888401E83B21B9800F078 +:10D0F00095FF8DF8180028B18328A8D10220BDE043 +:10D10000580000200D2189F80010BDF83000401CA7 +:10D110001EE1032E04D248067FF537AE002017E14A +:10D12000B5F80110ADF81C102878400602D58DF82E +:10D130003CE002E007208DF83C004FF0000803209F +:10D14000CDE902081E9BCDF810801D980193A6F131 +:10D15000030B00901FFA8BF342461B9800F034FD3E +:10D160008DF818008DF83C80297849060DD5208867 +:10D17000C00506D5208BBDF81C10884201D1C4F82B +:10D18000248040468DF81880E2E0832801D14FF0DA +:10D19000020A4FF48070ADF85000BDF81C002083E7 +:10D1A000A4F820B01E986062032060841321CCE0B4 +:10D1B000052EFFF4EAADB5F80110ADF81C10A28FF2 +:10D1C00062B3A2F57F43FE3B28D008228DF83C20B5 +:10D1D0004FF0000B0523CDE9023BDDF878C0CDF818 +:10D1E00010B01D9A80B2CDF804C040F40043009204 +:10D1F000B5F803201B9800F0E7FC8DF83CB04FF425 +:10D2000000718DF81800ADF85010832810D0F8B1D7 +:10D21000A18FA1F57F40FE3807D0DCE00B228DF80E +:10D220003C204FF6FE72A287D2E7A4F83CB0D2E0D1 +:10D2300000942B4631461E9A1B98FFF780FB8DF811 +:10D24000180008B183284BD1BDF81C00208355E796 +:10D2500000942B4631461E9A1B98FFF770FB8DF801 +:10D260001800E8BBE18FA06B0844811D8DE88203A4 +:10D270004388828801881B98FFF763FC824668E038 +:10D2800095F80180022E70D15FEA080002D0B8F153 +:10D29000010F6AD109208DF83C0007A800908DF895 +:10D2A00040804346002221461B98FFF72CFC8DF856 +:10D2B00042004FF0000B8DF843B050B9B8F1010FA8 +:10D2C00012D0B8F1000F04D1A18FA1F57F40FF3833 +:10D2D0000AD0A08F40B18DF83CB04FF4806000E0E0 +:10D2E00037E0ADF850000DE00FA91B98F9F71BFFD0 +:10D2F00082468DF83CB04FF48060ADF85000BAF132 +:10D30000020F06D0FC480068C07928B18DF81800DB +:10D3100027E0A4F8188044E0BAF1000F03D0812080 +:10D320008DF818003DE007A80090434601222146F1 +:10D330001B98FFF7E8FB8DF8180021461B98FFF7B4 +:10D34000CAFB9DF8180020B9192189F800100120A6 +:10D3500038809DF83C0020B10FA91B98F9F7E3FE37 +:10D360008246BAF1000F33D01BE018E08DF818E0C8 +:10D3700031E02078000712D5012E10D10A208DF857 +:10D380003C00E088ADF8400003201B9909F054F8F8 +:10D390000820ADF85000C1E648067FF5F6AC4FF026 +:10D3A000040A2088BDF8501008432080BDF85000C2 +:10D3B00080050BD5A18FA1F57F40FE3806D11E98C0 +:10D3C000E06228982063A6864FF0030A5046A1E445 +:10D3D0009DF8180078B1012089F80000297889F8B3 +:10D3E0000110BDF81C10A9F802109DF8181089F85A +:10D3F0000410052038802088BDF850108843208014 +:10D40000E4E72DE9FF4F8846087895B00121814077 +:10D410004FF20900249C0140ADF820102088DDF86F +:10D420008890A0F57F424FF0000AFF3A06D039B14C +:10D43000000705D5012019B0BDE8F08F0820FAE7F4 +:10D44000239E4FF0000B0EA886F800B018995D4699 +:10D450000988ADF83410A8498DF81CB0179A0A71E4 +:10D460008DF838B0086098F8000001283BD00228F9 +:10D4700009D003286FD1307820F03F001D30307084 +:10D48000B8F80400E08098F800100320022904D1C5 +:10D49000317821F03F011B31317094F846100907B3 +:10D4A00059D505ABB9F1000F13D0002102AA82E8CB +:10D4B0000B000720CDE90009BDF83400B8F80410CE +:10D4C000C01E83B20022159800F0A8FD0028D1D11B +:10D4D00001E0F11CEAE7B8F80400A6F80100BDF885 +:10D4E0001400C01C04E198F805108DF81C1098F881 +:10D4F0000400012806D04FF4007A02282CD003281B +:10D50000B8D16CE12188B8F8080011F40061ADF8D9 +:10D51000201020D017281CD3B4F84010814218D313 +:10D52000B4F84410172901D3814212D1317821F087 +:10D530003F01C91C3170A6F801000321ADF8341079 +:10D54000A4F8440094F8460020F0020084F8460055 +:10D5500065E105257EE177E1208808F1080700F400 +:10D56000FE60ADF8200010F0F00F1BD010F0C00FDF +:10D5700003D03888228B9042EBD199B9B878C00794 +:10D5800010D0B9680720CDE902B1CDF804B0009001 +:10D59000CDF810B0FB88BA883988159800F014FBD4 +:10D5A0000028D6D12398BDF82010401C80294ED0E9 +:10D5B00006DC10290DD020290BD0402987D124E08A +:10D5C000B1F5807F6ED051457ED0B1F5806F97D197 +:10D5D000DEE0C80601D5082000E0102082460DA933 +:10D5E00007AA0520CDE902218DF83800ADF83CB03E +:10D5F000CDE9049608A93888CDE9000153460722F1 +:10D6000021461598FFF7B4F8A8E09DF81C200121E9 +:10D610004FF00A0A002A9BD105ABB9F1000F00D0E8 +:10D620000020CDE902100720CDE90009BDF8340043 +:10D630000493401E83B2218B0022159800F0EEFC6B +:10D640008DF81C000B203070BDF8140020E09DF810 +:10D650001C2001214FF00C0A002A22D113ABB9F192 +:10D66000000F00D00020CDE902100720CDE900090D +:10D670000493BDF83400228C401E83B2218B159890 +:10D6800000F0CCFC8DF81C000D203070BDF84C0073 +:10D69000401CADF8340005208DF83800208BADF823 +:10D6A0003C00BCE03888218B88427FF452AF9DF863 +:10D6B0001C004FF0120A00281CD1606AA8B1B8788B +:10D6C000C0073FF446AF00E018E0BA680720CDE994 +:10D6D00002B2CDF804B00090CDF810B0FB88BA8843 +:10D6E000159800F071FA8DF81C001320307001209D +:10D6F000ADF8340093E00000580000203988208BFA +:10D700008142D2D19DF81C004FF0160A0028A06B70 +:10D7100008D0E0B34FF6FF7000215F46ADF808B0C7 +:10D72000019027E068B1B978C907BED1E18F0DAB90 +:10D730000844821D03968DE80C0243888288018884 +:10D7400009E0B878C007BCD0BA680DAB03968DE885 +:10D750000C02BB88FA881598FFF7F3F905005ED034 +:10D76000072D72D076E0019005AA02A92046FFF7A6 +:10D7700029F90146E28FBDF80800824201D0002954 +:10D78000F1D0E08FA16B084407800198E08746E064 +:10D790009DF81C004FF0180A40B1208BC8B13888A2 +:10D7A000208321461598FFF796F938E004F1180018 +:10D7B0000090237E012221461598FFF7A4F98DF8E9 +:10D7C0001C000028EDD1192030700120ADF8340084 +:10D7D000E7E7052521461598FFF77DF93AE020880F +:10D7E00000F40070ADF8200050452DD1A08FA0F5B9 +:10D7F0007F41FE3901D006252CE0D8F808004FF013 +:10D80000160A48B1A063B8F80C10A1874FF6FF7153 +:10D81000E187A0F800B002E04FF6FF70A087BDF8E6 +:10D82000200030F47F611AD078230022032015995C +:10D8300008F058FD98F8000020712088BDF82010ED +:10D84000084320800EE000E007252088BDF8201066 +:10D8500088432080208810F47F6F1CD03AE0218814 +:10D86000814321809DF8380020B10EA91598F9F761 +:10D870005AFC05469DF81C000028EBD086F801A054 +:10D8800001203070208B70809DF81C0030710520C5 +:10D89000ADF83400DEE7A18EE1B118980DAB008839 +:10D8A000ADF834002398CDE90304CDE90139206BAC +:10D8B0000090E36A179A1598FFF7FCF905460120D6 +:10D8C0008DF838000EA91598F9F72DFC00B1054622 +:10D8D000A4F834B094F8460040070AD52046FFF774 +:10D8E000A0F910F03E0F04D114F8460F20F0040008 +:10D8F00020701898BDF83410018028469BE500B5CB +:10D9000085B0032806D102208DF8000088B2694650 +:10D91000F9F709FC05B000BD10B5384C0B7822684A +:10D92000012B02D0022B2AD111E013780BB1052B69 +:10D9300001D10423137023688A889A802268CB88D7 +:10D94000D38022680B891381498951810DE08B882E +:10D9500093802268CB88D38022680B8913814B89FE +:10D9600053818B899381096911612168F9F7DBFB88 +:10D97000226800210228117003D0002800D08120E5 +:10D9800010BD832010BD806B002800D0012070479F +:10D990008178012909D10088B0F5205F03D042F6D3 +:10D9A0000101884201D10020704707207047F0B57F +:10D9B00087B0002415460E460746ADF8184011E022 +:10D9C00005980088288005980194811DCDE90241C1 +:10D9D000072104940091838842880188384600F02A +:10D9E000F3F830B905AA06A93046FEF7EBFF002888 +:10D9F000E6D00A2800D1002007B0F0BD5800002072 +:10DA000010B58B7883B102789A4205D10B885BB14F +:10DA100002E08B79091D4BB18B789A42F9D1B0F8AD +:10DA200001300C88A342F4D1002010BD812010BD2C +:10DA3000072826D012B1012A27D103E0497801F046 +:10DA4000070102E04978C1F3C20105291DD2DFE8D0 +:10DA500001F00318080C12000AB1032070470220DD +:10DA6000704704280DD250B10DE0052809D2801E60 +:10DA7000022808D303E0062803D0032803D005209A +:10DA80007047002070470F20704781207047C0B258 +:10DA900082060BD4000607D5FE48807A4143C01D9C +:10DAA00001EBD00080B270470846704700207047F5 +:10DAB00070B513880B800B781C0625D5F54CA47A1D +:10DAC000844204D843F010000870002070BD9568AF +:10DAD00000F0070605EBD0052D78F54065F304133B +:10DAE0000B701378D17803F0030341EA032140F26D +:10DAF0000123B1FBF3F503FB15119268E41D00FB54 +:10DB0000012000EBD40070BD906870BD37B514469D +:10DB1000BDF8041011809DF804100A061ED5C1F34B +:10DB20000013DC49A568897A814208D8FE2811D102 +:10DB3000C91DC9085A42284617F097FD0AE005EBAF +:10DB4000D00100F00702012508789540A8439340D2 +:10DB500018430870207820F0100020703EBD2DE999 +:10DB6000F0410746C81C0E4620F00300B04202D028 +:10DB70008620BDE8F081C74D002034462E60AF807E +:10DB80002881AA72E8801AE0E988491CE9808106A8 +:10DB900014D4E17800F0030041EA002040F20121B2 +:10DBA000B0FBF1F201FB12012068FFF770FF298939 +:10DBB000084480B22881381A3044A0600C342078A0 +:10DBC0004107E1D40020D4E72DE9FF4F89B0164684 +:10DBD000DDE9168A0F46994623F44045084600F0D1 +:10DBE0000DFB04000FD0099804F0CAFE02902078C3 +:10DBF00000060AD5A748817A0298814205D8872075 +:10DC00000DB0BDE8F08F0120FAE7224601A9029885 +:10DC1000FFF74EFF834600208DF80C004046B8F118 +:10DC2000070F1AD001222146FFF702FF0028E7D193 +:10DC30002078400611D502208DF80C00ADF8107048 +:10DC4000BDF80400ADF81200ADF814601898ADF8F6 +:10DC50001650CDF81CA0ADF818005FEA094004D5B5 +:10DC600000252E46A84601270CE02178E07801F037 +:10DC7000030140EA012040F20121B0FBF1F28046AD +:10DC800001FB12875FEA494009D5B84507D1A17861 +:10DC9000207901F0030140EA0120B04201D3BE42E5 +:10DCA00001D90720ACE7A8191FFA80F9B94501D9B5 +:10DCB0000D20A5E79DF80C0028B103A90998F9F7F4 +:10DCC00030FA00289CD1B84507D1A0784FEA192135 +:10DCD00061F30100A07084F804901A9800B10580E7 +:10DCE000199850EA0A0027D0199830B10BEB0600BA +:10DCF0002A46199917F042FC0EE00BEB060857462E +:10DD0000189E099804F0A8FF2B46F61DB5B23946B7 +:10DD10004246009504F093FB224601A90298FFF7C2 +:10DD2000C7FE9DF80400224620F010008DF8040084 +:10DD3000DDE90110FFF7EAFE002061E72DE9FF4F62 +:10DD4000DFF8509182461746B9F80610D9F800005E +:10DD500001EB410100EB810440F20120B2FBF0F144 +:10DD600085B000FB11764D46DDF84C8031460698B3 +:10DD7000FFF78DFE29682A898B46611A0C31014410 +:10DD80001144AB8889B28B4202D8842009B038E7AD +:10DD90000699CDB2290603D5A90601D50620F5E7D7 +:10DDA000B9F806C00CF1010C1FFA8CFCA9F806C0EA +:10DDB000149909B1A1F800C0A90602D5C4F80880D9 +:10DDC00007E0104480B2A9F80800191A01EB0B0013 +:10DDD000A0602246FE200699FFF798FEE7702671A4 +:10DDE0002078390A61F30100320AA17840F004007A +:10DDF00062F30101A17020709AF802006071BAF814 +:10DE00000000E08000262673280602D599F80A70E3 +:10DE100000E00127A80601D54FF000084D46002478 +:10DE20004FF007090FE0CDE902680196CDF80090A8 +:10DE30000496E9882046129B089AFFF7C5FE002841 +:10DE4000A4D1641CE4B2BC42EDD300209EE72DE9CE +:10DE5000F047804600F0D2F9070005D0002644467E +:10DE60000C4D40F2012919E00120BDE8F087204661 +:10DE700000F0C4F90278C17802F0030241EA0222FC +:10DE8000B2FBF9F309FB13210068FFF700FE3044F1 +:10DE900086B201E0F8050020641CA4B2E988601E87 +:10DEA0008142E4DCA8F10100E8802889801B2881F8 +:10DEB00000203870D9E710B5144631B1491E2180D1 +:10DEC00004F05EFDA070002010BD012010BD10B553 +:10DED000D24904460088CA88904201D30A2010BD66 +:10DEE000096800EB400001EB80025079A072D088F5 +:10DEF00020819178107901F0030140EA0120A0818E +:10DF0000A078E11CFFF7D4FD20612088401C208010 +:10DF1000E080002010BD0121018270472DE9FF4FF4 +:10DF200085B04FF6FF788246A3F8008048681F4608 +:10DF30000D4680788DF8060048680088ADF804002A +:10DF400000208DF80A00088A0C88A04200D30446FD +:10DF50002C8241E0288A401C2882701D6968FFF7E6 +:10DF60004FFDB8BB3988414501D1601E38806888B3 +:10DF7000A04236D3B178307901F0030140EA01299B +:10DF800001A9701DFFF73CFD20BB298941452CD01C +:10DF9000002231460798FFF74BFDD8B9298949453A +:10DFA00018D1E9680391B5F80AC0D6F808B0504610 +:10DFB000CDF800C004F050FEDDF800C05A460CF168 +:10DFC000070C1FFA8CFC4B460399CDF800C004F0F7 +:10DFD00000FA50B1641CA4B2204600F00FF906000C +:10DFE000B8D1641E2C820A20D0E67C807079B8718A +:10DFF000F088B8803178F07801F0030140EA012020 +:10E000007881A7F80C90504604F0BAFC324607F12C +:10E010000801FFF74DFD38610020B7E62DE9FF4FFD +:10E0200087B081461C469246DDF860B0DDF854802A +:10E03000089800F0E3F805000CD0484604F0A0FC76 +:10E040002978090608D57549897A814204D887203C +:10E050000BB0D6E50120FBE7CAF309062A4601A961 +:10E06000FFF726FD0746149807281CD000222946F2 +:10E07000FFF7DEFC0028EBD12878400613D50120FD +:10E080008DF808000898ADF80C00BDF80400ADF854 +:10E090000E00ADF81060ADF8124002A94846F9F73D +:10E0A00040F80028D4D12978E87801F0030140EA4B +:10E0B0000121AA78287902F0030240EA022056459D +:10E0C00007D0B1F5007F04D9611E814201DD0B202C +:10E0D000BEE7864201D90720BAE7801B85B2A54278 +:10E0E00000D92546BBF1000F01D0ABF800501798BE +:10E0F00018B1B9192A4617F041FAB8F1000F0DD03E +:10E100003E4448464446169F04F0B8FD2146FF1D94 +:10E11000BCB232462B46009404F0C5F9002097E7C4 +:10E120002DE9F04107461D461646084600F066F800 +:10E1300004000BD0384604F023FC2178090607D5EB +:10E140003649897A814203D8872012E5012010E5FB +:10E1500022463146FFF7ACFC65B12178E07801F04A +:10E16000030140EA0120B0F5007F01D8012000E062 +:10E17000002028700020FCE42DE9F04107461D46F0 +:10E180001646084600F03AF804000BD0384604F072 +:10E19000F7FB2178090607D52049897A814203D8FF +:10E1A0008720E6E40120E4E422463146FFF7AEFC96 +:10E1B000FF2D14D02178E07801F0030240EA02201C +:10E1C00040F20122B0FBF2F302FB130015B900F29A +:10E1D000012080B2E070000A60F30101217000208C +:10E1E000C7E410B50C4600F009F828B1C1882180B9 +:10E1F0004079A070002010BD012010BD0749CA88D9 +:10E20000824209D340B1096800EB40006FF00B0275 +:10E2100002EB80000844704700207047F80500209A +:10E2200010B508F0EFFAF4F741FB08F051F9BDE83A +:10E23000104008F019BA302834BF01200020704780 +:10E24000202834BF4FF0A0420C4A012300F01F00E9 +:10E2500003FA00F0002914BFC2F80C05C2F8080543 +:10E260007047202834BF4FF0A041044900F01F0040 +:10E27000012202FA00F0C1F81805704700030050AF +:10E2800070B50346002002466FF02F050EE09C5C3F +:10E29000A4F130060A2E02D34FF0FF3070BD00EB20 +:10E2A000800005EB4000521C2044D2B28A42EED3DB +:10E2B00070BD30B50A230BE0B0FBF3F403FB14048C +:10E2C000B0FBF3F08D183034521E05F8014CD2B279 +:10E2D000002AF1D130BD30B500234FF6FF7510E0B4 +:10E2E000040A44EA002084B2C85C6040C0F303140E +:10E2F000604005EA00344440E0B25B1C84EA401010 +:10E300009BB29342ECD330BD2DE9F041FA4B00268D +:10E31000012793F860501C7893F864C0B8B183F873 +:10E320008D40A3F88E1083F88C2083F88A70BCF19E +:10E33000000F0CBF83F8906083F89050EF4880681E +:10E34000008804F089FCBDE8F04104F01FB94FF6E5 +:10E35000FF7083F88D40A3F88E0083F88C2083F83B +:10E360008A70BCF1000F14BF83F8905083F890605E +:10E37000BDE8F08170B5E14E0446306890F8981021 +:10E380000025012919D090F89210012924D090F885 +:10E39000681001292AD090F88A1001291CBF00209A +:10E3A00070BD657017212170D0F88C106160B0F8D5 +:10E3B0009010218180F88A5016E065701C21217030 +:10E3C000D0F899106160D0F89D10A16090F8A1106C +:10E3D000217380F8985007E0657007212170D0F80C +:10E3E0009410616080F89250012070BD6570142116 +:10E3F000217000F16A012022201D17F0BFF80121D1 +:10E400002172306880F86850BB48B0F86C20A0F8E2 +:10E410009420B268537B80F8963080F89210108870 +:10E4200004F01AFC04F0C1F8DEE7B448006890F884 +:10E430006810002914BFB0F86C004FF6FF707047E9 +:10E4400070B5AE4C06462068002808BFFFDF0025E7 +:10E45000206845706660002808BFFFDF20684178AB +:10E4600000291CBFFFDF70BDA42117F028F9206828 +:10E47000FF2101707F2180F836101321418428216B +:10E4800080F86510012180F8581080F85D5008F080 +:10E4900082FEBDE8704008F048B8984909680978DC +:10E4A00081420CBF0120002070479448006890F81A +:10E4B0002200C0F3400070479048006890F82200A6 +:10E4C00000F0010070478D48006890F82200C0F30A +:10E4D000001070472DE9F04388480024016891F846 +:10E4E0002400B1F822C0C0F38002C0F340031A44F4 +:10E4F00000F001000244CCF3001060B3BCF1130F34 +:10E5000021D00BDCBCF1100F02BF7D4830F81200A7 +:10E51000BDE8F083BCF1120F15D008E0BCF1150F77 +:10E5200009D0BCF11D0F04BF7648BDE8F083FFDFC2 +:10E530002046BDE8F0837449002031F8121012FB28 +:10E540000010BDE8F0837149002031F8121012FB71 +:10E550000010BDE8F08391F85A3091F85B002E2648 +:10E560004FF47A774FF014084FF04009022B04BFA4 +:10E570004AF2D745B5FBF7F510D0012B04BF4AF29C +:10E580002F75B5FBF7F510D04AF62315B5FBF7F557 +:10E59000082B08BF4E4613D0042B18D02646082B54 +:10E5A0000ED0042B13D0022B49D004F12806042BE3 +:10E5B0000FD0082B1CBF4FF01908082304D00AE025 +:10E5C0004FF0140806F5A8764FF0400303E006F577 +:10E5D000A8764FF0100318FB036313FB0253C2EB42 +:10E5E00002124B4D02EB820205EB82021A441CF030 +:10E5F000010F4FF4C8734FF4BF7504BFCCF340064E +:10E60000002E77D0CCF3400602F5A572EEB10828B3 +:10E6100004BF1E4640270CD0042804BF2E461027F6 +:10E6200007D0022807BF04F11806042704F12806C2 +:10E63000082707EB870808EB87173E441BE004F127 +:10E6400018064FF019080423C5E7082804BF1E4622 +:10E6500040270CD0042804BF2E46102707D00228DC +:10E6600007BF04F11806042704F12806082707EB62 +:10E67000871706EB8706324402F19C0691F8652065 +:10E6800010F00C0F08BF00223244082804BF1E46B9 +:10E6900040270CD0042804BF2E46102707D002289C +:10E6A00007BF04F11806042704F128060827C7EB62 +:10E6B000C70707EB470706EB4706324498321CF0C2 +:10E6C000010F27D0082808BF40200CD0042804BF21 +:10E6D0002B46102007D0022807BF04F1180304209E +:10E6E00004F12803082000EB400101EB001018445E +:10E6F00002444AE04DE000000406002060000020D3 +:10E70000285B02008E891300305B0200205B020050 +:10E71000D4FEFFFF082804BF9C4640260CD00428E6 +:10E7200004BFAC46102607D0022807BF04F1180C1E +:10E73000042604F1280C082606EB8616898F0CEBBC +:10E74000860C6244EB2920D944F2552C0B3101FB95 +:10E750000CF1890D082807D0042802D0022805D022 +:10E7600008E02B46102008E0402006E004F11803E2 +:10E77000042002E004F12803082000EB801003EBE2 +:10E78000800000F5A57001FB002202F26510BDE8D3 +:10E79000F08302F5A572082804BF9C4640260CD0E1 +:10E7A000042804BFAC46102607D0022807BF04F196 +:10E7B000180C042604F1280C082606EB8616B1F87E +:10E7C00044100CEB860C6244EB29DED944F2552C44 +:10E7D0000B3101FB0CF1890D0828C5D00428C0D0ED +:10E7E0000228C7D1C2E7FE4840F271210068806A62 +:10E7F00048437047FA48006890F83500002818BF71 +:10E800000120704710B5F74C207B022818BF032861 +:10E8100008D1207D04F115010DF0A1FC08281CBFD2 +:10E82000012010BD207B002816BF022800200120F7 +:10E83000BDE8104009F0C0B9EA4908737047E849DB +:10E84000096881F8300070472DE9F047E44C2168F1 +:10E85000087B002816BF022800200120487301F120 +:10E860000E0109F093F92168087B022816BF0328DE +:10E870000122002281F82F204FF0080081F82D009E +:10E88000487B01F10E034FF001064FF0000701280D +:10E8900004BF5B7913F0C00F0AD001F10E03012809 +:10E8A00004D1587900F0C000402801D0002000E0D9 +:10E8B000012081F82E00002A04BF91F8220010F0F8 +:10E8C000040F07D0087D01F115010DF048FC216807 +:10E8D00081F82D002068476006F0CEF92168C14D0F +:10E8E0004FF00009886095F82D000DF054FC80462B +:10E8F00095F82F00002818BFB8F1000F04D095F844 +:10E900002D000DF00FFA68B195F8300000281CBFFB +:10E9100095F82E0000281DD0697B05F10E00012915 +:10E920000ED012E06E734A4605F10E01404609F022 +:10E9300082F995F82D1005F10E000DF023FD09E088 +:10E94000407900F0C000402831D0394605F10E0072 +:10E9500009F0A8F92068C77690F8220010F0040F9B +:10E9600008BFBDE8F087002795F82D000DF08EFA5E +:10E97000050008BFBDE8F08710210EF04CFA002812 +:10E9800018BFBDE8F08720683A4600F11C01C67642 +:10E99000284609F050F9206800F11C0160680EF06B +:10E9A00093FE6068BDE8F04701210EF0A8BE0DF0AF +:10E9B00026FD4A4605F10E0109F03DF9CAE7884AED +:10E9C0001268137B0370D2F80E000860508A8880AA +:10E9D000704778B583490446814D407B08732A68A7 +:10E9E000207810706088ADF8000080B200F001015E +:10E9F000C0F3400341EA4301C0F3800341EA8301CD +:10EA0000C0F3C00341EAC301C0F3001341EA03119C +:10EA1000C0F3401341EA4311C0F3801041EA801073 +:10EA20005084E07D012808BF012607D0022808BFD6 +:10EA3000022603D0032814BFFFDF0826286880F8C9 +:10EA40005A60607E012808BF012607D0022808BF4F +:10EA5000022603D0032814BFFFDF0826286880F8A9 +:10EA60005B60217B80F82410418C1D290CBF0021A4 +:10EA700061688162617D80F83510A17B002916BF35 +:10EA80000229002101210175D4F80F10C0F81510DA +:10EA9000B4F81310A0F81910A17EB0F8662061F345 +:10EAA0000302A0F86620E17E012918BF002180F84A +:10EAB0003410002078BD4A480068408CC0F3001133 +:10EAC00031B1C0F38000002804BF1F20704702E06E +:10EAD000C0F3400109B10020704710F0010F14BFCE +:10EAE000EE20FF2070473E480068408CC0F30011C4 +:10EAF00019B1C0F3800028B102E0C0F3400008B1B2 +:10EB000000207047012070473549002209680A66D5 +:10EB10004B8C1D2B0CBF81F8642081F8640070477A +:10EB200000232F4A126882F859309164A2F84C00F1 +:10EB3000012082F859007047294A0023126882F8A0 +:10EB40005830A2F854000120116582F8580070472F +:10EB50002349096881F85D0070472148006890F9F1 +:10EB60005D0070471E48006890F82200C0F3401016 +:10EB700070471B48006890F82200C0F3C00070473F +:10EB8000012070471648006890F85B00704770B528 +:10EB900008F0EBFA08F0CAFA08F0A2F908F020FA37 +:10EBA0000F4C2068016E491C016690F83300002567 +:10EBB00030B108F0F0FA07F0B8FC206880F8335064 +:10EBC0002068457090F8371021B1BDE870400420EE +:10EBD00009F0D7BC90F8641001B3006E814203E0E5 +:10EBE000600000200406002018D8042009F0C9FCA9 +:10EBF000206890F8220010F0010F07D0A06843228F +:10EC00000188BDE870400120FFF77EBBBDE8704081 +:10EC100043224FF6FF710020FFF776BBBDE870403E +:10EC2000002009F0AEBC2DE9F04782B00F468146C6 +:10EC3000FE4E4FF000083068458C15F0030F10D0E1 +:10EC400015F0010F05F0020005D0002808BF4FF0B5 +:10EC5000010806D004E0002818BF4FF0020800D1D8 +:10EC6000FFDF4FF0000A544615F0010F05F00200D7 +:10EC70000DD080B915F0040F0DD04AF00800002F18 +:10EC80001CBF40F0010040F0020440D08FE010B102 +:10EC900015F0040F0DD015F0070F10D015F0010F6F +:10ECA00005F0020036D0002808BF15F0040F27D069 +:10ECB0003DE0002F18BF4AF0090478D134E02FB1AD +:10ECC0004AF0080415F0200F14D070E0316805F008 +:10ECD0002002B1F84400104308BF4AF0010466D096 +:10ECE0004AF0180415F0200F61D191F85A10082944 +:10ECF00059D155E0316891F85A10082950D152E0A5 +:10ED00004AF00800002F18BF40F001044FD140F036 +:10ED100010044CE0002818BF15F0040F07D0002F96 +:10ED200018BF4AF00B0442D14AF018043FE015F036 +:10ED3000030F3BD115F0040F38D077B131684AF09A +:10ED4000080091F85A1008290CBF40F0020420F086 +:10ED5000020415F0200F21D029E0316805F02002CF +:10ED6000B1F84400104308BF4AF003041FD04AF032 +:10ED7000180015F0200F08D091F85A10082914BF78 +:10ED800040F0020420F0020411E091F85A20082A11 +:10ED900014BF40F0010020F00100EDE7082902D087 +:10EDA00024F0010403E044F0010400E0FFDF15F06B +:10EDB000400F1BD0C7B93168B1F84400002804BF28 +:10EDC000488C10F0010F0BD110F0020F08BF10F0AB +:10EDD000200F05D115F0010F08BF15F0020F03D069 +:10EDE00091F85A00082801D044F040047068A0F857 +:10EDF00000A0017821F02001017007210EF030FC05 +:10EE0000414670680EF023FE214670680EF02BFE1E +:10EE100014F0010F0AD006230022854970680EF015 +:10EE2000FCFD3068417B70680EF05CFC14F0020F52 +:10EE300018D0D6E90010B9F1000F4FF006034FF0DB +:10EE4000010207D01C310EF0E8FD012170680EF0C0 +:10EE500056FC07E015310EF0E0FD3068017D70686A +:10EE60000EF04DFC14F0040F18BFFFDF14F0080F74 +:10EE700017D0CDF800A03068BDF800100223B0F81C +:10EE80006600020962F30B01ADF800109DF8011055 +:10EE9000032260F307118DF80110694670680EF0C7 +:10EEA000BCFD012F61D13068B0F84410E9B390F88F +:10EEB0002200C0F34000C0BB70680EF0C4FD401CCF +:10EEC000C7B23068B0F84420B0F85610551AC7F1F0 +:10EED000FF018D42A8BF0D46AA423AD990F8220000 +:10EEE00010F0010F35D144F01004214670680EF087 +:10EEF000BAFDF81CC0B2ED1E284482B23068B0F8EA +:10EF00006610036E090951FA83F190F85C30494F9D +:10EF10001944BC460023E1FB07C31B096FF0240C16 +:10EF200003FB0C1180F85C1000E01EE090F85B0021 +:10EF3000012101F037F80090BDF800009DF80210A3 +:10EF4000032340EA01400190042201A970680EF0F9 +:10EF500064FD3068AAB2016C70680EF0B2FD3068D2 +:10EF6000B0F856102944A0F8561014F0400F06D0FF +:10EF7000D6E90010012306225D310EF04EFD14F09B +:10EF8000200F18BFFFDF0020002818BFFFDF02B0EE +:10EF9000BDE8F0872DE9F843244C2068002808BF1D +:10EFA000FFDF2068417839BB0178FF2924D0002693 +:10EFB00080F83160A0F85660867080F8376030467F +:10EFC00008F022F807F0E2FC206890F95D0007F0F5 +:10EFD00082FD194807F085FD184807F0FBFF6068BF +:10EFE00008F015F8206890F8240010F0010F06D002 +:10EFF000252007F07EFD09E00C20BDE8F88310F025 +:10F00000020F18BF262075D007F073FD206890F816 +:10F010005A10252007F078FC206880F82C6007F053 +:10F02000EDFF206890F85A10002009E060000020F1 +:10F030001206002053E4B36E1C5B0200195B020051 +:10F0400007F04BFE0F21052007F019FD206890F80E +:10F050002E10002901BF90F82F10002990F82200EF +:10F0600010F0040F75D005F007FE0546206829460C +:10F07000806806F01AFBDFF83084074690FBF8F052 +:10F0800008FB10704142284605F0F7FA21688860B5 +:10F0900097FBF8F04A68104448600DF05DF80146AF +:10F0A0002068426891426FD8C0E90165FF4D4FF07A +:10F0B000010895F82D000DF06EF8814695F82F00A7 +:10F0C0000127002818BFB9F1000F04D095F82D00D2 +:10F0D0000CF028FEA8B195F8300000281CBF95F868 +:10F0E0002E00002825D0697B05F10E00012916D0DD +:10F0F0001AE0FFE710F0040F14BF2720FFDF83D1D1 +:10F1000084E73A466F7305F10E01484608F093FD17 +:10F1100095F82D1005F10E000DF034F909E0407955 +:10F1200000F0C000402815D0414605F10E0008F05F +:10F13000B9FD206890F8220010F0040F24D095F853 +:10F140002D000CF0A3FE05001ED010210DF063FE73 +:10F1500040B119E00DF053F93A4605F10E0108F0FF +:10F160006AFDE6E720683A4600F11C01C7762846AA +:10F1700008F061FD206800F11C0160680EF0A4FA3F +:10F18000012160680EF0BBFA2068417B0E3007F069 +:10F190005AFC206890F8581059B3B0F85410A0F8F1 +:10F1A0004410016D016490F82210C1F30011E9B917 +:10F1B000B0F8660002210509ADF80050684606F077 +:10F1C0003DFE28B1BDF80000C0F30B00A84204D1F9 +:10F1D000BDF80000401CADF800002168BDF800003B +:10F1E000B1F8662060F30F12A1F86620206880F85D +:10F1F0005860206890F8591031B1B0F84C108187F0 +:10F20000816C816380F85960B0F86610026E09095C +:10F2100051FA82F190F85C20DFF894C21144634601 +:10F220000022E1FB0C3212096FF0240302FB0311F0 +:10F2300080F85C100DF013F8032160680DF092F86F +:10F24000216881F833000020BDE8F883994988607F +:10F2500070472DE9F043974C83B0226892F8313023 +:10F260003BB1508C1D2808BFFFDF03B0BDE8F04361 +:10F270008DE401260027F1B1054692F85C0007F005 +:10F2800038FC206890F85B10FF2007F03DFB2068F9 +:10F290004FF4A57190F85B20002007F0E4FD206892 +:10F2A00090F8221011F0030F00F02E81002D00F0D5 +:10F2B000258100F029B992F822108046D07EC1F352 +:10F2C0000011002956D0054660680780017821F0BA +:10F2D00020010170518C132937D01FDC102908BF81 +:10F2E000022144D0122908BF062140D0FFDF6F4D14 +:10F2F000606805F10E010EF0D9F9697B60680EF0C7 +:10F30000F1F92068418C1D2918BF152965D0B0F886 +:10F310004420016C60680EF0FEF95EE0152918BF0C +:10F320001D29E3D14FF001010EF09AF960680178D0 +:10F3300041F020010170216885B11C310EF0C4F943 +:10F34000012160680EF0DBF9D1E700210EF088F9A9 +:10F350006068017841F020010170C8E715310EF0B6 +:10F36000B3F92068017D60680EF0C9F9BFE70EF0BF +:10F3700077F9BCE70021FFF756FC6068C17811F00F +:10F380003F0F2AD0017911F0100F26D00EF066F948 +:10F390002368024693F82410C1F38000C1F3400CA7 +:10F3A000604401F0010100EB010C93F82C10C1F353 +:10F3B0008000C1F34005284401F001010844ACEB92 +:10F3C0000000C1B293F85A0000F0ECFD0090032356 +:10F3D0000422694660680EF020FB2068002590F842 +:10F3E000241090F82C0021EA000212F0010F18BF3F +:10F3F00001250ED111F0020F04D010F0020F08BF4A +:10F40000022506D011F0040F03D010F0040F08BF3E +:10F410000425B8F1000F2BD0012D1BD0022D08BF01 +:10F4200026201BD0042D14BFFFDF272016D0206814 +:10F4300090F85A10252007F067FA206890F82210FB +:10F44000C1F3001169B101224FF49671002007F059 +:10F450000AFD0DE0252007F04CFBE8E707F049FB2B +:10F46000E5E790F85A204FF49671002007F0FBFC76 +:10F47000206890F82C10294380F82C1090F8242054 +:10F4800032EA01011DD04670418C13292CD027DCB3 +:10F49000102904BF03B0BDE8F083122924D000BFB7 +:10F4A000C1F30010002807E040420F0004060020CE +:10F4B00053E4B36E6000002018BFFFDF03B0BDE867 +:10F4C000F083418C1D2908BF80F82C70DBD0C1F37C +:10F4D0000011002914BF80F8316080F83170D2E744 +:10F4E000152918BF1D29DBD190F85A2003B04FF021 +:10F4F0000101BDE8F043084607F092BE90F85B209A +:10F500000121084607F08CFE2168002DC87E7CD0C2 +:10F510004A8C3D46C2F34000002808BF47F008056A +:10F5200012F0400F18BF45F04005002819BFD1F870 +:10F530003890B1F83C80D1F84090B1F844806068D0 +:10F54000072107800EF08CF8002160680EF07FFA2A +:10F55000294660680EF087FA15F0080F15D020686C +:10F56000BDF800100223B0F86600020962F30B0137 +:10F57000ADF800109DF80110032260F307118DF81B +:10F580000110694660680EF048FA60680EF024F9D0 +:10F590002168C0F1FE00B1F85620A8EB02018142BB +:10F5A000A8BF0146CFB2D019404542D245F0100164 +:10F5B00060680EF058FA60680EF00EF92168C0F12C +:10F5C000FE00B1F85610A8EB01018142A8BF014628 +:10F5D000CFB260680EF037FA3844421C2068B0F8A9 +:10F5E0006610036E090951FA83F190F85C30FF4D03 +:10F5F0001944AC460023E1FB05C31B096FF0240C42 +:10F6000003FB0C1180F85C1000E038E090F85B0020 +:10F61000012100F0C7FC0090BDF800009DF8021029 +:10F62000032340EA01400190042201A960680EF022 +:10F63000F4F9216891F8220010F0400F05D0012361 +:10F6400006225D3160680EF0E8F920683A46B0F8AD +:10F65000560000EB090160680EF033FA2068B0F83C +:10F6600056103944A0F8561008F0C1F9002818BF08 +:10F67000FFDF20684670867003B0BDE8F08301218B +:10F68000FFF7D1FAF0E7DA4810B50068417841B9E0 +:10F690000078FF2805D000210846FFF7DAFD00209A +:10F6A00010BD07F062FD07F041FD07F019FC07F0FF +:10F6B00097FC0C2010BD10B5CD4C206890F82200AE +:10F6C00010F0010F1CBFA06801884FF03C0212BF70 +:10F6D00001204FF6FF710020FEF716FE2168012081 +:10F6E00081F8370010BDC249096881F832007047BF +:10F6F0002DE9F041002508F010FF002800F00581F9 +:10F70000BB4C2068417801270026012906D0022938 +:10F7100001D003297ED0FFDFBDE8F0818178022689 +:10F720000029418C46D0C1F34002002A08BF11F0E5 +:10F73000010F70D090F85B204FF001014FF00000F6 +:10F7400007F06EFD216891F82200C0F34000002808 +:10F7500014BF0C20222091F85B1007F0D5F8206828 +:10F76000467090F8330058B106F0CBFE206890F850 +:10F770005B0010F00C0F0CBF4020452007F001FD8E +:10F78000206890F83400002818BF07F019FD2168A0 +:10F7900091F85B0091F8651010F00C0F08BF002184 +:10F7A000962007F055FC08F019F9002818BFFFDF74 +:10F7B000BDE8F081C1F3001282B110293FD090F86A +:10F7C000330020B106F09DFE402007F0DAFC2068EF +:10F7D00090F8221011F0040F36D043E090F8242066 +:10F7E00090F82C309A422AD1B0F84400002808BF83 +:10F7F00011F0010F05D111F0020F08BF11F0200F19 +:10F800007ED04FF001014FF00000FFF722FD20688D +:10F81000418C01E040E034E011F0010F04BFC1F37E +:10F820004001002907D1B0F85610B0F844209142A9 +:10F8300018BFBDE8F08180F83170BDE8F081BDE807 +:10F84000F0410021012004E590F83510012914BF92 +:10F850000329102545F00E0190F85A204FF00000C2 +:10F8600007F0DEFC206890F83400002818BF07F08D +:10F87000A7FC0021962007F0EBFB20684670BDE84E +:10F88000F081B0F85610B0F8440081423DD0BDE898 +:10F89000F04101210846DCE48178D9B1418C11F0B6 +:10F8A000010F1CD080F8687090F86A20B0F86C10D6 +:10F8B0000120FEF729FD2068467007F056FC07F08E +:10F8C00035FC07F00DFB07F08BFBBDE8F041032092 +:10F8D00008F057BE8178BDE8F0410120B9E411F08D +:10F8E000020F04BFFFDFBDE8F081B0F85610808F33 +:10F8F00081420AD001210846FFF7ABFC032000E05B +:10F9000003E021684870BDE8F081BDE8F041FFF7F1 +:10F910003EB9FFF73CB910B5354C206890F834106B +:10F9200049B1363007F05BFC18B921687F2081F8B7 +:10F93000360007F03BFC206890F8330018B107F060 +:10F940002AFC06F0F2FD08F0E8FDA8B1206890F866 +:10F950002210C1F3001179B14078022818BFFFDFEF +:10F9600000210120FFF775FC2068417800291EBFA7 +:10F9700040780128FFDF10BDBDE81040FFF707B950 +:10F980002DE9F0471A4C0F4680462168B8F1030F65 +:10F99000488C08BFC0F3400508D000F0010591F87D +:10F9A0003200002818BF4FF0010901D14FF00009C3 +:10F9B00007F093F80646B8F1030F0CBF4FF00208AA +:10F9C0004FF0010835EA090008BFBDE8F08720685C +:10F9D00090F8330090B10CF025FC38700146FF28F8 +:10F9E0000CD06068C01C0CF0F6FB03E053E4B36E6F +:10F9F0006000002038780CF022FC06436068017833 +:10FA0000C1F3801221680B7D9A4208D10622C01CE6 +:10FA1000153115F087FD002808BF012000D0002017 +:10FA20003978FF2906D0C8B9206890F82D0088429F +:10FA300016D113E0A0B1616811F8030BC0F3801078 +:10FA40000CF08DFB05460CF0EDFC38B128460CF0AF +:10FA50001DFA18B110210DF0DEF908B1012000E007 +:10FA60000020216891F8221011F0040F01D0F0B1AC +:10FA70001AE0CEB9FE4890F83500002818BF40457E +:10FA800015D1616811F8030BC0F380100CF067FB0F +:10FA900004460CF0C7FC38B120460CF0F7F918B159 +:10FAA00010210DF0B8F910B10120BDE8F087002059 +:10FAB000BDE8F0872DE9F04FEE4D074683B028688A +:10FAC00000264078022818BFFFDF28684FF07F0922 +:10FAD00090F8341049B1363007F081FB002804BF9C +:10FAE000286880F8369007F061FB68680DF0DAFD51 +:10FAF0000446002F00F0048268680DF05EFF0028C5 +:10FB000000F0FE8106F0B7FF002800F0F981FF2029 +:10FB1000DFF864B3DFF8588300274FF0010A062CA2 +:10FB200080F00082DFE804F0EFEFEF03EFF78DF8ED +:10FB3000000069460320FFF723FF002800F0E4805F +:10FB4000296891F8340010B191F89800D0B1286874 +:10FB5000817801294CD06868042107800DF080FD70 +:10FB600008F10E0168680DF0A1FD98F80D106868A5 +:10FB70000DF0B8FD2868828F816B68680DF0EFFD8D +:10FB800000F04DB99DF8000081F898A00A7881F83E +:10FB90009920FF280FD001F19B029A310CF004FB51 +:10FBA000002808BFFFDF286890F89A1041F0020192 +:10FBB00080F89A100DE068680278C2F3801281F82C +:10FBC0009A20D0F80320C1F89B20B0F80700A1F8D4 +:10FBD0009F00286800F1A10490F836007F2808BF34 +:10FBE000FFDF286890F83610217080F83690AEE775 +:10FBF00090F822009BF80490C0F38014686864F3C6 +:10FC00008619072107800DF02BFD002168680DF093 +:10FC10001EFF494668680DF026FF0623002208F102 +:10FC20000E0168680DF0F9FE2868417B68680DF0E8 +:10FC300059FD68680DF0D0FD29688A8FC0F1FE017A +:10FC40008A42B8BF1146CFB2BA423DD9F81EC7B2F8 +:10FC500049F0100A514668680DF005FF68680DF01C +:10FC6000F2FE3844431C2868B0F86610026E090999 +:10FC700051FA82F190F85C20DFF800920A44C846FD +:10FC80004FF0000CE2FB098C4FEA1C116FF0240CC2 +:10FC900001FB0C2180F85C1090F85B001A460121F2 +:10FCA00000F080F90190BDF804009DF806100323D0 +:10FCB00040EA01400290042202A968680DF0ADFEFE +:10FCC000514668680DF0CFFE34B1D5E9001001232C +:10FCD00006225D310DF0A1FE28683A46816B686806 +:10FCE0000DF0EFFE2868A0F85670818F8F420CBF90 +:10FCF0000121002180F8311007F079FE002818BF9B +:10FD0000FFDF8CE007E00DE128688078002840F0F4 +:10FD1000F98000F0F5B88DF8000068680178C1F34B +:10FD20008019D0F803100191B0F80700ADF8080071 +:10FD300069460520FFF724FE0028286873D08178E3 +:10FD4000002972D090F85BA0D5E90104D0F80F101B +:10FD5000C4F80E10B0F813106182417D2175817DC9 +:10FD60006175B0F81710E182B0F819106180B0F831 +:10FD70001B10A180B0F81D10E18000F11F0104F1FB +:10FD8000080015F0B0FD686890F8241001F01F011C +:10FD9000217690F82400400984F8740184F854A076 +:10FDA00084F855A0286890F8651084F8561090F8EB +:10FDB0005D0084F857009DF80010A86800F05BF91A +:10FDC000022008F0DEFB6868DBF800400DF1040A51 +:10FDD000078008210DF044FC002168680DF037FE13 +:10FDE000214668680DF03FFE0623002208F10E014F +:10FDF00068680DF012FE2868417B68680DF072FC9F +:10FE0000494668680DF07BFC06230122514668686C +:10FE10000DF003FE07F0EBFD002818BFFFDF032005 +:10FE20002968487070E066E0FFE76868AC684FF0EA +:10FE300001080278617BC2F3401211406173D0F86F +:10FE40000F10C4F80E10B0F813106182417D2175B7 +:10FE5000817D6175B0F81710E182B0F819106180EA +:10FE6000B0F81B10A180B0F81D10E18008E0000080 +:10FE70000406002060000020145B020053E4B36E0F +:10FE800000F11F0104F1080015F02DFD686890F8DD +:10FE9000241001F01F01217690F82400400984F815 +:10FEA000740184F8548084F85580286890F86510AF +:10FEB00084F8561090F85D0084F857009DF8001003 +:10FEC000A86800F0D8F8286880F868A090F86A2040 +:10FED000B0F86C100120FEF717FA2868477007F099 +:10FEE00044F907F023F906F0FBFF07F079F8012049 +:10FEF00008F047FB08E090F82200C0F3001008B1BA +:10FF0000012601E0FEF743FE286890F8330018B19F +:10FF100007F041F906F009FB66B100210120FFF767 +:10FF200098F910E0286890F82200C0F3001000282B +:10FF3000E8D0E5E728688178012904D190F85B10C2 +:10FF4000FF2006F0E1FC28684178002919BF4178BC +:10FF5000012903B0BDE8F08F4078032818BFFFDF08 +:10FF600003B0BDE8F08F70B57E4C06460D462068A4 +:10FF7000807858B106F07EFC21680346304691F83F +:10FF80005B202946BDE8704009F0C6B806F072FC57 +:10FF900021680346304691F85A202946BDE8704052 +:10FFA00009F0BAB878B50C4600210091082804BFC2 +:10FFB0004FF4C87040210DD0042804BF4FF4BF7027 +:10FFC000102107D0022807BF01F11800042101F118 +:10FFD00028000821521D02FB010562489DF800100F +:10FFE000006890F85C2062F3050141F040068DF84E +:10FFF000006090F85B00012828D002282DD0082846 +:020000040001F9 +:1000000018BFFFDF2FD000BF26F080008DF8000062 +:10001000C4EB041000EB80001E2101EB800005FB07 +:1000200004045148844228BFFFDF5048A0FB04105D +:10003000BDF80110000960F30C01ADF80110BDF826 +:1000400000009DF8021040EA014078BD9DF80200D2 +:1000500020F0E0008DF80200D6E79DF8020020F0C5 +:10006000E000203004E09DF8020020F0E000403085 +:100070008DF80200C8E72DE9F0413A4D04460E46DE +:10008000286890F86800002818BFFFDF002728685C +:1000900080F86A702188A0F86C106188A0F882103E +:1000A000A188A0F88410E188A0F8861094F8741153 +:1000B00080F8881090F82F1049B1427B00F10E01B2 +:1000C000012A04D1497901F0C001402934D090F8C7 +:1000D000301041B1427B00F10E01012A04BF497981 +:1000E00011F0C00F28D000F1760015F0F3FB68681E +:1000F000FF2E0178C1F380116176D0F80310C4F8A7 +:100100001A10B0F80700E08328681DD0C167E18BA2 +:10011000A0F8801000F17002511E30460CF044F837 +:10012000002808BFFFDF286890F86F1041F0020137 +:1001300080F86F10BDE8F081D0F80E10C0F876108E +:10014000418AA0F87A10D2E7C767A0F88070617E74 +:1001500080F86F10D4F81A100167E18BA0F87410C2 +:10016000BDE8F08160000020C4BF03008988888852 +:100170000178406829B190F8141190F8730038B9EB +:1001800001E001F0CDBD19B1042901D00120704773 +:100190000020704770B50C460546062102F02AFC87 +:1001A000606008B1002006E00721284602F022FC2A +:1001B000606018B101202070002070BD022070BD69 +:1001C0002DE9FC470C4606466946FFF7E3FF002889 +:1001D0007DD19DF8000050B1FEF727F9B0427CD0E8 +:1001E000214630460AF088F9002873D12DE00DF041 +:1001F000E7FEB04271D02146304613F027FB0028BD +:1002000068D1019D95F8D80022E0012000E000208F +:10021000804695F837004FF0010A4FF00009F0B121 +:1002200095F8380080071AD584F8019084F800A06A +:1002300084F80490E68095F839102172698F618105 +:10024000A98FA18185F8379044E0019D95F81401AC +:1002500058350028DBD1E87E0028D8D0D5E73046D5 +:1002600002F00CFD070000D1FFDF384601F01CFF53 +:1002700040B184F801900F212170E680208184F83C +:1002800004A027E0304602F0E7FC070000D1FFDFC2 +:10029000B8F1000F21D0384601F05DFFB8B19DF8EC +:1002A000000038B90198D0F800014188B14201D16D +:1002B00080F80090304607F0E8FB84F801900C21AC +:1002C000217084F80490E680297F217200E004E028 +:1002D00085F81B900120BDE8FC870020FBE71CB5DA +:1002E0006946FFF757FF00B1FFDF684601F024FDC4 +:1002F000FB4900208968A1F8DA001CBD2DE9FC410A +:1003000004460E46062002F01DFB0546072002F0BB +:1003100019FB2844C7B20025A8463E4417E02088B0 +:10032000401C80B22080B04202D34046A4F8008036 +:1003300080B2B84204D3B04202D20020BDE8FC81B2 +:100340006946FFF727FF0028F8D06D1CEDB2AE42DA +:10035000E5D84FF6FF7020801220EFE738B54FF652 +:10036000FF70ADF800000DE00621BDF8000002F0BE +:1003700053FB04460721BDF8000002F04DFB0CB111 +:1003800000B1FFDF00216846FFF7B8FF0028EBD07F +:1003900038BD70B507F0E6FB0BF0CDFCD14C4FF645 +:1003A000FF7600256683A683CFA0257001680079BB +:1003B000A4F14002657042F8421FA11C1071601C3C +:1003C00013F065FB25721B2060814FF4A471A1819D +:1003D000E08121820321A1740422E274A082E082E0 +:1003E000A4F13E00218305704680BD480C300570A5 +:1003F000A4F110000570468070BD70B5B84C16466B +:100400000D466060217007F027FBFFF7A7FFFFF79D +:10041000C0FF207810F0CDFFB5480EF07CFA2178AF +:10042000606813F0D9FA20780AF0D4FE284608F064 +:1004300010FCAF48FEF704F8217860680AF042F932 +:100440003146207813F0DAFDBDE870400BF073BC44 +:1004500010B501240AB1002010BD21B1012903D03B +:100460000024204610BD02210DF068FBF9E72DE9BC +:10047000F047040000D1FFDF9A4802211C3081467A +:10048000FFF73CFF00B1FFDF964D0620B5F81C805A +:1004900002F058FA0646072002F054FA3044C6B279 +:1004A000701CC7B2A88BB04228D120460DF0FEFCCC +:1004B000B0B1207818283FD1207901283CD1E088BC +:1004C000062102F097FA040000D1FFDF208807F030 +:1004D000DCFA2088062102F09FFA40B3FFDF2BE010 +:1004E000287860B300266670142020702021201D1B +:1004F00015F0E5F8022020712E701DE0B84217D1EA +:100500002046FDF737FFD0B12078172814D1207985 +:1005100068B1E088072102F06DFA40B1008807F069 +:10052000B4FAE088072102F077FA00B1FFDF03E0B8 +:100530002146FFF745FE10B10120BDE8F0870221FA +:100540004846FFF7DBFE10B9A98B4145AAD12046EA +:10055000BDE8F04713F098BD10B501F089FB08B174 +:100560000C2010BD0BF03AFC002010BD10B5044665 +:10057000007818B1012801D0122010BD01F089FBCC +:1005800020B10BF0DBFD08B10C2010BD207801F08C +:1005900036FBE21D04F11703611CBDE810400BF0AF +:1005A000C2BC10B5044601F063FB08B10C2010BDBD +:1005B000207828B1012803D0FF280BD0122010BDCD +:1005C00001F01DFB611C0BF0C9FB08B1002010BD40 +:1005D000072010BD01200BF0FBFBF7E710B50BF077 +:1005E000B0FD08B1002010BD302010BD10B504468C +:1005F00001F04FFB08B10C2010BD20460BF09BFD15 +:10060000002010BD10B501F044FB20B10BF096FDA9 +:1006100008B10C2010BD0BF0EBFC002010BDFF2139 +:1006200081704FF6FF7181802D4949680A78827187 +:100630008A880281498841810121417000207047E8 +:100640007CB50025022A19D015DC12F10C0F15D04B +:1006500009DC12F1280F11D012F1140F0ED012F193 +:10066000100F11D10AE012F1080F07D012F1040F98 +:1006700004D04AB902E0D31E052B05D8012806D0C4 +:10068000022808D003280AD0122528467CBD10462F +:10069000FEF75EFAF9E710460EF0E8F8F5E70846CF +:1006A00014466946FFF776FD08B10225EDE79DF88F +:1006B00000000198002580F85740E6E710B5134682 +:1006C00001220CF0E5FB002010BD10B5044611F02E +:1006D00070FC05280ED0204610F05AFE002010BDF8 +:1006E0006C000020E8070020FFFFFFFF1F00000054 +:1006F000A80600200C20F2E710B5044601F0C9FA64 +:1007000008B10C20EBE72146002007F02CFA00206E +:10071000E5E710B5044610F0C9FE50B108F02AFD17 +:1007200038B1207808F0BBFA20780EF0DBFB00200F +:10073000D5E70C20D3E710B5044601F0AAFA08B1BA +:100740000C20CCE72146012007F00DFA0020C6E777 +:1007500038B504464FF6FF70ADF80000A079E17996 +:10076000884216D02079FDF766FD90B16079FDF7DB +:1007700062FD70B10022A079114614F0B3F840B9BF +:100780000022E079114614F0ADF810B9207A07285C +:1007900001D9122038BD08F0FAFC60B911F009FC4B +:1007A00048B900216846FFF7A9FD20B1204606F0B0 +:1007B00086F8002038BD0C2038BD2DE9FC41817839 +:1007C00005461A2925D00EDC16292DD2DFE801F0C6 +:1007D0002C2C2C2C2C212C2C2C2C2C2C2C2C2C2C64 +:1007E0002C2C2C2121212A291ED00BDCA1F11E0149 +:1007F0000C2919D2DFE801F0181818181818181861 +:100800001818180D3A3904290ED2DFE801F00D024C +:100810000D022888B0F5706F06D201276946FFF7F0 +:10082000B9FC18B1022089E5122087E59DF8000087 +:1008300001F0ECF9019C08B1FC3401E004F5BC7452 +:100840009DF8000001F0E2F9019E08B1FD3601E0DB +:1008500006F279166846FFF78BFC08B1207808B1DC +:100860000C206BE52770A8783070684601F064FAB8 +:10087000002063E57CB50D466946FFF78BFC00263A +:1008800018B12E602E7102207CBD9DF8000001F091 +:10089000BDF9019C9DF80000583401F0B7F90198AA +:1008A00084F8406081682960017B297194F84010C8 +:1008B0000029F5D100207CBD70B5044691F85500A3 +:1008C00091F856300D4610F00C0F00D1002321890D +:1008D000A0880CF0A1FC696A81421DD2401A401C1C +:1008E000A1884008091A8AB2A2802189081A2081A9 +:1008F000668895F8541010460CF035FC864200D2FC +:1009000030466080E68895F8551020890CF02BFC65 +:10091000864200D23046E08070BDF0B585B00D460D +:10092000064603A9FFF736FC00282DD19DF80C00E0 +:1009300060B300220499FB20B1F84A30FB2B00D3AE +:100940000346B1F84C40FB20FB2C00D30446DFF8F3 +:100950003CCC9CE8811000900197CDF808C0ADF820 +:100960000230ADF806406846FFF7A6FF6E80BDF87E +:100970000400E880BDF808006881BDF80200A88086 +:10098000BDF806002881002005B0F0BD0122D1E7A6 +:100990002DE9F04186B0044600886946FFF7FAFB6E +:1009A000002876D12189E08801F0D5F9002870D19E +:1009B000A188608801F0CFF900286AD12189E088F8 +:1009C00001F0D7F9002864D1A188608801F0D1F93D +:1009D00007005ED1208802A9FFF79FFF00B1FFDF6B +:1009E000BDF8101062880920914252D3BDF80C1056 +:1009F000E28891424DD3BDF81210BDF80E20238934 +:100A00001144A2881A44914243D39DF80010019DDD +:100A10004FF00008012640F6480041B185F8A36177 +:100A2000019991F8E61105F5D17541B91AE085F8FB +:100A30000D61019991F8301105F5867509B13A27D4 +:100A400024E0E18869806188E9802189814200D3BE +:100A50000146A980A188814200D20846288101224E +:100A600001990FE0E18869806188E98021898142EC +:100A700000D30146A980A188814200D2084628817E +:100A8000019900222846FFF717FF2E7085F8018094 +:100A9000384606B0BDE8F0817AE710B5044601F0AB +:100AA000F8F820B10BF04AFB08B10C2017E62078CB +:100AB00001F0A5F8E279611C0BF0C1FC08B100203F +:100AC0000DE602200BE610B503780446002B4068C3 +:100AD00013460A46014609D05FF001000CF0A5FB61 +:100AE0006168496A884203D90120F8E50020F5E7EA +:100AF0000020F4E52DE9F04117468A781E4680462D +:100B000042B11546C87838B10446690706D52AB1FE +:100B1000012104E00725F5E70724F6E70021620735 +:100B200002D508B1012000E00020014206D00122D8 +:100B300011464046FFF7C7FF98B93BE051B100228C +:100B400001214046FFF7BFFF58B9600732D50122A7 +:100B500011461FE058B1012200214046FFF7B3FFC4 +:100B600008B1092096E7680724D5012206E0680746 +:100B70004FEA44700AD5002813DB002201214046C9 +:100B8000FFF7A1FFB0B125F0040513E0002811DA4A +:100B9000012200214046FFF796FF58B124F00404DB +:100BA00008E0012211464046FFF78DFF10B125F005 +:100BB0000405F3E73D70347000206BE710B586B094 +:100BC0000446008803A9FFF7E5FA002806D1A088AB +:100BD00030B1012804D0022802D0122006B07EE5F0 +:100BE0006B4602AA214603A8FFF784FF0028F5D12F +:100BF0009DF80C3000220121002B049B06D083F8C5 +:100C0000AD11049B93F8FA316BBB24E083F8171104 +:100C1000049B93F83C313BB9049B93F816311BB904 +:100C2000049B93F87D300BB13A2010E0049B83F8CD +:100C30001611049B9DF8081083F81811049B9DF869 +:100C4000001083F81911049BA188A3F81A110499C4 +:100C500081F81721C2E7049B93F8AC311BB9049BC0 +:100C600093F87D300BB13A2010E0049B83F8AC116F +:100C7000049B9DF8081083F8AE11049B9DF80010AA +:100C800083F8AF11049BA188A3F8B011049981F8EF +:100C9000AD21A3E710B504460020A17801B90120D9 +:100CA000E2780AB940F0020001F06CF8002803D1A4 +:100CB0002046BDE8104081E711E570B51C460D46A1 +:100CC00018B1012801D0122070BD1946104601F05C +:100CD00069F830B12146284601F06EF808B10020CD +:100CE00070BD302070BD70B5044600780E460128F6 +:100CF00004D018B1022801D0032841D1607828B16E +:100D0000012803D0022801D0032839D1E07B10B993 +:100D1000A078012834D1A07830F0050130D110F04E +:100D2000050F2DD06289E188E0783346FFF7C5FFD3 +:100D3000002826D1A07805281ED16589A28921899D +:100D400020793346FFF7B9FF00281AD15FF0010080 +:100D500004EB40014A8915442218D37892789342D3 +:100D60000ED1CA8889888A420AD1401CC0B20228A2 +:100D7000EED3E088A84203D3A07B08B1072801D9AD +:100D8000122070BD002070BD10B586B0044600F082 +:100D900062FF08B10C2021E7022104F10A0001F0F2 +:100DA0001EF8A0788DF80800A0788DF80000607813 +:100DB0008DF8040020788DF80300A07B8DF80500E5 +:100DC000E07B00B101208DF80600A078C10717D0A4 +:100DD000E07800F0FBFF8DF80100E088ADF80A0034 +:100DE0006089ADF80C00A078400716D5207900F096 +:100DF000EDFF8DF802002089ADF80E00A0890AE011 +:100E000040070AD5E07800F0E1FF8DF80200E088A5 +:100E1000ADF80E006089ADF8100002A810F052FB8A +:100E20000028B8D168460EF062F8D7E610B504463F +:100E30000121FFF758FF002803D12046BDE81040EC +:100E4000A2E74CE40278012A01D0BAB118E0427856 +:100E50003AB1012A05D0022A12D189B1818879B12B +:100E600000E059B1418849B1808838B101EB810176 +:100E7000490000EB8000B1EB002F01D20020704749 +:100E80001220704770B5044600780D46012809D03D +:100E900011F08FF8052803D010F025FA002800D0B3 +:100EA0000C2070BD0DF0F0FE88B10DF002FF0DF0CA +:100EB000FBFF0028F5D125B160780DF08CFF0028EC +:100EC000EFD1A1886088BDE8704010F021BB1220EE +:100ED00070BD10B504460121FFF7B4FF002804D10E +:100EE0002046BDE810400121CCE704E42DE9F0479D +:100EF0000746B0F84C50FB2092460E46FB2D00D31F +:100F00000546DFF88C86B8F80A00A84200D20546EC +:100F100097F85510284600F08DFEB8F80C10814265 +:100F200000D208468146B7F84A40FB20FB2C00D38C +:100F30000446B8F80E00A04200D2044697F85410B8 +:100F4000204600F077FEB8F81010814200D2084623 +:100F50004FF4A4721B2C01D0904203D11B2D25D03D +:100F6000914523D0F580A6F808907480B080524651 +:100F700039463046FFF7A0FC01203070F0881B385E +:100F8000E02800D9FFDF70881B38E02800D9FFDF98 +:100F9000308940F64814A0F5A470A04200D9FFDFC4 +:100FA000B088A0F5A470A04200D9FFDFBDE8F087AB +:100FB000F0B5871FDDE9056540F67B44A74213D2F3 +:100FC0008F1FA74210D288420ED8B2F5FA7F0BD2FB +:100FD000A3F10A00241FA04206D2521C4A43B2EBDE +:100FE000830F01DAAE4201D90020F0BD0120F0BD2F +:100FF0002DE9FC47477A8946044617F0050F7DD056 +:10100000F8087BD194F83A0008B9012F76D1002571 +:10101000A8462E46F90789F0010A19D0208A5146C0 +:1010200000F0C0FEF0B36089514600F0C5FEC8B3C1 +:10103000208A6189884261D8A18EE08DCDE90001C6 +:10104000238D628CA18BE08AFFF7B2FF50B301259C +:10105000B8070ED504EB4500828EC18DCDE9001294 +:10106000038D428C818BC08AFFF7A2FFD0B1A846C6 +:101070006D1C78071ED504EB45065146308A00F0FA +:1010800091FE78B17089514600F096FE50B1308AD9 +:10109000718988425ED8B18EF08DCDE90001338D23 +:1010A000728C00E00AE0B18BF08AFFF781FF28B173 +:1010B0002E466D1CB9F1000F03D030E03020BDE8A2 +:1010C000FC87F80707D0780705D504EB460160894F +:1010D000498988423ED1228A01211BE0414503D043 +:1010E00004EB4100008A024404EB4100C38A868A73 +:1010F000B3422FD1838B468BB34200E02AE029D143 +:10110000438C068CB34225D1038DC08C834221D100 +:10111000491CC9B2A942E1D3608990421AD3207810 +:1011200010B1012816D10DE0A078B9F1000F07D059 +:1011300040B1012806D0022804D003280AD101E0DA +:101140000028EED1607838B1012805D0022803D0FC +:10115000032801D01220B2E70020B0E7002147E7C2 +:101160000178C90702D0406812F061BF12F02EBFAB +:101170002DE9F04788B00D46AFF69422D2E90092EF +:10118000014690462846FFF733FF06000CD100F0D9 +:1011900062FD40B9FE4F387828B90CF011FFA0F578 +:1011A0007F41FF3902D00C2008B0FFE6032105F192 +:1011B000100000F014FEF64801AA3E380190F548F0 +:1011C0000290F34806211038039007A801F0E0FBD5 +:1011D000040035D003210BF0BBFBB98AA4F84A10F8 +:1011E000FA8AA4F84C20FB7C0093BA46BB7C20888A +:1011F00001F0BBFC00B1FFDF208806F045FC218830 +:1012000004F10E0000F04FFDE3A004F112070068A6 +:1012100000900321684604F007FE002069460A5C3E +:101220003A54401CC0B20328F9D3A88B6080688C64 +:10123000A080288DE080687A410703D508270AE05E +:101240000920B1E7C10701D0012704E0800701D5DB +:10125000022700E000273A46BAF81800114610F0BD +:10126000EBF90146A062204610F0F4F917F00C0FDC +:1012700009D001231A46214600200BF0D6FF616AEF +:10128000884200D90926002784F85E7084F85F70D0 +:10129000A87800F0B4FC6076D5F80300C4F81A0012 +:1012A000B5F80700E083C4F8089084F80C800120AA +:1012B00084F80801024604F586712046FFF716FE01 +:1012C0008DF800700121684604F0AEFD9DF8000025 +:1012D00000F00701C0F3C1021144C0F340100844FC +:1012E0008DF80000401D2076092801D208302076B4 +:1012F000002120460BF02CFB68780DF0D0FCEEBBF3 +:10130000A9782878EA1C0DF092FC48B10DF0D1FCC8 +:10131000A9782878EA1C0DF038FD060002D052E0CA +:10132000122650E0687A00F005010020CA0700D0BC +:1013300001208A0701D540F00200490701D540F09D +:1013400008000DF05DFC06003DD1214603200DF0A4 +:1013500046FD060037D10DF04CFD060033D1697A09 +:1013600001F005018DF81010697AC90708D0688965 +:10137000ADF81200288AADF8140000E023E0012047 +:10138000697A8A0700D5401C490707D505EB40005C +:101390004189ADF81610008AADF8180004A810F0C5 +:1013A00091F8064695F83A0000B101200DF03AFC9C +:1013B0004EB90DF079FD060005D1A98F204610F039 +:1013C00023F8060008D0208806F05FFB208806215D +:1013D00001F022FB00B1FFDF3046E5E601460020C8 +:1013E000C6E638B56A48007878B910F0E2FD0528FD +:1013F00005D00CF0E5FDA0F57F41FF3905D068462A +:1014000010F0C9F8040002D00CE00C2038BD0098A0 +:10141000008806F03AFB00980621008801F0FCFAEB +:1014200000B1FFDF204638BD1CB582894189CDE976 +:1014300000120389C28881884088FFF7B9FD08B18E +:1014400000201CBD30201CBD70B50546FFF7ECFF29 +:1014500000280ED12888062101F0CCFA040007D01C +:1014600000F05EFC20B1D4F80001017831B901E050 +:10147000022070BDD4F84C11097809B13A2070BD32 +:1014800005218171D4F8001100200881D4F80011E1 +:10149000A8884881D4F80011E8888881D4F8001120 +:1014A0002889C881D4F80001028941898A4204D878 +:1014B0008279082A01D88A4201D3122070BD298876 +:1014C0004180D4F8001102200870002070BD3EB5A4 +:1014D00004460BF06FFCB0B12D480125A0F140028D +:1014E0004570236842F8423F23790021137141700F +:1014F0006946062001F007FA00B1FFDF684601F0F7 +:10150000E0F910B10EE012203EBDBDF80440029893 +:1015100080F80851684601F0D4F918B9BDF8040004 +:10152000A042F4D100203EBD70B5054600880621DA +:1015300001F060FA040007D000F0F2FB20B1D4F80B +:101540000011087830B901E0022070BDD4F84C01D8 +:10155000007808B13A2070BD9620005D10F0010FB0 +:1015600024D0D5F802004860D5F806008860D4F889 +:101570000001698910228181D4F8000105F10C0174 +:101580000E3004F5807413F0F9FF07E0385B0200B9 +:10159000E807002078000020112233002168032092 +:1015A0000870216828884880002070BD0C2070BD1C +:1015B00038B504460078EF284DD86088ADF80000B3 +:1015C000009800F01DFC88B36188080708D4D4E9AE +:1015D000012082423FD8202A3DD3B0F5804F3AD82F +:1015E000207B18B3072836D8607B28B1012803D0A8 +:1015F000022801D003282ED14A0703D4022801D0A3 +:10160000032805D1A07B08B1012824D1480707D4BD +:10161000607D28B1012803D0022801D003281AD107 +:10162000C806E07D03D5012815D110E013E001289C +:1016300001D003280FD1C80609D4607E012803D049 +:10164000022801D0032806D1A07E0F2803D8E07E0F +:1016500018B1012801D0122038BD002038BDF8B5DE +:1016600014460D46064607F092FD08B10C20F8BD61 +:101670003046FFF79DFF0028F9D1FDF76EFA28707C +:10168000B07554B9FF208DF8000069460020FDF7C1 +:1016900053FA69460020FDF743FA3046BDE8F840AA +:1016A000FDF797B90022DAE770B50C46054612B18E +:1016B0001F2907D80CE0FF2C04D8FCF704FF18B151 +:1016C0001F2C01D9122070BD2846FCF7E6FE08B198 +:1016D000002070BD422070BD10B50446408810B196 +:1016E000FDF701FA78B12078618800F00102607896 +:1016F000FFF7DAFF002805D1FDF7DDF962888242A5 +:1017000003D9072010BD122010BD10466168FDF7F7 +:1017100013FA002010BD10B50446408810B1FCF744 +:10172000C4FE70B12078618800F001026078FFF794 +:10173000BBFF002804D160886168FDF7F1F9002043 +:1017400010BD122010BD7CB504464078422501280A +:1017500008D8A078FCF7A1FE20B120781225012836 +:1017600002D090B128467CBDFDF703FA20B1A088D5 +:101770000028F7D08028F5D8FDF702FA60B160782C +:101780000028EFD02078012808D006F09DFA044602 +:1017900007F0BCF900287DD00C207CBDFDF732F8A5 +:1017A00010B9FDF7DFF990B307F0F1FC0028F3D191 +:1017B000FCF73BFEA0F57F41FF39EDD1FDF744F882 +:1017C000A68842F210704643A079FDF79DF9FCF718 +:1017D00073FEF8B10022072101A801F0D9F8040036 +:1017E00043D0FA480321846020460AF0B6FF204621 +:1017F000FDF72CFDF64DA88AA4F84A00E88AA4F863 +:101800004C00FCF760FE60B1288B01210DE0FFE782 +:1018100012207CBD3146002007F044FAD8B3FFDF28 +:101820004CE0FDF7AFF90146288B07F0F0FA0146CE +:10183000A0620022204606F04AFAFCF744FEB0B946 +:10184000FDF7A0F910F00C0F11D001231A46214624 +:1018500018460BF0EAFC616A884208D90721BDF8F6 +:10186000040001F0D9F800B1FFDF09207CBDE87C5D +:101870000090AB7CEA8AA98A208801F076F900B151 +:10188000FFDF208806F000F93146204607F00AFA0B +:1018900018B101E008E011E0FFDF002204F5D1718A +:1018A0002046FFF723FB09E044B1208806F0EDF85D +:1018B0002088072101F0B0F800B1FFDF00207CBDD7 +:1018C000002140E770B50D46072101F093F80400B0 +:1018D00003D094F87B0110B10AE0022070BD94F8A7 +:1018E0006500142801D0152802D194F8C80108B168 +:1018F0000C2070BD1022294604F5BE7013F03EFE88 +:10190000012084F87B01002070BD10B5072101F093 +:1019100071F818B190F87B1111B107E0022010BDE9 +:1019200090F86510142903D0152901D00C2010BDA2 +:10193000022180F87B11002010BD2DE9FC410C46EE +:101940004BF68032122194421DD8E4B16946FEF76D +:1019500021FC002815D19DF8000000F057F9019EE8 +:101960009DF80000583600F051F9019DAD1C2F88FC +:101970002246394630460AF0E6FE2888B842F6D1BB +:101980000020BDE8FC810846FBE77CB504460088E2 +:101990006946FEF7FFFB002810D19DF8000000F01B +:1019A00035F9019D9DF80000583500F02FF9019898 +:1019B000A27890F82C10914201D10C207CBD7F219F +:1019C0002972A9720021E972E17880F82D1021793D +:1019D00080F82E10A17880F82C1000207CBD1CB55A +:1019E0000C466946FEF7D6FB00280AD19DF8000098 +:1019F00000F00CF9019890F8730000B101202070FC +:101A000000201CBD7CB50D4614466946FEF7C2FB9E +:101A1000002809D19DF8000000F0F8F8019890F82E +:101A20002C00012801D00C207CBD9DF8000000F0A6 +:101A3000EDF8019890F86010297090F8610020701E +:101A400000207CBD70B50D461646072100F0D2FF80 +:101A500018B381880124C388428804EB4104AC4256 +:101A600017D842F210746343A4106243B3FBF2F23E +:101A7000521E94B24FF4FA72944200D91446A54211 +:101A800000D22C46491C641CB4FBF1F24A43521E9E +:101A900091B290F8B4211AB901E0022070BD01841E +:101AA0003180002070BD10B50C46072100F0A2FF68 +:101AB00048B180F8E74024B190F8E51009B107F08B +:101AC000BCF9002010BD022010BD017899B1417809 +:101AD00089B141881B290ED381881B290BD3C1886A +:101AE000022908D33A490268403941F8522F406828 +:101AF0004860002070471220704710B504460FF070 +:101B000097FD204607F052F9002010BD10B507F0F0 +:101B100050F9002010BD2DE9F04115460F4606464C +:101B20000122114638460FF087FD04460121384650 +:101B300007F06DF9844200D2044601213046653C2D +:101B400000F069F806460121002000F064F83044F6 +:101B500001219630844206D900F19601201AB0FB8B +:101B6000F1F0401C81B229800020BDE8F08110B561 +:101B7000044600F08EF808B10C2010BD601C0AF07D +:101B800039FC207800F00100FCF759FE207800F0C5 +:101B900001000DF089F8002010BD10B507F003F921 +:101BA000002010BD10B50446072000F0BDFE08B1AE +:101BB0000C2010BD2078C00716D000226078114696 +:101BC00012F090FE30B1122010BD00006C00002019 +:101BD000E8070020A06809F0D4F86078D4F8041071 +:101BE00009F0D8F80020EFE7002009F0CAF800213A +:101BF0000846F5E710B505F02BFB0020E4E718B127 +:101C0000022801D0012070470020704708B1002051 +:101C100070470120704710B5012904D0022905D072 +:101C2000FFDF2046D0E7C000503001E080002C30BC +:101C300084B2F6E711F00C0F04D04FF4747101EB8D +:101C4000801006E0022902D0C000703001E0800060 +:101C50003C3080B2704710B510F0ABF9042805D0C5 +:101C600010F0A7F9052801D00020ADE70120ABE76F +:101C700010B5FFF7F0FF10B10DF0DAF828B907F052 +:101C800086FA20B1FCF7B6FD08B101209CE70020E0 +:101C90009AE710B5FFF7DFFF18B907F078FA0028C8 +:101CA00092D0012090E72DE9FE4300250F468046A3 +:101CB0000A260421404604F0E0F840460BF01BF8E9 +:101CC000062000F03FFE044615E06946062000F0BD +:101CD0001AFE0AE0BDF80400B84206D002980422B9 +:101CE00041460E3013F01EFC50B1684600F0E9FD8D +:101CF0000500EFD0641E002C06DD002DE5D005E0C8 +:101D000040460BF001F8F5E705B9FFDFD8F8000011 +:101D10000BF015F8761E01D00028CAD0BDE8FE836E +:101D200090F8D81090F8730020B919B1042901D0A7 +:101D30000120704700207047017800290AD04168CF +:101D400091F8E520002A05D0002281F8E5204068BE +:101D500007F073B870471B38E12806D2B1F5A47FAD +:101D600003D344F29020814201D912207047002011 +:101D70007047FB2802D8B1F5296F01D911207047AF +:101D80000020704770B514460546012200F05CF84B +:101D9000002806D121462846BDE87040002200F008 +:101DA00053B870BD042803D321B9B0F5804F01D9D1 +:101DB0000020704701207047042803D321B9B0F5F3 +:101DC000804F01D90020704701207047012802D0C0 +:101DD00018B100207047022070470120704710B5ED +:101DE00000224FF4C84408E030F81230A34200D972 +:101DF000234620F81230521CD2B28A42F4D3E3E6D2 +:101E000080B2C1060BD401071CD481064FEAC07111 +:101E100001D5B9B900E099B1800713D410E04106AB +:101E200010D481060ED4C1074FEA807104D0002976 +:101E300002DB400704D405E0010703D4400701D4C6 +:101E400001207047002070470AB1012200E0022201 +:101E5000024202D1C80802D109B100207047112006 +:101E60007047000030B5058825F4004421448CB249 +:101E70004FF4004194420AD2121B92B21B339A4291 +:101E800001D2A94307E005F40041214303E0A21A6F +:101E900092B2A9431143018030BD08440830504339 +:101EA0004A31084480B2704770B51D4616460B464D +:101EB000044629463046049AFFF7EFFF0646B34230 +:101EC00000D2FFDF2821204613F0F9FB4FF6FF7008 +:101ED000A082283EB0B265776080B0F5004F00D98F +:101EE000FFDF618805F13C00814200D2FFDF60889E +:101EF0000835401B343880B220801B2800D21B20BC +:101F000020800020A07770BD8161886170472DE935 +:101F1000F05F0D46C188044600F12809008921F4CC +:101F2000004620F4004800F062FB10B10020BDE83C +:101F3000F09F4FF0000A4FF0010BB0450CD9617FC4 +:101F4000A8EB0600401A0838854219DC09EB0600A8 +:101F50000021058041801AE06088617F801B471A5C +:101F6000083F0DD41B2F00DAFFDFBD4201DC2946FC +:101F700000E0B9B2681A0204120C04D0424502DD36 +:101F800084F817A0D2E709EB06000180428084F8AC +:101F900017B0CCE770B5044600F12802C088E37D95 +:101FA00020F400402BB110440288438813448B4234 +:101FB00001D2002070BD00258A4202D301804580F5 +:101FC00008E0891A0904090C418003D0A01D00F023 +:101FD0001EFB08E0637F00880833184481B26288E2 +:101FE000A01DFFF73FFFE575012070BD70B50346EA +:101FF00000F12804C588808820F400462644A842C1 +:1020000002D10020188270BD98893588A84206D375 +:10201000401B75882D1A2044ADB2C01E05E02C1A55 +:10202000A5B25C7F20443044401D0C88AC4200D9EE +:102030000D809C8924B1002414700988198270BD18 +:102040000124F9E770B5044600F12801808820F4E6 +:1020500000404518208A002825D0A189084480B274 +:10206000A08129886A881144814200D2FFDF288834 +:10207000698800260844A189884212D1A069807F1E +:102080002871698819B1201D00F0C1FA08E0637F4A +:1020900028880833184481B26288201DFFF7E2FEC9 +:1020A000A6812682012070BD2DE9F04141898788F3 +:1020B0000026044600F12805B94218D004F10A08A8 +:1020C00021F400402844418819B1404600F09FFAAD +:1020D00008E0637F00880833184481B26288404674 +:1020E000FFF7C0FE761C6189B6B2B942E8D130462E +:1020F000BDE8F0812DE9F04104460B4627892830E0 +:10210000A68827F40041B4F80A8001440D46B7427E +:1021100001D10020ECE70AB1481D106023B1627FB5 +:10212000691D184613F02AFA2E88698804F1080000 +:1021300021B18A1996B200F06AFA06E0637F6288DC +:102140000833991989B2FFF78DFE474501D12089DF +:1021500060813046CCE78188C088814201D101206E +:1021600070470020704701898088814201D1012099 +:1021700070470020704770B58588C38800F1280437 +:1021800025F4004223F4004114449D421AD083896F +:10219000058A5E1925886388EC18A64214D313B10A +:1021A0008B4211D30EE0437F08325C1922444088F1 +:1021B00092B2801A80B22333984201D211B103E067 +:1021C0008A4201D1002070BD012070BD2DE9F04789 +:1021D0008846C1880446008921F4004604F1280796 +:1021E00020F4004507EB060900F001FA002178BB56 +:1021F000B54204D9627FA81B801A002503E06088DD +:10220000627F801B801A083823D4E28962B1B9F852 +:102210000020B9F802303BB1E81A2177404518DBBD +:10222000E0893844801A09E0801A217740450ADBAA +:10223000607FE1890830304439440844C01EA4F866 +:102240001280BDE8F087454503DB01202077E7E7F2 +:10225000FFE761820020F4E72DE9F74F044600F123 +:102260002805C088884620F4004A608A05EB0A06E3 +:1022700008B1404502D20020BDE8FE8FE08978B168 +:102280003788B6F8029007EB0901884200D0FFDFDB +:10229000207F4FF0000B50EA090106D088B33BE0E5 +:1022A0000027A07FB9463071F2E7E18959B1607F1C +:1022B0002944083050440844B4F81F1020F8031D86 +:1022C00094F821108170E28907EB080002EB080105 +:1022D000E1813080A6F802B002985F4650B1637F7A +:1022E00030880833184481B26288A01DFFF7BAFD18 +:1022F000E78121E0607FE1890830504429440844A7 +:102300002DE0FFE7E089B4F81F102844C01B20F837 +:10231000031D94F82110817009EB0800E28981B255 +:1023200002EB0800E081378071800298A0B1A01D07 +:1023300000F06DF9A4F80EB0A07F401CA077A07D3E +:1023400008B1E088A08284F816B000BFA4F812B0EB +:1023500084F817B001208FE7E0892844C01B30F8CB +:10236000031DA4F81F10807884F82100EEE710B553 +:10237000818800F1280321F400442344848AC28820 +:10238000A14212D0914210D0818971B9826972B193 +:102390001046FFF7E8FE50B91089283220F40040BB +:1023A000104419790079884201D1002010BD1846E7 +:1023B00010BD00F12803407F08300844C01E1060A3 +:1023C000088808B9DB1E136008884988084480B271 +:1023D00070472DE9F04100F12806407F1C46083087 +:1023E0009046431808884D88069ADB1EA0B1C01C91 +:1023F00080B2904214D9801AA04200DB204687B2F6 +:1024000098183A46414613F08DF8002816D1E01B83 +:1024100084B2B844002005E0ED1CADB2F61EE8E73A +:10242000101A80B20119A94206D83044224641460A +:10243000BDE8F04113F076B84FF0FF3058E62DE9D3 +:10244000F04100F12804407F1E46083090464318B2 +:10245000002508884F88069ADB1E90B1C01C80B208 +:10246000904212D9801AB04200DB304685B29918EA +:102470002A46404613F082F8701B86B2A84400201A +:1024800005E0FF1CBFB2E41EEAE7101A80B2811912 +:10249000B94206D821183246404613F06FF8A81901 +:1024A00085B2284624E62DE9F04100F12804407F5A +:1024B0001E46083090464318002508884F88069A23 +:1024C000DB1E90B1C01C80B2904212D9801AB0427B +:1024D00000DB304685B298182A46414613F04EF884 +:1024E000701B86B2A844002005E0FF1CBFB2E41EAA +:1024F000EAE7101A80B28119B94206D82044324660 +:10250000414613F03BF8A81985B22846F0E5401D76 +:10251000704710B5044600F12801C288808820F475 +:1025200000431944904206D0A28922B9228A12B9E6 +:10253000A28A904201D1002010BD0888498831B19B +:10254000201D00F064F800202082012010BD637F70 +:1025500062880833184481B2201DFFF783FCF2E73C +:102560000021C18101774182C1758175704703885F +:102570001380C28942B1C28822F4004300F12802CC +:102580001A440A60C08970470020704710B504469D +:10259000808AA0F57F41FF3900D0FFDFE088A0826C +:1025A000E08900B10120A07510BD4FF6FF71818256 +:1025B00000218175704710B50446808AA0F57F41DF +:1025C000FF3900D1FFDFA07D28B9A088A18A884209 +:1025D00001D1002010BD012010BD8188828A914266 +:1025E00001D1807D08B1002070470120704720F4A0 +:1025F000004221F400439A4207D100F4004001F464 +:102600000041884201D0012070470020704730B55A +:10261000044600880D4620F40040A84200D2FFDFA7 +:1026200021884FF4004088432843208030BD70B596 +:102630000C00054609D0082C00D2FFDF1DB1A1B265 +:10264000286800F044F8201D70BD0DB100202860FE +:10265000002070BD0021026803E0938812681944CD +:1026600089B2002AF9D100F032B870B500260D46C3 +:102670000446082900D2FFDF206808B91EE004469E +:1026800020688188A94202D001680029F7D1818899 +:102690000646A94201D100680DE005F1080293B297 +:1026A0000022994209D32844491B02608180216895 +:1026B000096821600160206000E00026304670BD9E +:1026C00000230B608A8002680A6001607047002363 +:1026D0004360021D018102607047F0B50F4601881A +:1026E000408815460C181E46AC4200D3641B30448B +:1026F000A84200D9FFDFA019A84200D9FFDF38198E +:10270000F0BD2DE9F041884606460188408815460F +:102710000C181F46AC4200D3641B3844A84200D9B1 +:10272000FFDFE019A84200D9FFDF708838447080CD +:1027300008EB0400BDE8F0812DE9F0410546008872 +:102740001E461746841B8846BC4200D33C442C805E +:1027500068883044B84200D9FFDFA019B84200D9D8 +:10276000FFDF68883044688008EB0400E2E72DE969 +:10277000F04106881D460446701980B21746884607 +:102780002080B84201D3C01B20806088A84200D2BC +:10279000FFDF7019B84200D9FFDF6088401B6080FE +:1027A00008EB0600C6E730B50D460188CC18944208 +:1027B00000D3A41A4088984200D8FFDF281930BD02 +:1027C0002DE9F041C84D04469046A8780E46A04237 +:1027D00000D8FFDF05EB8607B86A50F8240000B187 +:1027E000FFDFB868002816D0304600F044F90146F3 +:1027F000B868FFF73AFF05000CD0B86A082E40F819 +:10280000245000D3FFDFB9484246294650F826300D +:10281000204698472846BDE8F0812DE9F8431E463A +:102820008C1991460F460546FF2C00D9FFDFB145B4 +:1028300000D9FFDFE4B200954DB300208046E81CCC +:1028400020F00300A84200D0FFDF4946DFF898924D +:10285000684689F8001089F8017089F8024089F803 +:10286000034089F8044089F8054089F8066089F832 +:102870000770414600F008F9002142460F464B46DA +:102880000098C01C20F00300009012B10EE001205F +:10289000D4E703EB8106B062002005E0D6F828C03B +:1028A0004CF82070401CC0B2A042F7D30098491CDD +:1028B00000EB8400C9B200900829E1D3401BBDE8B9 +:1028C000F88310B50446EEF724FD08B1102010BDC2 +:1028D0002078854A618802EB800092780EE0836A56 +:1028E00053F8213043B14A1C6280A180806A50F8BD +:1028F0002100A060002010BD491C89B28A42EED898 +:102900006180052010BD70B505460C460846EEF7FF +:1029100000FD08B1102070BD082D01D3072070BD47 +:1029200025700020608070BD0EB56946FFF7EBFF93 +:1029300000B1FFDF6846FFF7C4FF08B100200EBDFD +:1029400001200EBD10B50446082800D3FFDF6648FD +:10295000005D10BD3EB5054600246946FFF7D3FF74 +:1029600018B1FFDF01E0641CE4B26846FFF7A9FF7D +:102970000028F8D02846FFF7E5FF001BC0B23EBD97 +:1029800059498978814201D9C0B27047FF20704708 +:102990002DE9F041544B062903D007291CD19D791C +:1029A00000E0002500244FF6FF7603EB810713F8C3 +:1029B00001C00AE06319D7F828E09BB25EF823E073 +:1029C000BEF1000F04D0641CA4B2A445F2D8334673 +:1029D00003801846B34201D100201CE7BDE8F04156 +:1029E000EEE6A0F57F43FF3B01D0082901D300208C +:1029F0007047E5E6A0F57F42FF3A0BD0082909D2DF +:102A0000394A9378834205D902EB8101896A51F8EA +:102A100020007047002070472DE9F04104460D4624 +:102A2000A4F57F4143F20200FF3902D0082D01D303 +:102A30000720F0E62C494FF000088A78A242F8D926 +:102A400001EB8506B26A52F82470002FF1D02748B6 +:102A50003946203050F8252020469047B16A284654 +:102A600041F8248000F007F802463946B068FFF7C5 +:102A700027FE0020CFE61D49403131F810004FF607 +:102A8000FC71C01C084070472DE9F843164E88467B +:102A9000054600242868C01C20F00300286020465A +:102AA000FFF7E9FF315D4843B8F1000F01D0002284 +:102AB00000E02A680146009232B100274FEA0D007B +:102AC000FFF7B5FD1FB106E001270020F8E706EB90 +:102AD0008401009A8A602968641C0844E4B2286072 +:102AE000082CD7D3EBE6000008080020445B020066 +:102AF00070B50E461D46114600F0D4F8044629462E +:102B0000304600F0D8F82044001D70BD2DE9F0419A +:102B100090460D4604004FF0000610D00027E01C40 +:102B200020F00300A04200D0FFDFDDB141460020CD +:102B3000FFF77DFD0C3000EB850617B112E0012791 +:102B4000EDE7614F04F10C00A9003C602572606064 +:102B500000EB85002060606812F0B1FD41463868E6 +:102B6000FFF765FD3046BDE8F0812DE9FF4F564C7B +:102B7000804681B020689A46934600B9FFDF2068FE +:102B8000027A424503D9416851F8280020B143F246 +:102B9000020005B0BDE8F08F5146029800F082F8BF +:102BA00086B258460E9900F086F885B27019001D5D +:102BB00087B22068A14639460068FFF756FD040039 +:102BC0001FD0678025802946201D0E9D07465A4646 +:102BD00001230095FFF768F9208831463844012326 +:102BE000029ACDF800A0FFF75FF92088C119384696 +:102BF000FFF78AF9D9F800004168002041F8284021 +:102C0000C7E70420C5E770B52F4C0546206800B91A +:102C1000FFDF2068017AA9420ED9426852F82510D8 +:102C200051B1002342F825304A880068FFF748FD7B +:102C3000216800200A7A08E043F2020070BD4B6868 +:102C400053F8203033B9401CC0B28242F7D808682C +:102C5000FFF700FD002070BD70B51B4E0546002437 +:102C6000306800B9FFDF3068017AA94204D94068B2 +:102C700050F8250000B1041D204670BD70B5124EFD +:102C800005460024306800B9FFDF3068017AA942A8 +:102C900006D9406850F8251011B131F8040B4418DA +:102CA000204670BD10B50A460121FFF7F6F8C01C9A +:102CB00020F0030010BD10B50A460121FFF7EDF822 +:102CC000C01C20F0030010BD8000002070B5044639 +:102CD000C2F11005281912F051FC15F0FF0108D0BF +:102CE000491EC9B2802060542046BDE8704012F0F1 +:102CF000C4BC70BD30B505E05B1EDBB2CC5CD55CFE +:102D00006C40C454002BF7D130BD10B5002409E04D +:102D10000B78521E44EA430300F8013B11F8013BD3 +:102D2000D2B2DC09002AF3D110BD2DE9F04389B0FD +:102D30001E46DDE9107990460D00044622D0024679 +:102D40000846F949FDF7BAFC102221463846FFF73C +:102D5000DCFFE07B000606D5F34A3946102310322B +:102D60000846FFF7C7FF102239464846FFF7CDFF58 +:102D7000F87B000606D5EC4A494610231032084677 +:102D8000FFF7B8FF1021204612F077FC0DE0103E4F +:102D9000B6B208EB0601102322466846FFF7AAFFE9 +:102DA000224628466946FDF789FC102EEFD818D038 +:102DB000F2B241466846FFF789FF10234A4669464A +:102DC00004A8FFF797FF1023224604A96846FFF7DF +:102DD00091FF224628466946FDF770FC09B0BDE820 +:102DE000F08310233A464146EAE770B59CB01E4690 +:102DF0000546134620980C468DF8080020221946F7 +:102E00000DF1090012F0BAFB202221460DF1290034 +:102E100012F0B4FB17A913A8CDE90001412302AABF +:102E200031462846FFF781FF1CB070BD2DE9FF4FEA +:102E30009FB014AEDDE92D5410AFBB49CDE900764B +:102E4000202320311AA8FFF770FF4FF000088DF8FB +:102E500008804FF001098DF8099054F8010FCDF862 +:102E60000A00A088ADF80E0014F8010C1022C0F37F +:102E700040008DF8100055F8010FCDF81100A8881A +:102E8000ADF8150015F8010C2C99C0F340008DF831 +:102E9000170006A8824612F071FB0AA8834610228A +:102EA000229912F06BFBA0483523083802AA40682B +:102EB0008DF83C80CDE900760E901AA91F98FFF797 +:102EC00034FF8DF808808DF809902068CDF80A004D +:102ED000A088ADF80E0014F8010C1022C0F34000D9 +:102EE0008DF810002868CDF81100A888ADF81500FD +:102EF00015F8010C2C99C0F340008DF817005046CE +:102F000012F03CFB58461022229912F037FB8648FB +:102F10003523083802AA40688DF83C90CDE9007648 +:102F20000E901AA92098FFF700FF23B0BDE8F08F9C +:102F3000F0B59BB00C460546DDE922101E4617464B +:102F4000DDE92032D0F801C0CDF808C0B0F805C0E6 +:102F5000ADF80CC00078C0F340008DF80E00D1F839 +:102F60000100CDF80F00B1F80500ADF813000878A6 +:102F70001946C0F340008DF815001088ADF8160012 +:102F800090788DF818000DF11900102212F0F6FA61 +:102F90000DF129001022314612F0F0FA0DF139003E +:102FA0001022394612F0EAFA17A913A8CDE9000158 +:102FB000412302AA21462846FFF7B7FE1BB0F0BD09 +:102FC000F0B5A3B017460D4604461E46102202A8CF +:102FD000289912F0D3FA06A82022394612F0CEFA28 +:102FE0000EA82022294612F0C9FA1EA91AA8CDE976 +:102FF0000001502302AA314616A8FFF796FE169844 +:10300000206023B0F0BDF0B589B00446DDE90E07BD +:103010000D463978109EC1F340018DF800103178CB +:103020009446C1F340018DF801101968CDF80210E3 +:103030009988ADF8061099798DF808100168CDF8D7 +:1030400009108188ADF80D1080798DF80F001023DC +:103050006A46614604A8FFF74DFE2246284604A9A9 +:10306000FDF72CFBD6F801000090B6F80500ADF88E +:103070000400D7F80100CDF80600B7F80500ADF858 +:103080000A000020039010236A46214604A8FFF797 +:1030900031FE2246284604A9FDF710FB09B0F0BD19 +:1030A0001FB51C6800945B68019313680293526813 +:1030B0000392024608466946FDF700FB1FBD10B5A6 +:1030C00088B004461068049050680590002006906F +:1030D000079008466A4604A9FDF7F0FABDF800001B +:1030E000208008B010BD1FB51288ADF800201A88E6 +:1030F000ADF8022000220192029203920246084695 +:103100006946FDF7DBFA1FBD7FB5074B1446054640 +:10311000083B9A1C6846FFF7E6FF224669462846A8 +:10312000FFF7CDFF7FBD00009C5B020070B5044639 +:1031300000780E46012813D0052802D0092813D1A3 +:103140000EE0A06861690578042003F075F9052D8B +:103150000AD0782300220420616903F0C3F803E059 +:103160000420616903F068F931462046BDE87040EB +:1031700001F084B810B500F12D03C2799C78411D8F +:10318000144064F30102C271D2070DD04A795C7910 +:1031900022404A710A791B791A400A718278C978EB +:1031A0008A4200D9817010BD00224A71F5E741784A +:1031B000012900D00C21017070472DE9F04F93B028 +:1031C0004FF0000B0C690D468DF820B009780126F0 +:1031D0000C2017464FF00D084FF0110A4FF0080968 +:1031E0001B2975D2DFE811F01B00C20205031D0385 +:1031F0005C036F03A103B603F70318046004920491 +:103200009F04EB042905330551055C05ED053006E7 +:10321000330662067E06F8061C07E506EA0614B1C8 +:1032200020781D282AD0D5F808805FEA08004FD002 +:1032300001208DF82000686A02220D908DF824206C +:103240000A208DF82500A8690A90A8880028EED0E9 +:1032500098F8001091B10F2910D27DD2DFE801F06B +:103260007C1349DEFCFBFAF9F8F738089CF6F50008 +:1032700002282DD124B120780C2801D00026EEE3BD +:103280008DF82020CAE10420696A03F0D5F8A888E7 +:103290000728EED1204600F0ECFF022809D0204696 +:1032A00000F0E7FF032807D9204600F0E2FF0728D7 +:1032B00002D20120207004E0002CB8D02078012830 +:1032C000D7D198F80400C11F0A2902D30A2061E06F +:1032D000C3E1A070D8F80010E162B8F804102186AC +:1032E00098F8060084F8320001202870032020702E +:1032F00044E00728BDD1002C99D020780D28B8D102 +:1033000098F8031094F82F20C1F3C000C2F3C00254 +:10331000104201D0062000E00720890707D198F865 +:1033200005100142D2D198F806100142CED194F88E +:10333000312098F8051020EA02021142C6D194F813 +:10334000322098F8061090430142BFD198F804004B +:10335000C11F0A29BAD200E006E2617D81427CD811 +:10336000D8F800106160B8F80410218198F80600C0 +:10337000A072012028700E20207003208DF82000FC +:10338000686A0D9004F12D000990601D0A900F30BD +:103390000B9021E12875FDE3412891D1204600F0F2 +:1033A00068FF042802D1E078C00704D1204600F06D +:1033B00060FF0F2884D1A88CD5F80C8080B24FF024 +:1033C000400BE669FFF748FC324641465B464E46F5 +:1033D000CDF80090FFF733F80B208DF82000686AD5 +:1033E0000D90E0690990002108A8FFF79FFE207862 +:1033F000042806D0A07D58B1012809D003280AD09E +:1034000048E305202070032028708DF82060CCE16F +:1034100084F800A032E712202070E8E11128BCD126 +:10342000204600F026FF042802D1E078C00719D01A +:10343000204600F01EFF062805D1E078C00711D114 +:10344000A07D02280ED0204608E0CBE084E070E1A9 +:103450004FE122E102E1E8E019E0AEE100F009FF0E +:1034600011289AD1102208F1010104F13C0012F058 +:1034700085F8607801286ED012202070E078C007AF +:1034800060D0A07D0028C8D00128C6D05AE01128FD +:1034900090D1204600F0EDFE082804D0204600F030 +:1034A000E8FE132886D104F16C00102208F1010116 +:1034B000064612F063F8207808280DD014202070FA +:1034C000E178C8070DD0A07D02280AD06278022AD0 +:1034D00004D00328A1D035E00920F0E708B1012885 +:1034E00037D1C80713D0A07D02281DD0002000903E +:1034F000D4E9062133460EA8FFF777FC10220EA967 +:1035000004F13C0012F00EF8C8B1042042E7D4E9FF +:103510000912201D8DE8070004F12C0332460EA885 +:10352000616BFFF770FDE9E7606BC1F34401491E71 +:103530000068C84000F0010040F08000D7E7207824 +:10354000092806D185F800908DF8209032E3287084 +:10355000EBE30920FBE79CE1112899D1204600F01C +:1035600088FE0A2802D1E078C00704D1204600F086 +:1035700080FE15288CD104F13C00102208F10101D5 +:10358000064611F0FBFF20780A2816D0162020707E +:10359000D4E90932606B611D8DE80F0004F15C0312 +:1035A00004F16C0247310EA8FFF7C2FC10220EA9ED +:1035B000304611F0B7FF18B1F6E20B20207071E22F +:1035C0002046FFF7D7FDA078216A0A18C0F1100144 +:1035D000104612F052F823E3394608A8FFF7A6FD7B +:1035E00006463BE20228B8D1204600F042FE0428FD +:1035F00004D3204600F03DFE082809D3204600F001 +:1036000038FE0E2829D3204600F033FE122824D29B +:10361000A07D0228A1D10E208DF82000686A0D90AF +:1036200098F801008DF82400F0E3022895D1204697 +:1036300000F01FFE002810D0204600F01AFE0128DE +:10364000F9D0204600F015FE0C28F4D004208DF8A7 +:10365000240098F801008DF825005EE21128FCD1C5 +:10366000002CFAD020781728F7D16178606A0229F7 +:1036700011D0002101EB4101182606EBC1011022F7 +:10368000405808F1010111F079FF0420696A00F047 +:10369000E3FD2670F2E50121ECE70B28DDD1002CDB +:1036A000DBD020781828D8D16078616A02281CD035 +:1036B0005FF0000000EB4002102000EBC200095850 +:1036C000B8F8010008806078616A02280FD00020F5 +:1036D00000EB4002142000EBC2000958404650F8AD +:1036E000032F0A604068486039E00120E2E70120CA +:1036F000EEE71128B1D1002CAFD020781928ACD139 +:103700006178606A022912D05FF0000101EB41018B +:103710001C2202EBC1011022405808F1010111F0F6 +:103720002DFF0420696A00F097FD1A20B6E0012100 +:10373000ECE7082891D1002C8FD020781A288CD162 +:10374000606A98F80120017862F347010170616AAC +:10375000D8F8022041F8012FB8F80600888004202C +:10376000696A00F079FD8EE2072013E638780128B7 +:1037700094D1182204F11400796811F044FFE07923 +:10378000C10894F82F0001EAD001E07861F300004D +:10379000E070217D002974D12178032909D0C00768 +:1037A00025D0032028708DF82090686A0D90412064 +:1037B00004E3607DA178884201D90620EAE502266B +:1037C0002671E179204621F0E001E171617A21F072 +:1037D000F0016172A17A21F0F001A172FFF7CAFC39 +:1037E0002E708DF82090686A0D900720E6E2042084 +:1037F000ADE6387805289DD18DF82000686A0D90D7 +:10380000B8680A900720ADF824000A988DF830B007 +:103810006168016021898180A17A81710420207012 +:10382000F4E23978052985D18DF82010696A0D9167 +:10383000391D09AE0EC986E80E004121ADF82410ED +:103840008DF830B01070A88CD7F80C8080B240266C +:10385000A769FFF713FA41463A463346C846CDF802 +:103860000090FEF720FE002108A8FFF75FFCE0783B +:1038700020F03E00801CE0702078052802D00F2048 +:103880000CE049E1A07D20B1012802D0032802D03C +:1038900002E10720C0E584F80080EFE42070EDE449 +:1038A000102104F15C0002F0E8FA606BB0BBA07D6F +:1038B00018B1012801D00520FDE006202870F74846 +:1038C0006063A063BEE23878022894D1387908B1E9 +:1038D0002875B3E3A07D022802D0032805D022E09A +:1038E000B8680028F5D060631CE06078012806D035 +:1038F000A07994F82E10012805D0E84806E0A179B7 +:1039000094F82E00F7E7B8680028E2D06063E0780A +:10391000C00701D0012902D0E04803E003E0F868C5 +:103920000028D6D0A063062011E68DF82090696AA1 +:103930000D91E1784846C90709D06178022903D181 +:10394000A17D29B1012903D0A17D032900D0072041 +:10395000287031E138780528BBD1207807281ED09F +:1039600084F800A005208DF82000686A0D90B868E2 +:103970000A90ADF824A08DF830B003210170E178F1 +:10398000CA070FD0A27D022A1AD000210091D4E9E3 +:10399000061204F15C03401CFFF727FA67E384F882 +:1039A0000090DFE7D4E90923211D8DE80E0004F122 +:1039B0002C0304F15C02401C616BFFF724FB56E30F +:1039C000626BC1F34401491E1268CA4002F0010152 +:1039D00041F08001DAE738780528BDD18DF8200064 +:1039E000686A0D90B8680A90ADF824A08DF830B0E0 +:1039F000042100F8011B102204F15C0111F0BEFD4E +:103A0000002108A8FFF792FB2078092801D0132095 +:103A100044E70A2020709CE5E078C10742D0A17DF0 +:103A2000012902D0022927D038E0617808A80129AD +:103A300016D004F16C010091D4E9061204F15C0384 +:103A4000001DFFF7BDFA0A20287003268DF820809C +:103A5000686A0D90002108A8FFF768FBDDE2C3E269 +:103A600004F15C010091D4E9062104F16C03001D0E +:103A7000FFF7A6FA0026E9E7C0F3440114290DD2A6 +:103A80004FF0006101EBB0104FEAB060E070607879 +:103A9000012801D01020BFE40620FFE6607801284D +:103AA0003FF4B8AC0A2052E5E178C90708D0A17DFF +:103AB000012903D10B20287004202FE028702DE06D +:103AC0000E2028706078616B012817D004F15C0328 +:103AD00004F16C020EA8FFF7E3FA2046FFF74AFB59 +:103AE000A0780EAEC0F11001304411F0C6FD0620E2 +:103AF0008DF82000686A09960D909AE004F16C0335 +:103B000004F15C020EA8FFF7CBFAE9E73978022945 +:103B100003D139790029D1D029758FE28DF82000A1 +:103B2000686A0D9058E538780728F6D1D4E909215C +:103B30006078012809D000BF04F16C00CDE90002D3 +:103B4000029105D104F16C0304E004F15C00F5E797 +:103B500004F15C0304F14C007A680646216AFFF721 +:103B600065F96078012821D1A078216A0A18C0F18E +:103B70001001104611F081FDD4E90923606B04F1B6 +:103B80002D018DE80F0004F15C0304F16C02314655 +:103B90000EA800E054E2FFF7CBF910220EA904F1C1 +:103BA0003C0011F0BFFC08B10B20AFE485F80080A9 +:103BB0008DF82090686A0D908DF824A00CE5387877 +:103BC0000528AAD18DF82000686A0D90B8680A907F +:103BD000ADF824A08DF830B080F80080617801291C +:103BE0001AD0D4E9093204F12D01A66B0392009694 +:103BF000CDE9011304F16C0304F15C0204F14C0102 +:103C0000401CFFF795F9002108A8FFF78FFA6078AC +:103C1000012805D0152041E6D4E90923611DE4E718 +:103C20000E20287006208DF82000686ACDF824B098 +:103C30000D90A0788DF82800CEE438780328C0D104 +:103C4000E079C00770D00F202870072066E7387829 +:103C500004286BD11422391D04F1140011F0D3FC97 +:103C6000616A208CA1F80900616AA078C871E179C5 +:103C7000626A01F003011172616A627A0A73616A11 +:103C8000A07A81F82400162061E485F800A08DF860 +:103C90002090696A50460D9190E000009C5B020004 +:103CA0003878052842D1B868A8616178606A02292D +:103CB00001D0012100E0002101EB4101142606EBB7 +:103CC000C1014058082102F0D8F86178606A0229E1 +:103CD00001D0012100E0002101EB410106EBC1010F +:103CE000425802A8E169FFF70FFA6078626A022879 +:103CF00001D0012000E0002000EB4001102000EB8B +:103D0000C1000223105802A90932FEF7F3FF626ACC +:103D1000FD4B0EA80932A169FFF7E5F96178606AE9 +:103D2000022904D0012103E042E18BE0BDE0002143 +:103D300001EB4101182606EBC101A27840580EA9FB +:103D400011F01CFC6178606A022901D0012100E0B9 +:103D5000002101EB410106EBC1014058A178084464 +:103D6000C1F1100111F089FC05208DF82000686A6E +:103D70000D90A8690A90ADF824A08DF830B0062106 +:103D800001706278616A022A01D0012200E00022FB +:103D900002EB420206EBC202401C8958102211F0CD +:103DA000EDFB002108A8FFF7C1F91220C5F818B0F3 +:103DB00028708DF82090686A0D900B208DF82400F3 +:103DC0000AE43878052870D18DF82000686A0D90D3 +:103DD000B8680A900B20ADF824000A9807210170FA +:103DE0006178626A022901D0012100E0002101EB23 +:103DF0004103102101EBC30151580988A0F80110BB +:103E00006178626A022902D0012101E02FE10021DC +:103E100001EB4103142101EBC30151580A6840F83A +:103E2000032F4968416059E01920287001208DF85E +:103E3000300077E6162028708DF830B0002108A8F1 +:103E4000FFF774F9032617E114202870B0E63878DC +:103E500005282AD18DF82000686A0D90B8680A906C +:103E6000ADF824A08DF830B080F800906278616AD7 +:103E70004E46022A01D0012200E0002202EB42025B +:103E80001C2303EBC202401C8958102211F076FB60 +:103E9000002108A8FFF74AF9152028708DF8206046 +:103EA000686A0D908DF824603CE680E0387805283B +:103EB0007DD18DF82000686A0D90B8680A90ADF841 +:103EC000249009210170616909784908417061698C +:103ED00051F8012FC0F802208988C18020781C2861 +:103EE000A8D1A1E7E078C00702D04FF0060C01E0AE +:103EF0004FF0070C607802280AD000BF4FF0000096 +:103F000000EB040101F1090105D04FF0010004E0CC +:103F10004FF00100F4E74FF000000B78204413EA63 +:103F20000C030B7010F8092F02EA0C02027004D186 +:103F30004FF01B0C84F800C0D2B394F801C0BCF160 +:103F4000010F00D09BB990F800C0E0465FEACC7C3E +:103F500004D028F001060670102606E05FEA887C8F +:103F600005D528F00206067013262E70032694F855 +:103F700001C0BCF1020F00D092B991F800C05FEA15 +:103F8000CC7804D02CF001060E70172106E05FEA11 +:103F90008C7805D52CF002060E70192121700026B0 +:103FA0000078D0BBCAB3C3BB1C20207035E012E040 +:103FB00002E03878062841D11A2019E42078012837 +:103FC0003CD00C283AD02046FFF7F1F809208DF8B4 +:103FD0002000686A0D9031E03878052805D0062069 +:103FE000387003261820287046E005218DF820102F +:103FF000686A0D90B8680A900220ADF8240001208C +:104000008DF830000A980170297D4170394608A862 +:10401000FFF78CF8064618202870012E0ED02BE0F2 +:1040200001208DF82000686A0D9003208DF824008F +:10403000287D8DF8250085F814B012E0287D80B128 +:104040001D202070172028708DF82090686A0D9030 +:1040500002208DF82400394608A8FFF767F80646C5 +:104060000AE00CB1FE2020709DF8200020B1002154 +:1040700008A8FFF75BF810E413B03046BDE8F08FF6 +:104080002DE9F04387B00C464E6900218DF80410ED +:1040900001202578034602274FF007094FF0050C51 +:1040A00085B1012D53D0022D39D1FE2030708DF80D +:1040B0000030606A059003208DF80400207E8DF8A2 +:1040C000050063E02179012925D002292DD003299B +:1040D00028D0042923D1B17D022920D131780D1FA8 +:1040E000042D04D30A3D032D01D31D2917D12189A5 +:1040F000022914D38DF80470237020899DF80410D0 +:1041000088421BD2082001E0945B02008DF8000079 +:10411000606A059057E070780128EBD0052007B061 +:10412000BDE8F0831D203070E4E771780229F5D1F5 +:1041300031780C29F3D18DF80490DDE7083402F8CA +:1041400004CB94E80B0082E80B000320E7E7157826 +:10415000052DE4D18DF800C0656A05959568029536 +:104160008DF8101094F80480B8F1010F13D0B8F155 +:10417000020F2DD0B8F1030F1CD0B8F1040FCED12F +:10418000ADF804700E202870207E6870002168460B +:10419000FEF7CCFF0CE0ADF804700B202870207EF9 +:1041A000002100F01F0068706846FEF7BFFF3770FF +:1041B0000020B4E7ADF804708DF8103085F800C029 +:1041C000207E6870277011466846FEF7AFFFA6E7AD +:1041D000ADF804902B70207F6870607F00F00100C4 +:1041E000A870A07F00F01F00E870E27F2A71C0076E +:1041F0001CD094F8200000F00700687194F82100AA +:1042000000F00700A87100216846FEF78FFF2868BC +:10421000F062A8883086A87986F83200A0694078D4 +:1042200070752879B0700D203070C1E7A97169717F +:10423000E9E700B587B004280CD101208DF8000013 +:104240008DF80400002005918DF8050001466846B0 +:10425000FEF76CFF07B000BD70B50C46054602F0D6 +:10426000EBF821462846BDE870407823002202F092 +:1042700039B808B1007870470C20704770B50C0051 +:1042800005784FF000010CD021702146F0F7D9FFDE +:1042900069482178405D884201D1032070BD022029 +:1042A00070BDF0F7CEFF002070BD0279012A05D065 +:1042B00000220A704B78012B02D003E004207047E3 +:1042C0000A758A6102799300521C0271C150032061 +:1042D0007047F0B587B00F4605460124287905EBF5 +:1042E000800050F8046C7078411E02290AD25249AD +:1042F0003A46083901EB8000314650F8043C284624 +:10430000984704460CB1012C11D12879401E10F0B9 +:10431000FF00287101D00324E0E70A208DF8000097 +:10432000706A0590002101966846FFF7A7FF032CED +:10433000D4D007B02046F0BD70B515460A460446F5 +:1043400029461046FFF7C5FF064674B12078FE28BF +:104350000BD1207C30B100202870294604F10C00DC +:10436000FFF7B7FF2046FEF722FF304670BD7047CB +:1043700070B50E4604467C2111F0A1F90225012EEC +:1043800003D0022E04D0052070BD0120607000E033 +:1043900065702046FEF70BFFA575002070BD28B1A3 +:1043A000027C1AB10A4600F10C01C5E701207047F2 +:1043B00010B5044686B0042002F03EF82078FE28AE +:1043C00006D000208DF8000069462046FFF7E7FF81 +:1043D00006B010BD7CB50E4600218DF80410417862 +:1043E000012903D0022903D0002405E0046900E07C +:1043F00044690CB1217C89B16D4601462846FFF71E +:1044000054FF032809D1324629462046FFF794FF7E +:104410009DF80410002900D004207CBD04F10C0597 +:10442000EBE730B40C460146034A204630BC034B50 +:104430000C3AFEF758BE0000D85B0200945B020005 +:1044400070B50D46040011D085B12101284611F048 +:1044500014F910225449284611F090F852480121CD +:104460000838018044804560002070BD012070BD87 +:1044700070B54D4E00240546083E10E07068AA7BDA +:1044800000EB0410817B914208D1C17BEA7B914211 +:1044900004D10C22294611F045F830B1641C308853 +:1044A0008442EBDB4FF0FF3070BD204670BD70B52D +:1044B0000D46060006D02DB1FFF7DAFF002803DB1A +:1044C000401C14E0102070BD374C083C20886288E6 +:1044D000411C914201D9042070BD6168102201EB9A +:1044E0000010314611F04AF82088401C20802870C6 +:1044F000002070BD2C480838008870472A490839C8 +:104500000888012802D0401E08800020704770B53E +:1045100014460D0018D0BCB10021A170022802D0B1 +:10452000102811D105E0288870B10121A1701080F8 +:1045300008E02846FFF79CFF002805DB401CA07020 +:10454000A8892080002070BD012070BD70B505468F +:1045500014460E000BD000203070A878012808D037 +:1045600005D91149A1F108010A8890420AD9012010 +:1045700070BD24B1287820702888000A507002206D +:1045800008700FE064B14968102201EB0011204669 +:10459000103910F0F3FF287820732888000A607320 +:1045A00010203070002070BD8C0000202DE9F041FB +:1045B00090460C4607460025FE48072F00EB88165C +:1045C00007D2DFE807F00707070704040400012506 +:1045D00000E0FFDF06F81470002D13D0F54880309E +:1045E00000EB880191F82700202803D006EB40005B +:1045F000447001E081F8264006EB44022020507010 +:1046000081F82740BDE8F081F0B51F4614460E46FC +:104610000546202A00D1FFDFE649E648803100EB5D +:10462000871C0CEB440001EB8702202E07D00CEB1B +:10463000460140784B784870184620210AE092F8ED +:104640002530407882F82500F6E701460CEB410062 +:1046500005704078A142F8D192F82740202C03D071 +:104660000CEB4404637001E082F826300CEB41044B +:104670002023637082F82710F0BD30B50D46CE4B75 +:1046800044190022181A72EB020100D2FFDFCB4856 +:10469000854200DDFFDFC9484042854200DAFFDF86 +:1046A000C548401C844207DA002C01DB204630BD9F +:1046B000C148401C201830BDBF48C043FAE710B5C0 +:1046C00004460168407ABE4A52F82020114450B195 +:1046D0000220084420F07F40EEF7AFFA94F908106A +:1046E000BDE81040C9E70420F3E72DE9F047B14EDB +:1046F000803696F82D50DFF8BC9206EB850090F8D6 +:10470000264034E009EB85174FF0070817F814002E +:10471000012806D004282ED005282ED0062800D047 +:10472000FFDF01F00AF9014607EB4400427806EB8F +:10473000850080F8262090F82720A24202D120226E +:1047400080F82720084601F003F92A462146012077 +:10475000FFF72CFF9B48414600EB041002682046FF +:10476000904796F82D5006EB850090F82640202CB7 +:10477000C8D1BDE8F087022000E003208046D0E7E2 +:1047800010B58C4C2021803484F8251084F8261034 +:1047900084F82710002084F8280084F82D0084F87D +:1047A0002E10411EA16044F8100B20746074207319 +:1047B0006073A0738449E077207508704870002109 +:1047C0007C4A103C02F81100491CC9B22029F9D3D7 +:1047D0000120EEF722F90020EEF71FF9012084F8FE +:1047E0002200EEF765FB7948EEF777FB764CA41EC6 +:1047F00020707748EEF771FB6070BDE81040EEF76F +:1048000099B810B5EEF7BBF86F4CA41E2078EEF700 +:104810007DFB6078EEF77AFBBDE8104001F0C5B88B +:10482000202070472DE9F34F624C0025803404EBC3 +:10483000810A89B09AF82500202821D0691E0291AA +:104840006049009501EB0017391D03AB07C983E8E8 +:104850000700A18BADF81C10A07F8DF81E009DF8FD +:104860001500A046C8B10226554951F820400399C9 +:10487000A219114421F07F41019184B102210FE07E +:104880000120EEF7CAF80020EEF7C7F8EEF795F82A +:1048900001F08BF884F82F50A7E00426E4E700210C +:1048A0008DF81810022801D0012820D10398011991 +:1048B0000998081A801C9DF81C1020F07F4001B157 +:1048C0000221353181420BD203208DF81500039867 +:1048D000C4F13201401A20F07F40322403900CE0F2 +:1048E00098F8240018B901F0F8F900284DD0322CBE +:1048F00003D214B101F04DF801E001F056F8324A4C +:10490000107820B393465278039B121B00219DF828 +:104910001840994601281BD0032819D05FF00000E9 +:104920008DF81E00002A04DD981A039001208DF8EE +:1049300018009DF81C0000B102210398254A20F0C0 +:104940007F40039003AB099801F03BF810B110E0F1 +:104950000120E5E79DF81D0018B99BF80000032829 +:1049600012D08DF81C50CDF80C908DF818408DF8B1 +:104970001E509DF8180058B1039801238119002298 +:104980001846EEF79DF806E000200BB0BDE8F08F6A +:104990000120EEF742F897F90C200123002001993D +:1049A000EEF78EF8F87BC00701D0EEF772F901211F +:1049B00012E00000500A0020FF7F841E0020A107A3 +:1049C000E85B0200500800209E0000209361010077 +:1049D000EB460100FFFF3F0088F82F108AF82850AF +:1049E00020226946F74810F00EFE0120CDE72DE9A0 +:1049F000F05FDFF8D083064608EB860090F825507C +:104A0000202D1FD0A8F180002C4600EB8617A0F5C2 +:104A10000079DFF8B4B305E0A24607EB4A0044781A +:104A2000202C0AD0EEF797F809EB04135A4601211F +:104A30001B1D00F0C6FF0028EED0AC4202D033466A +:104A400052461EE0E14808B1AFF30080EEF783F86C +:104A500098F82F206AB1D8F80C20411C891A090255 +:104A6000CA1701EB12610912002902DD0020BDE81E +:104A7000F09F3146FFF7D6FE08B10120F7E7334635 +:104A80002A4620210420FFF7BFFDEFE72DE9F04182 +:104A9000CC4C2569EEF75FF8401B0002C11700EB14 +:104AA0001160001200D4FFDF94F8220000B1FFDF94 +:104AB000012784F8227094F82E00202800D1FFDF0F +:104AC00094F82E60202084F82E00002584F82F50C2 +:104AD00084F8205084F82150BD48256000780228D1 +:104AE00033D0032831D000202077A068401C05D0A7 +:104AF0004FF0FF30A0600120EDF78FFF0020EDF7B1 +:104B00008CFFEEF788F8EEF780F8EDF756FF0FF020 +:104B100085FFB048056005604FF0E0214FF400408C +:104B2000B846C1F88002EEF722F994F82D703846A5 +:104B3000FFF75DFF0028FAD0A248803800EB87100D +:104B400010F81600022802D006E00120CCE73A4611 +:104B500031460620FFF72AFD84F8238004EB870006 +:104B600090F82600202804D09948801E4078EEF75F +:104B7000D3F9207F002803D0EEF73DF8257765773D +:104B800040E5904910B591F82D200024803901EBC3 +:104B9000821100BF11F814302BB1641CE4B2202C38 +:104BA000F8D3202010BD8C4901EB041108600020CF +:104BB000C87321460120FFF7F9FC204610BD10B54F +:104BC000012801D0032800D171B37E4A92F82D301C +:104BD0007C4C0022803C04EB831300BF13F812408E +:104BE0000CB1082010BD521CD2B2202AF6D3784A4C +:104BF00048B1022807D0072916D2DFE801F01506D0 +:104C0000080A0C0E100000210AE01B2108E03A21DE +:104C100006E0582104E0772102E0962100E0B5216A +:104C200051701070002010BD072010BD684810B5ED +:104C30004078EEF702F880B210BD10B5202811D2EE +:104C4000604991F82D30A1F1800202EB831414F831 +:104C500010303BB191F82D3002EB831212F8102086 +:104C6000012A01D0002010BD91F82D20014600201E +:104C7000FFF79CFC012010BD10B5EDF76CFFBDE8FF +:104C80001040EDF7DABF2DE9F0410E464D4F0178A7 +:104C90002025803F0C4607EB831303E0254603EBFA +:104CA00045046478944202D0202CF7D108E0202CEF +:104CB00006D0A14206D103EB41014978017007E01B +:104CC00000209FE403EB440003EB4501407848706B +:104CD000424F7EB127B1002140F2DD30AFF30080BA +:104CE0003078A04206D127B100214FF47870AFF39D +:104CF0000080357027B1002140F2E530AFF300802D +:104D000001207FE410B542680B689A1A1202D4178A +:104D100002EB1462121216D4497A91B1427A82B926 +:104D20002F4A006852F82110126819441044001DDF +:104D3000891C081A0002C11700EB1160001232280A +:104D400001DB012010BD002010BD2DE9F047814698 +:104D50001C48214E00EB8100984690F82540202009 +:104D6000107006F50070154600EB81170BE000BFD0 +:104D700006EB04104946001DFFF7C4FF28B107EBFE +:104D800044002C704478202CF2D1297888F8001047 +:104D900013E000BF06EB0415291D4846FFF7B2FFDC +:104DA00068B988F80040A97B99F80A00814201D8C7 +:104DB0000020DEE407EB44004478202CEAD10120F7 +:104DC000D7E40000D00A0020FFFF3F0000000000F1 +:104DD0009E00002000F50040500800200000000068 +:104DE000E85B02002DE9FC410E4607460024FE4D1B +:104DF00009E000BF9DF8000005EB0010816838460F +:104E000000F0F3FD01246B4601AA31463846FFF756 +:104E10009CFF0028EED02046BDE8FC8170B504461A +:104E2000F2480125A54300EB841100EB85104022D8 +:104E300010F0A4FBEE4E26B1002140F25F40AFF32C +:104E40000080EA48803000EB850100EB8400D0F858 +:104E50002500C1F8250026B1002140F26340AFF3E0 +:104E60000080284670BD2DE9FC418446DF48154688 +:104E7000089C00EB85170E4617F81400012803D094 +:104E8000022801D00020C7E70B46DA4A012160461C +:104E900000F097FDA8B101AB6A4629463046FFF7FE +:104EA00054FF70B1D1489DF804209DF80010803067 +:104EB00000EB85068A4208D02B460520FFF7A4FBAD +:104EC0000BE02A462146042014E0202903D007EBFA +:104ED0004100407801E096F8250007EB4401487056 +:104EE0009DF80000202809D007EB400044702A46B6 +:104EF00021460320FFF75AFB01208DE706F8254FD6 +:104F00000120F070F3E7B84901EB0010001DFFF736 +:104F1000D6BB7CB51D46134604460E4600F108027A +:104F200021461846EDF796FE94F908000F2804DD97 +:104F30001F3820722068401C206096B10220AE49C4 +:104F400051F82610461820686946801B20F07F40E3 +:104F5000206094F908002844C01C1F2803DA0120AF +:104F600009E00420EBE701AAEDF774FE9DF80400C8 +:104F700010B10098401C009000992068314408440A +:104F8000C01C20F07F4060607CBD2DE9FE430C46D4 +:104F900006460978607990722079984615465072D5 +:104FA00041B19248803090F82E1020290AD0006933 +:104FB000401D0BE0D4E90223217903B02846BDE867 +:104FC000F043A6E78D484178701D084420F07F47E4 +:104FD000217900222846A368FFF79BFF394628461F +:104FE00000F003FDD4E9023221796846FFF791FF12 +:104FF00041462846019CFFF7F5FE2B46224600213C +:10500000304600F0DEFC002803D13146284600F08F +:10501000ECFCBDE8FE832DE9FE4F814600F0A1FCCB +:1050200030B1002799F8000020B10020BDE8FE8FC4 +:105030000127F7E76D4D6E4C4FF0000A803524B123 +:10504000002140F2D640AFF3008095F82D8085F81E +:1050500023A0002624B1002140F2DB40AFF3008002 +:105060001FB94046FFF7DAFE804624B1002140F226 +:10507000E340AFF30080EDF76EFD43466A464946D4 +:10508000FFF783FF24B1002140F2E940AFF3008035 +:1050900095F82E0020280CD029690098401A0002AB +:1050A000C21700EB1260001203D5684600F09DFCA9 +:1050B000012624B1002140F2F340AFF3008095F8BF +:1050C00023000028BBD124B1002140F2F940AFF306 +:1050D0000080EDF740FD6B46464A002100F071FC70 +:1050E0000028A3D027B941466846FFF77BFE064358 +:1050F00026B16846FFF7E3FAC9F8080024B1002199 +:1051000040F20C50AFF3008001208FE72DE9F04F03 +:1051100089B08B46824600F024FC344C803428B39E +:105120009BF80000002710B1012800D0FFDF304DB0 +:1051300025B1002140F28250AFF300802A490120BE +:1051400001EB0A18A94607905FEA090604D000217E +:1051500040F28A50AFF30080079800F0F9FB94F812 +:105160002D50002084F8230067B119E094F82E0038 +:105170000127202800D1FFDF9BF800000028D6D0AF +:10518000FFDFD4E72846FFF749FE054626B1002198 +:1051900040F29450AFF3008094F823000028D3D15C +:1051A00026B1002140F29E50AFF30080EDF7D3FC12 +:1051B0002B4602AA59460790FFF7E7FE98F80F0022 +:1051C0005FEA060900F001008DF8130004D0002109 +:1051D0004FF4B560AFF300803B462A4602A9CDF8F4 +:1051E00000A007980CE0000050080020500A0020A2 +:1051F00000000000FFFF3F00E85B02009E0000206F +:10520000FFF731FE064604EB850090F82800009079 +:10521000B9F1000F04D0002140F2AF50AFF300808D +:1052200000F08BFB0790B9F1000F04D0002140F291 +:10523000B550AFF3008094F82300002884D1B9F171 +:10524000000F04D0002140F2BD50AFF300800DF1FB +:10525000080C9CE80E00C8E90112C8F80C304EB3E7 +:105260005FEA090604D0002140F2CA50AFF3008083 +:105270000098B84312D094F82E0020280ED126B101 +:10528000002140F2CF50AFF300802846FFF7AFFB7C +:1052900020B99BF80000D8B3012849D0B9F1000F1C +:1052A00004D0002140F2EC50AFF30080284600F01B +:1052B0003DFB01265FEA090504D0002140F2F550CC +:1052C000AFF30080079800F043FB25B1002140F2C6 +:1052D000F950AFF300808EB194F82D0004EB8000FC +:1052E00090F82600202809D025B100214FF4C06095 +:1052F000AFF30080F9484078EDF70EFE25B10021AC +:1053000040F20560AFF3008009B03046BDE8F08F91 +:10531000FFE7B9F1000F04D0002140F2D750AFF3FE +:10532000008094F82D2051460420FFF73FF9C0E794 +:10533000002E3FF409AF002140F2E250AFF30080AD +:1053400002E72DE9F84FE64D814695F82D004FF024 +:105350000008E44C4FF0010B474624B1002140F215 +:105360001360AFF30080584600F0F2FA85F823701E +:1053700024B100214FF4C360AFF3008095F82D00F5 +:10538000FFF74CFD064695F8230028B1002CE4D029 +:10539000002140F21E604BE024B1002140F2226067 +:1053A000AFF30080CE48803800EB861111F8190069 +:1053B000032856D1334605EB830A4A469AF825005E +:1053C000904201D1012000E0002000900AF1250068 +:1053D0000021FFF758FC01460098014203D001224A +:1053E0008AF82820AF77E1B324B1002140F227608A +:1053F000AFF30080324649460120FFF7D7F89AF80C +:1054000028A024B1002140F23260AFF3008000F008 +:1054100094FA834624B1002140F23760AFF3008054 +:1054200095F8230038B1002C97D0002140F23B6062 +:10543000AFF3008091E7BAF1000F07D095F82E0086 +:10544000202803D13046FFF7D2FAE0B124B1002181 +:1054500040F24F60AFF30080304600F067FA4FF043 +:10546000010824B100214FF4CB60AFF3008058460F +:1054700000F06EFA24B1002140F25C60AFF30080CE +:105480004046BDE8F88F002CF1D0002140F24A6080 +:10549000AFF30080E6E70020EDF798BA0120EDF7C2 +:1054A00095BA8E48007870472DE9F0418C4C94F8FD +:1054B0002E0020281FD194F82D6004EB860797F862 +:1054C0002550202D00D1FFDF8549803901EB861062 +:1054D00000EB4500407807F8250F0120F87084F8AC +:1054E0002300294684F82E50324602202234FFF74A +:1054F0005DF80020207004E42DE9F0417A4E784CEC +:10550000012538B1012821D0022879D003287DD087 +:10551000FFDFF0E700F03DFAFFF7C6FF207E00B1A5 +:10552000FFDF84F821500020EDF777FAA168481CCE +:1055300004D0012300221846EDF7C2FA14F82E0F0A +:10554000217806EB01110A68012154E0FFF7ACFF56 +:105550000120EDF762FA94F8210050B1A068401CD8 +:1055600007D014F82E0F217806EB01110A680621E6 +:1055700041E0207EDFF86481002708F1020801285D +:1055800003D002281ED0FFDFB5E7A777EDF733FB86 +:1055900098F80000032801D165772577607D53498D +:1055A00051F8200094F8201051B948B161680123E6 +:1055B000091A00221846EDF783FA022020769AE7AE +:1055C000277698E784F8205000F0E3F9A07F50B1E7 +:1055D00098F8010061680123091A00221846EDF7C6 +:1055E0006FFA257600E0277614F82E0F217806EB67 +:1055F00001110A680021BDE8F041104700E005E014 +:1056000036480078BDE8F041EDF786BCFFF74CFF67 +:1056100014F82E0F217806EB01110A680521EAE73C +:1056200010B52F4C94F82E00202800D1FFDF14F87D +:105630002E0F21782C4A02EB01110A68BDE81040B8 +:10564000042110477CB5264C054694F82E002028EE +:1056500000D1FFDFA068401C00D0FFDF94F82E00CF +:10566000214901AA01EB0010694690F90C00284479 +:10567000EDF7F0FA9DF904000F2801DD012000E0AC +:105680000020009908446168084420F07F41A1602F +:1056900094F82100002807D002B00123BDE8704033 +:1056A00000221846EDF70CBA7CBD30B5104A0B1A33 +:1056B000541CB3EB940F1FD3451AB5EB940F1BD3B7 +:1056C000934203D9101A43185B1C15E0954211D977 +:1056D000511A0844401C43420EE000009C00002088 +:1056E000D00A00200000000050080020E85B020003 +:1056F000FF7F841EFFDF0023184630BD01230022F8 +:1057000001460220EDF7DCB90220EDF786B9EDF78E +:1057100022BA2DE9FC47BA4C054694F82E00202801 +:1057200000D1FFDF642D58D3B64A0021521B71EB24 +:10573000010052D394F82E20A0462046DFF8C892EC +:1057400090F82D7009EB0214D8F8000001AA284443 +:105750006946EDF77FFA9DF90400002802DD009804 +:10576000401C0090A068009962684618B21A22F0A6 +:105770007F42B2F5800F30D208EB8702444692F8A0 +:105780002520202A0AD009EB02125268101A0002C2 +:10579000C21700EB1260001288421EDBA068401C9A +:1057A00010D0EDF7D8F9A168081A0002C11700EB74 +:1057B00011600012022810DD0120EDF72EF94FF0E4 +:1057C000FF30A06020682844206026F07F402061E0 +:1057D000012084F82300BDE8FC870020FBE72DE9C9 +:1057E000F047874C074694F82D00A4F1800606EB9D +:1057F000801010F8170000B9FFDF94F82D50A04674 +:10580000824C24B100214FF40760AFF3008040F6D2 +:105810007C0940F6850A06EB851600BF16F81700CE +:10582000012818D0042810D005280ED006280CD046 +:105830001CB100214846AFF3008020BF002CEDD002 +:1058400000215046AFF30080E8E72A4639460120A0 +:10585000FEF7ACFEF2E74FF0010A4FF000094546B3 +:1058600024B1002140F68C00AFF30080504600F0D8 +:105870006FF885F8239024B1002140F69100AFF332 +:10588000008095F82D00FFF7C9FA064695F8230029 +:1058900028B1002CE4D0002140F697001FE024B18D +:1058A000002140F69B00AFF3008005EB860000F17D +:1058B000270133463A462630FFF7E5F924B10021A7 +:1058C00040F69F00AFF3008000F037F8824695F86D +:1058D000230038B1002CC3D0002140F6A500AFF35F +:1058E0000080BDE785F82D60012085F82300504633 +:1058F00000F02EF8002C04D0002140F6B200AFF3E7 +:105900000080BDE8F08730B504463D480D4690F86C +:105910002D003B49803901EB801010F8140000B9CC +:10592000FFDF394800EB0410C57330BD344981F8FE +:105930002D00012081F82300704710B5344808B1CC +:10594000AFF30080EFF3108000F0010072B610BDDD +:1059500010B5002804D12F4808B1AFF3008062B61B +:1059600010BD2D480068C005C00D10D0103840B2E1 +:10597000002804DB00F1E02090F8000405E000F0CE +:105980000F0000F1E02090F8140D40097047082046 +:10599000704710B51A4C94F82400002804D1F6F78B +:1059A0005FF8012084F8240010BD10B5144C94F861 +:1059B0002400002804D0F6F77CF8002084F82400A6 +:1059C00010BD10B51C685B68241A181A24F07F44B7 +:1059D00020F07F40A14206D8B4F5800F03D2904258 +:1059E00001D8012010BD002010BDD0E90032D21A2C +:1059F00021F07F43114421F07F41C0E9003170471D +:105A0000D00A0020FF1FA10750080020000000005E +:105A1000000000000000000004ED00E02DE9F0416E +:105A2000044680074FF000054FF001060CD56B4887 +:105A3000056006600EF01BFE20B16948016841F464 +:105A40008061016024F00204E0044FF0FF3705D5C7 +:105A500064484660C0F8087324F48054600003D59D +:105A60006148056024F08044E0050FD55F48C0F828 +:105A70000052C0F808735E490D60091D0D605C4A54 +:105A800004210C321160066124F48074A00409D54D +:105A900058484660C0F80052C0F808735648056080 +:105AA00024F40054C4F38030C4F3C031884200D0E1 +:105AB000FFDF14F4404F14D050484660C0F808731C +:105AC0004F488660C0F80052C0F808734D490D6019 +:105AD0000A1D16608660C0F808730D60166024F415 +:105AE000404420050AD5484846608660C0F80873DF +:105AF000C0F848734548056024F400640EF068FF60 +:105B00004348044200D0FFDFBDE8F081F0B5002239 +:105B1000202501234FEA020420FA02F1C9072DD003 +:105B200051B2002910DB00BF4FEA51174FEA870737 +:105B300001F01F0607F1E02703FA06F6C7F88061B7 +:105B4000BFF34F8FBFF36F8F0CDB00BF4FEA5117CE +:105B50004FEA870701F01F0607F1E02703FA06F670 +:105B6000C7F8806204DB01F1E02181F8004405E020 +:105B700001F00F0101F1E02181F8144D02F1010261 +:105B8000AA42C9D3F0BD10B5224C20600846F6F7F2 +:105B90007CF82068FFF742FF2068FFF7B7FF0EF0A0 +:105BA000FDFA00F01AF90EF013FF0EF056FEEDF7B5 +:105BB0007FF9BDE810400EF0A1BB10B5154C206870 +:105BC000FFF72CFF2068FFF7A1FF0EF001FFF6F7AB +:105BD0004FF90020206010BD0A207047FC1F0040D4 +:105BE0003C17004000C0004004E501400080004038 +:105BF0000485004000D0004004D5004000E0004093 +:105C000000F0004000F5004000B0004008B5004042 +:105C1000FEFF0FFDA000002070B526490A680AB3F8 +:105C20000022154601244B685B1C4B600C2B00D3F3 +:105C30004D600E7904FA06F30E681E420FD0EFF3A2 +:105C4000108212F0010272B600D001220C689C434F +:105C50000C6002B962B649680160002070BD521C38 +:105C60000C2AE0D3052070BD4FF0E0214FF48000F6 +:105C7000C1F800027047EFF3108111F0010F72B606 +:105C80004FF0010202FA00F20A48036842EA0302F6 +:105C9000026000D162B6E7E706480021016041607A +:105CA00070470121814003480068084000D001206E +:105CB00070470000A40000200120810708607047A1 +:105CC0000121880741600021C0F8001118480170C7 +:105CD000704717490120087070474FF08040D0F896 +:105CE0000001012803D012480078002800D00120CC +:105CF000704710480068C00700D0012070470D4869 +:105D00000C300068C00700D00120704709481430EB +:105D100000687047074910310A68D20306D5096840 +:105D200001F00301814201D101207047002070473A +:105D3000AC000020080400400021017008467047B4 +:105D40000146002008707047EFF3108101F0010157 +:105D500072B60278012A01D0012200E0002201235C +:105D6000037001B962B60AB1002070474FF40050C9 +:105D70007047E9E7EFF3108111F0010F72B64FF0B1 +:105D80000002027000D162B600207047F2E7000006 +:105D90002DE9F04115460E460446002700F0E7F8CD +:105DA000A84215D3002341200FE000BF94F8422001 +:105DB000A25CF25494F84210491CB1FBF0F200FBD3 +:105DC00012115B1C84F84210DBB2AB42EED3012708 +:105DD00000F0D9F83846BDE8F081704910B5802050 +:105DE00081F800046E49002081F8420081F84100EA +:105DF000433181F8420081F84100433181F842008B +:105E000081F841006748FFF797FF6648401CFFF79D +:105E100093FFECF7BBFFBDE8104000F0B4B84020A2 +:105E200070475F4800F0A3B80A4601465C48AFE7F8 +:105E3000402070475A48433000F099B80A4601465E +:105E400057484330A4E7402101700020704710B547 +:105E500004465348863000F08AF82070002010BDB8 +:105E60000A4601464E4810B58630FFF791FF08B14B +:105E7000002010BD42F2070010BD70B50C4605466B +:105E8000412900D9FFDF48480068103840B200F0CF +:105E900050F8C6B20D2000F04CF8C0B2864203D2D2 +:105EA000FFDF01E0ECF7C2FF224629463C48FFF73E +:105EB0006FFF0028F6D070BD2DE9F041394F002565 +:105EC00006463F1D57F82540204600F041F810B324 +:105ED0006D1CEDB2032DF5D33148433000F038F896 +:105EE000002825D02E4800F033F8002820D02C4878 +:105EF000863000F02DF800281AD0ECF76DFF294805 +:105F0000FFF722FFB0F5005F00D0FFDFBDE8F041F2 +:105F10002448FFF72FBF94F841004121265414F87C +:105F2000410F401CB0FBF1F201FB12002070D3E7DF +:105F300051E7002804DB00F1E02090F8000405E0C0 +:105F400000F00F0000F1E02090F8140D40097047B8 +:105F500010F8411F4122491CB1FBF2F302FB13115F +:105F60004078814201D1012070470020704710F82D +:105F7000411F4078814201D3081A02E0C0F141007C +:105F80000844C0B2704710B50648FFF7DDFE002890 +:105F900003D1BDE81040ECF70ABF10BD0DE000E0F2 +:105FA000000B0020B000002004ED00E070B5154D9E +:105FB0002878401CC4B26878844202D0F5F7EFFF1D +:105FC0002C7070BD2DE9F0410E4C4FF0E02600BF63 +:105FD000F5F7DAFF20BF40BF20BF677820786070F8 +:105FE000D6F80052EBF70CFA854305D1D6F8040237 +:105FF00010B92078B842EBD0F5F7C1FF0020BDE81A +:10600000F0810000C00000202DE9F04101252803A7 +:106010004FF0E0210026C1F88001BFF34F8FBFF39E +:106020006F8F1F4CC4F800610C2000F02CF81D4845 +:1060300001680268C94341F3001142F01002026096 +:10604000C4F804532560491C00E020BFD4F80021A7 +:10605000002AFAD019B9016821F010010160124834 +:1060600007686560C4F80853C4F800610C2000F0AC +:106070000AF83846BDE8F08110B50446FFF7C4FFC2 +:106080002060002010BD002809DB00F01F02012164 +:1060900091404009800000F1E020C0F88012704774 +:1060A00000C0004010ED00E008C500402DE9F047B9 +:1060B000FF4C0646FF21A06800EB06121170217804 +:1060C000FF2910D04FF0080909EB011109EB061761 +:1060D0004158C05900F0F4F9002807DDA168207884 +:1060E00001EB061108702670BDE8F08794F8008077 +:1060F00045460DE0A06809EB05114158C05900F074 +:10610000DFF9002806DCA068A84600EB0810057837 +:10611000FF2DEFD1A06800EB061100EB08100D7009 +:106120000670E1E7F0B5E24B0446002001259A68CD +:106130000C269B780CE000BF05EB0017D75DA7424B +:1061400004D106EB0017D7598F4204D0401CC0B2CF +:106150008342F1D8FF20F0BD70B5FFF7D8FAD44CD8 +:1061600008252278A16805EB0212895800F0A8F9E9 +:10617000012808DD2178A06805EB01114058BDE831 +:106180007040FFF7BBBAFFF78CF9BDE87040ECF741 +:10619000C3BE2DE9F041C64C2578FFF7B8FAFF2DB4 +:1061A0006ED04FF00808A26808EB0516915900F070 +:1061B00087F90228A06801DD80595DE000EB051138 +:1061C00009782170022101EB0511425C5AB1521E7F +:1061D0004254815901F5800121F07F4181512846C7 +:1061E000FFF764FF34E00423012203EB051302EB05 +:1061F000051250F803C0875CBCF1000F10D0BCF54D +:10620000007F10D9CCF3080250F806C00CEB423CDA +:106210002CF07F4C40F806C0C3589A1A520A09E085 +:10622000FF2181540AE0825902EB4C3222F07F4276 +:106230008251002242542846FFF738FF0C21A06803 +:1062400001EB05114158E06850F827203846904787 +:106250002078FF2814D0FFF75AFA2278A16808EBBB +:1062600002124546895800F02BF9012893DD217868 +:10627000A06805EB01114058BDE8F041FFF73EBAB8 +:10628000BDE8F081F0B51D4614460E460746FF2BCB +:1062900000D3FFDFA00700D0FFDF8548FF210022E9 +:1062A000C0E90247C57006710170427082701046E5 +:1062B000012204E002EB0013401CE154C0B2A842EA +:1062C000F8D3F0BD70B57A4C0646657820798542E2 +:1062D00000D3FFDFE06840F825606078401C607004 +:1062E000284670BD2DE9FF5F1D468B460746FF24FB +:1062F000FFF70DFADFF8B891064699F80100B842A9 +:1063000000D8FFDF00214FF001084FF00C0A99F888 +:106310000220D9F808000EE008EB0113C35CFF2B44 +:106320000ED0BB4205D10AEB011350F803C0DC4587 +:106330000CD0491CC9B28A42EED8FF2C02D00DE025 +:106340000C46F6E799F803108A4203D1FF2004B007 +:10635000BDE8F09F1446521C89F8022008EB041196 +:106360000AEB0412475440F802B00421029B0022B9 +:10637000012B01EB04110CD040F801204FF4007800 +:1063800008234FF0020C454513D9E905C90D02D089 +:1063900002E04550F2E7414606EB413203EB0413BD +:1063A00022F07F42C250691A0CEB0412490A815450 +:1063B0000BE005B9012506EB453103EB041321F091 +:1063C0007F41C1500CEB0411425499F80050204613 +:1063D000FFF76CFE99F80000A84201D0FFF7BCFE61 +:1063E0003846B4E770B50C460546FFF790F9064607 +:1063F00021462846FFF796FE0446FF281AD02C4D6A +:10640000082101EB0411A8684158304600F058F803 +:1064100000F58050C11700EBD14040130221AA685B +:1064200001EB0411515C09B100EB4120002800DCB4 +:10643000012070BD002070BD2DE9F04788468146DF +:10644000FFF770FE0746FF281BD0194D2E78A8686D +:106450003146344605E0BC4206D0264600EB061223 +:106460001478FF2CF7D10CE0FF2C0AD0A6420CD1F7 +:1064700000EB011000782870FF2804D0FFF76CFEB5 +:1064800003E0002030E6FFF73FF941464846FFF7BA +:10649000A9FF0123A968024603EB0413FF20C85497 +:1064A000A878401EB84200D1A87001EB041001E0AA +:1064B000CC0B002001EB061100780870104613E6A3 +:1064C000081A0002C11700EB1160001270470000AB +:1064D0005E4800210170417010218170704770B5D5 +:1064E000054616460C460220ECF7F2F95749012002 +:1064F00008705749F01E086056480560001F046088 +:1065000070BD10B50220ECF7E3F950490120087086 +:1065100051480021C0F80011C0F80411C0F808115A +:106520004E494FF40000086010BD48480178D9B1C9 +:106530004B4A4FF4000111604749D1F80031002265 +:10654000002B1CBFD1F80431002B02D0D1F8081168 +:1065500019B142704FF0100104E04FF00101417099 +:1065600040490968817002704FF00000ECF7B0B943 +:1065700010B50220ECF7ACF934480122002102707A +:106580003548C0F80011C0F80411C0F808110260C5 +:1065900010BD2E480178002904BF407870472E486E +:1065A000D0F80011002904BF02207047D0F8001174 +:1065B00000291CBFD0F80411002905D0D0F808012B +:1065C000002804BF01207047002070471F4800B515 +:1065D0000278214B4078C821491EC9B282B1D3F854 +:1065E00000C1BCF1000F10D0D3F8000100281CBF7F +:1065F000D3F8040100280BD0D3F8080150B107E00C +:10660000022802D0012805D002E00029E4D1FFDFF2 +:10661000002000BD012000BD0C480178002904BF06 +:10662000807870470C48D0F8001100291CBFD0F8C2 +:106630000411002902D0D0F8080110B14FF0100069 +:10664000704708480068C0B270470000C2000020D0 +:1066500010F5004008F5004000F0004004F501404E +:1066600008F5014000F400405648002101704170D7 +:10667000704770B5064614460D460120ECF728F920 +:1066800051480660001D0460001D05604F49002050 +:10669000C1F850014E49032008604F494D48086039 +:1066A000091D4E48086070BD2DE9F041054645487A +:1066B0000C46012606704A4945EA024040F08070C7 +:1066C00008600DF0AAFF002804BF464804600027B8 +:1066D000454CC4F80471464944480860002D02BF87 +:1066E000C4F800622660BDE8F081012D18BFFFDF0D +:1066F000C4F80072266040493E480860BDE8F08159 +:106700003048017871B13A4A384911603649D1F8B8 +:1067100004210021002A08BF417002D0374A1268C4 +:10672000427001700020ECF7D3B8264801780029A8 +:1067300004BF407870472C48D0F80401002808BFF7 +:1067400070472E480068C0B27047002808BF7047E5 +:1067500030B51C480078002808BFFFDF2248D0F879 +:106760000411002918BF30BD0224C0F80443DFF82B +:1067700090C0DCF80010C1F30015DCF8001041F007 +:106780001001CCF80010D0F80411002904BF4FF418 +:1067900000414FF0E02206D1C2F8801220BFD0F8AD +:1067A0000431002BF8D02DB9DCF8001021F01001D5 +:1067B000CCF80010C0F8084330BD0B4901208860B8 +:1067C00070470000C500002008F5004000100040A0 +:1067D0001CF500405011004098F501400CF00040BD +:1067E00004F5004018F5004000F0004000000203EE +:1067F00008F501400000020204F5014000F40040E9 +:1068000010ED00E010B5FE48002401214470047032 +:1068100044728472C17280F82540C462846380F837 +:106820003C4080F83D40FF2180F83E105F2180F819 +:106830003F1018300FF052F8F249601E0860091D31 +:106840000860091D0C60091D0860091D0C60091D08 +:106850000860091D0860091D0860091D0860091D00 +:106860000860091D0860091D0860091D0860091DF0 +:10687000086010BDE448016801F00F01032904BF5E +:1068800001207047016801F00F01042904BF0220B4 +:106890007047016801F00F01052904D0006800F07D +:1068A0000F00062807D1D948006810F0060F0CBF6A +:1068B00008200420704700B5FFDF012000BD10B59F +:1068C000CF4C0168A1614168E161007A84F8200041 +:1068D000207E48B1207FF7F7C4FCA07E011C18BFC2 +:1068E0000121207FF7F7ACFC607E002808BF10BDB7 +:1068F000607FF7F7B6FCE07E011C18BF0121607FC6 +:10690000BDE81040F7F79CBC30B5002405460129CE +:106910000AD0022908BF4FF0807405D0042916BFA1 +:1069200008294FF0C744FFDF44F4847040F480101E +:10693000B749086045F4403001F1040140F00070AF +:10694000086030BD30B50024054601290AD002296F +:1069500008BF4FF0807405D0042916BF08294FF0F6 +:10696000C744FFDF44F4847040F48010A8490860F5 +:1069700045F4403001F1040140F000700860A54882 +:10698000D0F80001002818BFFFDF30BD2DE9F0412D +:1069900002274FF0E02801250024C8F88071BFF3DA +:1069A0004F8FBFF36F8F9C48046005600DF05FFE52 +:1069B0009A4E18B1306840F4806030600DF02DFEC2 +:1069C00038B1306820F0770040F0880040F0004097 +:1069D00030609449924808604FF01020806CB0F10C +:1069E000FF3F04D090490A6860F317420A608F495C +:1069F00040F25B600860091F40F203100860081F46 +:106A00000560814903200860894805608A4A8949F0 +:106A100011608B4A89491160121F8A49116001680F +:106A200021F440710160016841F480710160C8F88F +:106A3000807278491020C1F80403714880F8314011 +:106A4000C462BDE8F0816E4A0368C2F802308088F3 +:106A5000D080117270476A4B10B51A7A8A4208D1F9 +:106A600001460622981C0EF05DFD002804BF01209F +:106A700010BD002010BD624890F825007047604AA4 +:106A8000517010707047F0B50546800000F18040ED +:106A900000F580508B88C0F820360B78D1F80110B3 +:106AA00043EA0121C0F8001605F10800012707FAA2 +:106AB00000F6654C002A04BF2068B04304D0012AC8 +:106AC00018BFFFDF206830432060206807FA05F117 +:106AD00008432060F0BD0EF0D1B8494890F832006C +:106AE00070475A4AC178116000685949000208602D +:106AF0007047252808BF02210ED0262808BF1A217A +:106B00000AD0272808BF502106D00A2894BF0422A3 +:106B1000062202EB4001C9B24E4A11604E4908609C +:106B2000704737498A7A012A49D0022A18BF70472C +:106B30004B7E002B08BF7047012A44D0CB7E4A7F92 +:106B400013F1000C18BF4FF0010C24231844434BE1 +:106B50001860434B0020C3F84C0110028CF0010276 +:106B600040EA025040F0031291F82000830003F144 +:106B7000804303F5C043C3F810253A4A8B7F02EBEC +:106B80008000DA0002F1804202F5F832C2F8140502 +:106B9000DFF8D4C0C2F810C5C97FCA0002F1804234 +:106BA00002F5F832C2F814052648C2F81005012093 +:106BB00000FA03F288402D491043086070470B7EAD +:106BC000002BB9D170478B7E0A7F002B14BF4FF08A +:106BD000010C4FF0000C1123B8E72DE9F0410D4EE8 +:106BE000804603200D46C6F8000220492048086070 +:106BF00028460EF082F80124014FB8F1000F39E069 +:106C0000DC0B0020000E0040101500401414004062 +:106C10001415004000100040FC1F00403C170040CD +:106C20002C000089781700408C1500403815004072 +:106C30005016004000000E0408F50140408000405E +:106C4000A4F50140101100404016004024150040FA +:106C50001C15004008150040541500404C850040AC +:106C600000800040006000404C81004004F501407D +:106C70000000040404BFBC72346026D0B8F1010FD8 +:106C800023D1FE48006860B915F00C0F09D0C6F892 +:106C90000443012000F0B4FEF463346487F83C4000 +:106CA00002E0002000F0ACFE28460EF00EF90220B3 +:106CB000B8720DF0CAFC38B90DF0D9FC20B9F04813 +:106CC000016841F4C02101607460EE48C464EE487C +:106CD00000682946BDE8F04123E72DE9F047EB4E77 +:106CE000814603200D46C6F80002DFF8A883E84875 +:106CF000C8F8000008460EF000F828460EF0E5F847 +:106D00000124E54FB9F1000F03D0B9F1010F0AD00A +:106D100026E0BC72B86B40F48010B8634FF480106A +:106D2000C8F800001CE00220B872B86B40F40010F4 +:106D3000B8634FF40010C8F80000D048006860B98C +:106D400015F00C0F09D0C6F80443012000F058FEDE +:106D5000F463346487F83C4002E0002000F050FE09 +:106D6000EBF794FF2946BDE8F047DAE62DE9F84F46 +:106D7000C64C8246032088461746C4F80002DFF856 +:106D80001493C348C9F8000010460DF0B6FFDFF8B1 +:106D90000CB3C14E0125BAF1000F04BFCBF800407F +:106DA000B57204D0BAF1010F18BFFFDF2FD0BC4875 +:106DB000C0F80080BC49BB480860B06B40F40020BC +:106DC000B063D4F800321021C4F808130020C4F8CE +:106DD0000002DFF8D8C28A03CCF80020C4F8000112 +:106DE000C4F80C01C4F81001C4F80401C4F814017B +:106DF000C4F81801AE4800680090C4F80032C9F821 +:106E00000020C4F80413BAF1010F09D01BE0384682 +:106E10000EF05BF8A748CBF800000220B072C6E77E +:106E20009648006860B917F00C0F09D0C4F80453F5 +:106E3000012000F0E5FDE563256486F83C5002E0A2 +:106E4000002000F0DDFD4FF40020C9F800008D485F +:106E5000C5648D480068404528BFFFDF394640467D +:106E6000BDE8F84F5DE62DE9F0418B4C0646002564 +:106E700094F8310017468846002808BFFFDF16B196 +:106E8000012E16D021E094F83100012808D094F8A2 +:106E90003020394640460DF045FFE16A451814E0C0 +:106EA00094F830103A4640460DF07AFFE16A4518F2 +:106EB0000BE094F8310094F8301001283A4640462F +:106EC00009D00DF095FFE16A45183A46294630464B +:106ED000BDE8F0414AE70DF045FFE16A4518F4E7E7 +:106EE0002DE9F84F694CD4F8000220F00B09D4F8D2 +:106EF00004034FF0100AC0F30018C4F808A30026DA +:106F0000C4F8006269486C490160634D0127A97AA1 +:106F1000012902D0022903D015E0297E11B912E01F +:106F2000697E81B1A97FEA7F07FA01F107FA02F2CF +:106F30001143016095F82000800000F1804000F5C9 +:106F4000C040C0F81065FF208DF80000C4F8106143 +:106F5000276104E09DF80000401E8DF800009DF8B8 +:106F6000000018B1D4F810010028F3D09DF80000FB +:106F7000002808BFFFDFC4F81061002000F040FDCA +:106F80006E72AE72EF72C4F80092B8F1000F18BFC3 +:106F9000C4F804A3BDE8F88FFF2008B58DF8000001 +:106FA0003A480021C0F810110121016105E000BF3D +:106FB0009DF80010491E8DF800109DF8001019B1C1 +:106FC000D0F810110029F3D09DF80000002808BF68 +:106FD000FFDF08BD0068394920F07F400860704736 +:106FE0004FF0E0200221C0F8801100F5C070BFF31F +:106FF0004F8FBFF36F8FC0F8001170474FF0E02143 +:107000000220C1F8000170472D49087070472D49D2 +:107010000860704770B50546EBF738FE1E4C2844F3 +:10702000E16A884298BFFFDF01202074EBF72EFE53 +:10703000144A284400216061C2F8441122490860C2 +:10704000A06B144940F48000A063D001086070BDBB +:1070500070B5114C05461D4A0220207410680E467A +:1070600000F00F00032808BF01223ED0106800F096 +:107070000F00042808BF022237D029E088170040FB +:1070800068150040008000404C8500400010004022 +:107090000000040404F50140DC0B0020ACF50140C5 +:1070A0004885004048810040A8F5014008F50140AE +:1070B000181100400410004000000E043C15004070 +:1070C000C700002004150040448500401015004012 +:1070D000106800F00F0005281BD0106800F00F00AA +:1070E00006281CBFFFDF012213D094F8310094F86A +:1070F0003010012815D028460DF0C1FEFF4960610F +:107100000020C1F844016169E06A0844FC49086054 +:1071100070BDFC48006810F0060F0CBF0822042266 +:10712000E3E7334628460DF078FEE7E7F6494FF4EB +:1071300080000860F548816B21F4800181630021A3 +:1071400001747047C20002F1804202F5F832F04B40 +:10715000C2F81035C2F8141501218140ED480160D4 +:10716000EA48826B114381637047E4480121416022 +:10717000C1600021C0F84411E1480160E348C162E8 +:107180007047E5490860E548D0F8001241F0400139 +:10719000C0F800127047E148D0F8001221F0400119 +:1071A000C0F80012DC49002008607047DB48D0F8C6 +:1071B000001221F01001C0F8001201218161704716 +:1071C000D249FF2081F83E00D4480021C0F81C11AC +:1071D000D0F8001241F01001C0F800127047CF49FA +:1071E00081B0D1F81C21012A0DD0C84991F83E1078 +:1071F000FF290DBF00204942017001B008BF704750 +:10720000012001B07047C64A126802F07F02524264 +:1072100002700020C1F81C01C24800680090EFE72E +:10722000F0B517460C00064608BFFFDFB74D14F057 +:10723000010F2F731CBF012CFFDF002E0CBF01209C +:1072400002206872EC7201281CBF0228FFDFF0BD2B +:10725000AE4981F83F0070472DE9F84FDFF8C8A22A +:107260009AF80000042828BFFFDFA84CDFF89882B6 +:10727000AA4D94F83C0000260127E0B1D5F804019E +:1072800010F1000918BF4FF00109D5F810010028CE +:1072900018BF012050EA09014FF4002B17D08021BC +:1072A000C5F80813C8F800B084F83C6090F0010FEE +:1072B00018BFBDE8F88FDFF84492D9F84C010028D8 +:1072C0007ED0A07A01287CD002287BD0AEE0D5F811 +:1072D0000001DFF84CA230B3C5F800616F61FF20F8 +:1072E000009002E0401E009005D0D5F81C01002857 +:1072F0000098F7D000B9FFDFDAF8000000F07F0A4D +:1073000094F83F0050453CBF002000F079FB84F822 +:107310003EA0C5F81C61C5F808738248006800905B +:107320002F64AF6302E0B9F1000F03D0B9F1000F91 +:107330002BD05DE0DAF8000000F07F0084F83E001A +:10734000C5F81C6194F83D1049B194F83F10814292 +:1073500018D2002000F054FB2F64AF6312E0734991 +:10736000096894F83F308AB2090C984203D30F2A77 +:1073700006D9022904D2012000F042FB2F6401E06B +:107380002F64AF636748006800908022C5F804232B +:107390005A48876466490B68A1F1040CDCF800C008 +:1073A00043F698273B44634519D20A6842F21073AA +:1073B0001A440A60C0F848615F495E48086002E00C +:1073C00034E01CE01EE0091F5C4808605148C0F82A +:1073D00000B0A06B40F40020A063BDE8F88F0E6001 +:1073E000C0F84861C5F80823C8F800B0C0F8486183 +:1073F0008020C5F80803C8F800B0BDE8F88F207EEB +:1074000010B913E0607E88B1A07FE17F07FA00F039 +:1074100007FA01F10843C8F8000094F82000800042 +:1074200000F1804000F5C040C0F810653648A16BFF +:107430000160A663217C002019B1D9F8441101290B +:1074400000D00021A27A012A56D0022A55D000BFCE +:10745000D5F8101101290CBF1021002141EA0008C4 +:107460003748016811F0FF0F03D0D5F81411012936 +:1074700000D0002184F83210006810F0FF0F03D014 +:10748000D5F81801012800D0002084F833002D48D9 +:10749000006884F83400FFF77CF8012818BF00204A +:1074A00084F83500C5F80061C5F80C61C5F81061B5 +:1074B000C5F80461C5F81461C5F818612248006870 +:1074C00000900E48C0F8446120480068DFF8309012 +:1074D0000090D9F80000A062A9F104000068E06201 +:1074E0001B48016801F00F01032908BF012042D0A9 +:1074F000016801F00F012DE045E04BE00080004005 +:10750000448500401414004008F50140DC0B0020C5 +:107510000411004004F501406015004000100040D7 +:10752000481500401C110040C700002074150040A1 +:107530004885004014100040ACF5014048810040EF +:1075400040160040101400401811004044810040D3 +:1075500010150040042908BF02200CD0016801F07A +:107560000F01052925D0006800F00F0006281CBF78 +:10757000FFDF01201DD084F83000A07A84F83100AC +:1075800002282BD11DE0D5F80C01012814BF0020E2 +:1075900008205DE7D5F80C01012814BF0020022067 +:1075A000F64A1268012A14BF04220022104308433D +:1075B0004EE7F348006810F0060F0CBF08200420C7 +:1075C000D9E7607850B1EF49096809780840217817 +:1075D00031EA000008BF84F8247001D084F82460E8 +:1075E00018F0020F0AD0EBF751FBA16AE64A081A1D +:1075F0009AF80010490852F82110884718F0010F36 +:1076000018BF4FF0000B11D0EBF740FBE16A9AF87E +:107610000020081ADD4951F822205946904700BF42 +:107620009AF8000010F0010F2FD10CE018F0020FB3 +:1076300018BF4FF0010BE7D118F0080F18BF4FF03B +:10764000020BE1D1ECE7DFF83CB3DBF80000007897 +:1076500000F00F00072828BF84F8256015D2DBF85A +:107660000000062200F10901A01C0DF05BFF40B9EB +:10767000207ADBF800100978B0EBD11F08BF012099 +:1076800001D04FF0000084F82500E17A4FF00000AF +:1076900011F0020F1CBF18F0020F18F0040F19D1DF +:1076A00011F0100F1CBF94F83320002A02D094F878 +:1076B00035207AB111F0080F1CBF94F82420002A5D +:1076C00008D111F0040F02D094F8251011B118F070 +:1076D000010F01D04FF00100617A19B168B1FFF7D5 +:1076E000FFFB10E0AB48AA490160D5F8000220F08A +:1076F0000300C5F80002E77205E001290DD0022958 +:1077000018BFFFDF10D018F0010F17D0A2489AF869 +:10771000001050F82100804756E06672E772A772A9 +:107720009621227B002006E06672E7720220A0729A +:10773000227B96210120FFF796FBE4E718F0020F69 +:107740002DD018F0040F21D10CF07FFFF0B90CF010 +:107750008EFFD8B991480168001F0068C0F3006C23 +:10776000C0F3425500F00F03C0F30312C0F303202F +:10777000BCF1000F0AD0002B1CBF002A002805D145 +:10778000002918BF032D38BF48F0040827EA9800E5 +:1077900083499AF8002051F82210884714E018F025 +:1077A000080F06D07F489AF8001050F82100804753 +:1077B0000AE018F0100F08BFFFDF05D07A489AF8EA +:1077C000001050F821008047A07A022818BFBDE8B9 +:1077D000F88F207C002808BFBDE8F88F7349C1F8F6 +:1077E0004461022814D0012818BFFFDFE16A6069F4 +:1077F000884298BFFFDF6069C9F80000A06B4FF4B2 +:10780000800140F48000A06369480160BDE8F88F02 +:107810006169E06A0844EFE738B5664D0024002846 +:1078200018BFC5F800426448006864498A7A012A92 +:1078300002D0022A03D018E00A7E12B915E04A7E6F +:107840009AB18B7F012291F81FC002FA03F302FA6A +:107850000CF21A434F4B1A6091F82010890001F185 +:10786000804101F5C041C1F810450121FFF759F9E8 +:10787000C5F80041C5F80C41C5F81041C5F80441F0 +:10788000C5F81441C5F818414D480068009038BD4E +:10789000012804BF28207047022804BF1820704721 +:1078A000042812BF08284FF4A870704700B5FFDF06 +:1078B000282000BD012804BF41F6A47070470228AB +:1078C00004BF41F288307047042804BF46F2180014 +:1078D0007047082804BF47F2A030704700B5FFDFAB +:1078E00041F6A47000BD10B502280DD0012804BFD8 +:1078F00042F6CE3010BD042817BF082843F6A44036 +:10790000FFDF41F66A0010BD0CF07AFE30B90CF0D2 +:1079100084FE002808BF41F6583001D041F264309F +:1079200041F29A01084410BD012812BF022800202C +:107930007047042812BF08284FF4C870704700B57C +:10794000FFDF002000BD1B490820C1F800021149DB +:107950000F4808601C491B480860091D1B48086047 +:107960001C491B480860091D1B48086010494FF45A +:10797000602008601149022088727047001400409E +:107980001414004004150040005C0200485C020032 +:107990000000040408F50140085C02005414004093 +:1079A000185C0200285C0200385C02000080004085 +:1079B00004F501400010004040850040DC0B002031 +:1079C000181100400011004098F5014014100040CB +:1079D0001C110040A8F50140101000401948016832 +:1079E00003291BBF006802280120002070471548AA +:1079F00001680B291BBF00680A280120002070477E +:107A000011490968C9B9114A1149136870B123F0C5 +:107A1000820343F07D0343F0004313600A6822F0C1 +:107A2000100242F0600242F0004205E023F0004301 +:107A300013600A6822F000420A60064981F83D009E +:107A40007047000050150040881700403C17004068 +:107A50007C170040DC0B002010B53F4822210DF0C0 +:107A60000CFE3D480024017821F010010170012135 +:107A700006F064F839494FF6FF7081F82240888497 +:107A800037490880488010BD704734498A8C82424B +:107A900018BF7047002081F822004FF6FF708884DD +:107AA00070472D49016070472D49088070472B4968 +:107AB0008A8CA2F57F43FF3B03D00021016008467A +:107AC000704791F822202549012A1ABF0160012040 +:107AD00000207047214901F1220091F82220012A5B +:107AE00004BF00207047012202701D48008888846E +:107AF000104670471A49488070471849184B8A8CBD +:107B00005B889A4206D191F82220002A1EBF0160AC +:107B100001207047002070471048114A818C52881C +:107B2000914209D14FF6FF71818410F8221F19B1DB +:107B30000021017001207047002070470748084A63 +:107B4000818C5288914205D190F8220000281CBFF8 +:107B50000020704701207047420C00201C0C0020C0 +:107B6000C80000207047574A012340B1012818BFC0 +:107B700070471370086890608888908170475370D0 +:107B80000868C2F802008888D08070474D4A10B15A +:107B9000012807D00EE0507860B1D2F802000860EA +:107BA000D08804E0107828B19068086090898880B7 +:107BB0000120704700207047424910B1012803D0CE +:107BC00006E0487810B903E0087808B10120704752 +:107BD0000020704730B58DB00C4605460D2104A835 +:107BE0000DF06DFDE0788DF81F0020798DF81E00F6 +:107BF00060798DF81D002868009068680190A86879 +:107C00000290E868039068460CF062FB20789DF8CB +:107C10002F1088420CD160789DF82E10884207D131 +:107C2000A0789DF82D10884202BF01200DB030BD14 +:107C300000200DB030BD30B50C4605468DB04FF07C +:107C4000030104F1030012B1FEF7F8F801E0FEF7BA +:107C500014F960790D2120F0C00040F040006071FF +:107C600004A80DF02CFDE0788DF81F0020798DF828 +:107C70001E0060798DF81D002868009068680190EA +:107C8000A8680290E868039068460CF021FB9DF814 +:107C90002F0020709DF82E0060709DF82D00A070C0 +:107CA0000DB030BD10B5002904464FF0060102D0DA +:107CB000FEF7C4F801E0FEF7E0F8607920F0C000BC +:107CC000607110BDCC000020FE48406870472DE96F +:107CD000F0410F46064601461446012005F0F8FA29 +:107CE000054696F85500FFF7E5FD4AF2B121084434 +:107CF0004FF47A71B0FBF1F0718840F27122514378 +:107D0000C0EB4100001BA0F2653403F03DF80028F1 +:107D100018BF1E3CAF4234BF28463846A04203D2AB +:107D2000AF422CBF3C462C467462BDE8F0812DE981 +:107D3000FF4F95B0044690F8550089461190DDE953 +:107D4000171008431390E048002605780C2D28BF33 +:107D5000FFDFDE4F37F8158094F874510C2D28BFE3 +:107D6000FFDFDA4830F8150040441FFA80F894F835 +:107D700065000D280CBF012000200C9017980028EA +:107D800004BF94F8140103282BD10C9848B3B4F81D +:107D90009601484525D1D4F81C01C4F80801608833 +:107DA00040F2E2414843C4F80C01B4F86201B4F86F +:107DB000EE100844C4F81001204602F0EFFFB4F8BA +:107DC0009A01E08294F898016075B4F89C01608093 +:107DD000B4F89E01A080B4F8A001E080022084F8ED +:107DE0001401D4F86C011090D4F868010F90B4F825 +:107DF000EE70B4F86001D4F85C110891179921B1C4 +:107E000094F8281151B100F0DDB804F1E8010391B4 +:107E100074310D9104F5A475091D07E004F59E71F8 +:107E20000391091D0D9104F59675091D0E91B4F885 +:107E30005810A9EB0000A9EB01010FFA80FA0FFA24 +:107E400081FBBAF1000F05DAD4F85801089001203F +:107E5000DA461390002002909B480079E8B3F3F7CC +:107E600039FFD0B3B4F80001022836D394F81401D6 +:107E7000022832D094F82B0178BB94F87481B8F1C1 +:107E80000C0F28BFFFDF914830F8180000F5C860DC +:107E90001FFA80F894F8140101287DD0618840F21F +:107EA000E24041430020B8F1000F05D0884808FBAC +:107EB00001F1B1FBF0F0401C07EB0B01A1EB0A0252 +:107EC000D4F81C1180B2431A029902FB03110291EB +:107ED000C4F81C01012084F82B0194F81401002837 +:107EE00074D0012800F04682022800F09481032813 +:107EF00018BFFFDF00F078820298311A0898FCF76B +:107F0000BCFB0D99012640F2712208600E98A0F882 +:107F10000090002028702E710D980068A86061887C +:107F2000D4F81C015143C0EB41006749A0F2353041 +:107F30000862C969814287BF03990860039801609C +:107F40000398616A0068084400F2A510E86002F036 +:107F50001BFF10B1E8681E30E8606E71B4F8D800FD +:107F6000A0EB090000B20028C4BF032068710C9880 +:107F70000028189800F09A82D8B100BFB4F8001118 +:107F800000290CBF0020B4F80201A4F8020194F803 +:107F90000421401C504300E019E0884209D268796E +:107FA000401E002805DD6E71B4F80201401CA4F8E3 +:107FB00002011798002800F0A18294F828010028F7 +:107FC00000F0988219B00220BDE8F08F65E094F8C7 +:107FD0006800032857D03B4894F8551090F83000BB +:107FE00005F023FBE18A40F27122514300EB41018D +:107FF0000020D4F80C21B8F1000F06D0344808FB5B +:1080000002F2B2FBF0F000F10100D4F80831D4F82C +:108010001021A0EB030C029BC4F8080102FB0C33F7 +:108020004FF0000007D000BF294808FB01F1B1FB69 +:10803000F0F000F10100D4F81811C4F81801A0EB19 +:1080400001011944608840F2E24300FB03F34FF062 +:10805000000006D01E4808FB03F3B3FBF0F000F16C +:10806000010007EB0B03A3EB0A03A3EB0202D4F816 +:108070001C31A2F10102A0EB030302FB03110291E8 +:10808000C4F81C0126E7E18A40F27122D4F80C0101 +:1080900001FB02F100EB4101AAE70F98002808BF9D +:1080A000FFDF94F85510074890F8300005F0BDFA4E +:1080B0000790E18A40F271204143079800EB4101AB +:1080C000002007E0640C0020DC000020585C020067 +:1080D00040420F00B8F1000F07D000BFFF4808FB77 +:1080E00001F1B1FBF0F000F10100C4F81801618862 +:1080F00040F2E24001FB00F14FF0000006D0F748EB +:1081000008FB01F1B1FBF0F000F10100C4F81C0123 +:1081100086B221464FF00100D4F828A005F0D8F827 +:10812000074694F85500FFF7C5FB4AF2B12B5844B7 +:108130004FF47A78B0FBF8F0618840F27122514335 +:10814000C0EB4100801BA0F2653602F01DFE002846 +:1081500018BF1E3EBA4534BF38465046B04203D21F +:10816000BA452CBF56463E46666294F85500FFF766 +:10817000DBFB00F2E140B0FBF8F10F980E1894F829 +:108180005500FFF7D1FB074694F85500FFF792FB27 +:1081900038444AF2AB310844B0FBF8F1E28A40F2CD +:1081A000712042430798D4F8187100EB4200401A3E +:1081B000C01B3044A0F12006617D40F2E24011FB7B +:1081C00000FA94F85500009010F00C0F0ABF0098C8 +:1081D0004EF62830FFF76EFB5844B0FBF8F000EB8A +:1081E000470000EB0A070098FFF752FB384400F104 +:1081F0006201BB48C16194F85500FFF795FB00F29E +:10820000E140B0FBF8F10F980844301AB0F53D7F1B +:1082100098BFFFDF70E6E18A40F27122D4F80C01CA +:10822000514300EB41010020B8F1000F07D000BF1F +:10823000AA4808FB01F1B1FBF0F000F10100C4F81D +:108240001801608840F2E24100FB01F14FF00000AC +:1082500006D0A24808FB01F1B1FBF0F000F10100EB +:10826000C4F81C0186B221464FF00100D4F828A0C2 +:1082700005F02EF8804694F85500FFF71BFB4AF2F4 +:10828000B12B00EB0B014FF47A70B1FBF0F0618879 +:1082900040F271225143C0EB4100801BA0F26536D1 +:1082A00002F072FD002818BF1E3EC24534BF404692 +:1082B0005046B04203D2C2452CBF5646464666627F +:1082C0000FBB1898F8B194F855603046FFF7F2FAF2 +:1082D00000EB0B014FF47A70B1FBF0F0D4F81811F9 +:1082E000E38A084440F27122D4F80C115A4301EB9E +:1082F00042010F1A3046FFF7CBFA1099081A38449A +:10830000A0F120060AE0E18A40F27122D4F80C01C3 +:10831000514300EB4100D4F81811461AD4F810214B +:10832000D4F80811D4F8180101FB020A607D40F26C +:10833000E24110FB01F894F8557017F00C0F0ABFDA +:1083400038464EF62830FFF7B5FA00EB0B014FF434 +:108350007A70B1FBF0F000EB4A0080443846FFF73A +:1083600097FA404400F160015D48C161012084F842 +:108370001401C1E5618840F271225143D4F81C0117 +:10838000D4F81021C0EB410101FB0AF607EB0B0109 +:10839000891AD4F808C1D4F81831491E0CFB0232EE +:1083A00001FB002A607D40F2E24110FB01F894F8E5 +:1083B000557017F00C0F0ABF38464EF62830FFF7FD +:1083C00079FA4AF2B12101444FF47A70B1FBF0F02E +:1083D00000EB4A0080443846FFF75AFA404400F167 +:1083E00060013F48C16187E5628840F27121D4F89D +:1083F0001C015143C0EB410000FB0AF694F86400F5 +:1084000024281CBF94F8650024280BD1B4F89601E9 +:10841000A9EB000000B2002804DB94F899010028C1 +:1084200018BF1190139800B3FFB9109800281ABF15 +:108430000F980028FFDF94F8550010F00C0F14BFC0 +:108440004EF62830FFF736FA4AF2B12101444FF4D4 +:108450007A70B1FBF0F0361A94F85500FFF718FA6D +:108460001099081A3044A0F12006D4F81C1107EB2B +:108470000B0000FB01F7119810F00C0F0ABF1198C8 +:108480004EF62830FFF716FA4AF2B12101444FF4B4 +:108490007A70B1FBF0F000EB47071198FFF7F8F99D +:1084A000384400F160010E48C16125E500287FF4E1 +:1084B00065AD94F8140100283FF47BAD618840F26B +:1084C0007122D4F81C015143C0EB4101284604F04D +:1084D000CFFD0004000C3FF46CAD03E040420F0000 +:1084E000DC0000202299002918BF0880012019B063 +:1084F000BDE8F08F94F86401FCF723FF94F8640161 +:108500002946FCF703FE20B1179880F0010084F89B +:10851000290119B00020BDE8F08F70B5FE4C607ADB +:1085200000281CBF002070BD94F8340038B1A16B46 +:10853000606A884203D9F7F7BEF8002070BDA06AD0 +:10854000E8B1F6F750F90546F5F7C4FF284442F2C2 +:1085500010714618FCF790FB05462946E06AFDF7C6 +:10856000A4F8E562A16A8219914224BF081AA062A8 +:1085700005D20120A062F7F79EF8002070BD01200F +:1085800070BDF8B5E44C02460025E44E6168606AAF +:10859000052A4ED2DFE802F003353A3D4400A07AC6 +:1085A000002760B101216846FDF748FC9DF80000F6 +:1085B00042F210710002B0FBF1F201FB1207F6F774 +:1085C00012F9C119A069FCF758F8A06125740320BD +:1085D00060757079002814BF012003202075607A2F +:1085E00038B9207B04F11001FCF790FD002808BF8A +:1085F000FFDF2584FCF74AFAB079BDE8F840EAF7D6 +:108600008BBCBDE8F840002100F0C7BDC1F868018F +:10861000F8BDD1F86801BDE8F840012100F0BDBD0A +:1086200084F83450FCF732FAB079BDE8F840EAF744 +:1086300073BCFFDFF8BD2DE9F04FDFF8DC820446A4 +:1086400083B098F800008B4601270025B34E4FF009 +:108650000209032804BF98F80C00A04240F0E7800C +:10866000D8F80400B06198F80000032818BFFFDFB5 +:108670000324BBF1080F80F0D680DFE80BF0040F75 +:1086800031312CD4D4CBC8F82450F6F783FC002821 +:1086900018BFFFDFB47003B0BDE8F08FF5F71AFF25 +:1086A0000446D8F81C00A04228BFC8F81C4005D2D8 +:1086B000201AFDF72EF8C8F81C4038B1F6F7E3FF92 +:1086C000002818BFFFDF03B0BDE8F08F03B0002023 +:1086D000BDE8F04F55E703B0BDE8F04FFEF7BCBD75 +:1086E00070794FF0010A002814BF0120032088F898 +:1086F000140088F8105098F8340042F2107B68B1EA +:108700004FF47A71D8F81800FBF7B7FFC8F81800D3 +:10871000002108F1100004F0ABFC1CE001216846C8 +:10872000FDF782FB9DF800000002B0FBFBF10BFBA4 +:10873000110AF6F758F800EB0A018A46D8F8180033 +:10874000FBF79BFFC8F81800514608F1100004F031 +:108750008FFC00F1010AB8F82000411C0A293CBF37 +:108760005044A8F82000D8F8040038B1B8F8200028 +:10877000401C0A2828BF88F8159001D288F81540B7 +:1087800098F8090070BB98F8340040B1D8F8381058 +:10879000D8F82400884202D9F6F78DFF22E0D8F8F5 +:1087A000280058B3F6F71FF80446F5F793FE204467 +:1087B00000EB0B09FCF760FA04462146D8F82C00C0 +:1087C000FCF773FFC8F82C40D8F8281000EB09021A +:1087D000914224BF081AC8F828000FD2C8F82870A0 +:1087E000F6F769FF98F80C00FCF727FA88F80050B4 +:1087F000B07903B0BDE8F04FEAF78EBB98F80C00F3 +:1088000008F11001FCF782FC002808BFFFDF03B06D +:10881000BDE8F08F98F80C00FCF70FFA88F80050CC +:1088200003B0BDE8F08FFFDF03B0BDE8F08F202C70 +:1088300028BFFFDFDFF8E880072138F81400FAF7D7 +:10884000D9F85FEA000A08BFFFDF202C28BFFFDF4E +:1088500038F81400BAF80010884218BFFFDF5446F9 +:10886000C6F818A04FF0200ABBF1080F80F04A812B +:10887000DFE80BF0049FA9A9A2F4F3F2C4F8685151 +:108880003581C4F86C5194F8290138B9FCF7F4F932 +:10889000D4F83411FCF709FF00281BDCB4F82611CA +:1088A000B4F85800814206D1B4F8DC10081AA4F8D4 +:1088B000DE00204605E0081AA4F8DE00B4F8261110 +:1088C0002046A4F85810D4F85011C4F83411C0F858 +:1088D00058111DE0B4F82411B4F85800081AA4F88F +:1088E000DE00B4F824112046A4F85810D4F834114E +:1088F000C4F85011C4F85811D4F83C11C4F8E81069 +:10890000D4F84011C4F85C11B4F84411A4F8601113 +:1089100002F020F906E00000640C0020DC000020DA +:10892000A00C0020FCF782F9804694F85500FEF771 +:10893000C1FF4AF2B12108444FF47A71B0FBF1F063 +:10894000D4F81C1140F27122084461885143C0EBF5 +:108950004100A0F1300AB8F1B70F98BF4FF0B70847 +:108960002146012004F0B4FC4044AAEB0000A0F230 +:108970001A38A2462146012004F0AAFC00F19C010D +:10898000DAF82400884288BF451AC6F810804545A9 +:1089900028BF4546F560D4F85401A0F2A5107061D7 +:1089A000FCF750FE84F8287186F8029003B0BDE809 +:1089B000F08F02F0E4F901E0FEF74EFC84F8287134 +:1089C00003B0BDE8F08FFCF757F9D4F85821014601 +:1089D0001046FCF76AFE48B1628840F27123D4F871 +:1089E0001C115A43C1EB4201B0FBF1F094F8651041 +:1089F0000D290FD0B4F85820B4F826111318994255 +:108A0000AEBF481C401C1044A4F8260194F82A016B +:108A100078B905E0B4F82601401CA4F8260108E066 +:108A2000B4F82601B4F8DC10884204BF401CA4F856 +:108A30002601B4F862010DF1040B401CA4F8620198 +:108A4000B4F88000B4F87E10401AB4F85810401EF4 +:108A500008441FFA80F912E046E03EE052E00023AD +:108A60001A462046CDF800B0FFF761F9002804BF90 +:108A700003B0BDE8F08F012818BFFFDF25D0B4F8A0 +:108A80002611A9EB010000B20028E8DA082084F8DA +:108A9000740084F87370204601F01EFE84F81451AF +:108AA00094F864514FF6FF77202D00D3FFDF28F8AC +:108AB000157094F86401FCF7C0F884F864A1B079EB +:108AC00003B0BDE8F04FEAF727BAB4F82601BDF8C5 +:108AD00004100844A4F82601D1E7FEF75DFA03B0BC +:108AE000BDE8F04FFEF7B8BB94F81401042818BF96 +:108AF000FFDF84F8145194F864514FF6FF77202D6E +:108B0000D5D3D3E7FFDF03B0BDE8F08F10B5FA4C43 +:108B1000207850B101206072F6F7E5FD2078032837 +:108B200005D0207A002808BF10BD0C2010BD207B86 +:108B3000FCF707FC207BFCF752FE207BFCF77DF85E +:108B4000002808BFFFDF0020207010BD2DE9F04F86 +:108B5000E94F83B0387801244FF0000840B17C72AF +:108B60000120F6F7C0FD3878032818BF387A0DD0F9 +:108B7000DFF8889389F8034069460720F9F7C3FEB8 +:108B8000002818BFFFDF4FF6FF7440E0387BFCF78A +:108B9000D8FB387BFCF723FE387BFCF74EF8002827 +:108BA00008BFFFDF87F80080E2E7029800281CBFBB +:108BB00090F8141100292AD00088A0421CBFDFF8C9 +:108BC00040A34FF0200B3AD00721F9F713FF040020 +:108BD00008BFFFDF94F86401FCF701FE84F81481FC +:108BE00094F864514FF6FF76202D28BFFFDF2AF856 +:108BF000156094F86401FCF720F884F864B16946C4 +:108C00000720F9F780FE002818BFFFDF12E0684652 +:108C1000F9F757FE0028C8D011E0029800281CBFC1 +:108C200090F81411002905D00088A0F57F41FF3984 +:108C3000CAD104E06846F9F744FE0028EDD089F86F +:108C4000038087F8348087F80B8003B00020BDE8EC +:108C5000F08F70B50446AB4890F80004AA4D400967 +:108C600095F800144909884218BFFFDF95F8140DE4 +:108C70004009A64991F800144909884218BFFFDF4E +:108C80009E49002001228C7188700A7048700A7118 +:108C9000C870487198490870BDE8704056E7974918 +:108CA000087070472DE9F843934C064688462078B3 +:108CB000002867D19648FBF764FF2073202861D015 +:108CC000032766602770002565722572AEB1012109 +:108CD00006F58270FDF7D1F80620F9F733FE8146DC +:108CE0000720F9F72FFE96F804114844B1FBF0F283 +:108CF00000FB1210401C86F80401FBF797FF40F2BE +:108D0000F651884238BF40F2F65000F59F7086B2A7 +:108D1000F5F7E0FBE061F5F766FD4FF0010900288B +:108D200033D084F80A90FBF7A7FF814601216846FB +:108D3000FDF77AF89DF8000042F210710002B0FBD6 +:108D4000F1F201FB120081194846FBF796FCA06185 +:108D5000C4E90A8969484079002814BF012003202A +:108D6000207567752574207B04F11001FCF7CEF99E +:108D7000002808BFFFDF25840020F6F7B4FC0020A0 +:108D8000BDE8F8830C20BDE8F883FBF775FF31469A +:108D9000FBF773FCA061A57284F83490A8F28B50A5 +:108DA000A562A063D6E7554948717047534948709A +:108DB00070475249087170472DE9F0414F4C064603 +:108DC0002089401C2081D4E903516078D6F868716D +:108DD00020B13A46284604F076F90546E068854217 +:108DE00005D06169281A08446061FCF72BFCE56036 +:108DF000AF4209D896F81401012805D0E078002880 +:108E000004BF0120BDE8F0810020BDE8F08110B56D +:108E100004460846FEF74EFD4AF2B12108444FF4DD +:108E20007A71B0FBF1F040F2E241614300F235307B +:108E300081428CBF081A002010BD70B5044682B074 +:108E4000002084F8280194F8E600002807BF94F871 +:108E50001401032802B070BDFBF70EFFD4F85821AF +:108E600001461046FCF721FC0028DCBF02B070BDB3 +:108E7000628840F27123D4F81C115A43C1EB4201BD +:108E8000B0FBF1F0B4F85810401C0844A4F82401D9 +:108E9000B4F8DC00B4F82421801A00B20028DCBF4A +:108EA00002B070BD012084F82A01B4F88000B4F843 +:108EB0007E2001AE801A401E084485B212E0009662 +:108EC000B4F82411002301222046FEF730FF0028C9 +:108ED00004BF02B070BD01281CD0022812BFFFDF02 +:108EE00002B070BDB4F82401281A00B20028BCBF3B +:108EF00002B070BDE3E70000640C0020DC0000203D +:108F0000A00C002001E000E00BE000E019E000E030 +:108F100037860100B4F82401BDF804100844A4F811 +:108F20002401DFE7F8B50422002506295BD2DFE83B +:108F300001F007260319192A044680F8142107E0D6 +:108F40000446BD48C078002818BF84F814210AD010 +:108F5000FBF79CFDA4F86251B4F85800A4F8260170 +:108F600084F82A51F8BD0095B4F8DC1001230022E2 +:108F70002046FEF7DCFE002818BFFFDFE8E70321EC +:108F800080F81411F8BD0646876AB0F81C01314616 +:108F900085B2012004F09CF9044696F85500FEF7CE +:108FA00089FC4AF2B12108444FF47A71B0FBF1F028 +:108FB000718840F271225143C0EB4100401BA0F286 +:108FC000653501F0E1FE002818BF1E3DA74234BF01 +:108FD00020463846A84228BF2C4602D2A74228BFC6 +:108FE0003C467462F8BDFFDFF8BD2DE9F05F924E9C +:108FF000B178022906BF31890029BDE8F09FB46924 +:10900000C4F86C0194F85500FEF742FCD4F86C11DA +:10901000081AF1680144F160316908443061B469AB +:1090200094F82B01002808BFBDE8F09F94F81401C4 +:10903000032818BFBDE8F09F94F8555036780C2EE1 +:1090400028BFFFDF7D4F37F8168094F874610C2E2F +:1090500028BFFFDF37F81600404494F8748186B2C9 +:10906000B8F10C0F28BFFFDF37F8180000F5C86013 +:109070001FFA80F82846FEF70BFCD4F86C114FF06D +:10908000000A0F1A15F00C0F0ABF28464EF62830BA +:10909000FEF710FC4FF47A7900F2E730B0FBF9F0FC +:1090A0003F1A2846FEF7F4FBD4F8E81015F00C0F31 +:1090B000A1EB000B0ABF28464EF62830FEF7FAFB5C +:1090C0004AF2B1210844B0FBF9F0ABEB0000A0F18B +:1090D00060017143B1FBF8F1292202EB50006031CD +:1090E000A0EB510200EB5100BA4201D8B84201D8BE +:1090F000F2F794FE608840F2E241414300202EB135 +:1091000006FB01F04E49B0FBF1F0401CC4F81C0115 +:1091100084F82BA1BDE8F09F70B50546464890F84D +:1091200002C0BCF1020F07BF806900F5B474454866 +:1091300000F12404002904BF256070BD4FF47A7645 +:1091400001290DD002291CBFFFDF70BD1046FEF7BC +:10915000CAFB00F2E140B0FBF6F0281A206070BDB7 +:109160001846FEF7E1FB00F2E140B0FBF6F0281AEA +:10917000206070BD3348007800281CBF0020704775 +:1091800010B50720F9F7D0FB80F0010010BD2D4885 +:109190000078002818BF012070472DE9F843294CBA +:1091A0000025814684F83450D4F8188084F83010B3 +:1091B000E5722570012727722946606803F0CDFA11 +:1091C0006168C1F85881267B81F86461C1F86891B3 +:1091D000C1F85C81B1F80080202E28BFFFDF1A485B +:1091E00020F81680646884F814510023A4F86051B4 +:1091F0001A46194620460095FEF799FD002818BF2B +:10920000FFDFC4F81051C4F8085184F81471A4F8B1 +:109210002651A4F8245184F82A51B4F85800401E6D +:10922000A4F85800A4F86251FBF730FC024880799A +:10923000BDE8F843E9F770BEDC000020585C02008E +:1092400040420F00640C0020A00C0020012804D034 +:10925000022805D0032808D105E0012907D004E041 +:10926000022904D001E0042901D000207047012028 +:1092700070472DE9F0410E46044604F07CFD05469A +:10928000204604F07CFD044604F097F8FE4F0100F0 +:1092900015D0386990F854208A4210D090F8AC313B +:1092A0001BB190F8AE3123421FD02EB990F8513047 +:1092B000234201D18A4218D890F8AC01A8B12846BF +:1092C00004F07BF870B1396991F85520824209D0D9 +:1092D00091F8AC0118B191F8AF01284205D091F88E +:1092E000AC0110B10120BDE8F0810020FBE730B5F2 +:1092F000E54C85B0E06900285DD0142168460CF08B +:10930000DEF9206990F85500FEF7D4FA4FF47A712F +:1093100000F5FA70B0FBF1F5206990F85500FEF702 +:10932000B7FA2844ADF8060020690188ADF80010AE +:10933000B0F85810ADF804104188ADF8021090F85C +:109340008E0130B1A069C11C039104F0F5FB8DF8CA +:109350001000206990F88D018DF80800E1696846D9 +:1093600088472069002180F88E1180F88D110399BB +:10937000002920D090F88C1100291CD190F864109D +:10938000272918D09DF81010039A002913D01378BC +:109390000124FF2B11D0072B0DD102290BD15178BD +:1093A000FF2908D180F88C410399C0F890119DF8ED +:1093B000101080F88F1105B030BD1B29F2D9FAE7E3 +:1093C00070B5B14C206990F865001B2800D0FFDF14 +:1093D0002069002580F88D5090F8C00100B1FFDFB2 +:1093E000206990F88E1041B180F88E500188A0F865 +:1093F000C41180F8C2510E2108E00188A0F8C41100 +:1094000080F8C251012180F8C6110D2180F8C011E9 +:109410000088F9F721FCF9F7B9F82079E9F77CFD24 +:10942000206980F8655070BD70B5974CA0798007B1 +:109430002CD5A078002829D162692046D37801690B +:109440000D2B01F158005FD00DDCA3F102034FF0AA +:1094500001050B2B19D2DFE803F01A1844506127DD +:10946000182C183A6400152B6FD008DC112B4BD048 +:10947000122B5AD0132B62D0142B06D166E0162B78 +:1094800071D0172B70D0FF2B6FD0FFDF70BD91F81C +:1094900067200123194603F081FD0028F6D12169D8 +:1094A000082081F8670070BD1079BDE8704001F0B8 +:1094B00008BD91F86600C00700D1FFDF01F0C0FCD5 +:1094C000206910F8661F21F00101017070BD91F84C +:1094D0006500102800D0FFDF2069112180F88D5031 +:1094E00008E091F86500142800D0FFDF20691521FD +:1094F00080F88D5080F8651070BD91F865001528D2 +:1095000000D0FFDF172005E091F86500152800D096 +:10951000FFDF1920216981F8650070BDBDE870404A +:109520004EE7BDE8704001F0A0BC91F86420012333 +:10953000002103F033FD00B9FFDF0E200FE011F82A +:10954000660F20F0040008701DE00FE091F8642021 +:109550000123002103F022FD00B9FFDF1C20216957 +:1095600081F8640070BD12E01BE022E091F8660013 +:10957000C0F30110012800D0FFDF206910F8661F3A +:1095800021F010010170BDE8704001F059BC91F864 +:1095900064200123002103F001FD00B9FFDF1F203B +:1095A000DDE791F86500212801D000B1FFDF22201E +:1095B000B0E7BDE8704001F04FBC3348016991F855 +:1095C0006620130702D501218170704742F008021E +:1095D00081F866208069C07881F8C90001F027BC55 +:1095E00010B5294C21690A88A1F8042281F80202E9 +:1095F00091F8540001F009FC216981F8060291F804 +:10960000550001F002FC216981F80702012081F870 +:109610000002002081F8AC012079BDE81040E9F794 +:109620007BBCF0B4184C206900F5DA730188198509 +:10963000018E5985818E9985018FB0F84420914221 +:1096400000D31146D985828FB0F846108A4200D2E5 +:109650001146198690F855204FF0010512F00C0FB5 +:109660004FF4296203D0914200D81146198690F830 +:10967000540010F00C0F04D0988D904200D902468F +:109680009A8583F8265001E0000100202079F0BC83 +:10969000E9F742BC10B5F84C01230921206990F884 +:1096A0006420583003F07AFC38B12169002001F8B9 +:1096B0007C0F087301F8180C10BD0120A07010BDBC +:1096C00070B5ED4D012329462869896990F8642019 +:1096D00009790E2A01D1122903D000241C2A03D0B3 +:1096E00004E0BDE87040D5E7142902D0202A08D054 +:1096F00009E080F8644080F88840BDE8704001F0DF +:1097000003BC162906D0262A01D1162902D0172912 +:1097100009D00CE000F8644F80F8244040782128FC +:109720000CD01A2017E090F86520222A07D0EA69A9 +:10973000002A03D0FF2901D180F88E3112E780F88A +:10974000654001F07DFB286980F87D4090F8AC0110 +:109750000028F3D00020BDE8704041E72DE9F84330 +:10976000C54C206990F86410202909D05FF00007EB +:1097700090F86510222905D07FB300F1640503E05D +:109780000127F5E700F1650510F8961F41F0040187 +:109790000170A06904F0FBFA4FF00108002608B33D +:1097A0003946A069FFF765FDE0B16A46A169206905 +:1097B00003F012FE90B3A06904F0E7FA2169A1F862 +:1097C0009601B1F8581001F014FB40B3206928212C +:1097D00080F8741080F8738058E0FFE70220A070D2 +:1097E000BDE8F883206990F8AC0110B11E20FFF7A6 +:1097F000F7FEAFB1A0692169C07881F8CA0008FA04 +:1098000000F1C1F3006000B9FFDF20690A2180F890 +:10981000641090F8880040B9FFDF06E009E02AE014 +:109820002E7001F00DFBFFF7C8FE206980F87D6007 +:10983000D6E7226992F8AC0170B1B2F8583092F8CC +:109840005410B2F8B00102F5CB7203F0B7FE68B164 +:109850002169252081F86400206900F1650180F804 +:109860007D608D4212D180F865600FE00020FFF727 +:10987000B7FE2E70F0E720699DF8001080F898116F +:109880009DF8011080F8991124202870206900F1BA +:1098900065018D4203D1BDE8F84301F0D1BA80F8EB +:1098A00088609DE770B5744C01230B21206990F806 +:1098B0006520583003F072FB202650BB206901233D +:1098C000002190F86520583003F068FB0125F0B1C5 +:1098D000206990F8640024281BD0A06904F035FAB0 +:1098E000C8B1206990F8961041F0040180F89610F4 +:1098F000A1694A7902F0070280F85120097901F044 +:10990000070180F8501090F8AD311BBB06E0A57040 +:1099100028E6A67026E6BDE870404EE690F8AC3129 +:10992000C3B900F154035E788E4205D11978914293 +:1099300002D180F87D500DE000F5FD710D700288B8 +:109940004A8090F850200A7190F8510048712079AF +:10995000E9F7E2FA2169212081F86500BDE870404D +:1099600001F065BA70B54448006990F84E20448E05 +:10997000C38E418FB0F84050022A23D0A94200D3B1 +:1099800029460186C18FB0F84220914200D311468A +:109990008186018FB0F84420914200D31146418660 +:1099A000818FB0F84620914200D31146C186418E86 +:1099B000A14200D90C464486C18E994200D90B467B +:1099C000C386CFE5028E914200D31146C68F828EA8 +:1099D000964200D23246A94200D329460186B0F809 +:1099E00042108A4200D30A468286002180F84E1037 +:1099F000CFE770B5204C206990F8660010F0300F6A +:109A000004D0A07840F00100A070ABE5A06904F09C +:109A100081F948B32569A06904F078F92887256998 +:109A2000A06904F06FF968872569A06904F070F9EE +:109A3000A8872569A06904F067F9E887A0794FF045 +:109A40000102800703D56069C07814280FD020690F +:109A500090F864101C290AD090F84E10012910D0FB +:109A600090F8A31169B909E0BDE87040A5E5206947 +:109A700080F84E2005E000000001002090F8A211BF +:109A800031B1206910F8661F41F01001017016E035 +:109A900090F8661041F0200180F8661000F5DA7148 +:109AA00003888B86038FCB86438F0B87838F4B87EF +:109AB000C08F888781F832202079E9F72DFABDE838 +:109AC000704001F0B4B970B5FE4C206990F8661092 +:109AD000890707D590F8642001230821583003F046 +:109AE0005DFAE8B1206990F89000800712D4A0696F +:109AF00004F0ECF8216981F89100A06930F8052F95 +:109B0000A1F892204088A1F8940011F8900F40F03D +:109B100002000870206990F89010C90703D00FE088 +:109B20000120A0701EE590F86600800700D5FFDFD9 +:109B3000206910F8661F41F00201017001F077F909 +:109B40002069002590F86410062906D180F8645039 +:109B500080F888502079E9F7DFF9206990F89411AE +:109B60000429DFD180F894512079E9F7D5F92069EB +:109B700090F864100029D5D180F88850F2E470B5CF +:109B8000D04C01230021206990F86520583003F063 +:109B900005FA012578B9206990F86520122A0AD0C3 +:109BA00001230521583003F0F9F910B10820A07005 +:109BB000D8E4A570D6E4206990F88E0008B901F0C9 +:109BC00036F92169A069F03104F061F82169A069D2 +:109BD000C03104F067F8206990F8C80100B1FFDFD8 +:109BE00021690888A1F8CA0101F5E671A06904F0AD +:109BF0003CF82169A06901F5EA7104F03EF820699A +:109C000080F8C851142180F865102079BDE87040B3 +:109C1000E9F782B970B5AB4C01230021206990F8B7 +:109C20006520583003F0BAF90125A8B1A06903F006 +:109C3000E8FF98B1A0692169B0F80D00A1F896017C +:109C4000B1F8581001F0D5F858B12069282180F8F2 +:109C5000741080F8735085E4A57083E4BDE870400B +:109C6000ABE4A0692169027981F89821B0F8052058 +:109C7000A1F89A2103F0B8FF2169A1F89C01A0691D +:109C800003F0B5FF2169A1F89E01A06903F0B6FFBA +:109C90002169A1F8A0010D2081F8650062E47CB57E +:109CA000884CA079C00738D0A06901230521C57868 +:109CB000206990F86520583003F070F968B1AD1E46 +:109CC0000A2D06D2DFE805F0090905050909050591 +:109CD0000909A07840F00800A070A07800281CD1E5 +:109CE000A06903F057FF00287AD0A0690226C57842 +:109CF0001DB1012D01D0162D18D1206990F86400F6 +:109D000003F034F990B1206990F864101F290DD048 +:109D1000202903D0162D16D0A6707CBD262180F8F0 +:109D20006410162D02D02A20FFF75AFC0C2D58D0B3 +:109D30000CDC0C2D54D2DFE805F033301D44A7A70E +:109D4000479E57A736392020A0707CBD0120152DD5 +:109D500075D008DC112D73D0122D69D0132D64D06D +:109D6000142D3DD178E0162D7CD0182D7DD0FF2DFF +:109D700036D183E020690123194690F867205830D6 +:109D800003F00CF9F8B9A06903F068FF216981F8C4 +:109D90007A01072081F8670078E001F03CF975E06E +:109DA000FFF738FF72E001F016F96FE0206990F8D4 +:109DB0006510112901D0A67068E0122180F86510A5 +:109DC00064E0FFF7DCFE61E0206990F86500172889 +:109DD000F1D101F035F821691B2081F8650055E0CB +:109DE00052E0FFF770FE51E0206990F86600C0076E +:109DF00003D0A07840F001001FE06946A06903F09D +:109E00006CFF9DF8000000F02501206900F8961F06 +:109E10009DF8011001F04901417001F008F8206936 +:109E200010F8661F41F0010114E0FFF733FC2DE04C +:109E3000216991F86610490705D5A07026E00EE06B +:109E400016E00FE011E000F0F2FF206910F8661F45 +:109E500041F00401017019E0FFF7CBFD16E001F0BD +:109E600087F813E0FFF71EFD10E0FFF777FC0DE029 +:109E700001F05DF80AE0FFF723FC07E0E16919B1A2 +:109E8000216981F88E0101E0FFF797FB2069F0E975 +:109E90002A12491C42F10002C0E900127CBD70B5D3 +:109EA000084CA07900074DD5A07800284AD1206938 +:109EB00090F8CC00FE2800D1FFDF2069FE2180F859 +:109EC000CC1001E00001002090F865100025192950 +:109ED00006D180F88D5000F0B3FF206980F86550FE +:109EE000206990F864101F2902D0272921D119E098 +:109EF00090F8650003F03AF878B120692621012333 +:109F000080F8641090F865200B21583003F046F873 +:109F100078B92A20FFF764FB0BE02169202081F843 +:109F2000640006E0012180F88D1180F8645080F80B +:109F30008850206990F86710082903D10221217008 +:109F400080F8CC10E4E4F949096991F898210AB93C +:109F500091F8542081F8542091F899210AB991F888 +:109F6000552081F85520002802D00020FFF738BB8B +:109F7000704770B5ED4C06460D46206990F8CC0050 +:109F8000FE2800D0FFDF2269002082F8CC6015B1E6 +:109F9000A2F88A00BCE422F8840F01201071B7E413 +:109FA00070B5E24C01230021206990F864205830FC +:109FB00002F0F4FF00287AD0206990F8A21111B1C4 +:109FC00090F8A31139B190F8AC1100296ED090F837 +:109FD000AD1111B36AE090F8651024291BD090F8F8 +:109FE0006410242917D0002300F5CC7200F5D1713C +:109FF00003F084F82169002081F8A20101461420B1 +:10A00000FFF7B7FF206930F8421FA0F88C10818855 +:10A01000A0F88E1050E00123E6E790F865200123B8 +:10A020000B21583002F0BAFF68BB206990F8540049 +:10A0300000F0EBFE0646206990F8550000F0E5FEC2 +:10A040000546206990F8AE113046FFF7FFF8D8B109 +:10A05000206990F8AF112846FFF7F8F8A0B12269FF +:10A06000B2F8583092F85410B2F8B00102F5CB7241 +:10A0700003F0A4FA20B12169252081F864001BE0D7 +:10A080000020FFF7ADFA11E020690123032190F8C9 +:10A090006520583002F082FF40B920690123022177 +:10A0A00090F86520583002F079FF08B100202FE4C5 +:10A0B00000211620FFF75DFF012029E410B5E8BB61 +:10A0C0009A4C206990F86610CA0702D00121092035 +:10A0D00052E08A070AD501210C20FFF74AFF2069C8 +:10A0E00010F8901F41F00101017047E04A0702D5C6 +:10A0F0000121132040E00A0705D510F8C91F41715E +:10A100000121072038E011F0300F3BD090F8A31167 +:10A11000A1B990F8A211E1B190F8651024292FD0CF +:10A1200090F8641024292BD05FF0000300F5CC7266 +:10A1300000F5D17102F0E2FF206900E022E010F8A2 +:10A14000661F21F0200141F010010170002180F80C +:10A150003C11206990F86600C00613D5FFF702FC99 +:10A1600000F0D2FE206930F8421FA0F88C108188E0 +:10A17000A0F88E1001211520FFF7FBFE012010BD75 +:10A180000123D3E7002010BD70B5684C206990F81A +:10A19000CC10FE2978D1A178002975D190F86720DC +:10A1A00001231946583002F0F9FE00286CD12069CD +:10A1B00090F8781149B10021A0F8821090F8791137 +:10A1C00080F8CE10002102205BE090F8652001238A +:10A1D0000421583002F0E2FE0546FFF76FFF002829 +:10A1E00052D1284600F07BFF00284DD12069012381 +:10A1F000002190F86420583002F0D0FE78B1206938 +:10A200000123042190F86520583002F0C7FE30B9D0 +:10A21000206990F87C0010B10021122031E0206903 +:10A2200090F864200A2A0DD0002D2DD101230021A1 +:10A23000583002F0B3FE78B1206990F894110429E7 +:10A240000AD105E010F8CA1F01710021072018E0AB +:10A2500090F89000800718D0FFF7A2FE002813D1D5 +:10A2600020690123002190F86420583002F096FE06 +:10A27000002809D0206990F88C01002804D0002122 +:10A28000FF20BDE8704074E609E000210C20FFF7D4 +:10A2900070FE206910F8901F41F00101017041E447 +:10A2A0003EB505466846FDF702FC00B9FFDF2221F6 +:10A2B00000980BF0E2F90321009803F053FC00989A +:10A2C000017821F010010170294603F070FC174C51 +:10A2D0000D2D43D00BDCA5F102050B2D19D2DFE8C3 +:10A2E00005F01F184A19191F185518192700152DA0 +:10A2F0005DD008DC112D28D0122D0BD0132D09D0E4 +:10A30000142D06D153E0162D2CD0172D68D0FF2D1B +:10A3100072D0FFDFFDF7DEFB002800D1FFDF3EBD7E +:10A320002169009891F8CE101AE000000001002089 +:10A33000E26800981178017191884171090A817170 +:10A340005188C171090A0172E4E70321009803F002 +:10A3500038FD0621009803F038FDDBE70098062160 +:10A360000171D7E70098216991F8AE21027191F847 +:10A37000AF114171CEE721690098F83103F096FCE6 +:10A3800021690098C43103F09BFCC3E7F849D1E987 +:10A390000001CDE90101206901A990F8960000F0C3 +:10A3A00025008DF80400009803F0C5FCB2E7206991 +:10A3B000B0F84410009803F095FC2069B0F8D01074 +:10A3C000009803F093FC2069B0F84010009803F067 +:10A3D00091FC2069B0F8CE10009803F08FFC99E74B +:10A3E000216991F8AC0100280098BDD111F8542FD3 +:10A3F00002714978BDE7FFE7206990F88F21D0F816 +:10A400009011009803F0E1FB84E7DA4810B5006989 +:10A4100090F86A1041B990F8652001230621583060 +:10A4200002F0BCFD002800D0012010BD70B5D14D58 +:10A43000286990F8681039B1012905D0022906D0A1 +:10A44000032904D0FFDF06E4B0F8DC1037E090F811 +:10A450006710082936D0B0F87E10B0F880200024AC +:10A460008B1C9A4206D3511A891E0C04240C01D06D +:10A47000641EA4B290F87C1039B190F864200123D6 +:10A480000921583002F08AFD40B3FFF7BEFF78B1D2 +:10A4900029690020B1F87820B1F876108B1C9A4217 +:10A4A00003D3501A801E00D0401EA04200D284B2B6 +:10A4B0000CB1641EA4B22869B0F8DC102144A0F8E5 +:10A4C000D8103FE5B0F87E100329BDD330F8581FEF +:10A4D000028D1144491CA0F8801033E50024EAE7FE +:10A4E00070B50C4605464FF4027120460BF0E7F8B4 +:10A4F000258027E5F8F787BB2DE9F0410D46074693 +:10A500000721F8F777FA041E3CD094F8B40100262E +:10A51000A8B16E70092028700BE0268484F8B4611D +:10A52000D4F8B6016860D4F8BA01A860B4F8BE01E6 +:10A53000A88194F8B4010028EFD12E71BAE094F804 +:10A54000C00190B394F8C0010D2813D00E2801D09B +:10A55000FFDFAFE02088F8F77FFB0746F8F72BF81E +:10A5600078B96E700E20287094F8C2012871208886 +:10A57000E88014E02088F8F76FFB0746F8F71BF82F +:10A5800010B10020BDE8F0816E700D20287094F8A5 +:10A59000C20128712088E88094F8C601287284F8E6 +:10A5A000C0613846F8F701F884E0FFE794F8F80155 +:10A5B00030B16E701020287084F8F861AF8079E0B7 +:10A5C00094F8C80190B16E700A2028702088A88085 +:10A5D000D4F8CC11C5F80610D4F8D011C5F80A107B +:10A5E000B4F8D401E88184F8C86163E094F8D60136 +:10A5F00040B16E701A202870B4F8D801A88084F891 +:10A60000D66157E094F8F20170B16E701B2028708B +:10A6100005E000BF84F8F261D4F8F401686094F8B2 +:10A62000F2010028F6D145E094F8DA0190B16E709D +:10A630001520287004F5EE7707E000BF84F8DA6192 +:10A640000A223946281D0AF0DEFF94F8DA010028B4 +:10A65000F4D12FE094F8E60158B16E701D202870F7 +:10A6600084F8E6610A2204F5F471281D0AF0CBFF94 +:10A6700020E094F8FA0138B11E20287084F8FA61BD +:10A68000D4F8FC01686015E094F8000200283FF45B +:10A6900079AF6E701620287008E000BF84F8006261 +:10A6A000D4F802026860B4F80602288194F8000227 +:10A6B0000028F3D1012065E72E480021C161016225 +:10A6C0000846704730B52B4D0C46E860FFF7F4FFA5 +:10A6D00000B1FFDF2C7130BD002180F8641080F8DC +:10A6E000651080F8681090F8E61009B1022100E0CA +:10A6F0000321FEF717BC2DE9F0411E4C05462069E9 +:10A7000009B1002104E0B0F8EE10B0F8DE201144E9 +:10A71000A0F8EE1090F8781139B990F8672001236D +:10A720001946583002F03AFC30B1206930F8821FE7 +:10A73000B0F85C2011440180206990F8883033B172 +:10A74000B0F88410B0F8DE201144A0F8841090F91D +:10A750008C70002F06DDB0F88A10B0F8DE201144AE +:10A76000A0F88A1001213D2635B180F8746017E009 +:10A77000705C0200000100202278022A0AD0012A1F +:10A7800011D0A2782AB380F8731012F0140F0DD0F4 +:10A790001E2113E090F8CE20062A3CD016223AE083 +:10A7A00080F8731044E090F87A2134E0110702D564 +:10A7B00080F874603CE0910603D5232180F8741082 +:10A7C00036E0900700D1FFDF21692A2081F874006C +:10A7D0002AE02BB1B0F88420B0F886309A4210D22B +:10A7E000002F05DDB0F88A20B0F886309A4208D2F2 +:10A7F000B0F88230B0F88020934204D390F87831DA +:10A800000BB1222207E090F868303BB1B0F87E30FF +:10A81000934209D3082280F87420C1E7B0F87E2063 +:10A82000062A01D33E22F6E7206990F8731019B189 +:10A830002069BDE8F0414FE7BDE8F0410021FEF797 +:10A8400071BB2DE9F047FA4C81460D46206900881E +:10A85000F8F714FA060000D1FFDFA0782843A070B3 +:10A86000A0794FF000058006206904D5A0F87E503D +:10A8700080F8EC5003E030F87E1F491C0180FFF7A0 +:10A88000C4FD012740B3E088000506D5206990F893 +:10A890006A1011B1A0F876501EE02069B0F8761069 +:10A8A000491C89B2A0F87610B0F878208A4201D30A +:10A8B000531A00E00023B4F808C00CF1050C6345FE +:10A8C00001D880F87C70914206D3A0F8765080F8C9 +:10A8D000F8712079E8F720FBA0794FF0020810F01A +:10A8E000600F0ED0206990F8681011B1032908D1CB +:10A8F00002E080F8687001E080F868800121FEF7CE +:10A9000011FB206990F86810012904D1E188C9057C +:10A9100001D580F86880B9F1000F71D1E18889050F +:10A9200002D5A0F8005104E0B0F80011491CA0F8CD +:10A93000001100F09BFBFEF7DAFCFFF725FC00F0AE +:10A9400057FF0028206902D0A0F8E05003E030F85B +:10A95000E01F491C018000F04EFF38B1216991F8D9 +:10A96000EC00022807D8401C81F8EC00206990F820 +:10A97000EC00022804D9206920F8E05F45800573C7 +:10A9800020690123002190F86520583002F006FB71 +:10A9900020B9206990F865000C2859D1206901235D +:10A9A000002190F86420583002F0F8FA48B320698A +:10A9B0000123002190F86720583002F0EFFA00B32D +:10A9C000206990F86810022942D190F8EC00C0B9D3 +:10A9D0003046F7F7C0FBA0B1216991F8CC00FE2802 +:10A9E00036D1B1F8DA00012832D981F8E570B1F832 +:10A9F0008000B1F87E20831E9A4203DB012004E030 +:10AA000032E025E0801A401E80B2B1F8E0202389B0 +:10AA10009A4201D3012202E09A1A521C92B2904249 +:10AA200000D91046012801D181F8E55091F8702134 +:10AA300092B1B1F8E220B1F872118A4201D301213A +:10AA400002E0891A491C89B2884205D9084603E008 +:10AA50002169012081F8E5502169B1F8582010449E +:10AA6000A1F8DC00FFF7E2FCE088C0F34021484693 +:10AA7000FFF741FE206980F8E650BDE8F047FDF79A +:10AA80004BB86B4902468878CB78184312D10846F8 +:10AA9000006942B18979090703D590F86700082851 +:10AAA00008D001207047B0F84810028E914201D8BA +:10AAB000FEF782B90020704770B55D4C05460E4622 +:10AAC000E0882843E080A80703D5E80700D0FFDF2F +:10AAD0006661EA074FF000014FF001001AD0A6614D +:10AAE000F278062A02D00B2A14D10AE0226992F8E1 +:10AAF0006530172B0ED10023E2E9283302F8370C1A +:10AB000008E0226992F86530112B03D182F86910B0 +:10AB100082F88E00AA0718D56269D278052A02D079 +:10AB20000B2A12D10AE0216991F86520152A0CD16F +:10AB30000022E1E92A2201F83E0C06E0206990F8A3 +:10AB40006520102A01D180F86A10280601D5082056 +:10AB5000E07078E42DE9F84F354C00254FF00108FE +:10AB6000E580A570E5704146257061F3070220611C +:10AB70009246814680F8E6800088F8F77FF8070063 +:10AB800000D1FFDF20690088FCF78EFF2069008874 +:10AB9000FCF7B0FF2069B0F8DA1071B190F8CC1072 +:10ABA000FE290FD190F8781191B190F86720012318 +:10ABB0001946583002F0F2F980B1206990F8CC00C3 +:10ABC000FE2805D0206990F8CC0000BFFFF768FB95 +:10ABD000206990F8E71089B1258118E02069A0F874 +:10ABE000825090F8791180F8CE1000210220FFF7F2 +:10ABF000C0F9206980F8E5500220E7E790F8B41129 +:10AC000019B9018C8288914200D881882181B0F8DD +:10AC1000DE10491E8EB2B0F8E0103144A0F8E0100A +:10AC200090F8E41031B1A0F8E25080F8E45006E06A +:10AC300000010020B0F8E2103144A0F8E21030F832 +:10AC40007E1F31440180FFF7E0FB20B1206930F81E +:10AC5000761F314401802069B0F8DA10012902D84A +:10AC6000491CA0F8DA100EB180F8EC5090F8E5100D +:10AC7000A1B1B0F8E000218988420FD23846F7F739 +:10AC80006AFA58B1206990F8701139B1B0F8E21041 +:10AC9000B0F87201814201D300F0B0FD206980F864 +:10ACA000E55090F865100B2901D00C2916D1B0F8A9 +:10ACB0005820B0F89631D21A12B2002A0EDBD0F822 +:10ACC0009811816090F89C110173022101F045FDFB +:10ACD000206980F8655080F8988026E0242910D1FA +:10ACE000B0F85810B0F89621891A09B2002908DB8B +:10ACF00090F8AC01FFF727F9206900F8655F057649 +:10AD000013E090F86410242901D025290DD1B0F862 +:10AD10005810B0F89601081A00B2002805DB01208F +:10AD2000FFF711F9206980F8645020690146B0F8F6 +:10AD3000DE20583001F0E9FE206990F8701109B169 +:10AD4000A0F8E250F9480090F94BFA4A49465046BB +:10AD500000F0AEFC216A11B16078FCF7F3F92069CC +:10AD60000123052190F86520583002F017F90028DA +:10AD700003D0BDE8F84F00F036BABDE8F88F00F018 +:10AD80001DBDED49C8617047EB48C069002800D07F +:10AD900001207047E84A50701162704710B50446B0 +:10ADA000B0F89C214388B0F89E11B0F8A0019A42F7 +:10ADB00005D1A388994202D1E38898420FD0238815 +:10ADC000A4F8B831A4F8BA21A4F8BC11A4F8BE01C3 +:10ADD000012084F8B401D8480079E8F79DF80121F2 +:10ADE000204601F0BAFC002004F8650F0320E07053 +:10ADF00010BD401A00B247F6FE71884201DC0028FF +:10AE000001DC012070470020704710B5012808D0F0 +:10AE1000022808D0042808D0082806D0FFDF2046E2 +:10AE200010BD0124FBE70224F9E70324F7E7C24839 +:10AE30000021006920F88A1F8178491C81707047C1 +:10AE4000BD4800B5016911F88C0F401E40B2087072 +:10AE5000002800DAFFDF00BDB7482721006980F82D +:10AE60006410002180F88C11704710B5B24C206935 +:10AE700090F89411042916D190F864200123002140 +:10AE8000583002F08BF800B9FFDF206990F890107D +:10AE9000890703D4062180F8641004E0002180F8BB +:10AEA000881080F89411206990F86600800707D513 +:10AEB000FFF7C6FF206910F8661F21F0020101703C +:10AEC00010BD9D4910B5096991F864200A2A09D17D +:10AED00091F8CA20824205D1002081F8640081F8EF +:10AEE000880010BD91F86620130706D522F00800EF +:10AEF00081F86600BDE81040A2E7FF2801D0FFDF1F +:10AF000010BDBDE81040A7E710B58B4C05212069A6 +:10AF1000FEF708F8206990F84E10012903D0BDE82B +:10AF20001040FEF77EBB022180F84E1010BD10B518 +:10AF3000814C206910F8961F41F004010170A0694E +:10AF400002F041FF162806D1206990F864002028FD +:10AF500002D0262805D010BDA06902F038FFFEF708 +:10AF60003FFB2169002081F8640081F8880010BD52 +:10AF700070B5714C01230A21206990F86420583083 +:10AF800002F00CF810B3A06902F0C4FEA8B1256964 +:10AF9000A06902F0BBFE28872569A06902F0B2FE15 +:10AFA00068872569A06902F0B3FEA8872569A069B2 +:10AFB00002F0AAFEE887FEF7D5FC2169002081F89F +:10AFC000880081F86400BDE870409DE7A07840F0FB +:10AFD0000100A070BDE510B5574C01230021206988 +:10AFE00090F86520583001F0D9FF30B1FFF71FFF0E +:10AFF0002169102081F8650010BD20690123052119 +:10B0000090F86520583001F0C9FF08B1082000E031 +:10B010000120A07010BD70B5474C012300212069AC +:10B0200090F86520583001F0B9FF012588B1A0697A +:10B0300002F011FE2169A1F89601B1F85810FFF74E +:10B04000D8FE40B12069282180F8741080F8735030 +:10B050007FE5A5707DE52169A06901F5CC7102F05D +:10B06000F5FD21690B2081F8650072E510B5FEF74A +:10B0700016FFFEF714FE304CA079400708D5A078E3 +:10B0800030B9206990F86700072801D101202070AD +:10B09000FEF7CAF9A079C00609D5A07838B92069A9 +:10B0A00090F865100B2902D10C2180F86510E0782A +:10B0B00000070ED520690123052190F8652058303E +:10B0C00001F06CFF30B10820A0702169002081F8E8 +:10B0D000C00110BDBDE81040002000F093BB10B5CA +:10B0E000154C216991F86520F8B1102A06D0142A70 +:10B0F00007D0152A22D01B2A34D122E001210B20AF +:10B1000021E0FAF797FE0C281FD320690821F830B8 +:10B11000FAF794FE28B120690421C430FAF78EFEB4 +:10B1200000B9FFDF012104200DE010E043A8010079 +:10B1300083AA0100B9AA01000001002000F017F85D +:10B1400003E001210620FEF714FF012010BD212A93 +:10B1500008D191F87D0038B991F8AC0110B191F89F +:10B16000AD0108B1002010BD01211720EBE770B53B +:10B17000174C0025206990F87B1101290AD002297B +:10B1800025D190F88E10A9B1062180F8CE100121AA +:10B19000022017E090F8C011002918D100F1B00387 +:10B1A00000F1F001002200F5BE7001F071FE0121F6 +:10B1B000052007E090F89600400701D5112000E037 +:10B1C0000D200121FEF7D5FE206980F87B51C0E4F7 +:10B1D0000001002030B5FA4C05462078002818BF41 +:10B1E000FFDF257230BDF6490120C87170472DE997 +:10B1F000F14FF44E30464068044600F1580990F88B +:10B20000551001F0D2FF94F85510658E80B20829D0 +:10B210006CD001F0A8FF854238BF284600F0FF0837 +:10B22000DFF89CA3E848CAF824007768384697F806 +:10B230006AB07D8E97F8551001F0B7FF97F855105A +:10B2400080B2082956D001F08EFF854238BF2846CB +:10B25000BBF1000F1CBF001D80B2C0B297F85510A3 +:10B26000FBF770FB99F81200002847D009F158014C +:10B27000D54891E80E1000F5027585E80E10D9F852 +:10B280006810C0F82112D9F86C10C0F8251200F52A +:10B290008170FBF7BCFE307800280CBF0120002035 +:10B2A00080F00101C9480176D9E91412C0E90412FD +:10B2B000A0F58372DAF82410FBF7DBF994F8550057 +:10B2C000012808BF00220CD0022808BF012208D0A4 +:10B2D000042808BF032204D008281ABFFFDF002279 +:10B2E000022241460120FBF7DFF90DE0042101F0C5 +:10B2F0003AFF90E7042101F036FFA6E7DAF82400D0 +:10B30000FBF785FEFBF7FCF9009850B994F855005F +:10B3100094F8561010F00C0F08BF00219620FBF790 +:10B3200097FE94F8542001210020FBF779FF94F850 +:10B330002C00012808BFFBF743FF02208AF8000019 +:10B34000FCF74CFB002818BFFFDFBDE8F88F2DE9A4 +:10B35000F04FDFF870A28BB050469AF80020416899 +:10B360001438049091F85D0001F158050C464FF037 +:10B3700008080127AAF13406A0B3012800F00681CD +:10B38000022800F00781032818BFFFDF00F01081BA +:10B39000306A0423017821F008010170AA7908EAD3 +:10B3A000C202114321F004010170EA7903EA82022A +:10B3B000114321F01001017095F80590F06AF6F73D +:10B3C000DAFE8046FCF7BAFBB9F1020F00F000810B +:10B3D000B9F1010F00F00081B9F1030F00F0008115 +:10B3E00000F003B9FFE72B7B4FF002094FF0000B91 +:10B3F000242B1CBF95F80DC0BCF1240F07D01F2BC8 +:10B4000018BF202B2AD0BCF1220F4DD077E091F845 +:10B41000540092B191F89811002974D0082818BFEF +:10B42000042869D0082918BF042965D0012818BF4D +:10B43000012953D04FF0020065E091F8FA1000297D +:10B4400061D0082818BF042856D0082918BF04293D +:10B4500052D0012818BF012940D0EBE7BCF1220FE0 +:10B4600022D0002A4BD091F8540091F8AE1111F07F +:10B47000040F18BF41460CD0082818BF04283BD041 +:10B48000082918BF042937D0012818BF012925D061 +:10B49000D0E711F0010F18BF3946EDD111F0020FBE +:10B4A00018BF4946E8D12EE04AB391F8540091F80C +:10B4B000AE2191F8511002EA010111F0040F18BFFA +:10B4C00041460ED0082818BF042815D0082918BFF7 +:10B4D000042911D0012818BF0129ABD14FF0010078 +:10B4E00011E011F0010F18BF3946EBD111F0020F36 +:10B4F00018BF4946E6D106E04FF0080003E091F896 +:10B5000054000428F8D001460290204601F058FE6D +:10B5100080B2029901F027FE218E814238BF084691 +:10B52000ADF80C00A4F848000498FCF7E6FA60B106 +:10B53000B289316A42F48062B28172694FF48060EC +:10B54000904703206871EF7022E709AA03A9F06A07 +:10B55000F6F74CFD306210B195F8351021B1049822 +:10B56000FCF79FFA6F7113E79DF8241031B9A0F82A +:10B5700000B080F802B0012102F0F4FABDF80C101E +:10B58000306A02F026FC85F8059001E70498FCF784 +:10B5900088FAFDE6B4F84800ADF8080009AA02A947 +:10B5A000F06AF6F723FD3062002808BFFFDFEFE600 +:10B5B0000498FCF7A2FA002808BFFFDFE8E60000C5 +:10B5C0002401002058010020E00C0020E80E00209B +:10B5D00030EA080009D106E030EA080005D102E0AF +:10B5E000B8F1000F01D0012100E00021306A02789B +:10B5F00042EA01110170697C00291CBF69790129A7 +:10B600003DD005F15801FD4891E80E1000F5027893 +:10B6100088E80E10A96EC0F82112E96EC0F8251254 +:10B6200000F58170FBF7F3FC9AF8000000280CBFCE +:10B6300001200020F2490876D5E91202C1E904028E +:10B64000A1F5837101F58370326AFBF712F894F863 +:10B650005400012808BF00220CD0022808BF012294 +:10B6600008D0042808BF032204D008281ABFFFDF2F +:10B6700000220222FB210020FBF716F803E0FBF773 +:10B68000C6FCFBF73DF8012194F855200846FBF76E +:10B69000C7FD3771306A018831828078B0743770A5 +:10B6A000FCF7A5F9002818BFFFDF0BB0BDE8F08F4D +:10B6B0002DE9F047D34C8146DDF8208020781E46E6 +:10B6C00017460D4628B9002F1CBF002EB8F1000FF9 +:10B6D00000D1FFDFC4F81C80C4E90D95C4E90576EC +:10B6E0004FF00000E071A071E070A07020716071F7 +:10B6F000C54EA081E081307805F158072888F7F71A +:10B70000BDFAE0622888F7F7A7FA2063FBF73EF955 +:10B7100095F95700FBF7DFF905F11200FBF75AFC2A +:10B7200005F10E00FBF7DDF9307800280CBF03208F +:10B730000120FBF769FCB87EFBF7DBF9FBF75EFC49 +:10B740003078002804BFFF2095F8544019D0BF7C02 +:10B750006C8E95F85510284601F027FD95F8551088 +:10B7600080B208291FD001F0FEFC014620468C4221 +:10B7700028BF0846002F1CBF001D80B2C0B295F83C +:10B7800055402146FBF7DEF83078214680B1012094 +:10B79000FBF7A3FA7068D0F8E800FBF73BFCBDE8C4 +:10B7A000F047012023E5042101F0DDFC0146DDE73F +:10B7B0000020FBF792FABDE8F047C8E5924800B5D3 +:10B7C00001783438007819B1022818BFFFDF00BDB6 +:10B7D000012818BFFFDF00BD8A4810B50078022895 +:10B7E00018BFFFDFBDE8104000F034BA00F032BAF5 +:10B7F0008448007970478348C078704781490120A8 +:10B80000487170472DE9F04706007F487D4D40683C +:10B8100000F15804686A90F8019018BF012E03D116 +:10B82000296B09F069FB6870687800274FF0010800 +:10B83000A0B101283CD0022860D003281CBFFFDF44 +:10B84000BDE8F087012E08BFBDE8F087286BF6F74A +:10B8500087FE287ABDE8F047E7F75EBB012E14D0DB +:10B86000A86A002808BFFFDF6889C21CD5E9091053 +:10B8700009F084FEA86A686201224946286BF6F73F +:10B88000EBFC022E08BFBDE8F087D4E91401401C90 +:10B8900041F10001C4E91401E079012801D1E77107 +:10B8A00001E084F80780287ABDE8F047E7F734BB69 +:10B8B000012E14D0A86A002808BFFFDF6889C21CC7 +:10B8C000D5E9091009F05AFEA86A686200224946C3 +:10B8D000286BF6F7C1FC022E08BFBDE8F087D4E95B +:10B8E0001410491C40F10000C4E91410E07901284B +:10B8F0000CBFE77184F80780BDE8F087012E06D001 +:10B90000286BF6F72DFE022E08BFBDE8F087D4E9BC +:10B910001410491C40F10000C4E91410E07901281A +:10B92000BFD1BCE770B5384E3046A6F1340440684C +:10B9300000F158052078012818BFFFDFA87868B10A +:10B940000021A970A289042042F00402A281626948 +:10B950009047307800281CBF01202871216A0322FB +:10B96000087832EA000009D1A28912F4806F05D06C +:10B9700042F00202A2816269022090470121002068 +:10B9800000F087F918B1BDE8704000F063B9BDE878 +:10B99000704000202BE42DE9F14F1B4E002730466C +:10B9A000A6F134054068317800F1580A2878B84685 +:10B9B000022818BFFFDFE88940F40070E881716851 +:10B9C0003078FF2091F85410FAF7BCFF0098002857 +:10B9D0009AF8120000F00681FAF7B7FEFAF7A5FE12 +:10B9E0004FF00109E0B99AF81200C8B1686A4178CD +:10B9F000B1B10078C0F3C00008E00000E00C002006 +:10BA0000E80E002024010020580100209AF80710B9 +:10BA1000884205D185F80290BDE8F84F00F01AB9C8 +:10BA2000686A41786981002908BFAF6203D0286B3A +:10BA3000F6F7CCFBA862E88940F02000E881EF70BF +:10BA40003078706800F15804834690F82C00012883 +:10BA50001AD1FBF7ABFB2146584601F05AFA98B1D0 +:10BA60003078002870680CBF00F58E7000F5F97012 +:10BA7000BBF800104180217A0171617A417180F830 +:10BA80000090287AE7F748FA686A9AF80610007872 +:10BA9000C0F3800088423BD03078706800F15804D1 +:10BAA00090F85D0000282FD002284BD067713078C5 +:10BAB00000281CBF2079002809D02771AA8939469F +:10BAC00042F01002AA816A694FF010009047E078B6 +:10BAD000A0B1E770FCF720F8002808BFFFDF0820BE +:10BAE000AA89002142F00802AA816A699047D4E934 +:10BAF0001202411C42F10000C4E91210A079012891 +:10BB00000CBFA77184F80690E88940F48070E88142 +:10BB1000696A9AF807300878C0F3C0029A424ED199 +:10BB20003278726800F0030002F15804012818BF4F +:10BB300002282DD003281CBFA87940F0040012D0A1 +:10BB4000A8713CE0E86AF6F77DFA002808BFFFDF3D +:10BB5000D4E91202411C42F10000C4E91210287A13 +:10BB6000E7F7DAF9A2E784F80290EA89484642F456 +:10BB70000062EA81AA8942F00102AA816A699047BB +:10BB8000E079012801D1E77119E084F8079016E007 +:10BB9000487818B3E98941F40061E981A96A71B173 +:10BBA000FB2884BFA87940F01000C9D8E8790028A4 +:10BBB00008BFC84603D080206A6900219047012051 +:10BBC000009900F066F8B0B1B8F1000F1CBF00207A +:10BBD000FFF718FEBDE8F84F00F03CB8E079012807 +:10BBE000D3D1D0E7002818BFFAF7E7FDE88940F085 +:10BBF0004000E881E3E7B8F1000F1CBF0120FFF728 +:10BC000001FEFFF7A4FBB8F1000F08BFBDE8F88FF5 +:10BC10000220BDE8F84FF5E570B50D4606463D48F3 +:10BC20003C4900784C6850B1FAF724FE034694F87A +:10BC3000542029463046BDE87040FDF76DBAFAF74A +:10BC400019FE034694F8542029463046BDE870405A +:10BC500006F091B92F4910B54C68FBF786FAFBF74F +:10BC600065FAFBF73DF9FBF7BBF9FAF749FD94F8E4 +:10BC70002C00012808BFFBF799FA274C00216269C4 +:10BC8000E0899047E269A179A07890470020207070 +:10BC900010BD70B5204C0546002908BF012D06D106 +:10BCA000E07800F10100C0B2E07001282ED8A1694F +:10BCB00028468847002829D06179184839B1012DD4 +:10BCC00001BF41780029017811F0100F1ED0A17931 +:10BCD000E1B910490978002908BF012D01D091B1BF +:10BCE0008DB90F49097811F0100F04BF007810F0DA +:10BCF000100F0BD0A08948B9A06A20B9608910B193 +:10BD000011F0100F02D04FF0000070BD4FF0010095 +:10BD100070BD00005801002024010020E00C00202C +:10BD200034010020FE498A78824286BF084490F898 +:10BD300043010020704710B540F2D311F84809F0D4 +:10BD40009CFCFF220821F74809F08FFCF6480021EF +:10BD5000417081704FF46171818010BD2DE9F04117 +:10BD60000E46054600F0ADFBED4C102816D004EB56 +:10BD7000C00191F85A0110F0010F1CBF0120BDE86D +:10BD8000F081607808283CBF012081F85A011CD25C +:10BD90006078401C60700120BDE8F0816078082860 +:10BDA00013D222780127501C207004EBC20830689F +:10BDB000C8F85401B088A8F85801102A28BFFFDF3E +:10BDC00088F8535188F85A71E2E70020BDE8F08105 +:10BDD000D54988707047D4488078704770B4D0488F +:10BDE00000250178491E4BB2002B46DB00EBC30156 +:10BDF00091F85A1111F0010F3BD04278D9B2521E7E +:10BE0000427000EBC10282F85A5190F802C0002241 +:10BE1000BCF1000F0BD9841894F803618E4202D153 +:10BE2000102A26D103E0521CD2B29445F3D80278EE +:10BE3000521ED2B202708A421BD000EBC20200EB4B +:10BE4000C10CD2F85341CCF85341D2F85721CCF869 +:10BE50005721847890F800C00022002C09D9861858 +:10BE600096F8036166450CD1102A1CBF024482F883 +:10BE70000311591E4BB2002BB8DAAB48857070BC69 +:10BE80007047521CD2B29442E9D8F2E7A4498A78AA +:10BE9000824286BF01EB0010C01C002070472DE9D4 +:10BEA000F04101261F4690463446002500F009FB6C +:10BEB00010282AD09A494FF0000C01EBC00292F8EA +:10BEC0005A2102F001058A78002A1ED901EB0C03E1 +:10BED00093F8033183421FD1BCF1100F15D0002F0E +:10BEE00018BF87F800C0887860450ED901EB0C10A8 +:10BEF00010F1030F09D001EB0C0090F84B4190F8C2 +:10BF00003B0101280CBF0126002648EA050046EA4D +:10BF100004010840BDE8F0810CF1010303F0FF0CBF +:10BF20006245D3D8F1E72DE9F05F1F4690460E46F3 +:10BF3000814600F0C6FA7A4D044610283CD00146EE +:10BF4000AB780020002B0ED92A1892F803218A42E0 +:10BF500005D110281CBF1220BDE8F09F03E0401C53 +:10BF6000C0B28342F0D8082B3FD2102C27D0AE7835 +:10BF70001022701CA87005EB061909F10300414658 +:10BF800000F06CFF09F183001022394600F066FFD3 +:10BF90001021384600F03FFF3544102185F8430159 +:10BFA000404600F038FF85F84B0185F8034100203A +:10BFB00085F83B01BDE8F09FAB78082B15D22C78B3 +:10BFC000CA46601C287005EBC4093068C9F85401E2 +:10BFD000B0884FF0000BA9F85801102C28BFFFDFE4 +:10BFE00089F853A189F85AB1C1E70720BDE8F09F4D +:10BFF00070B44B488178491E4BB2002BBCBF70BC5B +:10C00000704700BF817803F0FF0C491ECAB28270EE +:10C0100050FA83F191F8031194453ED000EB0215DC +:10C0200000EB0C14D5F80360C4F80360D5F8076082 +:10C03000C4F80760D5F80B60C4F80B60D5F80F6042 +:10C04000C4F80F60D5F88360C4F88360D5F88760C2 +:10C05000C4F88760D5F88B60C4F88B60D5F88F5032 +:10C06000C4F88F50851800EB0C0402EB420295F8DF +:10C0700003610CEB4C0C00EB420284F8036100EB13 +:10C080004C0CD2F80B61CCF80B61B2F80F21ACF874 +:10C090000F2195F83B2184F83B2100EBC10292F877 +:10C0A0005A2112F0010F33D190F802C00022BCF1E6 +:10C0B000000F0BD9841894F803518D4202D1102A35 +:10C0C00026D103E0521CD2B29445F3D80278521E16 +:10C0D000D2B202708A421BD000EBC20200EBC10C4C +:10C0E000D2F85341CCF85341D2F85721CCF857211C +:10C0F000847890F800C00022002C09D9851895F8A2 +:10C100000351654512D1102A1CBF024482F8031165 +:10C11000591E4BB2002BBFF675AF70BC70470000C4 +:10C12000100F00206C01002060010020521CD2B2D0 +:10C130009442E3D8ECE7FE4948707047FC484078E9 +:10C14000704738B14AF2B811884203D8F84988805C +:10C150000120704700207047F5488088704710B56F +:10C1600000F0AFF9102814D0F24A0146002092F8EE +:10C1700002C0BCF1000F0CD9131893F803318B42A5 +:10C1800003D1102818BF10BD03E0401CC0B2844585 +:10C19000F2D8082010BDE7498A78824286BF01EBB9 +:10C1A0000010833000207047E24B93F802C08445B2 +:10C1B0009CBF00207047184490F8030103EBC000B7 +:10C1C00090F853310B70D0F854111160B0F8580149 +:10C1D000908001207047D74A114491F80321D44937 +:10C1E0000A700268C1F8062080884881704770B5DF +:10C1F00016460C460546FAF7CEFFFAF796F9CC48F4 +:10C20000407868B1CB48817851B12A19002E0CBF13 +:10C210008330C01CFAF763F9FAF7AAF9012070BD60 +:10C22000002070BD10B5FAF7D1F9002804BFFF2037 +:10C2300010BDBDE81040FAF7EFB9FAF7C7B9BD492C +:10C240008A7882429CBF00207047084490F803011E +:10C2500001EBC00090F85A0100F0010070472DE991 +:10C26000F047B44E00273D46307800288CBFDFF8F9 +:10C27000C882BDE8F0870024B078002808D93119B9 +:10C2800091F80321AA4204D0611CCCB2A042F6D896 +:10C290001024A04286BF06EB0410C01C002006EB51 +:10C2A000C50999F85A1111F0010F16D050B1102C90 +:10C2B00004D0311991F83B11012903D0102100F06D +:10C2C000AAFD50B108F8074038467B1C99F8532165 +:10C2D00009F5AA71DFB2FAF7D6FB681CC5B230784F +:10C2E000A842C8D8BDE8F0872DE9F041914C00265E +:10C2F0003546A07800288CBF8F4FBDE8F0816119CA +:10C30000C0B291F80381A84286BF04EB0510C01C9F +:10C31000002091F83B11012903D0102100F07BFD92 +:10C3200058B104EBC800BD5590F8532100F5AA712F +:10C330003046731CDEB2FAF7A6FB681CC5B2A078C3 +:10C34000A842DCD8BDE8F08101447A4810B500EB82 +:10C3500002100A4601218330FAF7C1F8BDE8104007 +:10C36000FAF706B90A46724910B5497841B1714BDE +:10C37000997829B10244D81CFAF7B1F8012010BD10 +:10C38000002010BD6B4A01EB410102EB4101026844 +:10C39000C1F80B218088A1F80F0170472DE9F04109 +:10C3A000644D07460024A878002898BFBDE8F081B6 +:10C3B000C0B2A04217D905EB041010F1830612D0C9 +:10C3C0001021304600F027FD68B904EB440005EB6E +:10C3D000400808F20B113A463046FBF72CFCB8F83F +:10C3E0000F01A8F80F01601CC4B2A878A042DFD8E2 +:10C3F000BDE8F08101461022504800F02FBD4F48A3 +:10C4000070474C498A78824203D90A1892F843212E +:10C410000AB10020704700EB400001EB400000F241 +:10C420000B10704743498A78824206D9084490F835 +:10C430003B01002804BF01207047002070472DE910 +:10C44000F0410E46074615460621304600F0E3FC53 +:10C45000384C98B1A17871B104F59D7011F0010FBD +:10C4600018BF00F8015FA178490804D0457000F8B2 +:10C47000025F491EFAD10120BDE8F08138463146FD +:10C4800000F01FF8102819D0A3780021002B15D92F +:10C49000621892F8032182420BD1102918BF082993 +:10C4A0000CD004EB010080F83B514FF00100BDE8D7 +:10C4B000F08101F10101C9B28B42E9D80020BDE849 +:10C4C000F0812DE9F0411B4D0646002428780F46E7 +:10C4D000002811D905EBC40090F85311B14206D1E0 +:10C4E0000622394600F5AA7009F01CF838B1601C24 +:10C4F000C4B22878A042EDD81020BDE8F0812046D3 +:10C50000BDE8F0810B4910B44A7801EBC003521E1C +:10C510004A70002283F85A2191F802C0BCF1000F42 +:10C5200016D98B1893F8034184420DD1102A07E0E5 +:10C5300060010020100F00206C010020E31000209B +:10C540001CBF10BC704703E0521CD2B29445E8D81F +:10C550000A78521ED2B20A7082421BD001EBC2028C +:10C5600001EBC003D2F853C1C3F853C1D2F857212D +:10C57000C3F857218C7891F800C00022002C09D90B +:10C580008B1893F80331634506D1102A1CBF114460 +:10C5900081F8030110BC7047521CD2B29442EFD80C +:10C5A00010BC704770B449490D188A78521ED3B236 +:10C5B0008B7095F8032198423DD001EB001401EBFC +:10C5C000031C00EB4000DCF80360C4F80360DCF8F7 +:10C5D0000760C4F80760DCF80B60C4F80B60DCF897 +:10C5E0000F60C4F80F60DCF88360C4F88360DCF887 +:10C5F0008760C4F88760DCF88B60C4F88B60DCF877 +:10C600008FC0C4F88FC001EB030C03EB43039CF80D +:10C61000034101EB430385F8034101EB4000D3F8EC +:10C620000B41C0F80B41B3F80F31A0F80F319CF863 +:10C630003B0185F83B0101EBC20090F85A0110F074 +:10C64000010F1CBF70BC704700208C78002C0DD9E6 +:10C650000B1893F803C1944504D110281CBF70BC7B +:10C66000704703E0401CC0B28442F1D80878401EF5 +:10C67000C0B20870904204BF70BC704701EBC203A7 +:10C6800001EBC000D0F853C1C3F853C1D0F8570133 +:10C69000C3F857018C780B780020002C9CBF70BC2D +:10C6A000704700BF01EB000C9CF803C19C4506D10C +:10C6B00010281CBF084480F8032170BC7047401C40 +:10C6C000C0B28442EED870BC70470000100F00204A +:10C6D00010B50A7B02F01F020A73002202768B1843 +:10C6E00093F808C00CF001034FEA5C0C0CF0010455 +:10C6F00023444FEA5C0C0CF0010423444FEA5C0C29 +:10C700000CF001041C444FEA5C0303F0010CA44448 +:10C710005B0803F00104A4445B0803F00104A44493 +:10C720000CEB530300EB020C521C8CF8133090F806 +:10C7300018C0D2B263440376052AD0D3D8B22528D4 +:10C7400088BFFFDF10BD0023C383428401EBC20218 +:10C75000521EB2FBF1F10184704770B5002504460A +:10C7600003290DD04FF4FA4200297FD001297CD053 +:10C77000022918BF70BD0146BDE870405830A7E7D8 +:10C7800004F158068021304608F099FFB571F57123 +:10C7900035737573F573357475717576B5762120BB +:10C7A00086F83E00492086F83F00FE2086F8740097 +:10C7B00084F82C502584012084F8540084F8550016 +:10C7C000282184F856101B21218761874FF4A4711A +:10C7D000E187A1871B21218661864FF4A471E18640 +:10C7E000A1861B21A4F84010A4F844104FF4A471B2 +:10C7F000A4F84610A4F842101B21A4F84A10A4F88B +:10C800004C10A4F8481060734FF448606080A4F89E +:10C81000D850A4F8DA50A4F8DC50A4F8DE50A4F8FC +:10C82000E050A4F8E25084F8E55084F8E750A4F80A +:10C83000EE5084F8EC50A4F80051A4F8025184F8AA +:10C84000A25184F8A35184F8AC5184F8AD5184F816 +:10C85000705184F8785184F87B5184F89451C4F86D +:10C860008C51C4F8905170BD00E041E0A4F8EE5046 +:10C8700084F8E6506088FE490144B1FBF0F1A4F869 +:10C8800078104BF68031A4F87A10E388A4F87E5033 +:10C89000B4F882C0DB000CFB00FCB3FBF0F39CFBA4 +:10C8A000F0FC5B1CA4F882C09BB203FB00FC04F10B +:10C8B0005801A4F88030BCF5C84FC4BF5B1E0B857F +:10C8C000B2FBF0F2521CCA8500F5802202F5EE326E +:10C8D000531EB3FBF0F20A84CB8B03FB00F2B2FBD6 +:10C8E000F0F0C883214604F15800BDE87040EFE63F +:10C8F000B4F89C11B4F8A031B4F802C004F15800A7 +:10C90000A4F87E50B4F88240DB0004FB0CF4B3FBC7 +:10C91000F1F394FBF1F45B1C44859BB203FB01F43F +:10C920000385B4F5C84FC4BF5B1E0385B2FBF1F2AB +:10C93000521CC285428C01EBC202521EB2FBF1F2C4 +:10C940000284C28B02FB0CF2B2FBF1F1C18370BD19 +:10C9500070B50025044603290DD04FF4FA42002992 +:10C9600063D001297ED0022918BF70BD0146BDE801 +:10C9700070405830ACE604F158068021304608F08B +:10C980009EFEB571F57135737573F57335747571F8 +:10C990007576B576212086F83E00492086F83F005E +:10C9A000FE2086F8740084F82C502584012084F839 +:10C9B000540084F85500282184F856101B21218743 +:10C9C00061874FF4A471E187A1871B2121866186CD +:10C9D0004FF4A471E186A1861B21A4F84010A4F8AD +:10C9E00044104FF4A471A4F84610A4F842101B217F +:10C9F000A4F84A10A4F84C10A4F848106073A4F8E6 +:10CA0000E050202084F8E20084F8D850C4F8DC50CC +:10CA100084F80C5184F80D5184F8165184F817519C +:10CA200084F8FC5084F8085170BD60889049014436 +:10CA3000B1FBF0F1A4F878104BF68031A4F87A102D +:10CA4000E388A4F87E50B4F882C0DB000CFB00FC45 +:10CA50009CFBF0FCB3FBF0F304F15801A4F882C096 +:10CA60005B1C00E021E09BB203FB00FCA4F88030DB +:10CA7000BCF5C84FC4BF5B1E0B85B2FBF0F2521C65 +:10CA8000CA8500F5802202F5EE32531EB3FBF0F2A8 +:10CA90000A84CB8B03FB00F2B2FBF0F0C883214683 +:10CAA00004F15800BDE8704012E6D4F80031B4F843 +:10CAB00002C004F158005989DB89A4F87E50B4F80B +:10CAC0008240DB0004FB0CF4B3FBF1F394FBF1F4C4 +:10CAD0005B1C44859BB203FB01F40385B4F5C84F8E +:10CAE000C4BF5B1E0385B2FBF1F2521CC285428CAF +:10CAF00001EBC202521EB2FBF1F20284C28B02FBB6 +:10CB00000CF2B2FBF1F1C18370BD2DE9F003047E9C +:10CB10000CB1252C03D9BDE8F00312207047002A80 +:10CB200002BF0020BDE8F003704791F80DC01F263A +:10CB30000123504D4FF00008BCF1000F74D0BCF140 +:10CB4000010F1EBF1F20BDE8F0037047B0F800C002 +:10CB50000A7C8F7B91F80F907A404F7C87EA090717 +:10CB600042EA072282EA0C0C5FF000070CF0FF0992 +:10CB70004FEA1C2C99FAA9F99CFAACFC4FEA196906 +:10CB80004FEA1C6C49EA0C2C0CEB0C1C7F1C9444E7 +:10CB9000FFB21FFA8CFC032FE8D38CEA020C354F4E +:10CBA0000022ECFB057212096FF0240502FB05C29E +:10CBB000D2B201EBD207427602F007053F7A03FAC0 +:10CBC00005F52F4218BF82767ED104FB0CF2120CC1 +:10CBD000521CD2B25FF0000400EB040C9CF813C0AE +:10CBE00094453CBFA2EB0C02D2B212D30D194FF008 +:10CBF000000C2D7A03FA0CF73D421CBF521ED2B234 +:10CC0000002A71D00CF1010C0CF0FF0CBCF1080FE4 +:10CC1000F0D304F1010C0CF0FF04052CDCD33046FA +:10CC2000BDE8F0037047FFE790F819C00C7E474657 +:10CC300004FB02C20F4C4FF0000CE2FB054C4FEA24 +:10CC40001C1C6FF024040CFB0422D2B201EBD204B2 +:10CC5000427602F0070C247A03FA0CFC14EA0C0F5B +:10CC60001FBF82764046BDE8F003704704E0000035 +:10CC7000FFDB050053E4B36E90F818C0B2FBFCF480 +:10CC80000CFB1422521CD2B25FF0000400EB040C27 +:10CC90009CF813C094453CBFA2EB0C02D2B212D355 +:10CCA0000D194FF0000C2D7A03FA0CF815EA080F55 +:10CCB0001CBF521ED2B27AB10CF1010C0CF0FF0C69 +:10CCC000BCF1080FF0D300E011E004F1010C0CF00E +:10CCD000FF04052CDAD3A2E70CEBC40181763846B9 +:10CCE000BDE8F0037047FFE70CEBC40181764046D6 +:10CCF000BDE8F0037047FD4A016812681140FC4A24 +:10CD0000126811430160704730B4FA49F74B0024B0 +:10CD10004FF0010C0A78521CD2B20A70202A08BFC8 +:10CD20000C700D781A680CFA05F52A42F2D00978D1 +:10CD300002680CFA01F15140016030BC704770B4D8 +:10CD40006FF01F02010C02EA90251F23A1F5AA40F3 +:10CD500054381CBFA1F5AA40B0F1550009D0A1F587 +:10CD60002850AA381EBFA1F52A40B0F1AA00012020 +:10CD700000D100204FF0000C624664468CEA0106A8 +:10CD8000F6431643B6F1FF3F11D005F001064FEA16 +:10CD90005C0C4CEAC63C03F0010652086D085B08C7 +:10CDA000641C42EAC632162CE8D370BC704770BCD3 +:10CDB00000207047017931F01F0113BF00200022CD +:10CDC0001146704710B4435C491C03F0010C5B082A +:10CDD00003F00104A4445B0803F00104A4445B08CD +:10CDE00003F00104A4445B0803F00104A4445B08BD +:10CDF00003F001045B08A44403F00104A4440CEB19 +:10CE000053031A44D2B20529DDDB012A8CBF01206D +:10CE1000002010BC704730B40022A1F1010CBCF11D +:10CE2000000F11DD431E11F0010F08BF13F8012F91 +:10CE30005C785FEA6C0C07D013F8025F22435C78E1 +:10CE40002A43BCF1010CF7D1491E5CBF405C024390 +:10CE5000002A0CBF0120002030BC7047002A08BF08 +:10CE600070471144401E12F0010F03D011F8013D2C +:10CE700000F8013F520808BF704700BF11F8013C9D +:10CE8000437011F8023D00F8023F521EF6D1704780 +:10CE900070B58CB000F110041D4616460DF1FF3C34 +:10CEA0005FF0080014F8012C8CF8012014F8022D12 +:10CEB0000CF8022F401EF5D101F1100C6C460DF15B +:10CEC0000F0108201CF8012C4A701CF8022D01F8F3 +:10CED000022F401EF6D1204607F0FAF97EB16A1EF5 +:10CEE00004F130005FF0080110F8013C537010F8B5 +:10CEF000023D02F8023F491EF6D10CB070BD089801 +:10CF00002860099868600A98A8600B98E8600CB0DF +:10CF100070BD38B505460C466846FAF760F900283A +:10CF200008BF38BD9DF900202272A07E607294F97E +:10CF30000A100020511A48BF494295F82D308B4203 +:10CF4000C8BF38BDFF2B08BF38BDE17A491CC9B244 +:10CF5000E17295F82E30994203D8A17A7F2918BF43 +:10CF600038BDA2720020E072012038BD0C2818BF25 +:10CF70000B2810D00D2818BF1F280CD0202818BF50 +:10CF8000212808D0222818BF232804D024281EBF17 +:10CF90002628002070474FF0010070470C2963D20B +:10CFA000DFE801F006090E13161B323C415C484EC7 +:10CFB000002A5BD058E0072A18BF082A56D053E051 +:10CFC0000C2A18BF0B2A51D04EE00D2A4ED04BE050 +:10CFD000A2F10F000C2849D946E023B1A2F11000BC +:10CFE0000B2843D940E0122A18BF112A3ED090F8EE +:10CFF000360020B1122A37D31A2A37D934E0162A3C +:10D0000032D31A2A32D92FE0A2F10F0103292DD9E8 +:10D0100090F8360008B31B2A28D925E0002B08BF5A +:10D02000042A21D122E013B1062A1FD01CE0012AD4 +:10D030001AD11BE01C2A1CBF1D2A1E2A16D013E081 +:10D040001F2A18BF202A11D0212A18BF222A0DD04A +:10D05000232A1CBF242A262A08D005E013B10E2A51 +:10D0600004D001E0052A01D000207047012070475C +:10D070002DE9F0410D4604468668F7F7CCFF58B914 +:10D08000F7F7FAFD40F23471F7F7F7FAA06020469F +:10D09000F7F7C1FF0028F3D095B13046A168F8F743 +:10D0A00004FB00280CDD2844401EB0FBF5F707FB0D +:10D0B00005F13046F7F7E1FAA0603846BDE8F081A7 +:10D0C0000020BDE8F08170B50446904228BF70BDD5 +:10D0D000101B642810D325188D4205D8F8F719FBCA +:10D0E00000281CBF284670BD204670BD785C020039 +:10D0F0007C5C0200740100206420ECE710B4B1F8FD +:10D1000002C0A0F840C0B1F806C0A0F844C0B1F811 +:10D1100004C090F85440098914F00C0F15D000BFDA +:10D12000BCF5296F98BF4FF4296C90F8554014F066 +:10D130000C0F11D0B1F5296F98BF4FF42961A0F8F9 +:10D1400042C0A0F8461010BC7047002B1CBF1478DA +:10D1500014F00C0FE4D1E8E7002B1CBF527812F05A +:10D160000C0FE7D1EBE711F00C0F13D001F0040125 +:10D1700000290DBF4022102296214FF4167101F5AF +:10D18000BC71A0EB010388428CBF93FBF2F000203E +:10D1900080B27047022919BF6FF00D0101EBD0007A +:10D1A0006FF00E0101EB9000F2E7C08E11F00C0F52 +:10D1B00008BF7047B0F5296F38BF4FF4296070473A +:10D1C0000246808E11F00C0F08BF704792F8553060 +:10D1D000D18E13F00C0F04D0B1F5296F38BF4FF486 +:10D1E0002961538840F2E24C03FB0CF3528E4FF45A +:10D1F000747C0CEB821C8C459CBF910101F5747111 +:10D20000591AA1F59671884228BF0846B0F5296FD2 +:10D2100038BF4FF429607047084418449830002AFA +:10D2200014BF0421002108447047F0B4002A14BF41 +:10D2300008220122002B14BF0824012412F00C0F35 +:10D240008B8ECA8E25D091F85550944615F00C0F50 +:10D2500004D0BCF5296F38BF4FF4296C4D8840F2DB +:10D26000E2466E434D8E4FF4747707EB85176745A2 +:10D270009CBF4FEA851C0CF5747CA6EB0C0CACF53E +:10D28000967C634528BF6346B3F5296F38BF4FF4DA +:10D29000296314F00C0F04D0B2F5296F38BF4FF496 +:10D2A00029621FFA83FC00280CBF0123002391F898 +:10D2B000560014F00C0F08BF00200CEB02010844CC +:10D2C0009830002B14BF042100210844F0BC7047A3 +:10D2D0002DE9F00391F854200B8E12F00C0F4FF44F +:10D2E00074771CBF07EB83139CB255D012F00C0F60 +:10D2F0008B8ECA8E4D8E91F855C021D016461CF0EB +:10D300000C0F04D0B6F5296F38BF4FF42966B1F879 +:10D31000028040F2E24908FB09F807EB8519B145A4 +:10D3200002D8AE0106F57476A8EB0606A6F5967649 +:10D33000B34228BF3346B3F5296F38BF4FF4296392 +:10D34000A34228BF23469CB21CF00C0F1CBF07EB66 +:10D3500085139BB228D000BF1CF00C0F04D0B2F58F +:10D36000296F38BF4FF429629A4228BF1A46002815 +:10D370000CBF0123002391F856001CF00C0F08BFCE +:10D380000020A11808449830002B14BF042100216C +:10D390000844BDE8F0037047022A07BF9B003C33F6 +:10D3A000DB0070339CB2A1E7BCF1020F07BFAB00FA +:10D3B0003C33EB0070339BB2CEE710F0010F1CBF83 +:10D3C0000120704710F0020F1CBF0220704710F0C0 +:10D3D000040018BF082070472DE9F047044617469F +:10D3E00089464FF00108084600F0C5FC054648464E +:10D3F00000F0C5FC10F0010F18BF012625D000BFBA +:10D4000015F0010F18BF01232AD000BF56EA03010F +:10D4100008BF4FF0000810F0070F08BF002615F0F6 +:10D42000070F08BF002394F85400B0420CBF00203F +:10D430003046387094F85510994208BF00237B702D +:10D44000002808BF002B25D115E010F0020F18BFEF +:10D450000226D5D110F0040F14BF08260026CFE70E +:10D4600015F0020F18BF0223D0D115F0040F14BF1E +:10D4700008230023CAE7484600F087FCB4F8581098 +:10D48000401A00B247F6FE71884201DC002801DC38 +:10D490004FF0000816B1082E0CD018E094F8540094 +:10D4A000012818BF022812D004281EBF0828FFDF59 +:10D4B000032D0CD194F8AC0148B1B4F8B0010128A7 +:10D4C00094F8540006D0082801D00820387040464F +:10D4D000BDE8F087042818BF0420F7D1F5E701283C +:10D4E00014BF0228704710F00C0018BF04207047CA +:10D4F00038B4CBB2C1F3072CC1B2C0F30724012B5F +:10D5000007D0022B09D0042B08BFBCF1040F2DD08B +:10D5100006E0BCF1010F03D128E0BCF1020F25D0D9 +:10D52000012906D0022907D0042908BF042C1DD0E8 +:10D5300004E0012C02D119E0022C17D001EA0C0101 +:10D5400061F3070204EA030161F30F22D1B211F083 +:10D55000020F18BF022310D0C2F307218DF800304C +:10D5600011F0020F18BF02211BD111E0214003EA84 +:10D570000C03194061F30702E6E711F0010F18BF31 +:10D580000123E9D111F0040F14BF08230023E3E7BE +:10D5900011F0010F18BF012103D111F0040118BFD0 +:10D5A00008218DF80110082B01BF000C0128042070 +:10D5B0008DF80000BDF8000038BC70474FF0000C3B +:10D5C000082902D0042909D011E001280FD1042034 +:10D5D000907082F803C0138001207047012806D0A4 +:10D5E0000820907082F803C013800120704700204B +:10D5F0007047162A10D12A220C2818BF0D280FD0E8 +:10D600004FF0230C1F280DD031B10878012818BF26 +:10D61000002805D0162805D000207047012070474B +:10D620001A70FBE783F800C0F8E7012908D0022947 +:10D630000BD0042912BF082940F6A660704707E006 +:10D64000002804BF40F2E240704740F6C410704723 +:10D6500000B5FFDF40F2E24000BD000040787047B7 +:10D6600030B50546007801F00F0220F00F0010439E +:10D670002870092912D2DFE801F00507050705091E +:10D68000050B0F0006240BE00C2409E0222407E020 +:10D6900001240020E87003E00E2401E00024FFDFF5 +:10D6A0006C7030BD007800F00F0070470A68C0F859 +:10D6B00003208988A0F807107047D0F803200A607B +:10D6C000B0F80700888070470A68C0F80920898888 +:10D6D000A0F80D107047D0F809200A60B0F80D00CE +:10D6E000888070470278402322F0400203EA8111CB +:10D6F0001143017070470078C0F3801070470278C2 +:10D70000802322F0800203EAC111114301707047A7 +:10D710000078C009704770B514460E4605461F2AAA +:10D7200088BFFFDF2246314605F1090007F026FFDA +:10D73000A01D687070BD70B544780E460546062C75 +:10D7400038BFFFDFA01F84B21F2C88BF1F242246D2 +:10D7500005F10901304607F011FF204670BD70B594 +:10D7600014460E4605461F2A88BFFFDF2246314673 +:10D7700005F1090007F002FFA01D687070BD09687F +:10D78000C0F80F1070470A88A0F8132089784175F7 +:10D79000704790F8242001F01F0122F01F0211436E +:10D7A00080F824107047072988BF072190F82420AB +:10D7B000E02322F0E00203EA4111114380F8241033 +:10D7C00070471F3008F08FB810B5044600F009FB11 +:10D7D000002818BF204410BDC17811F03F0F1BBFB7 +:10D7E000027912F0010F0022012211F03F0F1BBF3E +:10D7F000037913F0020F002301231A4402EB4202C3 +:10D80000530011F03F0F1BBF027912F0080F0022E6 +:10D81000012203EB420311F03F0F1BBF027912F00C +:10D82000040F00220122134411F03F0F1BBF0279A5 +:10D8300012F0200F0022012202EBC20203EB42038E +:10D8400011F03F0F1BBF027912F0100F00220122CE +:10D8500002EB42021A4411F03F0F1BBF007910F097 +:10D86000400F00200120104410F0FF0014BF0121E0 +:10D8700000210844C0B2704770B50278417802F0C8 +:10D880000F02082A4DD2DFE802F004080B4C4C4C82 +:10D890000F14881F1F280AD943E00C2907D040E045 +:10D8A000881F1F2803D93CE0881F1F2839D8012072 +:10D8B00070BD4A1EFE2A34D88446C07800258209ED +:10D8C000032A09D000F03F04601C884204D8604657 +:10D8D000FFF782FFA04201D9284670BD9CF80300E3 +:10D8E0004FF0010610F03F0F1EBF1CF1040000783E +:10D8F00010F0100F13D064460421604600F071FA56 +:10D90000002818BF14EB0000E6D0017801F03F01B9 +:10D910002529E1D280780221B1EB501FDCD33046BB +:10D9200070BD002070BD70B50178012501F00F01B8 +:10D93000002404290AD007290DD008291CBF002083 +:10D9400070BD40780E2836D0204670BD4078801FCC +:10D950001F2830D9F8E7844640789CF803108A09DC +:10D96000032AF1D001F03F06711C8142ECD86046D9 +:10D97000FFF732FFB042E7D89CF8030010F03F0FEA +:10D980001EBF1CF10400007810F0100F13D0664683 +:10D990000421604600F025FA002818BF16EB0000AD +:10D9A000D2D0017801F03F012529CDD28078022123 +:10D9B000B1EB501FC8D3284670BD10B4017801F0F8 +:10D9C0000F01032920D0052921D14478B0F819107E +:10D9D000B0F81BC0B0F81730827D222C17D1062971 +:10D9E00015D3B1F5486F98BFBCF5FA7F0FD272B16D +:10D9F000082A98BF8A420AD28B429CBFB0F81D0009 +:10DA0000B0F5486F03D805E040780C2802D010BC70 +:10DA10000020704710BC012070472DE9F0411F46DF +:10DA200014460D00064608BFFFDF2146304600F0D1 +:10DA3000D8F9040008BFFFDF30193A462946BDE88F +:10DA4000F04107F09BBDC07800F03F007047C02256 +:10DA500002EA8111C27802F03F021143C17070479F +:10DA6000C07880097047C9B201F00102C1F34003D8 +:10DA70001A4402EB4202C1F3800303EB4202C1F3FA +:10DA8000C00302EB4302C1F3001303EB43031A4448 +:10DA9000C1F3401303EBC30302EB4302C1F3801352 +:10DAA0001A4412F0FF0202D0521CD2B20171C378A4 +:10DAB00002F03F0103F0C0031943C170511C4170D3 +:10DAC00070472DE9F0410546C078164600F03F0446 +:10DAD0001019401C0F46FF2888BFFFDF2819324667 +:10DAE0003946001D07F04AFDA019401C6870BDE8CA +:10DAF000F081C178407801F03F01401A401E80B2A9 +:10DB0000704710B590F803C00B460CF03F01447805 +:10DB10000CF03F0CA4EB0C0CACF1010C1FFA8CF4D4 +:10DB2000944288BF14462BB10844011D2246184672 +:10DB300007F024FD204610BD4078704700B50278FC +:10DB400001F0030322F003021A430270012914BFFB +:10DB50000229002104D0032916BFFFDF012100BDE7 +:10DB6000417000BD00B5027801F0030322F003020A +:10DB70001A430270012914BF0229002104D003298D +:10DB800016BFFFDF012100BD417000BD007800F02D +:10DB900003007047417841B1C078192803D2C04AC8 +:10DBA000105C884201D1012070470020704730B5D9 +:10DBB00001240546C17019293CBFB948445C02D311 +:10DBC000FF2918BFFFDF6C7030BD70B515460E46DB +:10DBD00004461B2A88BFFFDF65702A463146E01CD9 +:10DBE000BDE8704007F0CABCB0F807007047B0F855 +:10DBF00009007047C172090A01737047B0F80B0041 +:10DC0000704730B4B0F80720B0F809C0B0F805305C +:10DC10000179941F40F67A45AC4298BFBCF5FA7F73 +:10DC20000ED269B1082998BF914209D293429FBF91 +:10DC3000B0F80B00B0F5486F012030BC98BF7047BA +:10DC4000002030BC7047001D07F04DBE021D084685 +:10DC5000114607F048BEB0F80900704700797047D8 +:10DC60000A68426049688160704742680A6080685B +:10DC700048607047098881817047808908807047B3 +:10DC80000A68C0F80E204968C0F812107047D0F832 +:10DC90000E200A60D0F81200486070470968C0F88A +:10DCA00016107047D0F81600086070470A68426086 +:10DCB00049688160704742680A60806848607047C0 +:10DCC0000968C1607047C068086070470079704794 +:10DCD0000A68426049688160704742680A608068EB +:10DCE000486070470171090A417170478171090AE2 +:10DCF000C17170470172090A417270478172090A45 +:10DD0000C172704780887047C0887047008970472B +:10DD10004089704701891B2924BF4189B1F5A47F3F +:10DD200007D381881B2921BFC088B0F5A47F0120BB +:10DD30007047002070470A684260496881607047F8 +:10DD400042680A60806848607047017911F0070FE7 +:10DD50001BBF407910F0070F0020012070470179A8 +:10DD600011F0070F1BBF407910F0070F00200120B2 +:10DD70007047017170470079704741717047407971 +:10DD800070478171090AC1717047C088704745A208 +:10DD900082B0D2E90012CDE900120179407901F098 +:10DDA000070269461DF80220012A07D800F0070083 +:10DDB000085C01289EBF012002B07047002002B01D +:10DDC0007047017170470079704741717047407921 +:10DDD000704730B50C460546FB2988BFFFDF6C70E5 +:10DDE00030BDC378024613F03F0008BF70470520DE +:10DDF000127903F03F0312F0010F36D0002914BF4F +:10DE00000B20704712F0020F32D0012914BF801D81 +:10DE1000704700BF12F0040F2DD0022914BF401C20 +:10DE2000704700BF12F0080F28D0032914BF801CD0 +:10DE3000704700BF12F0100F23D0042914BFC01C7C +:10DE4000704700BF12F0200F1ED005291ABF1230F4 +:10DE5000C0B2704712F0400F19D006291ABF401CFB +:10DE6000C0B27047072918D114E00029CAD114E0C4 +:10DE70000129CFD111E00229D4D10EE00329D9D153 +:10DE80000BE00429DED108E00529E3D105E00629ED +:10DE9000E8D102E0834288BF70470020704700004D +:10DEA000805C020000010102010202032DE9F04141 +:10DEB000FC4E0446736893F828000127002528B11A +:10DEC00093F8A001D8B993F84801C0B193F848017C +:10DED00098B383F8A071D3F84C113C2269B36570F4 +:10DEE000201D07F04BFB052020702771706890F80B +:10DEF000A011002918BF80F8485107D034E083F8FA +:10DF0000A05103F12A014FF48E72E7E71D212A3058 +:10DF100007F0B3FB70687F2180F84510FF2180F87F +:10DF2000381080F82B1080F83E10818E21F06001AF +:10DF30002031818680F8285016E0FFE793F8220010 +:10DF4000012814D0187801281BD093F8500101281B +:10DF50001CBF0020BDE8F081657018202070D3F848 +:10DF60005201606083F850510120BDE8F081657076 +:10DF700007202070586A606083F822500120BDE8B5 +:10DF8000F0816570142020702022991C201D07F05C +:10DF9000F5FA257271680D7081F85051C248828877 +:10DFA0008284D0F86421527B80F8262080F8227089 +:10DFB000D1F864010088F4F74FFEF4F7F6FAD3E7DE +:10DFC000B84840680178002914BF80884FF6FF7078 +:10DFD000704770B5B34C0546606890F874112046E0 +:10DFE0000629806803D0FFF73BFDB8B127E0FFF7B3 +:10DFF00037FD10BBA068FFF733FD00BB606890F8E9 +:10E00000A40110F00C0F1AD0A068C17811F03F0FD6 +:10E010001CBF007910F0100F11D00EE0616891F86C +:10E020007401082809D025B191F83E00FF2806D0D8 +:10E0300003E091F82B00FF2801D0012070BD0020E3 +:10E0400070BDF8B5974C07460E46606890F82810EA +:10E05000002906BF90F848110029F8BD00F13305EA +:10E0600020787F2808BFFFDF207828707F2020706D +:10E07000606890F89A1100F5D470085C012808BF18 +:10E08000012508D0022808BF022504D0042816BFA5 +:10E0900008280325FFDF606880F8365090F8971154 +:10E0A00080F8461090F87411072911D190F8A40156 +:10E0B000012808BF012508D0022808BF022504D086 +:10E0C000042816BF08280325FFDF606880F8375052 +:10E0D000606890F874014FF00005062804D1A0682C +:10E0E000FFF7BEFC00283CD0606890F87411082946 +:10E0F00004BF90F8A10102280ED04FF00301A068E0 +:10E10000FFF762FB40B141780A09616881F8382065 +:10E110000088C0F30B0048870095A068FFF7C2FA9B +:10E120006168BDF8005091F83420520962F3461539 +:10E13000ADF80050072818BFFFDF1CD0BDF8000065 +:10E1400000906068BDF8001081860421A068FFF788 +:10E150003BFB00287DD0B0F80100C004C00C79D092 +:10E16000B0E0A068C17811F03F0F1CBF007910F03B +:10E17000100FB9D1D0E791F87401062816D00728FE +:10E1800036D0082873D00A2818BFFFDFD6D145F053 +:10E190000A00ADF8000091F83E10FF2914BF0121DC +:10E1A000002161F38200ADF80000C7E7A068FFF727 +:10E1B00057FC58B1012808BF45F0010046D002289D +:10E1C00014BFFFDF45F0020040D0B7E7A068C17878 +:10E1D00011F03F0F1CBF007910F0020FAED00120EC +:10E1E000FFF7F7FE002808BF45F004002ED0A5E792 +:10E1F000A068FFF735FCB0B1012804BF45F001006D +:10E20000ADF800000FD0022898D145F00200ADF81B +:10E210000000A168CA7812F03F0F1CBF097911F005 +:10E22000020F21D118E0A068C17811F03F0F1CBF88 +:10E23000007910F0020F05D1606890F83E00FF28C9 +:10E240003FF47CAFBDF8000040F00400ADF80000E2 +:10E2500074E72BE02FE00AE0616891F83E10FF2997 +:10E2600008BF20F00400F1D040F00400EEE791F880 +:10E270003E00FF281CBF45F00400ADF8000091F8F7 +:10E28000A1010228BDF800000CBF40F0080020F0FA +:10E290000800ADF800000CBF40F0020020F00200C2 +:10E2A000D4E7000078010020F41000206068818E1F +:10E2B00021F0600105E06068818E21F0600101F1CC +:10E2C00040018186606890F8741106290DD190F89C +:10E2D000A40110F00C0F08D0A068C17811F03F0F16 +:10E2E0001CBF007910F0100F10D1A068C17811F098 +:10E2F0003F0F0BD0017911F0400F07D04FF006010E +:10E30000FFF762FA6168007881F84500606890F86C +:10E310007401062804D00020FFF75BFE18BB04E060 +:10E32000022F18BF012FF6D1F8BDA068C17811F0F7 +:10E330003F0F33D0017911F0010F2FD0616801F147 +:10E340002C0791F8783101F12B05FF2B0CD03A46C0 +:10E3500029461846FDF728FF002808BFFFDF287868 +:10E3600040F00200287019E0FFF7C5F92870A06896 +:10E37000FFF798F9072804D23946A068FFF79DF9FE +:10E380000CE0A068FFF78EF9072807D10021A068EC +:10E39000FFF71AFA016839608088B8800120FFF71A +:10E3A00018FE80BBA068C17811F03F0F2BD0017917 +:10E3B00011F0020F27D0616801F13F0591F8762135 +:10E3C0006F1E1AB1022E18BF032E08D0FFF76AF98C +:10E3D00007280AD22946A068FFF77DF912E0D1F894 +:10E3E0005A012860B1F85E010BE0A068FFF75AF906 +:10E3F000072807D10121A068FFF7E6F90168296025 +:10E400008088A8803E70606890F87401062808BF74 +:10E41000F8BD072818BF082802D00A2806D0F8BD82 +:10E42000A068FFF71DFB022808BFF8BD606800F177 +:10E430004705A068FFF75DFB626892F83230C3F1D0 +:10E44000FF01884228BF084605D9918E21F060015E +:10E4500001F140019186C2B203EB0501A068FFF70C +:10E4600050FB616891F83220104481F83200F8BD09 +:10E470002DE9F047FB4D06466C6894F8280000280B +:10E4800018BFBDE8F0871D212A34204607F0F5F8B3 +:10E4900001272770A868FFF705F920B3012827D0C6 +:10E4A00002282AD0062818BFFFDF2BD004F11D0157 +:10E4B000A868FFF740F92072686804F1020904F1C6 +:10E4C000010890F87801FF2821D04A464146FDF71F +:10E4D0006BFE002808BFFFDF98F8000040F0020044 +:10E4E00088F8000031E0608940F013006081DDE7CA +:10E4F000608940F015006081DEE7608940F010001F +:10E500006081D3E7608940F012006081CEE7A8689F +:10E51000FFF7F1F888F80000A868FFF7C3F80728AC +:10E5200004D24946A868FFF7C8F80EE0A868FFF7CC +:10E53000B9F8072809D10021A868FFF745F9016853 +:10E54000C9F800108088A9F80400287804F10908A7 +:10E550007F2808BFFFDF287888F800004FF07F0988 +:10E5600085F80090277300206073FF20A073A17AC4 +:10E5700011F0040F08BF20752DD0686804F115084C +:10E5800004F1140A90F8761119B1022E18BF032E67 +:10E5900009D0A868FFF786F807280BD24146A8687B +:10E5A000FFF799F815E0D0F85A11C8F80010B0F844 +:10E5B0005E010CE0A868FFF775F8072809D1012172 +:10E5C000A868FFF701F90168C8F800108088A8F86A +:10E5D00004008AF8006084F81B90686890F897112E +:10E5E000217780F82870BDE8F047062003F077BC5B +:10E5F0002DE9F0419B4C606890F82810FF2500271A +:10E60000A1B91D212A3007F038F860687F2180F811 +:10E61000451080F8385080F82B5080F83E50818E9D +:10E6200021F060012031818680F82870606800F553 +:10E63000D47290F89A11895C80F8A411002003F03C +:10E640005EF818B3F8F7DAFC6068874990F879014A +:10E650000E5C3046F8F74DFA606880F8976190F8E4 +:10E66000A41111F00C0F0CBF25200F20F8F74CF966 +:10E67000606890F8A4110120F8F7AFFA606890F88C +:10E680006811032918BF022910D103E0BDE8F04149 +:10E6900001F040B990F89A1100F5D470085C012897 +:10E6A00004D1012211460020F8F7BAFDF8F788FDE1 +:10E6B000606890F8A461012E07BF4FF001080321A4 +:10E6C0004FF000080521A068FDF74CFE616881F855 +:10E6D000760150B1B8F1000F18BF402623D000BF1B +:10E6E000F7F70FFF3046F8F74CFD6068D0F87C0173 +:10E6F000F8F790FC606890F87811FF291CBF00F2D1 +:10E700009110FDF768FD6068062180F8775180F868 +:10E71000785180F8867180F8857180F8A17180F851 +:10E720007411BDE8F08116F00C0F14BF5526502669 +:10E73000D6E770B54B4C0646606800F5BA752046C2 +:10E74000806841B1D0F80510C5F81D10B0F8090077 +:10E75000A5F8210003E005F11D01FEF7AEFFA0685A +:10E76000FEF7C9FF85F82400A0680021032E018070 +:10E7700002D0052E04D046E00321FEF771FF42E0EF +:10E780000521FEF76DFF6068D0F8640100F10E010D +:10E79000A068FEF7F4FF6068D0F8640100F1120190 +:10E7A000A068FEF7F0FFD4E90110D1F86421527D92 +:10E7B0008275D1F86421D28AC275120A0276D1F824 +:10E7C000642152884276120A8276D1F864219288B6 +:10E7D000C276120A0277D1F86421D2884277120AEF +:10E7E0008277D1F864110831FEF7EBFF6068D0F84A +:10E7F0006401017EA068FEF7CCFF606890F8AA1162 +:10E80000A068FEF7D0FF05F11D01A068FEF75CFFD0 +:10E8100095F82410A068FEF772FF606800F5AD75EA +:10E8200090F8596190F8751191B190F86811032929 +:10E8300006D190F86111002918BF90F87A0101D132 +:10E8400090F87701FDF7DDFD00281CBF0126054685 +:10E850002946A068FEF72AFF3146A068BDE870404F +:10E86000FEF740BF780100209C5C0200FD4949682A +:10E8700081F87301704770B5FA4D686890F87411AB +:10E8800002291FBF90F8741101290C2070BD00F1FE +:10E8900066014FF00004C0F84C1180F848414FF079 +:10E8A0001D0100F12A0006F0E8FE68687F2180F86B +:10E8B0004510FF2180F8381080F82B1080F83E10AA +:10E8C000818E21F060012031818680F8284004701B +:10E8D00080F8224080F85041012680F8A06190F82D +:10E8E000760130B1F8F757FCF7F71FFE686880F83B +:10E8F00076416868072180F8724180F8616180F88C +:10E90000684180F8794180F8734180F8A14180F82E +:10E910006011002070BDD34910B58860486800219F +:10E92000A0F8A51180F8A711012180F87411FFF754 +:10E93000A2FF002818BFFFDF10BD2DE9F041C94D2F +:10E940000446686890F87401012818BF022804D0B2 +:10E9500003281CBF0C20BDE8F081607A022823D078 +:10E96000F8F714F80220F8F74FFB686890F9730184 +:10E97000F8F7B1F8A868F8F74AFBBB48F8F72AFBA4 +:10E98000BA48F8F7AEF8686890F8591100F5AD701C +:10E99000F8F759F80F210720F8F771F8686890F830 +:10E9A0006101F0B1FDF7A0FC6868217A00F5D4722E +:10E9B00080F89A11217A895C80F8A4116168C0F806 +:10E9C0007C112168C0F88011627A6AB1012A23D0D3 +:10E9D0000524022A08BF80F8744175D0032A7FD02D +:10E9E00087E0FDF73CFCDFE7A14C90F860C1002117 +:10E9F00090F87921521CA4FB02635B08A3EB83030C +:10EA00001A4480F879212CFA02F212F0010F03D196 +:10EA1000491CC9B20329EBD3002680F8A16190F804 +:10EA20007111002904BF90F87501002848D0F6F74D +:10EA300023F9044668682146D0F86C01F6F735FEE4 +:10EA4000DFF83082074690FBF8F008FB1070414277 +:10EA50002046F5F712FE6968C1F86C0197FBF8F0E3 +:10EA6000D1F89C211044C1F89C01FDF775FB6A6840 +:10EA7000D2F89C11884223D8C2F89C61C2F86C413C +:10EA800092F8750100281CBF0120FDF787FC0121C9 +:10EA9000686890F87221002A1CBF90F87121002A42 +:10EAA0000ED090F8592100F5AD73012A04D15A799E +:10EAB00002F0C002402A09D000F5AD70F9F7F2F873 +:10EAC0006968042081F8740113E009E00124FDF76E +:10EAD00096FC6968224601F5AD71F9F7ACF8EFE7ED +:10EAE000002918BFFFDF012000F066FF686880F88A +:10EAF00074410020BDE8F08170B55A4C606890F810 +:10EB00007411042932D005291CBF0C2070BD90F867 +:10EB1000A1110026002900F2A51190F8A7114FEAD3 +:10EB2000511126D0002908BF012507D0012908BFAF +:10EB3000022503D0022914BF00250825D0F8800142 +:10EB400000281CBF002000F037FF6068D0F87C016F +:10EB5000F8F760FA606890F8681102293DD003293F +:10EB600004BF90F8900101283BD03FE0FFF740FD43 +:10EB700044E0002908BF012507D0012908BF02256C +:10EB800003D0022914BF00250825D0F880010028F1 +:10EB90001CBF002000F010FF6068D0F87C01F8F77F +:10EBA00039FA606890F86811022906D0032904BF79 +:10EBB00090F89001012804D008E090F89001022814 +:10EBC00004D12A4601210020F8F72AFB60680721BA +:10EBD00080F8A45180F885610EE090F89001022839 +:10EBE00004D12A4601210020F8F71AFB60680821A9 +:10EBF00080F8A45180F8856180F87411002070BD00 +:10EC00001849002210F0010F496802D0012281F852 +:10EC1000A82110F0080F03D01144082081F8A801A2 +:10EC2000002070470F49496881F87001704710B59E +:10EC30000C4C636893F85831022B14BF032B002847 +:10EC40000BD100291ABF0229012000201146FDF72F +:10EC500086FA08281CBF012010BD606890F8580192 +:10EC6000002809E078010020995C02009F5C020006 +:10EC7000ABAAAAAA40420F0016BF0228002001201A +:10EC8000BDE81040F8F798BFFE48406890F858017A +:10EC9000002816BF022800200120F8F78DBFF9498F +:10ECA000496881F858017047F649496881F872014E +:10ECB000704770B5F34C616891F85801002816BF91 +:10ECC00002280020012081F8590101F5AD71F8F703 +:10ECD0005DFF606890F85811022916BF03290121D1 +:10ECE000002180F8751190F8592100F5AD734FF0AF +:10ECF0000005012A04BF5B7913F0C00F0AD000F5AC +:10ED0000AD73012A04D15A7902F0C002402A01D021 +:10ED1000002200E0012280F87121002A04BF0029AE +:10ED200070BDC0F89C51F5F7A7FF6168C1F86C0190 +:10ED300091F8750100281CBF0020FDF72FFB00266D +:10ED4000606890F8721100291ABF90F871110029BB +:10ED500070BD90F8592100F5AD71012A04D14979AF +:10ED600001F0C001402906D02946BDE8704000F5F9 +:10ED7000AD70F8F797BFFDF742FB61683246BDE81A +:10ED8000704001F5AD71F8F756BF70B5BD4D0C463A +:10ED900000280CBF01230023696881F8613181F8E4 +:10EDA0006A014FF0080081F87A010CD1002C1ABFDB +:10EDB000022C012000201146FDF7D1F969680828CE +:10EDC00081F87A0101D0002070BD022C14BF032C01 +:10EDD0001220F8D170BD002818BF112070470328F9 +:10EDE000A84A526808BFC2F8641182F8680100207E +:10EDF000704710B5A34C606890F8681103291CBFD8 +:10EE0000002180F8841101D0002010BD0123D0F82A +:10EE100064111A460020FEF708FA6168D1F86421EF +:10EE2000526A904294BF0120002081F88401EBE7F0 +:10EE30009448416891F86801032804D0012818BF5C +:10EE4000022807D004E091F86A01012808BF704742 +:10EE50000020704791F86901012814BF03280120A0 +:10EE6000F6D1704770B5F8F780F9F8F75FF9F8F761 +:10EE700037F8F8F7B5F8834C0025606890F876010C +:10EE800030B1F8F788F9F7F750FB606880F87651F1 +:10EE900060680121A0F8A55180F8A75180F874118D +:10EEA00080F85051002070BD764810B5406800F5DC +:10EEB000C47006F0A8F8002010BD72480121406817 +:10EEC00090F86821032A03BF80F85211D0F864211A +:10EED0001288002218BF80F85221A0F8542180F82F +:10EEE000501170476749496881F8AA017047017855 +:10EEF000002311F0010F634949680AD04278032AC0 +:10EF000008BFC1F8643181F86821012281F8A82185 +:10EF10001346027812F0040F0CD082784FF0000CE8 +:10EF2000032A08BFC1F864C181F868210B44082294 +:10EF300083F8A821C27881F858210279002A16BFE7 +:10EF4000022A0123002381F8613181F86921427985 +:10EF500081F86021807981F870014FF000007047DE +:10EF60004848406800F5D27070472DE9F041454CA3 +:10EF700005460E46606890F87401032818BFFFDF4D +:10EF8000022D1EBF032DFFDFBDE8F0814FF000070B +:10EF90004FF00105AEB1606890F8371089B1818EED +:10EFA00021F0600101F14001818690F8282042B9EA +:10EFB00080F8285011F0080F14BF0720062002F037 +:10EFC0008EFF6068A0F8A57180F8A77180F8745171 +:10EFD000BDE8F08100F09EBC2DE9F047294C0646C3 +:10EFE000894660684FF00108072E90F8617138BFBC +:10EFF000032533D3082E4FF0000088BFBDE8F0870B +:10F00000FEF7E7FF002878D1A068C17811F03F0F24 +:10F0100012D0027912F0010F0ED061684FF0050591 +:10F0200091F87621002A18BFB9F1000F16D091F897 +:10F03000A411012909D011E011F03F0F1ABF007986 +:10F0400010F0100F002F58D151E04FF001024FF097 +:10F050000501FDF7CCF8616881F87601A1680878B0 +:10F060002944C0F3801030B1487900F0C000402836 +:10F0700008BF012000D00020616891F876110029B6 +:10F0800002E000007801002018BF002807D0FDF73B +:10F09000C9F80146606880F8771180F8858160685A +:10F0A00090F87711FF292BD080F878110846FDF7EA +:10F0B000C6F840EA0705606890F87721FF2A18BF74 +:10F0C000002D10D0072E0ED3A068C17811F03F0F8D +:10F0D00009D0017911F0020F05D00B21FDF734F9A9 +:10F0E000606880F886812846BDE8F08705E0FCF777 +:10F0F00072FE002808BFBDE8F0870120BDE8F08758 +:10F10000A36890F8612159191B78C3F3801C00F2A1 +:10F1100077136046FCF7C3FE0546CCE72DE9F041C6 +:10F12000FE4C84B0A068FEF79BFC0126002550B180 +:10F13000022501287ED002287DD0F7F7D1FE04B049 +:10F140000620BDE8F081F7F7CBFE606890F8680113 +:10F15000032800F0C480A068C17811F03F0F05D0EB +:10F16000027912F0100F18BF012600D10026002EE0 +:10F1700014BF0822012211F03F0F43D0007932EA78 +:10F1800000013FD110F0020F06D00120FEF721FF51 +:10F19000002808BF012000D000208DF800508DF815 +:10F1A00004508DF80850FF27D0B102AA694601A883 +:10F1B00000F051FC606890F859719DF8000000283B +:10F1C00018BF47F002070BD1A068FEF7A1FA8046EE +:10F1D0000121A068FEF7F8FA4146F7F73CFC90B130 +:10F1E00066B1012000F0B9FB002878D03946002034 +:10F1F000FEF727FF606880F890516CE039460020E8 +:10F2000000F06CFB6BE0606890F86901032818BFA0 +:10F21000022864D19DF80400002860D09DF8000009 +:10F2200000285CD17EB1012000F097FB002856D069 +:10F23000FE2101E00CE032E00020FEF702FF6068F2 +:10F2400080F8905147E0FE21002000F047FB46E0A7 +:10F25000F7F746FEA0681821C27812F03F0F3ED0A3 +:10F26000027991433BD10421FEF7AEFA616891F82F +:10F270006821032A01BF8078B5EB501F91F8840103 +:10F2800000282CD04FF0010000F067FB38B3FF21BD +:10F290000120FEF7D6FE606880F890611BE0F7F76A +:10F2A0001FFE606890F86801032818D0A068182134 +:10F2B000C27812F03F0F12D0007931EA00000ED16F +:10F2C000012000F04AFB50B1FF210220FEF7B9FEF9 +:10F2D000606880F8905104B00320BDE8F08104B06C +:10F2E0000620BDE8F081F0B58C4C074683B060681D +:10F2F0006D460078002818BFFFDF002661688E7019 +:10F30000D1F8640102888A8042884A8382888A838D +:10F31000C088C88381F8206047B10121A068FEF74A +:10F3200053FA0546A0680078C10907E06946A0685D +:10F33000FEF7C3F9A0680078C0F380116068012768 +:10F3400090F87521002A18BF002904D06A7902F0CC +:10F35000C002402A26D090F87221002A18BF002946 +:10F3600003D0697911F0C00F1CD000F10E0006F037 +:10F37000B1FA616891F87801FF2819D001F108020B +:10F38000C91DFCF711FF002808BFFFDF6068C179C5 +:10F3900041F00201C171D0F891114161B0F89511AD +:10F3A000018310E02968C0F80E10A9884182E0E7C7 +:10F3B000D1F86401427ECA71D0F81A208A60C08BED +:10F3C00088814E610E8360680770D0F8642190F8E0 +:10F3D000731182F85710D0F864010088F3F73CFCF1 +:10F3E000F3F7D4F803B0F0BD2DE9F0414B4C0546DE +:10F3F00001276068002690F86811012918BF0229CA +:10F4000002D0032918BFFFDF55B1A068FEF734FA18 +:10F4100018B9A068FEF787FA10B100F0C6FB2DE01E +:10F42000606890F874017F25801F062828BFBDE81A +:10F43000F081DFE800F003191930443E3748F7F750 +:10F44000CEFE002808BF2570F7F7B0FE606890F880 +:10F45000760130B1F7F79FFEF7F767F8606880F83C +:10F460007661F7F73DFD20E02C48F7F7B8FE00285D +:10F4700008BF2570F7F79AFE00F07DFB102880F09A +:10F480004481DFE800F036B9C2C6F7F712CFF6F7CD +:10F49000F7F7249F386C2148F7F7A1FE002808BF32 +:10F4A0002570F7F783FEF7F71BFDBDE8F041FFF786 +:10F4B0009FB81A48F7F793FE30B9257004E0174853 +:10F4C000F7F78DFE0028F8D0F7F770FE9DE00320D7 +:10F4D00002F015F9002874D000210320FFF729F964 +:10F4E000012211461046F7F79BFE61680C2081F857 +:10F4F0007401BDE8F081606800F5BA75042002F07F +:10F50000FEF800285DD00E202870012002F0E7FCF4 +:10F51000A06861680078C0F3401001E07801002025 +:10F5200081F8990100210520FFF703F9F749A06848 +:10F530004FF0200CD1F864210378527B23F0200394 +:10F540000CEA42121A430270D1F8640195F8253092 +:10F55000427B1A4042732820D1F864112DE0062026 +:10F5600002F0CDF8002850D0E84D0F2085F8740146 +:10F57000022002F0B4FC6068012190F8A421084642 +:10F58000F7F74EFEA06861680078C0F3401081F87C +:10F59000990101210520FFF7CCF8D5F864014773E4 +:10F5A000A068017821F020010170F8F720FA002806 +:10F5B00018BFFFDF2820D5F8641181F85600BDE898 +:10F5C000F08122E0052002F09AF8F0B10121032039 +:10F5D000FFF7AFF8F8F70BFA002818BFFFDF6068F5 +:10F5E000012190F8A4210846F7F71AFE61680D2062 +:10F5F00081F87401BDE8F0816068A0F8A56180F829 +:10F60000A76180F87471BDE8F081BDE8F04100F0B9 +:10F6100081B96168032081F87401BDE8F0410820D8 +:10F6200002F05DBC606890F8A711490908BF012588 +:10F6300007D0012908BF022503D0022914BF0025E5 +:10F640000825D0F8800100281CBF002000F0B4F984 +:10F650006068D0F87C01F7F7DDFC606890F868110D +:10F66000022908D0032904BF90F89001012806D090 +:10F670000AE010E049E090F89001022804D12A46FF +:10F6800001210020F7F7CCFD6068072180F8A45124 +:10F6900080F8856135E0606890F8A711490908BFD6 +:10F6A000012507D0012908BF022503D0022914BF74 +:10F6B00000250825D0F8800100281CBF002000F09C +:10F6C0007BF96068D0F87C01F7F7A4FC606890F8DB +:10F6D0006811022906D0032904BF90F8900101287F +:10F6E00004D008E090F89001022804D12A460121B4 +:10F6F0000020F7F795FD6068082180F8A45180F894 +:10F70000856180F87411BDE8F081FFDFBDE8F0810C +:10F7100070B57F4C606890F8743100210C2B38D0A4 +:10F7200001220D2B40D00E2B55D00F2B1CBFFFDF1D +:10F7300070BD042002F0D3FB606890F8A4110E2085 +:10F74000F7F7E2F8606890F8A40110F00C0F14BF0E +:10F75000282100219620F7F77BFCF7F731FD606840 +:10F76000052190F8A451A068FCF7FCFD616881F8C0 +:10F77000760148B115F00C0F0CBF50255525F6F752 +:10F78000C0FE2846F7F7FDFC61680B2081F8740184 +:10F7900070BDF7F715FD00219620F7F759FC616859 +:10F7A000092081F8740170BD90F8A411FF20F7F7CB +:10F7B000ABF8606890F8A40110F00C0F14BF28217A +:10F7C00000219620F7F744FCF7F7FAFC61680A205D +:10F7D00081F8740170BDA0F8A51180F8A71180F818 +:10F7E00074210020FFF77FFDBDE87040032002F088 +:10F7F00076BB70B5464C606890F874117F25891F00 +:10F80000062928BF70BDDFE801F017321D033D1146 +:10F810003F48F7F7E4FC002808BF2570F7F7C6FC5F +:10F82000F7F75EFBBDE87040FEF7E2BE3848F7F739 +:10F83000D6FC60BB25702AE03548F7F7D0FCD8B974 +:10F84000257019E090F8371089B1818E012221F0DE +:10F8500060014031818690F8283043B980F8282033 +:10F8600011F0080F14BF0720062002F038FB2848CB +:10F87000F7F7B5FC0028E3D0F7F798FCBDE8704037 +:10F8800000F048B82248F7F7AAFC0028D2D0F7F7D2 +:10F890008DFC6068002100F5C47005F065FBBDE8D3 +:10F8A000704000F037B870B5194C06460D46012976 +:10F8B00008D0606890F8A4213046BDE87040134637 +:10F8C00002F059BBF6F7D6FF61680346304691F85F +:10F8D000A4212946BDE8704002F04DBB10B5FEF7EB +:10F8E000B0FB0B48406890F82810002918BF10BDE5 +:10F8F000012280F8282090F8340010F0080F14BF7F +:10F9000007200620BDE8104002F0E9BAF4100020FC +:10F910007801002070B5F7F728FCF7F707FCF7F738 +:10F92000DFFAF7F75DFBFE4C0025606890F8760182 +:10F9300030B1F7F730FCF6F7F8FD606880F87651E3 +:10F940006068022180F87411A0F8A55180F8A751D1 +:10F95000BDE87040002002F0C2BA70B5F04D064616 +:10F960000421A868FDF730FF0446686890F8280075 +:10F97000A0B901F0A7FE217811F0800F14BF4FF459 +:10F9800096711E21B4F80120C2F30C0212FB01F1A2 +:10F990000A1AB2F5877F28BF814201D2002070BDCC +:10F9A00068682188A0F8A511A17880F8A7113046D1 +:10F9B000BDE8704001F0A3BE2DE9F041D84C0746E8 +:10F9C000606800F2A51690F8A701400908BF01255C +:10F9D00007D0012808BF022503D0022814BF002544 +:10F9E0000825F7F70BFB307800F03F063046F7F7B5 +:10F9F00080F8606880F8976190F8900102280CBF49 +:10FA00004020FF202946F6F77FFF27B12946012035 +:10FA1000F7F763F906E060682A46D0F88011012004 +:10FA2000F7F7A4F9F7F7CCFB0521A068FCF79AFCDF +:10FA30006168002881F8760108BFBDE8F08115F003 +:10FA40000C0F0CBF50245524F6F75BFD2046BDE893 +:10FA5000F041F7F796BB2DE9F74FB14C00259146E1 +:10FA600060688A4690F8750100280CBF4FF00108C5 +:10FA70004FF00008A0680178CE090121FDF7A4FE2F +:10FA800036B1407900F0C000402808BF012600D000 +:10FA90000026606890F87611002963D090F868110C +:10FAA0004FF0000B03291ED190F86111002918BFF7 +:10FAB00090F87A7117D0FF2F18BF082F22D0384640 +:10FAC000FCF730F9002818BF4FF00108002E49D08C +:10FAD000606890F88601D0B1FCF7AFFB054660681E +:10FAE00080F886B13EE0A168CA7812F03F0F19BFD6 +:10FAF000097911F0010F90F82B10FF2918BF90F829 +:10FB00007771D8D176B390F8850170B12AE0384684 +:10FB1000FCF741FB05460121A068FDF755FE0146B3 +:10FB20002846F8F757F805461CE0A068C17811F0A0 +:10FB30003F0F05D0017911F0010F18BF0B2101D142 +:10FB40004FF005014FF00002FCF751FB616881F8AE +:10FB5000760138B1FCF766FBFF2803D06168012508 +:10FB600081F877018AF800500098067089F80080C3 +:10FB700003B0BDE8F08F6A4810B5406890F83710C0 +:10FB800089B1818E012221F060014031818690F897 +:10FB9000283043B980F8282011F0080F14BF07203F +:10FBA000062002F09CF9022010BD2DE9F04F5C4DBB +:10FBB00083B00024686890F874017F27801F264670 +:10FBC0004FF00108062880F04082DFE800F00308CB +:10FBD0000893FEFD00F01EFC044600F037BA5048C2 +:10FBE000F7F7FDFA002808BF2F70F7F7DFFAA868CB +:10FBF000FDF758FD044607286AD1A868FDF730FFD5 +:10FC0000696891F89021824262D191F874010628C6 +:10FC100004D1A868FDF724FF002836D0686890F862 +:10FC20007411082904BF90F8A101022813D04FF0E5 +:10FC30000301A868FDF7C8FD002849D0696843782A +:10FC400091F83820B2EB131F42D10088498FC0F3DE +:10FC50000B0088423CD100212046FFF7BDF9B0B32C +:10FC60008DF800608DF804608DF80860A868FF24A6 +:10FC7000C17811F03F0F1CBF007910F0020F1CD0AB +:10FC80000120FEF7A6F950B117E0A868C17811F07D +:10FC90003F0F1CBF007910F0100FBFD1DBE702AAA5 +:10FCA000694601A8FFF7D7FE686890F859419DF8AA +:10FCB0000000002818BF44F0020423469DF80820E5 +:10FCC0009DF804109DF8000000F012FA02E0FFE732 +:10FCD000FFF751FF0446686890F87601002800F0AD +:10FCE000B581F7F758FAF6F720FC686880F8766176 +:10FCF00000F0ACB9A868FDF7D5FC8146A968686832 +:10FD0000CA7890F891319A4224D10A7990F89231C8 +:10FD10009A421FD14A7990F893319A421AD101E060 +:10FD2000780100208A7990F894319A4212D1CA79E8 +:10FD300090F895319A420DD10A7A90F896319A420C +:10FD400008D1097890F89801C1F38011814208BF69 +:10FD5000012400D00024F7F7C3F8FB48F7F73FFA77 +:10FD6000002808BF2F70F7F721FAB9F1040F76D1F8 +:10FD7000002C74D0686890F8481100296FD190F871 +:10FD8000281021B190F8341011F0100F67D0D0F87E +:10FD90004C411D21204605F070FC84F80080686805 +:10FDA00004F1020A04F1010990F87801FF2810D04B +:10FDB00052464946FCF7F8F9002808BFFFDF99F8DA +:10FDC000000040F0020001E04CE0FFE089F8000094 +:10FDD0001DE0A868FDF78FFC89F80000A868FDF712 +:10FDE00061FC072804D25146A868FDF766FC0EE0C6 +:10FDF000A868FDF757FC072809D10021A868FDF77E +:10FE0000E3FC0168CAF800108088AAF8040004F135 +:10FE10001D01A868FDF78FFC2072287804F10909FC +:10FE20007F2808BFFFDF287889F800002F706868F6 +:10FE3000618990F8A12162F3000141F01A0161810A +:10FE400084F80C806673FF21A1732175E77690F822 +:10FE50009711217780F84881072002F040F80624A6 +:10FE600000F0F4B84FF00208B748F7F7B8F90028E7 +:10FE700008BF2F70F7F79AF9A868FDF713FC04463E +:10FE8000A868FDF7EDFD082C08BF00287ED1A86802 +:10FE90004FF00301C27812F03F0F77D0007931EABA +:10FEA000000073D1686800F5BA7790F86101002806 +:10FEB00014BFBE79FE784FF00009B87878B1FCF72E +:10FEC000B1F90446FF280AD00146A868401DFCF796 +:10FED00082F9B4420CBF4FF001094FF00009002134 +:10FEE000A868FDF771FC062207F11D0105F01AFB59 +:10FEF00040B9A868FDF7FFFB97F82410884208BFB7 +:10FF0000012000D0002059EA00095DD0686800F5A2 +:10FF1000AD7490F859A1787838B13046FCF771FA91 +:10FF200000281CBF04464FF0010A0027A86801788A +:10FF30004FEAD11B0121FDF747FCBBF1000F07D0B1 +:10FF4000407900F0C000402808BF4FF0010B01D0FD +:10FF50004FF0000B0121A868FDF736FC0622214670 +:10FF600005F0E0FA30B9A868FDF7D2FB504508BFAC +:10FF7000012401D04FF000043BEA040018BFFF2E1B +:10FF80000FD03046FCF707F9060000E01CE008D06F +:10FF90000121A868FDF718FC01463046F7F71AFE64 +:10FFA000074644EA070019EA000F0DD068680121EE +:10FFB00000F5C47004F0D8FF4FF001084046FFF789 +:10FFC00092F9052001F08BFF44463FE002245E4891 +:10FFD000F7F705F9002808BF2F70F7F7E7F8A868CA +:10FFE000FDF760FB0646A868FDF73AFD072E08BF3F +:10FFF00000282BD1A8684FF00101C27812F03F0F02 +:020000040002F8 +:1000000024D00279914321D1696801F5BA760021A3 +:10001000FDF7DAFB062206F11D0105F083FAA8B907 +:10002000A868FDF768FB96F8241088420ED168682E +:10003000012100F5C47004F097FFFF21022000F0B9 +:1000400009F8002818BF032400E0FFDF03B02046B2 +:10005000BDE8F08F2DE9F0413B4C02460025606879 +:1000600090F8A1310BB3A0684FF000064FF00107E4 +:10007000C37813F03F0F1CBF007910F0100F1BD096 +:100080000020FDF7DEFF606890F83400C0F34110F7 +:1000900002281BD00220FFF760FC88B160680125B0 +:1000A00080F89061F6F71CFF1FE0002A14BF0223BE +:1000B000012380F8A131D6E71046FDF7C2FF05E025 +:1000C0006068818E21F0600140318186606890F81F +:1000D000281051B980F8287090F8340010F0080FFB +:1000E00014BF0720062001F0FAFE2846BDE8F08183 +:1000F0002DE9F047144C05461F4690460E46A06871 +:10010000FDF7AEFC002800F0D180012805D00228C0 +:1001100000F00E81BDE8F0472DE5A0680921C27806 +:1001200012F03F0F00F042810279914340F03E818E +:10013000616891F86811032908D012F0020F08BF16 +:10014000FF211BD075B118E0780100200021FDF7D8 +:100150003BFB61680622D1F864111A3105F0E2F91F +:1001600050BB1EE0FDF7D4FA05460121A068FDF75B +:100170002BFB2946F6F76FFC18B13946012000F039 +:1001800039B9606890F86901032818BF022840F067 +:100190000D81002E1CBFFE21012040F02B8100F0BC +:1001A00005B9A068FDF7A7FA6168D1F86411497E26 +:1001B000884208BF012600D00026A068C17811F04F +:1001C0003F0F05D0017911F0020F01D05DB338E087 +:1001D000616891F86A21012A01D0A6B119E0C6B977 +:1001E0000021FDF7F1FA61680268D1F86411C1F8E5 +:1001F0001A208088C883A068FDF77DFA6168D1F86D +:100200006411487605E091F8770191F87A118842F7 +:100210004BD1606800F5C47004F0EAFE002844D0B9 +:100220000F20BDE8F087B8F1000F0CD0FDF770FA91 +:1002300005460121A068FDF7C7FA2946F6F70BFC31 +:1002400008B1012200E00022616891F86A010128EA +:1002500007D040B92EB991F8773191F87A118B42D5 +:1002600001D1012100E000210A421ED0012808BF6F +:10027000002E13D14FF00001A068FDF7A5FA6168C8 +:100280000268D1F86411C1F81A208088C883A06878 +:10029000FDF731FA6168D1F864114876606800F5BD +:1002A000C47004F0A5FE0028BAD17FE06068A846BB +:1002B0004FF0020990F8680103282AD0A068C1789D +:1002C00011F03F0F1BBF007910F0020F002001203A +:1002D0004FF0FF05A8B14FF00100FDF77AFE0028AE +:1002E00004BF3D46B8F1000F0BD1A068FDF710FA2E +:1002F00007460121A068FDF767FA3946F6F7ABFB20 +:1003000050B129460020FFF7A5FE002818BF4FF086 +:1003100003094846BDE8F087606890F86901032842 +:1003200018BF0228F5D1002E18BFFE25E9D1F0E74D +:10033000626892F86831032B38D0A0684FF0090C3E +:10034000C17811F03F0F31D001793CEA010C2DD179 +:10035000022B01F0020105D0002908BFFF2147D080 +:10036000CDB344E009B135B113E002F5C47004F037 +:100370003FFEA0B91AE0B8F1000F1AD0FDF7C8F996 +:1003800005460121A068FDF71FFA2946F6F763FB31 +:1003900078B1606800F5C47004F02AFE30B13946C7 +:1003A0000220FDF74EFE0D20BDE8F0870220BDE8DB +:1003B000F087606890F86901032818BF0228F5D11A +:1003C000002EF3D04FF0FE014FF00200FFF786FA47 +:1003D0000220BDE8F087FFE7FDF79AF90546012105 +:1003E000A068FDF7F1F92946F6F735FB28B1394643 +:1003F0005FF00200FFF772FAD8E7606890F86901D1 +:10040000032818BF0228D1D1002E1CBFFE210220D4 +:10041000F0D1CBE72DE9F84F0027D048F6F7DFFE03 +:10042000CE4C002804BF7F202070F6F7BFFEA068E6 +:10043000FDF738F980460121FEF7CEFD61684FF0E7 +:10044000000B91F8A421012A13D0042A1CBF082A0A +:10045000FFDF00F07781606890F8760130B1F6F741 +:100460009AFEF6F762F8606880F876B13846BDE823 +:10047000F88F0125BA4EB8F1080F19D2DFE808F05D +:1004800024860418181811FD0546F6F729FD002DDD +:100490007AD0606890F86801012818BF022858D007 +:1004A00072E028B191F86801022805D0012850D0E7 +:1004B000F6F716FD0627CEE7FF20FDF7D9FF6068A7 +:1004C0000C2780F8A1B1C6E70027002800F02081A2 +:1004D00091F86801022834D001283AD00328BAD113 +:1004E000A068D1F86421C37892F81AC0634521D17D +:1004F000037992F81BC063451CD1437992F81CC064 +:10050000634517D1837992F81DC0634512D1C37931 +:1005100092F81EC063450DD1037A92F81FC063455F +:1005200008D1037892F819C0C3F38013634508BF5C +:10053000012300D0002391F86A1101290DD0D3B115 +:10054000E4E0FF20FDF794FF60680C2780F8A151DC +:1005500081E7FF20FDF78CFF16E0002B71D102F13F +:100560001A01FDF7AAF8A068FDF7C5F86168D1F88F +:1005700064114876CAE096F87A0108287CD096F88B +:10058000771181425DD0C3E0062764E7054691F804 +:10059000750100280CBF4FF001094FF0000900273A +:1005A000A06810F8092BD20907D0407900F0C000EC +:1005B000402808BF4FF0010A01D04FF0000A91F81F +:1005C0006801032806D191F86101002818BF91F84D +:1005D0007A0101D191F877010090FBF7DCFD5FEA29 +:1005E00000082AD00098FBF79DFB002818BF4FF0A9 +:1005F0000109BAF1000F20D0A06800F109014046BE +:10060000F7F7E8FA0700606890F8598118BF48F0DA +:100610000208606890F86811032913D0F6F760FCAF +:10062000002DB1D0F6F727FA00280CBF002F404666 +:1006300072D000BFFDF71CFFA6E7606890F85981F3 +:10064000E7E763E0A168D0F86401CA78837E9A4244 +:100650001FD10A79C37E9A421BD14A79037F9A42FD +:1006600017D18A79437F9A4213D1CA79837F9A42FC +:100670000FD10A7AC37F01E04AE05BE09A4208D1D9 +:100680000978407EC1F38011814208BF4FF0010814 +:1006900001D04FF0000896F87701082806D096F8A8 +:1006A0007A11884208BF4FF0010A01D04FF0000ACA +:1006B0002FB9B9F1000F04D0F6F7DDF908B1012028 +:1006C00000E000204DB196F86A11012903D021B94C +:1006D00058EA0A0101D0012100E00021084217D0A8 +:1006E000606890F86A11012908BFB8F1000F0DD1B8 +:1006F000D0F8640100F11A01A068FCF7DEFFA068E1 +:10070000FCF7F9FF6168D1F8641148760E27A2E67C +:10071000F6F7E6FB38E7FFE7606890F86901032821 +:1007200018BF02287FF430AFBAF1000F18BFFE20C7 +:1007300080D129E791F87011002918BF00283FF4F3 +:10074000B7AE06E0B8F1070F7FF4B2AE00283FF471 +:10075000AFAEFEF7E3FC07467DE60000780100201F +:10076000F4100020D0F8E81049B1D0E93B231A4436 +:100770008B691A448A61D0E93912D16003E0F74AE3 +:10078000D0F8E4101162D0E9391009B1086170475E +:100790000028FCD00021816170472DE9FF4F0646FB +:1007A0000C46488883B040F2E24148430190E08A19 +:1007B000002500FB01FA94F8640090460D2822D031 +:1007C0000C2820D024281ED094F8650024281AD0A4 +:1007D00000208346069818B10121204603F000F955 +:1007E00094F8541094F85500009094F8D8200F46CF +:1007F0004FF47A794AB1012A61D0022A44D0032AFF +:100800005DD0FFDFB5E00120E3E7B8F1000F00D1D4 +:10081000FFDFD24814F8541F243090F83800FCF75A +:1008200004FF01902078F7F75EF84D4600F2E730BC +:10083000B0FBF5F1DFF82493D9F80C0001EB0008C8 +:100840002078F7F750F8014614F85409022816D01A +:10085000012816D040F6340008444AF2EF0108445B +:10086000B0FBF5F10198D9F81C20411A514402EB74 +:1008700008000D18012084F8D8002D1D78E02846C6 +:10088000EAE74FF4C860E7E7DFF8D092A8F101008B +:10089000D9F80810014300D1FFDFB148B8F1000FCB +:1008A000016801EB0A0506D0D9F8080000F22330F0 +:1008B000A84200D9FFDF032084F8D80058E094F85C +:1008C0006420019D242A05D094F86530242B01D0A2 +:1008D000252A3AD1B4F85820B4F8F830D21A521C6C +:1008E00012B2002A31DB94F8FA2072B3174694F85A +:1008F000FB2002B110460090022916D0012916D023 +:1009000040F6340049F608528118022F12D0012F08 +:1009100012D040F634001044814210D9081A00F574 +:10092000FA70B0FBF9F005440FE04846EAE74FF4EF +:10093000C860E7E74846EEE74FF4C860EBE7401AC7 +:1009400000F5FA70B0FBF9F02D1AB8F1000F0FD0D6 +:10095000DFF80882D8F8080018B9B8F8020000B12A +:10096000FFDFD8F8080000F22330A84200D9FFDFEB +:1009700005B9FFDF2946D4F8DC00F3F77EFEC4F8A2 +:10098000DC00B060002030704FF0010886F8048071 +:10099000204603F080F8ABF10101084202D186F84D +:1009A000058005E094F8D80001282FD0032070714D +:1009B000606A3946009A01F026FBF060069830EA3A +:1009C0000B0020D029463046FCF752FB87B2204668 +:1009D00003F061F8B8420FD8074686F8058005FB9A +:1009E00007F1D4F8DC00F3F748FEB0602946304642 +:1009F000FCF73EFB384487B23946204602F0F0FF50 +:100A0000B068C4F8DC0007B0BDE8F08F0220CEE784 +:100A10002DE9F04106460C46012001F0D6FAC5B298 +:100A20000B2001F0D2FAC0B2854200D0FFDF0025D2 +:100A3000082C7DD2DFE804F00461696965C98E96EF +:100A4000304601F0D6FA0621F1F7D4FF040000D1B8 +:100A5000FFDF304601F0CDFA2188884200D0FFDF69 +:100A600094F8D80000B9FFDF204602F060FE3B4E4C +:100A700021460020B5607580F561FCF729FC00F186 +:100A80009807606AB84217D994F85500F6F712FF34 +:100A9000014694F854004FF47A72022828D00128B5 +:100AA00028D040F6340008444AF247310844B0FBED +:100AB000F2F1606A0844C51B214600203561FCF74D +:100AC00007FC618840F2E24251439830081AA0F2D4 +:100AD0002330706194F8552094F85410606A01F046 +:100AE00092FAA0F29310B061BDE8F041F4F7AABD0C +:100AF0001046D8E74FF4C860D5E7BDE8F04102F0F2 +:100B000080BEBDE8F041F6F7A7BB6FF0040001F02E +:100B10005CFAC4B2192001F058FAC0B2844200D085 +:100B2000FFDF304601F065FA0621F1F763FF00E0D0 +:100B30004BE0040000D1FFDF304601F05AFA218873 +:100B4000884200D0FFDF2046BDE8F04101220021AD +:100B500001F076BAF6F720FAD3E70000A0120020E1 +:100B600088010020304601F044FA0621F1F742FFE7 +:100B7000040000D1FFDF304601F03BFA21888842B3 +:100B800000D0FFDF94F8D800042800D0FFDF84F8FD +:100B9000D85094F8E2504FF6FF76202D00D3FFDFB7 +:100BA000FB4820F8156094F8E200F4F746F800B925 +:100BB000FFDF202084F8E2002046FFF7D3FDF54850 +:100BC0000078BDE8F041E2F7A7B9FFDFBDE8F081AA +:100BD00070B5EF4C0025483C84F82C50E07868B1A3 +:100BE000E570FEF76AF92078042803D0A06AFFF7C1 +:100BF000B9FDA562E7480078E2F78EF9BDE87040DC +:100C000001F02FBA70B5E24C0146483C206AF4F777 +:100C10004CFD6568A27890FBF5F172B140F271224B +:100C2000B5FBF2F292B2E36B01FB02F6B34202D9DA +:100C300001FB123200E00022E2634D43002800DA9B +:100C4000FFDF2946206AF3F718FD206270BD2DE909 +:100C5000F05FFEF785F98246CD486C3800F1240834 +:100C600081684646D8F81C00F3F707FD0146306A54 +:100C7000F4F71BFD4FF00009074686F839903C4613 +:100C80004FF423754E461CE00AEB06000079F6F798 +:100C900011FE4AF2B12101444FF47A70B1FBF0F138 +:100CA00008EB86024046926811448C4207D3641ACE +:100CB00090F83910A4F52374491C88F83910761C73 +:100CC000F6B298F83A00B042DED8002C0FDD98F862 +:100CD0003910404608EB81018968A14207D241687A +:100CE000C91BA94200D90D466C4288F8399098F882 +:100CF0003960C3460AEB060898F80400F6F7DAFDF7 +:100D000001464AF2B12001444FF47A7AB1FBFAF27B +:100D100098F80410082909D0042909D000201318D4 +:100D200004290AD0082908D0252007E0082000E07F +:100D3000022000EB40002830F1E70F20401D4FF467 +:100D4000A872082913D0042914D0022915D04FF015 +:100D5000080C282210FB0C20184462190BEB8603A8 +:100D600002449868D84682420BD8791925E04FF0A2 +:100D7000400CEFE74FF0100CECE74FF0040C18229A +:100D8000E8E798F8392098F83A604046B24210D225 +:100D9000521C88F839203C1B986862198418084650 +:100DA000F6F788FD4AF2B1210144B1FBFAF00119CE +:100DB00003E080F83990D8F80410D8F82000BDE896 +:100DC000F05FF3F75ABC2DE9FE4F14460546FEF7D7 +:100DD000C7F8DFF8BCB10290ABF1480B58469BF85E +:100DE00039604FF0000A0BEB86018968CBF84010A0 +:100DF000ECB3044600780027042827D0052840D00B +:100E0000FFDFA0463946A069F3F737FC0746F3F742 +:100E100033FF81463946D8F80440F4F746FC401EBB +:100E200090FBF4F0C14361433846F3F726FC0146DA +:100E3000C8F820004846F4F738FC002800DDFFDF42 +:100E4000012088F8140088F813008FE0D4F8189077 +:100E5000D4F8048001F06FF9070010D0387800B999 +:100E6000FFDF796978684A460844414600E00EE0B1 +:100E700001F049F907464045C3D9FFDFC1E75746AE +:100E8000BFE7A06A01F0FAF840F6B837B9E7016A9F +:100E90000BEB46000191C08D08B35C46DBF81800EF +:100EA000FFF7B0FE6168206AF3F7E7FB074684F8B6 +:100EB00039A0019CD8462046DBF81810F4F7F5FB62 +:100EC000814639462046F4F7F0FBD8F80420B9FBF8 +:100ED000F2F3B0FBF2F0834243D0012142E0F3F79A +:100EE000CBFEFFF78FFEFFF7B2FE9BF83910DBF861 +:100EF00004900BEB81010746896800913946DBF8C5 +:100F00002000F4F7D2FB00248046484504DB98FB20 +:100F1000F9F404FB09F41BE0002059469BF8392042 +:100F200008E000BF01EB800304F523749B68401CBC +:100F30001C44C0B28242F5D852B10120F6F7BAFC87 +:100F40004AF2B12101444FF47A70B1FBF0F004444D +:100F50000099A8EB04000C1A00D5FFDFCBF8404045 +:100F6000A7E7002188F8141088F813A09BF8020066 +:100F70005C46B8B13946206AF4F797FB0146E26B4C +:100F800040F2712042438A4206D2C4F840A009E0F0 +:100F90000C13002084010020206C511A884200D3D9 +:100FA00008462064AF6085F800A001202871029FE8 +:100FB00094F839003F1DC05DF6F77CFC4AF23B51C6 +:100FC00001444FF47A70B1FBF0F0216CFB3008441F +:100FD000E8602078042808D194F8390004EB400038 +:100FE000C08D0A2801D2032000E00220687104EBC2 +:100FF0004600C08DC0B128466168FCF739F882B25E +:101000000020761C0CE000BF04EB4003B042D98DF9 +:10101000114489B2D98501D3491CD985401CC0B27D +:1010200094F83A108142EFD2A868A061E06194F888 +:10103000390004EB4000C18D491CC18594F839008A +:10104000C05D082803D0042803D000210BE008214C +:1010500000E0022101EB410128314FF4A872082879 +:1010600004D0042802D0022807D028220A440428E9 +:1010700005D0082803D0252102E01822F6E70F2129 +:10108000491D08280CD004280CD002280CD00820B8 +:1010900011FB0020216C884208D20120BDE8FE8FA0 +:1010A0004020F5E71020F3E70420F1E70020F5E702 +:1010B00070B5FB4C061D14F8392F905DF6F7FAFB5E +:1010C0004FF47A7100F2E730B0FBF1F0D4F807107A +:1010D00045182078805DF6F7DBFB2178895D0829CB +:1010E00003D0042903D000220BE0082200E00222F2 +:1010F00002EB420228324FF4A873082904D00429D5 +:1011000002D0022907D028231344042905D0082936 +:1011100003D0252202E01823F6E70F22521D0829EA +:101120000AD004290AD002290AD0082112FB013171 +:10113000081A281A293070BD4021F7E71021F5E779 +:101140000421F3E7FEB504460F46012000F03DFF01 +:10115000C5B20B2000F039FFC0B2854200D0FFDFDE +:1011600001260025CE48082F50D2DFE807F00430D2 +:101170004747434F4F4C0446467406744078002856 +:1011800019D1FDF7EDFE009594F839108DF808108F +:101190004188C90410D0606C019003208DF80900CB +:1011A000BF4824388560C56125746846FDF7C5FBD6 +:1011B000002800D0FFDFFEBDFFF77AFF0190207D01 +:1011C00010B18DF80950EBE78DF80960E8E70446A7 +:1011D000407840B1207C08B9FDF744FE6574BDE855 +:1011E000FE40F3F753BCA674FDF786FC0028E2D05E +:1011F000FFDFFEBDBDE8FE40F6F72EB82046BDE895 +:10120000FE4000F0A1BFBDE8FE40E1E4FFDFFEBD0F +:10121000A34950B101228A704A6840F27123B2FB9F +:10122000F3F202EB0010C86370470020887070472B +:101230002DE9F05F894640F27121994E484300251F +:101240000446706090462F46D0074AF2B12A4FF408 +:101250007A7B0FD0B9F800004843B0600120F6F760 +:1012600029FB00EB0A01B1FBFBF0241AB76801254A +:10127000A4F523745FEA087016D539F8151040F20A +:101280007120414306EB85080820C8F80810F6F7DE +:1012900011FB00EB0A01B1FBFBF0241AD8F808009F +:1012A000A4F5237407446D1CA7421AD9002D18D049 +:1012B000391BB1FBF5F0B268101AB1FBF5F205FB72 +:1012C0001212801AB060012009E000BFB1FBF5F3F3 +:1012D00006EB80029468E31A401CC0B29360A842F7 +:1012E000F4D3BDE8F09F2DE9F0416D4C0026207845 +:1012F000042804D02078052801D00C2066E40120C1 +:101300006070607C002538B1EFF3108010F0010FA1 +:1013100072B610D001270FE0FDF722FE074694F8C1 +:101320002400F4F70EF87888C00411D000210320BF +:10133000FDF71BFE0CE00027607C38B1A07C28B1D3 +:10134000FDF790FD6574A574F3F7A0FB07B962B6CD +:1013500094F82400F4F743FA94F82C0030B184F8A0 +:101360002C502078052800D0FFDF0C26657000F097 +:1013700078FE30462AE44A4810B5007808B1FFF7F5 +:10138000B2FF00F011FF464900202439086210BD69 +:1013900010B5444C58B1012807D0FFDFA06841F6D2 +:1013A0006A01884200D3FFDF10BD40F6C410A06080 +:1013B000F4E73C4908B508703949002008704870C6 +:1013C00081F82C00C87008744874887420228862E0 +:1013D00081F82420243948704FF6FF7211F16C0116 +:1013E00021F81020401CC0B22028F9D30020FFF7BC +:1013F000CFFFFFF7C0FF1020ADF8000001226946C3 +:101400000420FFF715FF08BD7FB5254C05460E46A5 +:10141000207810B10C2004B070BD95F8552095F8D7 +:101420005410686A00F002FFC5F8EC00A56295F858 +:10143000D80000B1FFDF1A4900202439C861052116 +:101440002170607084F82C00014604E004EB410236 +:10145000491CD085C9B294F83A208A42F6D284F861 +:1014600039003046FFF7D4FE0F48F3F78AFB84F8C3 +:101470002400202800D1FFDFF3F7FEFBA06194F8E1 +:10148000241001226846FFF79EFC00B9FFDF94F8A4 +:1014900024006946F3F73AFE00B9FFDF0020BAE7FF +:1014A000C41200208401002045110200F84810B544 +:1014B000007808B1002010BD0620F1F735FA80F061 +:1014C000010010BDF8B5F24D0446287800B1FFDFE9 +:1014D0000020009023780246DE0701466B4605D0C7 +:1014E0006088A188ADF800100122114626787607A1 +:1014F00006D5E088248923F8114042F00802491CEF +:10150000491E85F83A101946FFF792FE0020F8BDF3 +:101510001FB511B1112004B010BDDD4C217809B107 +:101520000C20F8E70022627004212170114604E0CB +:1015300004EB4103491CDA85C9B294F83A308B4276 +:10154000F6D284F83920FFF763FED248F3F719FB8F +:1015500084F82400202800D1FFDF00F0ECFD10B15A +:10156000F3F78AFB05E0F3F787FB40F6B831F3F7B2 +:1015700084F8A06194F8241001226846FFF723FC48 +:1015800000B9FFDF94F824006946F3F7BFFD00B906 +:10159000FFDF0020BFE770B5BD4CA16A0160FFF717 +:1015A000A2FE050002D1A06AFFF7DCF80020A062CD +:1015B000284670BD7FB5B64C2178052901D00C2096 +:1015C00029E7B3492439C860A06A00B9FFDFA06ADF +:1015D00090F8D80000B1FFDFA06A90F8E200202860 +:1015E00000D0FFDFAC48F3F7CCFAA16A054620280B +:1015F00081F8E2000E8800D3FFDFA548483020F8CC +:101600001560A06A90F8E200202800D1FFDF0023D7 +:1016100001226846A16AFFF7C0F8A06A694690F8FF +:10162000E200F3F773FD00B9FFDF0020A062F2E6ED +:10163000974924394870704710B540F2E24300FBE7 +:1016400003F4002000F0F2FD844201D9201A10BDFD +:10165000002010BD70B50D46064601460020FBF780 +:1016600037FE044696F85500F6F724F9014696F839 +:1016700054004FF47A72022815D0012815D040F694 +:10168000340008444AF247310844B0FBF2F1708854 +:1016900040F271225043C1EB4000A0F22330A5423A +:1016A00006D2214605E01046EBE74FF4C860E8E7B4 +:1016B0002946814204D2A54201D2204600E02846B4 +:1016C000706270BD70B5F5F7D5F80446F6F7E0F82E +:1016D00001466F48243882684068101A0E18204668 +:1016E00000F06AFC05462046F6F7E4F8281A4FF4A5 +:1016F0007A7100F2E730B0FBF1F0304470BD70B5A4 +:101700000546FDF72DFC6249007824398C68983431 +:10171000072D30D2DFE805F0043434252C343400B2 +:1017200014214FF4A873042810D00822082809D0E7 +:101730002A2102280FD011FB024000222823D118B1 +:10174000441819E0402211FB0240F8E7102211FB77 +:1017500002402E22F3E7042211FB0240002218234C +:10176000EDE7282100F040FC044404F5317403E067 +:1017700004F5B07400E0FFDF4548006CA04201D9D9 +:10178000012070BD002070BD70B5414C243C6078D4 +:1017900070B1D4E904512846A268FBF794FC20619B +:1017A000A84205D0A169401B0844A061F3F74AFF95 +:1017B0002169A068884201D8207808B1002070BD56 +:1017C000012070BD2DE9F04F054685B016460F4645 +:1017D0001C461846F6F75CF805EB4701471820460B +:1017E00000F0EAFB4AF2C5714FF47A7908444D469D +:1017F000B0FBF5F0384400F16008254824388068D3 +:10180000304404902046F6F743F8A8EB0007204642 +:1018100000F0D2FB06462046F6F74CF8301AB0FB33 +:10182000F5F03A1A182128254FF4C8764FF4BF77FF +:101830004FF0020B082C34D0042C2FD00020022CA7 +:1018400032D0082310F1280003EB830C0CEB831338 +:10185000184402444FF0000A082C2DD0042C26D046 +:101860000020022C2DD0082100F5B07001EB0111F1 +:101870002944884232D2082C2AD0042C25D00020BA +:10188000022C28D00821283001EB011134E000009F +:10189000C412002045110200110A0200384610232C +:1018A000D2E730464023CFE704231830CCE73D464B +:1018B00040F2EE301021D9E735464FF43560402133 +:1018C000D4E70D460421B430D0E738461021DBE7D9 +:1018D00030464021D8E704211830D5E7082C4FD0F6 +:1018E000042C4AD00020022C4DD0082110F12800F1 +:1018F000C1EBC10303EB4111084415182821204610 +:1019000000F072FB05EB4000082C42D0042C3DD0C7 +:101910000026022C3FD0082116F1280601EB811188 +:1019200006EB810146180120FC4D8DF804008DF86E +:1019300000A08DF805B0E86906F227260499F2F7B1 +:101940009CFECDE902062046F5F7B4FF4AF23B5172 +:101950000144B1FBF9F0301AFB3828640298C5F84D +:101960004480E86195F824006946F3F7CFFB00282E +:1019700000D1FFDF05B0BDE8F08F38461021B7E792 +:1019800030464021B4E704211830B1E73E4610212B +:10199000C4E74021C2E704211836BFE72DE9FE4F16 +:1019A00004461D46174688464FF0010A1846F5F7CB +:1019B0006FFFDA4E0146243EB068021907EB48007B +:1019C00010440F18284600F0F7FA4FF47A7B00F61F +:1019D000FB01D846B1FBF8F0384400F12009284655 +:1019E000F5F756FFB1680246A9EB0100001B861A05 +:1019F000284600F0E1FA07462846F5F75BFF381A5B +:101A0000B0FBF8F0311A182628234FF4C8774FF4AA +:101A1000BF78082D2CD0042D27D00020022D2AD0ED +:101A20000822283002EB820C0CEB82121044014495 +:101A3000082D28D0042D21D00020022D28D01E46AC +:101A4000082200F5B07000BF02EB0212324490424F +:101A50002AD2082D22D0042D1DD00020022D20D006 +:101A60000822283002EB02122CE040461022D9E76F +:101A700038464022D6E704221830D3E7464640F2E3 +:101A8000EE301022E0E73E464FF435604022DBE7BF +:101A90000422B430D8E740461022E3E7384640221B +:101AA000E0E704221830DDE7082D4DD0042D48D0A2 +:101AB0000020022D4BD0082210F12800C2EBC203F7 +:101AC00003EB421210440E182821284600F08CFA2D +:101AD00006EB4000082D40D0042D3BD00027022DFE +:101AE0003DD0082117F1280701EB811107EB810197 +:101AF000451805F596750C98F5F7DCFE4AF23B5152 +:101B00000144B1FBFBF0854EFB30A6F12407316C9C +:101B100004F1FB020844B9684B191A44824228D9DF +:101B2000621911440D1AFB35E1F7B0F8B9680844A1 +:101B300061190844B0F1807F36D2642D12D264203E +:101B400011E040461022B9E738464022B6E70422A9 +:101B50001830B3E747461021C6E74021C4E7042107 +:101B60001837C1E72846F3F7D4FDE8B1306C2844B4 +:101B70003064E1F78BF8B968293821440844CDE98D +:101B8000000996F839008DF8080002208DF8090048 +:101B90006846FCF7D2FE00B1FFDFFCF7ADFF00B1F5 +:101BA000FFDF5046BDE8FE8F4FF0000AF9E71FB592 +:101BB00000F042FB594C607880B994F82410002260 +:101BC0006846FFF700F938B194F824006946F3F746 +:101BD0009DFA18B9FFDF01E00120E070F2F756FF2F +:101BE00000206074A0741FBD2DE9F84FFDF7B8F90F +:101BF0000646451CC07840090CD001280CD00228AC +:101C00000CD000202978824608064FF4967407D439 +:101C10001E2006E00120F5E70220F3E70820F1E7A7 +:101C20002046B5F80120C2F30C0212FB00F7C809E8 +:101C300001D010B103E01E2401E0FFDF0024FFF714 +:101C400041FDA7EB00092878B77909EB0408C0F338 +:101C5000801010B120B1322504E04FF4FA7501E094 +:101C6000FFDF00250C2F00D3FFDF2D482D4A30F871 +:101C70001700291801FB0821501CB1FBF0F5F4F7FF +:101C8000F9FDF5F717FE4FF47A7100F27160B0FBC1 +:101C9000F1F1A9EB0100471BA7F15900103FB0F586 +:101CA000237F11D31D4E717829B902465346294628 +:101CB0002046FFF787FD00F0BFFAF2F7E7FE0020AD +:101CC0007074B074BDE8F88F3078009053462246A7 +:101CD00029463846FFF762FE0028F3D10121022091 +:101CE000FDF743F9BDE8F84F61E710B50446012957 +:101CF00003D10A482438007830B1042084F8D80091 +:101D0000BDE81040F2F7C2BE00220121204600F0DB +:101D100097F934F8580F401C2080F1E7C4120020D6 +:101D2000A45C02003F420F002DE9F0410746FDF799 +:101D300017F9050000D1FFDF29783846FBF775FC5D +:101D4000F84C0146A4F12406E069B268024467B386 +:101D50002878082803D0042803D000270BE00823A4 +:101D600000E0022303EB430728374FF4A873082849 +:101D700004D0042802D002280FD028233B4408288E +:101D80000DD004280DD002280DD00820C0EBC007CC +:101D900007EB40101844983009E01823EEE7402084 +:101DA000F4E71020F2E70420F0E74FF4FC70104451 +:101DB000471828783F1DF5F77DFD024628784FF437 +:101DC0007A7102281DD001281DD040F6340010443D +:101DD0004AF2EF021044B0FBF1F03A1AA06A40F266 +:101DE000E241B0464788D8304F43316A81420DD036 +:101DF0003946606B00F087F90646B84207D9FFDF25 +:101E000005E00846E3E74FF4C860E0E70026C6486F +:101E10008068864207D2A16A40F271224888424314 +:101E200006EB420604E040F2E240B6FBF0F0A16AA5 +:101E3000C882A06A297880F85410297880F8551053 +:101E400005214175C08A6FF41C71484306EB4000C0 +:101E500040F63541C8F81C00B0EB410F00D3FFDF5E +:101E6000BDE8F08110B5052937D2DFE801F005099A +:101E7000030D3100002100E00121BDE8104034E7EE +:101E8000032180F8D81010BD0446408840F2E2419A +:101E90004843A549091D0860D4F800010089E08283 +:101EA000D4F8000180796075D4F800014089608021 +:101EB000D4F800018089A080D4F80001C089E080B6 +:101EC0002046A16AFFF7C6FB022084F8D80010BDA7 +:101ED000816ABDE81040FFF7BDBBFFDF10BD70B5E4 +:101EE000904C243C0928A1683FD2DFE800F0050BA4 +:101EF0000B15131538380800BDE8704057E6BDE8EB +:101F0000704071E6022803D00020BDE870400BE766 +:101F10000120FAE7E16070BD032802D005281CD03B +:101F200000E0E1605FF0000600F086F97D4D0120E1 +:101F300085F82C0085F83860A86AE9690026C0F8A1 +:101F4000DC1080F8D860E068FFF734FB00B1FFDFF9 +:101F5000F2F79CFD6E74AE7470BD0126E4E7724822 +:101F60000078BDE87040E0F7D7BFFFDF70BD6D4976 +:101F700024394860704770B56A4D0446243DB1B1BC +:101F80004FF47A76012903D0022905D0FFDF70BD16 +:101F90001846F5F7C9FC05E06888401C68801046C3 +:101FA000F5F7A1FC00F2E730B0FBF6F0201AA860CC +:101FB00070BD5C4800787047082803D0042801D021 +:101FC000F5F778BC4EF628307047002804DB00F1A6 +:101FD000E02090F8000405E000F00F0000F1E020A0 +:101FE00090F8140D4009704710F00C0000D008461E +:101FF000704710B50446202800D3FFDF4948483019 +:1020000030F8140010BD70B505460C461046F5F7C3 +:1020100051FC4FF47A71022C0DD0012C0DD040F6FA +:10202000340210444AF247321044B0FBF1F0284425 +:1020300000F2931070BD0A46F3E74FF4C862F0E770 +:102040001FB513460A46044601466846FEF7A5FB3F +:1020500094F8E2006946F3F759F8002800D1FFDF51 +:102060001FBD70B52F4C0025257094F82400F2F7A1 +:10207000E4FD00B9FFDF84F8245070BD2DE9F04184 +:10208000050000D1FFDF274A0024243AD5F8EC6090 +:102090002046631E116A08E08869B04203D3984263 +:1020A00001D203460C460846C9680029F4D104B998 +:1020B00004460021C5F8E840D835C4B1E068E560C1 +:1020C000E86000B105612E698846A96156B1B06922 +:1020D00030B16F69B84200D2FFDFB069C01BA861A0 +:1020E000C6F818800F4D5CB1207820B902E0E96095 +:1020F0001562E8E7FFDF6169606808446863AFE67E +:10210000C5F83480ACE610B50C4601461046F3F72E +:10211000CCFA00280ADA211A491EB1FBF4F101FBBE +:10212000040010BDC41200208401002090FBF4F1D3 +:1021300001FB1400F5E74648016A002001E008466B +:10214000C9680029FBD170477FB504466FF00400D1 +:10215000FFF73BFFC5B21920FFF737FFC0B285423A +:1021600000D0FFDFFCF7FCFE4088C00407D001214F +:102170000320FCF7FAFE37480078E0F7CDFE002296 +:1021800021466846FEF71FFE38B169462046F2F741 +:10219000BDFF002800D1FFDF7FBD2D490120243184 +:1021A000C870FEF715FD7FBD2DE9FE43284D0120C7 +:1021B000287000264FF6FF7420E00621F0F71AFC85 +:1021C000070000D1FFDF97F8E200D837F3F707FBED +:1021D00007F80A6BA14617F8E289B8F1200F00D37F +:1021E000FFDF1B4A6C3222F8189097F8E200F2F7F2 +:1021F00024FD00B9FFDF202087F8E20069460620B1 +:10220000F0F781FB50B1FFDF08E0029830B190F8A1 +:10221000D81019B10088A042CFD104E06846F0F789 +:1022200050FB0028F1D02E70BDE8FE8310B5FFF7FB +:10223000EAFE00F5C87074E705480021243090F8E4 +:10224000392000EB4200C18502480078E0F764BE07 +:10225000A012002084010020012804D0022805D00B +:10226000032808D105E0012907D004E0022904D0A1 +:1022700001E0042901D00020704701207047FE488A +:10228000806890F8881029B1B0F88410B0F88620E2 +:10229000914215D290F88C1029B1B0F88A10B0F89C +:1022A000862091420CD2B0F88220B0F880108A4289 +:1022B00006D290F86820B0F87E001AB1884203D3A5 +:1022C000012070470628FBD2002070472DE9F0411D +:1022D000E94D0746A86800F1580490F8FC0030B9B1 +:1022E000E27B002301212046FAF758FE10B1608DF1 +:1022F000401C608501263D21AFB92878022808D00E +:1023000001280AD06878C8B110F0140F09D01E2037 +:1023100039E0162037E0E6763EE0A86890F8FE0047 +:1023200031E0020701D52177F5E7810701D02A20A6 +:1023300029E0800600D4FFDF232024E094F8300059 +:1023400028B1A08D411CA185E18D884213D294F85B +:10235000340028B1608E411C6186E18D88420AD22A +:10236000618D208D814203D3AA6892F8FC2012B9B6 +:10237000E28D914201D3222005E0217C29B1E18C3C +:10238000814207D308202077C5E7E08C062801D3D7 +:102390003E20F8E7E07EB0B1002020736073207427 +:1023A0000221A868FFF75EFDA86890F8CC1001290B +:1023B00004D1D0F804110878401E0870E878BDE810 +:1023C000F041E0F7A9BDA868BDE8F0410021FFF7A2 +:1023D00049BDA9490C28896881F8CC0014D013287C +:1023E00012D0182810D0002211280ED007280BD0A8 +:1023F00015280AD0012807D0002805D0022803D0CC +:1024000021F8842F012008717047A1F88A207047B5 +:1024100010B5994CA1680A88A1F8462181F84401B9 +:1024200091F8540001F073FBA16881F8480191F81C +:10243000550001F06CFBA16881F84901012081F889 +:102440004201002081F81601E078BDE81040E0F775 +:1024500063BD70B5884C00231946A06890F86420CD +:102460005830FAF79BFD00283DD0A06890F808117D +:102470000025C9B3A1690978B1BB90F86500FAF7E6 +:1024800075FD88BBA168B1F858000A282DD905222E +:102490000831E06903F046F810B3A068D0F80411E1 +:1024A000087858B10522491CE06903F03BF8002880 +:1024B00019D1A068D0F80401007840B9A068E1699A +:1024C000D0F804010A68C0F8012009794171A068B8 +:1024D000D0F804110878401C08700120FFF779FF3C +:1024E000A06880F8085170BDFFE7A06890F80C1153 +:1024F00011B190F80D11B9B390F816110029F2D06E +:1025000090F817110029EED190F86500FAF72EFD2A +:102510000028E8D1A06890F8540001F0F8FA0646C7 +:10252000A06890F8550001F0F2FA0546A06890F80E +:1025300018113046FFF790FE90B3A06890F819117B +:102540002846FFF789FE58B3A268B2F8583092F8CF +:102550005410B2F81A01F832FBF730F818B3A1683A +:10256000252081F86400BEE7FFE790F86510242974 +:1025700017D090F86410242913D0002300F1FA0238 +:1025800000F58671FAF7BAFDA06880F80C5130F8B2 +:10259000421FA0F88C108188A0F88E10142007E04C +:1025A00005E00123EAE7BDE87040002030E716208F +:1025B000BDE870400DE710B5F3F73CFC0C2813D3D1 +:1025C0002D4C0821A068D0F800011E30F3F736FC2E +:1025D00028B1A0680421C030F3F730FC00B9FFDF58 +:1025E000BDE810400320F4E610BD10B5224CA068F1 +:1025F000D0F800110A78002A1FD049880288914239 +:102600001BD190F86420002319465830FAF7C6FC15 +:10261000002812D0A068D0F800110978022907D04C +:1026200003290BD0042917D0052906D108200DE075 +:1026300090F86500FAF79AFC40B110BD90F8691067 +:1026400039B190F86A0000B9FFDF0A20BDE81040F8 +:10265000BFE6BDE81040AEE790F890008007ECD1EF +:102660000C20FFF7B6FEA068002120F8841F01218E +:102670000171017B02E000009001002041F00101A6 +:10268000017310BD70B5FE4CA268556DFAF730FFAE +:10269000EBB2C1B200228B4203D0A36883F8FA10D8 +:1026A00002E0A16881F8FA20C5F30721C0F30720F2 +:1026B000814203D0A16881F8FB0014E7A06880F88C +:1026C000FB2010E770B5EE48806890F84E20448EED +:1026D000C38E418FB0F84050022A23D0A94200D3C4 +:1026E00029460186C18FB0F84220914200D311469D +:1026F0008186018FB0F84420914200D31146418673 +:10270000818FB0F84620914200D31146C186418E98 +:10271000A14200D90C464486C18E994200D90B468D +:10272000C386E0E6028E914200D31146C68F828EA8 +:10273000964200D23246A94200D329460186B0F81B +:1027400042108A4200D30A468286002180F84E1049 +:10275000CFE770B5CA4CA06890F8CC10FE2955D1CF +:102760006178002952D190F8672000230121583068 +:10277000FAF714FC002849D1A06890F8FC1009B1C0 +:10278000022037E090F86420002319465830FAF709 +:1027900005FC28B1A06890F87C0008B1122029E05F +:1027A000A068002590F86420122A1DD004DC032ABA +:1027B00023D0112A04D119E0182A1AD0232A26D0AE +:1027C000002304215830FAF7E9FB00281ED1A06845 +:1027D00090F86510192970D020DC01292AD002292F +:1027E00035D0032932D120E00B2003E0BDE8704052 +:1027F000E1E60620BDE87040EBE510F8CA1F017164 +:102800000720FFF7E6FDA06880F864506BE618200B +:10281000FFF7DFFDA068A0F8845064E61D2918D0FA +:102820001E2916D0212964D148E010F8C91F417132 +:1028300007206EE00C20FFF7CCFDA06820F88A5F2F +:10284000817941F00101817100F8255C51E013208C +:102850002AE090F80D217ABB90F80C21AAB1242926 +:1028600011D090F8641024290DD0002300F1FA0251 +:1028700000F58671FAF742FCA0681E2180F8651009 +:1028800080F80C5103E00123F0E71E2931D1FFF756 +:1028900019FF01F04EF9A06830F8421FA0F88C1023 +:1028A0008188A0F88E101520FFF793FDA068A0F88E +:1028B0008A5000BF80F865501BE029E090F87D1039 +:1028C00049B100F8FA5F45701820FFF782FDA06853 +:1028D000A0F88A500DE090F8171151B990F8161130 +:1028E00039B1016DD0F81801FFF7CCFE1820FFF7C1 +:1028F00070FDA06890F8CC00FE2887D1FFF775FE28 +:10290000A06890F8CC00FE2887D1BDE87040A0E513 +:102910001120FFF75EFDA068CCE7594A01299268B3 +:1029200019D0002302290FD003291ED010B301288B +:102930002BD0032807D192F86400132803D016285F +:1029400001D0182804D1704792F8CC000028FAD0A2 +:10295000D2F8000117E092F8CC000128F3D0D2F8A9 +:1029600004110878401E0870704792F8CC000328C4 +:10297000EED17047D2F80001B2F858108288891A57 +:1029800009B20029F5DB03707047B2F85800B2F8BD +:102990000A11401A00B20028F6DBD2F804010178CF +:1029A000491E0170704770B5044690F86400002518 +:1029B0000C2810D00D282ED1D4F80011B4F85800EE +:1029C0008988401C884226D1D4F84C012C4E0178CD +:1029D00011B3FFDF42E0B4F85800B4F80A11401C0C +:1029E000884218D1D4F80401D0F80110A1604079D0 +:1029F000207302212046F9F7ABFFD4F804010078D8 +:102A000000B9FFDF0121FE20FFF787FF84F8645043 +:102A1000012084F8980066E52188C180D4F800017F +:102A2000D4F84C1140890881D4F80001D4F84C1135 +:102A300080894881D4F80001D4F84C11C08988817C +:102A4000D4F84C010571D4F84C1109200870D4F861 +:102A50004C1120884880F078E0F75EFA012120468A +:102A6000F9F776FF03212046FFF7FCF9B068D0F8AC +:102A700000010078022800D0FFDF0221FE2001E0E3 +:102A800090010020FFF749FF84F864502BE52DE901 +:102A9000F041002603270125FE4CD4F808C088B178 +:102AA0002069C0788CF8CA0005FA00F0C0F3C05065 +:102AB00000B9FFDFA06800F8647F068480F8245026 +:102AC000BDE8F08100239CF8652019460CF1580000 +:102AD000FAF764FA70B160780028F1D12069C17802 +:102AE000A06880F8C91080F86570A0F88A6080F846 +:102AF0008C50E5E76570E3E7F0B5E64C002385B060 +:102B0000A068194690F865205830FAF747FA012571 +:102B100080B1A06890F8640023280ED024280CD03F +:102B20006846F4F7EAFF68B1009801A9C0788DF80B +:102B3000040008E0657005B0F0BD607840F020004A +:102B40006070F8E70021A06803AB162290F86400DB +:102B5000FAF74FFD002670B1A0689DF80C201621F1 +:102B600000F8F42F4170192100F88F1C00F8685C00 +:102B700020F86A6CDFE72069FBF7E7F878B1216994 +:102B8000087900F00702A06880F85020497901F028 +:102B9000070180F8511090F817310BBB03E00020BB +:102BA000FFF775FFC7E790F81631CBB900F1540372 +:102BB0005F78974205D11A788A4202D180F87D5019 +:102BC0000EE000F59F71028821F8022990F850204C +:102BD0000A7190F8510048710D70E078E0F79CF9A7 +:102BE000A068212180F8651080F88C50A0F88A60D8 +:102BF000A1E770B5A74C00231946A06890F865209E +:102C00005830FAF7CBF928B32069FBF783F830B3D3 +:102C1000A5682069FBF77AF82887A5682069FBF783 +:102C200071F86887A5682069FBF772F8A887A5681E +:102C30002069FBF769F8E887A068012590F864101F +:102C40001C2910D090F84E10012912D090F80D11C7 +:102C500079B90BE0607840F00100607043E4BDE8B2 +:102C60007040002013E780F84E5002E090F80C11FD +:102C700019B11E2180F8651012E01D2180F8651041 +:102C800000F58E710288CA82028F0A83428F4A83BE +:102C9000828F8A83C08FC8830D75E078E0F73CF996 +:102CA000A068002120F88A1F85701CE410B5794CBB +:102CB00000230921A06890F864205830FAF76EF9D3 +:102CC00048B16078002805D1A16801F87C0F08732D +:102CD00001F8180C10BD0120607010BD7CB56D4C62 +:102CE00000230721A06890F864205830FAF756F9BD +:102CF00038B36078002826D169462069FBF720F8B0 +:102D00009DF80000002500F02501A06880F89610CD +:102D10009DF8011001F0490180F8971080F8885063 +:102D2000D0F8001100884988814200D0FFDFA068F8 +:102D3000D0F800110D70D0F84C110A7822B1FFDFE5 +:102D400016E0012060707CBD30F8D02BCA80C16FC6 +:102D50000D71C16F009A8A60019ACA60C26F082122 +:102D6000117030F8D01CC06F4180E078E0F7D4F8E3 +:102D7000A06880F864507CBD70B5464C00231946AD +:102D8000A06890F865205830FAF708F9012540B995 +:102D9000A0680023082190F864205830FAF7FEF864 +:102DA00010B36078002820D1A06890F890008007C8 +:102DB00012D42069FAF78AFFA16881F8910020698E +:102DC00030F8052FA1F892204088A1F8940011F85E +:102DD000900F40F002000870A0684FF0000690F8D5 +:102DE0009010C90702D011E0657066E490F8652084 +:102DF000002319465830FAF7D1F800B9FFDFA06870 +:102E000080F8655080F88C50A0F88A60A06890F82F +:102E10006410012906D180F8646080F88860E07849 +:102E2000E0F77AF8A168D1F80001098842888A425F +:102E3000DBD101780429D8D10670E078E0F76CF88E +:102E4000A06890F864100029CFD180F8886034E43D +:102E500070B5104DA86890F864101A2902D00220AD +:102E600068702AE469780029FBD1002480F88D403D +:102E700080F88840D0F8001100884988814200D04D +:102E8000FFDFA868D0F800110C70D0F84C110A7858 +:102E900022B101E090010020FFDF25E090F88E20B4 +:102EA00072B180F88E400288CA80D0F84C110C7143 +:102EB000D0F84C210E2111700188D0F84C010DE0A2 +:102EC00030F8D02BCA80C16F0C71C26F0121117212 +:102ED000C26F0D21117030F8D01CC06F418000F01E +:102EE000A2FEE878E0F718F8A86880F8644018E4D3 +:102EF00070B5FA4CA16891F86420162A01D0132A03 +:102F000002D191F88E2012B10220607009E462783B +:102F1000002AFBD181F8C800002581F88D5081F886 +:102F20008850D1F8000109884088884200D0FFDF2E +:102F3000A068D0F800010078032800D0FFDF03214B +:102F4000FE20FFF7EAFCA068D0F84C110A780AB11D +:102F5000FFDF14E030F8C82BCA8010F8081BC26FDE +:102F60001171C16F0D72C26F0D21117030F8D01C3C +:102F7000C06F418000F057FEE078DFF7CDFFA0681A +:102F800080F8645042E470B5D44C09210023A06855 +:102F900090F864205830FAF701F8002518B120693C +:102FA000007912281ED0A0680A21002390F864201E +:102FB0005830F9F7F3FF18B120690079142814D0BC +:102FC0002069007916281AD1A06890F864101F298A +:102FD00015D180F8645080F88850BDE870401A2000 +:102FE000FFF716BABDE8704060E6A06800F8645FBD +:102FF000058480F82450BDE8704000F09ABD05E4D7 +:1030000070B5B64C2079C00773D020690023052124 +:10301000C578A06890F864205830F9F7BFFF98B1E0 +:10302000062D11D006DC022D0ED0042D0CD0052D5E +:1030300006D109E00B2D07D00D2D05D0112D03D0A1 +:10304000607840F0080060706078002851D12069F5 +:10305000FAF7A0FD00287ED0206900250226C1785D +:10306000891E162977D2DFE801F00B763437472224 +:10307000764D76254A457676763A53506A6D70736A +:10308000A0680023012190F867205830F9F786FFE7 +:1030900008BB2069FAF7E2FDA16881F8FE0007206D +:1030A00081F8670081F88C5081F8885056E0FFF76E +:1030B0006AFF53E0A06890F864100F2901D0667091 +:1030C0004CE0617839B980F86950122180F86410B9 +:1030D00044E000F0D3FD41E000F0AFFD3EE0FAF740 +:1030E00072FE03283AD12069FAF771FEFFF700FF5C +:1030F00034E03BE00079F9E7FFF7AAFE2EE0FFF7A6 +:103100003BFE2BE0FFF7EAFD28E0FFF7CFFD25E0CF +:10311000A0680023194690F865205830F9F73EFF63 +:10312000012110B16078C8B901E0617016E0A068B3 +:1031300020F88A5F817000F8256C0FE00BE0FFF744 +:1031400058FD0BE000F03CFD08E0FFF7D5FC05E082 +:1031500000F002FD02E00020FFF799FCA268F2E90E +:103160002A01401C41F10001C2E9000153E42DE9AC +:10317000F0415A4C2079800741D5607800283ED133 +:10318000E06801270026C17820461929856805F1E5 +:1031900058006FD2DFE801F04B3E0D6FC1C1801CBB +:1031A00034C1556287C1C1C1C1BE8B9598A4B0C15D +:1031B000BA0095F8672000230121F9F7EFFE0028F7 +:1031C0001DD1A068082180F8671080F8886090E021 +:1031D000002395F865201946F9F7E0FE10B1A068C4 +:1031E00080F88C60A0680023194690F8642058305D +:1031F000F9F7D4FE002802D0A06880F888605FE468 +:10320000002395F864201946F9F7C8FE00B9FFDFDE +:10321000042008E0002395F864201946F9F7BEFE63 +:1032200000B9FFDF0C20A16881F8640048E40023A6 +:1032300095F864201946F9F7B1FE00B9FFDF0D20BB +:10324000F1E7002395F864201946F9F7A7FE00B9C5 +:10325000FFDFA0680F2180F88D7008E095F864000A +:10326000122800D0FFDFA068112180F88E7080F84E +:10327000641025E451E0002395F864201946F9F71D +:103280008DFE20B9A06890F88E0000B9FFDFA0681D +:10329000132180F88D70EAE795F86400182800D0B3 +:1032A000FFDF1A20BFE7BDE8F04100F066BD002354 +:1032B00095F864201946F9F771FE00B9FFDF052083 +:1032C000B1E785F88C6014E4002395F86420194672 +:1032D000F9F764FE00B9FFDF1C20A4E7900100208D +:1032E000002395F865201946F9F758FE00B9FFDF6D +:1032F000A06880F88C6082E7002395F86420194666 +:10330000F9F74CFE00B9FFDF1F208CE7BDE8F04164 +:1033100000F0FBBC85F86560D3E7FFDF6FE710B511 +:10332000F74C6078002837D1207940070FD5A06886 +:1033300090F86400032800D1FFDFA06890F86710C0 +:10334000072904D101212170002180F86710FFF7BF +:103350000EFF00F0B8FCFFF753FEA078000716D56B +:10336000A0680023052190F864205830F9F716FE74 +:1033700050B108206070A068D0F84C1108780D2872 +:1033800000D10020087002E00020F8F73BFAA068A6 +:10339000BDE81040FFF707BB10BD2DE9F041D84C48 +:1033A00007464FF000056078084360702079810679 +:1033B0002046806802D5A0F87E5004E0B0F87E1068 +:1033C000491CA0F87E1000F01AFD0126F8B1A08873 +:1033D000000506D5A06890F86A1011B1A0F87650E3 +:1033E00015E0A068B0F87610491CA0F8761000F03F +:1033F000F5FCA068B0F87610B0F87820914206D3BA +:10340000A0F8765080F82261E078DFF785FD20791A +:1034100010F0600F08D0A06890F8681021B980F80B +:1034200068600121FEF71EFD1FB9FFF778FFFFF767 +:1034300090F93846FEF74AFFBDE8F041F4F76CBB5F +:10344000AF4A51789378194313D1114601288968FE +:1034500008D01079400703D591F86700072808D0F5 +:1034600001207047B1F84800098E884201D8FEF764 +:103470008BB900207047A249C2788968012A06D01A +:103480005AB1182A08D1B1F8F810FAF77ABCB1F895 +:103490000A114172090A81727047D1F800118988B6 +:1034A0004173090A8173704770B5954C05460E4605 +:1034B000A0882843A080A80703D5E80700D0FFDF35 +:1034C000E660E80700D02661A80719D5F07806283D +:1034D00002D00B2814D10BE0A06890F864101829D2 +:1034E0000ED10021E0E92A11012100F83E1C07E07D +:1034F000A06890F86410122902D1002180F86A10A7 +:10350000280601D50820A07068050AD5A068828821 +:10351000B0F85810304600F081FC3046BDE87040ED +:10352000A9E762E43EB505466846F4F7C0FA00B97B +:10353000FFDF2221009802F0A0F803210098FAF79B +:1035400011FB0098017821F0100101702946FAF76B +:103550002EFB6B4C192D71D2DFE805F020180D3EC3 +:10356000C8C8C91266C8C9C959C8C8C8C8BBC9C96A +:1035700071718AC89300A168009891F8FD1003E06A +:10358000A168009891F8CE100171B0E0A068D0F861 +:1035900004110098491CFAF756FBA8E0A1680098AE +:1035A000D1F8002192790271D1F80021128942717B +:1035B000120A8271D1F800215289C271120A027274 +:1035C000D1F8002192894272120A8272D1F8001158 +:1035D000C989FAF70FFB8AE0A068D0F800110098BB +:1035E000091DFAF73DFBA068D0F8001100980C31D6 +:1035F000FAF740FBA068D0F8001100981E31FAF7E6 +:103600003FFBA1680098C031FAF748FB6FE06269A0 +:1036100000981178017191884171090A817151886E +:10362000C171090A017262E03649D1E90001CDE9B0 +:10363000010101A90098FAF74BFB58E056E0A06899 +:10364000B0F840100098FAF755FBA068B0F8CE101B +:103650000098FAF753FBA068B0F844100098FAF706 +:1036600041FBA068B0F8D0100098FAF73FFB3EE0AD +:10367000A268009892F81811017192F8191141711D +:1036800035E0A06890F8FB00F9F729FF01460098A3 +:10369000FAF773FBA06890F8FA0000F033FA70B103 +:1036A000A06890F8540000F02DFA40B1A06890F89E +:1036B000FA1090F85400814201D0002002E0A06886 +:1036C00090F8FA00F9F70BFF01460098FAF751FB62 +:1036D0000DE0A06890F8F5100098FAF772FBA0686A +:1036E00090F8F4100098FAF770FB00E0FFDFF4F7B1 +:1036F000F1F900B9FFDF0098FFF7BDFE3EBD000005 +:1037000090010020BC5C0200F948806890F8FA1033 +:1037100009B990F8541080F8541090F8FB1009B9CA +:1037200090F8551080F855100020FEF771BEF8B5DE +:10373000EF4E00250446B060B5807570B5703570E9 +:103740000088F4F7B1F9B0680088F4F7D3F9B4F859 +:10375000E000B168401C82B201F15800F9F7D5F9D8 +:1037600000B1FFDF94F86500242809D1B4F858109F +:10377000B4F8F800081A00B2002801DB707830B104 +:1037800094F8640024280AD0252808D015E0FFF713 +:10379000BBFF84F86550B16881F87D500DE0B4F846 +:1037A0005810B4F8F800081A00B2002805DB707849 +:1037B00018B9FFF7A9FF84F86450A4F8E050FEF7A9 +:1037C0005EFD00281CD1B06890F8CC00FE2801D026 +:1037D000FFF7A8FEC7480090C74BC84A21462846B5 +:1037E000F7F766FFB0680023052190F86420583091 +:1037F000F9F7D4FB002803D0BDE8F840F7F7F3BC95 +:10380000F8BD10B5FEF73BFD20B10020BDE810402B +:103810000146C2E5BDE81040F7F7D0BF70B50C46D1 +:10382000064615464FF4A871204601F048FF268051 +:1038300005B9FFDF2868C4F800016868C4F804010E +:10384000A868C4F84C0191E4EFF7DDB92DE9F04127 +:103850000D4607460621EFF7CDF8041E3DD0D4F8FB +:103860004C110026087858B14A8821888A4207D12D +:1038700009280FD00E2819D00D2826D008283ED0B0 +:1038800094F82201D0B36E701020287084F8226161 +:10389000AF809FE06E7009202870D4F84C01416819 +:1038A00069608168A9608089A88133E00846EFF7E4 +:1038B000D3F90746EEF77FFE70B96E700E202870C0 +:1038C000D4F84C014068686011E00846EFF7C4F98D +:1038D0000746EEF770FE08B1002090E46E700D20F0 +:1038E0002870D4F84C014168696000892881D4F8B7 +:1038F0004C0106703846EEF758FE6BE00EE06E7035 +:1039000008202870D4F84C01416869608168A9607A +:10391000C068E860D4F84C0106705BE094F82401BC +:10392000A0B16E70152028700BE000BF84F82461F0 +:10393000D4F826016860D4F82A01A860B4F82E01F2 +:10394000A88194F824010028F0D143E094F83001D4 +:1039500070B16E701D20287084F83061D4F8320187 +:103960006860D4F83601A860B4F83A01A88131E063 +:1039700094F83C0140B16E701E20287084F83C61C0 +:10398000D4F83E01686025E094F81C0170B16E70B7 +:103990001B20287005E000BF84F81C61D4F81E01CC +:1039A000686094F81C010028F6D113E094F84201F5 +:1039B000002892D06E701620287007E084F84261CB +:1039C000D4F844016860B4F84801288194F84201B1 +:1039D0000028F3D1012012E4454A5061D1707047AC +:1039E00070B50D4604464EE0B4F8E000401CA4F863 +:1039F000E000B4F87E00401CA4F87E00204600F0F1 +:103A0000FEF9B8B1B4F87600401CA4F87600204660 +:103A100000F0E4F9B4F87600B4F87810884209D3DD +:103A20000020A4F87600012084F822013048C078F4 +:103A3000DFF772FA94F8880020B1B4F88400401CD3 +:103A4000A4F8840094F88C0020B1B4F88A00401CDB +:103A5000A4F88A0094F8FC0040B994F86720002389 +:103A6000012104F15800F9F799FA20B1B4F8820065 +:103A7000401CA4F882002046FEF795FFB4F85800D9 +:103A8000401CA4F858006D1EADB2ADD249E5184AED +:103A9000C2E90601704770B50446B0F87E0094F89C +:103AA0006810D1B1B4F880100D1A2D1F94F87C0065 +:103AB00040B194F864200023092104F15800F9F77B +:103AC0006DFA70B1B4F87660204600F098F938B11C +:103AD000B4F87800801B001F03E0C0F10205E5E7A1 +:103AE0002846A84200DA0546002D09DC002018E52A +:103AF000900100209B33020041340200A9340200EF +:103B0000A8B20EE510F00C0000D00120704710B5EF +:103B1000012808D0022808D0042808D0082806D098 +:103B2000FFDF204610BD0124FBE70224F9E7032450 +:103B3000F7E710B5EF4C0421A068FEF793F9A068F1 +:103B400090F84E10012903D0BDE8104000F098B95C +:103B5000022180F84E1010BD70B5E64CA06890F8B8 +:103B600064001F2804D0607840F001006070D8E441 +:103B70002069FAF7F4F8D8B1206901220179407977 +:103B800001F0070161F30705294600F0070060F323 +:103B90000F21A06880F888200022A0F8842023222A +:103BA00000F8642FD0F8B400BDE87040FEF76ABD9D +:103BB0000120FEF76CFFBDE870401E20FEF728BC18 +:103BC00070B5CC4C00230A21A06890F864205830CE +:103BD000F9F7E4F910B32069FAF79CF8A8B1A568E1 +:103BE0002069FAF793F82887A5682069FAF78AF818 +:103BF0006887A5682069FAF78BF8A887A568206907 +:103C0000FAF782F8E887FEF75DFDA168002081F8E9 +:103C1000880081F86400BDE870408AE7607840F071 +:103C2000010060707DE4B34810B580680088EFF74C +:103C300013F8BDE81040EEF7A9BC10B5AD4CA36871 +:103C400093F86400162802D00220607010BD6078DE +:103C50000028FBD1D3F80001002200F11E010E3034 +:103C6000B033F9F715F9A0680021C0E92811012146 +:103C700080F86910182180F8641010BD10B59D4CB3 +:103C8000A06890F86410132902D00220607010BD63 +:103C900061780029FBD1D0F8001100884988814261 +:103CA00000D0FFDFA068D0F8001120692631FAF7B4 +:103CB00002F8A1682069C431FAF705F8A168162056 +:103CC00081F8640010BD10B58A4C207900071BD51F +:103CD0006078002818D1A068002190F8CC00FEF789 +:103CE0001CFEA06890F8CC00FE2800D1FFDFA06881 +:103CF000FE2180F8CC1090F86710082904D1022129 +:103D00002170002180F8671010BD70B5794D242115 +:103D10000024A86890F86520212A05D090F8642036 +:103D2000232A18D0FFDF8EE590F8FA2012B990F818 +:103D3000FB202AB180F86510A86880F88C4082E5E5 +:103D400000F8654F047690F8B1000028F4D0002008 +:103D5000FEF75EFBF0E790F8FA2012B990F8FB202E +:103D60002AB180F86410A86880F888406BE580F874 +:103D700064400020FEF74CFBF5E770B55D4C002574 +:103D8000A068D0F8001103884A889A4218D10978AF +:103D9000042915D190F86420002319465830F9F70A +:103DA000FDF800B9FFDFA06890F89010890703D4F0 +:103DB000012180F8641003E000F8885F806F0570CF +:103DC000A0680023194690F865205830F9F7E6F806 +:103DD000002802D0A06880F88C5034E5B0F8782034 +:103DE000B0F876108A4201D3511A00E0002182888F +:103DF000521D8A4202D3012180F87C10704710B511 +:103E000090F86A1041B990F86420002306215830D8 +:103E1000F9F7C4F8002800D0012010BD70B5114496 +:103E2000344D891D8CB2C078A968012806D040B1F4 +:103E3000182805D191F8FA0038B109E0A1F80A4133 +:103E400001E5D1F800018480FDE491F8FB1091B107 +:103E5000FFF758FE80B1A86890F85400FFF752FEB3 +:103E600050B1A86890F8FA1090F85420914203D00D +:103E700090F8FB0000B90024A868A0F8F840E2E43C +:103E80002DE9F0411B4DA86800F58E740188618111 +:103E9000018EA181818EE181018FB0F84420914291 +:103EA00000D311462182828FB0F846108A4200D298 +:103EB0001146618290F85500FFF724FE4FF4296700 +:103EC00028B1608A3E46B84200D906466682A86894 +:103ED00090F85400FFF716FE20B1E089B84200D9EF +:103EE0000746E78101202072E878BDE8F041DFF75E +:103EF00013B800009001002070B58D4C0829207A7D +:103F000062D2DFE801F0041959592561615978B18D +:103F1000F2F73CFD01210846F2F7DFFEF3F713FD4F +:103F20000020A072F2F7E5FDBDE87040F3F766B837 +:103F3000BDE87040F0F7AABDD4E90001F0F79DFBA1 +:103F40002060A07A401CC0B2A07228281CD370BD8B +:103F5000A07A0025401EC6B2E0683044F3F73FF96E +:103F600010B9E1687F208855A07A272828BF01254D +:103F70002846F3F751FCA07A282809D2401CC0B289 +:103F8000A072282828BF70BDBDE87040F2F7B1BD0F +:103F9000207A00281CBF012000F085F8F2F7A0FF6E +:103FA000F3F71EF80120E07262480078DEF7B4FFF4 +:103FB000BDE87040F0F76ABD002808BF70BD002062 +:103FC000BDE8704000F06FB8FFDF70BD10B5584C11 +:103FD000207A002804BF0C2010BD00202072E0725F +:103FE000607AF1F7AEF9607AF1F7F9FB607AF0F7F1 +:103FF00024FE00280CBF1F20002010BD002270B539 +:104000004B4C06460D46207A68B12272E272607A05 +:10401000F1F797F9607AF1F7E2FB607AF0F70DFEBD +:10402000002808BFFFDF4348E560067070BD70B52B +:10403000050007D0A5F5E8503F494C3881429CBFA8 +:10404000122070BD3A4CE068002804BF092070BD02 +:10405000207A00281CBF0C2070BD3848F0F791FD75 +:104060006072202804BF1F2070BDF0F705FE20609D +:10407000002D1CBF284420600120656020720020B4 +:1040800000F011F8002070BD2949CA7A002A04BF47 +:10409000002070471F22027000224270CB684360EC +:1040A000CA72012070472DE9F04184B00746F0F74D +:1040B000E3FD1F4D8046414668682C6800EB800098 +:1040C00046002046F1F7F1FAB04206DB6868811B32 +:1040D0004046F0F7D2FA0446286040F23476214692 +:1040E0004046F1F7E2FAB04204DA31464046F0F7D2 +:1040F000C4FA044600208DF800004FF4DD60039000 +:1041000004208DF80500002F14BF012003208DF836 +:10411000040068460294F0F77EFF687A6946F0F77B +:10412000F5FF002808BFFFDF04B0BDE8F081000004 +:104130004C130020B0010020B5EB3C00F93E02001A +:104140002DE9F0410C4612490D68114A1149083217 +:104150001160A0F12001312901D301200CE0412898 +:1041600010D040CC0C4F94E80E0007EB8000241FC9 +:1041700050F8807C3046B84720600548001D056037 +:10418000BDE8F0812046DDF743F8F5E706207047EB +:104190001005024001000001C45C020010B5534844 +:1041A000F1F7CAFD00B1FFDF5048401CF1F7C4FD34 +:1041B000002800D0FFDF10BD2DE9F14F4C4ED6F89E +:1041C00000B001274948F1F7BFFDDFF8208128B989 +:1041D0005FF0000708F10100F1F7CCFD454C002528 +:1041E0004FF0030901206060C4F80051C4F8045185 +:1041F000009931602060DFF800A118E0DAF80000D3 +:10420000C00614D50E2000F064F8EFF3108010F013 +:10421000010072B600D00120C4F80493D4F8001154 +:1042200019B9D4F8041101B920BF00B962B6D4F8A5 +:10423000000118B9D4F804010028DFD0D4F8040133 +:104240000028CFD137B1C6F800B008F10100F1F76E +:104250007BFD11E008F10100F1F776FD0028B9D1EE +:10426000C4F80893C4F80451C4F800510E2000F0BB +:1042700030F81E48F1F77EFD0020BDE8F88F2DE9EB +:10428000F0438DB00D46064600240DF110090DF1E6 +:10429000200817E004EB4407102255F82710684661 +:1042A00001F06CF905EB870710224846796801F0A8 +:1042B00065F96846FFF780FF10224146B86801F0B3 +:1042C0005DF9641CB442E5DB0DB00020BDE8F0836D +:1042D00072E7002809DB00F01F020121914040092C +:1042E000800000F1E020C0F880127047B10100208A +:1042F00004E5004000E0004010ED00E0B14900207E +:104300000870704770B5B04D01232B60AF4B1C682F +:10431000002CFCD0002407E00E6806601E68002E0A +:10432000FCD0001D091D641C9442F5D300202860B8 +:1043300018680028FCD070BD70B5A24E0446A44D8C +:104340003078022800D0FFDFAC4200D3FFDF716974 +:10435000A048012903D847F23052944201DD0322DC +:104360004271491C7161291BC1609A497078F0F74C +:10437000CDFE002800D1FFDF70BD70B5914C0D4619 +:104380006178884200D0FFDF914E082D4BD2DFE8E4 +:1043900005F04A041E2D4A4A4A382078022800D0E7 +:1043A000FFDF03202070A078012801D020B108E0B1 +:1043B000A06800F039FE04E004F1080007C8FFF728 +:1043C000A1FF05202070BDE87040F0F75FBBF0F75B +:1043D00053FC01466068F1F768F9B04202D26169A6 +:1043E00002290BD30320F1F746FC12E0F0F744FC5E +:1043F00001466068F1F759F9B042F3D2BDE8704068 +:104400009AE7207802280AD0052806D0FFDF04208A +:104410002070BDE8704000F0CAB8022000E0032020 +:10442000F1F729FCF3E7FFDF70BD70B50546F0F743 +:1044300023FC644C60602078012800D0FFDF6549D0 +:10444000012008700020087104208D6048716048C8 +:10445000C860022020706078F0F758FE002800D174 +:10446000FFDF70BD10B5574C207838B90220F1F746 +:1044700018FC18B90320F1F714FC08B1112010BD85 +:104480005548F0F77EFB6070202804D00120207092 +:104490000020606110BD032010BD2DE9F0471446D7 +:1044A000054600EB84000E46A0F1040800F0CFFDA5 +:1044B00007464FF0805001694F4306EB8401091F06 +:1044C000B14201D2012100E0002189461CB10069FE +:1044D000B4EB900F02D90920BDE8F0872846DCF73D +:1044E000EBFE90B9A84510D3BD4205D2B84503D222 +:1044F00045EA0600800701D01020EDE73046DCF7E2 +:10450000DBFE10B9B9F1000F01D00F20E4E733480A +:1045100033490068884205D0224631462846FFF7D5 +:10452000F1FE14E0FFF79EFF0028D5D125480021B9 +:104530008560C0E90364817000F06FF810B14FF43A +:10454000A97000E0292060431830FFF76EFF0020BB +:10455000C2E770B505464FF0805004696C432046B1 +:10456000DCF7AAFE08B10F2070BD00F070FDA84274 +:1045700001D8102070BD194819490068884203D03D +:10458000204600F051FD10E0FFF76CFF0028F1D14C +:104590000C4801218460817000F03FF808B1114897 +:1045A00000E011481830FFF740FF002070BD10B543 +:1045B000044C6078F0F741FB00B9FFDF0020207069 +:1045C00010BD0000B401002004E5014000E40140FA +:1045D000105C0C005C1300207B43020054000020A0 +:1045E000BEBAFECA645E0100084C01004FF0805064 +:1045F000D0F830010A2801D0002070470120704710 +:1046000000B5FFF7F3FF20B14FF08050D0F8340130 +:1046100008B1002000BD012000BD4FF08050D0F84F +:104620003011062905D0D0F83001401C01D00020FF +:104630007047012070474FF08050D0F830010828B3 +:1046400001D0002070470120704700B5FFF7E5FF5B +:1046500048B14FF08050D0F83411062905D3D0F876 +:104660003401401C01D0002000BD012000BD00B578 +:10467000FFF7D3FF58B14FF08050D0F8341106291E +:1046800005D3D0F83401401C01D0012000BD00202A +:1046900000BD00007B49096801600020704779492E +:1046A00008600020704701218A0720B1012804D04A +:1046B00042F204007047916700E0D1670020704724 +:1046C00071490120086042F20600704708B50423D2 +:1046D0006D4A1907103230B1C1F80433106840F048 +:1046E000010010600BE0106820F001001060C1F8BC +:1046F00008330020C1F808016448006800900020D9 +:1047000008BD011F0B2909D85F4910310A6822F042 +:104710001E0242EA400008600020704742F2050095 +:1047200070470F2809D8584910310A6822F470627E +:1047300042EA002008600020704742F205007047FE +:10474000000100F18040C0F804190020704700010A +:1047500000F18040C0F8081900207047000100F106 +:104760008040D0F80009086000207047012801D976 +:1047700007207047464A52F8200002680A43026048 +:1047800000207047012801D907207047404A52F89D +:10479000200002688A43026000207047012801D986 +:1047A000072070473A4A52F820000068086000204D +:1047B0007047020037494FF0000003D0012A01D0B2 +:1047C000072070470A607047020033494FF000002D +:1047D00003D0012A01D0072070470A60704708B54E +:1047E0004FF40072510510B1C1F8042308E0C1F87C +:1047F00008230020C1F8240124481C3000680090E0 +:10480000002008BD08B58022D10510B1C1F80423ED +:1048100008E0C1F808230020C1F81C011B4814302F +:1048200000680090002008BD08B54FF48072910523 +:1048300010B1C1F8042308E0C1F808230020C1F832 +:1048400020011248183000680090002008BD0D4972 +:10485000383109680160002070474FF08041002026 +:10486000C1F80801C1F82401C1F81C01C1F82001F8 +:104870004FF0E020802180F800140121C0F80011E1 +:10488000704700000004004000050040080100409F +:10489000885D020078050040800500406249634B56 +:1048A0000A6863499A42096801D1C1F310010160A5 +:1048B000002070475C495D4B0A685D49091D9A42BA +:1048C00001D1C0F310000860002070475649574BD3 +:1048D0000A68574908319A4201D1C0F310000860B4 +:1048E0000020704730B5504B504D1C6842F2080311 +:1048F000AC4202D0142802D203E0112801D318469A +:1049000030BDC3004B481844C0F81015C0F814253A +:10491000002030BD4449454B0A6842F209019A42E1 +:1049200002D0062802D203E0042801D308467047CB +:10493000404A012142F83010002070473A493B4B71 +:104940000A6842F209019A4202D0062802D203E024 +:10495000042801D308467047364A012102EBC00003 +:1049600041600020704770B52F4A304E314C1568B9 +:1049700042F2090304EB8002B54204D0062804D2B7 +:10498000C2F8001807E0042801D3184670BDC1F32F +:104990001000C2F80008002070BD70B5224A234EF6 +:1049A000244C156842F2090304EB8002B54204D09E +:1049B000062804D2D2F8000807E0042801D31846DC +:1049C00070BDD2F80008C0F310000860002070BD70 +:1049D000174910B50831184808601120154A002100 +:1049E00002EBC003C3F81015C3F81415401C1428BB +:1049F000F6D3002006E0042804D302EB8003C3F8BA +:104A0000001807E002EB8003D3F80048C4F3100459 +:104A1000C3F80048401C0628EDD310BD04490648E1 +:104A2000083108607047000054000020BEBAFECA7A +:104A300000F5014000F001400000FEFF834B1B68C1 +:104A400003B19847BFF34F8F81480168814A01F451 +:104A5000E06111430160BFF34F8F00BFFDE710B568 +:104A6000EFF3108010F0010F72B601D0012400E0C6 +:104A7000002400F0E1F850B1DCF7BEFCEFF7C1FE16 +:104A8000F1F79BF8E7F75EFA73490020086004B974 +:104A900062B6002010BD2DE9F0410C460546EFF34B +:104AA000108010F0010F72B601D0012600E0002640 +:104AB00000F0C2F820B106B962B60820BDE8F08166 +:104AC000DCF78EFBDCF79CFC024600200123470943 +:104AD000BF0007F1E02700F01F01D7F80071CF40B9 +:104AE000F9071BD0202803D222FA00F1C90727D1E9 +:104AF00041B2002904DB01F1E02191F8001405E046 +:104B000001F00F0101F1E02191F8141D4909082974 +:104B100016D203FA01F717F0EC0F11D0401C6428ED +:104B2000D5D3E7F7EDF94D4A4D490020E7F730FAC4 +:104B300049494C4808602046DCF7C5FB60B904E0F1 +:104B400006B962B641F20100B8E7404804602DB1F1 +:104B50002846DCF705FC18B110242CE0424D19E082 +:104B60002878022802D94FF4805424E00724002832 +:104B7000687801D0F8B908E0E8B120281BD8A878F7 +:104B8000212818D8012816D001E0A87898B9E8782B +:104B90000B2810D83549802081F8140DDCF730FC43 +:104BA0002946F0F7F0FFEFF7EBFD00F083FA284617 +:104BB000DCF7F4FB044606B962B61CB1FFF74FFF01 +:104BC00020467BE7002079E710B5044600F034F872 +:104BD00000B101202070002010BD25490860002090 +:104BE000704770B50C4623490D682249224E0831A2 +:104BF0000E60102807D011280CD012280FD01328CF +:104C000011D0012013E0D4E90001FFF744FF35463D +:104C100020600DE0FFF723FF0025206008E02068FA +:104C2000FFF7D2FF03E012492068086000202060EF +:104C30001048001D056070BD07480A490068884299 +:104C400001D101207047002070470000CC010020F6 +:104C50000CED00E00400FA0554000020F8130020D9 +:104C600000000020BEBAFECA905D02000BE000E02A +:104C700004000020100502400100000100B59B491E +:104C800002282ED021DC10F10C0F08BFF42028D010 +:104C90000FDC10F1280F08BFD82022D010F1140F1C +:104CA00008BFEC201DD010F1100F08BFF02018D065 +:104CB00021E010F1080F08BFF82012D010F1040F06 +:104CC0000CBFFC2000280CD015E0A0F10300062842 +:104CD00011D2DFE800F00E0C0A080503082000E0FE +:104CE0000720086000BD0620FBE70520F9E7042047 +:104CF000F7E70320F5E7FFDF00BD00B57C49012899 +:104D000008BF03200CD0022808BF042008D00428C4 +:104D100008BF062004D0082816BFFFDF052000BD0D +:104D2000086000BD70B505460C4616461046F2F701 +:104D3000C1FD022C08BF4FF47A7105D0012C0CBFC5 +:104D40004FF4C86140F6340144183046F2F7ECFDE8 +:104D5000204449F6797108444FF47A71B0FBF1F0C0 +:104D6000281A70BD70B505460C460846F2F7BBFD23 +:104D7000022C08BF40F24C4105D0012C0CBF40F67C +:104D800034014FF4AF5149F6CA62511A08444FF446 +:104D90007A7100F2E140B0FBF1F0281A801E70BD7C +:104DA00070B5064615460C460846F2F79CFD022DE6 +:104DB00008BF4FF47A7105D0012D0CBF4FF4C861C4 +:104DC00040F63401022C08BF40F24C4205D0012CC1 +:104DD0000CBF40F634024FF4AF52891A084449F62A +:104DE000FC6108444FF47A71B0FBF1F0301A70BDE9 +:104DF00070B504460E460846F2F75CFD054630469F +:104E0000F2F792FD28444AF2AB3108444FF47A712C +:104E1000B0FBF1F0201A801E70BD2DE9F04107466D +:104E20001E460D4614461046082A16BF04284EF6A4 +:104E30002830F2F73FFD07EB4701C1EBC71100EB4C +:104E4000C100022D08BF40F24C4105D0012D0CBF1E +:104E500040F634014FF4AF5147182846F2F743FDAE +:104E6000381A4FF47A7100F6B730B0FBF1F52046EE +:104E7000F2F70EFD28443044401DBDE8F08170B5C6 +:104E8000054614460E460846F2F714FD05EB4502AA +:104E9000C2EBC512C0EBC2053046F2F745FD2D1A34 +:104EA0002046082C16BF04284EF62830F2F702FDE3 +:104EB00028444FF47A7100F6B730B0FBF1F5204684 +:104EC000F2F7E6FC2844401D70BD0A49082818BFC7 +:104ED0000428086803BF20F46C5040F4444040F0BC +:104EE000004020F000400860704700000C150040B2 +:104EF00010150040401700402DE9FE430C46804647 +:104F0000F8F744FE074698F80160204601A96A4672 +:104F1000EDF72DFB05000DD0012F02D00320BDE8D9 +:104F2000FE83204602AA0199EDF743FA0298B0F8F1 +:104F300003000AE0022F14D1042E12D3B8F80300A4 +:104F4000BDF80020011D914204D8001D80B2A919AE +:104F5000814202D14FF00000E1E702D24FF00100A0 +:104F6000DDE74FF00200DAE7C2790D2341B342BB1F +:104F70008188012904D94908818004BF01228280E7 +:104F80000168012918BF002930D001686FEA0101CA +:104F9000C1EBC10202EB011281796FEA010101EB61 +:104FA0008103C3EB811111444FEA91420160818872 +:104FB000B2FBF1F301FB132181714FF0010102E01B +:104FC0001AB14FF00001C17170478188FF2908D2E2 +:104FD0004FF6FF7202EA41018180FF2984BFFF2260 +:104FE00082800168012918BF0029CED10360CCE777 +:104FF000817931B1491E11F0FF0181711CBF002080 +:1050000070470120704710B50121C1718171818005 +:1050100004460421F0F712FF002818BF10BD2068D5 +:10502000401C206010BD00000B4A022111600B499A +:105030000B68002BFCD0084B1B1D1860086800286B +:10504000FCD00020106008680028FCD070474FF0AA +:10505000805040697047000004E5014000E40140D1 +:1050600002000B464FF00000014620D0012A04D078 +:10507000022A04D0032A0DD103E0012002E002201D +:1050800015E00320072B05D2DFE803F00406080A29 +:105090000C0E100007207047012108E0022106E0F5 +:1050A000032104E0042102E0052100E00621EFF7DE +:1050B00086BD0000F9480521817000210170417012 +:1050C0007047F7490A78012A05D0CA681044C860B9 +:1050D0004038F0F7B7BA8A6810448860F8E70028CB +:1050E00019D00378EF49F04A13B1012B0ED011E02B +:1050F0000379012B00D06BB943790BB1012B09D196 +:105100008368643B8B4205D2C0680EE00379012BB3 +:1051100002D00BB10020704743790BB1012BF9D1BC +:10512000C368643B8B42F5D280689042F2D801207C +:105130007047DB4910B501220A700279A2B1002242 +:105140000A71427992B104224A718268D34C523278 +:105150008A60C0681434C8606060EFF78DFDCF4985 +:1051600020600220887010BD0322E9E70322EBE7EC +:1051700070B5CB4D044600202870207988B10020FE +:105180002871607978B10420C44E6871A168F06814 +:10519000EFF773FAA860E0685230E8600320B0705F +:1051A00070BD0120ECE70320EEE72DE9F041054654 +:1051B0000226F0F773F9006800B1FFDFB74C012752 +:1051C0003DB12878B0B1012805D0022810D00328BD +:1051D00013D027710CE06868C82807D3F0F799FA54 +:1051E00020B16868FFF76DFF012603E0002601E0AB +:1051F00000F05EF93046BDE8F08120780028F7D154 +:105200006868FFF76CFF0028E3D06868017879B11F +:10521000A078042800D0FFDF01216868FFF7A8FF0D +:105220009F49E078EFF772FF0028E1D1FFDFDFE769 +:10523000FFF77FFF6770DBE72DE9F047974C884663 +:10524000E178884200D0FFDFDFF850920025012787 +:10525000934E09F11409B8F1080F75D2DFE808F090 +:10526000040C28527A808D95A078032802D0022859 +:1052700000D0FFDFBDE8F087A078032802D0022825 +:1052800000D0FFDF0420A07025712078002878D19D +:10529000FFF717FF3078012806D0B068E06000F013 +:1052A00027F92061002060E0E078EFF72CFEF5E7B9 +:1052B000A078032802D0022800D0FFDF2078002841 +:1052C0006DD1A078032816D0EFF7D6FC01464F46E3 +:1052D000D9F80000F0F7E9F900280EDB796881427F +:1052E0000BDB081AF0606E49E078EFF70FFF00283B +:1052F000C0D1FFDFBEE7042028E00420F0F7BBFCAC +:10530000A570B7E7A078032802D0022800D0FFDFFD +:10531000207888BBA078032817D0EFF7ADFC0146B2 +:105320004F46D9F80000F0F7C0F90028E5DB7968AE +:105330008142E2DB081AF0605949E078EFF7E6FEB7 +:10534000002897D1FFDF95E740E00520F0F793FCB8 +:10535000A7708FE7A078042800D0FFDF022004E0C8 +:10536000A078042800D0FFDF0120A1688847FFF75C +:105370001CFF054630E004E011E0A078042800D0CE +:10538000FFDFBDE8F04700F093B8A078042804D010 +:10539000617809B1022800D0FFDF207818B1BDE89C +:1053A000F04700F08EB8207920B10620F0F763FCBA +:1053B0002571CDE7607838B13949E078EFF7A6FE7E +:1053C00000B9FFDF657055E70720BFE7FFDF51E752 +:1053D0003DB1012D03D0FFDF022DF9D14AE70420B2 +:1053E000C3E70320C1E770B5050004D02B4CA078BB +:1053F000052806D101E0102070BD0820F0F751FC0F +:1054000008B1112070BD2948EFF7BBFBE0702028E0 +:1054100006D00121F0F777FA0020A560A07070BDDA +:10542000032070BD1D4810B5017809B1112010BDD1 +:105430008178052906D0012906D029B10121017002 +:10544000002010BD0F2010BD00F03BF8F8E770B54C +:10545000124C0546A07808B1012809D155B128465B +:10546000FFF73DFE40B1287840B1A078012809D06F +:105470000F2070BD102070BD072070BD2846FFF7BB +:1054800058FE03E000212846FFF772FE0449E07849 +:10549000EFF73CFE00B9FFDF002070BDD001002017 +:1054A0006C1300203D860100FF1FA1073952020046 +:1054B0000A4810B5006900F013F8BDE81040EFF796 +:1054C000E5BA064810B5C078EFF7B7FB00B9FFDFC3 +:1054D0000820F0F7D0FBBDE81040EBE5D00100203C +:1054E0000C490A6848F202139A4302430A60704763 +:1054F000084A116848F2021301EA03009943116057 +:1055000070470246044B10201344FC2B01D8116055 +:1055100000207047C80602400018FEBF7047704761 +:105520007047704740EA010310B59B070FD1042A6A +:105530000DD310C808C9121F9C42F8D020BA19BA5E +:10554000884201D9012010BD4FF0FF3010BD1AB1C3 +:10555000D30703D0521C07E0002010BD10F8013B18 +:1055600011F8014B1B1B07D110F8013B11F8014B3F +:105570001B1B01D1921EF1D1184610BD032A40F227 +:10558000308010F0030C00F0158011F8013BBCF1E5 +:10559000020F624498BF11F801CB00F8013B38BFFD +:1055A00011F8013BA2F1040298BF00F801CB38BF0B +:1055B00000F8013B11F0030300F02580083AC0F029 +:1055C000088051F8043B083A51F804CBA0E80810D1 +:1055D000F5E7121D5CBF51F8043B40F8043BAFF304 +:1055E0000080D20724BF11F8013B11F801CB48BF5E +:1055F00011F8012B24BF00F8013B00F801CB48BF94 +:1056000000F8012B704710B5203AC0F00B80B1E8CC +:105610001850203AA0E81850B1E81850A0E81850E7 +:10562000BFF4F5AF5FEA027C24BFB1E81850A0E8F0 +:10563000185044BF18C918C0BDE810405FEA827C0A +:1056400024BF51F8043B40F8043B08BF7047D20721 +:1056500028BF31F8023B48BF11F8012B28BF20F8C2 +:10566000023B48BF00F8012B704702F0FF0343EAFA +:10567000032242EA024200F002B84FF0000204297D +:10568000C0F0128010F0030C00F01B80CCF1040C71 +:10569000BCF1020F18BF00F8012BA8BF20F8022BA5 +:1056A000A1EB0C0100F00DB85FEAC17C24BF00F84B +:1056B000012B00F8012B48BF00F8012B70474FF079 +:1056C000000200B5134694469646203922BFA0E852 +:1056D0000C50A0E80C50B1F12001BFF4F7AF09075E +:1056E00028BFA0E80C5048BF0CC05DF804EB89004F +:1056F00028BF40F8042B08BF704748BF20F8022B92 +:1057000011F0804F18BF00F8012B704770477047A9 +:1057100070477047FEDF18490978F9B904207146CF +:1057200008421BD10699154A914217DC06990229B5 +:1057300014DB02394878DF2810D10878FE2807D01A +:10574000FF280BD14FF001004FF000020C4B18471F +:1057500041F201000099019A094B1847094B002BAF +:1057600002D01B68DB6818474FF0FF3071464FF0DE +:105770000002034B1847000028ED00E00060020023 +:105780003D4A020004000020174818497047FFF7FF +:10579000FBFFDBF713FD00BD154816490968884279 +:1057A00003D1154A13605B68184700BD20BFFDE7B1 +:1057B0000F4810490968884210D1104B18684FF003 +:1057C000FF318842F2D080F308884FF020218842D0 +:1057D00004DD0B48026803210A4302600948804740 +:1057E00009488047FFDF000080130020801300205D +:1057F00000100000000000200400002000600200F3 +:1058000014090040C52F000099570200042071467A +:10581000084202D0EFF3098101E0EFF308818869C3 +:1058200002380078102813DB20280FDB2C280BDB34 +:105830000A4A12680A4B9A4203D1602804DB094ADB +:105840001047022008607047074A1047074A104770 +:10585000074A12682C3212681047000054000020DA +:10586000BEBAFECA0514000041410200E34B02002B +:10587000040000200D4B0E4908470E4B0C49084709 +:105880000D4B0B4908470D4B094908470C4B08497C +:1058900008470C4B064908470B4B054908470B4B7B +:1058A000034908470A4B024908470000E1BC0000D1 +:1058B0005DC00000552D0000CF2B00005D2B0000C7 +:1058C000F72D0000211400001B2900004D2F0000BF +:1058D000C911000000210160818070470021016032 +:1058E0004160017270470A6802600B79037170476A +:1058F000959600003F980000A1990000059A0000CD +:105900003F9A0000739A0000AD9A0000DD9A0000F3 +:10591000579B00008D970000C5990000A71200005A +:10592000C14300000D44000073440000FF44000028 +:1059300023460000E546000017470000EF4700003F +:1059400087480000DB480000C1490000E149000031 +:10595000C3160000E7160000171600006B160000C3 +:1059600019170000AD17000047600000F761000044 +:10597000BD650000D56600005F670000DD670000C0 +:105980004168000061690000316A00009D6A000002 +:10599000034A0000094A0000134A00007B4A000045 +:1059A000A74A0000634C00008D4C0000C54C00006D +:1059B0002F4D0000194E00002F4E00003144000012 +:1059C000A7120000A7120000A7120000A7120000F3 +:1059D000A7120000A7120000A7120000A3250000D4 +:1059E000292600004526000061260000EF27000060 +:1059F0008B26000095260000D7260000F92600001F +:105A0000D527000017280000A7120000A7120000E9 +:105A1000CB830000EB830000F58300002F8400009F +:105A20005D8400004D850000DB850000EF850000EF +:105A30003D86000053870000F9880000218A00009D +:105A40004F730000398A0000A7120000A71200005F +:105A5000D9B5000043B7000097B7000003B80000B5 +:105A6000B3B80000010000000000000010011001A8 +:105A70003A0200001A02000405060000FFFFFFFFC3 +:105A80000000FFFFCDAD0000233D000049210000D4 +:105A900099730000118F000000000000D5910000F4 +:105AA00099910000C3910000AB910000000002003A +:105AB00000000000000200000000000000010000E3 +:105AC000000000007781000057810000C5810000C0 +:105AD00025250000E72400000725000037A9000065 +:105AE00063A900006BAB000041590000E581000094 +:105AF0000000000015820000732500000000000077 +:105B000000000000000000004DAA0000000000009E +:105B1000D55900000300000001555555D6BE898EA9 +:105B200000006306630C631200000703AB054F0817 +:105B3000000053044308330C00000000900A0000EA +:105B4000900A0000C3560000C35600009D430000A9 +:105B500079AC00001B7600005B2000001D380200BD +:105B6000E1A401000157000001570000BF430000FD +:105B7000DBAC00009F760000CD2000004938020019 +:105B8000F5A4010070017001400038005C002400A1 +:105B90005001080200000300656C74620000000000 +:105BA000000000000000000000000000870000006E +:105BB000000000000000000000000000BE83605AEA +:105BC000DB0B376038A5F5AA9183886C01000000D3 +:105BD000BB31010081400100000000010206030406 +:105BE00005000000070000000000000006000000A3 +:105BF0000A0000003200000073000000B400000042 +:105C0000EB8F01006F1F020017F90000D9B70100E8 +:105C1000F3F70100D9B70100B5FA000097B9010008 +:105C2000E9F3010097B90100F1F6000025B9010080 +:105C300011F7010025B9010013F90000EDB70100CB +:105C4000D5EF0100EDB7010067FF000019BC0100AE +:105C5000A7F8010019BC0100F401FA0096006400E5 +:105C60004B0032001E0014000A0005000200010073 +:105C70000049000000000000AAAED7AB154120107B +:105C80000C0802170D010102090901010602091899 +:105C9000180301010909030305555555252627D683 +:105CA000BE898E00F401FA00960064004B003200B9 +:105CB0001E0014000A000500020001002549000032 +:105CC000000000009D480200B5480200CD480200D7 +:105CD000E5480200154902003D49020067490200FB +:105CE0009B490200534502009B4402008D41020083 +:105CF00003550200395D0100495D0100755D010039 +:105D0000475E01004F5E0100615E0100A746020090 +:105D1000C1460200954602009F460200CD460200A1 +:105D20000347020023470200414702004F47020099 +:105D30005D4702006D470200854702009D47020053 +:105D4000B3470200C94702000000000087BA000004 +:105D5000DDBA0000F3BA000061500200B941020050 +:105D60007F420200E7530200255402004F54020014 +:105D7000195C010079600100DF470200054802005C +:105D8000294802004F4802001C0500402005004041 +:105D900000100200B45D020008000020E4010000D1 +:105DA00044110000E85D0200EC01002094110000A5 +:105DB000A0110000011413F8130240200B20040668 +:105DC000441A0102228C2720FB349B5F8012800240 +:105DD0001E101B430B5419042A8608019F0916CB79 +:085DE000327F0B6CF410C000CF +:02000004000FEB +:1040000000000420CDB20F00F5B20F00F7B20F0090 +:10401000F9B20F00FBB20F00FDB20F00000000006C +:10402000000000000000000000000000C1450F007B +:1040300001B30F000000000003B30F00214D0F007B +:10404000354E0F0007B30F0007B30F0007B30F0083 +:1040500007B30F0007B30F0007B30F0007B30F003C +:1040600007B30F0007B30F0007B30F0007B30F002C +:1040700007B30F0007B30F0007B30F0007B30F001C +:1040800007B30F0085720F0007B30F0007B30F00CF +:1040900041730F0007B30F00814B0F0007B30F00F0 +:1040A00007B30F0007B30F0007B30F0007B30F00EC +:1040B00007B30F0007B30F0000000000000000006E +:1040C00007B30F0007B30F0007B30F0007B30F00CC +:1040D00007B30F0007B30F0007B30F0005850F00EC +:1040E00007B30F0007B30F0007B30F000000000075 +:1040F0000000000007B30F000000000007B30F002E +:1041000000000000000000000000000000000000AF +:10411000000000000000000000000000000000009F +:10412000000000000000000000000000000000008F +:10413000000000000000000000000000000000007F +:10414000000000000000000000000000000000006F +:10415000000000000000000000000000000000005F +:10416000000000000000000000000000000000004F +:10417000000000000000000000000000000000003F +:10418000000000000000000000000000000000002F +:10419000000000000000000000000000000000001F +:1041A000000000000000000000000000000000000F +:1041B00000000000000000000000000000000000FF +:1041C00000000000000000000000000000000000EF +:1041D00000000000000000000000000000000000DF +:1041E00000000000000000000000000000000000CF +:1041F00000000000000000000000000000000000BF +:104200000348044B834202D0034B03B11847704765 +:10421000C8860020C8860020000000000548064926 +:104220000B1AD90F01EBA301491002D0034B03B1C4 +:1042300018477047C8860020C8860020000000008C +:1042400010B5064C237843B9FFF7DAFF044B13B1DE +:104250000448AFF300800123237010BDC8860020FE +:10426000000000005CBD0F0008B5044B1BB1044901 +:104270000448AFF30080BDE80840CFE7000000002D +:10428000CC8600205CBD0F00A3F5803A704700BFCC +:10429000154B002B08BF114B9D46FFF7F5FF002182 +:1042A0008B460F461148124A121A00F075F80C4B53 +:1042B000002B00D098470B4B002B00D098470020D4 +:1042C000002104000D000B4800F016F800F040F843 +:1042D0002000290000F074FA00F014F80000080033 +:1042E000000000000000000000000420C88600203C +:1042F000A4CE002025430F00002301461A4618468D +:1043000000F09CB808B50021044600F0CBF8044B3F +:104310001868C36B03B19847204600F029F900BF25 +:1043200058BB0F0038B5084B084D5B1B9C1007D0DD +:10433000043B1D44013C55F804399847002CF9D141 +:10434000BDE8384007F002BCC8860020C4860020C3 +:1043500070B50D4E0D4D761BB61006D0002455F8E5 +:10436000043B01349847A642F9D1094E094D761B0A +:1043700007F0E6FBB61006D0002455F8043B0134E4 +:104380009847A642F9D170BDBC860020BC860020AB +:10439000C4860020BC860020830730B548D0541E58 +:1043A000002A3FD0CAB2034601E0013C3AD303F8E9 +:1043B000012B9D07F9D1032C2DD9CDB245EA052556 +:1043C0000F2C45EA054536D9A4F1100222F00F0C56 +:1043D00003F1200EE6444FEA121C03F1100242E9F9 +:1043E000045542E9025510327245F8D10CF1010230 +:1043F00014F00C0F03EB021204F00F0C13D0ACF10D +:10440000040323F003030433134442F8045B934290 +:10441000FBD10CF003042CB1CAB21C4403F8012BED +:104420009C42FBD130BD64461346002CF4D1F9E721 +:1044300003461446BFE71A46A446E0E770B4184C9A +:104440002568D5F848411CB365681F2D25DC38B9AF +:10445000AB1C0135656044F82310002070BC704728 +:1044600004EB850C0228CCF88820D4F888614FF042 +:10447000010202FA05F246EA0206C4F88861CCF8A5 +:104480000831E5D1D4F88C311343C4F88C31DFE71F +:1044900005F5A674C5F84841D6E74FF0FF30DDE7D3 +:1044A00058BB0F002DE9F84F2B4B1F68D7F8486118 +:1044B0002DED028BC6B108EE100A8B464FF00108B5 +:1044C0004FF000097468651E0ED4013406EB8404B5 +:1044D000BBF1000F0CD0D4F800315B4508D0013D92 +:1044E0006B1CA4F10404F3D1BDEC028BBDE8F88F82 +:1044F00073682268013BAB420CBF7560C4F8009042 +:10450000002AECD0D6F88801D6F804A008FA05F104 +:1045100001420BD190477268524513D1D7F8483108 +:10452000B342DCD01E46002ECCD1DDE7D6F88C019C +:1045300001420CD1D4F8801018EE100A904772682E +:104540005245EBD0D7F84861002EBBD1CCE7D4F868 +:1045500080009047DFE700BF58BB0F00024B13B14C +:104560000248FFF7C9BE70470000000025430F0056 +:10457000FEE700BF38B50C46E8B90968C9B10F4D70 +:10458000A9420BD06B1A3B2B11D93C22284606F0CE +:10459000EDFE03E0CA5CEA54013BFBD2074800226F +:1045A0003C2103F0D3F90023A887236038BD3D23C5 +:1045B000F2E70E23F9E70123F7E700BF807F002031 +:1045C000074A6FF002039E4502D1EFF3098101E033 +:1045D000EFF308818869A0F102000078104700BF5E +:1045E00075450F0038B50446A8B10D4D00223C2199 +:1045F000284603F0ABF9AB8F83420ED12A4605F172 +:104600003C0152F8040B44F8040B8A42F9D10133FF +:10461000AB87002038BD0E20FCE70B20FAE700BF77 +:10462000807F00200B2970B50446154608462FD917 +:104630002389053304EB43012044431ADAB2012AEB +:1046400026D9814224D8134806F090FE2388522BA5 +:1046500006D1AB0711D062884CF668639A420CD041 +:104660000F2014E034F8022B824204D0AE89964227 +:1046700003F1010308D1002009E0218900230A3455 +:104680004FF6FE704FF440559942EBD80B2070BDA9 +:104690000920FCE7E486002008B5002203F056F963 +:1046A000034B1B88834214BF0B20002008BD00BFB2 +:1046B000E486002038B50C4C21684B1C054612D00E +:1046C0000A484FF4805206F01FFE48B115B1206829 +:1046D00000F00CFC054920684FF4806200F026FCD5 +:1046E0004FF0FF33236038BD30840020F086002077 +:1046F0002DE9F041DFF84480044624F47F65184634 +:10470000D8F8003025F00F05AB420E46174609D009 +:10471000FFF7D0FF0848C8F800504FF480522946F0 +:1047200006F024FE0448C4F30B043A463146204404 +:10473000BDE8F04106F01ABEF0860020308400206B +:10474000BFF34F8F0549064BCA6802F4E06213437A +:10475000CB60BFF34F8F00BFFDE700BF00ED00E06F +:104760000400FA054BDF704710DF704711DF704718 +:1047700013DF704718DF704760DF704769DF7047ED +:1047800061DF70471FB50023CDE90133039368460D +:1047900002230093FFF7EEFF05B05DF804FB08B5B8 +:1047A0004FF0E023D3F8F03DDB0700D500BEFFF764 +:1047B000C7FF0000014B1878704700BFF19600203A +:1047C0002DE9FF484C4B40F60212C3F8402500F09B +:1047D00005FA00F021FE002000F0AAFA00F09CFE8D +:1047E00048B1052000F0A4FA00F0A8FE00F0CCFECD +:1047F000062000F09DFA4FF08043DFF81081D3F8D7 +:104800001C55B12D0CBF0123002388F800304AD07D +:10481000A5F1A8014C424C41384EDFF8F49004F069 +:10482000010333704FF08043D3F8007407F00107A1 +:10483000002C3BD14E2D38D0572D36D0304B1B6835 +:104840001A68304B9A4200D17FBB6D2D2ED01220BA +:1048500000F0B0F9044633789BBB122000F0AAF9AF +:1048600010B1122000F0A6F900F00100307000F045 +:1048700005FDDFF8A0B0824630B12CB9204B1B6893 +:104880001A68214B9A4257D04FF440535A684A4510 +:104890000ABF9B684FF4905303F500731B685B4598 +:1048A0003AD1012448E00124B6E701244FF08043C7 +:1048B00000226D2DC3F81C2500F0E480002CCAD125 +:1048C000C5E70120D0E7544636E03C4634E04FF4DB +:1048D00080030B6071E0022000F02AFAA5F14E037C +:1048E0005842584103F012FEAEE000221146C1E0EA +:1048F00003F05CFEC6E000BF00A00040F19600207F +:1049000034840020D51A5A007E67E54EF2960020C6 +:10491000DBE5B1517CB0EE87002CC2D1BAF1000FBB +:10492000D1D0002FD1D0654B654A1B6865481A600D +:10493000654B43F0010398474FF440535A684A458A +:1049400008BF9B685D4A0CBF03F500734FF490539A +:1049500012681B685B450CBF5C4B002313601CB9DD +:10496000BAF1000F40F08E803378002BB3D00820CE +:1049700000F0DEF998F800300BB9FFF703FF0123D0 +:104980004FF4742088F80030FFF7F2FE504B514985 +:104990001868019001A8FFF7E7FE4F4991F8163318 +:1049A0005A09EC231341DA0707D54C4B9A68002AC1 +:1049B0008DD01A6842F480021A600C22484B029390 +:1049C00000210DEB0200FFF7E7FC40F20113029A11 +:1049D000039303A94020FFF7D1FE0C2200210DEB29 +:1049E0000200FFF7D9FC9DF80C30029A43F0010356 +:1049F00003A9A0208DF80C30FFF7C0FE0C22002187 +:104A00000DEB0200FFF7C8FC01241723029AADF852 +:104A10000E3003A923208DF80C40FFF7AFFE0C22C7 +:104A200000210DEB0200FFF7B7FC0623029A8DF878 +:104A30000C4003A920208DF80E40ADF81030FFF790 +:104A40009DFE02A8FFF798FE4FF4405330785A6855 +:104A50004A450ABF9B684FF4905303F500731B68E7 +:104A60005B4504D0572D02D04E2D7FF43EAF01227E +:104A700040F6B83100F0E8FC3378002B3FF438AF53 +:104A8000FFF774FE00F0EEF800F0F8FBA0B100F0C4 +:104A900043FD88B94FF440535B684B4506D198F805 +:104AA00000300BB9FFF76EFEFFF760FE034B1B688B +:104AB00000221A6000F004FDFFF742FE348400205B +:104AC000D51A5A000048E80160BB0F007E67E54E2A +:104AD0005CBB0F009F470F0000E100E0409D0020FD +:104AE0000080002010B58EB03423ADF802300DF1F7 +:104AF0000201002301A8ADF80430FFF741FE04468F +:104B000040B9BDF80430102B07D0112B0CD001A8F0 +:104B100000F0CCFF20460EB010BD054B01221A70EC +:104B2000072000F005F9F2E7014B18700820F8E7BC +:104B3000F096002013B5002301A80193FFF712FEA1 +:104B4000044660B9019802F053FA019B0A2B09D080 +:104B5000092B09D00B2B02D1012004F09BFB20462E +:104B600002B010BD2046F8E70220F6E708B5FFF7CF +:104B7000B9FF0528FBD1FFF7DDFF0528F7D108BDF8 +:104B80000021024A084602F003BE00BF6D4B0F0031 +:104B90001F2884BF00F01F00044B054A98BF4FF048 +:104BA000A04300F5E07043F8202070470003005058 +:104BB0000C0003001F288ABF064B4FF0A04300F0F3 +:104BC0001F00D3F8103523FA00F0C04300F00100B5 +:104BD000704700BF000300507047000008B54FF059 +:104BE000804301220021DA601220C3F818159A6070 +:104BF000FFF7CEFF1220FFF7CBFF154B4FF4C85045 +:104C000043F001039847FFF7E7FF124A1E210820EF +:104C100002F09AFD08B102F051FE02F0FBFC0E49D1 +:104C20000E4BE02081F823001B684FF47A72B3FB2F +:104C3000F2F3013BB3F1807F08D24FF0E0225361E1 +:104C4000002381F8230093610723136108BD00BF8F +:104C500070BB0F00F496002000ED00E038840020C7 +:104C6000704700004FF0E0224FF40031002310B5F0 +:104C70001361C2F8801102F1C04202F540524FF4B4 +:104C80008031C2F84813C2F80813012151609160C5 +:104C90004FF080420A4CD16002201F2B1A46C6BF3B +:104CA00003F01F0221464FF0A04102F5E0720133EC +:104CB000302B41F82200F0D1FFF7D2FF10BD00BF2A +:104CC00000030050074B23F81010074B002282B05E +:104CD000C3F81021D3F810210192019A01229A60A1 +:104CE00002B07047E898002000C001400A4A0B4B10 +:104CF00011681B68B1FBF3F203FB1211B1EB530F08 +:104D00004FEA530288BF591A4F2359430020B1FB81 +:104D1000F2F189B2FFF7D6BFE4980020F0980020A6 +:104D2000024A136801331360FFF7E0BFE4980020E4 +:104D30001B490A68082823D8DFE800F0130513226E +:104D4000221A1E242A00174B40F6B83018604FF480 +:104D50007F4303F0103323F080539A4218BF0B6057 +:104D60007047104B4FF4967018604FF47F03F0E7D4 +:104D70000C4B64221A6070470A4B40F6B83018603A +:104D80001346E6E7074B40F6B8301860FF23E0E72C +:104D9000044B4FF4967018604FF0FF13D9E700BF33 +:104DA000F4980020F098002000F1804382B01A6847 +:104DB000002A14BF0120002004D000221A601B68C2 +:104DC0000193019B02B070470F4A1378D3B903785F +:104DD0004FF08041C3F34003C1F88035037803F0FE +:104DE0000103C1F87835094B1968C90706D4E021D9 +:104DF00083F800130121C3F88011196001230448CE +:104E000013707047034870470499002000E100E0E8 +:104E10000000AD0B0C00AD0B014B02681A6070472F +:104E2000009900204FF080434FF46072C3F80423D0 +:104E30007047000010B54FF08043D3F80443620779 +:104E400007D54FF48470FFF7AFFF10B11E4B1B68FE +:104E50009847A30608D54FF48A70FFF7A5FF18B14D +:104E60001A4B00201B689847600608D54FF48C70D9 +:104E7000FFF79AFF18B1154B01201B6898472106D0 +:104E800008D54FF48E70FFF78FFF18B1104B00203C +:104E90001B689847E20508D54FF49070FFF784FF30 +:104EA00018B10B4B01201B689847A3050AD54FF496 +:104EB0009270FFF779FF28B1054BBDE810401B68E1 +:104EC0000220184710BD00BFF8980020FC98002071 +:104ED00000990020044AD2F80034DB07FBD50160BA +:104EE000BFF35F8F704700BF00E001404FF0805379 +:104EF0001A69B0FBF2F302FB130373B9084B0222E9 +:104F0000C3F80425C3F80805D3F80024D207FBD55D +:104F100000220448C3F8042570470348704700BFC7 +:104F200000E001400000AD0B0A00AD0BF8B50B4BE3 +:104F30001546012206460F46C3F804250024A54263 +:104F400004D1064B0022C3F80425F8BD57F82410FD +:104F500006EB8400FFF7BEFF0134F0E700E00140FC +:104F6000BFF34F8F0549064BCA6802F4E062134352 +:104F7000CB60BFF34F8F00BFFDE700BF00ED00E047 +:104F80000400FA054FF08053D3F83021082A06D1E7 +:104F9000D3F83431032B02D8024AD05C704700208A +:104FA000704700BF76BB0F0008B54FF08053D3F8B1 +:104FB0003021082A4ED14FF080420021C2F80C1156 +:104FC000C2F81011C2F8381502F54042D3F80414A3 +:104FD000C2F82015D3F80814C2F82415D3F80C141D +:104FE000C2F82815D3F81014C2F82C15D3F81414ED +:104FF000C2F83015D3F81814C2F83415D3F81C14BD +:10500000C2F84015D3F82014C2F84415D3F824147C +:10501000C2F84815D3F82814C2F84C15D3F82C144C +:10502000C2F85015D3F83014C2F85415D3F834141C +:10503000C2F86015D3F83814C2F86415D3F83C14DC +:10504000C2F86815D3F84014C2F86C15D3F844348C +:10505000C2F87035FFF796FF18B1494B494AC3F8BB +:105060008C26FFF78FFF18B1474BFB22C3F818259A +:10507000FFF788FF70B14FF080414FF08053D1F8B7 +:10508000E42ED3F8583222F00F0203F00F0313433B +:10509000C1F8E43EFFF776FF20B13C4B4FF40072BD +:1050A000C3F840264FF08053D3F83031082B09D194 +:1050B0004FF08043D3F80024D10744BF6FF00102C2 +:1050C000C3F80024324AD2F8883043F47003C2F89F +:1050D0008830BFF34F8FBFF36F8F4FF01023D3F89B +:1050E0000C22D2071DD52B4B0122C3F80425D3F87F +:1050F0000024002AFBD04FF01022D2F80C3223F00B +:105100000103C2F80C32234BD3F80024002AFBD051 +:105110000022C3F80425D3F80024002AFBD0FFF7AF +:105120001FFFD3F80022002A03DBD3F80432002B40 +:1051300022DA184B0122C3F80425D3F80024002AF0 +:10514000FBD04FF010221221C2F80012D3F8002435 +:10515000002AFBD04FF010231222C3F804220D4B7B +:10516000D3F80024002AFBD00022C3F80425D3F88A +:105170000024002AFBD0D2E7074B084A1A6008BD7A +:10518000005000404881030000F0004000900240C1 +:1051900000ED00E000E00140388400200090D003E2 +:1051A00013DF704718DF7047064B1878012803D1CA +:1051B000012904BF0221197012B1104602F07EBB12 +:1051C000704700BF3599002008B5FFF7F3FA88B1A2 +:1051D00011481C2101F098FF08B102F06FFB0F4944 +:1051E0000D4800231C2201F07FFF98B1BDE8084064 +:1051F00002F064BB4FF47F20FFF778FE07220749D7 +:105200004FF47F20FFF792FE054B1A78012A04BF66 +:1052100002221A7008BD00BF2C9900203899002086 +:105220003599002070B5124C124D134ED4F800344D +:105230007BB1C4F80056C4F80456C4F80856C4F844 +:105240000C56C4F81056C4F81456C4F81856C4F8CE +:105250001C5602F005FB05F0F4FF20B104F0DAFD66 +:10526000002005F003FA3378023B022BDED870BD34 +:10527000000001403546526E3599002013B54FF4B9 +:105280004053124A596891420CBF9C684FF48054B5 +:1052900001A800F0A7F92368013302D16368013344 +:1052A00011D0019B1A88012A0DD1588820B1996824 +:1052B0000022204602F04AFB019B5B881B1A5842E1 +:1052C000584102B010BD0020FBE700BFDBE5B15143 +:1052D00084B02DE9F34108AC84E80F009DF820402C +:1052E000BDF8228001A80F4616461D4600F07AF947 +:1052F00054B9384B0122FF21A3F802809D601A8027 +:105300009980354B1A7012E0012C17D1314BBA1924 +:105310002A449A60A5221A80FF229A800C9AA3F848 +:105320000280C3E903765D619A612B4B1C70FFF725 +:105330004BFF02B0BDE8F04104B07047032C0FD121 +:10534000019A244B11881980518892689A60C3E9A8 +:105350000376AA2259809A805D611F4B0122D1E712 +:10536000022C15D1019A1B4B1188A5290AD10022C4 +:105370009A60FF221A60FF229A800022C3E903226A +:105380005A61EAE719805188926859809A60F2E779 +:10539000052C0ED1FFF70EFA40B100F097FD08B1D1 +:1053A00002F08CFA0C4B03221A70C2E700F010FADC +:1053B000F5E7042C08D1074B00229A60FF221A60FF +:1053C000019A92889A80B2E7062CB2D1024B04224D +:1053D000EAE700BF389900203599002000B50C4B52 +:1053E0001B7889B063B90B4B1B786BB905238DF81B +:1053F0000C30079B009303AB0FCBFFF769FF03E073 +:1054000004F000FB0028EED009B05DF804FB00BFFB +:1054100034990020289900201FB50023CDE90233DC +:10542000074B019301F030FE30B906494FF47F235A +:1054300001A84B6001F046FE05B05DF804FB00BF1B +:10544000A9510F002C99002070B505460E460AB1EF +:1054500080F00102154B02F001021A7000F0B0FF5B +:10546000044628B935B100F08BFC0446FFF7DAFE9C +:10547000204670BDBEB10E4B0E4A0F481D70294626 +:1054800002F0F8F84FF400444FF4FA7029464FF454 +:105490007A720023E6FB040106F0D0F92A460146A1 +:1054A000064802F0F9F800F06FF9DEE734990020C1 +:1054B00028990020DD530F007CBB0F0008990020C5 +:1054C0001FB5134B4FF0FF32C3F88020C3F8802183 +:1054D0004FF440530F4A596891420DD19C682046C1 +:1054E000FFF75EFE10B14FF000531C60204604B081 +:1054F000BDE8104000F09AB80023CDE902334FF424 +:10550000805406236846CDE90034FFF74BFEE9E7F7 +:1055100000E100E0DBE5B15107B501A800F062F859 +:10552000019B1A88A52A07D09888A0F1AA0358429F +:10553000584103B05DF804FB0120FAE710B501F013 +:105540005DF9A8B10E4B0F4843F00103984701F0F5 +:10555000BFF808B102F0B2F901F050F908B102F059 +:10556000ADF901F001F9044638B102F0A7F904E001 +:1055700001F01EF904460028E4D1204610BD00BF0A +:1055800080BB0F0000A8610000B589B003AB1422F6 +:1055900000211846FEF700FF02228DF80C200022A1 +:1055A00000920FC8FFF794FEFFF73CFE002009B001 +:1055B0005DF804FB13B5044601A800F013F8019B45 +:1055C0001A8822805A8862809A68A2609A88A2808B +:1055D000DA68E2601A6922615A6962619B69A361B3 +:1055E00002B010BD014B0360704700BF00F00F0018 +:1055F000F0B50346186880F308885868FF2464B241 +:10560000EFF30585002D01D1A64600472546064645 +:1056100021273FBAF0B40024002500260027F0B46B +:10562000F92040B2004700BFF0BD00BFFFF7E0BF68 +:1056300073B500230DF1020101A8ADF8023001930A +:1056400002F0C4FDF8B9019C25785DB3174B93F8BF +:105650003020032A28D00C2606FB00F29958E9B91D +:1056600098189D5093F830200132D2B283F8302040 +:10567000BDF802300E4A9B08013B0434436084604D +:10568000084602F085F8019B33B128B1184602F0B4 +:10569000B9FD08B102F012F902B070BD0130042862 +:1056A000DAD1F0E70720EEE70420ECE75499002078 +:1056B000F9560F00084609B102F000B97047000022 +:1056C00010B50C220B4B504319181A5882B193F89D +:1056D00030208C68013AD2B283F8302000221A5070 +:1056E000C1E90122201F02F08DFD08B102F0E6F8A9 +:1056F000002010BD54990020214B70B50122214E8D +:105700001A7096F8303003B970BD1E4C002523681E +:1057100083B1013B042B07D8DFE803F01C0612031A +:105720002800204600F0DEFEE8B2FFF7C9FF08B10E +:1057300002F0C4F80135042D04F10C04E7D1E0E7D0 +:10574000A3686360204600F073FE0028ECD002F0EE +:10575000B5F8E9E7204600F053FF00F02FFF08B14D +:1057600002F0ACF80520FFF7E3FADDE700F074FF84 +:1057700000F092FFBDE870400620FFF7D9BA00BFE5 +:10578000289900205499002008B50E4B002283F878 +:10579000302004210139C3E901221A6003F10C030E +:1057A000F8D1094800F03EFE02F0A8FC08B102F072 +:1057B00085F8064802F08EFC08B102F07FF8002060 +:1057C00008BD00BF54990020B5560F0031560F0098 +:1057D00008B50020FFF774FF0120FFF771FF0220DA +:1057E000FFF76EFF0320FFF76BFFBDE8084002F0F4 +:1057F000CDBC006870476CDF70476DDF70476EDFAF +:1058000070476FDF704772DF704773DF704774DF78 +:10581000704776DF704777DF70477ADF70477CDF4D +:1058200070477FDF704786DF70478FDF704790DFFC +:105830007047AFDF7047B0DF7047B1DF7047B2DF4E +:105840007047B5DF704764DF704766DF70470C282C +:1058500013D8DFE800F01412121212120912071204 +:10586000120D0B0002207047032070470420704780 +:10587000042914BF06200520704706207047012028 +:10588000704702F01BB810B5044608460321FFF725 +:10589000DEFF0246204601F075F918B1BDE8104060 +:1058A00002F00CB810BD00000346032B10B50846EB +:1058B000144620D0042B23D169B1124B18884FF61F +:1058C000FF7398421CD01321FFF7A3FFC0B1BDE8BE +:1058D000104001F0F3BF104602F094F808B101F057 +:1058E000EDFF094B1B689C420AD1012203210748A6 +:1058F00001F048F9EAE70121FFF7A9FF0246F6E7C0 +:1059000010BD00BF3E840020489A0020F899002076 +:10591000F8B50A4DAB889E181D2E14460DDC2F6875 +:10592000FE1802F1010C07F803C07070B01C05F0FE +:105930001DFDAB8802331A19AA80F8BDA899002072 +:10594000F0B54E4E317895B0002940F092804C4C25 +:10595000019110222046FEF71FFD4A4B019923605A +:1059600018220EA8FEF718FD01238DF838302823E1 +:105970001093454B1B78002B7DD0444B04AC03F1B6 +:10598000100518685968224603C20833AB42144612 +:10599000F7D13F4C10220DEB0201E01D05F0B4FCE5 +:1059A000002868D03B4801210460FFF728FF08B1B8 +:1059B00001F084FF384B08AA03F1100C1746186851 +:1059C0005968154603C5083363452A46F7D1206850 +:1059D0000C903248A288A379ADF8342007600122E8 +:1059E00000218DF83630FFF70CFF08B101F066FF9B +:1059F00003238DF84C3004238DF80E3041F23053E0 +:105A0000ADF81030264B08AA9B798DF812300DF1B5 +:105A10000F0104A8FFF717FF012210460DF10E0138 +:105A2000FFF776FF1F4805F04BFE1E49C2B2092062 +:105A3000FFF76EFF102208A90620FFF769FF104943 +:105A400019480EAAFFF7DFFE08B101F037FF164C28 +:105A5000042221780120FFF7DEFE08B101F02EFFBD +:105A600020780121FFF7D1FE08B101F027FF0123C3 +:105A7000337015B0F0BD0623BEE700BF2C9A00209E +:105A8000A899002088990020F4990020A9BB0F0054 +:105A9000B8990020449A0020BF990020289A00203D +:105AA000F899002086BB0F003C840020F0B5044626 +:105AB0000146B1B0A84801F099F82388262B3BD8BD +:105AC0000F2B04D8012B00F0CC8031B0F0BD103B7F +:105AD000162BFAD801A252F823F000BF655B0F0025 +:105AE000735B0F00CB5A0F00A55B0F009F5C0F008C +:105AF000CB5A0F00CB5A0F00CB5A0F00CB5A0F00D6 +:105B0000CB5A0F00BB5C0F00CB5A0F00CB5A0F00D3 +:105B1000CB5A0F00CB5A0F00CB5A0F00CB5A0F00B5 +:105B2000315D0F00CB5A0F00235D0F00CB5A0F00E1 +:105B3000CB5A0F00255C0F00513B9AB2052AC4D8FE +:105B4000052BC2D801A252F823F000BF6F5C0F00F2 +:105B5000BB5C0F00CB5A0F00CB5A0F00475D0F0004 +:105B60000B5C0F007D4BA2881A807D4B00221A70BF +:105B7000ABE78023794CADF824307A4B2088322271 +:105B80001A6010A9012309AAFFF759FE08B101F014 +:105B900095FE754B1B780BB9FFF7D2FE4FF6FF73DE +:105BA000238092E7714B03AC9A79186899888DF835 +:105BB00022200790DA1DADF8201017332646106812 +:105BC0005168254603C508329A422C46F7D1684BE6 +:105BD00009AA03F11807154618685968144603C442 +:105BE0000833BB422246F7D1186820605B48614AFF +:105BF000008810AB8521CDE91456FFF712FE00286E +:105C00003FF463AF01F05AFE5FE7A379002B7FF406 +:105C10005CAF524B13211888FFF7FBFD00283FF4BF +:105C200054AF79E0237A012B7FF44FAF4C4B002225 +:105C30001A704C4B19680139196069B910AB1422FC +:105C40001846FEF7A9FB05228DF84020149A009211 +:105C50000FC8FFF73DFB38E731B0BDE8F040FFF774 +:105C60006FBE3E4B00211888FFF7EFFDD6E7A37902 +:105C7000002B3FF42AAFA27B043A022A3FF625AF5D +:105C8000022B18BF01238DF840304FF4C173ADF8DB +:105C90004430324B10A91888FFF7CDFDAFE7334AE7 +:105CA000258A508D02F118010023854218BF19463C +:105CB0000732A088FFF7B7FDB0E7284B1C884FF6E6 +:105CC000FF75AC4227D02C4B1B78F3B12B49012335 +:105CD00008222046FFF7B1FDF0B902460146022333 +:105CE0002046FFF7AAFDB8B92A460C212046FFF747 +:105CF000A0FDA0F54053023B012B7FF6E6AE08283D +:105D00003FF4E3AE112889D1DFE61A461946204652 +:105D1000FFF793FD82E7082031B0BDE8F04001F0C5 +:105D2000CDBD0E4B002218881146FFF780FD75E7A8 +:105D300000238DF840308DF84130084B10A91888A9 +:105D4000FFF773FDC1E6E188044B1729188828BFC7 +:105D50001721FFF776FD61E7F89900203E840020C7 +:105D60002C9A0020408400203F9A0020B8990020FF +:105D7000D09900203A9A0020F4990020EC99002054 +:105D800030B5464A464800231370464A95B0137012 +:105D900000F048FB01F0F6FD0446002868D14248B7 +:105DA000FEF720FC002866D1404B01221A70112317 +:105DB0003F488DF8043005F083FC3D4982B201A8CC +:105DC000FFF72DFD08B101F079FD0822002104A89C +:105DD000FEF7E2FA0823ADF810301823ADF81230C0 +:105DE0000023ADF8143004A84FF4C873ADF8163092 +:105DF000FFF713FD08B101F061FD00210C2201A89D +:105E0000FEF7CAFA0823ADF804302A4B02932A4859 +:105E10002A4B039301A900F02FFD08B101F04EFDBC +:105E2000274D4022002104A8FEF7B6FA284605F0C7 +:105E300047FCADF810002846059505F041FC079594 +:105E4000204DADF81800284605F03AFC1123ADF8B6 +:105E5000300004A8ADF84C300D9500F0DBFF1A4B74 +:105E600030221A7007225A7010229A70FFF768FDCC +:105E7000204615B030BD04A8FFF7BFFC08B101F003 +:105E80001DFD9DF8113004A801338DF81130FFF786 +:105E9000B2FC00288BD001F011FD88E73F9A00206A +:105EA000A9580F00399A0020B8990020F4990020D1 +:105EB00086BB0F001D5F0F00F899002083580F006C +:105EC0008DBB0F0098BB0F003A9A002010B50F4B06 +:105ED00001221A700E4B18884FF6FF73984207D0B4 +:105EE0001321FFF796FC08B101F0E8FC002010BD7B +:105EF000084C2378002BF9D0074B1878FFF787FC64 +:105F000008B101F0DBFC00232370EFE73F9A00208B +:105F10003E8400202C9A00203C840020F0B50B78B1 +:105F200089B005460C46092B35D8DFE813F02D0063 +:105F3000360041000A001900260007011001440044 +:105F4000150100F089FB0421FFF781FC0246284679 +:105F500000F018FEF8B109B0BDE8F04001F0AEBCA9 +:105F6000FFF7B4FF08B101F0A9FC00F095FB90B178 +:105F700009B0BDE8F04000F09DBBFFF7A7FF002887 +:105F8000F6D001F09BFCF3E7764B01221A704B68C8 +:105F90001A78754B1A7009B0F0BD724B02261E704C +:105FA0004B681B78012BF6D100F008FB3146CBE79C +:105FB0006C4B0322EEE70520FEF7BAFE694B1E7814 +:105FC000022E37D0032E59D0012EE4D104AB10227B +:105FD00018460021FEF7E0F9634A237A12788DF81B +:105FE0001020002203920C2B4FF00302CDE9012078 +:105FF00008D03146284600F0C5FD0028CBD001F07E +:106000005DFCC8E763681846FFF7F3FB0590181DB1 +:10601000FFF7EFFB069003F10800FFF7EAFB07909C +:1060200001A800F005FA0028B5D03146FFF70FFCB3 +:106030000246DFE7237A13F0030011D0C0F1040217 +:106040001A44D2B219464FF0000C0E46013167686F +:10605000C9B2914207F806C0F7D11B1A0433237264 +:106060000123049363680693237A04A89B0805938D +:1060700000F0C6FA00288ED00221D7E7207A8307E5 +:1060800002D03246314662E7384E0190314601F087 +:106090008FFC014618B12846FFF7F5FB7BE76168E6 +:1060A000019A306805F062F9019801F0E7FC0146B9 +:1060B0000028F0D101A9304601F0F0FC014600288B +:1060C000E9D104230493019B9B08059304A833683A +:1060D000069300F007FA074640B9254A237A11686B +:1060E0000B441360234B32681A6054E709281BD114 +:1060F0001F4B217A1A68114419601F4B1B78002B23 +:106100003FF449AF1D4C2388013B9BB22380002BF9 +:106110007FF441AF284600F0FBFC08B101F0CEFB54 +:10612000174B1B88238036E7306801F06BFC014673 +:1061300010B12846FFF7A7FB3946ACE70E4B01220A +:106140001A700F4A8B8813800C4A138023E70A4A7F +:10615000002313700A4AF8E7054B196800F09CFC0D +:10616000F8E600BF399A0020409A00204C9A00209F +:10617000309A0020489A0020389A0020369A002051 +:10618000349A002018DF7047012973B514460D4674 +:106190001A4608D0032912D014B3204602B0BDE835 +:1061A000704001F08BBB0F4B1B78052BF4D10E4BCD +:1061B0001B68002BF0D0214604209847ECE7094EDD +:1061C0003378022BE8D1094B01925B689847064B64 +:1061D00035701B68002BDFD0019A21462846ECE77A +:1061E00002B070BD589A0020509A00205C9A00209E +:1061F00030B589B003AC142200212046FEF7CCF85C +:10620000094B1B88ADF80E30084BDB680693002560 +:10621000079B8DF80C50009394E80F00FFF758F897 +:10622000284609B030BD00BF689A0020B49A00200B +:1062300000B589B003238DF80C300A4B1B88ADF8EC +:106240000E30094B5A6804929A68DB680693079BE4 +:106250000093059203AB0FCBFFF73AF8002009B08B +:106260005DF804FB689A0020B49A002000B589B05C +:1062700001238DF80C300F4B0F4A1B88ADF80E3000 +:106280004FF440535968914208BF9A680B4B5968C4 +:10629000049118BF4FF480529968DB68069305910A +:1062A000009203AB0FCBFFF713F8002009B05DF8A5 +:1062B00004FB00BF689A0020DBE5B151B49A0020CE +:1062C00000B589B003AB142200211846FEF764F82C +:1062D00004228DF80C20002200920FC8FEF7F8FF70 +:1062E00009B05DF804FB0000194BF7B5194C1C60B0 +:1062F000194B02221A70FEF75DFA184B48B1196863 +:10630000204600F001FF00B303B0BDE8F04001F00B +:10631000D5BA1D68124F013D2D0B013504464FF4CF +:1063200040567368BB420CBFB0684FF4805000EB1E +:1063300004300134FEF7DAFDA542F2D80023054807 +:1063400000931A460321FFF71FFF03B0F0BD00BF03 +:10635000CC9A0020C49A0020589A00206C9A002001 +:10636000DBE5B15170B50C4686B00321CDE90210D2 +:106370000546960802A8019304940596FFF702FFCC +:10638000E0B1B4F5805F019B11D8012302A8CDE9EB +:106390000235CDE90446FFF7F5FE78B9032302A8DC +:1063A000CDE90235CDE90446FFF7ECFE06E01A46DA +:1063B000E11AE81AFFF7D6FF0028E6D006B070BD54 +:1063C0001FB5114B0193114B114900241C70114B47 +:1063D00001A81C80CDE9024400F074FE0E4B10B100 +:1063E0001C7004B010BD4FF440520C4954688C42EC +:1063F00007490CBF92684FF480524A60084A002156 +:10640000116001221A70ECE789610F00B09A002038 +:10641000C49A0020689A0020589A0020DBE5B15108 +:10642000549A0020014B1860704700BF509A00201A +:1064300038B54368214C0FCB84E80F002278500711 +:1064400001D5910733D16068830730D1A3689D07D8 +:106450002DD1E16811F0030429D1184408441849EA +:10646000B3F5204F086024D84FF4405315495D68B8 +:106470008D420ABF9B684FF46923C3F56A23984293 +:1064800017D8114B1149196011495960D10709D525 +:10649000104A9A60104B1B78012B0CD1FFF724FF98 +:1064A000204638BD92074CBF0C4A0D4AF1E706243E +:1064B000F6E70C24F4E70824F2E700BFB49A0020C2 +:1064C0006C9A0020DBE5B1515C9A0020E9620F0074 +:1064D000C1620F006D620F00589A002031620F00F8 +:1064E000F1610F002DE9F04385B0002853D0816899 +:1064F00011F0030451D12C4B1B78052B4FD12B4E9F +:1065000042683368DFF8AC9003EB82039500D9F85A +:106510000020934207D94FF0FF3333600C2420460C +:1065200005B0BDE8F0830391FEF744F9DFF88880F9 +:10653000039940B13368D8F800002A4600F0D4FD32 +:10654000D8B10446EBE74FF44053194A586837680E +:10655000904208BF9868039118BF4FF48050002301 +:106560002A463844FEF7C4F80399D8F8000000958D +:106570000B4600220121FFF707FE33681D44D9F8BE +:10658000003035609D420CD1FEF714F90028C6D1C9 +:10659000FEF790F8C3E70E24C1E71024BFE70824F4 +:1065A000BDE70924BBE700BF589A0020549A002099 +:1065B000DBE5B1516C9A0020CC9A002070B50B4BF2 +:1065C0001D6885B90A4E3378042B0CD1094C0A4B4F +:1065D00021781A780948FEF725F810B90523337099 +:1065E00070BD2570FCE70820FAE700BF549A002030 +:1065F000589A0020B09A0020B49A0020709A002087 +:10660000F8B5114B1A78032A03D0042A03D00824C2 +:1066100016E004221A700D4B1C68002CF7D10C4DAB +:1066200043682F789E0007EB8303402B0AD88168CC +:1066300008483246384404F099FE2B7833442B70D6 +:106640002046F8BD0924FBE7589A0020549A002000 +:10665000B09A0020709A002010B50B4C2378052BBF +:1066600010D10A4B0A4A1B68116899420AD10623C5 +:106670002370084B1B685868FEF70EF808B907230B +:10668000237010BD0820FCE7589A00206C9A002067 +:10669000549A0020CC9A0020044B1B78072B02D17F +:1066A000034B9B6818470820704700BF589A00208A +:1066B0005C9A002000B589B006238DF80C30079B4A +:1066C000009303AB0FCBFEF703FE09B05DF804FBAC +:1066D000F0B58DB005A8FEF76DFF089C002C3ED0EC +:1066E0000B9E04F58053B3422DD91E4BA6F5805561 +:1066F00003EA55054FF440539B689C420BD8690050 +:106700002B46A4EB450201F5805106EB4500FFF74F +:1067100029FE0DB0F0BD05F58053012701A8CDE994 +:106720000173CDE90337FFF72DFD0028F1D14FF4B8 +:10673000805301A8CDE9023301970497FFF722FDAA +:106740000028E6D1DBE70123CDE90136A4084FF4A8 +:10675000805301A803930494FFF714FDD9E7204662 +:10676000D7E700BF00F0FFFF00B58DB005A8FEF72A +:1067700021FF099880B1089B8BB94FF440530B4A15 +:10678000596891420ED19B6880080022039001A8AD +:10679000CDE90123FFF7F6FC0DB05DF804FB0B9A81 +:1067A0001344F1E74FF48053EEE700BFDBE5B1514E +:1067B00000B58DB005A8FEF7FDFE099898B1089BBD +:1067C000A3B94FF440530C4A5968914211D19B68C8 +:1067D0000393800803214FF47422049001A8CDE9AB +:1067E0000112FFF7CFFC0DB05DF804FB0B9A1344C8 +:1067F000EEE74FF48053EBE7DBE5B15110B58CB019 +:1068000005A8FEF7D7FE0898B8B10B9C00F5805399 +:10681000A34214D94FF440539B6898421BD80F4BA6 +:10682000A4F5805203EA52035900A0EB430201F59C +:10683000805104EB4300FFF795FD0CB010BD8008BC +:1068400003224FF48053049001A8CDE9012303945F +:10685000FFF798FCF1E70E20EFE700BF00F0FFFF25 +:10686000A8DF7047AADF7047ADDF7047AEDF704723 +:10687000B0DF704762DF70472DE9F0470E4694B0F5 +:106880000546002800F00181002900F0FE804B68D9 +:10689000002B00F0FA804FF6FF7303800023ADF861 +:1068A0000A307B4B04AA03F1100C1746186859688C +:1068B000144603C4083363452246F7D141F23053EE +:1068C0000DF10A013846ADF80830FFF7D3FF044652 +:1068D000002840F0D6802A1D02A90120FFF7C0FF42 +:1068E0000446002840F0CD809DF80A30AB71014687 +:1068F0001C220DA8FDF750FD9DF834300E9443F096 +:1069000004038DF8343001AFAB798DF80E30214699 +:1069100041F2325303223846CDE91044CDE9124406 +:10692000ADF80C30FDF738FD9DF806308DF80440C9 +:1069300023F01F0343F00303214614224FF0110AF2 +:1069400008A88DF806308DF805A00DF10C08FDF7AC +:1069500023FD4FF01409A8880A9405F1080308AA3A +:106960000DA90C94CDE90887ADF82C90FFF77AFFBC +:106970000446002840F0858001461C220DA8FDF742 +:106980000BFD9DF834300E9423F0180343F01803E8 +:106990008DF83430AB798DF80E30214641F2315309 +:1069A00003223846CDE91044CDE91244ADF80C304D +:1069B000FDF7F2FC9DF806308DF8044023F01F032C +:1069C00043F0130321464A4608A88DF806308DF897 +:1069D00005A0FDF7E1FC1723ADF82C30A8880A9438 +:1069E00005F1100308AA0DA90C94CDE90887FFF75B +:1069F00039FF0446002844D101461C220DA8FDF7AA +:106A0000CBFC9DF834300E9443F002038DF8343003 +:106A1000AB798DF80E30214641F2345303223846CB +:106A2000CDE91044CDE91244ADF80C30FDF7B4FCCB +:106A30009DF806308DF8054023F01F0343F0030353 +:106A400021464A4608A88DF806308DF804A0FDF7C7 +:106A5000A3FC02230A93ADF82C30A8880C9605F10C +:106A6000200308AA0DA9CDE90887FFF7FBFE04461D +:106A700038B97368AB62B36803B1EB62054B0122AE +:106A80001A70204614B0BDE8F0870E24F9E700BF65 +:106A9000B9BB0F00D09A002070B5054686B070B320 +:106AA00002884FF6FF739A422BD0174B1B7843B3E3 +:106AB000164C1022080AE170207121FA02F0090E2A +:106AC000072301266071A17102A800216370ADF84F +:106AD00006302270A670FDF75FFC2B8AADF80830F7 +:106AE0000023ADF80C3028888DF80A600DF10603FC +:106AF00002A9CDE90434FFF7B9FE06B070BD0E203F +:106B0000FBE70820F9E700BFD09A0020D19A0020C7 +:106B100030B5044687B060B302884FF6FF739A42DF +:106B200029D0164B1B7833B3154D11232B700B0A4C +:106B30006970AB700B0C090EEB70297105230021F5 +:106B4000102202A8ADF80630FDF726FC238AADF826 +:106B5000083001238DF80A300023ADF80C3020886E +:106B60000DF1060302A9CDE90435FFF77FFE07B05A +:106B700030BD0E20FBE70820F9E700BFD09A0020C7 +:106B8000D19A002030B5044687B038B300884FF65C +:106B9000FF73984224D0134B1B780BB3124D102374 +:106BA00069700321ADF80610AA7000211A4602A8E8 +:106BB0002B70FDF7F1FB238AADF8083001238DF827 +:106BC0000A300023ADF80C3020880DF1060302A92D +:106BD000CDE90435FFF74AFE07B030BD0E20FBE7D4 +:106BE0000820F9E7D09A0020D19A002070B50D4610 +:106BF00088B0044650B149B1826A3AB10B88502B33 +:106C000049D005D8102B43D0112B54D008B070BDFB +:106C1000512BFBD18E79022EF8D10A89038A9A4230 +:106C2000F4D18B7B043B022BF0D99DF816308DF804 +:106C3000106043F001038DF816300B8AADF8183060 +:106C40004B8AADF81A30082201F1140301A8002183 +:106C50000793FDF7A1FBA18A2088019601AACDF830 +:106C600008D0FFF701FE48B3E36A03B1984740F24A +:106C7000FD132088ADF8143004A9FFF7F9FD0028B2 +:106C8000C4D0E36A002BC1D008B0BDE870401847FB +:106C90008B882380BAE7C98803899942B6D1082333 +:106CA0008DF81030123535F8023C8DF81830059506 +:106CB00004A99047AAE74FF6FF73EAE7BDF8003052 +:106CC0002088DB07D3D5002604A9ADF81460FFF7B0 +:106CD000CFFD0028D5D1297D4B1E072B3ED8DFE8FC +:106CE00003F004192226282A3B2C6B8A8DF80460B5 +:106CF000012B05D8062201212046FFF743FFBEE7FE +:106D0000012315358DF80C300295A36A01A92046A0 +:106D100098477BE76A8A01239A428DF80430F0D8BD +:106D200006220221E8E702238DF80430EDE7032371 +:106D3000FAE70423F8E70523F6E76B8A022B02D86B +:106D400003220821D8E7B5F81530ADF80830002B3C +:106D50000CBF07230623E7E70923E5E70322CBE778 +:106D6000A8DF7047AADF70472DE9F04180468EB05A +:106D700015461F460E4611B9084600F09FFD15B98D +:106D8000284600F09BFD1C220DEB02000021FDF7C0 +:106D900003FB9DF81C30ADF80480002443F002038F +:106DA0008DF81C3021460123032268468DF80630F9 +:106DB000CDE90A44CDE90C440894FDF7EDFA3B789F +:106DC0008DF800307B788DF801309DF8023023F08B +:106DD0001F0343F002032146142202A88DF802305B +:106DE000FDF7DAFA0A48CDF80CD001AB029302AAFB +:106DF000149B0088ADF8105007A9ADF81240ADF80B +:106E000014500696FFF7AEFF0EB0BDE8F08100BF4C +:106E1000F89A002030B587B041F60A032B4AADF846 +:106E20000C30044603A901208DF80E00FFF798FFEF +:106E30000546D8B92288E2B922894AB1244B009389 +:106E4000E16804F13C0342F62420FFF78DFFD8B936 +:106E5000228C4AB11F4B0093616A04F13C0342F655 +:106E60002620FFF781FF78B9A36B7BB9284607B0CE +:106E700030BD194B0093616804F13C0342F62920B0 +:106E8000FFF772FF0028D7D00546EFE71A788DF894 +:106E900010205A888DF81120120A8DF812209A8835 +:106EA000DB888DF815301B0A8DF813208DF816300D +:106EB000120A0A4B8DF814200093072204F13C03B8 +:106EC00004A942F65020FFF74FFFDDE7F89A0020B3 +:106ED000E89A0020D89A0020E09A0020F09A00203A +:106EE00029DF704728DF7047064B182202FB00306D +:106EF00000230422C0E90423037183608361C3601B +:106F0000704700BF0C9B002023B502460846C968A5 +:106F100043680093044B53F82150436910F80C1B4D +:106F2000A84702B020BD00BFFC9A002038B5194C1C +:106F30002378182202FB03431A795869012A03D0E7 +:106F4000032A1AD00F2038BD134A996915689A6828 +:106F5000DB68A2EB0532B2F5805F184401EB053126 +:106F600000EB053034BF92084FF48062FFF7B8FFA2 +:106F70000028E8D10123A370E5E74FF080531B6997 +:106F80009BB2B0FBF3F0044B1B681844FFF7AAFF59 +:106F9000EEE700BF0C9B0020049C002070B5134D51 +:106FA0006C780A2C1FD02E783444E4B2092C84BFAC +:106FB0000A3CE4B2182606FB0454A261207103C9FE +:106FC000A360049BE360AB7804F1100282E8030045 +:106FD00023B100206B7801336B7070BDFFF7A6FF03 +:106FE0001128F7D1F5E70420F7E700BF0C9B00203C +:106FF00070B5234CA3782BB100260228A67002D0CE +:10700000032833D070BD25781E4A182101FB0541A5 +:10701000136889680133B1EB033F136014D86378B8 +:107020001660013B63706B1CDBB21821092B01FB5E +:10703000054188BFA5F10903002004312370FFF743 +:1070400063FF2846FFF750FF6378002BDAD0A37860 +:10705000002BD7D1FFF76AFF0028D3D01128D1D059 +:107060002178182303FB0141043105E0217818231E +:1070700003FB014104310D20BDE87040FFF744BF20 +:107080000C9B0020049C002008B50A4B00211960CD +:10709000094B1980997008460131FFF725FF0A292D +:1070A000F9D1064B00201860054BC3E90000C3E985 +:1070B000020008BD049C00200C9B0020009C0020C6 +:1070C000FC9A0020064A03461068042807D008608E +:1070D000411C11601A68034B43F8202000207047C0 +:1070E000009C0020FC9A002013B5CC180C43A40788 +:1070F00008D1009313460A4601460120FFF74EFFD0 +:1071000002B010BD1020FBE707B500220B4600922D +:1071100001460320FFF742FF03B05DF804FB0000C7 +:10712000094B5A7899780132D2B2914208BF0022B5 +:10713000197891421FBF02705878182202FB003064 +:1071400014BF043000207047089C0020082910B5A7 +:10715000044602D0002000F0B1FBD4E90030BDE8C5 +:107160001040184773B5054600240DF107000E4680 +:107170008DF8074000F0B0FB0DF10600FFF7D0FFDF +:1071800090B10670094B9DF8062045605A709DF835 +:10719000070000F0C5FB24B9054B4FF48012C3F87B +:1071A0000021204602B070BD0424F0E7089C0020B6 +:1071B00000E100E0204B21491A682F2300BF00BFE7 +:1071C00000BF00BF00BF00BF00BF00BF8A422FD07A +:1071D00000BF00BF00BF00BF00BF00BF00BF00BFB7 +:1071E00000BF00BF00BF00BF00BF00BF00BF00BFA7 +:1071F00000BF00BF00BF00BF00BF00BF00BF00BF97 +:1072000000BF00BF00BF00BF00BF00BF00BF00BF86 +:1072100000BF00BF00BF00BF00BF00BF00BF00BF76 +:1072200000BF00BF00BF00BF00BF00BF00BF00BF66 +:10723000013BC3D1704700BF388400200024F40014 +:107240000C4B0D484FF4003210B5C3F880200124D8 +:107250004FF48033C0F84833C0F808334460FFF778 +:10726000A9FF064B846000201860FFF7A3FF044BC2 +:10727000187010BD00E100E000100140249D0020C6 +:10728000159D00202DE9F3412549264B0025C1F825 +:107290004051C1F84451C1F84851C1F84C51C1F8AE +:1072A0000051C1F804511B68002B34D0D1F80445BB +:1072B0001D49DFF888800968641A24F07F442F464E +:1072C0001968A14212D81A7CDE69641A0D4462B1B1 +:1072D0005A691F7400929B690193424608216846CF +:1072E00000F056FA08B100F0E9FABEB90F4A104BA7 +:1072F00011781B788B4205D10133DBB2022B08BF1A +:107300000023137012780B4B43F822500A4B4FF4B2 +:107310008012C3F8002102B0BDE8F0813346CFE708 +:1073200000100140289D0020249D0020219D002068 +:10733000209D0020189D002000E100E04D710F000D +:107340002DE9F74FA84AA94913780978A84C994222 +:107350003BD00133DBB2022B08BF00231370A549D9 +:107360001278A54B0F6853F822003B1823F07F4397 +:1073700000220B60236815461646944613B942B1A5 +:10738000236006E0196881420DD902B12360091A11 +:10739000196001262368DFF8689200930027BDB9C1 +:1073A000DFF868A268E0401A0E44D968D3F81CE000 +:1073B000C3F800C031B1BA1922F07F42C3E90121FC +:1073C000DD611D4673460122D8E700252E46E1E720 +:1073D0002846ED69874BD0F804C01B68DFF830E21F +:1073E0008168ACEB030222F07F42724500F2AD806F +:1073F0000A4402600122027422680023C0E90133BA +:10740000C361002A40F0AB802060C8E75A1C9AF89C +:107410000210D4F800B0D2B291428AF8002004BF22 +:1074200000228AF80020182202FB03A31A79986828 +:10743000022A77D0032A00F08580012A1CD190F817 +:1074400010C0BCF1000F17D1D96841601A69826081 +:107450005A69C2609B698361684B1B78002B18BF17 +:1074600061464160B6E7904200F09E809046D26946 +:10747000002AF8D1002303749AF800309AF801200A +:107480009A42C3D1236827B9009A9A4201D1002EAB +:1074900042D0002B00F08580D3F80090584C554B1B +:1074A000D4F804651868574F351A3B7825F07F45A6 +:1074B00003359BB94FF48033C4F84433C4F8043324 +:1074C000514B4FF400324FF00108C3F880211A608D +:1074D000C4F80080FFF76EFE87F80080A9452CBF36 +:1074E0004844401920F07F40C4F84005D4F80435E2 +:1074F0009B1B23F07F43801B033320F07F4083429C +:107500000AD9D4F80435C4F84035FFF753FE3E4B92 +:107510004FF40032C3F80021384B00221A7003B038 +:10752000BDE8F08F5A46D846A2E78BF81020DBF86A +:107530001CB00123BBF1000FF7D1002B9CD0C4F885 +:1075400000B099E700231A46F4E7A3EB0C0323F0FD +:107550007F438B4234BFCB1A002303604AE70168A4 +:10756000136899421BD85B1A1360C2614CE7A1EB08 +:107570000C01D3F81CC01A46BCF1000F0AD06346B8 +:10758000D3F800C08C45F2D3ACEB010CC3F800C0BB +:107590009C4613460160C0F81CC0D861FFE6134644 +:1075A000EEE7FFF74DFEB7E7404510D1DBF81C30A2 +:1075B000236063B9DFF83CE001920121C9F80810AB +:1075C000CEF800300D4B1970FFF7F4FD019A1368E7 +:1075D000D269C8F81C2012B111680B4413602368EB +:1075E0005B4518BF012745E7209D0020219D002015 +:1075F000289D0020249D0020189D0020149D00201F +:1076000000100140159D002000E100E0089C0020D2 +:10761000FEFF7F0008B5FFF713FE104B00200B2282 +:1076200018809A700E4B18600E4B18700E4B187025 +:107630000E4B4FF48012E021C3F8802183F814131D +:107640001A6002F18042A2F56F22C2F8080583F8A1 +:107650001113074BD2F804251A6008BD089C0020BE +:10766000289D0020209D0020219D002000E100E0B9 +:10767000249D0020074B9B784BB132B128B10368A1 +:10768000187C20B959745A61704707207047082048 +:10769000704700BF089C00202DE9F743DFF8848085 +:1076A00098F8023005460E461746ABB3A0B304293E +:1076B00030D9436983B3437C0024012B0DF10700CB +:1076C0000CBF8946A1468DF8074000F005F90DF181 +:1076D0000600FFF725FDD8B1012303700F4B45606D +:1076E000D3F80435C0E90497C0E902369DF80630A6 +:1076F00088F801309DF8070000F012F924B9084B12 +:107700004FF48012C3F80021204603B0BDE8F08397 +:107710000424EFE70724F7E70824F5E70010014009 +:1077200000E100E0089C0020064A92783AB130B1AE +:10773000426922B1002202740221FFF713BD082022 +:10774000704700BF089C00204B1C30B5DB0004468E +:1077500012F003009BB20DD1074D2A601A44074B6B +:107760001A60074B1870074B1870074B1C80074BAB +:10777000198030BD0720FCE7349D0020309D00209B +:107780002C9D00203C9D0020389D00203A9D00202B +:107790002DE9F347DFF8C080B8F800308B42064689 +:1077A0000D4617464CD300240DF107008DF8074015 +:1077B00000F092F8244B254A25481B78008892F85F +:1077C00000C084455FFA8CF138BF4C1CDBB238BF77 +:1077D000E4B2A3422ED014781178CBB2884286BF8F +:1077E0000133DBB2002313709DF8070000F098F816 +:1077F0004FF6FF739C4225D0DFF86090D9F8002047 +:107800004FEAC40A42F8347002EBC403AEB1A5B12A +:10781000104BB8F800001B682A4604FB00303146C4 +:1078200003F0A4FDD9F80030534400209D8002B03D +:10783000BDE8F0874FF6FF74D6E700209880F6E7A2 +:107840000920F4E70420F2E73C9D00202C9D002055 +:107850003A9D0020309D0020389D0020349D00205E +:1078600070B5104C104D22782B789A4200D170BD23 +:107870000E480F4A2378126806880E4802EBC301AF +:10788000006852F83320898803FB060090470A49B4 +:1078900022780988D3B2914286BF0133DBB200233C +:1078A0002370E0E73C9D00202C9D0020389D0020A7 +:1078B000349D0020309D00203A9D00201FB50021FE +:1078C000CDE9021001AA44F20100ADF80410FCF762 +:1078D00066FF05B05DF804FB70B5EFF3108672B675 +:1078E0000C4A946801239CB993600B4B0B4DD3F861 +:1078F000801029401160C3F88050D3F88410516083 +:107900004FF0FF32C3F88420047006B962B670BD30 +:107910000370FAE7409D002000E100E0FC06FFBD97 +:1079200010B5084B9A685AB150B9EFF3108172B68E +:10793000054A1C6814605C685460986001B962B6BE +:1079400010BD00BF409D002000E100E003462AB1C9 +:1079500010881A4619448A4203D170474FF6FF70C7 +:10796000F7E712F8013B40BA80B25840C0F3031366 +:10797000584080EA0033580100F4FF509BB2584051 +:10798000E9E70000064B074A00201870064B1A6012 +:107990000822C3E90120C3E90300C3E905007047D9 +:1079A0004C9D0020509D002030B0002000207047EA +:1079B00030B5F9B1124B5C6800220A60E4B1B0F551 +:1079C000167F1BD8D868013C01305C60D8601C6809 +:1079D00018694FF4177505FB00440C60012101FA8A +:1079E00000F49969013000F00700214318619961A2 +:1079F000104630BD0E20FCE70420FAE70C20F8E723 +:107A000030B00020F0B51C498A689AB34D690E6801 +:107A1000AC1A04F0070423464FF4177707FB036CF6 +:107A2000604511D1012000FA03F58869684088613A +:107A300000204E68D1F818C04FF0010E73440025A5 +:107A4000164403F007030AE0013303F007039D42E5 +:107A5000E4D11020EDE74AB1013A1C4601250EFAA7 +:107A600004F414EA0C0FA6EB0207F4D00DB1C1E93F +:107A70000172F0BD0420FCE730B00020064A136913 +:107A80001268013B4FF4177103F0070301FB032356 +:107A9000C3F858020020704730B0002030B5C0B1A4 +:107AA000B9B10E4BDA68B2B1013ADA609A681C6873 +:107AB00001329A605A694FF4177505FB024404605D +:107AC0000132D4F85802086002F007025A6100201F +:107AD00030BD0E20FCE70420FAE700BF30B00020E4 +:107AE0003FB40C49086890B10B4B1C687CB10B4A41 +:107AF0001568CDE9025000238DF804300B60136047 +:107B000004AB13E90700234604B030BC184704B0A7 +:107B100030BC704754B0002058B0002064B0002042 +:107B2000DC2810B509D0DD2810D0C02816D1FFF709 +:107B3000D7FF0E4B0E4A1A6010BD0E4A0E4B196845 +:107B40001368581C1060C022CA54F2E7094A0A4B55 +:107B500019681368581C1060DB22F5E7064B054ACC +:107B6000196813685C1CC8541460E2E74484002060 +:107B7000917B0F0054B0002064B00020C02802BFE9 +:107B8000014B024A1A60704744840020917B0F0029 +:107B9000C02810B409D0DB280BD0094B094A19685A +:107BA00013685C1CC854146006E05DF8044BFFF7D2 +:107BB00097BF054B054A1A605DF8044B704700BF3C +:107BC00064B0002054B0002044840020217B0F00CA +:107BD00007B501228DF807000DF10701002002F022 +:107BE00085FD00280CBF0420002003B05DF804FBD5 +:107BF00010B5064A064C12682368D05CFFF7E8FF10 +:107C000010B923680133236010BD00BF68B00020A5 +:107C10005CB0002008B5C020FFF7DAFF28B9034B9D +:107C20001B6813B9024B034A1A6008BD5CB0002000 +:107C300048840020F17B0F0008B5DB20FFF7C8FF68 +:107C400010B9024B024A1A6008BD00BF48840020E8 +:107C5000557C0F0010B50C4A0C4C12682368D35C9D +:107C6000C02B03D0DB2B0DD0042010BDDC20FFF790 +:107C7000AFFF0028F9D12368054A01332360054B83 +:107C80001A60F2E7DD20F2E768B000205CB0002067 +:107C9000F17B0F00488400207FB5184C184D194E19 +:107CA000002002F0C3FC30B322689AB1296833681F +:107CB00099420FD2012201A9002002F0C3FC10B9A1 +:107CC0004FF0FF3001E09DF804000F4BC0B21B687D +:107CD0009847E5E70D4B1B686BB10292084A0221F9 +:107CE000126803928DF8041004AA12E9070004B088 +:107CF000BDE87040184704B070BD00BF64B00020FC +:107D000054B0002050B000204484002058B000201F +:107D1000014B18600020704758B00020034B1A78C0 +:107D20000AB901221A700020704700BF4CB0002031 +:107D3000014B0020187070474CB000202DE9F04F27 +:107D400085B0002851D02A4F3B78012B07D0022B59 +:107D500014BF08240424204605B0BDE8F08F254D4B +:107D6000DFF8A090244E254CDFF89C80DFF89CA023 +:107D7000DFF89CB0C9F8001000232B6002233060AC +:107D80003B70C4F800802A68D9F800309A4215D3B5 +:107D9000C4F80080FFF73EFF044608BB184B1B6881 +:107DA00001223A70E3B18DF80420326802922A6809 +:107DB000039204AA12E907009847CCE733682A68BF +:107DC0009A5CC02A03D02A689B5CDB2B04D1236811 +:107DD000534508BFC4F800B023689847042801D170 +:107DE0000024B8E71128CED1FAE71024B3E700BF8A +:107DF0004CB000205CB0002068B000204884002017 +:107E000058B0002060B00020157C0F00F17B0F00FF +:107E1000397C0F00054B064A1860064B1960064B6B +:107E200000201860054B1A60704700BF64B0002046 +:107E30007D7B0F0050B0002054B00020448400200F +:107E4000064B07481B68DB00DBB2002203705B4275 +:107E500042708270C3700421FFF770BF94B000209D +:107E60006CB0002070B52B4C2B4D02462378012BB3 +:107E700014D0022B21D0002B4BD1002A49D1274806 +:107E8000FFF752FC08B1FFF719FD254B1B68002BCB +:107E90003FD0244ABDE8704010781847012A38D1F5 +:107EA0002968214B06311868FFF748FF08B1FFF732 +:107EB00005FD022323700022D8E7022A16D0032AE8 +:107EC0000CD032BB194B15481A6041F67F21FFF7E1 +:107ED000E3FBF0B1BDE87040FFF7F0BC144A136853 +:107EE000013303F0070313600023E3E70F4A13682D +:107EF000052B0AD001331360074B19680A4BBDE804 +:107F0000704018680631FFF719BF064B01221A703E +:107F1000EAE770BDB8B00020ACB0002070B000201F +:107F2000A8B00020B0B00020C0B00020B4B0002045 +:107F300098B00020F0B585B004AB03E907009DF8C8 +:107F40000400032874D8DFE800F00802A3A601208B +:107F500005B0BDE8F040FFF785BF039E544C032EEB +:107F600040F28280029D6B7813F00F0265D00E2ADA +:107F70007AD1042E55D02A78500652D5110650D504 +:107F80001A44AB781A44EB781A4412F0FF0248D135 +:107F9000B71E39462846FFF7D9FCEB5B834240D138 +:107FA00044492A780B6802F00702D8B282422BD1EA +:107FB000013303F007030B60FFF742FF3E4B012242 +:107FC00030461A70FFF75AFD08B1FFF777FC3849C1 +:107FD0004FF41670FFF7ECFC002862D0042802D0A2 +:107FE0000020FFF76BFC35480521FFF713FF08B1B0 +:107FF000FFF764FC324B1B68002B56D04FF000009B +:1080000005B0BDE8F040184720684FF41671FFF73F +:1080100001FF08B1FFF752FC05B0BDE8F040FFF7E3 +:108020000FBF20684FF41671FFF7F4FE00283CD014 +:1080300005B0BDE8F040FFF741BC2978AA780B44B1 +:108040001344EA78134413F0FF030DD11D4A12685C +:108050000132C1F3C20102F00702914204D11A4A6F +:1080600003201370FFF7FEFE25681DB14FF4167153 +:108070002846D9E70E494FF41670FFF799FC60B116 +:10808000042802D02846FFF719FC0C480521CBE74D +:108090000A480521C8E70320CAE720684FF4167193 +:1080A000C2E720684FF416719FE705B0F0BD00BF2E +:1080B000BCB0002094B0002090B000209CB0002004 +:1080C000A4B0002098B00020B0B000200220FFF73C +:1080D000C9BE0000074B10B5044618600648FFF7FC +:1080E00017FE08B1FFF7EAFB002C0CBF0E200020A2 +:1080F00010BD00BFA4B00020357F0F00184A1948FA +:10810000002310B51360184A1360184A1360184A08 +:108110001370184A1370184B184A01211960184B34 +:108120001960184B1970FFF7A5FA08B1032010BDAC +:10813000FFF728FC0028FAD1FFF7F0FD0028F6D160 +:10814000114C4FF416702146FFF732FC0028EDD198 +:1081500020684FF41671BDE81040FFF75BBE00BF0A +:10816000C0B00020CCBB0F00ACB00020B4B00020E9 +:1081700090B00020B8B0002094B00020CD800F0057 +:1081800098B00020B0B00020BCB000200C4A08B568 +:10819000002313600B4A1360FFF708FC08B1FFF7D8 +:1081A0008DFBFFF7C5FD08B1FFF788FB0648FFF719 +:1081B000BBFA042802D10020FFF780FB002008BD95 +:1081C000A8B00020A4B0002070B0002037B50D4644 +:1081D000044698B191B10A4B19780022019259B125 +:1081E00001A91A70FFF75AFC019B063B2B802368FC +:1081F0000433236003B030BD0420FBE70E20F9E711 +:1082000090B000200438FFF7FDBB18DF7047000076 +:10821000F0B51D46154B87B018680F4659681B7A94 +:1082200003AC03C42370124B18685968114B0093B8 +:1082300001AC03C42046164603F042FA214602462A +:10824000384603F093F801A803F03AFA01A9024670 +:10825000304603F08BF8684603F032FA694602466E +:10826000284603F083F807B0F0BD00BFD0BB0F0075 +:10827000D9BB0F00312E30000120704710B51C46CD +:108280000B781E2B0AD000232022052102F07AFC55 +:108290004FF6FF70A04228BF204610BD0020F9E72E +:1082A000F8B5069F14460D463A46002118461E466C +:1082B000FCF772F87CB14FF0E023D3F8F03DDB0718 +:1082C00000D500BE4FF0FF300AE0284600F0F8F974 +:1082D000013504F50074BC4206EB0401F5D32046D9 +:1082E000F8BD0000F8B50A4F0D461E460024069B57 +:1082F0009C4206EB040101D32046F8BD3A462846CD +:1083000000F0B4FA0028F7D0013504F50074EEE768 +:10831000C4B0002030B5264D2A7A8DB09AB107AC92 +:108320001422002120460625FCF736F88DF81C5053 +:108330000B9B009394E80F00FCF7CAFF0620FCF7A4 +:10834000F7FC0DB030BD2B68002BFAD0194B197813 +:1083500019B105201A70FCF7EBFCD5E900329A42FE +:10836000EFD307AC142200212046FCF715F86B7AF6 +:10837000DBB106234FF420424FF474214FF4602008 +:108380008DF81C3002F0C0FF0028D1D0102200214F +:1083900003A8FCF701F84FF460224FF4205303A820 +:1083A000CDE90423FFF731FFC2E78DF81C30BFE7AA +:1083B000C4B000204C840020024B0B604FF40073CB +:1083C0001380704709010100012070470048704781 +:1083D000FA840020044B054A1878054B002814BF86 +:1083E00010461846704700BFE0B20020AF8400205E +:1083F0004D8400202DE9FF411E4B187020B11E4B0B +:108400002A229A720022DA721C4A1D4DDFF878C0C7 +:1084100017461C4BEE4603F110067446186859685F +:10842000F046A8E803000833B342C646F6D12B78DD +:1084300003F00F0310336B4413F8103CD373114B4C +:1084400018685968A646AEE803000833B34274467C +:10845000F6D115F8013B04A901EB1313654513F898 +:10846000103C9373A2F10202D3D100233B7404B0F9 +:10847000BDE8F081E0B20020FA84002064B300205F +:1084800060000010E1BB0F006800001010B570B96B +:10849000134B14481968022202F068FF01230133CC +:1084A000DBB211485B0043F44073038010BD052824 +:1084B00014D80B4B53F82040204603F001F9C3B207 +:1084C0001F2B28BF1F23084A2046E118884202F1CB +:1084D0000202E4D010F8014B1480F7E70020E5E732 +:1084E0000C850020E4B20020E2B200204DDF70478E +:1084F0004EDF70474FDF704750DF704712DF704725 +:1085000000F0C8BE002000F033BD00001FB5244BB2 +:10851000402283F8272300238DF807304FF440537F +:1085200004465A681F4B9A4227D10DF10700FFF706 +:10853000E5FF9DF8073003B30120FFF7D9FF0120C5 +:10854000FFF7D4FF0120FFF7D5FF02A8FFF7D4FF04 +:10855000029BDA0702D5002000F09CFE029B9B07DD +:1085600002D5022000F096FE2046FFF743FF00F000 +:1085700027F802F059FE04B010BD002301A88DF8C1 +:108580000430FCF721FC084B039303A8FCF744FCE0 +:10859000FCF748FC4FF08043D3F838340293D7E718 +:1085A00000E100E0DBE5B15101850F00012000F0A2 +:1085B00071BE0120FCF7BCBB0220FCF7B9BB000078 +:1085C0007FB52F492F4802F0E7FF4FF440532E4A62 +:1085D000596891424CD11A78102A46D9142A186940 +:1085E00044D95B69294CB3FBF4F50A2201A904FBC9 +:1085F000153403F021F92649224802F0CDFF01A9E4 +:10860000204802F0C9FF23491E4802F0C5FF0A2294 +:1086100001A9284603F010F901A91A4802F0BCFF8D +:108620001D49184802F0B8FF4FF47A760A2201A9D2 +:10863000B4FBF6F5284603F0FFF801A9114802F053 +:10864000ABFF15490F4802F0A7FF0A2201A906FB5C +:10865000154003F0F1F801A90A4802F09DFF0F4907 +:10866000084802F099FF04B070BD00200023B9E76C +:108670000B49044804B0BDE8704002F08DBF00BF54 +:10868000FFBB0F0024850020DBE5B15140420F0005 +:108690000CBC0F000ABC0F000EBC0F0019BC0F0071 +:1086A00010BC0F0010B503461C1A944200DB10BD2D +:1086B0000C781CB1013103F8014BF5E72024FAE7EF +:1086C0002DE9F3410C4605464FF400720021204687 +:1086D000FBF762FE6DB95E493E22204602F046FE7F +:1086E000552384F8FE31AA2384F8FF3102B0BDE897 +:1086F000F081B5F5017F2DD8691EB1F5817F24BFCA +:108700006FF4817805EB0801C9B10B02C1EBC151CF +:1087100003F5807004EB412440F693651A1FB2F50F +:10872000696F03F1010206D2AB4214BF91B24FF65A +:10873000FF7124F8131090421346EFD1D6E7F823C7 +:10874000237004F109022346FF2003F8010F93422E +:10875000FBD1DAE7B5F5027F3BD86FF4017C6544C5 +:108760003DB920463B490B22FFF79CFF2823E372CB +:10877000203439492E014FF0000801EB05256FF038 +:108780001F07022EB2D80B2229462046FFF78AFF88 +:108790005923212269216374E3746376B31C84F83E +:1087A0000D80A773E1732274A27484F8148084F896 +:1087B0001580A775E17522766383E86830B102F011 +:1087C0007FFFE061013620341035DAE74FF4E9101D +:1087D000F7E7224B9D4289D86FF40277EA19012A04 +:1087E0000FD81D4B03EB0213D9680191084602F024 +:1087F00067FF01990246204602B0BDE8F04102F051 +:10880000B5BD6FF4FD76A9190902B1F5801FBFF45B +:108810006DAF134B236003F1144303F52C1303F6E0 +:10882000023363600F4BC4F8FC314FF46963A361FA +:108830004FF40053A5F20B254FF48072A3600A4B4E +:108840006561E1602261E36104F12000D4E700BFCB +:108850001CBC0F0047BC0F00D4BC0F000801010076 +:108860005546320A306FB10A29009A23F7B5654B95 +:1088700014460A689A420D4639D103F114434A68F6 +:1088800003F52C1303F602339A4230D1D1F8FC21C0 +:108890005D4B9A422BD18B6823F4FF5323F01E03C8 +:1088A0009B049B0CB3F5005F21D10B69B3F5807F6E +:1088B0001DD1C86810F0FF0619D1CB69534A934205 +:1088C00005D0534A934215D0524A93420FD1A0F596 +:1088D0008053B3F5692F07D201234FF4807205F15D +:1088E0002001FBF705FF22E0B0F5805F1FD34FF0BA +:1088F000FF3021E001276772CB68B3F1102F1DD143 +:1089000004223431684602F031FD042205F13801B9 +:108910000DEB020002F02AFD009BB3F5742F03D18A +:10892000019BB3F57E2F01D02772E0E7A772AB69F8 +:10893000002B37D14FF4007003B0F0BDA3F57422C3 +:10894000B2F5204F28D2E27A01F12007DAB9324A93 +:10895000934218D32B69B34215D90422B91968463A +:1089600002F004FD009BD02B14D10422311D0DEB2D +:108970000200394402F0FAFC264B019A9A424FF069 +:1089800001030DD1E372E8682A6901233946A0F595 +:10899000A030A6E70836DDE7B3F5805FC7D3012333 +:1089A0002372A4E72268934207D041F263018B420D +:1089B00000D80AB14FF0FF3323606B6941F26302C4 +:1089C0009342B7D803F0070204EBD303012191408F +:1089D0001A7B1142C8B204D16168024301311A7393 +:1089E0006160D4E900329A42A4D30120FBF762FE11 +:1089F000637A002B9ED0A37A002B9BD10123237294 +:108A000098E700BF5546320A306FB10A4028A5AD3D +:108A10003C8263D629009A2300D80F004FF0805380 +:108A2000D3F83001082802D1D3F8343123B9A0F1AA +:108A30000D0358425841704701207047094B0122ED +:108A400083F8D8200260BFF36F8FBFF34F8F064AC1 +:108A5000904202D0043A904202D1002283F8D820FA +:108A6000704700BF78B300205070024042DF70476B +:108A700043DF704744DF704712DF704710B5134B78 +:108A8000134A5B68C3F3080373B9EFF310835BB950 +:108A900010494B681B0607D58024C1F8844092F822 +:108AA000D8307BB14C60F8E792F8D83033B101464A +:108AB000BDE810400848012201F094B8BDE810401C +:108AC000FFF7BCBFFFF7BAFF4C6010BD00ED00E040 +:108AD00078B3002000E100E07D8A0F000D4B1822E2 +:108AE00002FB0033598A1A8A521A998A92B28A4230 +:108AF00028BF0A46D9681423434303F1804303F592 +:108B00001C33C3F80016C3F80426034B03EB8000A4 +:108B1000FFF7B4BF78B300200470024007B54FF4EC +:108B2000405300205A68084B9A420AD18DF807003A +:108B30000DF10700FFF7A0FF9DF80700003818BFF0 +:108B4000012003B05DF804FBDBE5B15107B5FFF789 +:108B5000E5FF58B1002301A80193FFF78BFF0198AF +:108B6000003818BF012003B05DF804FB4FF08043CC +:108B7000D3F80C0400F00110A0F101135842584141 +:108B8000F1E70000074BD3F8C024D10309D406490C +:108B90000648D1F8C010C3F8A017C3F8A427FFF700 +:108BA0006DBF70470070024078B3002048700240EB +:108BB0000828F0B402D1F0BCFFF7E4BF0F4D104A13 +:108BC000182141436E1800F59473695852F82340F8 +:108BD000F788B388DB1B9BB2A4B2A34228BF23460D +:108BE000142404FB0022C2F80017C2F80437054B16 +:108BF000F0BC03EB8000FFF741BF00BF78B300205B +:108C0000007002402870024070470000014B802233 +:108C10005A60704700E100E0024B8022C3F88420D4 +:108C2000704700BF00E100E0074BD3F80014D3F811 +:108C300000240A43C3F800240022C3F858214FF44B +:108C40008002C3F8042370470070024070B5887832 +:108C5000404D00F07F0318220C26C4095A4306FB3E +:108C600004228E882A44C6F30A061681CA7802F0C6 +:108C70000302012A29D00121374A01FA03F5D4B9A8 +:108C800003F10C06B140C2F80413D2F8141503F531 +:108C900094732943C2F8141542F823402E4BC3F8AD +:108CA000180540F48070C3F80C05BFF36F8FBFF355 +:108CB0004F8F012014E002339940C2F80413D2F818 +:108CC00010352B43C2F81035E8E7082B09D04FF0D8 +:108CD000E023D3F8F00D10F0010001D000BE002019 +:108CE00070BD1D4BDCB9B5F8D42012B18022C3F899 +:108CF0001C250022C3F85021D3F8002312F40012DF +:108D000008BFC3F85421144B4FF44012C3F8042396 +:108D1000D3F8142542F48072C3F81425BEE700226C +:108D2000C3F82C21B5F8C82012B18022C3F81C2545 +:108D3000094BD3F8002312F4001208BFC3F85421E2 +:108D4000064AC3F80423D3F8102542F48072C3F80E +:108D50001025A3E778B3002000700240000820002F +:108D60001D4B2DE9F041802201241C4DDFF8788055 +:108D7000C3F88420274604F10C03A21C07FA02F270 +:108D800007FA03F31343C5F80833A30003F1804344 +:108D900003F51C330026182202FB04805E60314676 +:108DA0009E620134FBF7F8FA082C4FF01802E2D16A +:108DB0000B4BC5F808330B48C5F81C6531466E628D +:108DC000AE64FBF7E9FA044BC5F814758022C5F8C8 +:108DD00010755A60BDE8F08100E100E000700240CB +:108DE0000008300038B4002078B30020F7B51F46E3 +:108DF00001F07F0018231F4CCD090E4643430C2180 +:108E000001FB053104EB010C62500022ACF8047048 +:108E1000ACF8062018B1F5B1FFF760FE18E017BBFB +:108E2000154BD3F88034C3F3C0139D421BD01348B5 +:108E3000FFF724FE124B5B68C3F30803003B18BF27 +:108E4000012300933A463B463146384600F0B5FED2 +:108E5000012003B0F0BD1C44A37A002BF8D0A5720A +:108E6000FFF7A6FEF4E7002DD6D10648FFF706FE71 +:108E7000EEE700BF78B3002000700240507002405F +:108E800000ED00E04C70024011F07F0008B507D102 +:108E90000D4B01225A65BFF36F8FBFF34F8F08BD93 +:108EA0000828F8D0084B41F48072C909C3F8182586 +:108EB000F1D1064B182202FB00339A7A002AEAD03D +:108EC0009972FFF775FEE6E70070024078B3002064 +:108ED00011F0770F12D00A4B41F48072C3F80C15D1 +:108EE000C3F80C25CA09C3F8181504BF01F594711D +:108EF00043F82120BFF36F8FBFF34F8F704700BF40 +:108F000000700240174B0122002110B5C3F8142550 +:108F1000C3F810250A468B0003F1804303F51C3388 +:108F2000013108295A609A62F5D10E4B0E4C5A62F3 +:108F30009A64C3F85821D3F80014D3F800240A43E4 +:108F4000C3F80024D3F80023C3F80823074AC3F862 +:108F500004230021DC222046FBF71EFA4023A382D3 +:108F6000238110BD0070024078B300200514C001B9 +:108F70002DE9F04FB24BB34AD3F80013002385B06C +:108F80001C4601201D4621FA03F6F6070BD552F8C0 +:108F9000236046B100FA03F6344342F82350BFF38E +:108FA0006F8FBFF34F8F0133192BECD1E20706D53A +:108FB000FFF7A8FF00210122084600F0D5FD14F4B8 +:108FC000006FA14D08D09E4BD3F8A8369BB2A5F8F0 +:108FD000D230012385F8D730A30221D5984ED6F898 +:108FE000143513F4807302D0FFF7CCFD0123D6F8BB +:108FF0001025D70540F1188195F8D7305BB1B5F849 +:10900000D2200023012185F8D73092B20091184672 +:10901000882100F0D2FD01220321002000F094FD00 +:10902000660228D5864BD3F8006406F4E062F005AA +:10903000C3F8002406D50122C3F82C250421002002 +:1090400000F082FD71050FD57D4B0122C3F8082584 +:109050009A65D3F8002312F4001208BFC3F8542114 +:109060004FF40012C3F80423B20504D501220521F0 +:10907000002000F069FD23022BD5714BD3F880143A +:10908000C9B28DF80810D3F88424D2B28DF8092023 +:10909000D3F888048DF80A00D3F88C048DF80B00FF +:1090A000D3F890048DF80C00D3F894048DF80D00DB +:1090B000D3F898048DF80E00D3F89C348DF80F3057 +:1090C0004F0601D1052A04D0012202A9002000F098 +:1090D0005EFD5E4B23405BB195F8D830002B40F02D +:1090E000AB804FF0E023D3F8F03DDE0700D500BEA3 +:1090F000DFF85481DFF84891DFF858B14746002681 +:109100004FF0140A06F10C0324FA03F3D807F1B266 +:1091100022D50AFB0693082ED3F808273B6853FA9A +:1091200082F33B604FF0180303FB0653D2B2D8889A +:1091300012FA80F080B2D88000F08E803889904298 +:1091400040F08A80DB88BA889BB29A4240F28480E1 +:1091500011B95846FFF792FC0136092E07F118079E +:10916000D0D13B4B2340002B5BD0354BD3F86C94D4 +:10917000C3F86C94BFF36F8FBFF34F8F14F4806408 +:1091800007D0D3F88044D3F880341906C4F3C01450 +:1091900070D54FF0000A2C4F00264FF0180B29FA1B +:1091A00006F3DA07F0B201D4CEB9C4B1244B1422CD +:1091B00002FB0633FA68D3F8083652FA83F2FA60F3 +:1091C0000BFB0652518A89B251FA83F39BB2538248 +:1091D000538A398A9BB299424FD9FFF77FFC0136F7 +:1091E000082E07F11807DAD100241826012704F108 +:1091F000100329FA03F3DB07E0B203D464B9BAF130 +:10920000000F09D006FB0452B8F80410D3889BB2B3 +:1092100099423DD9FFF7CCFC0134082C08F118081D +:10922000E5D105B0BDE8F08F002B7FF4F4AE4FF42C +:109230000013C6F80833EEE6002385F8D83057E768 +:10924000007002400071024078B30020FCFB1F0058 +:10925000000400014C700240182303FB0653DA8817 +:10926000BA80DA8801230093002392B2184600F0F6 +:10927000A4FC71E74FF0010A8DE7528A01230093A5 +:10928000002340F0800192B2184600F096FCA6E759 +:109290009772C1E7012813B5044600F0C380022885 +:1092A00059D0002855D1784BD3F80025002A50D149 +:1092B0004FF48002C3F808234FF40062C3F800247F +:1092C000BFF36F8FBFF34F8FFFF7A8FB60B16F4BFA +:1092D000D3F8001C032269BB49F27531C3F8001CA6 +:1092E000C3F8142DC3F8001C4FF08053D3F830316D +:1092F000082B0CD1654BD3F8001CC022E9B949F208 +:109300007531C3F8001CC3F8142CC3F8001C5E4B65 +:109310000124C3F80045BFF36F8FBFF34F8FFFF7F2 +:1093200015FCB0B9FFF7FAFB50B102B0BDE8104030 +:10933000FFF79CBBC3F8142DD6E7C3F8142CE6E75F +:109340004FF08043C3F80001D3F800210192019A45 +:109350001C6002B010BD4C4CD4F804351BB1FFF7B3 +:10936000F5FB0028F5D1D4F800341B05FBD54FF4EC +:109370000063C4F80034BFF36F8FBFF34F8F4FF01B +:109380008053D3F83031082B0CD1404BD3F8001C5C +:1093900000293FD149F27532C3F8002CC3F8141CE0 +:1093A000C3F8002CFFF73AFB58B1384BD3F8001C38 +:1093B000A1BB49F27532C3F8002CC3F8141DC3F8E1 +:1093C000002C4FF08053D3F83031082B2E4B0AD1AC +:1093D00040F2E372C3F800284022C3F80428BFF328 +:1093E0006F8FBFF34F8F80220121C3F81C25C3F874 +:1093F0000413274BC3F884215A60FFF7A7FB00280A +:10940000FBD0214B0122C3F80425BFF36F8FBFF3BC +:109410004F8F9EE70022C3F8142CC3E70022C3F845 +:10942000142DCEE7184BD3F80025002A91D0002246 +:10943000C3F80425BFF36F8FBFF34F8F144980200B +:10944000C1F88400D3F80013C3F80813C3F800254B +:10945000BFF36F8FBFF34F8FFFF760FB78B1FFF75C +:1094600007FB0C4B5A68C2F30802003A18BF0122EE +:109470000221002002B0BDE8104000F065BB4FF0B3 +:1094800080435C60EDE700BF0070024000E00640F2 +:1094900000E100E000ED00E00A44034690B288429B +:1094A00002D39A89824202D25A89104480B270470C +:1094B00082888A4210B504D884898B1A9BB29C4258 +:1094C00003D243891A44891A8BB2038210BD9308D0 +:1094D00013B501EB8303044699420BD112F003024A +:1094E00006D0002301A8019301F040FF019B2360F7 +:1094F00002B010BD51F8040B2060EDE72DE9F043F8 +:1095000085B01446BDF83050AB4238BF4289A3EB5A +:1095100005091FFA89F938BFA9EB0209828838BF0B +:109520001FFA89F94A4588460746194605D2FFF7CA +:10953000BFFF058AB0F80490ADB2B9F1000F1FD09B +:10954000A14528BFA146BC88AC421DD9FA889DF828 +:1095500034003968661BA9EB04042C44B3B214FB35 +:1095600002F416FB02F60128B6B2A4B202FB051102 +:1095700016D099450BD802FB09F2404601F0F6FEE1 +:10958000484605B0BDE8F0832D1BADB2DCE732469E +:10959000404601F0EBFE3968224608EB0600EDE795 +:1095A000994506D819FB02F292B24046FFF78FFFA9 +:1095B000E6E726F00305ADB22A4640460191FFF7E3 +:1095C00086FF16F0030628D001990D44C6F1040168 +:1095D00089B2A1424FF0000328BF2146641A0393C9 +:1095E00003ABA4B2A8191A4685420CD13B68013ED0 +:1095F000164419448B420BD1039BC8F80030002C51 +:10960000BED02246D1E715F801CB03F801CBEBE73A +:1096100013F8012B06F8012FECE73968EFE713B5D3 +:10962000930800EB8303984209D112F0030204D09F +:109630000B68019301A901F099FE02B010BD0C68FE +:1096400040F8044BEFE770B59A42A2EB03041D46C5 +:1096500038BF4389A4B238BFE41A838838BFA4B2A4 +:10966000A3420E46114602D2FFF722FF848874B14E +:109670008288AA4208D9C2880168304602FB0511D7 +:1096800001F074FE012070BDAD1AADB2F1E72046C5 +:10969000F9E72DE9F0430746B0F80E908588048A73 +:1096A0001646C288007A85B01FFA89F9A4B288BB31 +:1096B000A145A9EB040038BF7C8980B23CBF001BE8 +:1096C00080B2281A80B2864228BF06464C46AC4279 +:1096D00028D2A5EB0408751B386825441FFA88FCBE +:1096E00015FB02F518FB02F8012B1FFA88F8ADB242 +:1096F00002FB040022D0664517D8724301F036FE03 +:10970000324649463846FFF7C7FEF881304605B075 +:10971000BDE8F083AE4221BF761B02FB0611A146D5 +:109720002E46D3E7641BA4B2D1E74246009101F074 +:109730001DFE009938682A464144DFE7664505D892 +:1097400016FB02F292B2FFF76AFFD9E728F0030492 +:10975000A4B22246CDE90001FFF761FF18F003082B +:10976000019929D0C8F104039BB2AB4200980A6862 +:10977000039228BF2B46013CED1A0DF10C0C20443E +:10978000ADB244466246013C1CF801EB00F801EF23 +:1097900014F0FF04F7D13868904408EB0304421E2C +:1097A000A04504D11844002DAAD02A46CBE718F8CA +:1097B00001CB02F801CFF3E73868F4E7B2F5004FC8 +:1097C00010D882805200C38092B29DF8003003729C +:1097D000531E838152420023C38101604281038270 +:1097E0000120704700207047C189028A89B292B275 +:1097F0009142A1EB020338BF428980889BB23CBFF3 +:109800009B1A9BB2984228BF18467047C289038AA8 +:1098100092B29BB2D31A58425841704710B5C189D1 +:10982000028A848889B292B2914238BF4089A1EB02 +:1098300002039BB23CBF1B1A9BB2E01A80B210BD60 +:1098400038B5C289038A04469BB292B2FFF7FBFE89 +:10985000218A054682B289B22046FFF71DFE20828A +:10986000284638BD73B5C389058A0026ADB20446C3 +:10987000CDE900569BB2FFF741FE218A054602461C +:1098800089B22046FFF708FE2082284602B070BD4C +:1098900038B5C589028AADB292B2AA42A5EB0203DD +:1098A00088BF42899BB288BF9B1A828888BF9BB2BF +:1098B0009A42044614D1007A90B938BD9B1A9BB2E3 +:1098C0009342FBD2E288206802FB030001F04EFDC8 +:1098D000012229462046FFF7DFFDE0810120ECE769 +:1098E0002B46EDE712B10023FFF7D3BE10467047B9 +:1098F0000023C381038283885B009BB25A1E5B42B4 +:10990000828143810120704701720120704700006D +:109910000B4B63B10B4B1B78834206D90A4B1B6878 +:1099200000EB400003EBC0007047C01AC0B2012832 +:1099300003D8064B00EB4000F4E70020704700BF5F +:109940000000000058B4002054B4002004BD0F00F3 +:1099500070B5104E054600242046FFF7D9FF436836 +:109960002846984733780134E4B20133A342F3DA4E +:10997000372200210848FAF70FFD1022FF2107487F +:10998000FAF70AFDBDE8704005481222FF21FAF7F8 +:1099900003BD00BF58B4002059B400205CB40020BF +:1099A0006CB4002037B50C460546C868019200F03B +:1099B000A5FDE368019A0021284603B0BDE83040C8 +:1099C000184773B5054614463AB90378012B04D1FC +:1099D00010460191FFF720F90199281DFFF758FF64 +:1099E00006462CB92B78012B02D12046FFF70EF941 +:1099F00036B94FF0E023D3F8F03DDB0700D500BEC9 +:109A000002B070BD024B5878003818BF0120704773 +:109A100059B40020024B1878C0F38000704700BF93 +:109A200059B40020014B1878704700BF90B4002053 +:109A3000F8B5164E3178054629BB154C1548372226 +:109A4000FAF7AAFC201DFFF753FF134B1C60134BC2 +:109A500023B11348AFF30080124B1860104F00245D +:109A60002046FFF755FF036898473B780134E4B27E +:109A70000133A342F4DA2846FFF7C6F82846FFF779 +:109A8000C5F8012333700120F8BD00BF90B4002059 +:109A9000A486002059B4002094B4002000000000E7 +:109AA00058B4002054B400201FB54378023B0A4646 +:109AB000032B12D8DFE803F0022A1921204B197872 +:109AC0006FF300011970197800246FF341011970C8 +:109AD0005C70197864F3820119701A4B014618689A +:109AE00004B0BDE81040FFF76CBF154B1978C907EB +:109AF00023D5197841F00401EEE711490B78DC0712 +:109B00001BD50B786FF382030B70E6E70C490B78DB +:109B10005B0712D50B786FF382030B700023CDE93E +:109B20000133039303788DF8043005238DF8053055 +:109B3000044B01A91868FFF744FF04B010BD00BF33 +:109B400059B4002094B400201FB50023CDE901339F +:109B50008DF804008DF8051001A811460393FFF756 +:109B6000A3FF05B05DF804FB1FB50023CDE9013369 +:109B700003938DF8040001238DF8081001A8114605 +:109B80008DF80530FFF790FF05B05DF804FB1FB5B9 +:109B9000144600230822CDE9013303938DF8040015 +:109BA00006230DEB02008DF8053001F0DFFB2146A6 +:109BB00001A8FFF779FF04B010BD1FB50024CDE95F +:109BC00001448DF8040007208DF805008DF8081079 +:109BD00001A89DF8181003928DF80930FFF764FF73 +:109BE00004B010BD1FB54FF40063CDE901300391FF +:109BF00001A81146FFF758FF05B05DF804FB00000F +:109C000038B58B7803F07F03082B05460C4608D93E +:109C10004FF0E023D3F8F03DDB0700D500BE002075 +:109C200038BD064B2046997801F00DFB0028EFD097 +:109C300021462846BDE83840FFF708B859B400204F +:109C40002DE9F047DDE9085681460C4690469A46D4 +:109C50000027B84501DC01200EE06378052B04D114 +:109C6000E37803F0030353450AD04FF0E023D3F821 +:109C7000F03DDA0702D40020BDE8F08700BEFAE725 +:109C800021464846FFF7BCFF38B94FF0E023D3F830 +:109C9000F03DDB07EFD500BEEEE7A378DA0914BF8D +:109CA00033702B70237801371C44D2E70B4B01F043 +:109CB0007F0203EB420303EBD1112031487910F00E +:109CC000010008D14B795B0706D44B7943F00403BC +:109CD0004B71012070470020704700BF59B400202D +:109CE0000B4B01F07F0203EB420303EBD111203158 +:109CF0004B7913F0010209D14B79C3F380005B0764 +:109D000005D54B7962F382034B7170470020704791 +:109D100059B4002070B5164D01F07F0605EB4605DD +:109D200005EBD11420346579ED0709D54FF0E02318 +:109D3000D3F8F03DDA0701D4002070BD00BEFBE788 +:109D4000657945F001056571FFF750F80028F4D1F9 +:109D5000637960F300036371637960F38203637175 +:109D60004FF0E023D3F8F03DDB07E5D500BEE4E794 +:109D700059B40020054B01F07F0203EB420303EBD3 +:109D8000D11191F8250000F00100704759B400206E +:109D900010B50B4B01F07F0203EB420303EBD11430 +:109DA000203463799B0709D4FFF76EF8637943F099 +:109DB00002036371637943F00103637110BD00BF57 +:109DC00059B4002010B50B4B01F07F0203EB4203A6 +:109DD00003EBD114203463799B0709D5FFF778F89A +:109DE00063796FF34103637163796FF30003637108 +:109DF00010BD00BF59B40020054B01F07F0203EBFA +:109E0000420303EBD11191F82500C0F340007047E5 +:109E100059B400202DE9F04F87B001F012FA002864 +:109E200000F09182AF4B1D682B78012B02D10020EE +:109E3000FEF7F2FE03A9281DFFF702FD2B78012B88 +:109E4000044602D10020FEF7E1FE002C00F07B82E8 +:109E50009DF80D30013B072B00F2B382DFE813F0D1 +:109E600008001300A8027E028D021F004A02AA0207 +:109E70009DF80C00FFF76CFD00F038FB9A4B9DF845 +:109E800010209A70CEE79DF80C00FFF761FD00F0FE +:109E90002DFB964B002BC5D0FEF78EFBC2E7924CF4 +:109EA0009DF80C50237843F00103237094F825307B +:109EB0006FF3000384F8253094F825306FF38203A4 +:109EC00084F8253094F826306FF3000384F82630A8 +:109ED00094F826306FF3820384F82630002000F0D7 +:109EE0000DFB9DF8106006F06002602A11D14FF062 +:109EF000E023D3F8F03DDC0700D500BE9DF80C0050 +:109F00000021FEF7C1FF9DF80C008021FEF7BCFF89 +:109F100088E7402A0DD176480028EFD000F0EEFA0D +:109F200004AA00212846AFF3008000287FF47AAF0E +:109F3000E4E706F01F06012E00F07181022E00F00A +:109F40009881002ED3D1202A0FD19DF814300F2BE9 +:109F5000D4D82344D878FFF7DBFC01460028CDD0C5 +:109F600004AA2846FFF71EFDDFE7002ABFD19DF8AF +:109F70001130092BBBD801A252F823F009A20F001F +:109F8000F7A10F00EF9E0F00E3A10F00EF9E0F005F +:109F9000A59F0F0021A10F00EF9E0F00BF9F0F0094 +:109FA000D59F0F0004A800F0AFFA9DF812102846C4 +:109FB000FEF73AFE237843F00203237032E763781A +:109FC0008DF80A3001230DF10A0204A9284600F099 +:109FD00059FA27E79DF812906378994537D063784E +:109FE0003BB12846FEF7BCFEA6782846FFF7B0FC3A +:109FF000A670B9F1000F2AD009F1FF30C0B2FEF708 +:10A00000E9F910B14378022B08D04FF0E023D3F8E0 +:10A01000F03DDF077FF56BAF00BE68E7C379C3F3A0 +:10A020008012C3F340131B0143EA4213227822F04B +:10A030003002134323704388C31800F109060093CC +:10A04000009BB3420AD82B4B0BB1FEF7B2FA84F84F +:10A05000019004A9284600F003FAE3E673780B2B7D +:10A0600003BF337896F80380F6184FF00108737831 +:10A07000042BCAD1009B9A1B93B201934FF0000BA3 +:10A080001D4B1B785FFA8BF70133BB42BDDB3846B3 +:10A09000FFF73EFC31468368019A8246284698477E +:10A0A0000828024639D9019B834236D3B8F1010F03 +:10A0B00006D1DAF8083011498B4208BF4FF0020888 +:10A0C0000021CBB298451DD83B4631460C48019241 +:10A0D00001F0E9F8084B019A1B7801339F421644BE +:10A0E000AEDD92E794B4002059B40020B9850F008A +:10A0F00000000000B3850F0058B40020A9A70F008E +:10A100006CB40020B078034454FA83F30131D8785A +:10A11000FF287FF47AAFDF70D3E70BF1010BAFE7D5 +:10A12000BDF81200030A5A1EC0B20E2A3FF6E6AE70 +:10A1300001A151F822F000BF75A10F009FA10F00EF +:10A14000C1A10F00FD9E0F00FD9E0F00D5A10F00C5 +:10A150009FA10F00FD9E0F00FD9E0F00FD9E0F00B2 +:10A16000FD9E0F00FD9E0F00FD9E0F00FD9E0F0047 +:10A1700087A10F00FEF72AF91223024604A92846F8 +:10A1800000F080F9D1E6934B002B3FF4B7AEAFF36C +:10A190000080024600283FF4AAAE4388EEE7022B77 +:10A1A00007D1FEF717F900283FF4A1AE4388024615 +:10A1B000E4E7894B002B3FF4A1AEAFF30080F2E758 +:10A1C000BDF81410FEF762F9024600283FF496AE7F +:10A1D0000378D3E7814B002B3FF490AEAFF30080C0 +:10A1E000F2E7BDF81230012B7FF488AE237843F0FC +:10A1F000080323702DE7BDF81230012B7FF47EAEEB +:10A2000023786FF3C303F4E72378C3F340129B086A +:10A2100003F002031343ADF80A300223D3E69DF89E +:10A2200014300F2B3FF66AAE2344D878FFF770FB4B +:10A23000014600283FF462AE04AA2846FFF7B2FBAD +:10A2400000287FF4EFAD9DF8103013F060047FF428 +:10A2500055AE9DF811300A3B012B3FF64FAE00F092 +:10A260004DF99DF811300A2B7FF4F3AE8DF80A40BA +:10A27000A8E69DF8141001F07F03082B3FF637AED7 +:10A2800004EB430303EBD113D87CFFF741FB0746F4 +:10A290002AB100283FF432AE04AA014661E69DF8D7 +:10A2A000113003F0FD02012A08D0002B7FF41FAE0D +:10A2B0002846FFF7A1FDADF80A00AEE7BDF8122071 +:10A2C00022B9012B284612D1FFF77CFD002F3FF465 +:10A2D000A9AD04AA39462846FFF764FB002000F028 +:10A2E0000DF994F82630DE073FF59CADB1E6FFF797 +:10A2F0004FFDEBE79DF81010394B01F07F0403EBA5 +:10A30000440303EBD11393F825006FF3000083F8A7 +:10A31000250093F825006FF3820083F825003CB9EF +:10A32000059B9DF811209DF80C0000F0FBF879E5E5 +:10A33000D87CFFF7EDFA48B94FF0E023D3F8F03DB1 +:10A34000D80700D500BE07B0BDE8F08F0469059BB3 +:10A350009DF811209DF80C00A04763E5204B1A786A +:10A36000D1077FF55FAD1F4A002A3FF45BAD187837 +:10A37000C0F3C000AFF3008054E5194B1B78DA0737 +:10A380007FF550AD184B002B3FF44CADAFF3008080 +:10A3900048E5FFF7BDFA436913B19DF80C009847F3 +:10A3A0000134124B1B78E0B201338342F1DA39E514 +:10A3B0000024F6E7049B002B3FF434AD0598984742 +:10A3C00030E54FF0E023D3F8F03DDB077FF52AAD11 +:10A3D00000BE27E5000000000000000000000000B3 +:10A3E00059B40020000000000000000058B4002014 +:10A3F00037B514490446CA89888991F90050831AEF +:10A400009BB2402B28BF4023002D10DA904214D07D +:10A410001A4689680C48019300F0A8FF0A4A019B7C +:10A420008021204603B0BDE83040FFF773BC904266 +:10A430004FF0000103D10022F3E78021FBE7024A3D +:10A44000EFE700BF58B500206CB5002011F0800F79 +:10A450004FF000031A460CBF80211946FFF75ABC83 +:10A4600030B4074C05460B4608684968224603C2CB +:10A470000022C4E902222846197830BCFFF7E6BF63 +:10A4800058B50020F8B5184E0C46054608684968CE +:10A49000B260374603C70021F181E1888B4228BFB3 +:10A4A0000B46B381E18889B153B14AB94FF0E0233B +:10A4B000D3F8F03DDA0701D40020F8BD00BEFBE779 +:10A4C0002846FFF795FF30B10120F6E721782846AE +:10A4D000FFF7BCFFF7E74FF0E023D3F8F03DDB07D1 +:10A4E000EAD500BEE9E700BF58B5002002481422B3 +:10A4F0000021F9F751BF00BF58B50020014B18618A +:10A50000704700BF58B5002010B50246044C0068E3 +:10A510005168234603C30023C4E9023310BD00BFC2 +:10A5200058B5002070B52D4C1E462378C909B1EBF3 +:10A53000D31F054618D04EB14FF0E023D3F8F03DBD +:10A54000DA0701D4002070BD00BEFBE7244B13B135 +:10A550002146AFF3008023690BB90120F3E71F4ABE +:10A56000022128469847F8E794F90030002B06DBD3 +:10A57000A0680028E6D01B49324600F0F7FEA2682A +:10A58000E38932443344A260E2889BB29A42E38179 +:10A5900001D03F2E1ED823696BB921782846FFF7DA +:10A5A00055FF0028D9D14FF0E023D3F8F03DDB0769 +:10A5B000C8D500BEC7E70121084A2846984701468A +:10A5C0000028EAD12846FEF75FFC80212846FEF7E6 +:10A5D0005BFCC2E72846FFF70BFFE2E758B5002017 +:10A5E000000000006CB5002070B500F110050446B5 +:10A5F0002846FFF713F93F2817D9E1780020FFF725 +:10A6000055FB90B12846FFF709F93F28E17807D9B3 +:10A6100004F638024023BDE870400020FFF77ABB03 +:10A62000BDE870400020FFF75BBB70BD08B5044B70 +:10A6300040F6B80202FB00301030FFF7D5F808BD35 +:10A64000ACB5002070B540F6B804074E444304F1A1 +:10A65000100092B23044FFF705F905463019FFF7B4 +:10A66000C3FF284670BD00BFACB500202DE9F04106 +:10A670000446FFF7C7F910B90020BDE8F081FFF7E5 +:10A68000C9F906460028F7D140F6B801164D4C43EB +:10A6900004F12408A8444046FFF7A6F80028EBD0B0 +:10A6A0002F193046B978FFF701FB0028E4D004F6F3 +:10A6B00078042544294640224046FFF7D3F8B9786C +:10A6C000044668B103462A463046FFF723FB48B9E3 +:10A6D0004FF0E023D3F8F03DDB07CDD500BECCE74B +:10A6E000FFF7FEFA2046C8E7ACB5002070B50B4C6A +:10A6F00040F6B80303FB0044243492B205462046DA +:10A70000FFF7F0F806462046FFF76EF83F2802D91B +:10A710002846FFF7ABFF304670BD00BFACB5002048 +:10A7200037B5144C40F6B80200212046F9F734FE44 +:10A73000FF234FF4424201256371E2800023082287 +:10A7400063812273009304F138012B464FF4806239 +:10A7500004F110002581FFF731F800952B464FF4E6 +:10A76000806204F5876104F12400FFF727F803B045 +:10A7700030BD00BFACB5002010B50A4C0021052249 +:10A780002046F9F709FE04F110002434FFF7B0F871 +:10A790002046FFF7ADF820460121BDE81040FFF745 +:10A7A000B3B800BFACB50020F7B54B79022B064615 +:10A7B00003D00025284603B0F0BD8B79022BF8D1D9 +:10A7C000204FBB787BBB8B783B700C7809250C4401 +:10A7D00003E023781D44ADB21C446378242B1BD1C5 +:10A7E0009542F6D96378042B12D163790A2B0FD1E5 +:10A7F000154B277801930133009302231A46E11980 +:10A800003046FFF71DFA70B10E3517FA85F5ADB277 +:10A810000C48FFF7E9FECDE7052BE3D12146304692 +:10A82000FFF7EEF938B94FF0E023D3F8F03DDB073E +:10A83000BFD500BEBDE7A3787B7023781D44ADB2C1 +:10A840001C44CFE7ACB50020AEB5002070B50B4678 +:10A850001146127802F06002202A45D1234E8A88E0 +:10A860003478944240D14A78203A032A3CD8DFE831 +:10A8700002F00213162F2BB91D4A0723FFF702FE21 +:10A88000012070BD022BFBD11A4B002BF8D01849C8 +:10A890000020AFF30080F3E7002BF1D1ECE713B910 +:10A8A000FFF7DEFDECE7022BEAD14C881248347149 +:10A8B00004F0010585F00101FFF726F80F4B002B8E +:10A8C000DED0C4F3400229460020AFF30080D7E772 +:10A8D000002BE5D0022BD3D1094B002BD0D04988D7 +:10A8E0000020AFF30080CBE70020CAE7ACB5002022 +:10A8F000B2B5002000000000D0B50020000000002C +:10A90000000000002DE9F347374D1C46EB788B42E1 +:10A9100007460E4607D0AB788B4258D1AB78B3428E +:10A9200032D001245CE0A2B205F6380105F1100036 +:10A93000FEF7D8FF2D4B2BB92D4BEBB92A48FFF76B +:10A9400053FEEBE76B79FF2BF6D005F638094FF095 +:10A95000000805F1100AA045EED019F8013B6A790C +:10A960009A4206D15046FEF751FF10B96979AFF30C +:10A97000008008F10108EEE71E48FEF747FF0028B7 +:10A98000DCD1FDF789F9D9E71B4B13B10020AFF3F8 +:10A9900000800020FFF76AFE0028C2D11748FEF7AA +:10A9A00023FF0028BDD1002CBBD014F03F03B8D149 +:10A9B000A97801933846FFF779F9019B04460028EE +:10A9C000AFD0A9781A463846FFF7A4F908E04FF04F +:10A9D000E023D3F8F04D14F0010401D000BE0024B0 +:10A9E000204602B0BDE8F087ACB5002000000000B2 +:10A9F000997C0F00BCB5002000000000D0B50020FD +:10AA000030B4104B02249A6B83F82C10996883F8A9 +:10AA1000304093F83C408A1A9A6224B942F2050504 +:10AA20009D8783F83E4051B14AB11A7BD20930BCB0 +:10AA300014BF93F82E1093F82F10FFF7A9B930BC6C +:10AA4000704700BF64CE002038B5154B154C054645 +:10AA500073B1607BAFF3008050B942F2077384F8A2 +:10AA60003E00A38728460121BDE83840FFF7C8BF54 +:10AA7000A26BA36894F82F109B1AB3F5805F28BFD0 +:10AA80004FF48053084A9BB22846FFF743F930B988 +:10AA90004FF0E023D3F8F03DDB0700D500BE38BD12 +:10AAA0000000000064CE002064BE002073B5234C7B +:10AAB000E28AA36852BA92B2054612B1B3FBF2F22F +:10AAC00092B2A06BD4F81110B0FBF2F61B1AB3F5DA +:10AAD000805F28BF4FF4805309BA009302FB16022F +:10AAE000174B607B3144FDF7DBFB031E0CDA43F2AE +:10AAF0000333A38701210023284684F83E3002B0A7 +:10AB0000BDE87040FFF77CBF94F82E1006D100938B +:10AB10001A462846FFF751F802B070BD084A9BB2AA +:10AB20002846FFF7F7F80028F6D14FF0E023D3F8D6 +:10AB3000F03DDB07F0D500BEEEE700BF64CE00209D +:10AB400064BE0020C28A836852BA92B223B9002A36 +:10AB50000CBF002002207047C17B282904D1017B53 +:10AB6000C90906D1022070472A2902D1017BC909EF +:10AB7000F8D122B1934234BF022000207047012057 +:10AB800070470000044880F83C1080F83D2080F8B1 +:10AB90003E300120704700BF64CE002002484022B2 +:10ABA0000021F9F7F9BB00BF64CE00200248402223 +:10ABB0000021F9F7F1BB00BF64CE002073B54B79DB +:10ABC000082B054602D0002002B070BD8B79062B01 +:10ABD000F9D1CB79502BF6D1162A07D84FF0E023C4 +:10ABE000D3F8F03DD907EED500BEECE7164C8B78D4 +:10ABF00084F82D3004F12E030E78019304F12F0315 +:10AC0000009302231A463144FFF71AF838B94FF07F +:10AC1000E023D3F8F03DDA07D5D500BED4E7002312 +:10AC200084F8303094F82F101F2322462846FFF76F +:10AC300071F830B94FF0E023D3F8F03DDB0700D5D1 +:10AC400000BE1720C0E700BF64CE00207FB50646D7 +:10AC50001546A1B9137803F07F02022A48D16C7817 +:10AC6000012C45D16A88002A42D1AB883C4D95F829 +:10AC70003020042ADBB204D11946FFF789F80120FD +:10AC80001AE095F82E10994218D1022AF7D1AA6B32 +:10AC9000AB689B1AAB62032385F8303005F12002C4 +:10ACA0000D23FFF737F80028E9D14FF0E023D3F860 +:10ACB000F03DDB0752D500BE04B070BD95F82F10F3 +:10ACC0009942DCD1002ADAD10191FFF753F80199BA +:10ACD0000028D4D13046FFF78FF80028CFD10023C9 +:10ACE00085F830301E4A95F82F101F233046D8E7DC +:10ACF00003F06003202B31D16B78FE2B13D0FF2B98 +:10AD00002CD16B8853BBE9888AB23ABB144B3046CE +:10AD100099872946C3E90D2283F8302083F83E2025 +:10AD2000FFF79EFBABE76B88C3B9EB88012B15D10E +:10AD30008DF80F300B4B1BB1AFF300808DF80F0077 +:10AD40009DF80F3053B1013B8DF80F300DF10F021C +:10AD5000012329463046FFF795FB90E70020ABE73B +:10AD600064CE0020000000002DE9F041AB4C94F8C7 +:10AD70003070012F90B005461E4600F0A181032FD0 +:10AD800000F00D82002F41D194F82F308B4201D07A +:10AD9000012013E01F2E03D12268A14B9A4210D04C +:10ADA00094F82E100423284684F83030FEF7F0FF84 +:10ADB00094F82F102846FEF7EBFF002010B0BDE8F6 +:10ADC000F081984B23626368E67BD4F8088084F8AE +:10ADD0002C70C4E90937012384F8303006F0FD03F4 +:10ADE000282BC4E90D872BD12046FFF7ABFE014687 +:10ADF00018B12846FFF704FE08E0B8F1000F56D05E +:10AE0000282E40F0A8812846FFF750FE94F83030F5 +:10AE1000022BBDD194F82E102846FEF7EDFF002836 +:10AE2000B6D1A368A26B94F82E10934240F2E08151 +:10AE3000207BC00900F0DC812846FEF7A9FFA7E7C8 +:10AE4000B8F1000F17D0237BDB0914D1B8F5805F70 +:10AE500001D90121CDE7744A1FFA88F32846FEF78D +:10AE600059FF0028D2D14FF0E023D3F8F03DDA07A4 +:10AE7000A3D500BEA2E7252E677B08D8192E1AD8C5 +:10AE8000032E00F0F780122E00F09F8096B394F806 +:10AE90003C30002BDDD1A38E634A6449607BFDF713 +:10AEA000EDF9031ED5DB60D1A368002BD1D10223BD +:10AEB00084F83030AAE71A3E0B2EE8D801A353F8E5 +:10AEC00026F000BF35B00F0015AF0F008FAE0F009A +:10AED0008FAE0F008FAE0F008FAE0F008FAE0F0042 +:10AEE0008FAE0F008FAE0F0085AF0F008FAE0F003B +:10AEF0002FAF0F003846FDF7BFF90028D4D194F8E2 +:10AF00003C30002BC3D140F20243A387002384F8D6 +:10AF10003E30BCE7464B002BC6D0E17C3846C1F33F +:10AF2000400301F001020909FDF74EFAE5E70DF1D2 +:10AF3000160206A93846FDF73FFA069B13B1BDF885 +:10AF400016203AB994F83C30002BA0D140F20242CE +:10AF5000A287DCE712BA013B1BBA0892324807937A +:10AF6000082207A900F002FA0823A06800283FF48D +:10AF700070AF834228BF034663632B4A94F82E10B8 +:10AF80009BB26BE70023CDE90733099308238DF8C3 +:10AF90001F300DF11602022306A938468DF8243021 +:10AFA000FDF70AFA069A002ACCD0BDF81630002B1D +:10AFB000C8D012BA5BBA08921B48ADF826300C22F2 +:10AFC00007A900F0D3F90C23CFE72422002107A81A +:10AFD000F9F7E2F980238DF81D30082202232021A1 +:10AFE00009A88DF81E308DF81F30F9F7D5F9102219 +:10AFF00020210BA8F9F7D0F9042220210FA8F9F796 +:10B00000CBF90FAB0BAA09A93846FDF701F90648A1 +:10B01000242207A900F0AAF92423A6E764CE002081 +:10B02000555342435553425364BE002073CE002013 +:10B03000C9830F0003238DF81C3000238DF81D30C9 +:10B040008DF81E308DF81F30704B8BB13846AFF342 +:10B0500000809DF81E3080F0010060F3C7130422C9 +:10B060006B488DF81E3007A900F080F904237CE7B7 +:10B070000120EEE71222002107A8F9F78DF9F0234D +:10B0800094F83C208DF81C300A238DF823304FF0C3 +:10B09000000362F303038DF81E3094F83D308DF801 +:10B0A00028305B4894F83E308DF82930122207A9E9 +:10B0B00000F05CF90023A38784F83E30122354E7A4 +:10B0C000E37BA06B282B06D1636B30449842A063CE +:10B0D000BFF4EDAE97E62A2B41D1E28A52BA92B282 +:10B0E0001AB1A368B3FBF2F292B2D4F81110484F30 +:10B0F000B0FBF2FC09BA02FB1C020096607B3B46E7 +:10B100006144FDF7EFF8002809DAA36B3344A36329 +:10B1100043F20333A387002384F83E3099E6864246 +:10B1200012D9321A40B1A36B03920344391838463E +:10B13000A36300F0B5F9039A94F82F10002300934D +:10B140002846FEF73AFD61E6A36B1E44636BA663D7 +:10B150009E42BFF4ACAE2846FFF776FC56E6237B52 +:10B160003044DB09A0630CD1A38E294A607B04F133 +:10B170000F01FDF783F8002803DA39462846FFF768 +:10B180003FFCD4E90D329A42BFF491AE4FF0E02378 +:10B19000D3F8F03DDB077FF539AE00BE36E694F814 +:10B1A0002E308B427FF432AE0D2E7FF42FAEE37B38 +:10B1B000282B09D02A2B14D0164B53B1607B04F1F5 +:10B1C0000F01AFF3008004E0134B13B1607BAFF3CA +:10B1D0000080002384F83030104A94F82F101F2389 +:10B1E0003CE60F4B002BF4D0607BFDF793F8F0E7C3 +:10B1F0009B1AA362032384F830300A4A0D232846A1 +:10B20000FEF788FD00287FF4C3AD2CE600000000A7 +:10B2100064BE0020000000000000000064CE00209A +:10B2200015830F0084CE002008B50020FEF700FC37 +:10B2300030B94FF0E023D3F8F03DDB0700D500BE76 +:10B2400008BDFEF7EFBB8388C07800F0030002283A +:10B25000C3F30A0315D003281DD001280FD10229FA +:10B2600040F2FF3208BF4FF480629A420FD24FF093 +:10B27000E023D3F8F00D10F0010008D000BE00204C +:10B280007047022904D1B3F5007FF0D10120704747 +:10B29000402BFBD9EBE702290CBF4FF48062402220 +:10B2A0009A42F3D2E3E730B50A44914200D330BD6D +:10B2B0004C78052C06D18C7804F07F0500EB450511 +:10B2C000E4092B550C782144EFE700000649074AB2 +:10B2D000074B9B1A03DD043BC858D050FBDCF9F741 +:10B2E00063FEF8F7D5FF000068BD0F000080002066 +:10B2F000C8860020FEE7FEE7FEE7FEE7FEE7FEE782 +:10B30000FEE7FEE7FEE7FEE7032A70B515D940EA3F +:10B31000010C1CF0030F04460B4621D119462046B0 +:10B320000E680568B54204F1040403F1040317D163 +:10B33000043A032A20461946F0D8541EA2B100F15F +:10B34000FF3C013901E0C3180CD01CF801EF11F8E3 +:10B35000012F9645A4EB0C03F5D0AEEB020070BDB7 +:10B36000541EECE7184670BD104670BD844641EA95 +:10B37000000313F003036DD1403A41D351F8043B6D +:10B3800040F8043B51F8043B40F8043B51F8043BBF +:10B3900040F8043B51F8043B40F8043B51F8043BAF +:10B3A00040F8043B51F8043B40F8043B51F8043B9F +:10B3B00040F8043B51F8043B40F8043B51F8043B8F +:10B3C00040F8043B51F8043B40F8043B51F8043B7F +:10B3D00040F8043B51F8043B40F8043B51F8043B6F +:10B3E00040F8043B51F8043B40F8043B51F8043B5F +:10B3F00040F8043B51F8043B40F8043B403ABDD2CE +:10B40000303211D351F8043B40F8043B51F8043B6F +:10B4100040F8043B51F8043B40F8043B51F8043B2E +:10B4200040F8043B103AEDD20C3205D351F8043BFE +:10B4300040F8043B043AF9D2043208D0D2071CBFCA +:10B4400011F8013B00F8013B01D30B8803806046F3 +:10B45000704700BF082A13D38B078DD010F0030369 +:10B460008AD0C3F10403D21ADB071CBF11F8013BD9 +:10B4700000F8013B80D331F8023B20F8023B7BE728 +:10B48000043AD9D3013A11F8013B00F8013BF9D253 +:10B490000B7803704B7843708B78837060467047ED +:10B4A00088420DD98B1883420AD900EB020CBAB13D +:10B4B000624613F801CD02F801CD9942F9D17047E7 +:10B4C0000F2A0ED8034602F1FF3C4AB10CF1010CE1 +:10B4D000013B8C4411F8012B03F8012F6145F9D190 +:10B4E000704740EA01039B0750D1A2F1100370B5E9 +:10B4F00001F1200C23F00F0501F1100E00F11004F2 +:10B50000AC441B095EF8105C44F8105C5EF80C5CFF +:10B5100044F80C5C5EF8085C44F8085C5EF8045C77 +:10B5200044F8045C0EF1100EE64504F11004E9D174 +:10B53000013312F00C0F01EB031102F00F0400EBCA +:10B54000031327D0043C24F003064FEA940C1E4456 +:10B550001C1F8E465EF8045B44F8045FB442F9D1C8 +:10B560000CF1010402F0030203EB840301EB8401FC +:10B5700002F1FF3C4AB10CF1010C013B8C4411F883 +:10B58000012B03F8012F6145F9D170BD02F1FF3C99 +:10B5900003469BE72246EBE7830710B5044610D12C +:10B5A0000268A2F1013323EA020313F0803F08D1BD +:10B5B00050F8042FA2F1013323EA020313F0803F75 +:10B5C000F6D003781BB110F8013F002BFBD100F03F +:10B5D00003F8204610BD00BF80EA0102844612F045 +:10B5E000030F4FD111F0030F32D14DF8044D11F07C +:10B5F000040F51F8043B0BD0A3F101329A4312F02F +:10B60000803F04BF4CF8043B51F8043B16D100BF07 +:10B6100051F8044BA3F101329A4312F0803FA4F198 +:10B6200001320BD14CF8043BA24312F0803F04BF1F +:10B6300051F8043B4CF8044BEAD023460CF8013B8C +:10B6400013F0FF0F4FEA3323F8D15DF8044B704736 +:10B6500011F0010F06D011F8012B0CF8012B002A74 +:10B6600008BF704711F0020FBFD031F8022B12F063 +:10B67000FF0F16BF2CF8022B8CF8002012F47F4F1E +:10B68000B3D1704711F8012B0CF8012B002AF9D126 +:10B69000704700BF00000000000000000000000034 +:10B6A000000000000000000000000000000000009A +:10B6B000000000000000000000000000000000008A +:10B6C00090F800F06DE9024520F007016FF0000CE2 +:10B6D00010F0070491F820F040F049804FF000048A +:10B6E0006FF00700D1E9002391F840F000F1080065 +:10B6F00082FA4CF2A4FA8CF283FA4CF3A2FA8CF39D +:10B700004BBBD1E9022382FA4CF200F10800A4FA03 +:10B710008CF283FA4CF3A2FA8CF3E3B9D1E9042357 +:10B7200082FA4CF200F10800A4FA8CF283FA4CF38E +:10B73000A2FA8CF37BB9D1E9062301F1200182FA48 +:10B740004CF200F10800A4FA8CF283FA4CF3A2FA4E +:10B750008CF3002BC6D0002A04BF04301A4612BA5C +:10B76000B2FA82F2FDE8024500EBD2007047D1E95F +:10B77000002304F00305C4F100004FEAC50514F0EE +:10B78000040F91F840F00CFA05F562EA05021CBFBF +:10B7900063EA050362464FF00004A9E7F0B5254FC0 +:10B7A000A2F1020E164605460C460FCF8BB0EC46B2 +:10B7B000ACE80F000FCFACE80F0097E803004CF89F +:10B7C000040BBEF1220F8CF800102ED804F1FF3EBE +:10B7D00070464FF0000CB5FBF6F206FB125328330F +:10B7E0006B44614613F828CC00F801CF2B469E42EB +:10B7F00001F1010C1546EED9002304F80C3089B193 +:10B80000A44472461EF8010F1CF8015D8EF800502A +:10B810006FEA0E0302322344121B0B449A428CF847 +:10B820000000EEDB20460BB0F0BD002020700BB016 +:10B83000F0BD00BF34BD0F00FFF7B0BF53B94AB928 +:10B84000002908BF00281CBF4FF0FF314FF0FF3028 +:10B8500000F074B9ADF1080C6DE904CE00F006F803 +:10B86000DDF804E0DDE9022304B070472DE9F0477C +:10B87000089D04468E46002B4DD18A42944669D9D4 +:10B88000B2FA82F252B101FA02F3C2F1200120FAB7 +:10B8900001F10CFA02FC41EA030E94404FEA1C4805 +:10B8A000210CBEFBF8F61FFA8CF708FB16E341EA01 +:10B8B000034306FB07F199420AD91CEB030306F187 +:10B8C000FF3080F01F81994240F21C81023E6344A8 +:10B8D0005B1AA4B2B3FBF8F008FB103344EA03444C +:10B8E00000FB07F7A7420AD91CEB040400F1FF3361 +:10B8F00080F00A81A74240F207816444023840EA9E +:10B900000640E41B00261DB1D4400023C5E90043D6 +:10B910003146BDE8F0878B4209D9002D00F0EF8059 +:10B920000026C5E9000130463146BDE8F087B3FA8C +:10B9300083F6002E4AD18B4202D3824200F2F98074 +:10B94000841A61EB030301209E46002DE0D0C5E977 +:10B95000004EDDE702B9FFDEB2FA82F2002A40F0C3 +:10B960009280A1EB0C014FEA1C471FFA8CFE0126C6 +:10B97000200CB1FBF7F307FB131140EA01410EFB6A +:10B9800003F0884208D91CEB010103F1FF3802D211 +:10B99000884200F2CB804346091AA4B2B1FBF7F00B +:10B9A00007FB101144EA01440EFB00FEA64508D92E +:10B9B0001CEB040400F1FF3102D2A64500F2BB806B +:10B9C0000846A4EB0E0440EA03409CE7C6F12007BA +:10B9D000B34022FA07FC4CEA030C20FA07F401FA00 +:10B9E00006F31C43F9404FEA1C4900FA06F3B1FB89 +:10B9F000F9F8200C1FFA8CFE09FB181140EA0141EE +:10BA000008FB0EF0884202FA06F20BD91CEB01018A +:10BA100008F1FF3A80F08880884240F28580A8F1E2 +:10BA200002086144091AA4B2B1FBF9F009FB101134 +:10BA300044EA014100FB0EFE8E4508D91CEB0101D2 +:10BA400000F1FF346CD28E456AD90238614440EA75 +:10BA50000840A0FB0294A1EB0E01A142C846A646F5 +:10BA600056D353D05DB1B3EB080261EB0E0101FA7E +:10BA700007F722FA06F3F1401F43C5E900710026DB +:10BA80003146BDE8F087C2F12003D8400CFA02FC31 +:10BA900021FA03F3914001434FEA1C471FFA8CFE41 +:10BAA000B3FBF7F007FB10360B0C43EA064300FB31 +:10BAB0000EF69E4204FA02F408D91CEB030300F1CF +:10BAC000FF382FD29E422DD9023863449B1B89B286 +:10BAD000B3FBF7F607FB163341EA034106FB0EF30F +:10BAE0008B4208D91CEB010106F1FF3816D28B42BC +:10BAF00014D9023E6144C91A46EA004638E72E4688 +:10BB0000284605E70646E3E61846F8E64B45A9D27F +:10BB1000B9EB020864EB0C0E0138A3E74646EAE7EE +:10BB2000204694E74046D1E7D0467BE7023B61449C +:10BB300032E7304609E76444023842E7704700BF05 +:10BB4000F8B500BFF8BC08BC9E467047F8B500BF0A +:10BB5000F8BC08BC9E467047088000200010020018 +:10BB60000338FDD87047000000000000000000000E +:10BB70000338FDD87047010000000000089900203C +:10BB80000338FDD87047416461444655004D6573E4 +:10BB90006874617374696300302E392E32207331FA +:10BBA000343020362E312E3100000000000000001D +:10BBB00000000000000000000023D1BCEA5F7823F1 +:10BBC00015DEEF12120000000000000070B000202F +:10BBD0004164616672756974006E52462055463242 +:10BBE00000303132333435363738394142434445F9 +:10BBF00046006E52462053657269616C0009045319 +:10BC00006F66744465766963653A200053002E00C0 +:10BC10006E6F7420666F756E640D0A00EB3C905574 +:10BC20004632205546322000020101000240000049 +:10BC300000F80201010001000000000009010100FC +:10BC4000800029420042004D455348544153544915 +:10BC5000430046415431362020203C21646F6374F8 +:10BC60007970652068746D6C3E0A3C68746D6C3E3A +:10BC70003C626F64793E3C7363726970743E0A6C17 +:10BC80006F636174696F6E2E7265706C6163652895 +:10BC90002268747470733A2F2F6275796D656163D1 +:10BCA0006F666665652E636F6D2F6D61726B2E62B8 +:10BCB0006972737322293B0A3C2F73637269707433 +:10BCC0003E3C2F626F64793E3C2F68746D6C3E0A77 +:10BCD00000000000494E464F5F554632545854000C +:10BCE00024850020494E44455820202048544D00CA +:10BCF0005ABC0F0043555252454E5420554632000F +:10BD00000000000021A70F0079A70F00A9A70F00CE +:10BD10004DA80F0005A90F00000000009DAB0F000B +:10BD2000ADAB0F00BDAB0F004DAC0F0069AD0F0008 +:10BD30000000000030313233343536373839616233 +:10BD4000636465666768696A6B6C6D6E6F7071724B +:10BD5000737475767778797A00000000000000002F +:10BD60002885FF7F010000000880002000000000FF +:10BD700000000000F48200205C830020C4830020C7 +:10BD800000000000000000000000000000000000B3 +:10BD900000000000000000000000000000000000A3 +:10BDA0000000000000000000000000000000000093 +:10BDB0000000000000000000000000000000000083 +:10BDC0000000000000000000000000000000000073 +:10BDD0000000000000000000000000000000000063 +:10BDE0000000000000000000000000000000000053 +:10BDF0000000000000000000000000000000000043 +:10BE00000000000000000000000000000000000032 +:10BE10000000000000000000010000000000000021 +:10BE20000E33CDAB34126DE6ECDE05000B000000E6 +:10BE30000000000000000000000000000000000002 +:10BE400000000000000000000000000000000000F2 +:10BE500000000000000000000000000000000000E2 +:10BE600000000000000000000000000000000000D2 +:10BE700000000000000000000000000000000000C2 +:10BE800000000000000000000000000000000000B2 +:10BE900000000000000000000000000000000000A2 +:10BEA0000000000000000000000000000000000092 +:10BEB0000000000000000000000000000000000082 +:10BEC0000000000000000000000000000000000072 +:10BED0000000000000000000000000000000000062 +:10BEE0000000000000000000000000000000000052 +:10BEF0000000000000000000000000000000000042 +:10BF00000000000000000000000000000000000031 +:10BF10000000000000000000000000000000000021 +:10BF20000000000000000000000000000000000011 +:10BF30000000000000000000000000000000000001 +:10BF400000000000000000000000000000000000F1 +:10BF500000000000000000000000000000000000E1 +:10BF600000000000000000000000000000000000D1 +:10BF700000000000000000000000000000000000C1 +:10BF800000000000000000000000000000000000B1 +:10BF900000000000000000000000000000000000A1 +:10BFA0000000000000000000000000000000000091 +:10BFB0000000000000000000000000000000000081 +:10BFC0000000000000000000000000000000000071 +:10BFD0000000000000000000000000000000000061 +:10BFE0000000000000000000000000000000000051 +:10BFF0000000000000000000000000000000000041 +:10C000000000000000000000000000000000000030 +:10C010000000000000000000000000000000000020 +:10C020000000000000000000000000000000000010 +:10C030000000000000000000000000000000000000 +:10C0400000000000000000000000000000000000F0 +:10C0500000000000000000000000000000000000E0 +:10C0600000000000000000000000000000000000D0 +:10C0700000000000000000000000000000000000C0 +:10C0800000000000000000000000000000000000B0 +:10C0900000000000000000000000000000000000A0 +:10C0A0000000000000000000000000000000000090 +:10C0B0000000000000000000000000000000000080 +:10C0C0000000000000000000000000000000000070 +:10C0D0000000000000000000000000000000000060 +:10C0E0000000000000000000000000000000000050 +:10C0F0000000000000000000000000000000000040 +:10C10000000000000000000000000000000000002F +:10C11000000000000000000000000000000000001F +:10C12000000000000000000000000000000000000F +:10C1300000000000000000000000000000000000FF +:10C1400000000000000000000000000000000000EF +:10C1500000000000000000000000000000000000DF +:10C1600000000000000000000000000000000000CF +:10C1700000000000000000000000000000000000BF +:10C1800000000000000000000000000000000000AF +:10C190000000000000000000FFFFFFFF7C7F002088 +:10C1A0000090D003FF00FFFF320000007D7B0F00F6 +:10C1B000F17B0F0001090262000301008032080BCD +:10C1C000000202020000090400000102020004054E +:10C1D0002400200105240100010424020205240694 +:10C1E00000010705810308001009040100020A008C +:10C1F000000007050202400000070582024000001F +:10C200000904020002080650050705030240000069 +:10C210000705830240000009024B00020100803242 +:10C22000080B0002020200000904000001020200E3 +:10C230000405240020010524010001042402020554 +:10C24000240600010705810308001009040100020B +:10C250000A000000070502024000000705820240B4 +:10C26000000012010002EF0201409A2329000001A0 +:10C2700001020301FDBB0F008DBB0F008DBB0F0042 +:10C2800064B30020F2BB0F00D9BB0F00554632202B +:10C29000426F6F746C6F6164657220302E392E327C +:10C2A000206C69622F6E726678202876322E302ECE +:10C2B0003029206C69622F74696E79757362202849 +:10C2C000302E31322E302D3134352D673937373518 +:10C2D000653736393129206C69622F75663220281E +:10C2E00072656D6F7465732F6F726967696E2F6306 +:10C2F0006F6E6669677570646174652D392D67614D +:10C30000646262386337290D0A4D6F64656C3A20A8 +:10C3100068747470733A2F2F6275796D6561636FFD +:10C32000666665652E636F6D2F6D61726B2E626937 +:10C330007273730D0A426F6172642D49443A204D45 +:10C340006573687461737469630D0A446174653A56 +:10C350002053657020203120323032340D0A000025 +:10C3600000000000000000000000000000000000CD +:10C3700000000000000000000000000000000000BD +:10C3800000000000000000000000000000000000AD +:10C39000000000000000000000000000000000009D +:10C3A000000000000000000000000000000000008D +:10C3B000000000000000000000000000000000007D +:10C3C000000000000000000000000000000000006D +:10C3D000000000000000000000000000000000005D +:10C3E000000000000000000000000000000000004D +:10C3F000000000000000000000000000000000003D +:10C40000000000000000000000000000010000002B +:10C4100098B4002010000C000000E0FF1F00000096 +:10C42000000000005D450F0069420F0041420F000F +:10D80000F1109E1E797A22200500000064000000BD +:10D81000CC00000000001000CD000000000004005B +:10D82000D000000029009A23D10000004028A5ADB7 +:10D83000D2000000200000000000000000000000F6 +:10D8400000000000000000000000000000000000D8 +:08D850000000000000000000D0 +:020000041000EA +:0810140000400F0000E00F0096 +:00000001FF diff --git a/bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.zip b/bin/generic/Meshtastic_6.1.0_bootloader-0.9.2_s140_6.1.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..ae035898dfa0e679bcfb1726c0f63075ec084f08 GIT binary patch literal 190874 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 literal 0 HcmV?d00001 diff --git a/bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.hex b/bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.hex new file mode 100644 index 000000000..37750fc2c --- /dev/null +++ b/bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.hex @@ -0,0 +1,11851 @@ +:04000003F000B2CD8A +:020000040000FA +:1000000000040020810A000015070000610A0000BA +:100010001F07000029070000330700000000000050 +:10002000000000000000000000000000A50A000021 +:100030003D070000000000004707000051070000D6 +:100040005B070000650700006F07000079070000EC +:10005000830700008D07000097070000A10700003C +:10006000AB070000B5070000BF070000C90700008C +:10007000D3070000DD070000E7070000F1070000DC +:10008000FB070000050800000F0800001908000029 +:10009000230800002D080000370800004108000078 +:1000A0004B080000550800005F08000069080000C8 +:1000B000730800007D080000870800009108000018 +:1000C0009B080000A5080000AF080000B908000068 +:1000D000C3080000CD080000D7080000E1080000B8 +:1000E000EB080000F5080000FF0800000909000007 +:1000F000130900001D090000270900003109000054 +:100100003B0900001FB500F003F88DE80F001FBD8C +:1001100000F0ACBC40F6FC7108684FF01022401CA7 +:1001200008D00868401C09D00868401C04D0086842 +:1001300000F037BA9069F5E79069F9E7704770B554 +:100140000B46010B184400F6FF70040B4FF0805073 +:100150000022090303692403406943431D1B104621 +:1001600000F048FA29462046BDE8704000F042BA47 +:10017000F0B54FF6FF734FF4B4751A466E1E11E0DA +:10018000A94201D3344600E00C46091B30F8027B3B +:10019000641E3B441A44F9D19CB204EB134394B25D +:1001A00004EB12420029EBD198B200EB134002EBB2 +:1001B000124140EA0140F0BDF34992B00446D1E952 +:1001C0000001CDE91001FF224021684600F0F4FB58 +:1001D00094E80F008DE80F00684610A902E004C8FB +:1001E00041F8042D8842FAD110216846FFF7C0FF7C +:1001F0001090AA208DF8440000F099F9FFF78AFFCB +:1002000040F6FC7420684FF01025401C0FD0206889 +:1002100010226946803000F078F92068401C08D030 +:100220002068082210A900F070F900F061F9A869AF +:10023000EEE7A869F5E74FF080500369406940F6A2 +:10024000FC71434308684FF01022401C06D0086838 +:1002500000F58050834203D2092070479069F7E788 +:100260000868401C04D00868401C03D00020704778 +:100270009069F9E70420704770B504460068C34DE3 +:10028000072876D2DFE800F033041929631E250021 +:10029000D4E9026564682946304600F062F92A46CE +:1002A0002146304600F031F9AA002146304600F0E0 +:1002B00057FB002800D0032070BD00F009FC4FF46C +:1002C000805007E0201D00F040F90028F4D100F034 +:1002D000FFFB60682860002070BD241D94E80700C3 +:1002E000920000F03DFB0028F6D00E2070BDFFF715 +:1002F000A2FF0028FAD1D4E901034FF0805100EBAE +:10030000830208694D69684382420ED840F6F8704E +:1003100005684FF010226D1C09D0056805EB8305B8 +:100320000B6949694B439D4203D9092070BD55694A +:10033000F4E70168491C03D00068401C02D003E0C8 +:100340005069FAE70F2070BD2046FFF735FFFFF731 +:1003500072FF0028F7D1201D00F0F7F80028F2D135 +:1003600060680028F0D100F0E2F8FFF7D3FE00F05B +:10037000BFF8072070BD10B50C46182802D0012028 +:10038000086010BD2068FFF777FF206010BD41684E +:10039000054609B1012700E0002740F6F8742068FF +:1003A0004FF01026401C2BD02068AA68920000F065 +:1003B000D7FA38B3A86881002068401C27D020688D +:1003C000FFF7BDFED7B12068401C22D026684FF051 +:1003D0008050AC686D68016942695143A9420DD9EA +:1003E000016940694143A14208D92146304600F0E5 +:1003F000B8F822462946304600F087F800F078F831 +:100400007069D2E700F093F8FFF784FEF6E77069B1 +:10041000D6E77669DBE740F6FC7420684FF01026DB +:10042000401C23D02068401C0CD02068401C1FD0EA +:100430002568206805F18005401C1BD027683879A5 +:10044000AA2819D040F6F8700168491C42D001680A +:10045000491C45D00168491C3ED001680968491C07 +:100460003ED00168491C39D000683EE0B069DAE747 +:10047000B569DEE7B769E2E710212846FFF778FEA5 +:100480003968814222D12068401C05D0D4F8001080 +:1004900001F18002C03107E0B169F9E730B108CA63 +:1004A00051F8040D984201D1012000E000208A4259 +:1004B000F4D158B1286810B1042803D0FEE72846CB +:1004C000FFF765FF3149686808600EE0FFF722FE1C +:1004D00000F00EF87169BBE77169BFE7706904E06D +:1004E0004FF480500168491C01D000F0CBFAFEE7C0 +:1004F000BFF34F8F26480168264A01F4E06111439B +:100500000160BFF34F8F00BFFDE72DE9F0411746B3 +:100510000D460646002406E03046296800F054F8EF +:10052000641C2D1D361DBC42F6D3BDE8F08140F69B +:10053000FC700168491C04D0D0F800004FF48051D1 +:10054000FDE54FF010208069F8E74FF080510A690F +:10055000496900684A43824201D810207047002050 +:10056000704770B50C4605464FF4806608E0284693 +:1005700000F017F8B44205D3A4F5806405F5805562 +:10058000002CF4D170BD0000F40A0000000000202F +:100590000CED00E00400FA05144801680029FCD0C5 +:1005A0007047134A0221116010490B68002BFCD0E0 +:1005B0000F4B1B1D186008680028FCD0002010603D +:1005C00008680028FCD07047094B10B501221A605A +:1005D000064A1468002CFCD0016010680028FCD08A +:1005E0000020186010680028FCD010BD00E4014015 +:1005F00004E5014070B50C46054600F073F810B9EB +:1006000000F07EF828B121462846BDE8704000F091 +:1006100007B821462846BDE8704000F037B8000012 +:100620007FB5002200920192029203920A0B000B06 +:100630006946012302440AE0440900F01F0651F80C +:10064000245003FA06F6354341F82450401C8242F8 +:10065000F2D80D490868009A10430860081D016827 +:10066000019A1143016000F03DF800280AD00649C4 +:1006700010310868029A10430860091D0868039A3F +:10068000104308607FBD00000006004030B50F4CED +:10069000002200BF04EB0213D3F800582DB9D3F8A1 +:1006A000045815B9D3F808581DB1521C082AF1D3C3 +:1006B00030BD082AFCD204EB0212C2F80008C3F8CD +:1006C00004180220C3F8080830BD000000E0014013 +:1006D0004FF08050D0F83001082801D0002070473A +:1006E000012070474FF08050D0F83011062905D016 +:1006F000D0F83001401C01D0002070470120704725 +:100700004FF08050D0F830010A2801D00020704707 +:100710000120704708208F490968095808471020B0 +:100720008C4909680958084714208A4909680958FA +:100730000847182087490968095808473020854923 +:100740000968095808473820824909680958084744 +:100750003C20804909680958084740207D490968BC +:100760000958084744207B49096809580847482028 +:1007700078490968095808474C207649096809589A +:10078000084750207349096809580847542071499F +:1007900009680958084758206E49096809580847E8 +:1007A0005C206C4909680958084760206949096854 +:1007B00009580847642067490968095808476820AC +:1007C00064490968095808476C2062490968095852 +:1007D000084770205F4909680958084774205D4937 +:1007E00009680958084778205A490968095808478C +:1007F0007C205849096809580847802055490968EC +:10080000095808478420534909680958084788202F +:1008100050490968095808478C204E490968095809 +:10082000084790204B4909680958084794204949CE +:10083000096809580847982046490968095808472F +:100840009C204449096809580847A0204149096883 +:1008500009580847A4203F49096809580847A820B3 +:100860003C49096809580847AC203A4909680958C1 +:100870000847B0203749096809580847B420354966 +:10088000096809580847B8203249096809580847D3 +:10089000BC203049096809580847C0202D4909681B +:1008A00009580847C4202B49096809580847C82037 +:1008B0002849096809580847CC2026490968095879 +:1008C0000847D0202349096809580847D4202149FE +:1008D000096809580847D8201E4909680958084777 +:1008E000DC201C49096809580847E02019490968B3 +:1008F00009580847E4201749096809580847E820BB +:100900001449096809580847EC2012490968095830 +:100910000847F0200F49096809580847F4200D4995 +:10092000096809580847F8200A490968095808471A +:10093000FC2008490968095808475FF48070054998 +:10094000096809580847000003480449024A034B54 +:100950007047000000000020000B0000000B0000AA +:1009600040EA010310B59B070FD1042A0DD310C82C +:1009700008C9121F9C42F8D020BA19BA884201D97E +:10098000012010BD4FF0FF3010BD1AB1D30703D0C6 +:10099000521C07E0002010BD10F8013B11F8014B7C +:1009A0001B1B07D110F8013B11F8014B1B1B01D198 +:1009B000921EF1D1184610BD02F0FF0343EA032254 +:1009C00042EA024200F005B87047704770474FF0A6 +:1009D00000020429C0F0128010F0030C00F01B800C +:1009E000CCF1040CBCF1020F18BF00F8012BA8BF1A +:1009F00020F8022BA1EB0C0100F00DB85FEAC17CDE +:100A000024BF00F8012B00F8012B48BF00F8012B90 +:100A100070474FF0000200B51346944696462039C1 +:100A200022BFA0E80C50A0E80C50B1F12001BFF4A7 +:100A3000F7AF090728BFA0E80C5048BF0CC05DF80D +:100A400004EB890028BF40F8042B08BF704748BF5B +:100A500020F8022B11F0804F18BF00F8012B7047CF +:100A6000014B1B68DB6818470000002009480A4951 +:100A70007047FFF7FBFFFFF745FB00BD20BFFDE719 +:100A8000064B1847064A1060016881F308884068E1 +:100A900000470000000B0000000B000017040000DE +:100AA000000000201EF0040F0CBFEFF30881EFF3ED +:100AB0000981886902380078182803D100E0000015 +:100AC000074A1047074A12682C3212681047000084 +:100AD00000B5054B1B68054A9B58984700BD0000B0 +:100AE0007703000000000020F00A0000040000006E +:100AF000001000000000000000FFFFFF0090D00386 +:10100000C8130020395E020085C100009F5D020008 +:1010100085C1000085C1000085C1000000000000FE +:10102000000000000000000000000000C55E02009B +:1010300085C100000000000085C1000085C10000DE +:101040002D5F0200335F020085C1000085C10000F2 +:1010500085C1000085C1000085C1000085C1000078 +:10106000395F020085C1000085C100003F5F0200BA +:1010700085C10000455F02004B5F0200515F020026 +:1010800085C1000085C1000085C1000085C1000048 +:1010900085C1000085C1000085C1000085C1000038 +:1010A00085C10000575F020085C1000085C10000B6 +:1010B00085C1000085C1000085C1000085C1000018 +:1010C0005D5F020085C1000085C1000085C1000090 +:1010D00085C1000085C1000085C1000085C10000F8 +:1010E00085C1000085C1000085C1000085C10000E8 +:1010F00085C1000085C1000085C1000085C10000D8 +:1011000085C1000085C1000000F002F824F083FED4 +:101110000AA090E8000C82448344AAF10107DA4552 +:1011200001D124F078FEAFF2090EBAE80F0013F0F7 +:10113000010F18BFFB1A43F00103184718530200B0 +:10114000385302000A444FF0000C10F8013B13F032 +:10115000070408BF10F8014B1D1108BF10F8015B10 +:10116000641E05D010F8016B641E01F8016BF9D103 +:1011700013F0080F1EBF10F8014BAD1C0C1B09D15A +:101180006D1E58BF01F801CBFAD505E014F8016BCC +:1011900001F8016B6D1EF9D59142D6D3704700005E +:1011A0000023002400250026103A28BF78C1FBD870 +:1011B000520728BF30C148BF0B6070471FB500F011 +:1011C00003F88DE80F001FBD24F022BE70B51A4C45 +:1011D00005460A202070A01C00F0D5F85920A080F8 +:1011E00029462046BDE8704008F082B908F08BB966 +:1011F00070B50C461149097829B1A0F160015E294A +:1012000008D3012013E0602804D0692802D043F2FB +:1012100001000CE020CC0A4E94E80E0006EB8000A2 +:10122000A0F58050241FD0F8806E2846B04720607B +:1012300070BD012070470000080000201C00002045 +:10124000A05F02003249884201D20120704700208D +:10125000704770B50446A0F500002E4EB0F1786FCF +:1012600002D23444A4F500042948844201D2012565 +:1012700000E0002500F043F848B125B9B44204D39A +:101280002548006808E0012070BD002070BD002DD9 +:10129000F9D1B442F9D321488442F6D2F3E710B52C +:1012A0000446A0F50000B0F1786F03D21948044459 +:1012B000A4F5000400F023F84FF0804130B1164847 +:1012C000006804E08C4204D2012003E01348844209 +:1012D000F8D2002080F0010010BD10B520B1FFF75A +:1012E000DEFF08B1012010BD002010BD10B520B1F7 +:1012F000FFF7AFFF08B1012010BD002010BD084866 +:1013000008490068884201D10120704700207047D9 +:1013100000700200000000202000002008000020D3 +:101320005C000020BEBAFECA10B5044600210120B0 +:1013300000F042F800210B2000F03EF800210820C8 +:1013400000F03AF80421192000F036F804210D20AD +:1013500000F032F804210E2000F02EF804210F20B6 +:1013600000F02AF80421C84300F026F806211620D0 +:1013700000F022F80621152000F01EF82046FFF7A5 +:1013800025FF002010BD40F2231101807047FFF7B8 +:101390002DBF1148704710487047104A10B51468A7 +:1013A0000E4B0F4A08331A60FFF722FF0B48001D4F +:1013B000046010BD704770474907090E002804DB20 +:1013C00000F1E02080F80014704700F00F0000F1F9 +:1013D000E02080F8141D704703F900421005024018 +:1013E00001000001FD48002101604160018170475A +:1013F0002DE9FF4F93B09B46209F160004460DD069 +:101400001046FFF726FF18B1102017B0BDE8F08F87 +:101410003146012001F0D3FE0028F6D101258DF8D8 +:1014200042504FF4C050ADF84000002210A92846A9 +:1014300006F0C5FC0028E8D18DF84250A8464FF4CC +:1014400028500025ADF840001C2229466846079523 +:101450000DF01DF89DF81C000DF11C0A20F00F0086 +:10146000401C20F0F00010308DF81C0020788DF822 +:101470001D0061789DF81E000DF1400961F34200E6 +:1014800040F001008DF81E009DF8000008AA40F011 +:1014900002008DF800002089ADF83000ADF8325020 +:1014A0006089ADF83400CDF82CA060680E900AA9D0 +:1014B000CDF82890684606F090FA0028A5D160681B +:1014C000FFF70BFF40B16068FFF710FF20B96078AD +:1014D00000F00300022801D0012000E00020BF4CF2 +:1014E00008AA0AA92072BDF8200020808DF8428049 +:1014F00042F60120ADF840009DF81E0020F00600E5 +:10150000801C20F001008DF81E000220ADF8300094 +:10151000ADF8340014A80E90684606F05EFA002874 +:1015200089D1BDF82000608036B1211D304600F021 +:101530005FF90028C2D109E0BBF1000F05D00CF023 +:1015400021FDE8BB0CF01EFDD0BBA58017B1012F1B +:1015500043D04AE08DF8428042F6A620ADF8400024 +:1015600046461C220021684607950CF090FF9DF826 +:101570001C00ADF8346020F00F00401C20F0F0009B +:1015800010308DF81C009DF81D0020F0FF008DF834 +:101590001D009DF81E0020F0060040F00100801C98 +:1015A0008DF81E009DF800008DF8446040F00200A8 +:1015B0008DF80000CDE90A9AADF8306011A800E07E +:1015C00011E00E9008AA0AA9684606F006FA00285B +:1015D000A6D1BDF82000E08008E00CF0D3FC10B9E3 +:1015E0000CF0D0FC08B103200FE7E58000200CE7E9 +:1015F0003EB50446794D0820ADF80000A88828B112 +:101600002046FFF726FE18B110203EBD06203EBD45 +:101610002146012001F0D3FD0028F8D12088ADF843 +:1016200004006088ADF80600A088ADF80800E088E6 +:10163000ADF80A00A88801AB6A46002106F0AAFDB1 +:10164000BDF800100829E2D003203EBD7FB5634DF0 +:101650000446A88868B1002002900820ADF8080070 +:10166000CDF80CD02046FFF7F4FD20B1102004B0D7 +:1016700070BD0620FBE7A98802AA4FF6FF7006F0AE +:10168000CCFF0028F3D1BDF80810082901D00320B1 +:10169000EDE7BDF800102180BDF802106180BDF8B3 +:1016A0000410A180BDF80610E180E0E701B582B02A +:1016B0000220ADF80000494802AB6A46408800218C +:1016C00006F068FDBDF80010022900D003200EBD11 +:1016D0001CB5002100910221ADF800100190FFF728 +:1016E000DEFD08B110201CBD3C486A4641884FF61B +:1016F000FF7006F092FFBDF800100229F3D003201E +:101700001CBDFEB5354C06461546207A0F46C0076F +:1017100005D00846FFF79DFD18B11020FEBD0F2033 +:10172000FEBDF82D01D90C20FEBD3046FFF791FD1E +:1017300018BB208801A905F03AFE0028F4D13078C2 +:101740008DF80500208801A906F003FD0028EBD1E3 +:1017500000909DF800009DF8051040F002008DF803 +:101760000000090703D040F008008DF80000208831 +:10177000694606F08BFC0028D6D1ADF808502088C9 +:101780003B4602AA002106F005FDBDF80810A9425B +:10179000CAD00320FEBD7CB5054600200090019014 +:1017A0000888ADF800000C4628460195FFF795FD26 +:1017B00018B92046FFF773FD08B110207CBD15B1A4 +:1017C000BDF8000060B105486A4601884FF6FF7019 +:1017D00006F023FFBDF8001021807CBD240200200C +:1017E0000C20FAE72F48C088002800D0012070475D +:1017F00030B5044693B000200D46014600901422F7 +:1018000001A80CF044FE1C22002108A80CF03FFEA9 +:101810009DF80000CDF808D020F00F00401C20F00B +:10182000F00010308DF800009DF8010006AA20F0AD +:10183000FF008DF801009DF8200001A940F0020092 +:101840008DF8200001208DF8460042F60420ADF806 +:10185000440011A801902088ADF83C006088ADF8E4 +:101860003E00A088ADF84000E088ADF842009DF849 +:10187000020020F00600801C20F001008DF802001C +:101880000820ADF80C00ADF810000FA8059008A8CE +:1018900006F0A3F8002803D1BDF818002880002026 +:1018A00013B030BD24020020F0B5007B059F1E461A +:1018B00014460D46012800D0FFDF0C2030803A206E +:1018C0003880002C08D0287A032806D0287B0128ED +:1018D00000D0FFDF17206081F0BDA889FBE72DE96C +:1018E000F0470D4686B095F80C900E991446B9F164 +:1018F000010F0BD01022007B2E8A9046052807D0BE +:10190000062839D0FFDF06B0BDE8F0870222F2E7F3 +:10191000E8890C2200EB400002EB400018803320E5 +:101920000880002CEFD0E8896081002720E0009635 +:10193000688808F1020301AA696900F097FF06EBC5 +:101940000800801C07EB470186B204EB4102BDF89A +:1019500004009081F848007808B1012300E00023DA +:101960000DF1060140460E3214F029F87F1CBFB27B +:101970006089B842DBD8C6E734200880E889B9F12D +:10198000010F11D0122148430E301880002CBAD01C +:10199000E88960814846B9F1010F00D00220207328 +:1019A00000270DF1040A1FE00621ECE70096688885 +:1019B00008F1020301AA696900F058FF06EB08006C +:1019C000801C86B2B9F1010F12D007EBC70004EBFF +:1019D0004000BDF80410C18110220AF1020110304C +:1019E0000CF02BFD7F1CBFB26089B842DED88AE7BD +:1019F00007EB470104EB4102BDF80400D0810AF176 +:101A000002014046103213F0FCFFEBE72DE9F047EE +:101A10000E4688B090F80CC096F80C80378AF5898D +:101A20000C20DFF81493109902F10C04BCF1030FA1 +:101A300008D0BCF1040F3DD0BCF1070F75D0FFDF1B +:101A400008B061E705EB850C00EB4C0018803120F5 +:101A50000880002AF4D0A8F1060000F0FF0A5581A2 +:101A600024E01622002101A80CF011FD00977088D7 +:101A7000434601AA716900F0F9FEBDF80400208018 +:101A8000BDF80600E080BDF80800208199F800004C +:101A900008B1012300E00023A21C0DF10A01504609 +:101AA00013F08DFF07EB080087B20A346D1EADB24C +:101AB000D7D2C5E705EB850C00EB4C00188032202F +:101AC0000880002ABCD0A8F1050000F0FF0A55816B +:101AD00037E000977088434601AA716900F0C6FE9E +:101AE0009DF80600BDF80410E1802179420860F3FA +:101AF000000162F34101820862F38201C20862F3CD +:101B0000C301020962F30411420962F3451182091B +:101B100062F386112171C0096071BDF80700208150 +:101B200099F8000010B1012301E00EE000232246E5 +:101B30000DF10901504613F042FF07EB080087B290 +:101B40000A346D1EADB2C4D27AE7A8F1020084B2A5 +:101B500005FB08FC0CF10E00188035200880002AD7 +:101B6000A7D05581948100971FFA8CF370880E32AC +:101B7000716900F07BFE63E72DE9F84F1E460A9D70 +:101B80000C4681462AB1607A00F58070D080E089E9 +:101B9000108199F80C000C274FF000084FF00E0A46 +:101BA0000D2872D2DFE800F09D070E1B272F374566 +:101BB000546972727200214648460095FFF774FE20 +:101BC000BDE8F88F207B9146082802D0032800D07A +:101BD000FFDF3780302009E0A9F80A80F0E7207B9A +:101BE0009146042800D0FFDF378031202880B9F1EA +:101BF000000FF1D1E4E7207B9146042800D0FFDFFD +:101C000037803220F2E7207B9146022800D0FFDFA8 +:101C100037803320EAE7207B1746022800D0FFDF19 +:101C20003420A6F800A02880002FC9D0A7F80A8089 +:101C3000C6E7207B1746042800D0FFDF3520A6F832 +:101C400000A02880002FBBD04046A7F80A8012E0F1 +:101C5000207B1746052802D0062800D0FFDF102081 +:101C6000308036202880002FAAD0E0897881A7F81C +:101C70000E80B9F80E00B881A2E7207B91460728B4 +:101C800000D0FFDF37803720B0E72AE04FF01200A6 +:101C900018804FF038001700288091D0E0897881B3 +:101CA000A7F80E80A7F8108099F80C000A2805D034 +:101CB0000B2809D00C280DD0FFDF81E7207B0A28F4 +:101CC00000D0FFDF01200AE0207B0B2800D0FFDFDF +:101CD000042004E0207B0C2800D0FFDF05203873AF +:101CE0006EE7FFDF6CE770B50C46054601F0AAFB16 +:101CF00020B10078222804D2082070BD43F20200EF +:101D000070BD0521284612F0D1F8206008B10020EE +:101D100070BD032070BD30B44880087820F00F00FB +:101D2000C01C20F0F000903001F8080B1DCA81E8BB +:101D30001D0030BC07F05DBC100000202DE9FF47FE +:101D400084B0002782460297079890468946123051 +:101D50000AF069FA401D20F00306079828B907A980 +:101D60005046FFF7C0FF002854D1B9F1000F05D04D +:101D70000798017B19BB052504681BE098F8000053 +:101D8000092803D00D2812D0FFDF46E0079903256C +:101D90004868B0B3497B42887143914239D98AB2CD +:101DA000B3B2011D11F0F5FE0446078002E0079C66 +:101DB000042508340CB1208810B1032D29D02CE063 +:101DC0000798012112300AF060FAADF80C000246C3 +:101DD00002AB2946504608F0B8FA070001D1A01C12 +:101DE000029007983A461230C8F80400A8F802A0FA +:101DF00003A94046029B0AF055FAD8B10A2817D227 +:101E000000E006E0DFE800F007091414100B0D14E1 +:101E10001412132014E6002012E6112010E6082008 +:101E20000EE643F203000BE6072009E60D2007E665 +:101E3000032005E6BDF80C002346CDE900702A46D4 +:101E40005046079900F022FD57B9032D08D1079895 +:101E5000B3B2417B406871438AB2011D11F0ADFEFF +:101E6000B9F1000FD7D0079981F80C90D3E72DE98D +:101E7000FE4F91461A881C468A468046FAB102AB4C +:101E8000494608F062FA050019D04046A61C27888A +:101E900012F04FF93246072629463B46009611F0CC +:101EA0005EFD20882346CDE900504A465146404613 +:101EB00000F0ECFC002020800120BDE8FE8F002017 +:101EC000FBE710B586B01C46AAB104238DF800309C +:101ED0001388ADF808305288ADF80A208A788DF85A +:101EE0000E200988ADF80C1000236A462146FFF742 +:101EF00025FF06B010BD1020FBE770B50D4605218B +:101F000011F0D4FF040000D1FFDF294604F11200D4 +:101F1000BDE870400AF0A2B92DE9F8430D468046AD +:101F2000002607F063FB04462878102878D2DFE803 +:101F300000F0773B345331311231313108313131D6 +:101F400031312879001FC0B2022801D0102810D1E9 +:101F500014BBFFDF35E004B9FFDF0521404611F077 +:101F6000A5FF007B032806D004280BD0072828D023 +:101F7000FFDF072655E02879801FC0B2022820D055 +:101F800050B1F6E72879401FC0B2022819D01028B6 +:101F900017D0EEE704B9FFDF13E004B9FFDF2879BB +:101FA00001280ED1172137E00521404611F07EFFB0 +:101FB000070000D1FFDF07F1120140460AF02BF9BC +:101FC0002CB12A4621464046FFF7A5FE29E0132101 +:101FD000404602F01FFD24E004B9FFDF0521404622 +:101FE00011F064FF060000D1FFDF694606F1120020 +:101FF0000AF01BF9060000D0FFDFA988172901D2DB +:10200000172200E00A46BDF80000824202D90146CC +:1020100002E005E01729C5D3404600F047FCD0E7B1 +:10202000FFDF3046BDE8F883401D20F0030219B100 +:1020300002FB01F0001D00E000201044704713B5C2 +:10204000009858B10024684611F04DFD002C04D1D1 +:10205000F749009A4A6000220A701CBD0124002042 +:10206000F2E72DE9F0470C461546242200212046D0 +:102070000CF00DFA05B9FFDFA87860732888DFF847 +:10208000B0A3401D20F00301AF788946DAF80400C0 +:1020900011F047FD060000D1FFDF4FF00008266079 +:1020A000A6F8008077B109FB07F1091D0AD0DAF81C +:1020B000040011F036FD060000D1FFDF6660C6F8AF +:1020C000008001E0C4F80480298804F11200BDE812 +:1020D000F0470AF091B82DE9F047804601F112006F +:1020E0000D4681460AF09FF8401DD14F20F00302B3 +:1020F0006E7B14462968786811F03EFD3EB104FB02 +:1021000006F2121D03D06968786811F035FD0520CC +:1021100011F074FE0446052011F078FE201A012803 +:1021200002D1786811F0F2FC49464046BDE8F0471C +:102130000AF078B870B50546052111F0B7FE040025 +:1021400000D1FFDF04F112012846BDE870400AF01B +:1021500062B82DE9F04F91B04FF0000BADF828B008 +:10216000ADF804B047880C4605469246052138462E +:1021700011F09CFE060000D1FFDF24B1A780A4F877 +:1021800006B0A4F808B0297809220B20B2EB111F81 +:1021900073D12A7A04F1100138274FF00C084FF060 +:1021A00012090291102A69D2DFE802F068F2F1F018 +:1021B0008008D3898EA03DDCF3EEB7B7307B0228D0 +:1021C00000D0FFDFA88908EBC001ADF80410302172 +:1021D000ADF82810002C25D06081B5F80E800027BE +:1021E0001DE004EBC709317C89F80E10F189A9F8CC +:1021F0000C10CDF800806888042305AA296900F036 +:1022000035FBBDF81410A9F8101008F10400BDF852 +:1022100016107F1C1FFA80F8A9F81210BFB260894F +:10222000B842DED80CE1307B022800D0FFDFE9891C +:1022300008EBC100ADF804003020ADF8280095F897 +:102240000C90002CA9F10400C0B20F90EAD061817B +:10225000B5F81080002725E0CDF8008068884B464F +:1022600003AA696900F002FB08EB09001FFA80F875 +:102270006F48007818B1012302E0DDE0DAE00023C6 +:1022800004EBC702009204A90C320F9813F097FBDD +:10229000009ABDF80C007F1C1082009ABDF80E0059 +:1022A000BFB250826089B842D6D8C9E00AA800906F +:1022B00001AB224629463046FFF711FBC0E0307BD8 +:1022C000082805D0FFDF03E0307B082800D0FFDFBF +:1022D000E8891030ADF804003620ADF82800002C55 +:1022E0003FD0A9896181F189A18127E0307B09284C +:1022F00000D0FFDFA88901460C30ADF8040037207C +:10230000ADF82800002C2CD06181E8890090AB89C1 +:10231000688804F10C02296955E0E88939211030F8 +:1023200080B2ADF80400ADF82810002C72D0A98955 +:102330006181287A0E280AD002212173E989E1817E +:10234000288A0090EB8968886969029A3BE001213C +:10235000F3E70AA8009001AB224629463046FFF772 +:1023600055FB6DE0307B0A2800D0FFDFADF804900C +:10237000ADF828704CB3A9896181A4F810B0A4F815 +:102380000EB0012020735BE020E002E030E038E096 +:1023900041E0307B0B2800D0FFDF288AADF82870A1 +:1023A0001230ADF8040084B104212173A989618140 +:1023B000E989E181298A2182688A00902B8A6888CC +:1023C00004F11202696900F051FA39E0307B0C28FF +:1023D00000D0FFDFADF80490ADF828703CB30521C4 +:1023E0002173A4F80AB0A4F80EB0A4F810B027E046 +:1023F0000AA8009001AB224629463046FFF754FA5E +:102400001EE00AA8009001AB224629463046FFF79D +:10241000B3FB15E034E03B21ADF80400ADF8281023 +:1024200074B30120E080A4F808B084F80AB007E093 +:1024300010000020FFDF03E0297A012917D0FFDF19 +:10244000BDF80400AAF800006CB1BDF82800208097 +:10245000BDF804006080BDF82800392803D03C286E +:1024600001D086F80CB011B00020BDE8F08F3C21FF +:10247000ADF80400ADF8281014B1697AA172DFE755 +:10248000AAF80000EFE72DE9F84356880F4680468A +:1024900015460521304611F009FD040000D1FFDF8B +:1024A000123400943B46414630466A680AF02EF8E2 +:1024B000B8E570B50D46052111F0F8FC040000D117 +:1024C000FFDF294604F11200BDE8704009F0B8BEF4 +:1024D00070B50D46052111F0E9FC040000D1FFDFC5 +:1024E000294604F11200BDE8704009F0D6BE70B56F +:1024F0000546052111F0DAFC040000D1FFDF04F1EC +:10250000080321462846BDE870400422AFE470B5B8 +:102510000546052111F0CAFC040000D1FFDF214669 +:1025200028462368BDE870400522A0E470B5064641 +:10253000052111F0BBFC040000D1FFDF04F1120003 +:1025400009F071FE401D20F0030511E0011D008817 +:102550000322431821463046FFF789FC00280BD0A0 +:10256000607BABB2684382B26068011D11F05BFB17 +:10257000606841880029E9D170BD70B50E460546F6 +:1025800007F034F8040000D1FFDF012020726672EA +:102590006580207820F00F00C01C20F0F000303063 +:1025A0002070BDE8704007F024B8602801D00720F3 +:1025B00070470878C54900F0010008700020704796 +:1025C0002DE9F0438BB00D461446814606A9FFF76E +:1025D0008AFB002814D14FF6FF7601274FF42058CC +:1025E0008CB103208DF800001020ADF8100007A872 +:1025F000059007AA204604A913F005FA78B1072030 +:102600000BB0BDE8F0830820ADF808508DF80E70CF +:102610008DF80000ADF80A60ADF80C800CE006986B +:10262000A17801742188C1818DF80E70ADF8085031 +:10263000ADF80C80ADF80A606A4602214846069B58 +:10264000FFF77CFBDCE708B501228DF8022042F69B +:102650000202ADF800200A4603236946FFF731FC69 +:1026600008BD08B501228DF8022042F60302ADF83C +:1026700000200A4604236946FFF723FC08BD00B585 +:1026800087B079B102228DF800200A88ADF80820C1 +:102690004988ADF80A1000236A460521FFF74EFB72 +:1026A00007B000BD1020FBE709B1072309E40720AC +:1026B000704770B588B00D461446064606A9FFF768 +:1026C00012FB00280ED17CB10620ADF808508DF821 +:1026D0000000ADF80A40069B6A460821DC813046BE +:1026E000FFF72CFB08B070BD05208DF80000ADF899 +:1026F0000850F0E700B587B059B107238DF80030D6 +:10270000ADF80820039100236A460921FFF716FB64 +:10271000C6E71020C4E770B588B00C460646002511 +:1027200006A9FFF7E0FA0028DCD106980121123053 +:1027300009F0ABFD9CB12178062921D2DFE801F038 +:10274000200505160318801E80B2C01EE28880B2E4 +:102750000AB1A3681BB1824203D90C20C2E7102042 +:10276000C0E7042904D0A08850B901E00620B9E7E9 +:10277000012913D0022905D004291CD005292AD00B +:102780000720AFE709208DF800006088ADF8080049 +:10279000E088ADF80A00A068039023E00A208DF8D5 +:1027A00000006088ADF80800E088ADF80A00A06875 +:1027B0000A25039016E00B208DF800006088ADF824 +:1027C0000800A088ADF80A00E088ADF80C00A06809 +:1027D0000B25049006E00C208DF8000060788DF841 +:1027E00008000C256A4629463046069BFFF7A6FAE4 +:1027F00078E700B587B00D228DF80020ADF80810FD +:1028000000236A461946FFF799FA49E700B587B0F1 +:1028100071B102228DF800200A88ADF8082049889D +:10282000ADF80A1000236A460621FFF787FA37E75A +:10283000102035E770B586B0064601200D46ADF88C +:1028400008108DF80000014600236A463046FFF765 +:1028500075FA040008D12946304605F0B5FC002180 +:10286000304605F0CFFC204606B070BDF8B51C46DA +:1028700015460E46069F11F04AFC2346FF1DBCB2CA +:1028800031462A46009411F036F8F8BD30B41146AE +:10289000DDE902423CB1032903D0002330BC08F03B +:1028A00032BE0123FAE71A8030BC704770B50C467F +:1028B0000546FFF722FB2146284605F094FC2846F2 +:1028C000BDE87040012105F09DBC00001000002013 +:1028D0004FF0E0224FF400400021C2F88001BFF326 +:1028E0004F8FBFF36F8F1748016001601649900248 +:1028F00008607047134900B500220A600A60124B55 +:102900004FF060721A60002808BF00BD0F4A104BDC +:10291000DFF840C001280CD002281CBFFFDF00BD3B +:10292000032008601A604FF4000000BFCCF80000DC +:1029300000BD022008601A604FF04070F6E700B555 +:10294000FFDF00BD00F5004008F50140A4020020B3 +:1029500014F5004004F5014070B50B2000F0BDF9FE +:10296000082000F0BAF900210B2000F0D4F9002172 +:10297000082000F0D0F9F44C01256560A560002026 +:10298000C4F84001C4F84401C4F848010B2000F029 +:10299000B5F9082000F0B2F90B2000F091F925609C +:1029A00070BD10B50B2000F098F9082000F095F9E3 +:1029B000E548012141608160E4490A68002AFCD1B0 +:1029C0000021C0F84011C0F84411C0F848110B2094 +:1029D00000F094F9BDE81040082000F08FB910B560 +:1029E0000B2000F08BF9BDE81040082000F086B9FC +:1029F00000B530B1012806D0022806D0FFDF002044 +:102A000000BDD34800BDD34800BDD248001D00BD65 +:102A100070B5D1494FF000400860D04DC00BC5F8EB +:102A20000803CF4800240460C5F840410820C4359D +:102A300000F053F9C5F83C41CA48047070BD08B5B0 +:102A4000C14A002128B1012811D002281CD0FFDF83 +:102A500008BD4FF48030C2F80803C2F84803BB48F1 +:102A60003C300160C2F84011BDE80840D0E74FF4A7 +:102A70000030C2F80803C2F84803B448403001608F +:102A8000C2F84411B3480CE04FF48020C2F80803A8 +:102A9000C2F84803AD4844300160C2F84811AD485F +:102AA000001D0068009008BD70B516460D4604462E +:102AB000022800D9FFDF0022A348012304F11001FE +:102AC0008B4000EB8401C1F8405526B1C1F840218C +:102AD000C0F8043303E0C0F80833C1F84021C0F85F +:102AE000443370BD2DE9F0411D46144630B1012834 +:102AF00033D0022838D0FFDFBDE8F081891E0022E4 +:102B000021F07F411046FFF7CFFF012D23D0002099 +:102B1000944D924F012668703E61914900203C39E6 +:102B200008600220091D08608D49042030390860C2 +:102B30008B483D34046008206C6000F0DFF83004FE +:102B4000C7F80403082000F0BBF88349F007091F09 +:102B500008602E70D0E70120DAE7012B02D00022B6 +:102B6000012005E00122FBE7012B04D00022022016 +:102B7000BDE8F04198E70122F9E774480068704722 +:102B800070B500F0D8F8704C0546D4F84001002626 +:102B9000012809D1D4F80803C00305D54FF48030CB +:102BA000C4F80803C4F84061D4F8440101280CD1EA +:102BB000D4F80803800308D54FF40030C4F80803A4 +:102BC000C4F84461012013F0EEFED4F84801012856 +:102BD0000CD1D4F80803400308D54FF48020C4F882 +:102BE0000803C4F84861022013F0DDFE5E4805606A +:102BF00070BD70B500F09FF85A4D0446287850B16A +:102C0000FFF706FF687818B10020687013F0CBFE5C +:102C10005548046070BD0320F8E74FF0E0214FF401 +:102C20000010C1F800027047152000F067B84B494A +:102C300001200861082000F061B848494FF47C1079 +:102C4000C1F808030020024601EB8003C3F84025C9 +:102C5000C3F84021401CC0B20628F5D37047410A92 +:102C600043F609525143C0F3080010FB02F000F58F +:102C7000807001EB5020704710B5430B48F2376469 +:102C800063431B0C5C020C60384C03FB0400384BA4 +:102C90004CF2F72443435B0D13FB04F404EB402098 +:102CA00000F580704012107008681844086010BD6C +:102CB0002C484068704729490120C1F8000270473C +:102CC000002809DB00F01F0201219140400980002B +:102CD00000F1E020C0F80011704700280DDB00F083 +:102CE0001F02012191404009800000F1E020C0F85E +:102CF0008011BFF34F8FBFF36F8F7047002809DB40 +:102D000000F01F02012191404009800000F1E02005 +:102D1000C0F8801270474907090E002804DB00F153 +:102D2000E02080F80014704700F00F0000F1E02070 +:102D300080F8141D70470C48001F00680A4A0D49AE +:102D4000121D11607047000000B0004004B5004043 +:102D50004081004044B1004008F50140008000403F +:102D6000408500403C00002014050240F7C2FFFFF0 +:102D70006F0C0100010000010A4810B50468094900 +:102D800009480831086013F0A2FE0648001D0460DF +:102D900010BD0649002008604FF0E0210220C1F874 +:102DA000800270471005024001000001FC1F004036 +:102DB000374901200860704770B50D2000F049F8D0 +:102DC000344C0020C4F800010125C4F804530D2040 +:102DD00000F050F825604FF0E0216014C1F80001C8 +:102DE00070BD10B50D2000F034F82A480121416073 +:102DF0000021C0F80011BDE810400D2000F03AB8E5 +:102E0000254810B504682449244808310860214940 +:102E1000D1F80001012804D0FFDF1F48001D046025 +:102E200010BD1B48001D00680022C0B2C1F800217F +:102E300014F07FFBF1E710B5164800BFD0F8001181 +:102E40000029FBD0FFF7DCFFBDE810400D2000F0AB +:102E500011B800280DDB00F01F020121914040094C +:102E6000800000F1E020C0F88011BFF34F8FBFF366 +:102E70006F8F7047002809DB00F01F02012191408D +:102E80004009800000F1E020C0F880127047000087 +:102E900004D5004000D000401005024001000001B0 +:102EA0004FF0E0214FF00070C1F8800101F5C071D2 +:102EB000BFF34F8FBFF36F8FC1F80001394B8022F2 +:102EC00083F8002441F8800C704700B502460420C6 +:102ED000354903E001EBC0031B792BB1401EC0B2A2 +:102EE000F8D2FFDFFF2000BD41F8302001EBC00128 +:102EF00000224A718A7101220A7100BD2A4A00210A +:102F000002EBC0000171704710B50446042800D3DD +:102F1000FFDF254800EBC4042079012800D0FFDF43 +:102F20006079A179401CC0B2814200D060714FF03D +:102F3000E0214FF00070C1F8000210BD70B504250B +:102F4000194E1A4C16E0217806EBC1000279012ACD +:102F500008D1427983799A4204D04279827156F835 +:102F6000310080472078401CC0B22070042801D373 +:102F7000002020706D1EEDB2E5D270BD0C4810B57A +:102F800004680B490B4808310860064890F80004B3 +:102F90004009042800D0FFDFFFF7D0FF0448001DE0 +:102FA000046010BD19E000E0E0050020580000209A +:102FB00010050240010000010548064A01689142DF +:102FC00001D1002101600449012008607047000020 +:102FD0005C000020BEBAFECA40E5014070B50C4658 +:102FE000054609F02FFC21462846BDE870400AF04E +:102FF00010BD7047704770470021016081807047A5 +:103000002CFFFFFFDBE5B151007002002301FFFF41 +:103010008C00000078DB6A007A2E9AC67DB66CFAC6 +:10302000F35721CCC310D5E51471FB3C30B5FC4DF2 +:103030000446062CA9780ED2DFE804F0030E0E0E2B +:103040000509FFDF08E0022906D0FFDF04E00329BD +:1030500002D0FFDF00E0FFDFAC7030BD30B50446CA +:103060001038EF4D07280CD2DFE800F0040C060CF6 +:103070000C0C0C00FFDF05E0287E112802D0FFDFDA +:1030800000E0FFDF2C7630BD2DE9F04112F026FE86 +:10309000044614F063F8201AC5B2062010F0AEFE04 +:1030A0000446062010F0B2FE211ADD4C207E1228C4 +:1030B00018D000200F18072010F0A0FE06460720A9 +:1030C00010F0A4FE301A3918207E13280CD00020EE +:1030D0000144A078042809D000200844281AC0B26E +:1030E000BDE8F0810120E5E70120F1E70120F4E7E8 +:1030F000CB4810B590F825004108C94800F12600DA +:1031000005D00EF0F5FEBDE8104006F08CB80EF0CC +:10311000D0FEF8E730B50446A1F120000D460A289C +:103120004AD2DFE800F005070C1C2328353A3F445B +:10313000FFDF42E0207820283FD1FFDF3DE0B848A4 +:103140008178052939D0007E122836D020782428AD +:1031500033D0252831D023282FD0FFDF2DE0207851 +:1031600022282AD0232828D8FFDF26E0207822280A +:1031700023D0FFDF21E0207822281ED024281CD075 +:1031800026281AD0272818D0292816D0FFDF14E0C7 +:103190002078252811D0FFDF0FE0207825280CD0DB +:1031A000FFDF0AE02078252807D0FFDF05E0207840 +:1031B000282802D0FFDF00E0FFDF257030BD1FB5FB +:1031C00004466A46002001F0A5FEB4B1BDF8022015 +:1031D0004FF6FF700621824201D1ADF80210BDF812 +:1031E0000420824201D1ADF80410BDF808108142DC +:1031F00003D14FF44860ADF8080068460FF0E2FADA +:1032000006F011F804B010BD70B516460C46054620 +:10321000FEF71FF848B90CB1B44208D90C2070BDB4 +:1032200055F82400FEF715F808B1102070BD2046AF +:10323000641EE4B2F4D270BD2DE9F04105461F468C +:1032400090460E4600240068FEF750F830B9A98871 +:1032500028680844401EFEF749F808B110203FE7EF +:1032600028680028A88802D0B84202D850E0002878 +:10327000F5D0092034E72968085DB8B1671CCA5D3C +:10328000152A2ED03CDC152A3AD2DFE802F039129A +:10329000222228282A2A313139393939393939391C +:1032A00039392200085D30BB641CA4B2A242F9D8AF +:1032B00033E00228DDD1A01C085C88F80000072854 +:1032C00001D2400701D40A200AE7307840F001001B +:1032D00015E0C143C90707E0012807D010E0062028 +:1032E000FEE60107A1F180510029F5D01846F7E666 +:1032F0003078810701D50B20F2E640F002003070F3 +:103300002868005D384484B2A888A04202D2B0E7A1 +:103310004FF4485382B2A242ADD80020E0E610B587 +:10332000027843F2022354080122022C12D003DC5B +:103330003CB1012C16D106E0032C10D07F2C11D10A +:1033400012E0002011E080790324B4EB901F09D132 +:103350000A700BE08079B2EB901F03D1F8E7807917 +:103360008009F5D0184610BDFF200870002010BD60 +:1033700008B500208DF80000294890F82E1051B1B2 +:1033800090F82F0002280FD003280FD0FFDF00BFD6 +:103390009DF8000008BD22486946253001F009FE6D +:1033A0000028F5D0FFDFF3E7032000E001208DF8CF +:1033B0000000EDE738B50C460546694601F0F9FD19 +:1033C00000280DD19DF80010207861F3470020708F +:1033D00055F8010FC4F80100A888A4F805000020E2 +:1033E00038BD38B5137888B102280FD0FF281BD01C +:1033F0000CA46D46246800944C7905EB9414247851 +:1034000064F347031370032805D010E023F0FE0394 +:1034100013700228F7D1D8B240F001000AE0000092 +:10342000F00100200302FF0143F0FE00107010784D +:1034300020F0010010700868C2F801008888A2F826 +:10344000050038BD022110F031BD38B50C460978B1 +:10345000222901D2082038BDADF800008DF80220E5 +:1034600068460EF087FD05F0DEFE050003D1212140 +:103470002046FFF74FFE284638BD1CB500208DF8CA +:103480000000CDF80100ADF80500FB4890F82E00D3 +:10349000022801D0012000E000208DF807006846D6 +:1034A0000EF0F0FD002800D0FFDF1CBD00220A80D6 +:1034B000437892B263F3451222F040020A8000780A +:1034C0000C282BD2DFE800F02A06090E1116191C71 +:1034D0001F220C2742F0110009E042F01D00088075 +:1034E0000020704742F0110012E042F0100040F05E +:1034F0000200F4E742F01000F1E742F00100EEE7CD +:1035000042F0010004E042F00200E8E742F002006D +:1035100040F00400E3E742F00400E0E707207047D2 +:103520002DE9FF478AB00025BDF82C6082461C4675 +:1035300091468DF81C50700703D56068FDF789FE31 +:1035400068B9CD4F4FF0010897F82E0058B197F8A1 +:103550002F00022807D16068FDF7C8FE18B11020BF +:103560000EB0BDE8F087300702D5A08980283DD88D +:10357000700705D4B9F1000F02D097F8240098B372 +:10358000E07DC0F300108DF81B00627D0720032151 +:103590005AB3012A2CD0022AE2D0042AE0D18DF8B5 +:1035A0001710F00627D4A27D072022B3012A22D0CB +:1035B000022A23D0042AD3D18DF819108DF8159042 +:1035C000606810B307A9FFF7AAFE0028C8D19DF8CC +:1035D0001C00FF2816D0606850F8011FCDF80F10AE +:1035E0008088ADF8130014E000E001E00720B7E7A1 +:1035F0008DF81780D5E78DF81980DFE702208DF868 +:103600001900DBE743F20220AAE7CDF80F50ADF82E +:103610001350E07B40B9207C30B9607C20B9A07C9D +:1036200010B9E07CC00601D0062099E78DF800A013 +:10363000BDF82C00ADF80200A0680190A0680290CF +:1036400004F10F0001F0A9FC8DF80C00FFF790FECB +:103650008DF80D009DF81C008DF80E008DF81650A9 +:103660008DF81850E07D08A900F00F008DF81A00C1 +:1036700068460FF0E3F905F0D6FD71E7F0B58FB0BD +:1036800000258DF830508DF814508DF834500646D2 +:103690008DF82850019502950395049519B10FC92D +:1036A00001AC84E80F00744CA078052801D00428F0 +:1036B0000CD101986168884200D120B90398E16873 +:1036C000884203D110B108200FB0F0BD207DC006A4 +:1036D00001D51F2700E0FF273B460DAA05A903A837 +:1036E000FFF7AAFD0028EFD1A08AC10702D0C006CB +:1036F00000D4EE273B460AAA0CA901A8FFF79CFDBF +:103700000028E1D19DF81400C00701D00A20DBE7B2 +:10371000A08A410708D4A17D31B19DF828108907FE +:1037200002D043F20120CFE79DF82810C90709D045 +:10373000400707D4208818B144F25061884201D96B +:103740000720C1E78DF818508DF81960BDF8080002 +:10375000ADF81A000198079006A80FF07BF905F064 +:1037600062FD0028B0D18DF820508DF82160BDF8A1 +:103770001000ADF822000398099008A80FF08CF90A +:1037800005F051FD00289FD101AD241D95E80F00E3 +:1037900084E80F00002097E770B586B00D4604005E +:1037A00005D0FDF7A3FD20B1102006B070BD0820A4 +:1037B000FBE72078C107A98802D0FF2902D303E0E4 +:1037C0001F2901D20920F0E7800763D4FFF75CFCD2 +:1037D00038B12178C1F3C100012804D0032802D0F8 +:1037E00005E01320E1E7244890F82400C8B1C80799 +:1037F0004FF001064FF0000502D08DF80F6001E098 +:103800008DF80F50FFF7B4FD8DF800002078694661 +:10381000C0F3C1008DF8010060788DF80250C20835 +:1038200001D00720C1E730B3C20701D08DF8026094 +:10383000820705D59DF8022042F002028DF8022091 +:10384000400705D59DF8020040F004008DF8020005 +:10385000002022780B18C2F38002DA7001EB4002DC +:103860006388D380401CA388C0B253810228F0D360 +:10387000207A78B905E001E0F00100208DF80260BF +:10388000E6E7607A30B9A07A20B9E07A10B9207BF7 +:10389000C00601D0062088E704F1080001F07DFB96 +:1038A0008DF80E0068460EF0F6FC05F0BCFC002812 +:1038B00089D18DF810608DF81150E088ADF81200B4 +:1038C000ADF8145004A80EF039FD05F0ACFC00284A +:1038D00088D12078C00701D0152000E01320FFF721 +:1038E000BDFB002061E72DE9FF470220FD4E8DF86A +:1038F00004000027708EADF80600B84643F20209B6 +:103900004CE001A810F039FA050006D0708EA8B37B +:10391000A6F83280ADF806803EE0039CA07F010748 +:103920002DD504F124000090A28EBDF80800214698 +:1039300004F1360301F0BCFC050005D04D452AD04A +:10394000112D3CD0FFDF3AE0A07F20F00801E07F9E +:10395000420862F3C711A177810861F30000E077A4 +:1039600094F8210000F01F0084F820002078282817 +:1039700026D129212046FFF7CDFB21E014E04007A6 +:103980000AD5BDF8080004F10E0101F01CFB05008A +:103990000DD04D4510D100257F1CFFB2022010F044 +:1039A0002DFA401CB842ACD8052D11D008E0A07FFC +:1039B00020F00400A07703E0112D00D0FFDF0025E8 +:1039C000BDF806007086052D04D0284604B0C8E571 +:1039D000A6F832800020F9E770B50646FFF732FD01 +:1039E000054605F003FE040000D1FFDF6680207865 +:1039F00020F00F00801C20F0F00020302070032009 +:103A0000207295F83E006072BDE8704005F0F1BD8F +:103A10002DE9F04786B0040000D1FFDF2078B14DDA +:103A200020F00F00801C20F0F000703020706068E3 +:103A30000178491F1B2933D2DFE801F0FE32323210 +:103A400055FD320EFDFD42FC32323278FCFCFBFAB1 +:103A500032FCFCF9F8FCFC00C6883046FFF7F2FCAB +:103A60000546304607F045FCE0B16068007A85F80D +:103A70003E0021212846FFF74DFB3046FEF75AFB5A +:103A8000304603F0D7FE3146012014F017F8A87F26 +:103A900020F01000A877FFF726FF002800D0FFDFF6 +:103AA00006B05EE5207820F0F00020302070032082 +:103AB000207266806068007A607205F09AFDD8E72F +:103AC000C5882846FFF7BEFC00B9FFDF60680079B3 +:103AD000012800D0FFDF6068017A06B02846BDE803 +:103AE000F04707F0EBBDC6883046FFF7ABFC05009A +:103AF00000D1FFDF05F07DFD606831460089288137 +:103B000060684089688160688089A881012013F01D +:103B1000D5FF0020A875A87F00F003000228BFD1C0 +:103B2000FFF7E1FE0028BBD0FFDFB9E7007928B13D +:103B30000228B5D03C28B3D0FFDFB1E705F059FD2E +:103B40006668B6F806A0307A361D012806D0687E71 +:103B5000814605F0D4FA070003D101E0E878F7E7E1 +:103B6000FFDF00220221504610F097F9040000D137 +:103B7000FFDF22212046FFF7CDFA3079012800D05F +:103B80000220A17F804668F30101A177308B20815C +:103B9000708B6081B08BA08184F822908DF80880B2 +:103BA000B8680090F86801906A460321504610F00A +:103BB00074F900B9FFDFB888ADF81000B8788DF857 +:103BC000120004AA0521504610F067F900B9FFDF82 +:103BD000B888ADF80C00F8788DF80E0003AA04211F +:103BE000504610F05AF900B9FFDF062106F1120025 +:103BF0000DF00EF940B37079800700D5FFDF7179C1 +:103C0000E07D61F34700E075D6F80600A061708999 +:103C1000A083062106F10C000DF0FAF8F0B195F83A +:103C200025004108607861F3470006E041E039E093 +:103C300071E059E04EE02FE043E06070D5F82600D7 +:103C4000C4F80200688D12E0E07D20F0FE00801CC8 +:103C5000E075D6F81200A061F08AD9E7607820F00C +:103C6000FE00801C6070F068C4F80200308AE080BA +:103C7000B8F1010F04D0B8F1020F05D0FFDF0FE754 +:103C80000320FFF7D3F90BE7287E122800D0FFDFCF +:103C90001120FFF7E3F903E706B02046BDE8F0473F +:103CA00001F092BD05F0A5FC15F8300F40F00200C0 +:103CB00005E005F09EFC15F8300F40F00400287078 +:103CC000EEE6287E13280AD01528D8D15FF016001A +:103CD000FFF7C4F906B0BDE8F04705F08ABC142030 +:103CE000F6E70000F0010020A978052909D0042991 +:103CF000C5D105F07EFC022006B0BDE8F047FFF715 +:103D000095B900790028BAD0E87801F02DF905F0CE +:103D100070FC0320F0E7287E122802D1687E01F0B3 +:103D200023F91120D4E72DE9F05F054600784FF024 +:103D300000080009DFF8B8A891460C46464601285D +:103D40006ED002286DD007280BD00A286AD0FFDF7A +:103D5000A9F8006014B1A4F8008066800020BDE8D6 +:103D6000F09F6968012704F108000B784FF0020BFF +:103D70005B1F4FF6FF721B2B7ED2DFE803F0647DE2 +:103D80007D7D0E7D7D7D7D7D7D217D7D7D2BFDFC81 +:103D9000FBFA7D14D2F9E7F8F700C8884FF0120853 +:103DA000102621469AE14FF01C080A26BCB38888E9 +:103DB000A0806868807920726868C0796072C7E7FF +:103DC0004FF01B08142654B30320207268688088C3 +:103DD000A080BDE70A793C2ABAD00D1D4FF010082B +:103DE0002C26E4B16988A180298B6182298B2182EC +:103DF000698BA182A98BE1826B790246A91D1846C5 +:103E0000FFF7EFFA2979002001290CD084F80FB0D0 +:103E1000FF212176E06120626062A06298E70FE0F6 +:103E20003BE15EE199E1E77320760AF1040090E856 +:103E30000E00DAF81000C4E90930C4E9071287E778 +:103E4000A9F800608AE72C264FF01D08002CF7D057 +:103E5000A28005460F1D897B008861F30000288041 +:103E6000B97A490861F341002880B97A890861F379 +:103E700082002880B97A00E00CE1C90861F3C30030 +:103E80002880B97AAA1C0911491C61F3041000F0BA +:103E90007F0028807878B91CFFF7A3FA387D05F1F8 +:103EA000090207F11501FFF79CFA387B01F0A9F828 +:103EB0002874787B01F0A5F86874F87EA874787A85 +:103EC000E874387F2875B87B6875388AE882DAF834 +:103ED0001C10A961B97A504697F808A0C1F34111A6 +:103EE000012904D0008C504503D2824609E0FFDF4F +:103EF00010E0022903D0288820F0600009E0504536 +:103F000004D1288820F06000403002E0288840F08A +:103F100060002880A4F824A0524607F11D01A8697A +:103F20009BE011264FF02008002C89D0A280686801 +:103F300004F10A02007920726868007B6072696887 +:103F40008B1D48791946FFF74CFA01E70A264FF016 +:103F50002108002CE9D08888A080686880792072C8 +:103F60006868C07960729AF8301006E078E06BE01B +:103F700052E07FE019E003E03AE021F00401A6E01E +:103F80000B264FF02208002CCFD0C888A08068688C +:103F9000007920726868007A01F033F8607268680E +:103FA000407A01F02EF8A072D2E61C264FF02608C7 +:103FB000002CBAD0A2806868407960726868007A84 +:103FC000A0720AF1040090E80E00DAF81000C4E9CB +:103FD0000530C4E90312686800793C2803D04328FF +:103FE00003D0FFDFB4E62772B2E684F808B0AFE68C +:103FF00010264FF02408002C97D08888A08068688D +:10400000807920816868807A608168680089A081F1 +:1040100068688089E0819BE610264FF02308002C19 +:1040200098D08888A0806868C088208168680089E6 +:10403000608168684089A08168688089E0819AF819 +:10404000301021F0020142E030264FF02508002C0C +:104050009AD0A2806968282249680AF0EEF977E6CA +:104060002A264FF02F08002C8ED0A28069682222C9 +:10407000091DF2E714264FF01B08002C84D0A28003 +:10408000686800790128B0D02772DAE90710C4E91E +:1040900003105DE64A46214660E0287A012803D0F5 +:1040A000022817D0FFDF53E610264FF01F08002C20 +:1040B000A2D06888A080A8892081E8896081288AA8 +:1040C000A081688AE0819AF8301021F001018AF815 +:1040D00030103DE64FF012081026688800F07EFF91 +:1040E00036E6287AC8B3012838D0022836D003280B +:1040F00001D0FFDF2CE609264FF01108002C8FD0ED +:104100006F883846FFF79EF990F822A0A780687A5A +:104110002072042138460FF0DBFE052138460FF0EF +:10412000D7FE002138460FF0D3FE012138460FF0AC +:10413000CFFE032138460FF0CBFE022138460FF0A8 +:10414000C7FE062138460FF0C3FE072138460FF0A0 +:10415000BFFE504600F008FFFAE5FFE72846BDE83D +:10416000F05F01F0BBBC70B5012803D0052800D07A +:10417000FFDF70BD8DB22846FFF764F9040000D15F +:10418000FFDF20782128F4D005F030FA80B10178E3 +:1041900021F00F01891C21F0F00110310170022182 +:1041A000017245800020A075BDE8704005F021BA7D +:1041B00021462846BDE870401322FFF746B92DE995 +:1041C000F04116460C00804600D1FFDF307820F029 +:1041D0000F00801C20F0F000103030702078012893 +:1041E00004D0022818D0FFDFBDE8F0814046FFF779 +:1041F00029F9050000D1FFDF0320A87505F0F9F9C2 +:1042000094E80F00083686E80F00F94810F8301FD0 +:1042100041F001010170E7E74046FFF713F905009F +:1042200000D1FFDFA1884FF6FF700027814202D145 +:10423000E288824203D0814201D1E08840B105F09A +:10424000D8F994E80F00083686E80F00AF75CBE781 +:10425000A87D0128C8D178230022414613F084FBB1 +:104260000220A875C0E738B50C4624285CD008DCCD +:1042700020280FD0212825D022284BD0232806D152 +:104280004CE0252841D0262832D03F2851D00725A0 +:10429000284638BD0021052013F0E6FB08B11120A7 +:1042A00038BDA01C0EF0E1FA04F0BDFF0500EFD10F +:1042B000002208231146052013F056FB0528E7D0FD +:1042C000FFDFE5E76068FDF708F808B1102038BDAA +:1042D000618820886A460EF071FD04F0A4FF050095 +:1042E000D6D160680028D3D0BDF800100180CFE798 +:1042F000206820B1FCF7FAFF08B11025C8E7204676 +:104300000EF03BFE1DE00546C2E7A17820880EF0C6 +:1043100086FD16E0086801F08DFEF4E7087800F0ED +:1043200001000DF0B9FD0CE0618820880EF0C1FCA1 +:1043300007E0087800F001008DF8000068460EF0F4 +:10434000DFF804F070FFDEE770B505460C4608465E +:10435000FCF7A5FF08B1102070BD202D07D0212D3E +:104360000DD0222D0BD0252D09D0072070BD20881F +:10437000A11C0DF065FEBDE8704004F054BF06209E +:1043800070BD9B482530704708B5342200219848FD +:104390000AF07DF80120FEF749FE1120FEF75EFECF +:1043A00093496846263105F0B7F891489DF80020FA +:1043B00010F8251F62F3470121F00101017000216F +:1043C00041724FF46171A0F8071002218172FEF76B +:1043D0008FFE00B1FFDFFDF705F801F0BEF908BD63 +:1043E00010B50C464022002120460AF050F8A07F6C +:1043F00020F00300A077202020700020A07584F812 +:10440000230010BD70472DE9FC410746FCF721FF52 +:1044100010B11020BDE8FC81754E06F12501D6F8DB +:1044200025000090B6F82950ADF8045096F82B40BE +:104430008DF806403846FEF7BDFF0028EAD1FEF7AA +:1044400057FE0028E6D0009946F8251FB580B471C4 +:10445000E0E710B50446FCF722FF08B1102010BDBC +:1044600063486349224690F8250026314008FEF74C +:10447000B8FF002010BD3EB504460D460846FCF7C7 +:104480000EFF08B110203EBD14B143F204003EBD42 +:1044900057488078052803D0042801D008203EBD65 +:1044A000694602A80AF012FC2A4669469DF80800EF +:1044B000FEF797FF00203EBDFEB50D4604004FF00D +:1044C000000712D00822FEF79FFE002812D1002616 +:1044D00009E000BF54F826006946FEF720FF0028D7 +:1044E00008D1761CF6B2AE42F4D30DF01CFC10B12C +:1044F00043F20320FEBD3E4E86F824700CB3002725 +:104500001BE000BF54F8270002A9FEF708FF00B126 +:10451000FFDF9DF808008DF8000054F8270050F8E0 +:10452000011FCDF801108088ADF8050068460DF038 +:104530001FFC00B1FFDF7F1CFFB2AF42E2D386F861 +:1045400024500020FEBD2DE9F0418AB01546884672 +:1045500004001ED00F4608222946FEF755FE00280B +:1045600011D1002613E000BF54F826006946103030 +:1045700000F01FFD002806D13FB157F82600FCF7D8 +:1045800068FE10B110200AB02EE6761CF6B2AE42DC +:10459000EAD3681EC6B217E0701CC7B212E000BFB3 +:1045A00054F82600017C4A0854F827100B7CB2EB23 +:1045B000530F05D106221130113109F011FF50B10E +:1045C0007F1CFFB2AF42EBD3761EF6B2E4D2464672 +:1045D00024B1012003E043F20520D4E700200DF0D0 +:1045E000ECFB10B90DF0F5FB20B143F20420CAE753 +:1045F000F001002064B300270DF1170826E000BF8A +:1046000054F827006946103000F0D3FC00B1FFDFFA +:1046100054F82700102250F8111FCDF8011080889F +:10462000ADF8050054F827100DF1070009F005FF5B +:1046300096B156F827101022404609F0FEFE684653 +:104640000DF07BFB00B1FFDF7F1CFFB2AF42D7D381 +:10465000FEF713FF002096E7404601F0DFFCEEE78F +:1046600030B585B00446FDF7BDF830B906200FF02F +:10467000C5FB10B1062005B030BD2046FCF7E9FDB2 +:1046800018B96068FCF732FE08B11020F3E76088C3 +:104690004AF2B811884206D82078F94D28B101288D +:1046A00006D0022804D00720E5E7FEF721FD18E038 +:1046B0006078022804D0032802D043F20220DAE70F +:1046C00085F82F00C1B200200090ADF80400022947 +:1046D0002CD0032927D0FFDF68460DF009FC04F039 +:1046E000A2FD0028C7D1606801F08BFC207858B18A +:1046F00001208DF800000DF1010001F08FFC6846EB +:104700000EF0FDFB00B1FFDF207885F82E00FEF7EC +:10471000B4FE608860B1A88580B20DF046FB00B1A0 +:10472000FFDF0020A7E78DF80500D5E74020FAE776 +:104730004FF46170EFE710B50446FCF7B0FD20B907 +:10474000606838B1FCF7C9FD08B1102010BD606881 +:1047500001F064FCCA4830F82C1F6180C178617098 +:1047600080782070002010BD2DE9F843144689465A +:104770000646FCF794FDA0B94846FCF7B7FD80B9A2 +:104780002046FCF7B3FD60B9BD4DA878012800D1E3 +:104790003CB13178FF2906D049B143F20400BDE8AD +:1047A000F8831020FBE7012801D00420F7E7CCB301 +:1047B000052811D004280FD069462046FEF776FE62 +:1047C0000028ECD1217D49B1012909D0022909D065 +:1047D000032909D00720E2E70820E0E7024604E0C9 +:1047E000012202E0022200E003228046234617460F +:1047F00000200099FEF794FE0028D0D1A0892880DF +:10480000A07BE875BDF80000A882AF75BDF8001068 +:10481000090701D5A18931B1A1892980C00704D038 +:10482000032003E006E08021F7E70220FEF7FEFB0D +:1048300086F800804946BDE8F8430020FEF71EBF19 +:104840007CB58F4C05460E46A078022803D003287D +:1048500001D008207CBD15B143F204007CBD0720C7 +:104860000FF0D4FA10B9A078032806D0FEF70CFC9C +:1048700028B1A078032804D009E012207CBD1320C1 +:104880007CBD304600F053FB0028F9D1E670FEF7FE +:104890006FFD0AF058F901208DF800008DF8010035 +:1048A0008DF802502088ADF80400E07D8DF80600F8 +:1048B00068460EF0C1F904F0B6FC0028E0D1A078FB +:1048C000032805D05FF00400FEF7B0FB00207CBD9C +:1048D000E07800F03CFB0520F6E71CB510B143F290 +:1048E00004001CBD664CA078042803D0052801D024 +:1048F00008201CBD00208DF8000001218DF801105A +:104900008DF8020068460EF097F904F08CFC002840 +:10491000EFD1A078052805D05FF00200FEF786FBF6 +:1049200000201CBDE07800F01FFB0320F6E72DE916 +:10493000FC4180460E4603250846FCF7D7FC0028BC +:1049400066D14046FEF77EFD040004D02078222880 +:1049500004D208205EE543F202005BE5A07F00F090 +:1049600003073EB1012F0CD000203146FEF727FC93 +:104970000500EFD1012F06D0022F1AD0FFDF284605 +:1049800048E50120F1E7A07D3146022801D011B1B0 +:1049900007E011203EE56846FCF758FE0028D9D113 +:1049A0006946404606F04DFE0500E8D10120A0759D +:1049B000E5E7A07D032804D1314890F83000C00716 +:1049C00001D02EB30EE026B1A07F40071ED40021F7 +:1049D00000E00121404606F054FE0500CFD1A0754D +:1049E000002ECCD03146404600F0EDFA05461128A5 +:1049F000C5D1A07F4107C2D4316844F80E1F716849 +:104A0000616040F0040020740025B8E71125B6E786 +:104A10001020FFE470B50C460546FEF713FD0100BB +:104A200005D022462846BDE87040FEF70EBD43F291 +:104A3000020070BD10B5012807D1114B9B78012BE6 +:104A400000D011B143F2040010BD0DF0E0F9BDE853 +:104A5000104004F0E8BB012300F090BA00231A468E +:104A6000194600F08BBA70B506460C460846FCF7AE +:104A7000F0FB18B92068FCF712FC18B1102070BDCB +:104A8000F0010020F84D2A7E112A04D0132A00D309 +:104A90003EB10820F3E721463046FEF77DFE60B1C7 +:104AA000EDE70920132A0DD0142A0BD0A188FF2985 +:104AB000E5D31520FEF7D2FA0020D4E90012C5E9AB +:104AC0000712DCE7A1881F29D9D31320F2E71CB510 +:104AD000E548007E132801D208201CBD00208DF877 +:104AE000000068460DF02AFC04F09DFB0028F4D17C +:104AF0001120FEF7B3FA00201CBD2DE9F04FDFF8BE +:104B000068A3814691B09AF818009B4615460C465A +:104B1000132803D3FFF7DBFF00281FD12046FCF743 +:104B200098FBE8BB2846FCF794FBC8BB20784FF005 +:104B30000107C0074FF0000102D08DF83A7001E084 +:104B40008DF83A1020788846C0F3C1008DF8000037 +:104B500060788DF80910C10803D0072011B0BDE8B6 +:104B6000F08FB0B3C10701D08DF80970810705D56A +:104B70009DF8091041F002018DF80910400705D594 +:104B80009DF8090040F004008DF809009DF8090027 +:104B9000810703D540F001008DF80900002000E0F6 +:104BA00015E06E4606EB400162884A81401CA288EF +:104BB000C0B20A820328F5D32078C0F3C1000128CF +:104BC00025D0032823D04846FCF743FB28B110200A +:104BD000C4E7FFE78DF80970D8E799F800004008AE +:104BE00008D0012809D0022807D0032805D043F2B5 +:104BF0000220B3E78DF8028001E08DF8027048468C +:104C000050F8011FCDF803108088ADF80700FEF7BB +:104C1000AFFB8DF801000021424606EB41002B88D6 +:104C2000C3826B888383AB884384EB880385491CEC +:104C3000C285C9B282860329EFD3E088ADF83C0073 +:104C400068460DF053FC002887D19AF818005546A5 +:104C5000112801D0082081E706200FF0D7F838B1DD +:104C60002078C0F3C100012804D0032802D006E058 +:104C7000122073E795F8240000283FF46EAFFEF78A +:104C800003FA022801D2132068E7584600F04FF9D2 +:104C900000289DD185F819B068460DF06DFD04F02F +:104CA000C2FA040094D1687E00F051F91220FEF798 +:104CB000D5F9204652E770B56B4D287E122801D0F9 +:104CC0000820DCE60DF05BFD04F0ADFA040005D130 +:104CD000687E00F049F91120FEF7C0F92046CEE6C3 +:104CE00070B5064615460C460846FCF7D8FA18B9C2 +:104CF0002846FCF7D4FA08B11020C0E62A4621461F +:104D000030460EF03BF804F08EFA0028F5D12178F9 +:104D10007F29F2D10520B2E67CB505460C4608464F +:104D2000FCF797FA08B110207CBD2846FEF78AFBF5 +:104D300020B10078222804D208207CBD43F2020072 +:104D40007CBD494890F83000400701D511207CBD5A +:104D50002078C00802D16078C00801D007207CBD4F +:104D6000ADF8005020788DF8020060788DF80300CF +:104D70000220ADF8040068460CF03BFE04F053FA44 +:104D80007CBD70B586B014460D460646FEF75AFB4C +:104D900028B10078222805D2082006B06FE643F239 +:104DA0000200FAE72846FCF7A1FA20B944B12046F0 +:104DB000FCF793FA08B11020EFE700202060A080F4 +:104DC000294890F83000800701D51120E5E703A9B4 +:104DD00030460CF05EFE10B104F025FADDE7ADF8C8 +:104DE0000060BDF81400ADF80200BDF81600ADF883 +:104DF0000400BDF81000BDF81210ADF80600ADF8C3 +:104E000008107DB1298809B1ADF80610698809B18B +:104E1000ADF80210A98809B1ADF80810E98809B108 +:104E2000ADF80410DCB1BDF80610814201D9081AB2 +:104E30002080BDF80210BDF81400814201D9081A83 +:104E40006080BDF80800BDF80410BDF816200144CC +:104E5000BDF812001044814201D9081AA0806846AA +:104E60000CF0D5FEB8E70000F00100201CB56C493D +:104E70000968CDE9001068460DF03AFB04F0D3F95B +:104E80001CBD1CB500200090019068460DF030FB61 +:104E900004F0C9F91CBD70B505460C460846FCF780 +:104EA000FEF908B11020EAE5214628460DF012F976 +:104EB000BDE8704004F0B7B93EB505460C4608465B +:104EC000FCF7EDF908B110203EBD002000900190E4 +:104ED0000290ADF800502089ADF8080020788DF8D8 +:104EE0000200606801902089ADF808006089ADF883 +:104EF0000A0068460DF000F904F095F93EBD0EB5C4 +:104F0000ADF800000020019068460DF0F5F804F0BF +:104F10008AF90EBD10800888508048889080C88823 +:104F200010818888D080002050819081704710B512 +:104F3000044604F0E4F830B1407830B1204604F083 +:104F4000FCFB002010BD052010BD122010BD10B5C7 +:104F500004F0D5F8040000D1FFDF607800B9FFDF6E +:104F60006078401E607010BD10B504F0C8F80400F1 +:104F700000D1FFDF6078401C607010BD1CB5ADF83B +:104F800000008DF802308DF803108DF8042068467B +:104F90000DF0B7FE04F047F91CBD0CB521A2D2E913 +:104FA0000012CDE900120079694601EB501000783B +:104FB0000CBD0278520804D0012A02D043F202202C +:104FC0007047FEF7ACB91FB56A46FFF7A3FF684606 +:104FD0000DF008FC04F027F904B010BD70B50C000A +:104FE00006460DD0FEF72EFA050000D1FFDFA680A1 +:104FF00028892081288960816889A081A889E08129 +:105000003DE500B540B1012805D0022803D00328B2 +:1050100004D0FFDF002000BDFF2000BD042000BD44 +:1050200014610200070605040302010010B50446DE +:10503000FCF70FF908B1102010BD2078C0F3021062 +:10504000042807D86078072804D3A178102901D84C +:10505000814201D2072010BDE078410706D42179B2 +:105060004A0703D4000701D4080701D5062010BD64 +:10507000002010BD10B513785C08837F64F3C7135C +:10508000837713789C08C37F64F30003C377107899 +:10509000C309487863F34100487013781C090B7802 +:1050A00064F347130B701378DB0863F30000487058 +:1050B0005078487110BD10B5C4780B7864F30003C4 +:1050C0000B70C478640864F341030B70C478A408BF +:1050D00064F382030B70C478E40864F3C3030B70B9 +:1050E0000379117863F30001117003795B0863F3AE +:1050F0004101117003799B0863F3820111700079FB +:10510000C00860F3C301117010BD70B514460D46A0 +:10511000064604F06BFA80B10178182221F00F01E5 +:10512000891C21F0F001A03100F8081B214609F08C +:1051300084F9BDE8704004F05CBA29463046BDE809 +:1051400070401322FEF781B92DE9F047064608A802 +:10515000904690E8300489461F46142200212846D4 +:1051600009F095F90021CAF80010B8F1000F03D03A +:10517000B9F1000F03D114E03878C00711D02068CE +:10518000FCF78DF8C0BBB8F1000F07D120681230D2 +:1051900028602068143068602068A8602168CAF818 +:1051A00000103878800724D56068FCF796F818BBA3 +:1051B000B9F1000F21D0FFF7E4F80168C6F86811D3 +:1051C0008188A6F86C11807986F86E0101F013FDD4 +:1051D000F94FEF60626862B196F8680106F26911F2 +:1051E00040081032FEF7FDF810223946606809F0D9 +:1051F00024F90020BDE8F08706E0606820B1E8608F +:105200006068C6F86401F4E71020F3E730B505469E +:1052100008780C4620F00F00401C20F0F0011031FF +:1052200021700020607095F8230030B104280FD061 +:10523000052811D0062814D0FFDF20780121B1EB1A +:10524000101F04D295F8200000F01F00607030BDE0 +:1052500021F0F000203002E021F0F000303020702A +:10526000EBE721F0F0004030F9E7F0B591B002270C +:1052700015460C4606463A46ADF80870092103ABC0 +:1052800005F063F80490002810D004208DF8040085 +:105290008DF80170E034099605948DF818500AA92C +:1052A000684610F0FCFA00B1FFDF012011B0F0BD3C +:1052B00010B588B00C460A99ADF80000CBB118685B +:1052C000CDF80200D3F80400CDF80600ADF80A20AE +:1052D000102203A809F0B1F868460DF0F3FA03F0C4 +:1052E000A2FF002803D1A17F41F01001A17708B0EF +:1052F00010BD0020CDF80200E6E72DE9F84F064684 +:10530000808A0D4680B28246FEF79CF804463078CB +:10531000DFF8A48200274FF00209A8F120080F2827 +:1053200070D2DFE800F06FF23708387D8CC8F1F0FA +:10533000EFF35FF3F300A07F00F00300022809D031 +:105340005FF0000080F0010150460EF0AFFD050057 +:1053500003D101E00120F5E7FFDF98F85C10C907F1 +:1053600002D0D8F860000BE0032105F11D0012F017 +:10537000BEF8D5F81D009149B0FBF1F201FB120017 +:10538000C5F81D0070686867B068A8672078252890 +:1053900000D0FFDFCAE0A07F00F00300022809D0A0 +:1053A0005FF0000080F0010150460EF07FFD060026 +:1053B00003D101E00120F5E7FFDF3078810702D556 +:1053C0002178252904D040F001003070BDE8F88F25 +:1053D00085F80090307F287106F11D002D36C5E953 +:1053E0000206F3E7A07F00F00300022808D00020A7 +:1053F00080F0010150460EF059FD040004D102E096 +:105400000120F5E7A7E1FFDF2078C10604D50720DA +:1054100028703D346C60D9E740F008002070D5E773 +:10542000E07F000700D5FFDF307CB28800F0010389 +:1054300001B05046BDE8F04F092106F064B804B948 +:10544000FFDF716821B1102204F1240008F0F5FF9C +:1054500028212046FDF75EFEA07F00F00300022811 +:105460000ED104F12400002300901A462146504634 +:10547000FFF71EFF112807D029212046FDF74AFE1D +:10548000307A84F82000A1E7A07F000700D5FFDF75 +:1054900014F81E0F40F008002070E782A761E76152 +:1054A000C109607861F34100014660F382016170D7 +:1054B000307AE0708AE7A07F00F00300022809D06C +:1054C0005FF0000080F0010150460EF0EFFC040098 +:1054D00003D101E00120F5E7FFDF022104F185009F +:1054E00012F005F80420287004F5B4706860B4F870 +:1054F00085002882304810387C346C61C5E9028010 +:1055000064E703E024E15BE02DE015E0A07F00F01C +:105510000300022807D0002080F0010150460EF061 +:10552000C5FC18B901E00120F6E7FFDF324621464D +:105530005046BDE8F84FE8E504B9FFDF20782128A0 +:10554000A1D93079012803D1E07F40F00800E0774D +:10555000324621465046FFF7D8FD2046BDE8F84FB9 +:105560002321FDF7D7BD3279AA8005F1080309216F +:10557000504604F0EAFEE86010B10520287025E7E7 +:10558000A07F00F00300022808D0002080F0010175 +:1055900050460EF08BFC040003D101E00120F5E73A +:1055A000FFDF04F1620102231022081F0EF005FB49 +:1055B00007703179417009E75002002040420F0026 +:1055C000A07F00F00300022808D0002080F0010135 +:1055D00050460EF06BFC050003D101E00120F5E719 +:1055E000FFDF95F8840000F0030001287AD1A07F46 +:1055F00000F00307E07F10F0010602D0022F04D173 +:1056000033E095F8A000C0072BD0D5F8601121B386 +:1056100095F88320087C62F387000874A17FCA098B +:10562000D5F8601162F341000874D5F8601166F393 +:1056300000000874AEB1D5F86001102204F1240115 +:10564000883508F0FAFE287E40F001002876287898 +:1056500020F0010005F8880900E016B1022F04D0FF +:105660002DE095F88800C00727D0D5F85C1121B34C +:1056700095F88320087C62F387000874A17FCA092B +:10568000D5F85C1162F341000874D5F85C1166F33B +:10569000000008748EB1D5F85C01102204F12401D9 +:1056A000883508F0CAFE287840F0010005F8180B8C +:1056B000287820F0010005F8A009022F44D000202E +:1056C00000EB400005EBC00090F88800800709D58A +:1056D00095F87C00D5F86421400805F17D01103271 +:1056E000FDF77FFE8DF8009095F884006A4600F083 +:1056F00003008DF8010095F888108DF8021095F8D8 +:10570000A0008DF803002146504601F05DFA207894 +:10571000252805D0212807D0FFDF2078222803D9AB +:1057200022212046FDF7F6FCA07F00F003000228AE +:105730000CD0002080F0010150460EF0C9FB00287B +:105740003FF44FAEFFDF41E60120B9E70120F1E76A +:10575000706847703AE6FFDF38E670B5FE4C00250A +:1057600084F85C50256610F066F804F110012046BC +:1057700003F0F8FE84F8305070BD70B50D46FDF7AB +:1057800061FE040000D1FFDF4FF4B872002128460B +:1057900008F07DFE04F124002861A07F00F00300E2 +:1057A000022809D05FF0010105F1E00010F044F893 +:1057B000002800D0FFDF70BD0221F5E70A46014650 +:1057C00002F1E00010F059B870B50546406886B0A7 +:1057D00001780A2906D00D2933D00E292FD0FFDFFA +:1057E00006B070BD86883046FDF72CFE040000D15F +:1057F000FFDF20782128F3D028281BD168680221F8 +:105800000E3001F0D6F9A8B168680821801D01F0BA +:10581000D0F978B104F1240130460DF00FFA03F00D +:1058200002FD00B1FFDF06B02046BDE8704029212F +:10583000FDF770BC06B0BDE8704003F0DABE012190 +:1058400001726868C6883046FDF7FCFD040000D18F +:10585000FFDFA07F00F00301022902D120F0100039 +:10586000A077207821280AD06868017A09B10079E8 +:1058700080B1A07F00F00300022862D0FFDFA07F8C +:1058800000F003000228ABD1FEF72DF80028A7D0C6 +:10589000FFDFA5E703F0ADFEA17F08062BD5E07F73 +:1058A000C00705D094F8200000F01F00102820D079 +:1058B0005FF0050084F82300207829281DD02428D3 +:1058C000DDD13146042012F0F9F822212046FDF7FF +:1058D00021FCA07F00F00300022830D05FF0000020 +:1058E00080F0010130460EF0F3FA0028C7D0FFDF48 +:1058F000C5E70620DEE70420DCE701F0030002280C +:1059000008D0002080F0010130460EF0CFFA0500EB +:1059100003D101E00120F5E7FFDF25212046FDF757 +:10592000F9FB03208DF80000694605F1E0000FF057 +:105930009BFF0228A3D00028A1D0FFDF9FE7012012 +:10594000CEE703F056FE9AE72DE9F04387B099467B +:10595000164688460746FDF775FD04004BD02078B3 +:10596000222848D3232846D0E07F000743D4A07FD5 +:1059700000F00300022809D05FF0000080F0010170 +:1059800038460EF093FA050002D00CE00120F5E74E +:10599000A07F00F00300022805D001210022384634 +:1059A0000EF07BFA05466946284601F034F9009866 +:1059B00000B9FFDF45B10098E03505612078222865 +:1059C00006D0242804D007E000990020086103E0F5 +:1059D00025212046FDF79EFB00980121417047627A +:1059E000868001A9C0E902890FF059FF022802D080 +:1059F000002800D0FFDF07B0BDE8F08370B586B0A7 +:105A00000546FDF71FFD017822291ED9807F00F091 +:105A10000300022808D0002080F0010128460EF083 +:105A200045FA04002FD101E00120F5E7FFDF2AE06D +:105A3000B4F85E0004F1620630440178427829B17E +:105A400021462846FFF711FCB0B9C9E6ADF804209D +:105A50000921284602AB04F078FC03900028F4D01A +:105A600005208DF80000694604F1E0000FF0FCFE0F +:105A7000022801D000B1FFDF02231022314604F1D9 +:105A80005E000EF0D0F8B4F860000028D0D1A7E690 +:105A900010B586B00446FDF7D5FC017822291BD944 +:105AA000807F00F00300022808D0002080F0010170 +:105AB00020460EF0FBF9040003D101E00120F5E7D8 +:105AC000FFDF06208DF80000694604F1E0000FF0CA +:105AD000CBFE002800D0FFDF06B010BD2DE9F05F3F +:105AE00005460C4600270078904601093E4604F121 +:105AF000080BBA4602297DD0072902D00A2909D10C +:105B000046E0686801780A2905D00D2930D00E29B1 +:105B10002ED0FFDFBBE114271C26002C6BD0808821 +:105B2000A080FDF78FFC5FEA000900D1FFDF99F844 +:105B300017005A46400809F11801FDF752FC686841 +:105B4000C0892082696851F8060FC4F812004868BD +:105B5000C4F81600A07E01E03002002020F006000C +:105B600040F00100A07699F81E0040F020014DE0C1 +:105B70001A270A26002CD1D0C088A080FDF762FC2D +:105B8000050000D1FFDF59462846FFF73FFB7EE1C5 +:105B90000CB1A88BA080287A0B287DD006DC0128C8 +:105BA0007BD0022808D0032804D135E00D2875D019 +:105BB0000E2874D0FFDF6AE11E270926002CADD025 +:105BC000A088FDF73FFC5FEA000900D1FFDF287BDA +:105BD00000F003000128207A1BD020F00100207281 +:105BE000297B890861F341002072297BC90861F390 +:105BF000820001E041E1F2E02072297B090961F3B2 +:105C0000C300207299F81E0040F0400189F81E1070 +:105C10003DE140F00100E2E713270D26002CAAD059 +:105C2000A088FDF70FFC8146807F00F0030002286A +:105C300008D0002080F00101A0880EF037F905009F +:105C400003D101E00120F5E7FFDF99F81E0000F025 +:105C50000302022A50D0686F817801F00301012904 +:105C6000217A4BD021F00101217283789B0863F3E4 +:105C7000410121728378DB0863F38201217283780A +:105C80001B0963F3C3012172037863F306112172C8 +:105C9000437863F3C71103E061E0A9E090E0A1E07D +:105CA000217284F809A0C178A172022A29D0027950 +:105CB000E17A62F30001E1720279520862F3410174 +:105CC000E1720279920862F38201E1720279D208EC +:105CD00062F3C301E1724279217B62F30001217317 +:105CE0004279520862F3410121734279920862F3CA +:105CF00082012173407928E0A86FADE741F00101EE +:105D0000B2E74279E17A62F30001E1724279520826 +:105D100062F34101E1724279920862F38201E17219 +:105D20004279D20862F3C301E1720279217B62F306 +:105D3000000121730279520862F341012173027953 +:105D4000920862F3820121730079C00860F3C301F5 +:105D5000217399F80000232831D9262140E0182723 +:105D60001026E4B3A088FDF76DFB8346807F00F02A +:105D70000300022809D0002080F00101A0880EF065 +:105D800095F85FEA000903D101E00120F4E7FFDFA5 +:105D9000E868A06099F8000040F0040189F800105C +:105DA00099F80100800708D5012020739BF80000B6 +:105DB00023286CD92721584651E084F80CA066E0CE +:105DC00015270F265CB1A088FDF73CFB8146062213 +:105DD0005946E86808F0C7FB0120A073A0E041E045 +:105DE00048463CE016270926E4B3287B20724EE0A3 +:105DF000287B19270E26ACB3C4F808A0A4F80CA081 +:105E0000012807D0022805D0032805D0042803D094 +:105E1000FFDF0DE0207207E0697B042801F00F012D +:105E200041F0800121721ED0607A20F00300607280 +:105E3000A088FDF707FB05460078212827D02328F6 +:105E400000D0FFDFA87F00F00300022813D000205D +:105E500080F00101A0880EF03BF822212846FDF7D2 +:105E600059F914E004E0607A20F00300401CDEE7FA +:105E7000A8F8006010E00120EAE70CB16888A08073 +:105E8000287A68B301280AD002284FD0FFDFA8F88B +:105E900000600CB1278066800020BDE8F09F1527C8 +:105EA0000F26002CE4D0A088FDF7CCFA807F00F00C +:105EB0000300022808D0002080F00101A0880DF026 +:105EC000F5FF050003D101E00120F5E7FFDFD5F87C +:105ED0001D000622594608F046FB84F80EA0D6E7BE +:105EE00017270926002CC3D0A088FDF7ABFA8146FE +:105EF000807F00F00300022808D0002080F001011C +:105F0000A0880DF0D3FF050003D101E00120F5E7E3 +:105F1000FFDF6878800701D5022000E001202072B1 +:105F200099F800002328B2D9272159E719270E260E +:105F3000002C9DD0A088FDF785FA5FEA000900D10A +:105F4000FFDFC4F808A0A4F80CA084F808A0A07A89 +:105F500040F00300A07299F81E10C90961F3820095 +:105F6000A07299F81F2099F81E1012EAD11F05D0CF +:105F700099F8201001F01F0110292BD020F0080003 +:105F8000A07299F81F10607A61F3C3006072697A99 +:105F900001F003010129A2D140F00400607299F8D8 +:105FA0001E0000F003000228E87A16D0217B60F37F +:105FB00000012173AA7A607B62F300006073EA7AC1 +:105FC000520862F341012173A97A490861F3410043 +:105FD00060735CE740F00800D2E7617B60F300018A +:105FE0006173AA7A207B62F300002073EA7A520878 +:105FF00062F341016173A97A490861F3410020739A +:1060000045E710B5FE4C30B10146102204F12000E6 +:1060100008F013FA012084F8300010BD10B50446D2 +:1060200000F0E9FDF64920461022BDE8104020317D +:1060300008F003BA70B5F24D06004FF0000413D01B +:10604000FBF707F908B110240CE00621304608F0F0 +:1060500071FA411C05D028665FF0010085F85C00EC +:1060600000E00724204670BD0020F7E7007810F01C +:106070000F0204D0012A05D0022A0CD110E0000939 +:1060800009D10AE00009012807D0022805D0032819 +:1060900003D0042801D00720704708700020704703 +:1060A0000620704705282AD2DFE800F003070F1703 +:1060B0001F00087820F0FF001EE0087820F00F0095 +:1060C000401C20F0F000103016E0087820F00F009F +:1060D000401C20F0F00020300EE0087820F00F0087 +:1060E000401C20F0F000303006E0087820F00F006F +:1060F000401C20F0F000403008700020704707205E +:1061000070472DE9F041804688B00D4600270846CB +:10611000FBF7ECF8A8B94046FDF794F9040003D06A +:106120002078222815D104E043F2020008B0BDE82F +:10613000F08145B9A07F410603D500F00300022895 +:1061400001D01020F2E7A07FC10601D4010702D5DB +:106150000DB10820EAE7E17F090701D50D20E5E749 +:1061600000F0030002280DD165B12846FEF75EFF5E +:106170000700DBD1FBF736FB20B9E878800701D5B3 +:106180000620D3E7A07F00F00300022808D00020FB +:1061900080F0010140460DF089FE060002D00FE0BC +:1061A0000120F5E7A07F00F0030002280ED00020B8 +:1061B00080F00101002240460DF06FFE060007D07E +:1061C000A07F00F00300022804D009E00120EFE7DF +:1061D0000420ABE725B12A4631462046FEF74AFFA8 +:1061E0006946304600F017FD009800B9FFDF0099BE +:1061F000022006F1E0024870C1F824804A610022C2 +:106200000A81A27F02F00302022A1CD00120087139 +:10621000287800F00102087E62F3010008762A78EF +:10622000520862F3820008762A78920862F3C3006B +:1062300008762A78D20862F30410087624212046D2 +:10624000FCF768FF33E035B30871301D88613078A2 +:10625000400908777078C0F340004877287800F04C +:106260000102887F62F301008877A27FD20962F37E +:1062700082008877E27F62F3C3008877727862F3E6 +:1062800004108877A878C87701F1210228462031C8 +:10629000FEF711FF03E00320087105200876252191 +:1062A0002046FCF737FFA07F20F04000A07701A92F +:1062B00000980FF0F4FA022801D000B1FFDF384651 +:1062C00034E72DE9FF4F8DB09A4693460D460027DF +:1062D0000D98FDF7B7F8060006D03078262806D0CE +:1062E000082011B0BDE8F08F43F20200F9E7B07F5B +:1062F00000F00309B9F1020F11D04DB95846FEF76D +:1063000095FE0028EDD1B07F00F00300022806D0F2 +:10631000BBF1000F11D0FBF765FA20B10DE0BBF126 +:10632000000F50D109E006200DF068FD28B19BF860 +:106330000300800701D50620D3E7B07F00F00300FB +:10634000022809D05FF0000080F001010D980DF0E7 +:10635000ADFD040003D101E00120F5E7FFDF852D4D +:1063600027D007DCEDB1812D1DD0822D1DD0832DCE +:1063700008D11CE0862D1ED0882D1ED0892D1ED060 +:106380008A2D1ED00F2020710F281CD003F02EF96B +:10639000D8B101208DF81400201D06902079B0B1ED +:1063A00056E10020EFE70120EDE70220EBE70320B4 +:1063B000E9E70520E7E70620E5E70820E3E709200D +:1063C000E1E70A20DFE707208BE7112089E7B9F131 +:1063D000020F03D0A56F03D1A06F02E0656FFAE74B +:1063E000606F804631D04FF0010001904FF0020005 +:1063F00000905A4621463046FEF73CFE02E000007F +:10640000300200209BF8000000F00101A87861F341 +:106410000100A870B17FC90961F38200A870F17F03 +:1064200061F3C300A870617861F30410A87020784C +:10643000400928706078C0F3400068709BF8020043 +:10644000E87000206871287103E0022001900120AB +:106450000090A87898F80210C0F3C000C1F3C00102 +:10646000084003902CD05046FAF7F3FEC0BBDAF890 +:106470000C00FAF7EEFE98BBDAF81C00FAF7E9FE1A +:1064800070BBDAF80C00A060DAF81C00E0606078FD +:1064900098F8012042EA500161F34100607098F8D9 +:1064A0000210C0B200EA111161F300006070002018 +:1064B0002077009906F11700022907D0012106E094 +:1064C000607898F8012002EA5001E5E7002104EB2A +:1064D000810148610199701C022902D0012101E06B +:1064E00028E0002104EB81014861A87800F0030056 +:1064F000012857D198F8020000F00300012851D17B +:10650000B9F1020F04D02A1D691D5846FEF7D3FDCC +:10651000287998F8041008408DF82C00697998F8CB +:10652000052011408DF8301008433BD05046FAF753 +:1065300090FE08B11020D4E60AF110018B46B9F1A3 +:10654000020F17D00846002104F18C03CDE90003A7 +:1065500004F5AE7202920BAB2046039AFEF7F4FDEF +:106560000028E8D1B9F1020F08D0504608D14FF009 +:10657000010107E050464FF00101E5E75846F5E715 +:106580004FF0000104F1A403CDE9000304F5B0725B +:10659000029281F001010CAB2046039AFEF7D4FD74 +:1065A0000028C8D16078800733D4A87898F8021002 +:1065B000C0F38000C1F3800108432AD0297898F8FD +:1065C0000000F94AB9F1020F06D032F81120430059 +:1065D000DA4002F003070AE032F810204B00DA40FC +:1065E00012F0030705D0012F0AD0022F0AD0032F83 +:1065F00006D0039A6AB1012906D0042904D008E024 +:106600000227F6E70127F4E7012801D0042800D18A +:106610000427B07F40F08000B077F17F039860F3EB +:106620000001F1776078800705D50320A0710398F9 +:1066300070B9002029E00220022F18D0012F18D0B5 +:10664000042F2AD00020A071B07F20F08000B07706 +:1066500025213046FCF75EFD05A904F1E0000FF0AE +:1066600003F910B1022800D0FFDF002039E6A07145 +:10667000DFE7A0710D22002104F1200007F007FFE1 +:10668000207840F00200207001208DF8100004AA4C +:1066900031460D9800F098FADAE70120A071D7E7AB +:1066A0002DE9F04387B09046894604460025FCF763 +:1066B000C9FE060006D03078272806D0082007B08B +:1066C000BDE8F08343F20200F9E7B07F00F0030079 +:1066D000022809D05FF0000080F0010120460DF093 +:1066E000E5FB040003D101E00120F5E7FFDFA77916 +:1066F0005FEA090005D0012821D0B9F1020F26D1A7 +:1067000010E0B8F1000F22D1012F05D0022F05D0E3 +:10671000032F05D0FFDF2EE00C252CE001252AE019 +:10672000022528E04046FAF794FDB0B9032F0ED1B8 +:106730001022414604F11D0007F07FFE1BE0012FEF +:1067400002D0022F03D104E0B8F1000F13D00720CC +:10675000B5E74046FAF77DFD08B11020AFE71022FB +:10676000002104F11D0007F092FE0621404607F0CB +:10677000E1FEC4F81D002078252140F002002070C1 +:106780003046FCF7C7FC2078C10713D020F0010089 +:10679000207002208DF8000004F11D0002908DF899 +:1067A00004506946C3300FF05FF8022803D010B1DF +:1067B000FFDF00E02577002081E730B587B00D4688 +:1067C0000446FCF73FFE98B1807F00F003000228EA +:1067D00011D0002080F0010120460DF067FB04007D +:1067E0000ED02846FAF735FD38B1102007B030BD7D +:1067F00043F20200FAE70120ECE72078400701D4D9 +:106800000820F3E7294604F13D002022054607F061 +:1068100014FE207840F01000207001070FD520F002 +:106820000800207007208DF80000694604F1E000A0 +:1068300001950FF019F8022801D000B1FFDF002008 +:10684000D4E770B50D460646FCF7FCFD18B101789B +:10685000272921D102E043F2020070BD807F00F0C1 +:106860000300022808D0002080F0010130460DF01E +:106870001DFB040003D101E00120F5E7FFDFA07953 +:10688000022809D16078C00706D02A462146304642 +:10689000FEF7EBFC10B10FE0082070BDB4F860000B +:1068A0000E280BD204F1620102231022081F0DF002 +:1068B00084F9012101704570002070BD112070BD68 +:1068C00070B5064614460D460846FAF7C2FC18B9DC +:1068D0002046FAF7E4FC08B1102070BDA6F57F4011 +:1068E000FF380ED03046FCF7ADFD38B14178224676 +:1068F0004B08811C1846FCF774FD07E043F20200C8 +:1069000070BD2046FDF7A5FD0028F9D11021E01D3E +:1069100010F0EDFDE21D294604F1170000F08BF99F +:10692000002070BD2DE9F04104468AB01546884626 +:1069300000270846FAF7DAFC18B92846FAF7D6FC19 +:1069400018B110200AB0BDE8F0812046FCF77AFDAE +:10695000060003D0307827281BD102E043F2020062 +:10696000F0E7B07F00F00300022809D05FF00000DC +:1069700080F0010120460DF099FA040003D101E0F6 +:106980000120F5E7FFDF2078400702D56078800717 +:1069900001D40820D6E7B07F00F00300022805D01C +:1069A000A06F05D1A16F04E01C610200606FF8E7E1 +:1069B000616F407800B19DB1487810B1B8F1000F17 +:1069C0000ED0ADB1EA1D06A8E16800F034F910223E +:1069D00006A905F1170007F003FD18B1042707E029 +:1069E0000720AFE71022E91D04F12D0007F025FD77 +:1069F000B8F1000F06D0102208F1070104F11D00C4 +:106A000007F01BFD2078252140F002002070304661 +:106A1000FCF780FB2078C10715D020F00100207022 +:106A200002208DF8000004F11D0002901030039048 +:106A30008DF804706946B3300EF016FF022803D0BB +:106A400010B1FFDF00E0277700207BE7F8B515469F +:106A50000E460746FCF7F6FC040004D020782228F6 +:106A600004D00820F8BD43F20200F8BDA07F00F07A +:106A70000300022802D043F20500F8BD3046FAF7C1 +:106A8000E8FB18B92846FAF7E4FB08B11020F8BD76 +:106A900000953288B31C21463846FEF709FC1128C0 +:106AA00015D00028F3D1297C4A08A17F62F3C711D1 +:106AB000A177297CE27F61F30002E277297C8908D3 +:106AC00084F82010A17F21F04001A177F8BDA17FBB +:106AD0000907FBD4D6F80200C4F83600D6F8060041 +:106AE000C4F83A003088A0861022294604F1240018 +:106AF00007F0A3FC287C4108E07F61F34100E077C8 +:106B0000297C61F38200E077287C800884F82100EA +:106B1000A07F40F00800A0770020D3E770B50D46B5 +:106B200006460BB1072070BDFCF78CFC040007D0B3 +:106B30002078222802D3A07F800604D4082070BDCC +:106B400043F2020070BDADB1294630460CF076F834 +:106B500002F069FB297C4A08A17F62F3C711A17783 +:106B6000297CE27F61F30002E277297C890884F8BE +:106B7000201004E030460CF084F802F054FBA17FB2 +:106B800021F02001A17770BD70B50D46FCF75AFCCD +:106B9000040005D02846FAF782FB20B1102070BD12 +:106BA00043F2020070BD29462046FEF72FFB00206D +:106BB00070BD04E010F8012B0AB100207047491E97 +:106BC00089B2F7D20120704770B51546064602F02B +:106BD0000DFD040000D1FFDF207820F00F00801CA5 +:106BE00020F0F0002030207066802868A060BDE8AA +:106BF000704002F0FEBC10B5134C94F83000002831 +:106C000008D104F12001A1F110000EF06FFE012067 +:106C100084F8300010BD10B190F8B9202AB10A48AC +:106C200090F8350018B1002003E0B83001E00648C4 +:106C300034300860704708B50023009313460A46B5 +:106C40000DF031FB08BD00003002002018B1817842 +:106C5000012938D101E010207047018842F6011265 +:106C6000881A914231D018DC42F60102A1EB0200F1 +:106C700091422AD00CDC41B3B1F5C05F25D06FF44E +:106C8000C050081821D0A0F57060FF381BD11CE05F +:106C900001281AD002280AD117E0B0F5807F14D05D +:106CA00008DC012811D002280FD003280DD0FF28BE +:106CB00009D10AE0B0F5817F07D0A0F580700338D4 +:106CC00003D0012801D0002070470F2070470A2808 +:106CD0001FD008DC0A2818D2DFE800F0191B1F1F9C +:106CE000171F231D1F21102815D008DC0B2812D0D8 +:106CF0000C2810D00D2816D00F2806D10DE0112831 +:106D00000BD084280BD087280FD003207047002099 +:106D1000704705207047072070470F2070470420F8 +:106D20007047062070470C20704743F202007047FE +:106D300038B50C46050041D06946FFF797F90028A1 +:106D400019D19DF80010607861F302006070694607 +:106D5000681CFFF78BF900280DD19DF800106078B2 +:106D600061F3C5006070A978C1F34101012903D026 +:106D7000022905D0072038BD217821F0200102E04A +:106D8000217841F020012170410704D0A978C90879 +:106D900061F386106070607810F0380F07D0A97822 +:106DA000090961F3C710607010F0380F02D16078E4 +:106DB000400603D5207840F040002070002038BD08 +:106DC00070B504460020088015466068FFF7B0FFE4 +:106DD000002816D12089A189884211D8606880785E +:106DE000C0070AD0B1F5007F0AD840F20120B1FBFC +:106DF000F0F200FB1210288007E0B1F5FF7F01D907 +:106E00000C2070BD01F201212980002070BD10B559 +:106E10000478137864F3000313700478640864F34F +:106E2000410313700478A40864F382031370047898 +:106E3000E40864F3C30313700478240964F30413AF +:106E400013700478640964F34513137000788009A3 +:106E500060F38613137031B10878C10701D1800740 +:106E600001D5012000E0002060F3C713137010BDAE +:106E70004278530702D002F0070306E012F0380F01 +:106E800002D0C2F3C20300E001234A7863F3020296 +:106E90004A70407810F0380F02D0C0F3C20005E00D +:106EA000430702D000F0070000E0012060F3C502B4 +:106EB0004A7070472DE9F04F95B00D00824613D00F +:106EC00012220021284607F0E2FA4FF6FF7B05AABE +:106ED0000121584607F0A3F80024264637464FF410 +:106EE00020586FF4205972E0102015B0BDE8F08FE3 +:106EF0009DF81E0001280AD1BDF81C1041450BD099 +:106F000011EB09000AD001280CD002280CD0042C67 +:106F10000ED0052C0FD10DE0012400E00224BDF8B5 +:106F20001A6008E0032406E00424BDF81A7002E0A9 +:106F3000052400E00624BDF81A10514547D12C74F1 +:106F4000BEB34FF0000810AA4FF0070ACDE9028245 +:106F5000CDE900A80DF13C091023CDF81090424670 +:106F60003146584607F02BF908BBBDF83C002A46CD +:106F7000C0B210A90EF045FDC8B9AE81CFB1CDE9C0 +:106F800000A80DF1080C0AAE40468CE8410213231C +:106F900000223946584607F012F940B9BDF83C00C6 +:106FA000F11CC01EC0B22A1D0EF02BFD10B1032033 +:106FB0009BE70AE0BDF82900E881062C05D19DF881 +:106FC0001E00A872BDF81C00288100208DE705A8CE +:106FD00007F031F800288BD0FFF779FE85E72DE91F +:106FE000F0471C46DDE90978DDF8209015460E00D3 +:106FF000824600D1FFDF0CB1208818B1D5B1112035 +:10700000BDE8F087022D01D0012100E0002106F14A +:10701000140005F0CDFEA8F8000002463B462946C4 +:10702000504603F092F9C9F8000008B9A41C3C606E +:107030000020E5E71320E3E7F0B41446DDE904524D +:107040008DB1002314B1022C09D101E0012306E027 +:107050000D7CEE0703D025F0010501230D742146B8 +:10706000F0BC04F050BA1A80F0BC70472DE9FE4F16 +:1070700091461A881C468A468046FAB102AB4946B8 +:1070800003F063F9050019D04046A61C27880DF0CF +:1070900050F83246072629463B4600960CF05FFC26 +:1070A00020882346CDE900504A4651464046FFF726 +:1070B000C3FF002020800120BDE8FE8F0020FBE7F9 +:1070C0002DE9F04786B082460EA8904690E8B000C1 +:1070D000894604AA05A903A88DE807001E462A468A +:1070E00021465046FFF77BFF039901B1012139701A +:1070F000002818D1FA4904F1140204AB086003987F +:1071000005998DE8070042464946504606F003FAC5 +:10711000A8B1092811D2DFE800F005080510100A0F +:107120000C0C0E00002006B06AE71120FBE70720D8 +:10713000F9E70820F7E70D20F5E70320F3E7BDF8AE +:1071400010100398CDE9000133462A4621465046E7 +:10715000FFF772FFE6E72DE9F04389B01646DDE957 +:1071600010870D4681461C461422002103A807F013 +:107170008EF9012002218DF810108DF80C008DF889 +:107180001170ADF8146064B1A278D20709D08DF8FF +:107190001600E088ADF81A00A088ADF81800A068C5 +:1071A000079008A80095CDE90110424603A948467A +:1071B0006B68FFF785FF09B0BDE8F083F0B58BB0D1 +:1071C00000240646069407940727089405A8099406 +:1071D000019400970294CDE903400D461023224606 +:1071E000304606F0ECFF78B90AA806A9019400978A +:1071F0000294CDE90310BDF8143000222946304630 +:1072000006F07BFD002801D0FFF761FD0BB0F0BD5B +:1072100006F00CBC2DE9FC410C468046002602F02D +:10722000E5F9054620780D287ED2DFE800F0BC079E +:1072300013B325BD49496383AF959B00A8480068F7 +:1072400020B1417841F010014170ADE0404602F0BC +:10725000FDF9A9E0042140460CF028FE070000D10A +:10726000FFDF07F11401404605F037FDA5BB1321F0 +:107270004046FDF7CFFB97E0042140460CF016FE98 +:10728000070000D1FFDFE088ADF800000020B881E2 +:107290009DF80000010704D5C00602D5A088B8817A +:1072A00005E09DF8010040067ED5A088F88105B96B +:1072B000FFDF22462946404601F0ACFC022673E07F +:1072C000E188ADF800109DF8011009060FD50728D8 +:1072D00003D006280AD00AE024E0042140460CF03E +:1072E000E5FD060000D1FFDFA088F0810226CDB9C0 +:1072F000FFDF17E0042140460CF0D8FD070000D165 +:10730000FFDF07F1140006F0C8FB90F0010F02D177 +:10731000E079000648D5387C022640F00200387437 +:1073200005B9FFDF224600E03DE02946404601F076 +:1073300071FC39E0042140460CF0B8FD017C002DC1 +:1073400001F00206C1F340016171017C21F00201EC +:107350000174E7D1FFDFE5E702260121404602F094 +:10736000A7F921E0042140460CF0A0FD0546606825 +:1073700000902089ADF8040001226946404602F0E1 +:10738000B8F9287C20F0020028740DE0002DC9D146 +:10739000FFDFC7E7022600214046FBF799F8002DE2 +:1073A000C0D1FFDFBEE7FFDF3046BDE8FC813EB560 +:1073B0000C0009D001466B4601AA002006F084FFAC +:1073C00020B1FFF784FC3EBD10203EBD0020208090 +:1073D000A0709DF8050002A900F00700FEF762FE0C +:1073E00050B99DF8080020709DF8050002A9C0F36F +:1073F000C200FEF757FE08B103203EBD9DF808000D +:1074000060709DF80500C109A07861F30410A070B8 +:107410009DF80510890961F3C300A0709DF8041060 +:10742000890601D5022100E0012161F342009DF8A7 +:10743000001061F30000A07000203EBD70B514463E +:1074400006460D4651EA040005D075B10846F9F725 +:1074500044FF78B901E0072070BD2946304606F0A8 +:107460009AFF10B1BDE8704031E454B12046F9F7FD +:1074700034FF08B1102070BD21463046BDE8704091 +:1074800095E7002070BD2DE9FC5F0C46904605464F +:10749000002701780822007A3E46B2EB111F7DD109 +:1074A00004F10A0100910A31821E4FF0020A04F130 +:1074B000080B0191092A72D2DFE802F0EDE005F530 +:1074C00028287BAACE00688804210CF0EFFC060077 +:1074D00000D1FFDFB08928B152270726C3E00000A2 +:1074E0009402002051271026002C7DD06888A080AF +:1074F0000120A071A88900220099FFF79FFF0028B2 +:1075000073D1A8892081288AE081D1E0B5F8129052 +:10751000072824D1E87B000621D5512709F1140062 +:1075200086B2002CE1D0A88900220099FFF786FFDF +:1075300000285AD16888A08084F806A0A8892081F4 +:107540000120A073288A2082A4F81290A88A0090B3 +:1075500068884B46A969019A01F038FBA8E05027DA +:1075600009F1120086B2002C3ED0A88900225946AB +:10757000FFF764FF002838D16888A080A889E080E0 +:10758000287A072813D002202073288AE081E87B1C +:10759000C0096073A4F81090A88A01E085E082E039 +:1075A000009068884B4604F11202A969D4E70120D3 +:1075B000EAE7B5F81290512709F1140086B2002CC1 +:1075C00066D0688804210CF071FC83466888A0802E +:1075D000A88900220099FFF731FF00286ED184F8B6 +:1075E00006A0A889208101E052E067E00420A07392 +:1075F000288A2082A4F81290A88A009068884B46B6 +:10760000A969019A01F0E2FAA989ABF80E104FE0DE +:107610006888FBF717FF0746688804210CF046FCD2 +:10762000064607B9FFDF06B9FFDF687BC00702D057 +:107630005127142601E0502712264CB36888A080F9 +:10764000502F06D084F806A0287B594601F0CEFAC8 +:107650002EE0287BA11DF9E7FE49A88949898142CE +:1076600005D1542706269CB16888A08020E05327C6 +:107670000BE06888A080A889E08019E06888042170 +:107680000CF014FC00B9FFDF55270826002CF0D1C0 +:10769000A8F8006011E056270726002CF8D068886B +:1076A000A080002013E0FFDF02E0012808D0FFDF08 +:1076B000A8F800600CB1278066800020BDE8FC9F20 +:1076C00057270726002CE3D06888A080687AA0712D +:1076D000EEE7401D20F0030009B14143091D01EB15 +:1076E0004000704713B5DB4A00201071009848B184 +:1076F000002468460CF0F7F9002C02D1D64A009914 +:1077000011601CBD01240020F4E770B50D4614463D +:10771000064686B05C220021284606F0B8FE04B971 +:10772000FFDFA0786874A2782188284601F089FAE2 +:107730000020A881E881228805F11401304605F077 +:10774000B0FA6A460121304606F069FC1AE000BF33 +:107750009DF80300000715D5BDF806103046FFF769 +:107760002DFD9DF80300BDF8061040F010008DF8C7 +:107770000300BDF80300ADF81400FF233046059A5E +:1077800006F0D1FD684606F056FC0028E0D006B0B1 +:1077900070BD10B50C4601F1140005F0BAFA0146AF +:1077A000627C2046BDE8104001F080BA30B5044646 +:1077B000A84891B04FF6FF75C18905AA284606F082 +:1077C0002EFC30E09DF81E00A0422AD001282AD1CC +:1077D000BDF81C00B0F5205F03D042F601018842DD +:1077E00021D1002002AB0AAA0CA9019083E807006E +:1077F00007200090BDF81A1010230022284606F03A +:10780000DEFC38B9BDF828000BAAC0B20CA90EF0F6 +:10781000F8F810B1032011B030BD9DF82E00A04241 +:1078200001D10020F7E705A806F005FC0028C9D023 +:107830000520F0E770B5054604210CF037FB040085 +:1078400000D1FFDF04F114010C46284605F045FA8B +:1078500021462846BDE8704005F046BA70B58AB0AA +:107860000C460646FBF7EEFD050014D028782228CA +:1078700027D30CB1A08890B101208DF80C00032013 +:107880008DF8100000208DF8110054B1A088ADF8DB +:107890001800206807E043F202000AB070BD09201A +:1078A000FBE7ADF818000590042130460CF0FEFA15 +:1078B000040000D1FFDF04F1140005F040FA0007D6 +:1078C00001D40820E9E701F091FE60B108A8022187 +:1078D0000094CDE9011095F8232003A93046636890 +:1078E000FFF7EEFBD9E71120D7E72DE9F04FB2F80B +:1078F00002A0834689B0154689465046FBF7A2FD93 +:107900000746042150460CF0D1FA0026044605969D +:107910004FF002080696ADF81C6007B9FFDF04B906 +:10792000FFDF4146504603F055FF50B907AA06A9AC +:1079300005A88DE807004246214650466368FFF7D8 +:107940004EFB444807AB0660DDE9051204F1140064 +:10795000CDF80090CDE90320CDE9013197F823203F +:10796000594650466B6805F033FA06000AD0022EDD +:1079700004D0032E14D0042E00D0FFDF09B030460F +:10798000BDE8F08FBDF81C000028F7D00599CDE9BF +:1079900000104246214650466368FFF74DFBEDE775 +:1079A000687840F008006870E8E710B50C46FFF70B +:1079B000BFF900280BD1607800F00701012905D13B +:1079C00010F0380F02D02078810601D5072010BDB5 +:1079D00040F0C8002070002010BD2DE9F04F99B094 +:1079E00004464FF000081B48ADF81C80ADF820801D +:1079F000ADF82480A0F80880ADF81480ADF81880A8 +:107A0000ADF82880ADF82C80007916460D46474623 +:107A1000012808D0022806D0032804D0042802D068 +:107A2000082019B0ACE72046F9F713FCF0BB284654 +:107A3000F9F70FFCD0BB6068F9F758FCB0BB606881 +:107A400068B160892189884202D8B1F5007F05D9E3 +:107A50000C20E6E7940200201800002080460EAAC1 +:107A600006A92846FFF7ACF90028DAD168688078C3 +:107A7000C0F34100022808D19DF8190010F0380F1A +:107A800003D02869F9F729FC80B905A92069FFF717 +:107A90004FF90028C5D1206950B1607880079DF862 +:107AA000150000F0380002D5F0B301E011E0D8BBBA +:107AB0009DF8140080060ED59DF8150010F0380FC3 +:107AC00003D06068F9F709FC18B96068F9F70EFC93 +:107AD00008B11020A5E70BA906A8FFF7C9F99DF882 +:107AE0002D000BA920F00700401C8DF82D006069C7 +:107AF000FFF75BFF002894D10AA9A069FFF718F9E6 +:107B000000288ED19DF8280080062BD4A06940B1B2 +:107B10009DF8290000F00701012923D110F0380F4A +:107B200020D0E06828B100E01CE00078D0B11C282B +:107B300018D20FAA611C2046FFF769F901213846C7 +:107B400061F30F2082468DF85210B94642F60300C9 +:107B50000F46ADF850000DF13F0218A928680DF04E +:107B600052FF08B107205CE79DF8600015A9CDF829 +:107B70000090C01CCDE9019100F0FF0B00230BF237 +:107B80000122514614A806F075F9E8BBBDF854006F +:107B90000C90FB482A8929690092CDE901106B8974 +:107BA000BDF838202868069906F064F9010077D1FD +:107BB00020784FF0020AC10601D4800616D58DF850 +:107BC000527042F60210ADF85000CDF80C9008A9A2 +:107BD00003AACDF800A0CDE90121002340F2032241 +:107BE00014A80B9906F046F9010059D1E4484D4616 +:107BF00008380089ADF83D000FA8CDE90290CDF816 +:107C00000490CDF8109000E00CE04FF007095B46BF +:107C10000022CDF80090BDF854104FF6FF7006F02A +:107C20006CF810B1FFF753F8FBE69DF83C00000636 +:107C300024D52946012060F30F218DF852704FF4AE +:107C400024500395ADF8500062789DF80C00002395 +:107C500062F300008DF80C006278CDF800A05208A5 +:107C600062F341008DF80C0003AACDE9012540F232 +:107C7000032214A806F0FEF8010011D1606880B359 +:107C80002069A0B905A906A8FFF7F2F86078800777 +:107C900007D49DF8150020F038008DF8150006E097 +:107CA00077E09DF8140040F040008DF814008DF846 +:107CB000527042F60110ADF85000208940F20121C7 +:107CC000B0FBF1F201FB1202606809ABCDF8008055 +:107CD000CDE90103002314A8059906F0CBF80100B3 +:107CE00057D12078C00728D00395A06950B90AA9B8 +:107CF00006A8FFF7BDF89DF8290020F00700401CFA +:107D00008DF829009DF8280007A940F040008DF863 +:107D100028008DF8527042F60310ADF8500003AA07 +:107D2000CDF800A0CDE90121002340F2032214A8E0 +:107D30000A9906F09FF801002BD1E06868B3294644 +:107D4000012060F30F218DF8527042F60410ADF857 +:107D50005000E068002302788DF8582040788DF8B4 +:107D60005900E06816AA4088ADF85A00E06800792A +:107D70008DF85C00E068C088ADF85D00CDF800903B +:107D8000CDE901254FF4027214A806F073F8010042 +:107D900003D00C9800F0B6FF43E679480321083879 +:107DA000017156B100893080BDF824007080BDF8A3 +:107DB0002000B080BDF81C00F080002031E670B5D6 +:107DC00001258AB016460B46012802D0022816D19A +:107DD00004E08DF80E504FF4205003E08DF80E5063 +:107DE00042F60100ADF80C005BB10024601C60F3AA +:107DF0000F2404AA08A918460DF005FE18B10720A3 +:107E00004BE5102049E504A99DF820205C48CDE908 +:107E10000021801E02900023214603A802F20122C5 +:107E200006F028F810B1FEF752FF36E5544808383E +:107E30000EB1C1883180057100202EE5F0B593B0F8 +:107E4000044601268DF83E6041F601000F46ADF86C +:107E50003C0011AA0FA93046FFF7B1FF002837D127 +:107E60002000474C4FF00005A4F1080432D01C223A +:107E7000002102A806F00BFB9DF808008DF83E607B +:107E800040F020008DF8080042F60520ADF83C00D7 +:107E900004200797ADF82C00ADF8300039480A905F +:107EA0000EA80D900E950FA80990ADF82E506A46B9 +:107EB00009A902A8FFF791FD002809D1BDF800002B +:107EC0006081BDF80400A081401CE0812571002084 +:107ED00013B0F0BD6581A581BDF84400F4E72DE93C +:107EE000F74F2749A0B00024083917940A79A14612 +:107EF000012A04D0022A02D0082023B040E5CA8813 +:107F0000824201D00620F8E721988A46824201D1B8 +:107F10000720F2E70120214660F30F21ADF8480069 +:107F20004FF6FF788DF86E000691ADF84A8042F664 +:107F3000020B8DF872401CA9ADF86CB0ADF8704022 +:107F40001391ADF8508012A806F0A4F800252E4633 +:107F50002F460DAB072212A9404606F09EF898B1B5 +:107F60000A2861D1B5B3AEB3ADF86450ADF8666020 +:107F70009DF85E008DF8144019AC012868D06FE0C0 +:107F80009C020020266102009DF83A001FB30128E0 +:107F900059D1BDF8381059451FD118A809A9019425 +:107FA0000294CDE9031007200090BDF8361010238D +:107FB0000022404606F003F9B0BBBDF8600004287B +:107FC00001D006284AD1BDF82410219881423AD127 +:107FD0000F2092E73AE0012835D1BDF83800B0F51E +:107FE000205F03D042F6010188422CD1BAF8060086 +:107FF000BDF83610884201D1012700E0002705B105 +:108000009EB1219881421ED118A809AA0194029418 +:10801000CDE90320072000900D46102300224046A2 +:1080200006F0CDF800B902E02DE04E460BE0BDF8B9 +:108030006000022801D0102810D1C0B217AA09A9E7 +:108040000DF0DFFC50B9BDF8369082E7052054E70B +:1080500005A917A8221D0DF0D6FC08B103204CE796 +:108060009DF814000023001DC2B28DF81420229840 +:108070000092CDE901401BA8069905F0FBFE10B95E +:1080800002228AF80420FEF722FE36E710B50B46DE +:10809000401E88B084B205AA00211846FEF7B7FE3C +:1080A00000200DF1080C06AA05A901908CE8070034 +:1080B000072000900123002221464FF6FF7005F0B3 +:1080C0001CFE0446BDF81800012800D0FFDF204642 +:1080D000FEF7FDFD08B010BDF0B5FF4F044687B0B8 +:1080E00038790E46032804D0042802D0082007B0AF +:1080F000F0BD04AA03A92046FEF762FE0500F6D1F2 +:1081000060688078C0F3410002280AD19DF80D0014 +:1081100010F0380F05D02069F9F7DFF808B110200A +:10812000E5E7208905AA21698DE807006389BDF884 +:1081300010202068039905F09DFE10B1FEF7C7FDE1 +:10814000D5E716B1BDF814003080042038712846F8 +:10815000CDE7F8B50C0006460BD001464FF6FF758B +:1081600000236A46284606F0AFF820B1FEF7AFFDBF +:10817000F8BD1020F8BD69462046FEF7D9FD00285D +:10818000F8D1A078314600F001032846009A06F0A5 +:10819000CAF8EBE730B587B0144600220DF1080CA1 +:1081A00005AD01928CE82C00072200920A46014698 +:1081B00023884FF6FF7005F0A0FDBDF81410218054 +:1081C000FEF785FD07B030BD70B50D4604210BF0FC +:1081D0006DFE040000D1FFDF294604F11400BDE864 +:1081E000704004F0A5BD70B50D4604210BF05EFE95 +:1081F000040000D1FFDF294604F11400BDE87040FF +:1082000004F0B9BD70B50D4604210BF04FFE04001B +:1082100000D1FFDF294604F11400BDE8704004F0EE +:10822000D1BD70B5054604210BF040FE040000D11D +:10823000FFDF214628462368BDE870400122FEF793 +:1082400015BF70B5064604210BF030FE040000D1C6 +:10825000FFDF04F1140004F05CFD401D20F0030575 +:1082600011E0011D00880022431821463046FEF728 +:10827000FDFE00280BD0607CABB2684382B2A068E0 +:10828000011D0BF0D0FCA06841880029E9D170BD28 +:1082900070B5054604210BF009FE040000D1FFDF94 +:1082A000214628466368BDE870400222FEF7DEBE24 +:1082B00070B50E46054601F099F9040000D1FFDFC4 +:1082C0000120207266726580207820F00F00001D6A +:1082D00020F0F00040302070BDE8704001F089B916 +:1082E00010B50446012900D0FFDF2046BDE810404C +:1082F0000121FAF7EDB82DE9F04F97B04FF0000AE1 +:108300000C008346ADF814A0D04619D0E06830B117 +:10831000A068A8B10188ADF81410A0F800A05846D4 +:10832000FBF790F8070043F2020961D03878222861 +:108330005CD3042158460BF0B9FD050005D103E0DC +:10834000102017B0BDE8F08FFFDF05F1140004F036 +:10835000E0FC401D20F00306A078012803D002288D +:1083600001D00720EDE7218807AA584605F057FEFF +:1083700030BB07A805F05FFE10BB07A805F05BFE49 +:1083800048B99DF82600012805D1BDF82400A0F5C4 +:108390002451023902D04FF45050D2E7E068B0B116 +:1083A000CDE902A00720009005AACDF804A0049210 +:1083B000A2882188BDF81430584605F09EFC10B103 +:1083C000FEF785FCBDE7A168BDF8140008809DF8A4 +:1083D0001F00C00602D543F20140B2E70B9838B146 +:1083E000A1780078012905D080071AD40820A8E7D1 +:1083F0004846A6E7C007F9D002208DF83C00A868DF +:108400004FF00009A0B1697C4288714391420FD9B5 +:108410008AB2B3B2011D0BF0BCFB8046A0F800A0ED +:1084200006E003208DF83C00D5F800804FF00109EC +:108430009DF8200010F0380F00D1FFDF9DF82000DC +:108440002649C0F3C200084497F8231010F8010C25 +:10845000884201D90F2074E72088ADF8400014A9A4 +:108460000095CDE90191434607220FA95846FEF732 +:1084700027FE002891D19DF8500050B9A07801281E +:1084800007D1687CB3B2704382B2A868011D0BF0BB +:1084900094FB002055E770B5064615460C46084685 +:1084A000FEF7D4FB002805D12A4621463046BDE818 +:1084B000704084E470BD12E570B51E4614460D0090 +:1084C0000ED06CB1616859B160B10349C98881426D +:1084D00008D0072070BD000094020020296102002E +:1084E0001020F7E72068FEF7B1FB0028F2D13246F2 +:1084F00021462846BDE87040FFF76FBA70B51546B3 +:108500000C0006D038B1FE490989814203D007200A +:10851000E0E71020DEE72068FEF798FB0028D9D1BD +:1085200029462046BDE87040D6E570B5064686B0BF +:108530000D4614461046F8F7B2FED0BB6068F8F757 +:10854000D5FEB0BBA6F57F40FF3803D03046FAF722 +:1085500079FF80B128466946FEF7ACFC00280CD1B3 +:108560009DF810100F2008293DD2DFE801F0080621 +:108570000606060A0A0843F2020006B0AAE703202C +:10858000FBE79DF80210012908D1BDF80010B1F5F4 +:10859000C05FF2D06FF4C052D142EED09DF8061009 +:1085A00001290DD1BDF80410A1F52851062907D2E3 +:1085B00000E029E0DFE801F0030304030303DCE744 +:1085C0009DF80A1001290FD1BDF80810B1F5245FFC +:1085D000D3D0A1F60211B1F50051CED00129CCD0F3 +:1085E000022901D1C9E7FFDF606878B9002305AA35 +:1085F0002946304605F068FE10B1FEF768FBBCE77F +:108600009DF81400800601D41020B6E76188224648 +:1086100028466368FFF7BEFDAFE72DE9F0438146CA +:1086200087B0884614461046F8F739FE18B1102076 +:1086300007B0BDE8F083002306AA4146484605F08E +:1086400043FE10B1FEF743FBF2E79DF81800C006A9 +:1086500002D543F20140EBE70025072705A8019565 +:1086600000970295CDE9035062884FF6FF734146AB +:10867000484605F0A4FD060013D16068F8F70FFE28 +:1086800060B960680195CDE9025000970495238890 +:1086900062884146484605F092FD0646BDF8140042 +:1086A00020803046CEE739B1954B0A889B899A42A3 +:1086B00002D843F2030070471DE610B586B0904C17 +:1086C0000423ADF81430638943B1A4898C4201D2EC +:1086D000914205D943F2030006B010BD0620FBE726 +:1086E000ADF81010002100910191ADF80030022189 +:1086F0008DF8021005A9029104A90391ADF812208A +:108700006946FFF7F8FDE7E72DE9FC4781460D468E +:108710000846F8F79EFD88BB4846FAF793FE5FEAE5 +:1087200000080AD098F80000222829D304214846DE +:108730000BF0BCFB070005D103E043F20200BDE8EB +:10874000FC87FFDF07F1140004F0F9FA06462878E9 +:10875000012803D0022804D00720F0E7B0070FD586 +:1087600002E016F01C0F0BD0A8792C1DC00709D011 +:10877000E08838B1A068F8F76CFD18B11020DEE78A +:108780000820DCE721882A780720B1F5847F35D0DE +:108790001EDC40F20315A1F20313A94226D00EDC21 +:1087A000B1F5807FCBD003DCF9B1012926D1C6E732 +:1087B000A1F58073013BC2D0012B1FD113E0012B27 +:1087C000BDD0022B1AD0032BB9D0042B16D112E046 +:1087D000A1F20912082A11D2DFE802F00B040410FA +:1087E00010101004ABE7022AA9D007E0012AA6D096 +:1087F00004E0320700E0F206002AA0DACDB200F071 +:10880000F5FE50B198F82300CDE90005FA8923461A +:1088100039464846FEF79FFC91E711208FE72DE986 +:10882000F04F8BB01F4615460C4683460026FAF7DC +:1088300009FE28B10078222805D208200BB081E576 +:1088400043F20200FAE7B80801D00720F6E7032F49 +:1088500000D100274FF6FF79CCB1022D71D320460D +:10886000F8F744FD30B904EB0508A8F10100F8F76A +:108870003DFD08B11020E1E7AD1E38F8028CAAB228 +:108880002146484605F081FE40455AD1ADB21C490B +:10889000B80702D58889401C00E001201FFA80F843 +:1088A000F80701D08F8900E04F4605AA4146584697 +:1088B00005F0B5FB4FF0070A4FF00009FCB1204668 +:1088C00008E0408810283CD8361D304486B2AE42BD +:1088D00037D2A01902884245F3D352E09DF8170021 +:1088E00002074ED57CB304EB0608361DB8F80230FB +:1088F000B6B2102B25D89A19AA4222D802E040E03D +:1089000094020020B8F8002091421AD1C0061BD56D +:10891000CDE900A90DF1080C0AAAA11948468CE876 +:108920000700B8F800100022584605F0E6F910B12B +:10893000FEF7CDF982E7B8F80200BDF828108842AA +:1089400002D00B207AE704E0B8F80200304486B287 +:1089500006E0C00604D55846FEF730FC00288AD150 +:108960009DF81700BDF81A1020F010008DF81700C0 +:10897000BDF81700ADF80000FF235846009A05F037 +:10898000D2FC05A805F057FB18B9BDF81A10B9427A +:10899000A4D9042158460BF089FA040000D1FFDF66 +:1089A000A2895AB1CDE900A94D4600232146584677 +:1089B000FEF7D1FB0028BDD1A5813FE700203DE7B0 +:1089C0002DE9FF4F8BB01E4617000D464FF00004F7 +:1089D00012D0B00802D007200FB0B3E4032E00D1AC +:1089E00000265DB10846F8F778FC28B93888691E7A +:1089F0000844F8F772FC08B11020EDE7C74AB00749 +:108A000001D5D18900E00121F0074FF6FF7802D0AF +:108A1000D089401E00E0404686B206AA0B9805F0B9 +:108A2000FEFA4FF000094FF0070B0DF1140A38E081 +:108A30009DF81B00000734D5CDF80490CDF800B0A8 +:108A4000CDF80890CDE9039A434600220B9805F033 +:108A5000B6FB60BB05B3BDF814103A882144281951 +:108A6000091D8A4230D3BDF81E2020F8022BBDF824 +:108A7000142020F8022BCDE900B9CDE90290CDF801 +:108A800010A0BDF81E10BDF8143000220B9805F0A0 +:108A900096FB08B103209FE7BDF814002044001D99 +:108AA00084B206A805F0C7FA20B10A2806D0FEF75E +:108AB0000EF991E7BDF81E10B142B9D934B17DB1BC +:108AC0003888A11C884203D20C2085E7052083E763 +:108AD00022462946404605F058FD014628190180E6 +:108AE000A41C3C80002077E710B50446F8F7D7FBBC +:108AF00008B1102010BD8948C0892080002010BD19 +:108B0000F0B58BB00D4606461422002103A805F0EF +:108B1000BEFC01208DF80C008DF8100000208DF8AF +:108B20001100ADF814503046FAF78CFC48B10078CB +:108B3000222812D3042130460BF0B8F9040005D1E5 +:108B400003E043F202000BB0F0BDFFDF04F11400BC +:108B5000074604F0F4F8800601D40820F3E7207CEF +:108B6000022140F00100207409A80094CDE9011011 +:108B7000072203A930466368FEF7A2FA20B1217CE0 +:108B800021F001012174DEE729463046F9F791FC16 +:108B900008A9384604F0C2F800B1FFDFBDF8204054 +:108BA000172C01D2172000E02046A84201D92C46FC +:108BB00002E0172C00D2172421463046FFF713FBA2 +:108BC00021463046F9F799F90020BCE7F8B51C4674 +:108BD00015460E46069F0BF09AFA2346FF1DBCB2BF +:108BE00031462A4600940AF086FEF8BD70B50C4660 +:108BF00005460E220021204605F049FC0020208079 +:108C00002DB1012D01D0FFDF64E4062000E0052036 +:108C1000A0715FE410B548800878134620F00F007B +:108C2000001D20F0F00080300C4608701422194618 +:108C300004F1080005F001FC00F0DBFC374804609B +:108C400010BD2DE9F047DFF8D890491D064621F008 +:108C5000030117460C46D9F800000AF062FF050030 +:108C600000D1FFDF4FF000083560A5F800802146F5 +:108C7000D9F800000AF055FF050000D1FFDF75604C +:108C8000A5F800807FB104FB07F1091D0BD0D9F8CE +:108C900000000AF046FF040000D1FFDFB460C4F812 +:108CA0000080BDE8F087C6F80880FAE72DE9F041BA +:108CB0001746491D21F00302194D06460168144666 +:108CC00028680AF059FF2246716828680AF054FFA4 +:108CD0003FB104FB07F2121D03D0B16828680AF007 +:108CE0004BFF04200BF08AF8044604200BF08EF8AA +:108CF000201A012804D12868BDE8F0410AF006BF17 +:108D0000BDE8F08110B50C4605F058F900B1FFDF61 +:108D10002046BDE81040FDF7DABF000094020020B5 +:108D20001800002038B50C468288817B19B1418932 +:108D3000914200D90A462280C188121D90B26A462B +:108D40000AF0B2F8BDF80000032800D30320C1B236 +:108D5000208801F020F838BD38B50C468288817B28 +:108D600019B10189914200D90A462280C188121D99 +:108D700090B26A460AF098F8BDF80000022800D3C5 +:108D80000220C1B2208801F006F8401CC0B238BDF4 +:108D90002DE9FF5F82468B46F74814460BF103022C +:108DA000D0E90110CDE9021022F0030201A84FF42E +:108DB000907101920AF097FEF04E002C02D1F0491A +:108DC000019A8A60019901440191B57F05F101057D +:108DD00004D1E8B20CF098FD00B1FFDF019800EB80 +:108DE0000510C01C20F0030101915CB9707AB27AC1 +:108DF0001044C2B200200870B08C80B204F03DFF75 +:108E000000B1FFDF0198716A08440190214601A872 +:108E100000F084FF80460198C01C20F00300019000 +:108E2000B37AF27A717A04B100200AF052FF019904 +:108E300008440190214601A800F0B8FFCF48002760 +:108E40003D4690F801900CE0284600F04AFF0646A7 +:108E500081788088F9F7E8F871786D1C00FB01775C +:108E6000EDB24D45F0D10198C01C20F003000190F7 +:108E700004B100203946F9F7E2F8019900270844C7 +:108E80000190BE483D4690F801900CE0284600F065 +:108E900028FF0646C1788088FEF71BFC71786D1CA0 +:108EA00000FB0177EDB24D45F0D10198C01C20F0D8 +:108EB0000300019004B100203946FEF713FC01992C +:108EC0004FF0000908440190AC484D4647780EE049 +:108ED000284600F006FF0646807B30B106F1080008 +:108EE00002F09CF9727800FB02996D1CEDB2BD4254 +:108EF000EED10198C01C20F00300019004B10020C5 +:108F00009F494A78494602F08DF901990844019039 +:108F1000214601A800F0B8FE0198C01D20F007000E +:108F20000190DAF80010814204D3A0EB0B01B1F5F7 +:108F3000803F04DB4FF00408CAF8000004E0CAF8E0 +:108F40000000B8F1000F03D0404604B0BDE8F09F28 +:108F500084BB8C490020019A0EF044FEFBF714FA02 +:108F6000864C207F0090607F012825D0002328B305 +:108F70000022824800211030F8F73AFA00B1FFDFF2 +:108F80007E49E07F2031FEF759FF00B1FFDF7B48CB +:108F90004FF4F6720021443005F079FA7748042145 +:108FA000443080F8E91180F8EA11062180F8EB11CD +:108FB000032101710020C8E70123D8E702AAD8E7FE +:108FC00070B56E4C06464434207804EB4015E078CA +:108FD000083598B9A01990F8E80100280FD0A078BA +:108FE0000F2800D3FFDF20220021284605F04FFA8A +:108FF000687866F3020068700120E070284670BD52 +:109000002DE9F04105460C460027007805219046E1 +:109010003E46B1EB101F00D0FFDF287A50B1012887 +:109020000ED0FFDFA8F800600CB12780668000201A +:10903000BDE8F0810127092674B16888A08008E0A6 +:109040000227142644B16888A0802869E060A88AB5 +:109050002082287B2072E5E7A8F80060E7E730B5BA +:10906000464C012000212070617020726072032242 +:10907000A272E07261772177217321740521218327 +:109080001F216183607440A161610A21A177E077AB +:1090900039483B4DB0F801102184C07884F8220093 +:1090A0004FF4B06060626868C11C21F00301814226 +:1090B00000D0FFDF6868606030BD30B5304C1568A7 +:1090C000636810339D4202D20420136030BD2B4BE5 +:1090D0005D785A6802EB0512107051700320D08041 +:1090E000172090800120D0709070002090735878E5 +:1090F000401C5870606810306060002030BD70B552 +:1091000006461E480024457807E0204600F0E9FDA9 +:109110000178B14204D0641CE4B2AC42F5D1002025 +:1091200070BDF7B5074608780C4610B3FFF7E7FFA8 +:109130000546A7F12006202F06D0052E19D2DFE81C +:1091400006F00F383815270000F0D6FD0DB169780C +:1091500000E00021401AA17880B20844FF2808D816 +:10916000A07830B1A088022831D202E060881728A8 +:109170002DD20720FEBD000030610200B0030020A8 +:109180001C000020000000206E52463578000000D0 +:10919000207AE0B161881729EBD3A1881729E8D399 +:1091A000A1790029E5D0E1790029E2D0402804D94D +:1091B000DFE7242F0BD1207A48B161884FF6FB708E +:1091C000814202D8A188814201D90420D2E765B941 +:1091D000207802AA0121FFF770FF0028CAD1207869 +:1091E000FFF78DFF050000D1FFDF052E18D2DFE865 +:1091F00006F0030B0E081100A0786870A088E880C4 +:109200000FE06088A8800CE0A078A87009E0A07842 +:10921000E87006E054F8020FA8606068E86000E0BB +:10922000FFDF0020A6E71A2835D00DDC132832D244 +:10923000DFE800F01B31203131272723252D313184 +:1092400029313131312F0F00302802D003DC1E28A4 +:1092500021D1072070473A3809281CD2DFE800F0F6 +:10926000151B0F1B1B1B1B1B07000020704743F225 +:109270000400704743F202007047042070470D203D +:1092800070470F2070470820704711207047132047 +:109290007047062070470320704710B5007800F033 +:1092A000010009F0F3FDBDE81040BCE710B50078FF +:1092B00000F0010009F0F3FDBDE81040B3E70EB582 +:1092C000017801F001018DF80010417801F00101F1 +:1092D0008DF801100178C1F340018DF8021041783A +:1092E000C1F340018DF80310017889088DF804104E +:1092F000417889088DF8051081788DF80610C178BD +:109300008DF8071000798DF80800684608F0FDFD1B +:10931000FFF789FF0EBD2DE9FC5FDFF8F883FE4CF7 +:1093200000264FF490771FE0012000F082FD01201D +:10933000FFF746FE05463946D8F808000AF0F1FB6B +:10934000686000B9FFDF686808F0AAFCB0B1284681 +:10935000FAF75EFB284600F072FD28B93A466968C4 +:10936000D8F808000AF008FC94F9E9010428DBDACF +:1093700002200AF043FD07460025AAE03A46696844 +:10938000D8F808000AF0F8FBF2E7B8F802104046F7 +:10939000491C89B2A8F80210B94201D300214180CA +:1093A0000221B8F802000AF081FD002866D0B8F862 +:1093B0000200694609F0CFFCFFF735FF00B1FFDF7F +:1093C0009DF80000019078B1B8F802000AF0B1FEF3 +:1093D0005FEA000900D1FFDF48460AF020F918B122 +:1093E000B8F8020002F0E4F9B8F802000AF08FFEC3 +:1093F0005FEA000900D1FFDF48460AF008F9E8BB40 +:109400000321B8F802000AF051FD5FEA000B4BD1CE +:10941000FFDF49E0DBF8100010B10078FF284DD0E5 +:10942000022000F006FD0220FFF7CAFD82464846F2 +:109430000AF0F9F9CAF8040000B9FFDFDAF804000D +:109440000AF0C1FA002100900170B8F802105046ED +:10945000AAF8021002F0B2F848460AF0B6FA00B9CB +:10946000FFDF019800B10126504600F0E8FC18B972 +:109470009AF80100000705D5009800E027E0CBF836 +:10948000100011E0DBF8101039B10878401C10F022 +:10949000FF00087008D1FFDF06E0002211464846B1 +:1094A00000F0F0FB00B9FFDF94F9EA01022805DBC8 +:1094B000B8F8020002F049F80028ABD194F9E901AC +:1094C000042804DB48460AF0E4FA00B101266D1CCA +:1094D000EDB2BD4204D294F9EA010228BFF655AFBD +:1094E000002E7FF41DAFBDE8FC5F032000F0A1BC9F +:1094F00010B5884CE06008682061AFF2E510F9F71C +:10950000E4FC607010BD844800214438017081483B +:10951000017082494160704770B505464FF0805038 +:109520000C46D0F8A410491C05D1D0F8A810C943A6 +:109530000904090C0BD050F8A01F01F0010129709B +:10954000416821608068A080287830B970BD06210C +:1095500020460DF0CCFF01202870607940F0C0005B +:10956000607170BD70B54FF080540D46D4F8801016 +:10957000491C0BD1D4F88410491C07D1D4F88810A9 +:10958000491C03D1D4F88C10491C0CD0D4F880109D +:109590000160D4F884104160D4F888108160D4F858 +:1095A0008C10C16002E010210DF0A1FFD4F89000F2 +:1095B000401C0BD1D4F89400401C07D1D4F898007B +:1095C000401C03D1D4F89C00401C09D054F8900FE3 +:1095D000286060686860A068A860E068E86070BDA6 +:1095E0002846BDE8704010210DF081BF4A4800793F +:1095F000E6E470B5484CE07830B3207804EB4010D6 +:10960000407A00F00700204490F9E801002800DCCF +:10961000FFDF2078002504EB4010407A00F00700BF +:10962000011991F8E801401E81F8E8012078401CFA +:10963000C0B220700F2800D12570A078401CA07007 +:109640000DF0D4FDE57070BDFFDF70BD3EB5054681 +:1096500003210AF02BFC044628460AF058FD054673 +:1096600004B9FFDF206918B10078FF2800D1FFDFBF +:1096700001AA6946284600F005FB60B9FFDF0AE051 +:10968000002202A9284600F0FDFA00B9FFDF9DF88C +:10969000080000B1FFDF9DF80000411E8DF80010AA +:1096A000EED220690199884201D1002020613EBD9F +:1096B00070B50546A0F57F400C46FF3800D1FFDFAE +:1096C000012C01D0FFDF70BDFFF790FF040000D137 +:1096D000FFDF207820F00F00401D20F0F000503018 +:1096E000207065800020207201202073BDE870404A +:1096F0007FE72DE9F04116460D460746FFF776FF56 +:10970000040000D1FFDF207820F00F00401D20F082 +:10971000F00005E01C000020F403002048140020A5 +:109720005030207067800120207228682061A8884E +:10973000A0822673BDE8F0415BE77FB5FFF7DFFC51 +:10974000040000D1FFDF02A92046FFF7EBFA05462F +:1097500003A92046FFF700FB8DF800508DF80100AB +:10976000BDF80800001DADF80200BDF80C00001D9A +:10977000ADF80400E088ADF80600684609F070FB1B +:10978000002800D0FFDF7FBD2DE9F05FFC4E814651 +:10979000307810B10820BDE8F09F4846F7F77FFD0C +:1097A00008B11020F7E7F74C207808B9FFF757FC0D +:1097B000A17A607A4D460844C4B200F09DFAA042F6 +:1097C00007D2201AC1B22A460020FFF776FC0028F3 +:1097D000E1D17168EB48C91C002721F003017160D9 +:1097E000B3463E463D46BA463C4690F801800AE004 +:1097F000204600F076FA4178807B0E4410FB01553C +:10980000641CE4B27F1C4445F2D10AEB870000EBF4 +:10981000C600DC4E00EB85005C46F17A012200EBCD +:109820008100DBF80410451829464846FFF7B0FAD6 +:10983000070012D00020FFF762FC05000BD005F1F5 +:109840001300616820F00300884200D0FFDF7078C9 +:10985000401E7070656038469DE7002229464846E4 +:10986000FFF796FA00B1FFDFD9F8000060604FF60D +:10987000FF7060800120207000208CE72DE9F0410E +:109880000446BF4817460D46007810B10820BDE8D1 +:10989000F0810846F7F7DDFC08B11020F7E7B94E74 +:1098A000307808B9FFF7DBFB601E1E2807D8012CB3 +:1098B00023D12878FE2820D8B0770020E7E7A4F14C +:1098C00020001F2805D8E0B23A462946BDE8F041FD +:1098D00027E4A4F140001F2805D829462046BDE80A +:1098E000F04100F0D4BAA4F1A0001F2805D8294601 +:1098F0002046BDE8F04100F006BB0720C7E72DE990 +:10990000F05F81460F460846F7F7C9FC48B948465C +:10991000F7F7E3FC28B909F1030020F003014945FA +:1099200001D0102037E797484FF0000B4430817882 +:1099300069B14178804600EB411408343E883A46CC +:109940000021204600F089FA050004D027E0A7F89E +:1099500000B005201FE7B9F1000F24D03888B042CD +:1099600001D90C251FE0607800F00700824600F066 +:1099700060FA08EB0A063A4696F8E8014946401CA8 +:1099800086F8E801204600F068FA054696F8E801F6 +:10999000401E86F8E801032000F04BFA2DB10C2D93 +:1099A00001D0A7F800B02846F5E6754F5046BAF149 +:1099B000010F25D002280DD0BAF1030F35D0FFDFFB +:1099C00098F801104046491CC9B288F801100F29C7 +:1099D00037D038E0606828B16078000702D460882A +:1099E000FFF734FE98F8EA014446012802D178785E +:1099F000F9F78AFA94F9EA010428E1DBFFDFDFE7EF +:109A0000616821B14FF49072B8680AF0B5F898F81F +:109A1000E9014446032802D17878F9F775FA94F9F8 +:109A2000E9010428CCDBFFDFCAE76078C00602D575 +:109A30006088FFF70BFE98F9EB010628C0DBFFDF1B +:109A4000BEE780F801B08178491E88F8021096F8C8 +:109A5000E801401C86F8E801A5E770B50C4605460C +:109A6000F7F7F7FB18B92046F7F719FC08B11020F3 +:109A700070BD28460BF07FFF207008B1002070BD3C +:109A8000042070BD70B505460BF08EFFC4B22846A9 +:109A9000F7F723FC08B1102070BD35B128782C7081 +:109AA00018B1A04201D0072070BD2046FDF77EFE10 +:109AB000052805D10BF07BFF012801D0002070BDE7 +:109AC0000F2070BD70B5044615460E460846F7F7E0 +:109AD000C0FB18B92846F7F7E2FB08B1102070BDAB +:109AE000022C03D0102C01D0092070BD2A4631462B +:109AF00020460BF086FF0028F7D0052070BD70B51A +:109B000014460D460646F7F7A4FB38B92846F7F782 +:109B1000C6FB18B92046F7F7E0FB08B1102070BD6E +:109B20002246294630460BF06EFF0028F7D007206A +:109B300070BD3EB50446F7F7B2FB08B110203EBD3C +:109B4000684608F053F9FFF76EFB0028F7D19DF83F +:109B500006002070BDF808006080BDF80A00A080F3 +:109B600000203EBD70B505460C460846F7F7B5FB2C +:109B700020B95CB12068F7F792FB28B1102070BDC6 +:109B80001C000020B0030020A08828B121462846F0 +:109B9000BDE87040FDF762BE0920F0E770B50546EC +:109BA0000C460846F7F755FBA0BB681E1E280ED8CA +:109BB000032D01D90720E2E705B9FFDFFE4800EBDE +:109BC000850050F8041C2046BDE870400847A5F108 +:109BD00020001F2805D821462846BDE87040FAF726 +:109BE00042BBA5F160001F2805D821462846BDE8E4 +:109BF0007040F8F7DABCF02D0DD0F12D15D0BF2D47 +:109C0000D8D1A078218800F0010001F08DFB98B137 +:109C10000020B4E703E0A068F7F71BFB08B11020B1 +:109C2000ADE7204609F081F902E0207809F0A0F9BB +:109C3000BDE87040FFF7F7BA0820A0E770B504460A +:109C40000D460846F7F72BFB30B9601E1E280FD8CB +:109C50002846F7F7FEFA08B1102090E7012C03D050 +:109C6000022C01D0032C01D1062088E7072086E7CB +:109C7000A4F120001F28F9D829462046BDE87040ED +:109C8000FAF762BB09F092BC38B50446CB48007BBA +:109C900000F00105F9B904F01DFC0DB1226800E0E7 +:109CA0000022C7484178C06807F06DFDC4481030F5 +:109CB000C0788DF8000010B1012802D004E0012026 +:109CC00000E000208DF80000684608F0FFF8BA4870 +:109CD000243808F0B5FE002D02D02068283020601E +:109CE00038BD30B5B54D04466878A04200D8FFDFD6 +:109CF000686800EB041030BD70B5B04800252C46F4 +:109D0000467807E02046FFF7ECFF4078641C2844C3 +:109D1000C5B2E4B2B442F5D1284630E72DE9F041AE +:109D20000C4607464FF0000800F01FF90646FF28D2 +:109D300001D94FF013083868C01C20F003023A60C4 +:109D400054EA080421D19D48F3B2072128300DF0D0 +:109D5000DBFD09E0072C10D2DFE804F00604080858 +:109D60000A040600974804E0974802E0974800E09C +:109D700097480DF0E9FD054600E0FFDFA54200D061 +:109D8000FFDF641CE4B2072CE4D3386800EB061054 +:109D9000386040467BE5021D5143452900D24521EC +:109DA0000844C01CB0FBF2F0C0B270472DE9FC5F64 +:109DB000064682484FF000088B464746444690F8D6 +:109DC000019022E02046FFF78CFF050000D1FFDF65 +:109DD000687869463844C7B22846FEF7A3FF824632 +:109DE00001A92846FEF7B8FF0346BDF80400524615 +:109DF000001D81B2BDF80000001D80B20AF0D4F849 +:109E00006A78641C00FB0288E4B24C45DAD1306801 +:109E1000C01C20F003003060BBF1000F00D0002018 +:109E2000424639460AF0CEF8316808443060BDE851 +:109E3000FC9F6249443108710020C87070475F4937 +:109E40004431CA782AB10A7801EB421108318142C3 +:109E500001D001207047002070472DE9F0410646EF +:109E60000078154600F00F0400201080601E0F4699 +:109E7000052800D3FFDF50482A46183800EB84003D +:109E8000394650F8043C3046BDE8F04118472DE90A +:109E9000F0414A4E0C46402806D0412823D04228A3 +:109EA0002BD0432806D123E0A07861780D18E17803 +:109EB000814201D90720EAE42078012801D9132042 +:109EC000E5E4FF2D08D80BF009FF07460DF046F931 +:109ED000381A801EA84201DA1220D8E42068B06047 +:109EE000207930730DE0BDE8F041084600F078B805 +:109EF00008780228DED8307703E008780228D9D81D +:109F000070770020C3E4F8B500242C4DA02805D0BC +:109F1000A12815D0A22806D00720F8BD087800F0A7 +:109F20000100E8771FE00E4669463046FDF73DFD2B +:109F30000028F2D130882884B07885F8220012E019 +:109F400008680921F82801D3820701D00846F8BD26 +:109F50006A7C02F00302012A04D16A8BD73293B2E1 +:109F60008342F3D868622046F8BD2DE9F047DFF858 +:109F70004C900026344699F8090099F80A2099F87F +:109F800001700244D5B299F80B20104400F0FF088C +:109F900008E02046FFF7A5FE817B407811FB0066B4 +:109FA000641CE4B2BC42F4D199F8091099F80A0093 +:109FB0002944294441440DE054610200B0030020CB +:109FC0001C0000206741000045B30000DD2F0000A9 +:109FD000FB56010000B1012008443044BDE8F08781 +:109FE00038B50446407800F00300012803D0022869 +:109FF0000BD0072038BD606858B1F7F777F9D0B9B2 +:10A000006068F7F76AF920B915E06068F7F721F999 +:10A0100088B969462046FCF729F80028EAD160781B +:10A0200000F00300022808D19DF8000028B1606804 +:10A03000F7F753F908B1102038BD6189F8290DD818 +:10A04000208988420AD8607800F003020A48012A71 +:10A0500006D1D731426A89B28A4201D2092038BD7D +:10A0600094E80E0000F1100585E80E000AB9002101 +:10A070000183002038BD0000B00300202DE9F0412D +:10A08000074614468846084601F08AFD064608EB56 +:10A0900088001C22796802EBC0000D18688C58B14A +:10A0A0004146384601F08BFD014678680078C200D1 +:10A0B000082305F120000CE0E88CA8B141463846A1 +:10A0C00001F084FD0146786808234078C20005F15C +:10A0D000240009F0A8FD38B1062121726681D0E97B +:10A0E0000010C4E9031009E0287809280BD00520E6 +:10A0F000207266816868E060002028702046BDE814 +:10A10000F04101F02EBD072020726681F4E72DE9B1 +:10A11000F04116460D460746406801EB85011C22BA +:10A1200002EBC1014418204601F072FD40B100214C +:10A13000708865F30F2160F31F4106200DF0BEFC0F +:10A1400009202070324629463846BDE8F04195E79F +:10A150002DE9F0410E46074600241C21F07816E058 +:10A1600004EB8403726801EBC303D25C6AB1FFF7AE +:10A170003DFA050000D1FFDF6F802A4621463046B8 +:10A18000FFF7C5FF0120BDE8F081641CE4B2A042E6 +:10A19000E6D80020F7E770B5064600241C21C078F9 +:10A1A0000AE000BF04EB8403726801EBC303D51817 +:10A1B0002A782AB1641CE4B2A042F3D8402070BDD2 +:10A1C00028220021284604F062F9706880892881DD +:10A1D000204670BD70B5034600201C25DC780CE0DD +:10A1E00000EB80065A6805EBC6063244167816B1B5 +:10A1F000128A8A4204D0401CC0B28442F0D8402067 +:10A2000070BDF0B5044600201C26E5780EE000BFC6 +:10A2100000EB8007636806EBC7073B441F788F425B +:10A2200002D15B78934204D0401CC0B28542EFD883 +:10A230004020F0BD0078032801D0002070470120A5 +:10A2400070470078022801D0002070470120704735 +:10A250000078072801D000207047012070472DE9C1 +:10A26000F041064688461078F1781546884200D3BA +:10A27000FFDF2C781C27641CF078E4B2A04201D8E0 +:10A28000201AC4B204EB8401706807EBC1010844D2 +:10A29000017821B14146884708B12C7073E72878CE +:10A2A000A042E8D1402028706DE770B514460B88B5 +:10A2B0000122A240134207D113430B8001230A223B +:10A2C000011D09F07AFC047070BD2DE9FF4F81B0CB +:10A2D0000878DDE90E7B9A4691460E4640072CD45D +:10A2E000019809F026FF040000D1FFDF07F1040800 +:10A2F00020461FFA88F109F065F8050000D1FFDF5C +:10A30000204629466A4609F0B0FA0098A0F8037082 +:10A31000A0F805A0284609F056FB017869F306016C +:10A320006BF3C711017020461FFA88F109F08DF810 +:10A3300000B9FFDF019807F094F906EB0900017FEF +:10A34000491C017705B0BDE8F08F2DE9F84F0E46A6 +:10A350009A4691460746032109F0A8FD0446008D60 +:10A36000DFF8B885002518B198F80000B0421ED17A +:10A37000384609F0DEFE070000D1FFDF09F10401D5 +:10A38000384689B209F01EF8050010D03846294633 +:10A390006A4609F06AFA009800210A460180817035 +:10A3A00007F01CFA0098C01DCAF8000021E098F8D8 +:10A3B0000000B04216D104F1260734F8341F012002 +:10A3C00000FA06F911EA090F00D0FFDF2088012307 +:10A3D00040EA090020800A22391D384609F008FCAD +:10A3E000067006E0324604F1340104F12600FFF75E +:10A3F0005CFF0A2188F800102846BDE8F88FFEB5FA +:10A4000015460C46064602AB0C220621FFF79DFFBF +:10A41000002827D00299607812220A70801C4870A8 +:10A4200008224A80A07002982988052381806988C3 +:10A43000C180A9880181E988418100250C20CDE9EE +:10A440000005062221463046FFF73FFF294600223D +:10A4500066F31F41F02310460DF086FA6078801CE9 +:10A4600060700120FEBDFEB514460D46062206466C +:10A4700002AB1146FFF769FF002812D0029B1320A0 +:10A4800000211870A8785870022058809C800620FF +:10A49000CDE900010246052329463046FFF715FFA6 +:10A4A0000120FEBD2DE9FE430C46804644E002AB90 +:10A4B0000E2207214046FFF748FF002841D0606880 +:10A4C0001C2267788678BF1C06EB860102EBC1016F +:10A4D000451802981421017047700A214180698A49 +:10A4E0000181E98A4181A9888180A98981813046D9 +:10A4F00001F056FB029905230722C8806F700420E3 +:10A50000287000250E20CDE9000521464046FFF7C2 +:10A51000DCFE294666F30F2168F31F41F023002279 +:10A5200006200DF021FA6078FD49801C6070626899 +:10A530002046921CFFF793FE606880784028B6D1D1 +:10A540000120BDE8FE83FEB50D46064638E002ABAD +:10A550000E2207213046FFF7F8FE002835D0686844 +:10A560001C23C17801EB810203EBC202841802981C +:10A5700015220270627842700A224280A2894281CA +:10A58000A2888281084601F00BFB01460298818077 +:10A59000618AC180E18A0181A088B8B10020207061 +:10A5A00000210E20CDE9000105230722294630466F +:10A5B000FFF78BFE6A68DB492846D21CFFF74FFE87 +:10A5C0006868C0784028C2D10120FEBD0620E6E7B9 +:10A5D0002DE9FE430C46814644E0204601F002FB93 +:10A5E000D0B302AB082207214846FFF7AEFE002891 +:10A5F000A7D060681C2265780679AD1C06EB860141 +:10A6000002EBC10147180298B7F8108006210170CB +:10A61000457004214180304601F0C2FA014602989B +:10A6200005230722C180A0F804807D7008203870BF +:10A630000025CDE9000521464846FFF746FE29469C +:10A6400066F30F2169F31F41F023002206200DF06D +:10A650008BF96078801C60706268B3492046121DD7 +:10A66000FFF7FDFD606801794029B6D1012068E758 +:10A670002DE9F34F83B00D4691E0284601F0B2FA80 +:10A6800000287DD068681C2290F806A00AEB8A0199 +:10A6900002EBC10144185146284601F097FAA1780F +:10A6A000CB0069684978CA00014604F1240009F02A +:10A6B000D6FA07468188E08B4FF00009091A8EB25E +:10A6C00008B1C84607E04FF00108504601F053FAC0 +:10A6D00008B9B61CB6B2208BB04200D80646B346C5 +:10A6E00002AB324607210398FFF72FFE060007D082 +:10A6F000B8F1000F0BD0504601F03DFA10B106E062 +:10A7000000201FE60299B8884FF0020908800196E0 +:10A71000E28B3968ABEB09001FFA80F80A44039812 +:10A720004E46009209F005FDDDE90021F61D434685 +:10A73000009609F014F9E08B404480B2E083B988B8 +:10A74000884201D1012600E00026CDE900B6238A27 +:10A75000072229460398FFF7B8FD504601F00BFA8F +:10A7600010B9E089401EE08156B1A078401CA0706D +:10A770006868E978427811FB02F1CAB2012300E06F +:10A7800007E081690E3009F018FA80F800A0002077 +:10A79000E0836A6865492846921DFFF760FD686896 +:10A7A000817940297FF469AF0120CBE570B5064679 +:10A7B00048680D4614468179402910D104EB840184 +:10A7C0001C2202EBC101084401F043FA002806D024 +:10A7D0006868294684713046BDE8704048E770BD1E +:10A7E000FEB50C460746002645E0204601F0FAF982 +:10A7F000D8B360681C22417901EB810102EBC101F1 +:10A800004518688900B9FFDF02AB082207213846E6 +:10A81000FFF79BFD002833D00299607816220A705A +:10A82000801C4870042048806068407901F0B8F9C5 +:10A83000014602980523072281806989C18008208A +:10A84000CDE9000621463846FFF73FFD6078801CC1 +:10A850006070A88969890844B0F5803F00D3FFDFA4 +:10A86000A88969890844A8816E81626830492046B8 +:10A87000521DFFF7F4FC606841794029B5D10120F1 +:10A88000FEBD30B5438C458BC3F3C704002345B1EF +:10A89000838B641EED1AC38A6D1E1D4495FBF3F372 +:10A8A000E4B22CB1008918B1A04200D8204603447C +:10A8B0004FF6FF70834200D3034613800C7030BD07 +:10A8C0002DE9FC41074616460D46486802EB860115 +:10A8D0001C2202EBC10144186A4601A92046FFF779 +:10A8E000D0FFA089618901448AB2BDF8001091426D +:10A8F00012D0081A00D5002060816868407940288D +:10A900000AD1204601F09BF9002805D06868294645 +:10A9100046713846FFF764FFBDE8FC813000002037 +:10A9200035A2000043A2000051A2000053BC000069 +:10A930003FBC00002DE9FE4F0F468146154650886A +:10A94000032109F0B3FA0190B9F8020001F01BF9F4 +:10A9500082460146019801F045F9002824D001986B +:10A960001C2241680AEB8A0002EBC0000C1820464A +:10A9700001F04EF9002817D1B9F80000E18A8842A9 +:10A980000ED8A18961B1B8420ED100265146019876 +:10A9900001F015F9218C01EB0008608B30B114E057 +:10A9A000504601F0E8F8A0B3BDE8FE8F504601F034 +:10A9B000E2F808B1678308E0022FF5D3B9F8040084 +:10A9C0006083618A884224D80226B81B87B2B8F80F +:10A9D0000400A28B801A002814DD874200DA384672 +:10A9E0001FFA80FB688869680291D8F800100A4451 +:10A9F000009209F08CFBF61D009A5B4602990096C6 +:10AA000008F079FFA08B384480B2A083618B884224 +:10AA100007D96888019903B05246BDE8F04F01F0AC +:10AA200035B91FD14FF009002872B9F802006881CA +:10AA3000D8E90010C5E90410608BA881284601F010 +:10AA400090F85146019801F0BAF8014601980823A0 +:10AA500040680078C20004F1200009F0E4F800200A +:10AA6000A0836083504601F086F810B9A089401E8B +:10AA7000A0816888019903B00AF0FF02BDE8F04F99 +:10AA80001EE72DE9F041064615460F461C461846BE +:10AA9000F6F7DFFB18B92068F6F701FC10B11020BB +:10AAA000BDE8F0817168688C0978B0EBC10F01D303 +:10AAB0001320F5E73946304601F081F80146706809 +:10AAC00008230078C20005F1200009F076F8D4E9E7 +:10AAD0000012C0E900120020E2E710B5044603218D +:10AAE00009F0E4F90146007800F00300022805D0DF +:10AAF0002046BDE8104001F1140280E48A8A204615 +:10AB0000BDE81040AFE470B50446032109F0CEF96A +:10AB1000054601462046FFF75BFD002816D0294672 +:10AB20002046FFF75DFE002810D029462046FFF79B +:10AB30000AFD00280AD029462046FFF7B3FC00286A +:10AB400004D029462046BDE8704091E570BD2DE94E +:10AB5000F0410C4680461EE0E178427811FB02F19C +:10AB6000CAB2816901230E3009F05DF80778606888 +:10AB70001C22C179491EC17107EB8701606802EB95 +:10AB8000C10146183946204601F02CF818B130466C +:10AB900001F037F820B16068C1790029DCD17FE786 +:10ABA000FEF724FD050000D1FFDF0A202872384699 +:10ABB00000F0F6FF68813946204601F007F80146AB +:10ABC000606808234078C20006F1240009F02BF8E1 +:10ABD000D0E90010C5E90310A5F80280284600F06E +:10ABE000C0FFB07800B9FFDFB078401EB07057E703 +:10ABF00070B50C460546032109F058F90146406836 +:10AC0000C2792244C2712846BDE870409FE72DE911 +:10AC1000FE4F8246507814460F464FF00008002839 +:10AC20004FD0012807D0022822D0FFDF2068B8606B +:10AC30006068F860B8E602AB0E2208215046FFF7C4 +:10AC400084FB0028F2D00298152105230170217899 +:10AC500041700A214180C0F80480C0F80880A0F843 +:10AC60000C80628882810E20CDE90008082221E054 +:10AC7000A678304600F094FF054606EB86012C22AC +:10AC8000786802EBC1010822465A02AB11465046D1 +:10AC9000FFF75BFB0028C9D00298072101702178DB +:10ACA00041700421418008218580C680CDE90018CB +:10ACB00005230A4639465046FFF707FB87F8088008 +:10ACC00072E6A678022516B1022E13D0FFDF2A1DE8 +:10ACD000914602AB08215046FFF737FB0028A5D06C +:10ACE00002980121022E01702178417045808680F2 +:10ACF00002D005E00625EAE7A188C180E18801814C +:10AD0000CDE900980523082239465046D4E710B50E +:10AD10000446032109F0CAF8014600F10802204662 +:10AD2000BDE8104073E72DE9F04F0F4605468DB0A2 +:10AD300014465088032109F0B9F84FF000088DF847 +:10AD400014800646ADF81680042F7BD36A78002A5B +:10AD500078D028784FF6FF794FF01C0A132834D0AA +:10AD600008DC012871D006284AD007286ED01228A6 +:10AD70000ED106E014286AD0152869D0162807D10C +:10AD8000AAE10C2F04D1307800F00301022907D08A +:10AD9000CDF80880CDF80C8068788DF808004CE07C +:10ADA00040F0080030706878B07001208DF8140011 +:10ADB000A888ADF81800E888ADF81A002889ADF821 +:10ADC0001C006889ADF81E0011E1B078904239D1BD +:10ADD0003078010736D5062F34D120F008003070C6 +:10ADE0006088414660F31F4100200CF067FE02209E +:10ADF0008DF81400ADF81890A888ADF81A00F6E0A8 +:10AE0000082F1FD1A888EF88814600F0BCFE80463D +:10AE10000146304600F0E6FE18B1404600F0ABFEB9 +:10AE2000B8B1FC48D0E90010CDE902106878ADF85F +:10AE30000C908DF80800ADF80E70608802AA3146BB +:10AE4000FFF7E5FE0DB0BDE8F08FB6E01EE041E093 +:10AE5000ECE0716808EB88002C2202EBC000085A75 +:10AE6000B842EFD1EB4802AAD0E90210CDE90210B6 +:10AE700068788DF8080008F0FF058DF80A506088A2 +:10AE80003146FFF7C4FE224629461FE0082FD9D1DC +:10AE9000B5F80480E88800F076FE074601463046A3 +:10AEA00000F0A0FE0028CDD007EB870271680AEB06 +:10AEB000C2000844028A4245C4D101780829C1D1A0 +:10AEC000407869788842BDD1F9B222463046FFF712 +:10AED0001EF9B7E70E2F7FF45BAFE9886F898B46C9 +:10AEE000B5F808903046FFF775F9ABF140014029FD +:10AEF00001D309204AE0B9F1170F01D3172F01D26E +:10AF00000B2043E040280ED000EB800271680AEB72 +:10AF1000C20008440178012903D140786978884249 +:10AF200090D00A2032E03046FFF735F9014640283C +:10AF30002BD001EB810372680AEBC30002EB00081F +:10AF4000012288F800206A7888F801207068AA88B1 +:10AF50004089B84200D93846AD8903232372A282C2 +:10AF6000E7812082A4F80C906582084600F018FE64 +:10AF70006081A8F81490A8F81870A8F80E50A8F8E6 +:10AF800010B0204600F0EDFD5CE7042005212172A1 +:10AF9000A4F80A80E081012121739E49D1E90421AE +:10AFA000CDE9022169788DF80810ADF80A006088B3 +:10AFB00002AA3146FFF72BFEE3E7062F89D3B078CC +:10AFC00090421AD13078010717D520F00800307070 +:10AFD0006088414660F31F4100200CF06FFD0220A5 +:10AFE0008DF81400A888ADF81800ADF81A906088A4 +:10AFF000224605A9F9F7E3F824E704213046FFF7D4 +:10B0000000F905464028BFD0022083030090224665 +:10B010002946304600F003FE4146608865F30F2163 +:10B0200060F31F4106200CF049FD0BE70E2FABD15A +:10B0300004213046FFF7E5F881464028A4D0414678 +:10B04000608869F30F2160F31F4106200CF036FD84 +:10B05000A8890B906889099070682F894089B84247 +:10B0600000D938468346B5F80680A8880A90484635 +:10B0700000F096FD60810B9818B1022000900B9BA8 +:10B0800024E0B8F1170F1ED3172F1CD30420207211 +:10B0900009986082E781A4F810B0A4F80C8009EB4D +:10B0A000890271680AEBC2000D18DDE90913A5F8E1 +:10B0B0001480A5F818B0E9812B82204600F051FDDC +:10B0C00006202870BEE601200B2300902246494648 +:10B0D000304600F0A4FDB5E6082F8DD1A988304692 +:10B0E000FFF778F80746402886D000F044FD002896 +:10B0F0009BD107EB870271680AEBC20008448046C7 +:10B1000000F086FD002890D1ED88B8F80E002844A4 +:10B11000B0F5803F05D360883A46314600F0B6FD71 +:10B1200090E6002DCED0A8F80E0060883A46314651 +:10B13000FFF73CFB08202072384600F031FD6081AB +:10B14000A5811EE72DE9F05F0C4601281FD09579F7 +:10B1500092F8048092F8056005EB85011F2202EB4E +:10B16000C10121F0030B08EB060111FB05F14FF6BD +:10B17000FF7202EAC10909F1030115FB0611264F0E +:10B1800021F0031ABB6840B101283ED125E0616877 +:10B19000E57891F800804E78DEE75946184608F0C9 +:10B1A000C0FC606000B9FFDF5A460021606803F010 +:10B1B0006EF9E5705146B86808F0B3FC6168486103 +:10B1C00000B9FFDF6068426902EB090181616068D4 +:10B1D00080F800806068467017E0606852464169F8 +:10B1E000184608F0C9FC5A466168B86808F0C4FC03 +:10B1F000032008F003FE0446032008F007FE201A8F +:10B20000012802D1B86808F081FC0BEB0A00BDE808 +:10B21000F09F000060610200300000200246002123 +:10B2200002208FE7F7B5FF4C0A20164620700098E1 +:10B2300060B100254FEA0D0008F055FC0021A17017 +:10B240006670002D01D10099A160FEBD012500208E +:10B25000F2E770B50C46154638220021204603F06F +:10B2600016F9012666700A22002104F11C0003F081 +:10B270000EF905B9FFDF297A207861F3010020700B +:10B28000A87900282DD02A4621460020FFF75AFF32 +:10B2900061684020E34A88706168C870616808711D +:10B2A000616848716168887161682888088161688F +:10B2B00068884881606886819078002811D061682C +:10B2C0000620087761682888C885616828884886CC +:10B2D00060680685606869889288018681864685EF +:10B2E000828570BDC878002802D00022012029E79D +:10B2F000704770B50546002165F31F4100200CF032 +:10B30000DDFB0321284608F0D1FD040000D1FFDF5A +:10B3100021462846FEF71CFF002804D0207840F084 +:10B3200010002070012070BD70B505460C4603204A +:10B3300008F056FD08B1002070BDBA4885708480C1 +:10B34000012070BD2DE9FF4180460E460F0CFEF72F +:10B350004DF9050007D06F800321384608F0A6FD9F +:10B36000040008D106E004B03846BDE8F0411321DE +:10B37000F9F750BBFFDF5FEA080005D0B8F1060F10 +:10B3800018D0FFDFBDE8FF8120782A4620F00800B2 +:10B3900020700020ADF8020002208DF800004FF66A +:10B3A000FF70ADF80400ADF8060069463846F8F7BE +:10B3B00006FFE7E7C6F3072101EB81021C23606863 +:10B3C00003EBC202805C042803D008280AD0FFDF08 +:10B3D000D8E7012000904FF440432A46204600F071 +:10B3E0001EFCCFE704B02A462046BDE8F041FEF738 +:10B3F0008EBE2DE9F05F05464089002790460C4639 +:10B400003E46824600F0BFFB8146287AC01E0828CF +:10B410006BD2DFE800F00D04192058363C47722744 +:10B420001026002C6CD0D5E90301C4E902015CE0D0 +:10B4300070271226002C63D00A2205F10C0104F1BA +:10B44000080002F0FAFF50E071270C26002C57D0BC +:10B45000E868A06049E0742710269CB3D5E9030191 +:10B46000C4E902016888032108F020FD8346FEF745 +:10B47000BDF802466888508049465846FEF7FEFDF2 +:10B4800033E075270A26ECB1A88920812DE07627C4 +:10B490001426BCB105F10C0004F1080307C883E8C9 +:10B4A000070022E07727102664B1D5E90301C4E93B +:10B4B00002016888032108F0F9FC01466888FFF75B +:10B4C00046FB12E01CE073270826CCB168880321F4 +:10B4D00008F0ECFC01460078C00606D56888FEF747 +:10B4E00037FE10B96888F8F777FAA8F800602CB131 +:10B4F0002780A4F806A066806888A080002086E6E1 +:10B50000A8F80060FAE72DE9FC410C461E461746F4 +:10B510008046032108F0CAFC05460A2C0AD2DFE85F +:10B5200004F005050505050509090907042303E0DD +:10B53000062301E0FFDF0023CDE9007622462946FD +:10B540004046FEF7C2FEBDE8FC81F8B50546A0F511 +:10B550007F40FF382BD0284608F0D9FD040000D1E9 +:10B56000FFDF204608F05FF9002821D001466A4637 +:10B57000204608F07AF900980321B0F805602846C3 +:10B5800008F094FC0446052E13D0304600F0FBFA78 +:10B5900005460146204600F025FB40B1606805EBFA +:10B5A00085013E2202EBC101405A002800D0012053 +:10B5B000F8BD007A0028FAD00020F8BDF8B504469E +:10B5C000408808F0A4FD050000D1FFDF6A46284648 +:10B5D000616800F0C4FA01460098091F8BB230F888 +:10B5E000032F0280428842800188994205D1042AB3 +:10B5F00008D0052A20D0062A16D022461946FFF781 +:10B6000099F9F8BD001D0E46054601462246304612 +:10B61000F6F739FF0828F4D1224629463046FCF7D0 +:10B6200064F9F8BD30000020636864880A46011D93 +:10B630002046FAF789F9F4E72246001DFFF773FB6D +:10B64000EFE770B50D460646032108F02FFC040015 +:10B6500004D02078000704D5112070BD43F2020009 +:10B6600070BD2A4621463046FEF7C9FE18B9286843 +:10B6700060616868A061207840F0080020700020B8 +:10B6800070BD70B50D460646032108F00FFC04009E +:10B6900004D02078000704D4082070BD43F20200D3 +:10B6A00070BD2A4621463046FEF7DDFE00B9A58270 +:10B6B000207820F008002070002070BD2DE9F04FA8 +:10B6C0000E4691B08046032108F0F0FB0446404648 +:10B6D00008F02FFD07460020079008900990ADF86C +:10B6E00030000A9002900390049004B9FFDF0DF13E +:10B6F0000809FFB9FFDF1DE038460BA9002207F05B +:10B7000055FF9DF82C0000F07F050A2D00D3FFDFC8 +:10B710006019017F491E01779DF82C00000609D5AC +:10B720002A460CA907A8FEF7C0FD19F80510491C08 +:10B7300009F80510761EF6B2DED204F13400F84D99 +:10B7400004F1260BDFF8DCA304F12A07069010E0D1 +:10B750005846069900F08CFA064628700A2800D34D +:10B76000FFDF5AF8261040468847E08CC05DB042A3 +:10B7700002D0208D0028EBD10A202870E94D4E46DA +:10B7800028350EE00CA907A800F072FA0446375DD0 +:10B7900055F8240000B9FFDF55F82420394640460B +:10B7A0009047BDF81E000028ECD111B0BDE8F08F25 +:10B7B00010B5032108F07AFB040000D1FFDF0A2254 +:10B7C000002104F11C0002F062FE207840F0040029 +:10B7D000207010BD10B50C46032108F067FB204413 +:10B7E000007F002800D0012010BD2DE9F84F8946C8 +:10B7F00015468246032108F059FB070004D028466D +:10B80000F5F727FD40B903E043F20200BDE8F88FE9 +:10B810004846F5F744FD08B11020F7E7786828B1ED +:10B8200069880089814201D90920EFE7B9F8000051 +:10B830001C2488B100F0A7F980460146384600F084 +:10B84000D1F988B108EB8800796804EBC000085C86 +:10B8500001280BD00820D9E73846FEF79CFC80462B +:10B86000402807D11320D1E70520CFE7FDF7BEFE22 +:10B8700006000BD008EB8800796804EBC0000C18B8 +:10B88000B9F8000020B1E88910B113E01120BDE73C +:10B890002888172802D36888172801D20720B5E71F +:10B8A000686838B12B1D224641463846FFF7E9F853 +:10B8B0000028ABD104F10C0269462046FEF7E1FFF7 +:10B8C000288860826888E082B9F8000030B10220E0 +:10B8D0002070E889A080E889A0B12BE003202070C7 +:10B8E000A889A08078688178402905D180F80280F5 +:10B8F00039465046FEF7D6FD404600F051F9A9F80A +:10B90000000021E07868218B4089884200D90846F0 +:10B910002083A6F802A004203072B9F800007081DC +:10B92000E0897082F181208B3082A08AB08130461C +:10B9300000F017F97868C178402905D180F80380B4 +:10B9400039465046FEF7FFFD00205FE770B50D4613 +:10B950000646032108F0AAFA04000ED0284600F09B +:10B9600012F905460146204600F03CF918B1284678 +:10B9700000F001F920B1052070BD43F2020070BD56 +:10B9800005EB85011C22606802EBC101084400F050 +:10B990003FF908B1082070BD2A462146304600F024 +:10B9A00075F9002070BD2DE9F0410C461746804620 +:10B9B000032108F07BFA0546204600F0E4F804462F +:10B9C00095B10146284600F00DF980B104EB8401E1 +:10B9D0001C22686802EBC1014618304600F018F9D5 +:10B9E00038B10820BDE8F08143F20200FAE70520F3 +:10B9F000F8E73B46324621462846FFF742F8002842 +:10BA0000F0D1E2B229464046FEF75AFF708C083862 +:10BA1000082803D242484078F7F776FA0020E1E799 +:10BA20002DE9F0410D4617468046032108F03EFA05 +:10BA30000446284600F0A7F8064624B13846F5F734 +:10BA400008FC38B902E043F20200CBE73868F5F7AA +:10BA500000FC08B11020C5E73146204600F0C2F8CE +:10BA600060B106EB86011C22606802EBC10145183B +:10BA7000284600F0CDF818B10820B3E70520B1E75B +:10BA8000B888A98A884201D90C20ABE76168E88CA4 +:10BA90004978B0EBC10F01D31320A3E7314620460C +:10BAA00000F094F80146606808234078C20005F170 +:10BAB000240008F082F8D7E90012C0E90012F2B2BF +:10BAC00021464046FEF772FE00208BE72DE9F04745 +:10BAD0000D461F4690468146032108F0E7F90446CB +:10BAE000284600F050F806463CB14DB13846F5F70F +:10BAF000F4FB50B11020BDE8F08743F20200FAE7F2 +:10BB0000606858B1A0F80C8027E03146204600F06C +:10BB100069F818B1304600F02EF828B10520EAE7A0 +:10BB2000300000207861020006EB86011C2260686C +:10BB300002EBC1014518284600F06AF808B1082058 +:10BB4000D9E7A5F80880F2B221464846FEF7B8FECC +:10BB50001FB1A8896989084438800020CBE707F025 +:10BB600084BE017821F00F01491C21F0F001103151 +:10BB70000170FDF73EBD20B94E48807808B1012024 +:10BB80007047002070474B498988884201D10020C6 +:10BB90007047402801D2402000E0403880B2704712 +:10BBA00010B50446402800D9FFDF2046FFF7E3FF29 +:10BBB00010B14048808810BD4034A0B210BD40682C +:10BBC00042690078484302EBC0007047C278406881 +:10BBD000037812FB03F24378406901FB032100EB79 +:10BBE000C1007047C2788A4209D9406801EB8101DF +:10BBF0001C2202EBC101405C08B10120704700200B +:10BC000070470078062801D901207047002070474E +:10BC10000078062801D00120704700207047F0B45A +:10BC200001EB81061C27446807EBC6063444049DDB +:10BC300005262670E3802571F0BCFEF71FBA10B50B +:10BC4000418911B1FFF7DDFF08B1002010BD0120CF +:10BC500010BD10B5C18C8278B1EBC20F04D9C18977 +:10BC600011B1FFF7CEFF08B1002010BD012010BDBB +:10BC700010B50C4601230A22011D07F0D4FF0078FD +:10BC80002188012282409143218010BDF0B402EB53 +:10BC900082051C264C6806EBC505072363554B68D7 +:10BCA0001C79402C03D11A71F0BCFEF791BCF0BC9A +:10BCB000704700003000002010B5EFF3108000F056 +:10BCC000010472B6FC484178491C41704078012853 +:10BCD00001D10BF0B3FA002C00D162B610BD70B5E3 +:10BCE000F54CA07848B90125A570FFF7E5FF0BF0EA +:10BCF000B6FA20B100200BF080FA002070BD4FF0A2 +:10BD00008040E570C0F80453F7E770B5EFF310809A +:10BD100000F0010572B6E84C607800B9FFDF60788A +:10BD2000401E6070607808B90BF08CFA002D00D1CD +:10BD300062B670BDE04810B5817821B10021C170B4 +:10BD40008170FFF7E2FF002010BD10B504460BF034 +:10BD500086FAD9498978084000D001202060002067 +:10BD600010BD10B5FFF7A8FF0BF079FA02220123EE +:10BD7000D149540728B1D1480260236103200872D9 +:10BD800002E00A72C4F804330020887110BD2DE966 +:10BD9000F84FDFF824934278817889F80420002650 +:10BDA00089F80510074689F806600078DFF810B3B7 +:10BDB000354620B1012811D0022811D0FFDF0BF049 +:10BDC00060FA4FF0804498B10BF062FAB0420FD1A4 +:10BDD00030460BF061FA0028FAD042E00126EEE787 +:10BDE000FFF76AFF58460168C907FCD00226E6E75C +:10BDF0000120E060C4F80451B2490E600107D1F897 +:10BE00004412B04AC1F3423124321160AD49343199 +:10BE100008604FF0020AC4F804A3A060AA480168B1 +:10BE2000C94341F3001101F10108016841F010011B +:10BE3000016001E019F0A8FFD4F804010028F9D04E +:10BE400030460BF029FA0028FAD0B8F1000F04D1DF +:10BE50009D48016821F010010160C4F808A3C4F8EE +:10BE6000045199F805004E4680B1387870B90BF04E +:10BE7000F6F980460BF006FC6FF00042B8F1000FB7 +:10BE800002D0C6E9032001E0C6E90302DBF80000A6 +:10BE9000C00701D00BF0DFF9387810B13572BDE87A +:10BEA000F88F4FF01808C4F808830127A7614FF4F2 +:10BEB0002070ADF8000000BFBDF80000411EADF8D5 +:10BEC0000010F9D2C4F80C51C4F810517A48C01DC2 +:10BED0000BF06CFA3570FFF744FF676179493079F0 +:10BEE00020310860C4F80483D9E770B5050000D19B +:10BEF000FFDF4FF080424FF0FF30C2F8080300210F +:10BF0000C2F80011C2F80411C2F80C11C2F81011E5 +:10BF1000694C61700BF0AFF910B10120A070607036 +:10BF200067480068C00701D00BF095F92846BDE8C6 +:10BF300070402CE76048007A002800D0012070474C +:10BF40002DE9F04F61484FF0000A85B0D0F800B0FD +:10BF5000D14657465D4A5E49083211608406D4F8DE +:10BF6000080110B14FF0010801E04FF000080BF09C +:10BF7000F0F978B1D4F8240100B101208246D4F858 +:10BF80001C0100B101208146D4F8200108B101272D +:10BF900000E00027D4F8000100B101200490D4F89B +:10BFA000040100B101200390D4F80C0100B101207C +:10BFB0000290D4F8100100B101203F4D0190287883 +:10BFC00000260090B8F1000F04D0C4F808610120E9 +:10BFD0000BF013F9BAF1000F04D0C4F82461092062 +:10BFE0000BF00BF9B9F1000F04D0C4F81C610A2062 +:10BFF0000BF003F927B1C4F820610B200BF0FDF81A +:10C000002D48C01D0BF0DAF900B1FFDFDFF8AC807E +:10C010000498012780B1C4F80873E87818B1EE706D +:10C0200000200BF0EAF8287A022805D103202872B4 +:10C030000221C8F800102761039808B1C4F8046110 +:10C04000029850B1C4F80C61287A032800D0FFDFB1 +:10C05000C8F800602F72FFF758FE019838B1C4F895 +:10C060001061287A012801D100F05CF8676100981E +:10C0700038B12E70287A012801D1FFF772FEFFF740 +:10C0800044FE0D48C01D0BF0AFF91049091DC1F861 +:10C0900000B005B0BDE8F08F074810B5C01D0BF02B +:10C0A0008DF90549B0B1012008704FF0E021C1F8C9 +:10C0B0000002BDE81040FFE544000020340C0040C1 +:10C0C0000C0400401805004010ED00E0100502408F +:10C0D00001000001087A012801D1FFF742FEBDE806 +:10C0E000104024480BF080B970B5224CE41FA078B2 +:10C0F00008B90BF0A7F801208507A861207A00266F +:10C10000032809D1D5F80C0120B900200BF0C4F8A0 +:10C110000028F7D1C5F80C6126724FF0FF30C5F842 +:10C12000080370BD70B5134CE41F6079F0B10128AD +:10C1300003D0A179401E814218DA0BF090F8054631 +:10C140000BF0A0FA6179012902D9A179491CA171EA +:10C150000DB1216900E0E168411A022902DA11F10A +:10C16000020F06DC0DB1206100E0E060BDE8704028 +:10C17000F7E570BD4B0000200F4A12680D498A4256 +:10C180000CD118470C4A12680A4B9A4206D101B5E5 +:10C190000BF04AFA0BF01DFDBDE8014007490968A4 +:10C1A0000958084706480749054A064B70470000EA +:10C1B00000000000BEBAFECA5C000020040000209F +:10C1C000C8130020C8130020F8B51D46DDE9064756 +:10C1D0000E000AD007F0ADFF2346FF1DBCB231466A +:10C1E0002A46009407F0BBFBF8BDD0192246194639 +:10C1F00002F023F92046F8BD70B50D460446102222 +:10C20000002102F044F9258117206081A07B40F0D5 +:10C210000A00A07370BD4FF6FF720A80014602202B +:10C220000BF04CBC704700897047827BD30701D16B +:10C23000920703D48089088000207047052070474A +:10C24000827B920700D581817047014600200988D2 +:10C2500041F6FE52114200D00120704700B503465E +:10C26000807BC00701D0052000BD59811846FFF72B +:10C27000ECFFC00703D0987B40F004009873987BD4 +:10C2800040F001009873002000BD827B520700D56A +:10C2900009B14089704717207047827B61F3C30260 +:10C2A000827370472DE9FC5F0E460446017896467E +:10C2B000012000FA01F14DF6FF5201EA020962681D +:10C2C0004FF6FF7B1188594502D10920BDE8FC9F3C +:10C2D000B9F1000F05D041F6FE55294201D00120E9 +:10C2E000F4E741EA090111801D0014D000232B70EE +:10C2F00094F800C0052103221F464FF0020ABCF14A +:10C300000E0F76D2DFE80CF0F909252F47646B7722 +:10C31000479193B4D1D80420D8E7616820898B7BFA +:10C320009B0767D517284AD30B89834247D389894E +:10C33000172901D3814242D185F800A0A5F8010058 +:10C340003280616888816068817B21F0020181739D +:10C35000C6E0042028702089A5F801006089A5F8AE +:10C3600003003180BCE0208A3188C01D1FFA80F8AC +:10C37000414524D3062028702089A5F80100608952 +:10C38000A5F80300A089A5F805000721208ACDE9BA +:10C390000001636941E00CF0FF00082810D008207C +:10C3A00028702089A5F801006089A5F80300318074 +:10C3B0006A1D694604F10C0009F025FB10B15EE02E +:10C3C0001020EDE730889DF800100844308087E0A9 +:10C3D0000A2028702089A5F80100328044E00C2052 +:10C3E00028702089A5F801006089A5F80300318034 +:10C3F0003AE082E064E02189338800EB41021FFAD1 +:10C4000082F843453BD3B8F1050F38D30E222A708A +:10C410000BEA4101CDE90010E36860882A467146C5 +:10C42000FFF7D2FEA6F800805AE04020287060890D +:10C430003188C01C1FFA80F8414520D32878714606 +:10C4400020F03F00123028702089A5F80100608993 +:10C45000CDE9000260882A46E368FFF7B5FEA6F83A +:10C460000080287840063BD461682089888037E0C6 +:10C47000A0893288401D1FFA80F8424501D2042766 +:10C480003DE0162028702089A5F801006089A5F8F4 +:10C490000300A089CDE9000160882A46714623691E +:10C4A000FFF792FEA6F80080DEE718202870207AB9 +:10C4B0006870A6F800A013E061680A88920401D4AD +:10C4C00005271CE0C9882289914201D0062716E081 +:10C4D0001E21297030806068018821F4005101809C +:10C4E000B9F1000F0BD061887823002202200BF0F5 +:10C4F0003BFA61682078887006E033800327606823 +:10C50000018821EA090101803846DFE62DE9FF4F65 +:10C5100085B01746129C0D001E461CD03078C1070E +:10C5200003D000F03F00192801D9012100E00021CB +:10C530002046FFF7AAFEA8420DD32088A0F57F4130 +:10C54000FF3908D03078410601D4000605D508200F +:10C5500009B0BDE8F08F0720FAE700208DF8000051 +:10C560008DF8010030786B1E00F03F0C0121A81EF1 +:10C570004FF0050A4FF002094FF0030B9AB2BCF1DD +:10C58000200F75D2DFE80CF08B10745E7468748C29 +:10C59000749C74B574BA74C874D474E1747474F10E +:10C5A00074EF74EE74ED748B052D78D18DF80090D6 +:10C5B000A0788DF804007088ADF8060030798DF809 +:10C5C0000100707800F03F000C2829D00ADCA0F1AF +:10C5D0000200092863D2DFE800F0126215621A62D5 +:10C5E0001D622000122824D004DC0E281BD0102845 +:10C5F000DBD11BE016281FD01828D6D11FE02078E9 +:10C60000800701E020784007002848DAEEE0207833 +:10C610000007F9E72078C006F6E720788006F3E700 +:10C6200020784006F0E720780006EDE72088C00576 +:10C63000EAE720884005E7E720880005E4E720884E +:10C64000C004E1E72078800729D5032D27D18DF894 +:10C6500000B0B6F8010081E0217849071FD5062D0A +:10C660001DD381B27078012803D0022817D102E0CF +:10C67000C9E0022000E0102004228DF8002072782A +:10C680008DF80420801CB1FBF0F2ADF8062092B2C8 +:10C6900042438A4203D10397ADF80890A6E079E0BF +:10C6A0002078000776D598B282088DF800A0ADF802 +:10C6B0000420B0EB820F6DD10297ADF8061095E023 +:10C6C0002178C90666D5022D64D381B206208DF883 +:10C6D0000000707802285DD3B1FBF0F28DF8040001 +:10C6E000ADF8062092B242438A4253D1ADF8089089 +:10C6F0007BE0207880064DD5072003E020784006B7 +:10C700007FD508208DF80000A088ADF80400ADF8B2 +:10C710000620ADF8081068E02078000671D50920E1 +:10C72000ADF804208DF80000ADF8061002975DE02A +:10C730002188C90565D5022D63D381B20A208DF801 +:10C740000000707804285CD3C6E72088400558D5DF +:10C75000012D56D10B208DF80000A088ADF8040003 +:10C7600044E021E026E016E0FFE72088000548D5F8 +:10C77000052D46D30C208DF80000A088ADF80400EC +:10C78000B6F803006D1FADF80850ADF80600ADF81F +:10C790000AA02AE035E02088C00432D5012D30D12E +:10C7A0000D208DF8000021E02088800429D4B6F8FF +:10C7B0000100E080A07B000723D5032D21D3307832 +:10C7C00000F03F001B2818D00F208DF800002088B3 +:10C7D00040F40050A4F80000B6F80100ADF80400E1 +:10C7E000ED1EADF80650ADF808B003976946059800 +:10C7F000F5F792FB050008D016E00E208DF800003A +:10C80000EAE7072510E008250EE0307800F03F0049 +:10C810001B2809D01D2807D0022005990BF04EF9DE +:10C82000208800F400502080A07B400708D52046D7 +:10C83000FFF70BFDC00703D1A07B20F00400A0731D +:10C84000284685E61FB5022806D101208DF8000094 +:10C8500088B26946F5F760FB1FBD0000F8B51D46BC +:10C86000DDE906470E000AD007F063FC2346FF1DF2 +:10C87000BCB231462A46009407F071F8F8BDD019D1 +:10C880002246194601F0D9FD2046F8BD2DE9FF4F9B +:10C890008DB09B46DDE91B57DDF87CA00C46082BCC +:10C8A00005D0E06901F0FEF850B11020D2E02888F0 +:10C8B000092140F0100028808AF80010022617E0B5 +:10C8C000E16901208871E2694FF420519180E169AA +:10C8D0008872E06942F601010181E06900218173FB +:10C8E0002888112140F0200028808AF800100426B2 +:10C8F00038780A900A2038704FF0020904F11800C5 +:10C900004D460C9001F0C6FBB04681E0BBF1100F24 +:10C910000ED1022D0CD0A9EB0800801C80B20221A0 +:10C92000CDE9001005AB52461E990D98FFF796FF12 +:10C93000BDF816101A98814203D9F74800790F9074 +:10C9400004E003D10A9808B138702FE04FF00201DB +:10C95000CDE900190DF1160352461E990D98FFF707 +:10C960007DFF1D980088401B801B83B2C6F1FF002D +:10C97000984200D203461E990BA8D9B15FF000027D +:10C98000DDF878C0CDE9032009EB060189B2CDE9D5 +:10C9900001C10F980090BDF8161000220D9801F00B +:10C9A0000EFC387070B1C0B2832807D0BDF81600F5 +:10C9B00020833AE00AEB09018A19E1E7022011B06D +:10C9C000BDE8F08FBDF82C00811901F0FF08022DA1 +:10C9D0000DD09AF80120424506D1BDF820108142C1 +:10C9E00007D0B8F1FF0F04D09AF801801FE08AF851 +:10C9F0000180C94800680178052902D1BDF81610E8 +:10CA0000818009EB08001FFA80F905EB080085B268 +:10CA1000DDE90C1005AB0F9A01F03FFB28B91D981A +:10CA20000088411B4145BFF671AF022D13D0BBF109 +:10CA3000100F0CD1A9EB0800801C81B20220CDE9B7 +:10CA4000000105AB52461E990D98FFF707FF1D9890 +:10CA50000580002038700020B1E72DE9F8439C469E +:10CA6000089E13460027B26B9AB3491F8CB2F18F10 +:10CA7000A1F57F45FF3D05D05518AD882944891D96 +:10CA80008DB200E000252919B6F83C8008314145F7 +:10CA900020D82A44BCF8011022F8021BBCF803106D +:10CAA00022F8021B984622F8024B914607F02FFB12 +:10CAB0004FF00C0C41464A462346CDF800C006F024 +:10CAC0001AFFF587B16B00202944A41D214408807A +:10CAD00003E001E0092700E083273846BDE8F8833A +:10CAE00010B50B88848F9C420CD9846BE0180488A5 +:10CAF00044B1848824F40044A41D23440B801060B6 +:10CB0000002010BD0A2010BD2DE9F0478AB0002595 +:10CB1000904689468246ADF8185007274BE00598A5 +:10CB200006888088000446D4A8F8006007A801950C +:10CB300000970295CDE903504FF40073002231466F +:10CB4000504601F03CFB04003CD1BDF81800ADF8A4 +:10CB50002000059804888188B44216D10A0414D4B0 +:10CB600001950295039521F400410097049541F445 +:10CB7000804342882146504601F0BFF804000BD1A3 +:10CB80000598818841F40041818005AA08A948469A +:10CB9000FFF7A6FF0400DCD00097059802950195E9 +:10CBA000039504950188BDF81C300022504601F021 +:10CBB000A4F80A2C06D105AA06A94846FFF790FF5B +:10CBC0000400ACD0ADF8185004E00598818821F439 +:10CBD0000041818005AA06A94846FFF781FF002889 +:10CBE000F3D00A2C03D020460AB0BDE8F08700201D +:10CBF000FAE710B50C46896B86B051B10C218DF85F +:10CC00000010A18FADF80810A16B01916946FAF7E9 +:10CC100001FB00204FF6FF71A063E187A08706B0FB +:10CC200010BD2DE9F0410D460746896B0020069E98 +:10CC30001446002911D0012B0FD13246294638461F +:10CC4000FFF762FF002808D1002C06D032462946A3 +:10CC50003846BDE8F04100F034BFBDE8F0812DE971 +:10CC6000FC411446DDE9087C0E46DDE90A15521D3B +:10CC7000BCF800E092B2964502D20720BDE8FC81E4 +:10CC8000ACF8002017222A70A5F80160A5F803303F +:10CC90000522CDE900423B462A46FFF7DFFD002092 +:10CCA000ECE770B50C46154648220021204601F0FD +:10CCB000EEFB04F1080044F81C0F00204FF6FF7152 +:10CCC000E06161842084A5841720E08494F82A0020 +:10CCD00040F00A0084F82A0070BD4FF6FF720A8007 +:10CCE000014603200AF0EABE30B585B00C46054681 +:10CCF000FFF77FFFA18E284629B101218DF8001092 +:10CD00006946FAF787FA0020E0622063606305B0A5 +:10CD100030BDB0F8400070476000002090F8462019 +:10CD2000920703D4408808800020F4E70620F2E749 +:10CD300090F846209207EED5A0F84410EBE70146A4 +:10CD4000002009880A0700D5012011F0F00F01D05A +:10CD500040F00200CA0501D540F004008A0501D563 +:10CD600040F008004A0501D540F010000905D2D571 +:10CD700040F02000CFE700B5034690F84600C0071A +:10CD800001D0062000BDA3F842101846FFF7D7FFD8 +:10CD900010F03E0F05D093F8460040F0040083F8F1 +:10CDA000460013F8460F40F001001870002000BD47 +:10CDB00090F84620520700D511B1B0F84200AAE71A +:10CDC0001720A8E710F8462F61F3C3020270A2E70C +:10CDD0002DE9FF4F9BB00E00DDE92B34DDE929780A +:10CDE000289D24D02878C10703D000F03F001928DF +:10CDF00001D9012100E000212046FFF7D9FFB04210 +:10CE000015D32878410600F03F010CD41E290CD020 +:10CE1000218811F47F6F0AD13A8842B1A1F57F428F +:10CE2000FF3A04D001E0122901D1000602D5042006 +:10CE30001FB0C5E5FA491D984FF0000A08718DF83A +:10CE400018A08DF83CA00FAA0A60ADF81CA0ADF8A0 +:10CE500050A02978994601F03F02701F5B1C04F135 +:10CE6000180C4FF0060E4FF0040BCDF858C01F2AD7 +:10CE70007ED2DFE802F07D7D107D267DAC7DF47DE5 +:10CE8000F37DF27DF17DF47DF07D7D7DEF7DEE7DA6 +:10CE90007D7D7D7DED0094F84610B5F80100890791 +:10CEA00001D5032E02D08DF818B01EE34FF40061B7 +:10CEB000ADF85010608003218DF83C10ADF84000B3 +:10CEC000D4E2052EEFD1B5F801002083ADF81C00A7 +:10CED000B5F80310618308B1884201D9012079E1D6 +:10CEE0000020A07220814FF6FF702084169801F078 +:10CEF000D1F8052089F800000220029083460AAB91 +:10CF00001D9A16991B9801F0C8F890BB9DF82E0049 +:10CF1000012804D0022089F80100102003E001203C +:10CF200089F8010002200590002203A90BA808F04F +:10CF30006AFDE8BB9DF80C00059981423DD1398816 +:10CF4000801CA1EB0B01814237DB02990220CDE965 +:10CF500000010DF12A034A4641461B98FFF77EFC6B +:10CF600002980BF1020B801C81B217AA029101E01A +:10CF70009CE228E003A90BA808F045FD02999DF862 +:10CF80000C00CDE9000117AB4A4641461B98FFF75C +:10CF900065FC9DF80C000AAB0BEB00011FFA81FB4E +:10CFA00002991D9A084480B2029016991B9800E0DD +:10CFB00003E001F072F80028B6D0BBF1020F02D0F6 +:10CFC000A7F800B04FE20A208DF818004BE20021CC +:10CFD0000391072EFFF467AFB5F801002083ADF889 +:10CFE0001C00B5F80320628300283FF477AF90421D +:10CFF0003FF674AF0120A072B5F805002081002033 +:10D00000A073E06900F04EFD78B9E16901208871F4 +:10D01000E2694FF420519180E1698872E16942F63A +:10D0200001000881E06900218173F01F20841E98AF +:10D03000606207206084169801F02CF8072089F8B8 +:10D0400000000120049002900020ADF82A0028E0A2 +:10D0500019E29FE135E1E5E012E2A8E080E043E07B +:10D060000298012814D0E0698079012803D1BDF825 +:10D070002800ADF80E00049803ABCDE900B04A4695 +:10D0800041461B98FFF7EAFB0498001D80B204900C +:10D09000BDF82A00ADF80C00ADF80E00059880B27E +:10D0A00002900AAB1D9A16991B9800F0F6FF28B95A +:10D0B00002983988001D05908142D1D2029801283A +:10D0C00081D0E0698079012803D1BDF82800ADF84E +:10D0D0000E00049803ABCDE900B04A4641461B98C8 +:10D0E000FFF7BCFB0298BDE1072E02D0152E7FF49E +:10D0F000DAAEB5F801102183ADF81C10B5F80320A5 +:10D10000628300293FF4EAAE91423FF6E7AE012187 +:10D11000A1724FF0000BA4F808B084F80EB0052EF1 +:10D1200007D0C0B2691DE26908F06BFC00287FF4EB +:10D130004AAF4FF6FF70208401A906AA14A8CDF8C3 +:10D1400000B081E885032878214600F03F031D9A4E +:10D150001B98FFF79BFB8246208BADF81C0082E1F9 +:10D160000120032EC3D14021ADF85010B5F80110B5 +:10D170002183ADF81C100AAAB8F1000F00D00023DB +:10D18000CDE9020304921D98CDF804800090388800 +:10D190000022401E83B21B9801F011F88DF8180090 +:10D1A00090BB0B2089F80000BDF8280035E04FF057 +:10D1B000010C052E9BD18020ADF85000B5F8011070 +:10D1C0002183B5F803002084ADF81C10B0F5007F72 +:10D1D00003D907208DF8180087E140F47C422284AF +:10D1E0000CA8B8F1000F00D00023CDE90330CDE941 +:10D1F000018C1D9800903888401E83B21B9800F067 +:10D20000DEFF8DF8180018B18328A8D10220BFE0F6 +:10D210000D2189F80010BDF83000401C22E100000B +:10D2200060000020032E04D248067FF53CAE0020AB +:10D2300018E1B5F80110ADF81C102878400602D5A9 +:10D240008DF83CE002E007208DF83C004FF000082C +:10D250000320CDE902081E9BCDF810801D98019394 +:10D26000A6F1030B00901FFA8BF342461B9800F0C7 +:10D2700044FD8DF818008DF83C80297849060DD5BD +:10D280002088C00506D5208BBDF81C10884201D12E +:10D29000C4F8248040468DF81880E3E0832801D14B +:10D2A0004FF0020A4FF48070ADF85000BDF81C003A +:10D2B0002083A4F820B01E986062032060841321AC +:10D2C000CDE0052EFFF4EFADB5F80110ADF81C1060 +:10D2D000A28F6AB3A2F57F43FE3B29D008228DF8C6 +:10D2E0003C2000BF4FF0000B0523CDE9023BDDF8E9 +:10D2F00078C0CDF810B01D9A80B2CDF804C040F4CB +:10D3000000430092B5F803201B9800F0F6FC8DF85E +:10D310003CB04FF400718DF81800ADF85010832820 +:10D3200010D0F8B1A18FA1F57F40FE3807D0DCE026 +:10D330000B228DF83C204FF6FE72A287D2E7A4F8AC +:10D340003CB0D2E000942B4631461E9A1B98FFF762 +:10D3500084FB8DF8180008B183284BD1BDF81C0060 +:10D36000208353E700942B4631461E9A1B98FFF703 +:10D3700074FB8DF81800E8BBE18FA06B0844831D97 +:10D380008DE888034388828801881B98FFF767FC33 +:10D39000824668E095F80180022E70D15FEA0800AD +:10D3A00002D0B8F1010F6AD109208DF83C0007A81E +:10D3B00000908DF840804346002221461B98FFF7DD +:10D3C00030FC8DF842004FF0000B8DF843B050B99F +:10D3D000B8F1010F12D0B8F1000F04D1A18FA1F55F +:10D3E0007F40FF380AD0A08F40B18DF83CB04FF499 +:10D3F000806000E037E0ADF850000DE00FA91B9809 +:10D40000F9F708FF82468DF83CB04FF48060ADF824 +:10D410005000BAF1020F06D0FC480068C07928B16C +:10D420008DF8180027E0A4F8188044E0BAF1000F46 +:10D4300003D081208DF818003DE007A800904346F6 +:10D44000012221461B98FFF7ECFB8DF818002146BE +:10D450001B98FFF7CEFB9DF8180020B9192189F819 +:10D460000010012038809DF83C0020B10FA91B98C6 +:10D47000F9F7D0FE8246BAF1000F33D01BE018E076 +:10D480008DF818E031E02078000712D5012E10D178 +:10D490000A208DF83C00E088ADF8400003201B997D +:10D4A0000AF00CFB0820ADF85000C0E648067FF5F6 +:10D4B000FAAC4FF0040A2088BDF8501008432080D1 +:10D4C000BDF8500080050BD5A18FA1F57F40FE3837 +:10D4D00006D11E98E06228982063A6864FF0030AC2 +:10D4E0005046A5E49DF8180078B1012089F80000A5 +:10D4F000297889F80110BDF81C10A9F802109DF8D0 +:10D50000181089F80410052038802088BDF85010C4 +:10D5100088432080E4E72DE9FF4F8846087895B0DE +:10D52000012181404FF20900249C0140ADF82010F8 +:10D530002088DDF88890A0F57F424FF0000AFF3A7E +:10D5400006D039B1000705D5012019B0BDE8F08F2C +:10D550000820FAE7239E4FF0000B0EA886F800B0D3 +:10D5600018995D460988ADF83410A8498DF81CB0AB +:10D57000179A0A718DF838B0086098F800000128F1 +:10D580003BD0022809D003286FD1307820F03F002B +:10D590001D303070B8F80400E08098F800100320C7 +:10D5A000022904D1317821F03F011B31317094F808 +:10D5B0004610090759D505ABB9F1000F13D000216A +:10D5C00002AA82E80B000720CDE90009BDF834006B +:10D5D000B8F80410C01E83B20022159800F0EFFDC9 +:10D5E0000028D1D101E0F11CEAE7B8F80400A6F860 +:10D5F0000100BDF81400C01C04E198F805108DF876 +:10D600001C1098F80400012806D04FF4007A022874 +:10D610002CD00328B8D16CE12188B8F8080011F4A7 +:10D620000061ADF8201020D017281CD3B4F84010AA +:10D63000814218D3B4F84410172901D3814212D182 +:10D64000317821F03F01C91C3170A6F80100032197 +:10D65000ADF83410A4F8440094F8460020F002001D +:10D6600084F8460065E105257EE177E1208808F130 +:10D67000080700F4FE60ADF8200010F0F00F1BD09A +:10D6800010F0C00F03D03888228B9042EBD199B9AB +:10D69000B878C00710D0B9680720CDE902B1CDF83D +:10D6A00004B00090CDF810B0FB88BA88398815987E +:10D6B00000F023FB0028D6D12398BDF82010401C91 +:10D6C00080294ED006DC10290DD020290BD040290E +:10D6D00087D124E0B1F5807F6ED051457ED0B1F581 +:10D6E000806F97D1DEE0C80601D5082000E0102049 +:10D6F00082460DA907AA0520CDE902218DF8380040 +:10D70000ADF83CB0CDE9049608A93888CDE9000110 +:10D710005346072221461598FFF7B8F8A8E09DF870 +:10D720001C2001214FF00A0A002A9BD105ABB9F158 +:10D73000000F00D00020CDE902100720CDE900093C +:10D74000BDF834000493401E83B2218B002215984B +:10D7500000F035FD8DF81C000B203070BDF8140072 +:10D7600020E09DF81C2001214FF00C0A002A22D154 +:10D7700013ABB9F1000F00D00020CDE90210072053 +:10D78000CDE900090493BDF83400228C401E83B219 +:10D79000218B159800F013FD8DF81C000D203070C2 +:10D7A000BDF84C00401CADF8340005208DF8380061 +:10D7B000208BADF83C00BCE03888218B88427FF498 +:10D7C00052AF9DF81C004FF0120A00281CD1606A6D +:10D7D000A8B1B878C0073FF446AF00E018E0BA68D7 +:10D7E0000720CDE902B2CDF804B00090CDF810B01A +:10D7F000FB88BA88159800F080FA8DF81C00132079 +:10D8000030700120ADF8340093E00000600000208B +:10D810003988208B8142D2D19DF81C004FF0160A26 +:10D820000028A06B08D0E0B34FF6FF7000215F46E0 +:10D83000ADF808B0019027E068B1B978C907BED14A +:10D84000E18F0DAB0844821D03968DE80C024388DE +:10D850008288018809E0B878C007BCD0BA680DABEF +:10D8600003968DE80C02BB88FA881598FFF7F7F944 +:10D8700005005ED0072D72D076E0019005AA02A9BE +:10D880002046FFF72DF90146E28FBDF808008242DD +:10D8900001D00029F1D0E08FA16B084407800198E6 +:10D8A000E08746E09DF81C004FF0180A40B1208B3D +:10D8B000C8B13888208321461598FFF79AF938E0D7 +:10D8C00004F118000090237E012221461598FFF7ED +:10D8D000A8F98DF81C000028EDD119203070012026 +:10D8E000ADF83400E7E7052521461598FFF781F9E3 +:10D8F0003AE0208800F40070ADF8200050452DD1AA +:10D90000A08FA0F57F41FE3901D006252CE0D8F884 +:10D9100008004FF0160A48B1A063B8F80C10A187B0 +:10D920004FF6FF71E187A0F800B002E04FF6FF70FC +:10D93000A087BDF8200030F47F611AD07823002240 +:10D94000032015990AF010F898F80000207120883B +:10D95000BDF82010084320800EE000E00725208855 +:10D96000BDF8201088432080208810F47F6F1CD0E1 +:10D970003AE02188814321809DF8380020B10EA92A +:10D980001598F9F747FC05469DF81C000028EBD0D8 +:10D9900086F801A001203070208B70809DF81C005B +:10D9A00030710520ADF83400DEE7A18EE1B11898A2 +:10D9B0000DAB0088ADF834002398CDE90304CDE920 +:10D9C0000139206B0090E36A179A1598FFF700FA67 +:10D9D000054601208DF838000EA91598F9F71AFCB4 +:10D9E00000B10546A4F834B094F8460040070AD5C3 +:10D9F0002046FFF7A4F910F03E0F04D114F8460FAB +:10DA000020F0040020701898BDF8341001802846DA +:10DA10009BE500B585B0032806D102208DF80000F3 +:10DA200088B26946F9F7F6FB05B000BD10B5384C71 +:10DA30000B782268012B02D0022B2AD111E0137837 +:10DA40000BB1052B01D10423137023688A889A80B7 +:10DA50002268CB88D38022680B8913814989518140 +:10DA60000DE08B8893802268CB88D38022680B8955 +:10DA700013814B8953818B899381096911612168D5 +:10DA8000F9F7C8FB226800210228117003D0002892 +:10DA900000D0812010BD832010BD806B002800D0F5 +:10DAA000012070478178012909D10088B0F5205FF5 +:10DAB00003D042F60101884201D1002070470720BF +:10DAC0007047F0B587B0002415460E460746ADF8FE +:10DAD000184011E005980088288005980194811D60 +:10DAE000CDE902410721049400918388428801888E +:10DAF000384600F002F930B905AA06A93046FEF70B +:10DB0000EFFF0028E6D00A2800D1002007B0F0BDC2 +:10DB10006000002010B58B7883B102789A4205D15D +:10DB20000B885BB102E08B79091D4BB18B789A426F +:10DB3000F9D1B0F801300C88A342F4D1002010BD17 +:10DB4000812010BD072826D012B1012A27D103E079 +:10DB5000497801F0070102E04978C1F3C2010529C3 +:10DB60001DD2DFE801F00318080C12000AB10320EF +:10DB700070470220704704280DD250B10DE00528EF +:10DB800009D2801E022808D303E0062803D0032808 +:10DB900003D005207047002070470F207047812078 +:10DBA0007047C0B282060BD4000607D5FA48807AC7 +:10DBB0004143C01D01EBD00080B27047084670475A +:10DBC0000020704770B513880B800B781C0625D594 +:10DBD000F14CA47A844204D843F01000087000206D +:10DBE00070BD956800F0070605EBD0052D78F5406F +:10DBF00065F304130B701378D17803F0030341EA43 +:10DC0000032140F20123B1FBF3F503FB15119268E8 +:10DC1000E41D00FB012000EBD40070BD906870BDD6 +:10DC200037B51446BDF804101180117841F0040195 +:10DC300011709DF804100A061ED5D74AA368C1F3D7 +:10DC40000011927A824208D8FE2811D1D21DD20842 +:10DC50004942184600F01BFC0AE003EBD00200F03A +:10DC60000703012510789D40A84399400843107090 +:10DC7000207820F0100020703EBD2DE9F0410746CD +:10DC8000C81C0E4620F00300B04202D08620BDE83A +:10DC9000F081C14D002034462E60AF802881AA72E9 +:10DCA000E8801AE0E988491CE980810614D4E1780B +:10DCB00000F0030041EA002040F20121B0FBF1F244 +:10DCC00001FB12012068FFF76CFF2989084480B22C +:10DCD0002881381A3044A0600C3420784107E1D400 +:10DCE0000020D4E7AC4801220189C08800EB400045 +:10DCF00002EB8000084480B270472DE9FF4F89B0E5 +:10DD00001646DDE9168A0F46994623F44045084633 +:10DD100000F054FB040002D02078400703D4012017 +:10DD20000DB0BDE8F08F099806F086F802902078D3 +:10DD3000000606D59848817A0298814201D887204A +:10DD4000EEE7224601A90298FFF73CFF8346002038 +:10DD50008DF80C004046B8F1070F1AD00122214679 +:10DD6000FFF7F0FE0028DBD12078400611D5022015 +:10DD70008DF80C00ADF81070BDF80400ADF812007D +:10DD8000ADF814601898ADF81650CDF81CA0ADF899 +:10DD900018005FEA094004D500252E46A846012751 +:10DDA0000CE02178E07801F0030140EA012040F224 +:10DDB0000121B0FBF1F2804601FB12875FEA494086 +:10DDC00009D5B84507D1A178207901F0030140EACF +:10DDD0000120B04201D3BE4201D90720A0E7A81913 +:10DDE0001FFA80F9B94501D90D2099E79DF80C007B +:10DDF00028B103A90998F9F70BFA002890D1B84582 +:10DE000007D1A0784FEA192161F30100A07084F8CE +:10DE100004901A9800B10580199850EA0A0027D09A +:10DE2000199830B10BEB06002A46199900F005FB52 +:10DE30000EE00BEB06085746189E099806F067F9A6 +:10DE40002B46F61DB5B239464246009505F053FD06 +:10DE5000224601A90298FFF7B5FE9DF8040022466C +:10DE600020F010008DF80400DDE90110FFF7D8FE66 +:10DE7000002055E72DE9FF4FDFF81C91824685B061 +:10DE8000B9F80610D9F8000001EB410100EB81045C +:10DE900040F20120B2FBF0F1174600FB1175DDE9FD +:10DEA000138B4E4629460698FFF77BFE0346FFF785 +:10DEB00019FF1844B1880C30884202D9842009B077 +:10DEC0002FE70698C6B2300603D5B00601D5062066 +:10DED000F5E7B9F80620521C92B2A9F80620BBF16A +:10DEE000000F01D0ABF80020B00602D5C4F80880BE +:10DEF0000AE0B9F808201A4492B2A9F80820D9F823 +:10DF00000000891A0844A0602246FE200699FFF707 +:10DF100087FEE77025712078390A61F301002A0A2B +:10DF2000A17840F0040062F30101A17020709AF81A +:10DF300002006071BAF80000E08000252573300609 +:10DF400002D599F80A7000E00127B00601D54FF01C +:10DF500000084E4600244FF007090FE0CDE90258B3 +:10DF60000195CDF800900495F1882046129B089AFF +:10DF7000FFF7C3FE0028A2D1641CE4B2BC42EDD37B +:10DF800000209CE700B5FFF7ADFE03490C308A88FE +:10DF9000904203D9842000BD00060020CA8808688A +:10DFA00002EB420300EB8300521C037823F00403CE +:10DFB0000370CA80002101730846ECE72DE9F047A1 +:10DFC000804600F0FBF9070005D000264446F74DD7 +:10DFD00040F2012916E00120BDE8F087204600F05C +:10DFE000EDF90278C17802F0030241EA0222B2FBA5 +:10DFF000F9F309FB13210068FFF7D3FD3044641CDB +:10E0000086B2A4B2E988601E8142E7DCA8F1010073 +:10E01000E8802889801B288100203870DCE710B553 +:10E02000144631B1491E218005F006FFA070002082 +:10E0300010BD012010BD70B50446DC48C1880368DE +:10E0400001E0401C20802088884207D200EB40027B +:10E0500013EB820202D015786D07F2D580B28842A8 +:10E0600016D2AAB15079A072D08820819178107907 +:10E0700001F0030140EA0120A081A078E11CFFF734 +:10E08000A1FD20612088401C2080E080002070BD20 +:10E090000A2070BD0121018270472DE9FF4F85B034 +:10E0A0004FF6FF798246A3F8009048681E460D4659 +:10E0B00080788DF8060048680088ADF804000020DC +:10E0C0008DF80A00088A0C88A04200D304462C82EE +:10E0D00051E03878400708D4641C288AA4B2401C58 +:10E0E000288208F10100C0B246E0288A401C28823C +:10E0F000781D6968FFF70EFDD8BB3188494501D10D +:10E10000601E30803188A1EB080030806888A04212 +:10E1100038D3B878397900F0030041EA002801A922 +:10E12000781DFFF7F7FC20BB298949452ED0002236 +:10E1300039460798FFF706FDD8B92989414518D116 +:10E14000E9680391B5F80AC0D7F808B05046CDF891 +:10E1500000C005F0DCFFDDF800C05A460CF1070CEA +:10E160001FFA8CFC43460399CDF800C005F08DFBE7 +:10E1700060B1641CA4B200208046204600F01EF965 +:10E180000700A6D1641E2C820A2098E67480787954 +:10E19000B071F888B0803978F87801F0030140EA6E +:10E1A00001207081A6F80C80504605F045FE3A46E5 +:10E1B00006F10801FFF706FD306100207FE62DE93A +:10E1C000FF4F87B081461C469246DDF860B0DDF80F +:10E1D0005480089800F0F2F8050002D02878400733 +:10E1E00002D401200BB09CE5484605F025FE2978B5 +:10E1F000090605D56D49897A814201D88720F1E762 +:10E20000CAF309062A4601A9FFF7DCFC0746149861 +:10E2100007281CD000222946FFF794FC0028E1D1F2 +:10E220002878400613D501208DF808000898ADF82D +:10E230000C00BDF80400ADF80E00ADF81060ADF8AC +:10E24000124002A94846F8F7E3FF0028CAD129780E +:10E25000E87801F0030140EA0121AA78287902F068 +:10E26000030240EA0220564507D0B1F5007F04D9E9 +:10E27000611E814201DD0B20B4E7864201D90720EF +:10E28000B0E7801B85B2A54200D92546BBF1000F3F +:10E2900001D0ABF80050179818B1B9192A4600F010 +:10E2A000CCF8B8F1000F0DD03E4448464446169FC6 +:10E2B00005F03FFF2146FF1DBCB232462B460094BD +:10E2C00005F04DFB00208DE72DE9F04107461D4686 +:10E2D0001646084600F072F8040002D02078400785 +:10E2E00001D40120D3E4384605F0A6FD21780906C3 +:10E2F00005D52E49897A814201D88720C7E4224674 +:10E300003146FFF75FFC65B12178E07801F0030149 +:10E3100040EA0120B0F5007F01D8012000E0002094 +:10E3200028700020B3E42DE9F04107461D4616464B +:10E33000084600F043F8040002D02078400701D4DA +:10E340000120A4E4384605F077FD2178090605D5BB +:10E350001649897A814201D8872098E422463146BD +:10E36000FFF75EFCFF2D14D02178E07801F0030266 +:10E3700040EA022040F20122B0FBF2F302FB13005C +:10E3800015B900F2012080B2E070000A60F30101CB +:10E39000217000207BE410B50C4600F00FF810B19E +:10E3A0000178490704D4012010BD000000060020B8 +:10E3B000C18821804079A0700020F5E70749CA880C +:10E3C000824209D340B1096800EB40006FF00B02B4 +:10E3D00002EB8000084470470020704700060020D0 +:10E3E00070B504460D4621462B460AB9002070BD83 +:10E3F00001E0491C5B1C501E021E03D008781E78E9 +:10E40000B042F6D008781E78801BF0E730B50C4695 +:10E4100001462346051B954206D202E0521E9D5C32 +:10E420008D54002AFAD107E004E01D780D70491CD4 +:10E430005B1C521E002AF8D130BDF0B50E460146D5 +:10E44000334680EA030404F00304B4B906E002B9D9 +:10E45000F0BD13F8017B01F8017B521E01F00307A8 +:10E46000002FF4D10C461D4602E080CD80C4121F5F +:10E47000042AFAD221462B4600BF04E013F8014BD0 +:10E4800001F8014B521E002AF8D100BFE0E7F0B5B9 +:10E490000C460146E6B204E002B9F0BD01F8016B9A +:10E4A000521E01F00307002FF6D10B46E5B245EAF4 +:10E4B000052545EA054501E020C3121F042AFBD2C9 +:10E4C000194602E001F8016B521E002AFAD100BF82 +:10E4D000E3E7000010B509F0A0FDF4F7F9F909F041 +:10E4E000E7FBBDE8104009F0AFBC302834BF012085 +:10E4F00000207047202834BF4FF0A0420C4A01236F +:10E5000000F01F0003FA00F0002914BFC2F80C0548 +:10E51000C2F808057047202834BF4FF0A0410449D5 +:10E5200000F01F00012202FA00F0C1F81805704740 +:10E530000003005070B50346002002466FF02F051F +:10E540000EE09C5CA4F130060A2E02D34FF0FF309F +:10E5500070BD00EB800005EB4000521C2044D2B29D +:10E560008A42EED370BD30B50A230BE0B0FBF3F462 +:10E5700003FB1404B0FBF3F08D183034521E05F881 +:10E58000014CD2B2002AF1D130BD30B500234FF694 +:10E59000FF7510E0040A44EA002084B2C85C6040C1 +:10E5A000C0F30314604005EA00344440E0B25B1C51 +:10E5B00084EA40109BB29342ECD330BD2DE9F04188 +:10E5C000FE4B0026012793F864501C7893F868C02E +:10E5D000B8B183F89140A3F8921083F8902083F8A3 +:10E5E0008E70BCF1000F0CBF83F8946083F89450D8 +:10E5F000F3488068008805F08AFDBDE8F04105F029 +:10E6000021BA4FF6FF7083F89140A3F8920083F887 +:10E61000902083F88E70BCF1000F14BF83F89450E3 +:10E6200083F89460BDE8F0812DE9F041E44D29685C +:10E6300091F89C200024012A23D091F89620012AE9 +:10E6400030D091F86C301422DC4E0127012B32D0EF +:10E6500091F88E30012B4FD091F8A620012A1CBFD3 +:10E660000020BDE8F08144701F2200F8042B222214 +:10E67000A731FFF7E2FE286880F8A6400120BDE838 +:10E68000F08144701B220270D1F89D204260D1F8C5 +:10E69000A120826091F8A520027381F89C4001209E +:10E6A000BDE8F081447007220270D1F898204260E2 +:10E6B00081F89640E2E78046447000F8042B20225F +:10E6C0006E31FFF7BAFE88F80870286880F86C4051 +:10E6D00090F86E000028D1D1B6F87000A6F8980026 +:10E6E000A868417B86F89A1086F89670008805F035 +:10E6F0000EFD05F0B6F9C1E791F86C30012B0BD097 +:10E70000447017220270D1F890204260B1F8942032 +:10E71000028181F88E40B1E78046447000F8042BF6 +:10E7200020226E31FFF789FE88F80870286880F88B +:10E730006C4090F86E000028A0D1CDE7A04800689A +:10E7400090F86C10002914BFB0F870004FF6FF70FD +:10E75000704770B59A4C06462068002808BFFFDF56 +:10E760000025206845706660002808BFFFDF20682C +:10E77000417800291CBFFFDF70BDCC220021FFF7CC +:10E7800086FE2068FF2101707F2180F83810132158 +:10E790004184282180F86910012180F85C1080F8FC +:10E7A00061500AF0C1F9BDE8704009F0AEBA844981 +:10E7B0000968097881420CBF012000207047804819 +:10E7C000006890F82200C0F3400070477C48006861 +:10E7D00090F8220000F0010070477948006890F836 +:10E7E0002200C0F3001070472DE9F0437448002464 +:10E7F000036893F82400B3F822C0C0F38001C0F38B +:10E800004002114400F001000844CCF3001121B390 +:10E81000BCF1100F02BF6B4931F81000BDE8F08366 +:10E82000BCF1120F18BFBCF1130F0ED0BCF1150FC5 +:10E830001EBFFFDF2046BDE8F0830021624A32F8A8 +:10E84000102010FB0120BDE8F083604A002132F85F +:10E85000102010FB0120BDE8F08393F85E2093F8B0 +:10E860005F102E264FF47A774FF014084FF04009CE +:10E87000022A04BF4AF2D745B5FBF7F510D0012AAA +:10E8800004BF4AF22F75B5FBF7F510D04AF62315F1 +:10E89000B5FBF7F5082A08BF4E4613D0042A18D056 +:10E8A0002646082A0ED0042A13D0022A49D004F1A1 +:10E8B0002806042A0FD0082A1CBF4FF01908082286 +:10E8C00004D00AE04FF0140806F5A8764FF0400295 +:10E8D00003E006F5A8764FF0100218FB026212FB67 +:10E8E0000052C0EB00103A4D00EB800005EB8000B9 +:10E8F00010441CF0010F4FF4C8724FF4BF7504BFF1 +:10E90000CCF34006002E65D0CCF3400600F5A57090 +:10E91000EEB1082904BF174640260CD0042904BFD5 +:10E920002F46102607D0022907BF04F11807042636 +:10E9300004F12807082606EB860808EB86163E44F5 +:10E940001BE004F118064FF019080422C5E7082956 +:10E9500004BF164640270CD0042904BF2E461027BA +:10E9600007D0022907BF04F11806042704F128067E +:10E97000082707EB871706EB8706304400F19C0653 +:10E9800093F8690001F00C07002F08BF0020304405 +:10E9900018BF00F5416027D1082904BF164640275B +:10E9A0001BD0042904BF2E46102716D0022906BF0B +:10E9B00004F11806042704F128060CE00C060020D8 +:10E9C00068000020DC610200E4610200D461020002 +:10E9D000D4FEFFFF64E018BF0827C7EBC70707EBAB +:10E9E000470706EB4706304498301CF0010F17D05C +:10E9F000082908BF40210CD0042904BF2A46102151 +:10EA000007D0022907BF04F11802042104F12802EB +:10EA1000082101EB410303EB0111114408443BE0E1 +:10EA2000082904BF944640260CD0042904BFAC46F4 +:10EA3000102607D0022907BF04F1180C042604F1A0 +:10EA4000280C082606EB8616B3F840300CEB860C33 +:10EA50006044EB2B20D944F2552C0B3303FB0CF311 +:10EA60009B0D082907D0042902D0022905D008E00F +:10EA70002A46102108E0402106E004F11802042192 +:10EA800002E004F12802082101EB811102EB81016F +:10EA900001F5A57103FB010000F5B470BDE8F0833A +:10EAA00000F5A570082904BF944640260CD004291F +:10EAB00004BFAC46102607D0022907BF04F1180C8A +:10EAC000042604F1280C082606EB8616B3F8483015 +:10EAD0000CEB860C6044EB2BDED944F2552C0B3347 +:10EAE00003FB0CF39B0D0829C5D00429C0D00229D3 +:10EAF000C7D1C2E7FE4840F271210068806A4843EE +:10EB00007047FB48006890F83700002818BF0120C4 +:10EB1000704710B5F74C207B022818BF032808D196 +:10EB2000207D04F115010EF0E6FE08281CBF01202F +:10EB300010BD207B002816BF022800200120BDE860 +:10EB400010400AF021BDEB4908737047E849096895 +:10EB500081F8300070472DE9F047E54C2168087BCB +:10EB6000002816BF022800200120487301F10E0181 +:10EB70000AF0F4FC2168087B022816BF0328012252 +:10EB8000002281F82F204FF0080081F82D00487BEB +:10EB900001F10E034FF001064FF00007012804BFFA +:10EBA0005B7913F0C00F0AD001F10E03012804D1E4 +:10EBB000587900F0C000402801D0002000E001207A +:10EBC00081F82E00002A04BF91F8220010F0040FF3 +:10EBD00007D0087D01F115010EF08DFE216881F846 +:10EBE0002D002068476007F0BFFA2168C14D4FF043 +:10EBF0000009886095F82D000EF089FE804695F892 +:10EC00002F00002818BFB8F1000F04D095F82D0090 +:10EC10000EF0B1FC68B195F8300000281CBF95F8E3 +:10EC20002E0000281DD0697B05F10E0001290ED0B1 +:10EC300012E06E734A4605F10E0140460AF0E4FC0C +:10EC400095F82D1005F10E000EF063FF09E04079F4 +:10EC500000F0C000402831D0394605F10E000AF01E +:10EC60000BFD2068C77690F8220010F0040F08BF53 +:10EC7000BDE8F087002795F82D000EF017FD050080 +:10EC800008BFBDE8F087102102F0C2F8002818BFC5 +:10EC9000BDE8F08720683A4600F11C01C676284698 +:10ECA0000AF0B2FC206800F11C0160680FF08EF8D9 +:10ECB0006068BDE8F04701210FF0A3B80EF066FFD1 +:10ECC0004A4605F10E010AF09FFCCAE7884A12681D +:10ECD000137B0370D2F80E000860508A888070475A +:10ECE00078B584490446824E407B087332682078A8 +:10ECF00010706088ADF8000080B200F00101C0F330 +:10ED0000400341EA4301C0F3800341EA8301C0F3B9 +:10ED1000C00341EAC301C0F3001341EA0311C0F389 +:10ED2000401341EA4311C0F3801041EA801050843F +:10ED3000E07D012808BF012507D0022808BF022571 +:10ED400003D0032814BFFFDF0825306880F85E5029 +:10ED5000607E012808BF012507D0022808BF0225D0 +:10ED600003D0032814BFFFDF0825316881F85F5006 +:10ED700091F83500012829D0207B81F82400488CA7 +:10ED80001D280CBF002060688862607D81F8370014 +:10ED9000A07B002816BF0228002001200875D4F8A7 +:10EDA0000F00C1F81500B4F81300A1F81900A07EF7 +:10EDB00091F86B2060F3071281F86B20E07E012848 +:10EDC00018BF002081F83400002078BD91F85E2043 +:10EDD0000420082A08BF81F85E00082D08BF81F8CA +:10EDE0005F00C9E742480068408CC0F3001131B1B0 +:10EDF000C0F38000002804BF1F20704702E0C0F36A +:10EE0000400109B10020704710F0010F14BFEE203F +:10EE1000FF20704736480068408CC0F3001119B1DC +:10EE2000C0F3800028B102E0C0F3400008B1002028 +:10EE30007047012070472E49002209684A664B8CB2 +:10EE40001D2B0CBF81F8682081F8680070470023F3 +:10EE5000274A126882F85D30D164A2F85000012080 +:10EE600082F85D007047224A0023126882F85C3005 +:10EE7000A2F858000120516582F85C0070471C49D7 +:10EE8000096881F8360070471949096881F86100FE +:10EE900070471748006890F961007047144800688F +:10EEA00090F82200C0F3401070471148006890F8B5 +:10EEB0002200C0F3C0007047012070470C48006872 +:10EEC00090F85F00704770B509F018FE09F0F7FD83 +:10EED00009F0C0FC09F06CFD054C2068416E491C2E +:10EEE000416690F83300002558B109F01DFE03E09B +:10EEF000680000200C06002008F007FF206880F85A +:10EF000033502068457090F8391021B1BDE8704049 +:10EF100004200AF0AEBF90F86810D9B1406E81426B +:10EF200018D804200AF0A5FF206890F8220010F0FD +:10EF3000010F07D0A06843220188BDE8704001207E +:10EF4000FFF73CBBBDE8704043224FF6FF71002045 +:10EF5000FFF734BBBDE8704000200AF08ABF2DE9FE +:10EF6000F04782B00F468146FE4E4FF000083068F1 +:10EF7000458C15F0030F10D015F0010F05F00200BD +:10EF800005D0002808BF4FF0010806D004E0002893 +:10EF900018BF4FF0020800D1FFDF4FF0000A5446BF +:10EFA00015F0010F05F002000DD080B915F0040F27 +:10EFB0000DD04AF00800002F1CBF40F0010040F0C7 +:10EFC00002044DD09EE010B115F0040F0DD015F0E5 +:10EFD000070F10D015F0010F05F0020043D00028F4 +:10EFE00008BF15F0040F34D04AE0002F18BF4AF0D4 +:10EFF000090444D141E037B14AF00800044615F055 +:10F00000200F1BD07EE0316805F02002B1F84800E7 +:10F01000104308BF4AF0010474D04AF018000446B7 +:10F0200015F0200F6ED191F85E1011F00C0118BF91 +:10F030000121C94361F30000044663E0316891F89F +:10F040005E1011F00C0118BF012161F300000446AD +:10F0500058E04AF00800002F18BF40F0010451D1D9 +:10F0600040F010044EE0002818BF15F0040F07D040 +:10F07000002F18BF4AF00B0444D14AF0180441E0B5 +:10F0800015F0030F3DD115F0040F3AD077B1306879 +:10F090004AF0080490F85E0010F00C0118BF01213E +:10F0A00061F3410415F0200F24D02BE0306805F007 +:10F0B0002002B0F84810114308BF4AF0030421D0E1 +:10F0C0004AF0180415F0200F0AD000BF90F85E0037 +:10F0D00010F00C0018BF0120C04360F3410411E0A0 +:10F0E00090F85E1011F00C0118BF0121C94361F3C3 +:10F0F0000004EBE710F00C0018BF012060F30004DF +:10F1000000E0FFDF15F0400F1CD0CFB93168B1F837 +:10F110004800002804BF488C10F0010F0BD110F0FC +:10F12000020F08BF10F0200F05D115F0010F08BF26 +:10F1300015F0020F04D091F85E0010F00C0F01D111 +:10F1400044F040047068A0F800A0017821F020018C +:10F1500001704FF007010EF005FE414670680EF099 +:10F16000F8FF214670680FF000F814F0010F0CD082 +:10F170004FF006034FF000027B4970680EF0CFFF9E +:10F180003068417B70680EF02FFE14F0020F18D02B +:10F19000D6E90010B9F1000F4FF006034FF001025D +:10F1A00007D01C310EF0BBFF012170680EF029FE64 +:10F1B00007E015310EF0B3FF3068017D70680EF086 +:10F1C00020FE14F0040F18BFFFDF14F0080F19D051 +:10F1D000CDF800A03068BDF800200223B0F86A1016 +:10F1E00061F30B02ADF8002090F86B0003220109D7 +:10F1F0009DF8010061F307108DF801006946706801 +:10F200000EF08DFF012F62D13068B0F84810E1B3E5 +:10F2100090F82200C0F34000B8BB70680EF095FF74 +:10F22000401CC7B23068C7F1FF05B0F84820B0F8FD +:10F230005A10511AA942B8BF0D46AA423BD990F8BC +:10F24000220010F0010F36D144F0100421467068FE +:10F250000EF08BFFF81CC0B2ED1E284482B230685D +:10F26000B0F86A10436EC1F30B0151FA83F190F8C4 +:10F2700060303E4F1944BC460023E1FB07C31B0925 +:10F280006FF0240C03FB0C1100E020E080F860100C +:10F2900090F85F00012101F01FF90090BDF8000017 +:10F2A0009DF80210032340EA01400190042201A9C5 +:10F2B00070680EF034FF3068AAB2416C70680EF0CE +:10F2C00082FF3068B0F85A102944A0F85A1014F0A0 +:10F2D000400F06D0D6E900100123062261310EF05E +:10F2E0001EFF14F0200F18BFFFDF0020002818BFFA +:10F2F000FFDF02B0BDE8F0872DE9F043194C89B07B +:10F300002068002808BFFFDF20684178002944D129 +:10F310000178FF2941D0002680F83160A0F85A60BA +:10F32000867080F83960304609F062FB104802AD03 +:10F3300000F1240191E80E1085E80E10D0E90D10BF +:10F34000CDE9061002A809F041FB08F0BCFF2068D7 +:10F3500090F9610009F090F8064809F093F8064822 +:10F360000CE00000680000201A06002053E4B36E91 +:10F37000C8610200D0610200CD61020009F012FBF9 +:10F38000606809F038FB206890F8240010F0010F45 +:10F3900007D0252009F07EF80AE009B00C20BDE86E +:10F3A000F08310F0020F18BF262069D009F072F820 +:10F3B000206890F85E10252008F043FF206880F850 +:10F3C0002C6009F00FFB206890F85E10002009F017 +:10F3D00028F90F21052008F0F8FF206890F82E107A +:10F3E000002901BF90F82F10002990F8220010F09A +:10F3F000040F74D006F0B8FE0546206829468068E0 +:10F4000007F0AAFBDFF82884074690FBF8F008FB1A +:10F4100010704142284606F08EFB2168886097FBF9 +:10F42000F8F04A68104448600EF062FA014620681D +:10F43000426891426ED8C0E90165FE4D4FF0010867 +:10F4400095F82D000EF063FA814695F82F000127FC +:10F45000002818BFB9F1000F04D095F82D000EF068 +:10F460008AF8A0B195F8300000281CBF95F82E004E +:10F47000002824D0687B05F10E01012815D019E081 +:10F4800010F0040F14BF2720FFDF8FD190E73A461A +:10F490006F7305F10E0148460AF0B6F895F82D1085 +:10F4A00005F10E000EF035FB09E0487900F0C000D0 +:10F4B000402815D0414605F10E000AF0DDF820681D +:10F4C00090F8220010F0040F24D095F82D000EF0D3 +:10F4D000EDF805001ED0102101F09AFC40B119E0B2 +:10F4E0000EF054FB3A4605F10E010AF08DF8E6E7FE +:10F4F00020683A4600F11C01C77628460AF084F8D5 +:10F50000206800F11C0160680EF060FC0121606859 +:10F510000EF077FC2068417B0E3008F038FF206841 +:10F5200090F85C1061B3B0F85810A0F84810416D25 +:10F53000416490F82210C1F30011F1B9B0F86A00EB +:10F540000221C0F30B05ADF80050684607F0B0FF8C +:10F5500028B1BDF80000C0F30B00A84204D1BDF8EB +:10F560000000401CADF800002168BDF80000B1F8B3 +:10F570006A2060F30B02A1F86A20206880F85C60C2 +:10F58000206890F85D1039B1B0F85010A0F8401024 +:10F59000C16CC16380F85D60B0F86A10426EC1F35F +:10F5A0000B0151FA82F190F86020DFF88CC211440F +:10F5B00063460022E1FB0C3212096FF0240302FBC8 +:10F5C000031180F860100EF00CFA032160680EF051 +:10F5D00090FA216881F8330009B00020BDE8F0837B +:10F5E0009649886070472DE9F043944C83B02268B7 +:10F5F00092F831303BB1508C1D2808BFFFDF03B0BB +:10F60000BDE8F0435FE401260027F1B1054692F81A +:10F61000600008F03FFF206890F85F10FF2008F0BE +:10F6200010FE20684FF4A57190F85F20002009F0CB +:10F63000D4F8206890F8221011F0030F00F02C810C +:10F64000002D00F0238100F027B992F822108046A7 +:10F65000D07EC1F30011002956D0054660680780AE +:10F66000017821F020010170518C132937D01FDC63 +:10F67000102908BF022144D0122908BF062140D01A +:10F68000FFDF6C4D606805F10E010EF091FB697BA8 +:10F6900060680EF0A9FB2068418C1D2918BF152950 +:10F6A00063D0B0F84820416C60680EF0B6FB5CE0B7 +:10F6B000152918BF1D29E3D14FF001010EF052FBAF +:10F6C0006068017841F020010170216885B11C312A +:10F6D0000EF07CFB012160680EF093FBD1E7002166 +:10F6E0000EF040FB6068017841F020010170C8E72E +:10F6F00015310EF06BFB2068017D60680EF081FB18 +:10F70000BFE70EF02FFBBCE70021FFF728FC606885 +:10F71000C17811F03F0F28D0017911F0100F24D0DB +:10F720000EF01EFB2368024693F82410C1F38000FC +:10F73000C1F3400C604401F00101084493F82C101F +:10F74000C1F3800CC1F34005AC4401F001016144F8 +:10F75000401AC1B293F85E0000F0BEFE0090032391 +:10F760000422694660680EF0DAFC2068002590F8F3 +:10F77000241090F82C0021EA000212F0010F18BFAB +:10F7800001250ED111F0020F04D010F0020F08BFB6 +:10F79000022506D011F0040F03D010F0040F08BFAB +:10F7A0000425B8F1000F2BD0012D1BD0022D08BF6E +:10F7B00026201BD0042D14BFFFDF272016D0206881 +:10F7C00090F85E10252008F03CFD206890F822108B +:10F7D000C1F3001169B101224FF49671002008F0C5 +:10F7E000FCFF0DE0252008F055FEE8E708F052FE8A +:10F7F000E5E790F85E204FF49671002008F0EDFFE9 +:10F80000206890F82C10294380F82C1090F82420C0 +:10F8100032EA01011CD04670418C13292BD026DC22 +:10F82000102904BF03B0BDE8F083122923D007E0FC +:10F8300040420F000C06002053E4B36E6800002025 +:10F84000C1F30010002818BFFFDF03B0BDE8F0834C +:10F85000418C1D2908BF80F82C70DCD0C1F3001149 +:10F86000002914BF80F8316080F83170D3E7152982 +:10F8700018BF1D29DBD190F85E2003B04FF00101C5 +:10F88000BDE8F043084609F094B900BF90F85F2046 +:10F890000121084609F08DF92168002DC87E7CD031 +:10F8A0004A8C3D46C2F34000002808BF47F00805D7 +:10F8B00012F0400F18BF45F04005002819BFD1F8DD +:10F8C0003C90B1F84080D1F84490B1F8488060682D +:10F8D000072107800EF046FA002160680EF039FC1F +:10F8E000294660680EF041FC15F0080F17D020681B +:10F8F000BDF800100223B0F86A2062F30B01ADF8E6 +:10F90000001090F86B00032201099DF8010061F3DB +:10F9100007108DF80100694660680EF000FC606811 +:10F920000EF0DCFA2168C0F1FE00B1F85A20A8EB15 +:10F9300002018142A8BF0146CFB2D019404544D24E +:10F9400045F0100160680EF010FC60680EF0C6FA19 +:10F950002168C0F1FE00B1F85A10A8EB0101814204 +:10F96000A8BF0146CFB260680EF0EFFB3844421CDE +:10F970002068B0F86A10436EC1F30B0151FA83F1AD +:10F9800090F86030FE4D1944AC460023E1FB05C3FE +:10F990004FEA131C6FF0240300E03CE00CFB031162 +:10F9A00080F8601090F85F00012100F095FD009054 +:10F9B000BDF800009DF80210032340EA01400190C9 +:10F9C000042201A960680EF0AAFB216891F82200C8 +:10F9D00010F0400F05D001230622613160680EF05F +:10F9E0009EFB20683A46B0F85A0000EB09016068B7 +:10F9F0000EF0E9FB2068B0F85A103944A0F85A100C +:10FA000009F0BFFC002818BFFFDF20684670867031 +:10FA100003B0BDE8F0830121FFF7A1FAF0E7D94870 +:10FA200010B50068417841B90078FF2805D0002161 +:10FA30000846FFF7D8FD002010BD09F05FF809F077 +:10FA40003EF808F007FF08F0B3FF0C2010BD2DE9C9 +:10FA5000F041CC4D0446174628680E4690F86C00DD +:10FA6000002818BFFFDF2868002F80F86E7018BFCD +:10FA7000BDE8F0812188A0F870106188A0F8861098 +:10FA8000A188A0F88810E188A0F88A1094F888115D +:10FA900080F88C1090F82F10002749B1427B00F1BC +:10FAA0000E01012A04D1497901F0C001402935D065 +:10FAB00090F8301041B1427B00F10E01012A04BFE1 +:10FAC000497911F0C00F29D000F17A00F3F794FAC8 +:10FAD0006868FF2E0178C1F380116176D0F80310B9 +:10FAE000C4F81A10B0F80700E08328681ED0C0F8E8 +:10FAF0008010E18BA0F8841000F17402511E304692 +:10FB00000DF014FF002808BFFFDF286890F873107D +:10FB100041F0020180F87310BDE8F081D0F80E10BA +:10FB2000C0F87A10418AA0F87E10D1E7C0F8807042 +:10FB3000A0F88470617E80F87310D4F81A104167C1 +:10FB4000E18BA0F87810BDE8F08170B58D4C0125EF +:10FB5000206890F82200C0F3C00038B13C22FF2199 +:10FB6000A068FFF774FF206880F86C50206890F858 +:10FB7000220010F0010F1CBFA06801884FF03C026A +:10FB800012BF01204FF6FF710020FEF717FD20681D +:10FB900080F8395070BD7B49096881F832007047A0 +:10FBA0002DE9F041774C0026206841780127354641 +:10FBB000012906D0022901D003297DD0FFDFBDE84D +:10FBC000F081817802250029418C46D0C1F34002A2 +:10FBD000002A08BF11F0010F6FD090F85F204FF09E +:10FBE00001014FF0000008F0E4FF216891F82200C5 +:10FBF000C0F34000002814BF0C20222091F85F10B1 +:10FC000008F01FFB2068457090F8330058B108F0E9 +:10FC100068F8206890F85F0010F00C0F0CBF4020CF +:10FC2000452008F077FF206890F83400002818BFBE +:10FC300008F08FFF216891F85F0091F8691010F0CB +:10FC40000C0F08BF0021962008F0F6FE09F090FB8B +:10FC5000002818BFFFDFBDE8F081C1F3001282B1B8 +:10FC600010293FD090F8330020B108F03AF8402036 +:10FC700008F050FF206890F8221011F0040F36D0E1 +:10FC800043E090F8242090F82C309A422AD1B0F822 +:10FC90004800002808BF11F0010F05D111F0020F34 +:10FCA00008BF11F0200F6FD04FF001014FF000009E +:10FCB000FFF799FC206801E041E035E0418C11F04C +:10FCC000010F04BFC1F34001002907D1B0F85A1059 +:10FCD000B0F84820914218BFBDE8F08180F831703B +:10FCE000BDE8F081BDE8F041002101207BE490F8FF +:10FCF0003710012914BF0329102646F00E0190F891 +:10FD00005E204FF0000008F054FF206890F83400A7 +:10FD1000002818BF08F01DFF0021962008F08CFE77 +:10FD200020684570BDE8F081B0F85A10B0F848007E +:10FD3000814242D0BDE8F0410121084653E4817878 +:10FD4000D9B1418C11F0010F22D080F86C7090F87D +:10FD50006E20B0F870100120FEF730FC206845706E +:10FD600008F0CCFE08F0ABFE08F074FD08F020FEB1 +:10FD7000BDE8F04103200AF07CB88178012004E05E +:10FD800053E4B36E6800002017E0BDE8F0412AE4B8 +:10FD900011F0020F04BFFFDFBDE8F081B0F85A1088 +:10FDA000B0F84000814208D001210846FFF71BFC53 +:10FDB000216803204870BDE8F081BDE8F041FFF7FD +:10FDC00082B8FFF780B810B5FE4C206890F8341068 +:10FDD00049B1383008F0CCFE18B921687F2081F88D +:10FDE000380008F0ACFE206890F8330018B108F035 +:10FDF0009BFE07F08AFF0AF02EFCA8B1206890F85D +:10FE00002210C1F3001179B14078022818BFFFDF3A +:10FE100000210120FFF7E7FB2068417800291EBF81 +:10FE200040780128FFDF10BDBDE81040FFF74BB858 +:10FE30002DE9F047E34C0F4680462168B8F1030FE7 +:10FE4000488C08BFC0F3400508D000F0010591F8C8 +:10FE50003200002818BF4FF0010901D14FF000090E +:10FE600008F00CFB0646B8F1030F0CBF4FF0020878 +:10FE70004FF0010835EA090008BFBDE8F0872068A7 +:10FE800090F8330068B10DF08FFD38700146FF28FF +:10FE900007D06068C01C0DF060FD38780DF091FD52 +:10FEA000064360680178C1F3801221680B7D9A4295 +:10FEB00008D10622C01C1531FEF792FA002808BFAF +:10FEC000012000D000203978FF2906D0C8B9206869 +:10FED00090F82D00884216D113E0A0B1616811F8A6 +:10FEE000030BC0F380100DF006FD05460DF061FE1A +:10FEF00038B128460DF0DAFB18B1102100F088FF68 +:10FF000008B1012000E00020216891F8221011F0D2 +:10FF1000040F01D0F0B11AE0CEB9AB4890F8370029 +:10FF2000002818BF404515D1616811F8030BC0F3D4 +:10FF300080100DF0E0FC04460DF03BFE38B1204689 +:10FF40000DF0B4FB18B1102100F062FF10B10120D8 +:10FF5000BDE8F0870020BDE8F0872DE9F04F994D0E +:10FF6000044683B0286800264078022818BFFFDFC7 +:10FF700028684FF07F0B90F8341049B1383008F002 +:10FF8000F7FD002804BF286880F838B008F0D7FDD6 +:10FF900068680DF009FF8046002C00F0458208F0EB +:10FFA00010FA002800F04082012400274FF0FF09DA +:10FFB000B8F1050F1ED1686890F8240000F01F000A +:10FFC000102817D9286890F8360098B18DF800905D +:10FFD00069460520FFF72CFF002800F025822868DD +:10FFE00080F8A64069682222A730C91CFEF725FACE +:10FFF00000F01ABA68680EF062F8002800F0148267 +:020000040001F9 +:100000004046DFF8C4814FF0030A062880F02182C1 +:10001000DFE800F0FCFCFC03FCFB8DF80090694677 +:100020000320FFF705FF002800F0F180296891F810 +:10003000340010B191F89C00D8B12868817801296A +:100040004DD06868042107800DF08CFE08F10E0188 +:1000500068680DF0ADFE98F80D1068680DF0C4FEEC +:100060002868B0F84020C16B68680DF0FAFE00F017 +:1000700063B99DF8000081F89C400A7881F89D20C2 +:10008000FF280FD001F19F029E310DF04FFC002898 +:1000900008BFFFDF286890F89E1041F0020180F849 +:1000A0009E100DE068680278C2F3801281F89E20ED +:1000B000D0F80320C1F89F20B0F80700A1F8A300F2 +:1000C000286800F1A50490F838007F2808BFFFDFFA +:1000D000286890F83810217080F838B0ADE790F8B3 +:1000E00022000721C0F3801938480479686869F351 +:1000F000861407800DF036FE002168680EF029F89E +:10010000214668680EF031F80623002208F10E013E +:1001100068680EF004F82868417B68680DF064FE9A +:1001200068680DF0DBFE2968B1F84020C0F1FE01DF +:100130008A42B8BF1146CFB2BA423CD9F81EC7B204 +:1001400044F0100B594668680EF00FF868680DF01F +:10015000FCFF384400F101082868B0F86A10426ECC +:10016000C1F30B0151FA82F190F86020184C0A4457 +:10017000A4460023E2FB04C319096FF0240301FB2A +:10018000032180F8601090F85F004246012100F0E2 +:10019000A3F90190BDF804009DF80610032340EA7E +:1001A00001400290042202A968680DF0B8FF594688 +:1001B00068680DF0DAFFB9F1000F0FD0D5E9001033 +:1001C000012307E0680000200C060020C86102003F +:1001D00053E4B36E062261310DF0A1FF28683A4660 +:1001E000C16B68680DF0EFFF2868A0F85A70B0F88E +:1001F00040108F420CBF0121002180F8311009F01E +:10020000C0F8002818BFFFDF96E007E021E128686A +:100210008078002840F00A8100F006B98DF800903F +:1002200068680178C1F38019D0F803100191B0F823 +:100230000700ADF8080069460520FFF7F9FD002822 +:1002400028687DD0817800297CD090F85FB0D5E90E +:100250000104D0F80F10C4F80E10B0F8131061822A +:10026000417D2175817D6175B0F81710E182B0F88C +:1002700019106180B0F81B10A180B0F81D10E1804A +:1002800000F11F0104F1080015F085FE686890F880 +:10029000241001F01F01217690F82400400984F811 +:1002A000880184F864B084F865B01BF00C0F0CBFB3 +:1002B0000021012104F130000EF0ABF92868002282 +:1002C00090F8691084F8661090F8610084F867006F +:1002D0009DF80010A868FFF7BAFB022009F0C9FDDD +:1002E000B2480DF1040B08210468686807800DF01E +:1002F00039FD002168680DF02CFF214668680DF07B +:1003000034FF0623002208F10E0168680DF007FF94 +:100310002868417B68680DF067FD494668680DF004 +:1003200070FD06230122594668680DF0F8FE09F0B9 +:1003300028F8002818BFFFDF286880F801A077E0C0 +:100340006DE0FFE76868D5F808804FF00109027892 +:1003500098F80D10C2F34012114088F80D10D0F833 +:100360000F10C8F80E10B0F81310A8F81210417D45 +:1003700088F81410817D88F81510B0F81710A8F8C7 +:100380001610B0F81910A8F80210B0F81B10A8F851 +:100390000410B0F81D10A8F8061000F11F0108F1B4 +:1003A000080015F0F8FD686890F8241001F01F01AE +:1003B00088F8181090F824000021400988F8880176 +:1003C00088F8649088F8659008F130000EF021F903 +:1003D0002868002290F8691088F8661090F861008B +:1003E00088F867009DF80010A868FFF730FB2868C0 +:1003F00080F86C4090F86E20B0F870100120FEF785 +:10040000DDF82868477008F079FB08F058FB08F021 +:1004100021FA08F0CDFA012009F02BFD08E090F850 +:100420002200C0F3001008B1012601E0FEF74BFDE9 +:10043000286890F8330018B108F076FB07F065FCE7 +:1004400096B10AF008F960B100210120FFF7CBF85E +:1004500013E0286890F82200C0F300100028E5D0CF +:10046000E2E7FEF730FD08E028688178012904D131 +:1004700090F85F10FF2007F0E4FE2868417800291B +:1004800019BF4178012903B0BDE8F08F40780328F7 +:1004900018BFFFDF03B0BDE8F08F70B5444C0646CF +:1004A0000D462068807858B107F0F2FD21680346B8 +:1004B000304691F85F202946BDE870400AF085BAC1 +:1004C00007F0E6FD21680346304691F85E20294694 +:1004D000BDE870400AF079BA78B50C460021009169 +:1004E000082804BF4FF4C87040210DD0042804BF71 +:1004F0004FF4BF70102107D0022807BF01F1180088 +:10050000042101F128000821521D02FB01062848A0 +:100510009DF80010006890F8602062F3050141F03A +:1005200040058DF8005090F85F00012829D002287E +:100530002ED004281CBF0828FFDF2FD025F0800014 +:100540008DF80000C4EB041000EB80004FF01E019A +:1005500001EB800006FB04041648844228BFFFDF3D +:100560001548A0FB0410BDF80110000960F30C0150 +:10057000ADF80110BDF800009DF8021040EA0140FE +:1005800078BD9DF8020020F0E0008DF80200D5E76C +:100590009DF8020020F0E000203004E09DF8020009 +:1005A00020F0E00040308DF80200C7E7C86102008B +:1005B00068000020C4BF0300898888880023C383A3 +:1005C000428401EBC202521EB2FBF1F1018470477A +:1005D0002DE9F04104460026D9B3552333224FF4C8 +:1005E000FA4501297DD0022900F01481032918BFA2 +:1005F000BDE8F08104F17001207B00F01F00207342 +:1006000084F889605FF0000004EB000C9CF808C0DF +:1006100003EA5C05ACEB050C0CF0FF0C0CF03305A9 +:1006200002EA9C0CAC440D180CEB1C1C0CF00F0CDB +:1006300085F814C04D7E401CAC44C0B281F819C08E +:100640000528E1D30CF0FF00252898BFBDE8F08114 +:10065000DCE0FFE704F17005802200212846FDF769 +:1006600016FFAE71EE712E736E73EE732E746E7193 +:10067000AE76EE76212085F84000492085F84100CD +:10068000FE2085F874002588702200212046FDF7A1 +:10069000FEFE2580012584F8645084F865502820EA +:1006A00084F86600002104F130000DF0B2FF1B2237 +:1006B000A4F84E20A4F85020A4F85220A4F8542006 +:1006C0004FF4A470A4F85600A4F8580065734FF4D2 +:1006D00048606080A4F8F060A4F8F260A4F8F460C8 +:1006E00000E023E0A4F8F660A4F8F86084F8FA606B +:1006F00084F8FD60A4F8066184F80461A4F8186128 +:10070000A4F81A6184F8B66184F8B76184F8C0610E +:1007100084F8C16184F88C6184F88F6184F8A861E1 +:10072000C4F8A061C4F8A461BDE8F081A4F8066132 +:1007300084F8FB606088FE490144B1FBF0F1A4F845 +:1007400090104BF68031A4F89210B4F806C0A4F8CB +:100750009860B4F89C704FEACC0C4743BCFBF0FCAB +:1007600097FBF0F70CF1010CA4F89C701FFA8CFCBD +:100770000CFB00F704F17001A4F89AC0B7F5C84F5C +:10078000C4BFACF1010CA1F82AC0B5FBF0FC0CF120 +:10079000010CA1F830C000F5802C0CF5EE3CACF15A +:1007A0000105B5FBF0FCA1F820C0CD8B05FB00FCDA +:1007B000BCFBF0F0C8830846217B01F01F012173C8 +:1007C0004676002104EB010C9CF808C003EA5C05A6 +:1007D000ACEB050C0CF0FF0C0CF0330502EA9C0CA2 +:1007E000AC4445180CEB1C1C0CF00F0C85F814C025 +:1007F000457E491CAC44C9B280F819C00529E1D333 +:100800000CF0FF00252898BFBDE8F081FFDFBDE8B0 +:10081000F08100BFB4F8B011B4F8B4316288A4F824 +:100820009860B4F89CC0DB000CFB02FCB3FBF1F356 +:100830009CFBF1FC5B1CA4F89CC09BB203FB01FC7D +:1008400004F17000A4F89A30BCF5C84FC4BF5B1E19 +:100850004385B5FBF1F35B1C0386438C01EBC303BB +:100860005B1EB3FBF1F30384C38B5A43B2FBF1F17C +:10087000C183BDE8F0812DE9F04104460025A1B314 +:1008800055234FF4FA464FF0330C01297DD002294D +:1008900000F0E080032918BFBDE8F08104F170008A +:1008A000217B01F01F01217384F889500021621817 +:1008B000127A03EA5205521BD2B202F033050CEA57 +:1008C00092022A44451802EB121202F00F022A7516 +:1008D000457E491C2A44C9B242760529E7D3D0B2E5 +:1008E000252898BFBDE8F081B1E0FFE704F170066C +:1008F000802200213046FDF7CAFDB571F5713573D0 +:100900007573F57335747571B576F576212086F8B3 +:100910004000492086F84100FE2086F874002688B1 +:10092000702200212046FDF7B2FD2680012684F8C2 +:10093000646084F86560282084F86600002104F172 +:1009400030000DF066FE1B22A4F84E20A4F85020C3 +:10095000A4F85220A4F854204FF4A470A4F8560030 +:10096000A4F858006673A4F8F850202084F8FA0020 +:1009700084F8F050C4F8F45084F8245184F82551D8 +:1009800084F82E5184F82F5100E005E084F81451CA +:1009900084F82051BDE8F081618865480844B0FBC7 +:1009A000F1F0A4F890004BF68030A4F89200E288B1 +:1009B000A4F89850B4F89C70D2004F43B2FBF1F207 +:1009C00097FBF1F7521CA4F89C7092B202FB01F75E +:1009D00004F17000A4F89A20B7F5C84FC4BF521EA6 +:1009E0004285B6FBF1F2521C028601F5802202F527 +:1009F000EE32561EB6FBF1F20284C68B06FB01F204 +:100A0000B2FBF1F1C1830146207B00F01F0020738F +:100A10004D7600202218127A03EA5205521BD2B2F8 +:100A200002F033050CEA92022A440D1802EB12126E +:100A300002F00F022A754D7E401C2A44C0B24A764D +:100A40000528E7D3D0B2252898BFBDE8F081FFDFA5 +:100A5000BDE8F081D0F81811628804F1700348896C +:100A6000C989A4F89850B4F89CC0C9000CFB02FCDA +:100A7000B1FBF0F19CFBF0FC491CA4F89CC089B2CE +:100A800001FB00FCA4F89A10BCF5C84FC4BF491E76 +:100A90005985B6FBF0F1491C1986598C00EBC10150 +:100AA000491EB1FBF0F11984D98B5143B1FBF0F031 +:100AB000D883BDE8F0812DE9F003447E0CB1252CEC +:100AC00003D9BDE8F00312207047002A02BF0020BE +:100AD000BDE8F003704791F80DC01F260123154DA6 +:100AE0004FF00008BCF1000F7AD0BCF1010F1EBF1F +:100AF0001F20BDE8F0037047B0F800C00A7C8F7B70 +:100B000091F80F907A404F7C87EA090742EA072262 +:100B100082EA0C0C5FF000070CF0FF0999FAA9F9C2 +:100B20004FEA1C2C4FEA19699CFAACFC04E0000067 +:100B3000FFDB050053E4B36E4FEA1C6C49EA0C2C52 +:100B40000CEB0C1C7F1C9444FFB21FFA8CFC032F8F +:100B5000E2D38CEA020CFB4F0022ECFB0572120977 +:100B60006FF0240502FB05C2D2B201EBD2078276F8 +:100B700002F007053F7A03FA05F52F4218BFC27647 +:100B80007ED104FB0CF2120C521CD2B25FF00004B6 +:100B900000EB040C9CF814C094453CBFA2EB0C0283 +:100BA000D2B212D30D194FF0000C2D7A03FA0CF7C4 +:100BB0003D421CBF521ED2B2002A69D00CF1010C7A +:100BC0000CF0FF0CBCF1080FF0D304F1010C0CF099 +:100BD000FF04052CDCD33046BDE8F0037047FFE787 +:100BE00090F81AC00C7E474604FB02C2D54C4FF069 +:100BF000000CE2FB054C4FEA1C1C6FF024040CFBBC +:100C00000422D2B201EBD204827602F0070C247ADD +:100C100003FA0CFC14EA0C0F1FBFC2764046BDE875 +:100C2000F003704790F819C0B2FBFCF40CFB1422DF +:100C3000521CD2B25FF0000400EB040C9CF814C00C +:100C400094453CBFA2EB0C02D2B212D30D194FF067 +:100C5000000C2D7A03FA0CF815EA080F1CBF521E7F +:100C6000D2B272B10CF1010C0CF0FF0CBCF1080F08 +:100C7000F0D304F1010C0CF0FF04052CDCD3AAE73F +:100C800009E00CEBC401C1763846BDE8F0037047BB +:100C90000CEBC401C1764046BDE8F0037047AA4A98 +:100CA000016812681140A94A126811430160704737 +:100CB00030B4A749A44B00244FF0010C0A78521C11 +:100CC000D2B20A70202A08BF0C700D781A680CFA8C +:100CD00005F52A42F2D0097802680CFA01F1514078 +:100CE000016030BC704770B46FF01F02010C02EA63 +:100CF00090251F23A1F5AA4054381CBFA1F5AA4096 +:100D0000B0F1550009D0A1F52850AA381EBFA1F5B1 +:100D10002A40B0F1AA00012000D100204FF0000CC1 +:100D2000624601248CEA0106F6431643B6F1FF3F02 +:100D300011D005F001064FEA5C0C4CEAC63C03F00A +:100D4000010652086D085B08641C42EAC632162C84 +:100D5000E8DD70BC704770BC0020704790F804C09C +:100D60003CF01F011CBF0020704730B401785522B1 +:100D700002EA5103C91AC9B201F03304332303EA6A +:100D800091012144447801EB111102EA5405641BDE +:100D9000E4B204F0330503EA94042C4404EB141485 +:100DA00001F00F0104F00F040C448178C07802EACE +:100DB0005105491BC9B201F0330503EA91012944E9 +:100DC00001EB111101F00F01214402EA5004001B54 +:100DD000C0B200F0330403EA9000204400EB10108E +:100DE00000F00F00014402EA5C00ACEB0000C0B26E +:100DF00000F0330203EA9000104400EB101000F002 +:100E00000F00084401288CBF0120002030BC70472F +:100E10000A000ED00123012A0BDB491EC9B210F8CB +:100E200001C0BCF1000F01D0002070475B1C934251 +:100E3000F3DD01207047002A08BF70471144401EAF +:100E400012F0010F03D011F8013D00F8013F5208E4 +:100E500008BF704711F8013C437011F8023D00F8DB +:100E6000023F521EF6D1704770B58CB000F11004ED +:100E70001D4616460DF1FF3C5FF0080014F8012CEA +:100E80008CF8012014F8022D0CF8022F401EF5D129 +:100E900001F1100C6C460DF10F0108201CF8012C1B +:100EA0004A701CF8022D01F8022F401EF6D1204690 +:100EB00013F01CFB7EB16A1E04F130005FF00801E4 +:100EC00010F8013C537010F8023D02F8023F491E31 +:100ED000F6D10CB070BD08982860099868600A982F +:100EE000A8600B98E8600CB070BD38B505460C469C +:100EF000684607F03DFE002808BF38BD9DF9002078 +:100F00002272E07E607294F90A100020511A48BFE4 +:100F1000494295F82D308B42C8BF38BDFF2B08BF22 +:100F200038BDE17A491CC9B2E17295F82E30994278 +:100F300003D8A17A7F2918BF38BDA2720020E072C1 +:100F4000012038BD53E4B36E04620200086202005F +:100F5000740000200C2818BF0B2810D00D2818BFD3 +:100F60001F280CD0202818BF212808D0222818BFFD +:100F7000232804D024281EBF2628002070474FF0C5 +:100F8000010070470C2963D2DFE801F006090E1357 +:100F9000161B323C415C484E002A5BD058E0072AC1 +:100FA00018BF082A56D053E00C2A18BF0B2A51D07C +:100FB0004EE00D2A4ED04BE0A2F10F000C2849D98B +:100FC00046E023B1A2F110000B2843D940E0122AD9 +:100FD00018BF112A3ED090F8380020B1122A37D31A +:100FE0001A2A37D934E0162A32D31A2A32D92FE0F6 +:100FF000A2F10F0103292DD990F8380008B31B2A5C +:1010000028D925E0002B08BF042A21D122E013B102 +:10101000062A1FD01CE0012A1AD11BE01C2A1CBF83 +:101020001D2A1E2A16D013E01F2A18BF202A11D00D +:10103000212A18BF222A0DD0232A1CBF242A262A9F +:1010400008D005E013B10E2A04D001E0052A01D032 +:1010500000207047012070472DE9F0410D460446FD +:10106000866805F02FFA58B905F07EF840F236711F +:1010700004F061FDA060204605F024FA0028F3D0BA +:1010800095B13046A16805F067FD00280CDD2844C5 +:10109000401EB0FBF5F707FB05F1304604F04BFDB1 +:1010A000A0603846BDE8F0810020BDE8F08170B551 +:1010B0000446904228BF70BD101B64280BD325182E +:1010C0008D4206D8042105F07AFD00281CBF284671 +:1010D00070BD204670BD6420F1E711F00C0F13D0F5 +:1010E00001F0040100290DBF4022102296214FF487 +:1010F000167101F5BC71A0EB010388428CBF93FB14 +:10110000F2F0002080B27047022919BF6FF00D0184 +:1011100001EBD0006FF00E0101EB9000F2E7084404 +:1011200018449830002A14BF042100210844704755 +:1011300010B4002A14BF4FF429624FF4A472002B9C +:1011400019BF4FF429634FF0080C4FF4A4734FF00C +:10115000010C00280CBF0124002491F866001CF04B +:101160000C0F08BF0020D11808449830002C14BF81 +:1011700004210021084410BC704700280CBF012343 +:10118000002391F86600002BA0F6482000F50050DF +:1011900018BF04231844496A81422CBF0120002053 +:1011A00012F00C0118BF012131EA000014BF002029 +:1011B0000120704710B413680B66137813F00C030A +:1011C00018BF0123527812F00C0218BF012253EA13 +:1011D000020C04BF10BC7047002B0CBF4FF4A4736B +:1011E0004FF42963002A19BF4FF429624FF0080C0D +:1011F0004FF4A4724FF0010C00280CBF012400240E +:1012000091F866001CF00C0F08BF00201A4410442F +:101210009830002C14BF0422002210444A6A8242F3 +:1012200024BF10BC704791F860004FF0030230F00B +:101230000C0381F8603091F8610020F00C0081F817 +:10124000610008BF81F86020002808BF81F8612094 +:1012500010BC704710F0010F1CBF0120704710F048 +:10126000020F1CBF0220704710F0040018BF0820B6 +:1012700070472DE9F0470446174689464FF00108AC +:1012800008460DF0FAF8054648460DF0FAF810F059 +:10129000010F18BF012624D015F0010F18BF01233C +:1012A0002AD000BF56EA030108BF4FF0000810F033 +:1012B000070F08BF002615F0070F08BF002394F89A +:1012C0006400B0420CBF00203046387094F86510BE +:1012D000994208BF00237B70002808BF002B25D14E +:1012E00015E010F0020F18BF0226D5D110F0040F40 +:1012F00014BF08260026CFE715F0020F18BF0223FF +:10130000D0D115F0040F14BF08230023CAE74846C4 +:101310000DF0BDF8B4F87010401A00B247F6FE7137 +:10132000884201DC002801DC4FF0000816B1082ECD +:101330000CD018E094F86400012818BF022812D0DD +:1013400004281EBF0828FFDF032D0CD194F8C0012C +:1013500048B1B4F8C401012894F8640006D0082804 +:1013600001D0082038704046BDE8F087042818BF37 +:101370000420F7D1F5E7012814BF0228704710F0C8 +:101380000C0018BF0420704738B4CBB2C1F3072C4F +:10139000C1B2C0F30724012B07D0022B09D0042BC4 +:1013A00008BFBCF1040F2DD006E0BCF1010F03D142 +:1013B00028E0BCF1020F25D0012906D0022907D070 +:1013C000042908BF042C1DD004E0012C02D119E02F +:1013D000022C17D001EA0C0161F3070204EA0301B1 +:1013E00061F30F22D1B211F0020F18BF022310D007 +:1013F000C2F307218DF8003011F0020F18BF02214F +:101400001BD111E0214003EA0C03194061F30702EC +:10141000E6E711F0010F18BF0123E9D111F0040F25 +:1014200014BF08230023E3E711F0010F18BF0121C7 +:1014300003D111F0040118BF08218DF80110082B09 +:1014400001BF000C012804208DF80000BDF8000049 +:1014500038BC70474FF0000C082902D0042909D08D +:1014600011E001280FD10420907082F803C013808E +:1014700001207047012806D00820907082F803C030 +:1014800013800120704700207047162A10D12A22AD +:101490000C2818BF0D280FD04FF0230C1F280DD09B +:1014A00031B10878012818BF002805D0162805D0CA +:1014B00000207047012070471A70FBE783F800C0D6 +:1014C000F8E7012908D002290BD0042912BF082906 +:1014D00040F6A660704707E0002804BF40F2E240F3 +:1014E000704740F6C410704700B5FFDF40F2E2409D +:1014F00000BD00000178406829B190F82C1190F8E7 +:101500008C0038B901E001F0BDBD19B1042901D04A +:10151000012070470020704770B50C460546062133 +:1015200002F0C4FC606008B1002006E007212846F4 +:1015300002F0BCFC606018B101202070002070BD7A +:10154000022070BD2DE9FC470C4606466946FFF7B0 +:10155000E3FF00287DD19DF8000050B1FDF7EEF8C3 +:10156000B0427CD0214630460AF008FC002873D1F6 +:101570002DE00DF097F9B04271D02146304612F0BF +:10158000B6FA002868D1019D95F8F00022E001200C +:1015900000E00020804695F839004FF0010A4FF036 +:1015A0000009F0B195F83A0080071AD584F8019047 +:1015B00084F800A084F80490E68095F83B1021722E +:1015C000A98F6181E98FA18185F8399044E0019D5F +:1015D00095F82C0170350028DBD1287F0028D8D061 +:1015E000D5E7304602F0A5FD070000D1FFDF384601 +:1015F00001F0B5FF40B184F801900F212170E68021 +:10160000208184F804A027E0304602F080FD070026 +:1016100000D1FFDFB8F1000F21D0384601F0F7FF0D +:10162000B8B19DF8000038B90198D0F81801418888 +:10163000B14201D180F80090304607F00DFF84F8E8 +:1016400001900C21217084F80490E680697F21725A +:1016500000E004E085F81C900120BDE8FC87002034 +:10166000FBE71CB56946FFF757FF00B1FFDF68468F +:1016700001F014FDFE4900208968A1F8F2001CBDAC +:101680002DE9FC4104460E46062002F0B7FB054654 +:10169000072002F0B3FB2844C7B20025A8463E4409 +:1016A00017E02088401C80B22080B04202D3404620 +:1016B000A4F8008080B2B84204D3B04202D2002025 +:1016C000BDE8FC816946FFF727FF0028F8D06D1CB4 +:1016D000EDB2AE42E5D84FF6FF7020801220EFE762 +:1016E00038B54FF6FF70ADF800000DE00621BDF8EB +:1016F000000002F0EDFB04460721BDF8000002F0F7 +:10170000E7FB0CB100B1FFDF00216846FFF7B8FF2F +:101710000028EBD038BD70B507F00CFF0BF034FF9C +:10172000D44C4FF6FF76002526836683D2A0257021 +:1017300001680079A4F14002657042F8421FA11CC3 +:101740001071601C12F0EFFA1B2020814FF4A4717D +:101750006181A081E18107212177617703212174D3 +:10176000042262746082A082A4F13E00E1820570CE +:101770004680BF480C300570A4F11000057046800B +:1017800084F8205070BD70B5B94C16460D466060A7 +:10179000217007F047FEFFF7A3FFFFF7BCFF20789B +:1017A0000FF0BDFFB6480DF0D0F92178606812F057 +:1017B0005FFA20780BF0DCF8284608F0AFFEB0485E +:1017C000FCF7C7FF217860680AF0B2FB3146207849 +:1017D00012F024FDBDE870400BF0D6BE10B5012418 +:1017E0000AB1002010BD21B1012903D000242046F8 +:1017F00010BD02210CF024FDF9E710B50378044672 +:10180000002B406813460A46014609D05FF00100EC +:10181000FFF78EFC6168496A884203D9012010BD38 +:101820000020F5E7002010BD2DE9F04117468A7829 +:101830001E46804642B11546C87838B1044669074D +:1018400006D52AB1012104E00725F5E70724F6E7CC +:101850000021620702D508B1012000E0002001420A +:1018600006D0012211464046FFF7C7FF98B93DE078 +:1018700051B1002201214046FFF7BFFF58B9600770 +:1018800034D50122114620E060B1012200214046FA +:10189000FFF7B3FF10B10920BDE8F081680725D537 +:1018A000012206E068074FEA44700AD5002814DBDD +:1018B000002201214046FFF7A0FFB8B125F0040542 +:1018C00014E0002812DA012200214046FFF795FFBC +:1018D00060B100BF24F0040408E001221146404634 +:1018E000FFF78BFF10B125F00405F3E73D7034706E +:1018F0000020D1E770B58AB0044600886946FFF73A +:101900000BFE002806D1A08830B1012804D002289F +:1019100002D012200AB070BD04AB03AA214668466B +:10192000FFF782FF0500F5D19DF800100120002689 +:101930000029019906D081F8C101019991F80C1292 +:10194000B1BB2DE081F82F01019991F8561139B9F9 +:10195000019991F82E1119B9019991F8971009B1CF +:101960003A2519E00199059681F82E01019A9DF812 +:101970000C0082F83001019B9DF8102083F8312182 +:10198000A388019CA4F832318DF814008DF815203D +:1019900005AA0020FFF70EFC019880F82F6126E0D1 +:1019A000019991F8C01119B9019991F8971009B1ED +:1019B0003A2519E00199059681F8C00101989DF832 +:1019C0000C2080F8C221019B9DF8100083F8C30110 +:1019D000A388019CA4F8C4318DF814208DF815005B +:1019E00005AA0120FFF7E6FB019880F8C1612846AF +:1019F00090E710B504460020A17801B90120E278F3 +:101A00000AB940F0020001F058FB002803D120463B +:101A1000BDE810406EE710BD70B5044691F8650052 +:101A200091F866300D4610F00C0F00D1002321898B +:101A3000A088FFF774FB696A814229D2401A401CD2 +:101A4000A1884008091A8AB2A2802189081A208137 +:101A5000668895F864101046FFF73FFB864200D277 +:101A600030466080E68895F8651020890AE000001D +:101A70007800002018080020FFFFFFFF1F00000073 +:101A8000D8060020FFF729FB864200D23046E080CE +:101A900070BDF0B585B00D46064603A9FFF73CFDC5 +:101AA00000282DD19DF80C0060B300220499FB2082 +:101AB000B1F84E30FB2B00D30346B1F85040FB2069 +:101AC000FB2C00D30446DFF85CC59CE88110009035 +:101AD0000197CDF808C0ADF80230ADF80640684671 +:101AE000FFF79AFF6E80BDF80400E880BDF808009B +:101AF0006881BDF80200A880BDF80600288100209A +:101B000005B0F0BD0122D1E72DE9F04186B00446D1 +:101B100000886946FFF700FD002876D12189E0881A +:101B200001F0E4FA002870D1A188608801F0DEFAA3 +:101B300000286AD12189E08801F0CFFA002864D119 +:101B4000A188608801F0C9FA07005ED1208802A947 +:101B5000FFF79FFF00B1FFDFBDF81010628809207A +:101B6000914252D3BDF80C10E28891424DD3BDF89A +:101B70001210BDF80E2023891144A2881A44914204 +:101B800043D39DF80010019D4FF00008012640F658 +:101B9000480041B185F8B761019991F8F81105F550 +:101BA000DB7541B91AE085F82561019991F84A1170 +:101BB00005F5927509B13A2724E0E18869806188CA +:101BC000E9802189814200D30146A980A188814210 +:101BD00000D208462881012201990FE0E18869803E +:101BE0006188E9802189814200D30146A980A188CA +:101BF000814200D208462881019900222846FFF739 +:101C00000BFF2E7085F80180384606B044E67BE76E +:101C100070B504460CF0FCFDB0B12078182811D145 +:101C2000207901280ED1E088062102F03FF9040056 +:101C300008D0208807F010FC2088062102F048F91F +:101C400000B1FFDF012070BDF74D28780028FAD0E1 +:101C5000002666701420207020223146201DFCF7DB +:101C600016FC022020712E70ECE710B50446FCF73C +:101C7000DBFC002813D0207817280FD1207968B119 +:101C8000E088072102F012F940B1008807F0E4FB78 +:101C9000E088072102F01CF900B1FFDF012010BD30 +:101CA0002DE9F0475FEA000800D1FFDFDE4802219E +:101CB0001A308146FFF7E4FC00B1FFDFDA4C062062 +:101CC000678B02F09BF80546072002F097F828443E +:101CD000C5B2681CC6B2608BB04203D14046FFF764 +:101CE000C4FF58B9608BA84203D14046FFF790FF6C +:101CF00020B9608B4146FFF725FC38B1404601F022 +:101D000003FA0028E7D10120BDE8F0870221484608 +:101D1000FFF7B6FC10B9608BB842DCD14046BDE895 +:101D2000F04712F0C1BA10B501F053F908B10C2018 +:101D300010BD0BF07DFC002010BD10B504460078EE +:101D400018B1012801D0122010BD01F053F920B1C3 +:101D50000BF0C0FD08B10C2010BD207801F013F984 +:101D6000E21D04F11703611CBDE810400BF0DABC62 +:101D700010B5044601F02DF908B10C2010BD2078F3 +:101D800028B1012803D0FF280BD0122010BD01F08C +:101D9000FAF8611C0BF00CFC08B1002010BD072004 +:101DA00010BD01200BF03EFCF7E710B50BF095FDE0 +:101DB00008B1002010BD302010BD10B5044601F060 +:101DC00019F908B10C2010BD20460BF080FD002051 +:101DD00010BD10B501F00EF920B10BF07BFD08B17C +:101DE0000C2010BD0BF0F6FC002010BDFF2181700F +:101DF0004FF6FF7181808D4949680A7882718A881F +:101E000002814988418101214170002070477CB5E1 +:101E10000025022A19D015DC12F10C0F15D009DCAF +:101E200012F1280F11D012F1140F0ED012F1100F71 +:101E300011D10AE012F1080F07D012F1040F04D0FB +:101E40004AB902E0D31E052B05D8012806D0022886 +:101E500008D003280AD0122528467CBD1046FDF77D +:101E600013F8F9E710460CF06BFEF5E70846144648 +:101E70006946FFF751FB08B10225EDE79DF8000028 +:101E80000198002580F86740E6E710B51346012267 +:101E9000FEF7EAFF002010BD10B5044610F02FFA3F +:101EA000052804D020460FF029FC002010BD0C208E +:101EB00010BD10B5044601F09DF808B10C2010BD0E +:101EC0002146002007F037FB002010BD10B5044666 +:101ED0000FF0A3FC50B108F0A6FD38B1207808F04F +:101EE00029FB20780DF04DF9002010BD0C2010BD0D +:101EF00010B5044601F07EF808B10C2010BD214653 +:101F0000012007F018FB002010BD38B504464FF63D +:101F1000FF70ADF80000A079E179884216D02079F1 +:101F2000FCF7E3FA90B16079FCF7DFFA70B10022B8 +:101F3000A079114612F0A0FD40B90022E0791146C7 +:101F400012F09AFD10B9207A072801D9122038BD65 +:101F500008F076FD60B910F0D2F948B90021684662 +:101F6000FFF78EFB20B1204606F044F9002038BD73 +:101F70000C2038BD2DE9FC41817805461A2925D071 +:101F80000EDC16292ED2DFE801F02D2D2D2D2D216E +:101F90002D2D2D2D2D2D2D2D2D2D2D2D2D21212195 +:101FA0002A291FD00BDCA1F11E010C291AD2DFE86F +:101FB00001F019191919191919191919190D3A399D +:101FC00004290FD2DFE801F00E020E022888B0F5D6 +:101FD000706F07D201276946FFF79EFA20B10220F1 +:101FE000BDE8FC811220FBE79DF8000000F0D2FF65 +:101FF000019C10B104F58A7401E004F5C6749DF8E3 +:10200000000000F0C7FF019E10B106F2151601E0B6 +:1020100006F28D166846FFF76DFA08B1207838B1E0 +:102020000C20DDE70C620200180800207800002078 +:102030002770A8783070684601F030F80020CFE7AC +:102040007CB50D466946FFF767FA002618B12E6089 +:102050002E7102207CBD9DF8000000F09BFF019CCA +:102060009DF80000703400F095FF019884F84260FC +:1020700081682960017B297194F842100029F5D10B +:1020800000207CBD10B5044600F0B4FF20B10BF079 +:1020900021FC08B10C2010BD207800F074FFE2791B +:1020A000611C0BF093FD08B1002010BD022010BD93 +:1020B00010B5886E60B1002241F8682F0120CA7106 +:1020C0008979884012F0CCFC002800D01F2010BD78 +:1020D0000C2010BD1CB50C466946FFF71DFA002800 +:1020E00009D19DF8000000280198B0F8700000D0D8 +:1020F000401C208000201CBD1CB504460088694699 +:10210000FFF70AFA08B102201CBD606828B1DDE9BA +:102110000001224601F04CF81CBDDDE90001FFF78B +:10212000C7FF1CBD70B51C460D4618B1012801D073 +:10213000122070BD1946104601F078F830B12146E2 +:10214000284601F07DF808B1002070BD302070BD38 +:1021500070B5044600780E46012804D018B1022854 +:1021600001D0032840D1607828B1012803D002288B +:1021700001D0032838D1E07B10B9A078012833D1F1 +:10218000A07830F005012FD110F0050F2CD0628916 +:10219000E188E0783346FFF7C5FF002825D1A07815 +:1021A00005281DD16589A289218920793346FFF749 +:1021B000B9FF002819D1012004EB40014A891544D8 +:1021C0002218D378927893420ED1CA8889888A429D +:1021D0000AD1401CC0B20228EED3E088A84203D343 +:1021E000A07B08B1072801D9122070BD002070BD66 +:1021F00010B586B0044600F0E1FE10B10C2006B028 +:1022000010BD022104F10A0001F02FF8A0788DF82A +:102210000800A0788DF8000060788DF80400207820 +:102220008DF80300A07B8DF80500E07B00B1012054 +:102230008DF80600A078C10717D0E07801F00CF8FF +:102240008DF80100E088ADF80A006089ADF80C0057 +:10225000A078400716D5207900F0FEFF8DF8020027 +:102260002089ADF80E00A0890AE040070AD5E07881 +:1022700000F0F2FF8DF80200E088ADF80E006089F2 +:10228000ADF8100002A80FF0D4FA0028B7D16846C4 +:102290000CF07CFFB3E710B504460121FFF758FFAF +:1022A000002803D12046BDE81040A1E710BD027808 +:1022B000012A01D0BAB118E042783AB1012A05D01A +:1022C000022A12D189B1818879B100E059B14188DF +:1022D00049B1808838B101EB8101490000EB8000F1 +:1022E000B1EB002F01D2002070471220704770B56B +:1022F000044600780D46012809D010F000F80528A2 +:1023000003D00FF0A6F9002800D00C2070BD0CF00F +:102310000AFE88B10CF01CFE0CF018FF0028F5D165 +:1023200025B160780CF0ACFE0028EFD1A188608860 +:10233000BDE870400FF0A3BA122070BD10B504467E +:102340000121FFF7B4FF002804D12046BDE810406A +:102350000121CCE710BDF0B5871FDDE9056540F62A +:102360007B44A74213D28F1FA74210D288420ED8B7 +:10237000B2F5FA7F0BD2A3F10A00241FA04206D2C5 +:10238000521C4A43B2EB830F01DAAE4201D900205E +:10239000F0BD0120F0BD2DE9FC47477A894604468F +:1023A00017F0050F7ED0F8087CD194F83A0008B9F0 +:1023B000012F77D10025A8462E46F90789F0010A9A +:1023C00019D0208A514600F031FFE8B360895146A8 +:1023D00000F036FFC0B3208A6189884262D8A18E9E +:1023E000E08DCDE90001238D628CA18BE08AFFF79F +:1023F000B2FF48B30125B8070ED504EB4500828E25 +:10240000C18DCDE90012038D428C818BC08AFFF70C +:10241000A2FFC8B1A8466D1C78071ED504EB45067F +:102420005146308A00F002FF70B17089514600F0C9 +:1024300007FF48B1308A7189884253D8B18EF08D38 +:10244000CDE90001338D00E00BE0728CB18BF08A96 +:10245000FFF781FF28B12E466D1CB9F1000F03D0A4 +:1024600030E03020BDE8FC87F80707D0780705D5B5 +:1024700004EB460160894989884233D1228A0121CF +:102480001BE0414503D004EB4100008A024404EB09 +:102490004100C38A868AB34224D1838B468BB342E0 +:1024A00020D100E01EE0438C068CB3421AD1038D8C +:1024B000C08C834216D1491CC9B2A942E1D36089BC +:1024C00090420FD3207810B101280BD102E0A07800 +:1024D0000028F9D1607838B1012805D0022803D04E +:1024E000032801D01220BDE70020BBE7002152E7FE +:1024F0000178C90702D0406811F0A9BE11F076BE7C +:1025000010B50078012800D00020FCF7B8FC0020AE +:1025100010BD2DE9F0478EB00D46AFF6A422D2E9EA +:102520000092014690462846FFF735FF06000CD181 +:1025300000F044FD40B9FE4F387828B90CF0B2F9EC +:10254000A0F57F41FF3903D00C200EB0BDE8F08725 +:10255000032105F1100000F088FEF54809AA3E3875 +:102560000990F4480A90F248062110380B900CA804 +:1025700001F06AFC040037D00021FEF77CF904F179 +:1025800030017B8ABA8ACB830A84797C0091BA466F +:102590003B7CBA8A798A208801F044FD00B1FFDFD4 +:1025A000208806F058FF218804F10E0000F02CFD71 +:1025B000E1A004F1120700680590032105A804F0CA +:1025C0006DFF002005A90A5C3A54401CC0B20328E4 +:1025D000F9D3A88B6080688CA080288DE080687A11 +:1025E000410703D508270AE00920AEE7C10701D05B +:1025F000012704E0800701D5022700E000273A46C2 +:10260000BAF8160011460FF0CFF90146A062204635 +:102610000FF0D8F93A4621460020FEF7AEFD00B98A +:102620000926C34A21461C320020FEF7C3FD0027BD +:1026300084F8767084F87770A87800F0A4FC60764F +:10264000D5F80300C4F81A00B5F80700E083C4F811 +:10265000089084F80C80012084F8200101468DF850 +:102660000070684604F01AFF9DF8000000F00701B2 +:10267000C0F3C1021144C0F3401008448DF80000BB +:10268000401D2076092801D20830207601212046FD +:10269000FEF7F1F868780CF051FCEEBBA9782878C9 +:1026A000EA1C0CF01EFC48B10CF052FCA97828780A +:1026B000EA1C0CF0BFFC060002D052E0122650E0EB +:1026C000687A00F005010020CA0700D001208A07BF +:1026D00001D540F00200490701D540F008000CF098 +:1026E000E9FB06003DD1214603200CF0CDFC06009D +:1026F00037D10CF0D2FC060033D1697A01F0050124 +:102700008DF80810697AC90708D06889ADF80A0001 +:10271000288AADF80C0000E023E00120697A8A07DE +:1027200000D5401C490707D505EB40004189ADF8AD +:102730000E10008AADF8100002A80FF07AF80646D5 +:1027400095F83A0000B101200CF0C6FB4EB90CF030 +:10275000FDFC060005D1A98F20460FF00BF80600FE +:1027600008D0208806F078FE2088062101F0B0FB12 +:1027700000B1FFDF3046E8E601460020C9E638B583 +:102780006B48007878B90FF0BAFD052805D00CF039 +:1027900089F8A0F57F41FF3905D068460FF0B3F8FE +:1027A000040002D00CE00C2038BD0098008806F030 +:1027B00053FE00980621008801F08AFB00B1FFDF7C +:1027C000204638BD1CB582894189CDE900120389B4 +:1027D000C28881884088FFF7BEFD08B100201CBD7B +:1027E00030201CBD70B50546FFF7ECFF00280ED168 +:1027F0002888062101F05AFB040007D000F042FCB3 +:1028000020B1D4F81801017831B901E0022070BD7F +:10281000D4F86411097809B13A2070BD052181719D +:10282000D4F8181100200881D4F81811A88848811C +:10283000D4F81811E8888881D4F818112889C8813B +:10284000D4F81801028941898A4204D88279082A79 +:1028500001D88A4201D3122070BD29884180D4F862 +:10286000181102200870002070BD3EB50446FEF726 +:1028700075FAB0B12E480125A0F1400245702368D9 +:1028800042F8423F237900211371417069460620C6 +:1028900001F095FA00B1FFDF684601F06EFA10B161 +:1028A0000EE012203EBDBDF80440029880F8205191 +:1028B000684601F062FA18B9BDF80400A042F4D1EC +:1028C00000203EBD70B505460088062101F0EEFAF5 +:1028D000040007D000F0D6FB20B1D4F81811087816 +:1028E00030B901E0022070BDD4F86401007808B16D +:1028F0003A2070BDB020005D10F0010F22D0D5F855 +:1029000002004860D5F806008860D4F8180169898B +:1029100010228181D4F8180105F10C010E3004F564 +:102920008C74FBF78AFD216803200870288805E075 +:1029300018080020840000201122330021684880FC +:10294000002070BD0C2070BD38B504460078EF281B +:102950004DD86088ADF80000009800F097FC88B36F +:102960006188080708D4D4E9012082423FD8202A90 +:102970003DD3B0F5804F3AD8207B18B3072836D81E +:10298000607B28B1012803D0022801D003282ED172 +:102990004A0703D4022801D0032805D1A07B08B13F +:1029A000012824D1480707D4607D28B1012803D02D +:1029B000022801D003281AD1C806E07D03D50128DA +:1029C00015D110E013E0012801D003280FD1C8066B +:1029D00009D4607E012803D0022801D0032806D143 +:1029E000A07E0F2803D8E07E18B1012801D0122064 +:1029F00038BD002038BDF8B514460D46064608F02F +:102A00001FF808B10C20F8BD3046FFF79DFF0028E5 +:102A1000F9D1FCF73EFA2870B07554B9FF208DF853 +:102A2000000069460020FCF71EFA69460020FCF70A +:102A30000EFA3046BDE8F840FCF752B90022DAE75A +:102A40000078C10801D012207047FA4981F82000AF +:102A50000020704710B504460078C00704D1608894 +:102A600010B1FCF7D7F980B12078618800F001023D +:102A7000607800F02FFC002806D1FCF7B3F901467E +:102A80006088884203D9072010BD122010BD6168FC +:102A9000FCF7E9F9002010BD10B504460078C00726 +:102AA00004D1608810B1FBF78AFE70B1207861888C +:102AB00000F00102607800F00DFC002804D160886D +:102AC0006168FCF7C4F9002010BD122010BD7CB570 +:102AD000044640784225012808D8A078FBF767FE15 +:102AE00020B120781225012802D090B128467CBD63 +:102AF000FCF7DBF920B1A0880028F7D08028F5D8B2 +:102B0000FCF7DAF960B160780028EFD0207801286E +:102B100008D006F0C3FD044607F05DFC00287FD016 +:102B20000C207CBDFBF7F5FF10B9FCF7B7F990B3AB +:102B300007F086FF0028F3D1FBF700FEA0F57F41E8 +:102B4000FF39EDD1FCF707F8A68842F21070464332 +:102B5000A079FCF770F9FBF739FEF8B100220721E4 +:102B600001A801F071F9040058D0B3480021846035 +:102B70002046FDF72DFD2046FCF732FDAD4D04F15A +:102B800030006A8AA98AC2830184FBF726FE60B1FD +:102B9000E88A01210DE0FFE712207CBD31460020CC +:102BA00007F0CBFC88B3FFDF44E0FCF787F9014670 +:102BB000E88A07F091FD0146A0620022204606F057 +:102BC00070FDFBF70AFE38B9FCF778F9024621469A +:102BD0000120FEF7D2FAD0B1964A21461C320120DC +:102BE000FEF7E8FA687C00902B7CAA8A698A208824 +:102BF00001F018FA00B1FFDF208806F02CFC314606 +:102C0000204607F09AFC00B1FFDF13E008E007213F +:102C1000BDF8040001F05CF900B1FFDF09207CBDC4 +:102C200044B1208806F018FC2088072101F050F9F3 +:102C300000B1FFDF00207CBD002148E770B50D46E4 +:102C4000072101F033F9040003D094F88F0110B18B +:102C50000AE0022070BD94F87D00142801D01528E8 +:102C600002D194F8DC0108B10C2070BD1022294675 +:102C700004F5C870FBF7E1FB012084F88F01002008 +:102C800070BD10B5072101F011F918B190F88F113E +:102C900011B107E0022010BD90F87D10142903D077 +:102CA000152901D00C2010BD022180F88F110020C1 +:102CB00010BD2DE9FC410C464BF6803212219442A6 +:102CC0001DD8E4B16946FEF727FC002815D19DF810 +:102CD000000000F05FF9019E9DF80000703600F0E2 +:102CE00059F9019DAD1C2F88224639463046FDF723 +:102CF00065FC2888B842F6D10020BDE8FC81084672 +:102D0000FBE77CB5044600886946FEF705FC002811 +:102D100010D19DF8000000F03DF9019D9DF80000E4 +:102D2000703500F037F90198A27890F82C10914294 +:102D300001D10C207CBD7F212972A9720021E9728A +:102D4000E17880F82D10217980F82E10A17880F894 +:102D50002C1000207CBD1CB50C466946FEF7DCFB40 +:102D600000280AD19DF8000000F014F9019890F8AD +:102D70008C0000B10120207000201CBD7CB50D46E8 +:102D800014466946FEF7C8FB002809D19DF80000EB +:102D900000F000F9019890F82C00012801D00C20D7 +:102DA0007CBD9DF8000000F0F5F8019890F87810CF +:102DB000297090F87900207000207CBD70B50D4618 +:102DC0001646072101F072F818B381880124C388E0 +:102DD000428804EB4104AC4217D842F210746343BA +:102DE000A4106243B3FBF2F2521E94B24FF4FA7293 +:102DF000944200D91446A54200D22C46491C641CBA +:102E0000B4FBF1F24A43521E91B290F8C8211AB9AC +:102E100001E0022070BD01843180002070BD10B53A +:102E20000C46072101F042F840B1022C08D91220CB +:102E300010BD000018080020780000200220F7E7ED +:102E400014F0010180F8FD10C4F3400280F8FC206A +:102E500004D090F8FA1009B107F054FC0020E7E71D +:102E6000017889B1417879B141881B290CD38188D7 +:102E70001B2909D3C188022906D3F64902680A65CD +:102E800040684865002070471220704710B504461E +:102E90000EF086FD204607F0D8FB0020C8E710B5ED +:102EA00007F0D6FB0020C3E72DE9F04115460F4699 +:102EB00006460122114638460EF076FD04460121F1 +:102EC000384607F009FC844200D20446012130460E +:102ED00000F065F806460121002000F060F8311886 +:102EE000012096318C4206D901F19600611AB1FB9E +:102EF000F0F0401C80B228800020BDE8F08110B5C1 +:102F0000044600F077F808B10C2091E7601C0AF045 +:102F100038FE207800F00100FBF718FE207800F062 +:102F200001000CF010F8002082E710B504460720DD +:102F300000F056FF08B10C207AE72078C00711D0C6 +:102F400000226078114611F097FD08B112206FE75A +:102F5000A06809F01DFB6078D4F8041009F021FB8B +:102F6000002065E7002009F013FB00210846F5E783 +:102F700010B505F036FE00205AE710B5006805F0E0 +:102F800084F8002054E718B1022801D001207047CE +:102F90000020704708B1002070470120704710B52D +:102FA000012904D0022905D0FFDF204640E7C000F8 +:102FB000503001E080002C3084B2F6E710B50FF0FD +:102FC0009EF9042803D0052801D0002030E7012015 +:102FD0002EE710B5FFF7F2FF10B10CF07BF828B91F +:102FE00007F02EFD20B1FBF78CFD08B101201FE793 +:102FF00000201DE710B5FFF7E1FF18B907F020FD2D +:10300000002800D0012013E72DE9FE4300250F46DC +:1030100080460A260421404604F069FA4046FDF73E +:103020003EFE062000F0EAFE044616E06946062051 +:1030300000F0C5FE0BE000BFBDF80400B84206D0AA +:103040000298042241460E30FBF7CAF950B1684697 +:1030500000F093FE0500EFD0641E002C06DD002D6D +:10306000E4D005E04046FDF723FEF5E705B9FFDFB4 +:10307000D8F80000FDF737FE761E01D00028C9D031 +:10308000BDE8FE8390F8F01090F88C0020B919B1DB +:10309000042901D0012070470020704701780029E1 +:1030A0000AD0416891F8FA20002A05D0002281F860 +:1030B000FA20406807F026BB704770B514460546F5 +:1030C000012200F01BF9002806D121462846BDE860 +:1030D0007040002200F012B970BDFB2802D8B1F593 +:1030E000296F01D911207047002070471B38E12853 +:1030F00006D2B1F5A47F03D344F29020814201D9D6 +:1031000012207047002070471FB55249403191F896 +:103110002010CA0702D102781D2A0AD08A0702D4D9 +:1031200002781C2A28D049073DD40178152937D0C8 +:1031300039E08088ADF8000002A9FEF7EDF900B192 +:10314000FFDF9DF80800FFF725FF039810F8601FC8 +:103150008DF8021040788DF803000020ADF80400CF +:1031600001B9FFDF9DF8030000B9FFDF6846FEF7F5 +:1031700040FCD8B1FFDF19E08088ADF800004FF4C3 +:103180002961FB20ADF80410ADF80200ADF806008F +:10319000ADF808106846FEF73AFD38B1FFDF05E0EC +:1031A000807BC00702D0002004B041E60120FBE78D +:1031B000F8B50746508915460C4640B1B0F5004FAA +:1031C00005D20022A878114611F056FC08B1122051 +:1031D000F8BDA06E04F1700630B1A97894F86E00C5 +:1031E000814201D00C20F8BD012184F86F10A9782C +:1031F00084F86E106968A1666989A4F86C10288942 +:10320000B084002184F86F1028886946FEF762FFB9 +:10321000B08CBDF80010081A00B2002804DD214669 +:103220003846FEF745FFDDE70020F8BD042803D34C +:1032300021B9B0F5804F01D90020704701207047B7 +:10324000042803D321B9B0F5804F01D9002070477D +:1032500001207047D8070020012802D018B10020B3 +:103260007047022070470120704710B500224FF4CC +:10327000C84408E030F81230A34200D9234620F8B1 +:103280001230521CD2B28A42F4D3D1E580B2C106C8 +:103290000BD401071CD481064FEAC07101D5B9B91E +:1032A00000E099B1800713D410E0410610D48106E4 +:1032B0000ED4C1074FEA807104D0002902DB400719 +:1032C00004D405E0010703D4400701D4012070476E +:1032D0000020704770B50C460546FF2904D8FBF75F +:1032E0007CFA18B11F2C01D9122070BD2846FBF7BB +:1032F0005EFA08B1002070BD422070BD0AB1012203 +:1033000000E00222024202D1C80802D109B1002025 +:10331000704711207047000030B5058825F400443F +:1033200021448CB24FF4004194420AD2121B92B253 +:103330001B339A4201D2A94307E005F4004121431F +:1033400003E0A21A92B2A9431143018030BD0844A0 +:10335000083050434A31084480B2704770B51D466A +:1033600016460B46044629463046049AFFF7EFFFFF +:103370000646B34200D2FFDF282200212046FBF799 +:1033800086F84FF6FF70A082283EB0B26577608065 +:10339000B0F5004F00D9FFDF618805F13C008142A4 +:1033A00000D2FFDF60880835401B343880B22080AF +:1033B0001B2800D21B2020800020A07770BD8161D7 +:1033C000886170472DE9F05F0D46C188044600F121 +:1033D0002809008921F4004620F4004800F063FB2E +:1033E00010B10020BDE8F09F4FF0000A4FF0010B34 +:1033F000B0450CD9617FA8EB0600401A0838854219 +:1034000019DC09EB06000021058041801AE0608884 +:10341000617F801B471A083F0DD41B2F00DAFFDFA6 +:10342000BD4201DC294600E0B9B2681A0204120C60 +:1034300004D0424502DD84F817A0D2E709EB06006C +:103440000180428084F817B0CCE770B5044600F1E3 +:103450002802C088E37D20F400402BB1104402888C +:10346000438813448B4201D2002070BD00258A425C +:1034700002D30180458008E0891A0904090C4180C3 +:1034800003D0A01D00F01FFB08E0637F0088083315 +:10349000184481B26288A01DFFF73EFFE575012048 +:1034A00070BD70B5034600F12804C588808820F4FB +:1034B00000462644A84202D10020188270BD988997 +:1034C0003588A84206D3401B75882D1A2044ADB21A +:1034D000C01E05E02C1AA5B25C7F20443044401D7C +:1034E0000C88AC4200D90D809C8924B10024147052 +:1034F0000988198270BD0124F9E770B5044600F10E +:103500002801808820F400404518208A002825D012 +:10351000A189084480B2A08129886A881144814227 +:1035200000D2FFDF2888698800260844A1898842E4 +:1035300012D1A069807F2871698819B1201D00F01F +:10354000C2FA08E0637F28880833184481B2628891 +:10355000201DFFF7E1FEA6812682012070BD2DE926 +:10356000F041418987880026044600F12805B942C8 +:1035700019D004F10A0800BF21F400402844418812 +:1035800019B1404600F09FFA08E0637F00880833D5 +:10359000184481B262884046FFF7BEFE761C6189FE +:1035A000B6B2B942E8D13046BDE8F0812DE9F0412C +:1035B00004460B4627892830A68827F40041B4F832 +:1035C0000A8001440D46B74201D10020ECE70AB160 +:1035D000481D106023B1627F691D1846FAF72DFF60 +:1035E0002E88698804F1080021B18A1996B200F08A +:1035F0006AFA06E0637F62880833991989B2FFF797 +:103600008BFE474501D1208960813046CCE7818817 +:10361000C088814201D10120704700207047018994 +:103620008088814201D1012070470020704770B529 +:103630008588C38800F1280425F4004223F4004162 +:1036400014449D421AD08389058A5E1925886388AF +:10365000EC18A64214D313B18B4211D30EE0437F72 +:1036600008325C192244408892B2801A80B2233317 +:10367000984201D211B103E08A4201D1002070BD0D +:10368000012070BD2DE9F0478846C18804460089B5 +:1036900021F4004604F1280720F4004507EB060951 +:1036A00000F001FA002178BBB54204D9627FA81B63 +:1036B000801A002503E06088627F801B801A08382A +:1036C00023D4E28962B1B9F80020B9F802303BB1E5 +:1036D000E81A2177404518DBE0893844801A09E070 +:1036E000801A217740450ADB607FE1890830304449 +:1036F00039440844C01EA4F81280BDE8F08745454F +:1037000003DB01202077E7E7FFE761820020F4E791 +:103710002DE9F74F044600F12805C088884620F4BB +:10372000004A608A05EB0A0608B1404502D2002033 +:10373000BDE8FE8FE08978B13788B6F8029007EBD4 +:103740000901884200D0FFDF207F4FF0000B50EAD4 +:10375000090106D088B33BE00027A07FB94630714D +:10376000F2E7E18959B1607F2944083050440844A8 +:10377000B4F81F1020F8031D94F821108170E2891D +:1037800007EB080002EB0801E1813080A6F802B0E7 +:1037900002985F4650B1637F30880833184481B285 +:1037A0006288A01DFFF7B8FDE78121E0607FE18915 +:1037B00008305044294408442DE0FFE7E089B4F87C +:1037C0001F102844C01B20F8031D94F8211081709D +:1037D00009EB0800E28981B202EB0800E081378042 +:1037E00071800298A0B1A01D00F06DF9A4F80EB090 +:1037F000A07F401CA077A07D08B1E088A08284F85B +:1038000016B000BFA4F812B084F817B001208FE7FB +:10381000E0892844C01B30F8031DA4F81F108078ED +:1038200084F82100EEE710B5818800F1280321F427 +:1038300000442344848AC288A14212D0914210D00D +:10384000818971B9826972B11046FFF7E8FE50B9FB +:103850001089283220F400401044197900798842F8 +:1038600001D1002010BD184610BD00F12803407F93 +:1038700008300844C01E1060088808B9DB1E1360B9 +:1038800008884988084480B270472DE9F04100F16A +:103890002806407F1C4608309046431808884D880B +:1038A000069ADB1EA0B1C01C80B2904214D9801AC7 +:1038B000A04200DB204687B298183A464146FAF704 +:1038C0008FFD002816D1E01B84B2B844002005E02B +:1038D000ED1CADB2F61EE8E7101A80B20119A9423C +:1038E00006D8304422464146BDE8F041FAF778BD9B +:1038F0004FF0FF3058E62DE9F04100F12804407FF9 +:103900001E46083090464318002508884F88069ABE +:10391000DB1E90B1C01C80B2904212D9801AB04216 +:1039200000DB304685B299182A464046FAF785FDF5 +:10393000701B86B2A844002005E0FF1CBFB2E41E45 +:10394000EAE7101A80B28119B94206D82118324626 +:103950004046FAF772FDA81985B2284624E62DE9FB +:10396000F04100F12804407F1E460830904643187D +:10397000002508884F88069ADB1E90B1C01C80B2D3 +:10398000904212D9801AB04200DB304685B29818B6 +:103990002A464146FAF751FD701B86B2A844002022 +:1039A00005E0FF1CBFB2E41EEAE7101A80B28119DD +:1039B000B94206D8204432464146FAF73EFDA819DE +:1039C00085B22846F0E5401D704710B5044600F169 +:1039D0002801C288808820F400431944904206D010 +:1039E000A28922B9228A12B9A28A904201D100206A +:1039F00010BD0888498831B1201D00F064F800200E +:103A00002082012010BD637F62880833184481B290 +:103A1000201DFFF781FCF2E70021C181017741827F +:103A2000C1758175704703881380C28942B1C2880D +:103A300022F4004300F128021A440A60C08970474A +:103A40000020704710B50446808AA0F57F41FF39F9 +:103A500000D0FFDFE088A082E08900B10120A075DE +:103A600010BD4FF6FF71818200218175704710B53E +:103A70000446808AA0F57F41FF3900D1FFDFA07D99 +:103A800028B9A088A18A884201D1002010BD012058 +:103A900010BD8188828A914201D1807D08B10020C9 +:103AA00070470120704720F4004221F400439A42FD +:103AB00007D100F4004001F40041884201D0012008 +:103AC00070470020704730B5044600880D4620F44A +:103AD0000040A84200D2FFDF21884FF40040884315 +:103AE0002843208030BD70B50C00054609D0082C55 +:103AF00000D2FFDF1DB1A1B2286800F044F8201DFC +:103B000070BD0DB100202860002070BD002102684A +:103B100003E093881268194489B2002AF9D100F0B1 +:103B200032B870B500260D460446082900D2FFDFE2 +:103B3000206808B91EE0044620688188A94202D0A6 +:103B400001680029F7D181880646A94201D10068A1 +:103B50000DE005F1080293B20022994209D32844EE +:103B6000491B026081802168096821600160206032 +:103B700000E00026304670BD00230B608A8002689A +:103B80000A600160704700234360021D01810260EA +:103B90007047F0B50F460188408815460C181E4640 +:103BA000AC4200D3641B3044A84200D9FFDFA01907 +:103BB000A84200D9FFDF3819F0BD2DE9F041884651 +:103BC00006460188408815460C181F46AC4200D3B3 +:103BD000641B3844A84200D9FFDFE019A84200D98D +:103BE000FFDF70883844708008EB0400BDE8F08186 +:103BF0002DE9F041054600881E461746841B88467D +:103C0000BC4200D33C442C8068883044B84200D980 +:103C1000FFDFA019B84200D9FFDF68883044688010 +:103C200008EB0400E2E72DE9F04106881D46044652 +:103C3000701980B2174688462080B84201D3C01B55 +:103C400020806088A84200D2FFDF7019B84200D9F6 +:103C5000FFDF6088401B608008EB0600C6E730B5D8 +:103C60000D460188CC18944200D3A41A408898428B +:103C700000D8FFDF281930BD2DE9F041C84D0446BA +:103C80009046A8780E46A04200D8FFDF05EB8607D5 +:103C9000B86A50F8240000B1FFDFB868002816D0D9 +:103CA000304600F044F90146B868FFF73AFF0500D6 +:103CB0000CD0B86A082E40F8245000D3FFDFB94872 +:103CC0004246294650F82630204698472846BDE807 +:103CD000F0812DE9F8431E468C1991460F460546A2 +:103CE000FF2C00D9FFDFB14500D9FFDFE4B200951A +:103CF0004DB300208046E81C20F00300A84200D00D +:103D0000FFDF4946DFF89892684689F8001089F885 +:103D1000017089F8024089F8034089F8044089F865 +:103D2000054089F8066089F80770414600F008F9F7 +:103D3000002142460F464B460098C01C20F003006D +:103D4000009012B10EE00120D4E703EB8106B062CF +:103D5000002005E0D6F828C04CF82070401CC0B206 +:103D6000A042F7D30098491C00EB8400C9B2009030 +:103D70000829E1D3401BBDE8F88310B50446EDF7F0 +:103D80008EFA08B1102010BD2078854A618802EBB8 +:103D9000800092780EE0836A53F8213043B14A1CC8 +:103DA0006280A180806A50F82100A060002010BDD0 +:103DB000491C89B28A42EED86180052010BD70B5D9 +:103DC00005460C460846EDF76AFA08B1102070BDAA +:103DD000082D01D3072070BD25700020608070BDC4 +:103DE0000EB56946FFF7EBFF00B1FFDF6846FFF74E +:103DF000C4FF08B100200EBD01200EBD10B5044661 +:103E0000082800D3FFDF6648005D10BD3EB50546BB +:103E100000246946FFF7D3FF18B1FFDF01E0641CFF +:103E2000E4B26846FFF7A9FF0028F8D02846FFF75C +:103E3000E5FF001BC0B23EBD59498978814201D9D6 +:103E4000C0B27047FF2070472DE9F041544B06295E +:103E500003D007291CD19D7900E0002500244FF6EE +:103E6000FF7603EB810713F801C00AE06319D7F866 +:103E700028E09BB25EF823E0BEF1000F04D0641C82 +:103E8000A4B2A445F2D8334603801846B34201D108 +:103E900000201CE7BDE8F041EEE6A0F57F43FF3BC4 +:103EA00001D0082901D300207047E5E6A0F57F4244 +:103EB000FF3A0BD0082909D2394A9378834205D9B1 +:103EC00002EB8101896A51F8200070470020704799 +:103ED0002DE9F04104460D46A4F57F4143F202006E +:103EE000FF3902D0082D01D30720F0E62C494FF00E +:103EF00000088A78A242F8D901EB8506B26A52F826 +:103F00002470002FF1D027483946203050F8252062 +:103F100020469047B16A284641F8248000F007F80F +:103F200002463946B068FFF727FE0020CFE61D495C +:103F3000403131F810004FF6FC71C01C084070474A +:103F40002DE9F843164E8846054600242868C01C13 +:103F500020F0030028602046FFF7E9FF315D484369 +:103F6000B8F1000F01D0002200E02A68014600925B +:103F700032B100274FEA0D00FFF7B5FD1FB106E093 +:103F800001270020F8E706EB8401009A8A6029687F +:103F9000641C0844E4B22860082CD7D3EBE6000088 +:103FA0003C0800201862020070B50E461D461146FE +:103FB00000F0D3F804462946304600F0D7F82044F4 +:103FC000001D70BD2DE9F04190460D4604004FF0F4 +:103FD000000610D00027E01C20F00300A04200D013 +:103FE000FFDFE5B141460020FFF77DFD0C3000EB1F +:103FF000850617B113E00127EDE7614F04F10C00CE +:10400000AA003C602572606000EB85002060002102 +:104010006068FAF73CFA41463868FFF764FD3046BD +:10402000BDE8F0812DE9FF4F554C804681B02068F6 +:104030009A46934600B9FFDF2068027A424503D9C9 +:10404000416851F8280020B143F2020005B0BDE8F4 +:10405000F08F5146029800F080F886B258460E99CB +:1040600000F084F885B27019001D87B22068A1465F +:1040700039460068FFF755FD04001FD06780258092 +:104080002946201D0E9D07465A4601230095FFF73D +:1040900065F92088314638440123029ACDF800A002 +:1040A000FFF75CF92088C1193846FFF788F9D9F87D +:1040B00000004168002041F82840C7E70420C5E718 +:1040C00070B52F4C0546206800B9FFDF2068017AE3 +:1040D000A9420DD9426852F8251049B1002342F88F +:1040E00025304A880068FFF747FD2168087A06E016 +:1040F00043F2020070BD4A6852F820202AB9401EDF +:10410000C0B2F8D20868FFF701FD002070BD70B59D +:104110001B4E05460024306800B9FFDF3068017A85 +:10412000A94204D9406850F8250000B1041D20467A +:1041300070BD70B5124E05460024306800B9FFDF2F +:104140003068017AA94206D9406850F8251011B1AB +:1041500031F8040B4418204670BD10B50A46012101 +:10416000FFF7F5F8C01C20F0030010BD10B50A469B +:104170000121FFF7ECF8C01C20F0030010BD000087 +:104180008C00002070B50446C2F110052819FAF71A +:1041900054F915F0FF0109D0491ECAB28020A0547D +:1041A0002046BDE870400021FAF771B970BD30B506 +:1041B00005E05B1EDBB2CC5CD55C6C40C454002BCC +:1041C000F7D130BD10B5002409E00B78521E44EA47 +:1041D000430300F8013B11F8013BD2B2DC09002A8D +:1041E000F3D110BD2DE9F04389B01E46DDE9107909 +:1041F00090460D00044622D002460846F949FDF7D4 +:1042000044FE102221463846FFF7DCFFE07B000623 +:1042100006D5F44A3946102310320846FFF7C7FF87 +:10422000102239464846FFF7CDFFF87B000606D539 +:10423000EC4A4946102310320846FFF7B8FF102217 +:1042400000212046FAF723F90DE0103EB6B208EB44 +:104250000601102322466846FFF7A9FF224628469A +:104260006946FDF712FE102EEFD818D0F2B2414683 +:104270006846FFF787FF10234A46694604A8FFF700 +:1042800096FF1023224604A96846FFF790FF2246B6 +:1042900028466946FDF7F9FD09B0BDE8F083102313 +:1042A0003A464146EAE770B59CB01E4605461346BD +:1042B00020980C468DF80800202219460DF10900BF +:1042C000FAF7BBF8202221460DF12900FAF7B5F8DC +:1042D00017A913A8CDE90001412302AA31462846B7 +:1042E000FFF780FF1CB070BD2DE9FF4F9FB014AEEB +:1042F000DDE92D5410AFBB49CDE9007620232031F4 +:104300001AA8FFF76FFF4FF000088DF808804FF0F4 +:1043100001098DF8099054F8010FCDF80A00A08822 +:10432000ADF80E0014F8010C1022C0F340008DF817 +:10433000100055F8010FCDF81100A888ADF8150050 +:1043400015F8010C2C99C0F340008DF8170006A851 +:104350008246FAF772F80AA8834610222299FAF7E1 +:104360006CF8A0483523083802AA40688DF83C80D4 +:10437000CDE900760E901AA91F98FFF733FF8DF84C +:1043800008808DF809902068CDF80A00A088ADF863 +:104390000E0014F8010C1022C0F340008DF810003C +:1043A0002868CDF81100A888ADF8150015F8010CA3 +:1043B0002C99C0F340008DF817005046FAF73DF8ED +:1043C000584610222299FAF738F8864835230838DB +:1043D00002AA40688DF83C90CDE900760E901AA9AB +:1043E0002098FFF7FFFE23B0BDE8F08FF0B59BB03B +:1043F0000C460546DDE922101E461746DDE920324F +:10440000D0F801C0CDF808C0B0F805C0ADF80CC0B8 +:104410000078C0F340008DF80E00D1F80100CDF80F +:104420000F00B1F80500ADF8130008781946C0F385 +:1044300040008DF815001088ADF8160090788DF8C2 +:1044400018000DF119001022F9F7F7FF0DF12900FE +:1044500010223146F9F7F1FF0DF1390010223946EB +:10446000F9F7EBFF17A913A8CDE90001412302AA30 +:1044700021462846FFF7B6FE1BB0F0BDF0B5A3B04D +:1044800017460D4604461E46102202A82899F9F741 +:10449000D4FF06A820223946F9F7CFFF0EA8202224 +:1044A0002946F9F7CAFF1EA91AA8CDE90001502331 +:1044B00002AA314616A8FFF795FE1698206023B091 +:1044C000F0BDF0B589B00446DDE90E070D46397838 +:1044D000109EC1F340018DF8001031789446C1F36D +:1044E00040018DF801101968CDF802109988ADF8D7 +:1044F000061099798DF808100168CDF809108188A7 +:10450000ADF80D1080798DF80F0010236A466146D2 +:1045100004A8FFF74CFE2246284604A9FDF7B5FC87 +:10452000D6F801000090B6F80500ADF80400D7F801 +:104530000100CDF80600B7F80500ADF80A0000202C +:10454000039010236A46214604A8FFF730FE224656 +:10455000284604A9FDF799FC09B0F0BD1FB51C68F9 +:1045600000945B68019313680293526803920246B9 +:1045700008466946FDF789FC1FBD10B588B00446A2 +:104580001068049050680590002006900790084637 +:104590006A4604A9FDF779FCBDF80000208008B048 +:1045A00010BD1FB51288ADF800201A88ADF80220A2 +:1045B0000022019202920392024608466946FDF7E4 +:1045C00064FC1FBD7FB5074B14460546083B9A1C8B +:1045D0006846FFF7E6FF224669462846FFF7CDFF0B +:1045E0007FBD00007062020070B5044600780E4680 +:1045F000012813D0052802D0092813D10EE0A068A5 +:1046000061690578042003F059FA052D0AD0782352 +:1046100000220420616903F0A7F903E00420616926 +:1046200003F04CFA31462046BDE8704001F08AB8EC +:1046300010B500F12D03C2799C78411D144064F33C +:104640000102C271D2070DD04A795C7922404A71C9 +:104650000A791B791A400A718278C9788A4200D98E +:10466000817010BD00224A71F5E74178012900D020 +:104670000C21017070472DE9F04F93B04FF0000B03 +:104680000C690D468DF820B0097801260C201746DC +:104690004FF00D084FF0110A4FF008091B2975D291 +:1046A000DFE811F01B00C40207031F035E03710360 +:1046B000A303B803F9031A0462049504A204EF04E7 +:1046C0002D05370555056005F305360639066806DC +:1046D0008406FE062207EB06F00614B120781D289A +:1046E0002AD0D5F808805FEA08004FD001208DF865 +:1046F0002000686A02220D908DF824200A208DF88F +:104700002500A8690A90A8880028EED098F8001023 +:1047100091B10F2910D27DD2DFE801F07C1349DE80 +:10472000FCFBFAF9F8F738089CF6F50002282DD1C1 +:1047300024B120780C2801D00026F0E38DF8202049 +:10474000CBE10420696A03F0B9F9A8880728EED103 +:10475000204600F0F2FF022809D0204600F0EDFFCD +:10476000032807D9204600F0E8FF072802D20120DD +:10477000207004E0002CB8D020780128D7D198F818 +:104780000400C11F0A2902D30A2061E0C4E1A0701D +:10479000D8F80010E162B8F80410218698F80600F5 +:1047A00084F83200012028700320207044E007289C +:1047B000BDD1002C99D020780D28B8D198F80310DD +:1047C00094F82F20C1F3C000C2F3C002104201D000 +:1047D000062000E00720890707D198F8051001425C +:1047E000D2D198F806100142CED194F8312098F831 +:1047F000051020EA02021142C6D194F8322098F83E +:10480000061090430142BFD198F80400C11F0A2945 +:10481000BAD200E008E2617D81427CD8D8F800106D +:104820006160B8F80410218198F80600A072012098 +:1048300028700E20207003208DF82000686A0D90EB +:1048400004F12D000990601D0A900F300B9022E1B9 +:104850002875FCE3412891D1204600F06EFF042822 +:1048600002D1E078C00704D1204600F066FF0F288F +:1048700084D1A88CD5F80C8080B24FF0400BE6694B +:10488000FFF745FC324641465B464E46CDF8009068 +:10489000FFF731F80B208DF82000686A0D90E06971 +:1048A0000990002108A8FFF79FFE2078042806D071 +:1048B000A07D58B1012809D003280AD04AE3052079 +:1048C0002070032028708DF82060CEE184F800A0CD +:1048D00032E712202070EAE11128BCD1204600F016 +:1048E0002CFF042802D1E078C00719D0204600F040 +:1048F00024FF062805D1E078C00711D1A07D022849 +:104900000ED0204608E0CCE084E072E151E124E1E1 +:1049100003E1E9E019E0B0E100F00FFF11289AD1BE +:10492000102208F1010104F13C00F9F786FD6078DE +:1049300001286ED012202070E078C00760D0A07DE2 +:104940000028C8D00128C6D05AE0112890D12046AE +:1049500000F0F3FE082804D0204600F0EEFE1328F5 +:1049600086D104F16C00102208F101010646F9F726 +:1049700064FD207808280DD014202070E178C80745 +:104980000DD0A07D02280AD06278022A04D0032824 +:10499000A1D035E00920F0E708B1012837D1C807D8 +:1049A00013D0A07D02281DD000200090D4E906215C +:1049B00033460EA8FFF777FC10220EA904F13C0045 +:1049C000F9F70EFDC8B1042042E7D4E90912201D11 +:1049D0008DE8070004F12C0332460EA8616BFFF747 +:1049E00070FDE9E7606BC1F34401491E0068C840EF +:1049F00000F0010040F08000D7E72078092806D1B8 +:104A000085F800908DF8209036E32870EFE30920B8 +:104A1000FBE79EE1112899D1204600F08EFE0A287E +:104A200002D1E078C00704D1204600F086FE1528A8 +:104A30008CD104F13C00102208F101010646F9F77F +:104A4000FCFC20780A2816D016202070D4E9093200 +:104A5000606B611D8DE80F0004F15C0304F16C02D2 +:104A600047310EA8FFF7C2FC10220EA93046F9F715 +:104A7000B7FC18B1F9E20B20207073E22046FFF773 +:104A8000D7FDA078216AC0F110020B18002118464A +:104A9000F9F7FDFC26E3394608A8FFF7A5FD064611 +:104AA0003CE20228B7D1204600F047FE042804D398 +:104AB000204600F042FE082809D3204600F03DFEC3 +:104AC0000E2829D3204600F038FE122824D2A07DDB +:104AD0000228A0D10E208DF82000686A0D9098F869 +:104AE00001008DF82400F5E3022894D1204600F05F +:104AF00024FE002810D0204600F01FFE0128F9D027 +:104B0000204600F01AFE0C28F4D004208DF8240072 +:104B100098F801008DF8250060E21128FCD1002CE6 +:104B2000FAD020781728F7D16178606A022912D06C +:104B30005FF0000101EB4101182606EBC1011022D4 +:104B4000405808F10101F9F778FC0420696A00F087 +:104B5000E7FD2670F0E50121ECE70B28DCD1002C05 +:104B6000DAD020781828D7D16078616A02281CD062 +:104B70005FF0000000EB4002102000EBC20009587B +:104B8000B8F8010008806078616A02280FD0002020 +:104B900000EB4002142000EBC2000958404650F8D8 +:104BA000032F0A604068486039E00120E2E70120F5 +:104BB000EEE71128B0D1002CAED020781928ABD167 +:104BC0006178606A022912D05FF0000101EB4101B7 +:104BD0001C2202EBC1011022405808F10101F9F733 +:104BE0002CFC0420696A00F09BFD1A20B6E001212C +:104BF000ECE7082890D1002C8ED020781A288BD191 +:104C0000606A98F80120017862F347010170616AD7 +:104C1000D8F8022041F8012FB8F806008880042057 +:104C2000696A00F07DFD90E2072011E638780128DE +:104C300094D1182204F114007968F9F7FEFBE079A9 +:104C4000C10894F82F0001EAD001E07861F3000078 +:104C5000E070217D002974D12178032909D0C00793 +:104C600025D0032028708DF82090686A0D9041208F +:104C700008E3607DA178884201D90620E8E5022694 +:104C80002671E179204621F0E001E171617A21F09D +:104C9000F0016172A17A21F0F001A172FFF7C8FC66 +:104CA0002E708DF82090686A0D900720EAE20420AB +:104CB000ABE6387805289DD18DF82000686A0D9004 +:104CC000B8680A900720ADF824000A988DF830B033 +:104CD0006168016021898180A17A8171042020703E +:104CE000F8E23978052985D18DF82010696A0D918F +:104CF000391D09AE0EC986E80E004121ADF8241019 +:104D00008DF830B01070A88CD7F80C8080B2402697 +:104D1000A769FFF70EFA41463A463346C846CDF832 +:104D20000090FEF71CFE002108A8FFF75DFCE0786C +:104D300020F03E00801CE0702078052802D00F2073 +:104D40000CE04AE1A07D20B1012802D0032802D066 +:104D500002E10720BEE584F80080EDE42070EBE47A +:104D6000102104F15C0002F0C2FB606BB0BBA07DBF +:104D700018B1012801D00520FDE006202870F84870 +:104D80006063A063C2E23878022894D1387908B110 +:104D90002875B7E3A07D022802D0032805D022E0C1 +:104DA000B8680028F5D060631CE06078012806D060 +:104DB000A07994F82E10012805D0E94806E0A179E1 +:104DC00094F82E00F7E7B8680028E2D06063E07836 +:104DD000C00701D0012902D0E14803E003E0F868F0 +:104DE0000028D6D0A06306200FE68DF82090696ACF +:104DF0000D91E1784846C90709D06178022903D1AD +:104E0000A17D29B1012903D0A17D032900D007206C +:104E1000287033E138780528BBD1207807281ED0C8 +:104E200084F800A005208DF82000686A0D90B8680D +:104E30000A90ADF824A08DF830B003210170E1781C +:104E4000CA070FD0A27D022A1AD000210091D4E90E +:104E5000061204F15C03401CFFF725FA6BE384F8AB +:104E60000090DFE7D4E90923211D8DE80E0004F14D +:104E70002C0304F15C02401C616BFFF722FB5AE338 +:104E8000626BC1F34401491E1268CA4002F001017D +:104E900041F08001DAE738780528BDD18DF820008F +:104EA000686A0D90B8680A90ADF824A08DF830B00B +:104EB000042100F8011B102204F15C01F9F7BDFA8E +:104EC000002108A8FFF790FB2078092801D01320C3 +:104ED00044E70A2020709AE5E078C10742D0A17D1E +:104EE000012902D0022927D038E0617808A80129D9 +:104EF00016D004F16C010091D4E9061204F15C03B0 +:104F0000001DFFF7BBFA0A20287003268DF82080C9 +:104F1000686A0D90002108A8FFF766FBE1E2C7E28E +:104F200004F15C010091D4E9062104F16C03001D39 +:104F3000FFF7A4FA0026E9E7C0F3440114290DD2D3 +:104F40004FF0006101EBB0104FEAB060E0706078A4 +:104F5000012801D01020BDE40620FFE6607801287A +:104F60003FF4B6AC0A2050E5E178C90708D0A17D2E +:104F7000012903D10B202870042030E028702EE096 +:104F80000E2028706078616B012818D004F15C0352 +:104F900004F16C020EA8FFF7E1FA2046FFF748FB88 +:104FA000A0780EAEC0F1100230440021F9F76FFA7C +:104FB00006208DF82000686A09960D909BE004F1A8 +:104FC0006C0304F15C020EA8FFF7C8FAE8E7397831 +:104FD000022903D139790029D0D0297592E28DF8C0 +:104FE0002000686A0D9056E538780728F6D1D4E994 +:104FF00009216078012808D004F16C00CDE9000295 +:10500000029105D104F16C0304E004F15C00F5E7C2 +:1050100004F15C0304F14C007A680646216AFFF74C +:1050200063F96078012822D1A078216AC0F11002CA +:105030000B1800211846F9F72AFAD4E90923606B06 +:1050400004F12D018DE80F0004F15C0300E05BE248 +:1050500004F16C0231460EA8FFF7C8F910220EA920 +:1050600004F13C00F9F7BCF908B10B20ACE485F879 +:10507000008000BF8DF82090686A0D908DF824A004 +:1050800009E538780528A9D18DF82000686A0D90C7 +:10509000B8680A90ADF824A08DF830B080F8008090 +:1050A000617801291AD0D4E9092104F12D03A66BF6 +:1050B00003910096CDE9013204F16C0304F15C0226 +:1050C00004F14C01401CFFF791F9002108A8FFF7FB +:1050D0008BFA6078012805D015203FE6D4E9091243 +:1050E000631DE4E70E20287006208DF82000686A12 +:1050F000CDF824B00D90A0788DF82800CBE4387856 +:105100000328C0D1E079C00770D00F202870072095 +:1051100065E7387804286BD11422391D04F1140096 +:10512000F9F78BF9616A208CA1F80900616AA0780F +:10513000C871E179626A01F003011172616A627AF1 +:105140000A73616AA07A81F8240016205DE485F86C +:1051500000A08DF82090696A50460D9192E0000001 +:10516000706202003878052842D1B868A861617879 +:10517000606A022901D0012100E0002101EB410118 +:10518000142606EBC1014058082102F0B0F96178FD +:10519000606A022901D0012100E0002101EB4101F8 +:1051A00006EBC101425802A8E169FFF70BFA6078EB +:1051B000626A022801D0012000E0002000EB4001DB +:1051C000102000EBC1000223105802A90932FEF79B +:1051D000EEFF626AFD4B0EA80932A169FFF7E1F903 +:1051E0006178606A022904D0012103E044E18DE086 +:1051F000BFE0002101EB4101182606EBC101A278B6 +:1052000040580EA9F9F719F96178606A022901D0AE +:10521000012100E0002101EB410106EBC1014158F1 +:10522000A0780B18C0F1100200211846F9F72FF9E9 +:1052300005208DF82000686A0D90A8690A90ADF8E5 +:1052400024A08DF830B0062101706278616A022ACC +:1052500001D0012200E0002202EB420206EBC20272 +:10526000401C89581022F9F7E8F8002108A8FFF738 +:10527000BBF91220C5F818B028708DF82090686A24 +:105280000D900B208DF8240005E43878052870D1A6 +:105290008DF82000686A0D90B8680A900B20ADF870 +:1052A00024000A98072101706178626A022901D0FE +:1052B000012100E0002101EB4103102101EBC301BA +:1052C00051580988A0F801106178626A022902D059 +:1052D000012101E02FE1002101EB4103142101EB49 +:1052E000C30151580A6840F8032F4968416059E0EA +:1052F0001920287001208DF8300074E616202870DF +:105300008DF830B0002108A8FFF76EF9032617E1E9 +:1053100014202870AEE6387805282AD18DF82000B0 +:10532000686A0D90B8680A90ADF824A08DF830B086 +:1053300080F800906278616A4E46022A01D001220C +:1053400000E0002202EB42021C2303EBC202401CDD +:1053500089581022F9F771F8002108A8FFF744F9DD +:10536000152028708DF82060686A0D908DF82460F3 +:1053700039E680E0387805287DD18DF82000686A0C +:105380000D90B8680A90ADF8249009210170616908 +:10539000097849084170616951F8012FC0F802206D +:1053A0008988C18020781C28A8D1A1E7E078C007AF +:1053B00002D04FF0060C01E04FF0070C6078022895 +:1053C0000AD000BF4FF0000000EB040101F1090119 +:1053D00005D04FF0010004E04FF00100F4E74FF07A +:1053E00000000B78204413EA0C030B7010F8092F0F +:1053F00002EA0C02027004D14FF01B0C84F800C0CA +:10540000D2B394F801C0BCF1010F00D09BB990F861 +:1054100000C0E0465FEACC7C04D028F001060670AC +:10542000102606E05FEA887C05D528F002060670A3 +:1054300013262E70032694F801C0BCF1020F00D091 +:1054400092B991F800C05FEACC7804D02CF0010644 +:105450000E70172106E05FEA8C7805D52CF0020665 +:105460000E701921217000260078D0BBCAB3C3BBCF +:105470001C20207035E012E002E03878062841D187 +:105480001A2015E4207801283CD00C283AD0204678 +:10549000FFF7EBF809208DF82000686A0D9031E0E5 +:1054A0003878052805D00620387003261820287083 +:1054B00046E005208DF82000696A0D91B9680A91CF +:1054C0000221ADF8241001218DF830100A990870DE +:1054D000287D4870394608A8FFF786F80646182048 +:1054E0002870012E0ED02BE001208DF82000686A74 +:1054F0000D9003208DF82400287D8DF8250085F877 +:1055000014B012E0287D80B11D2020701720287073 +:105510008DF82090686A0D9002208DF8240039469D +:1055200008A8FFF761F806460AE00CB1FE202070DB +:105530009DF8200020B1002108A8FFF755F80CE4E1 +:1055400013B03046BDE8F08F2DE9F04387B00C462C +:105550004E6900218DF804100120257803460227AA +:105560004FF007094FF0050C85B1012D53D0022DE6 +:1055700039D1FE2030708DF80030606A059003202C +:105580008DF80400207E8DF8050063E02179012963 +:1055900025D002292DD0032928D0042923D1B17D7B +:1055A000022920D131780D1F042D04D30A3D032D8B +:1055B00001D31D2917D12189022914D38DF8047034 +:1055C000237020899DF80410884201E0686202007F +:1055D00018D208208DF80000606A059057E07078B6 +:1055E0000128EBD0052007B0BDE8F0831D20307006 +:1055F000E4E771780229F5D131780C29F3D18DF8DF +:105600000490DDE7083402F804CB94E80B0082E84C +:105610000B000320E7E71578052DE4D18DF800C0D5 +:10562000656A0595956802958DF8101094F80480C8 +:10563000B8F1010F13D0B8F1020F2DD0B8F1030F5C +:105640001CD0B8F1040FCED1ADF804700E20287034 +:10565000207E687000216846FEF7C6FF0CE0ADF8BA +:1056600004700B202870207E002100F01F0068705D +:105670006846FEF7B9FF37700020B4E7ADF8047054 +:105680008DF8103085F800C0207E687027701146B4 +:105690006846FEF7A9FFA6E7ADF804902B70207FBF +:1056A0006870607F00F00100A870A07F00F01F000C +:1056B000E870E27F2A71C0071CD094F8200000F047 +:1056C0000700687194F8210000F00700A87100211C +:1056D0006846FEF789FF2868F062A8883086A879B6 +:1056E00086F83200A069407870752879B0700D2076 +:1056F0003070C1E7A9716971E9E700B587B0042886 +:105700000CD101208DF800008DF8040000200591D7 +:105710008DF8050001466846FEF766FF07B000BD3C +:1057200070B50C46054602F0C9F921462846BDE889 +:1057300070407823002202F017B908B10078704752 +:105740000C20704770B50C0005784FF000010CD0AC +:1057500021702146EFF7D1FD69482178405D8842EC +:1057600001D1032070BD022070BDEFF7C6FD0020FF +:1057700070BD0279012A05D000220A704B78012BF6 +:1057800002D003E0042070470A758A610279930011 +:10579000521C0271C15003207047F0B587B00F460C +:1057A00005460124287905EB800050F8046C7078D8 +:1057B000411E02290AD252493A46083901EB8000BB +:1057C000314650F8043C2846984704460CB1012C59 +:1057D00011D12879401E10F0FF00287101D0032458 +:1057E000E0E70A208DF80000706A0590002101961C +:1057F0006846FFF7A7FF032CD4D007B02046F0BDC2 +:1058000070B515460A46044629461046FFF7C5FFFF +:10581000064674B12078FE280BD1207C30B10020E0 +:105820002870294604F10C00FFF7B7FF2046FEF769 +:105830001CFF304670BD704770B50E4604467C2292 +:105840000021F8F724FE0225012E03D0022E04D0F9 +:10585000052070BD0120607000E065702046FEF7F5 +:1058600004FFA575002070BD28B1027C1AB10A465C +:1058700000F10C01C4E70120704710B5044686B062 +:10588000042002F01BF92078FE2806D000208DF8B5 +:10589000000069462046FFF7E7FF06B010BD7CB563 +:1058A0000E4600218DF804104178012903D0022909 +:1058B00003D0002405E0046900E044690CB1217CB8 +:1058C00089B16D4601462846FFF753FF032809D1E9 +:1058D000324629462046FFF793FF9DF80410002921 +:1058E00000D004207CBD04F10C05EBE730B40C467D +:1058F0000146034A204630BC024B0C3AFEF751BE2B +:10590000AC6202006862020070B50D46040011D05E +:1059100085B1220100212846F8F7B9FD102250492F +:105920002846F8F78AFD4F48012101704470456010 +:10593000002070BD012070BD70B505460024494EA1 +:1059400011E07068AA7B00EB0410817B914208D1C2 +:10595000C17BEA7B914204D10C222946F8F740FD35 +:1059600030B1641CE4B230788442EAD3002070BDC8 +:10597000641CE0B270BD70B50546FFF7DDFF00287E +:1059800005D1384C20786178884201D3002070BD61 +:105990006168102201EB00102946F8F74EFD2078CF +:1059A000401CC0B2207070BD2E48007870472D4951 +:1059B0000878012802D0401E08700020704770B59A +:1059C0000D460021917014461180022802D0102843 +:1059D00015D105E0288890B10121A17010800CE05C +:1059E000284613B1FFF7C7FF01E0FFF7A5FFA0703E +:1059F00010F0FF0F03D0A8892080002070BD012087 +:105A000070BD0023DBE770B5054614460E0009D0D3 +:105A100000203070A878012806D003D911490A78EF +:105A200090420AD9012070BD24B1287820702888BE +:105A3000000A5070022008700FE064B1496810221B +:105A400001EB001120461039F8F7F7FC2878207395 +:105A50002888000A607310203070002070BD00009C +:105A6000BB620200900000202DE9F04190460C46F8 +:105A700007460025FE48072F00EB881607D2DFE80F +:105A800007F00707070704040400012500E0FFDF13 +:105A900006F81470002D13D0F548803000EB880113 +:105AA00091F82700202803D006EB4000447001E065 +:105AB00081F8264006EB44022020507081F82740F0 +:105AC000BDE8F081F0B51F4614460E460546202A73 +:105AD00000D1FFDFE649E648803100EB871C0CEB84 +:105AE000440001EB8702202E07D00CEB46014078E2 +:105AF0004B784870184620210AE092F8253040780B +:105B000082F82500F6E701460CEB4100057040786D +:105B1000A142F8D192F82740202C03D00CEB44048A +:105B2000637001E082F826300CEB4104202363709F +:105B300082F82710F0BD30B50D46CE4B4419002237 +:105B4000181A72EB020100D2FFDFCB48854200DD5C +:105B5000FFDFC9484042854200DAFFDFC548401CEC +:105B6000844207DA002C01DB204630BDC148401CCE +:105B7000201830BDBF48C043FAE710B5044601689D +:105B8000407ABE4A52F82020114450B10220084405 +:105B900020F07F40EDF763F894F90810BDE810405D +:105BA000C9E70420F3E72DE9F047B14E803696F8B7 +:105BB0002D50DFF8BC9206EB850090F8264034E0CB +:105BC00009EB85174FF0070817F81400012806D0D5 +:105BD00004282ED005282ED0062800D0FFDF01F0A3 +:105BE00025F9014607EB4400427806EB850080F872 +:105BF000262090F82720A24202D1202280F82720D8 +:105C0000084601F01EF92A4621460120FFF72CFF25 +:105C10009B48414600EB041002682046904796F8E6 +:105C20002D5006EB850090F82640202CC8D1BDE809 +:105C3000F087022000E003208046D0E710B58C4CAE +:105C40002021803484F8251084F8261084F8271049 +:105C5000002084F8280084F82D0084F82E10411EBE +:105C6000A16044F8100B2074607420736073A073FB +:105C70008449E07720750870487000217C4A103C08 +:105C800002F81100491CC9B22029F9D30120ECF710 +:105C9000D6FE0020ECF7D3FE012084F82200EDF7B9 +:105CA000FFF87948EDF711F9764CA41E207077487B +:105CB000EDF70BF96070BDE81040ECF74DBE10B584 +:105CC000ECF76FFE6F4CA41E2078EDF717F96078A3 +:105CD000EDF714F9BDE8104001F0E0B8202070475E +:105CE0000020ECF785BE70B5054601240E46AC4099 +:105CF0005AB1FFF7F5FF0146654800EBC500C0F853 +:105D00001015C0F81465634801E06248001D046086 +:105D100070BD2DE9F34F564C0025803404EB810A09 +:105D200089B09AF82500202821D0691E0291544993 +:105D3000009501EB0017391D03AB07C983E8070085 +:105D4000A18BADF81C10A07F8DF81E009DF81500EA +:105D5000A046C8B10226494951F820400399A2192A +:105D6000114421F07F41019184B102210FE0012013 +:105D7000ECF765FE0020ECF762FEECF730FE01F078 +:105D80008DF884F82F50A9E00426E4E700218DF86F +:105D90001810022801D0012820D103980119099870 +:105DA000081A801C9DF81C1020F07F4001B10221D0 +:105DB000353181420BD203208DF815000398C4F1D0 +:105DC0003201401A20F07F40322403900CE098F812 +:105DD000240018B901F043FA002863D0322C03D212 +:105DE00014B101F04FF801E001F058F8254A10789D +:105DF00018B393465278039B121B00219DF818405C +:105E0000994601281AD0032818D000208DF81E00CA +:105E1000002A04DD981A039001208DF818009DF8DF +:105E20001C0000B1022103981B4A20F07F40039020 +:105E300003AB099801F03EF810B110E00120E5E74E +:105E40009DF81D0018B99BF80000032829D08DF893 +:105E50001C50CDF80C908DF818408DF81E509DF810 +:105E6000180010B30398012381190022184615E089 +:105E7000840A0020FF7F841E0020A107CC6202005C +:105E8000840800209A00002017780100A75B010019 +:105E900000F0014004F50140FFFF3F00ECF722FE57 +:105EA00006E000200BB0BDE8F08F0120ECF7C7FD45 +:105EB00097F90C20012300200199ECF713FEF87BE1 +:105EC000C00701D0ECF7F7FE012188F82F108AF8FF +:105ED000285020226946FE48F8F7AFFA0120E1E792 +:105EE0002DE9F05FDFF8E883064608EB860090F8BE +:105EF0002550202D1FD0A8F180002C4600EB8617DE +:105F0000A0F50079DFF8CCB305E0A24607EB4A0024 +:105F10004478202C0AD0ECF730FE09EB04135A46E3 +:105F200001211B1D00F0C6FF0028EED0AC4202D0BC +:105F3000334652461EE0E84808B1AFF30080ECF764 +:105F40001CFE98F82F206AB1D8F80C20411C891A41 +:105F50000902CA1701EB12610912002902DD0020B3 +:105F6000BDE8F09F3146FFF7D4FE08B10120F7E706 +:105F700033462A4620210420FFF7A4FDEFE72DE950 +:105F8000F041D34C2569ECF7F8FD401B0002C11726 +:105F900000EB1160001200D4FFDF94F8220000B182 +:105FA000FFDF012784F8227094F82E00202800D10A +:105FB000FFDF94F82E60202084F82E00002584F85E +:105FC0002F5084F8205084F82150C4482560007870 +:105FD000022833D0032831D000202077A068401C4D +:105FE00005D04FF0FF30A0600120ECF728FD002025 +:105FF000ECF725FDECF721FEECF719FEECF7EFFCD2 +:106000000EF0D6FDB648056005604FF0E0214FF474 +:106010000040B846C1F88002ECF7BBFE94F82D7042 +:106020003846FFF75DFF0028FAD0A948803800EB1A +:10603000871010F81600022802D006E00120CCE7F5 +:106040003A4631460620FFF70FFD84F8238004EB23 +:10605000870090F82600202804D0A048801E4078B1 +:10606000ECF752FF207F002803D0ECF7D6FD257710 +:10607000657725E5964910B591F82D2000248039E3 +:1060800001EB821111F814302BB1641CE4B2202C06 +:10609000F8D3202010BD934901EB041108600020C3 +:1060A000C87321460120FFF7DFFC204610BD10B564 +:1060B000012801D0032800D171B3854A92F82D3010 +:1060C000834C0022803C04EB831300BF13F8124082 +:1060D0000CB1082010BD521CD2B2202AF6D37F4A40 +:1060E00048B1022807D0072916D2DFE801F01506CB +:1060F000080A0C0E100000210AE01B2108E03A21DA +:1061000006E0582104E0772102E0962100E0B52165 +:1061100051701070002010BD072010BD6F4810B5E1 +:106120004078ECF79CFD80B210BD10B5202811D24C +:10613000674991F82D30A1F1800202EB831414F825 +:1061400010303BB191F82D3002EB831212F8102081 +:10615000012A01D0002010BD91F82D200146002019 +:10616000FFF782FC012010BD10B5ECF706FDBDE87D +:106170001040ECF774BD2DE9F0410E46544F017804 +:106180002025803F0C4607EB831303E0254603EBF5 +:1061900045046478944202D0202CF7D108E0202CEA +:1061A00006D0A14206D103EB41014978017007E016 +:1061B000002085E403EB440003EB45014078487080 +:1061C000494F7EB127B1002140F22D40AFF300804E +:1061D0003078A04206D127B100214FF48660AFF39A +:1061E0000080357027B1002140F23540AFF30080C8 +:1061F000012065E410B542680B689A1A1202D417A0 +:1062000002EB1462121216D4497A91B1427A82B921 +:10621000364A006852F82110126819441044001DD3 +:10622000891C081A0002C11700EB11600012322805 +:1062300001DB012010BD002010BD2DE9F047294EE3 +:10624000814606F500709846144600EB811712E06F +:1062500006EB0415291D4846FFF7CCFF68B988F8FE +:106260000040A97B99F80A00814201D80020DEE4B1 +:1062700007EB44004478202CEAD10120D7E42DE933 +:10628000F047824612480E4600EB8600DFF8548045 +:1062900090F825402020107008F5007099461546AA +:1062A00000EB86170BE000BF08EB04105146001D01 +:1062B000FFF7A0FF28B107EB44002C704478202C96 +:1062C000F2D1297889F800104B46224631460FE07A +:1062D000040B0020FFFF3F00000000009A00002098 +:1062E00000F500408408002000000000CC6202009D +:1062F0005046BDE8F047A0E72DE9FC410F460446B3 +:106300000025FE4E10E000BF9DF80000256806EB5A +:1063100000108168204600F0E1FD2068A84202D10B +:106320000020BDE8FC8101256B4601AA39462046C4 +:10633000FFF7A5FF0028E7D02846F2E770B504462E +:10634000EF480125A54300EB841100EB85104022A6 +:10635000F8F773F8EB4E26B1002140F29D40AFF301 +:106360000080E748803000EB850100EB8400D0F826 +:106370002500C1F8250026B1002140F2A140AFF36D +:106380000080284670BD8A4203D003460520FFF7EF +:1063900099BB202906D0DA4A02EB801000EB4100BD +:1063A00040787047D649803101EB800090F8250095 +:1063B0007047D24901EB0010001DFFF7DEBB7CB532 +:1063C0001D46134604460E4600F1080221461846B3 +:1063D000ECF752FC94F908000F2804DD1F382072F6 +:1063E0002068401C206096B10220C74951F8261051 +:1063F000461820686946801B20F07F40206094F991 +:1064000008002844C01C1F2803DA012009E00420EA +:10641000EBE701AAECF730FC9DF8040010B10098FE +:10642000401C00900099206831440844C01C20F0B2 +:106430007F4060607CBDFEB50C46064609786079F9 +:10644000907220791F461546507279B12179002249 +:106450002846A368FFF7B3FFA9492846803191F881 +:106460002E20202A0AD00969491D0DE0D4E9022313 +:10647000217903B02846BDE8F040A0E7A349497858 +:10648000052900D20521314421F07F4100F026FD8D +:1064900039462846FFF730FFD4E9023221796846B1 +:1064A000FFF78DFF2B4600213046019A00F002FDD8 +:1064B000002806D103B031462846BDE8F04000F080 +:1064C0000DBDFEBD2DE9F14F84B000F0C3FCF0B16D +:1064D00000270498007800284FF000006DD1884D07 +:1064E000884C82468346803524B1002140F2045016 +:1064F000AFF3008095F82D8085F823B0002624B1F5 +:10650000002140F20950AFF3008017B105E00127E8 +:10651000DFE74046FFF712FF804624B1002140F23A +:106520001150AFF30080ECF728FB814643466A46E2 +:106530000499FFF780FF24B1002140F21750AFF318 +:10654000008095F82E0020280CD029690098401A68 +:106550000002C21700EB1260001203D5684600F07B +:10656000BDFC01264CB1002140F22150AFF3008068 +:10657000002140F22650AFF300806B46644A0021B0 +:10658000484600F097FC98B127B941466846FFF7A6 +:10659000B3FE064326B16846FFF7EFFA0499886018 +:1065A0004FF0010A24B1002140F23A50AFF30080CD +:1065B00095F82300002897D1504605B073E42DE9E3 +:1065C000F04F89B08B46824600F044FC4C4C80343E +:1065D00030B39BF80000002710B1012800D0FFDF86 +:1065E000484D25B1002140F2F950AFF300804349F6 +:1065F000012001EB0A18A946CDF81C005FEA090644 +:1066000004D0002140F20160AFF30080079800F051 +:1066100018FC94F82D50002084F8230067B119E08D +:1066200094F82E000127202800D1FFDF9BF80000FE +:106630000028D5D0FFDFD3E72846FFF77FFE0546C9 +:1066400026B1002140F20B60AFF3008094F82300E4 +:106650000028D3D126B1002140F21560AFF30080AD +:10666000ECF78BFA2B4602AA59460790FFF7E3FE98 +:1066700098F80F005FEA060900F001008DF813009A +:1066800004D0002140F21F60AFF300803B462A4651 +:1066900002A9CDF800A0079800F02BFC064604EBF9 +:1066A000850090F828000090B9F1000F04D0002177 +:1066B00040F22660AFF3008000F0B8FB0790B9F11C +:1066C000000F04D0002140F22C60AFF3008094F85A +:1066D0002300002892D1B9F1000F04D0002140F22C +:1066E0003460AFF300800DF1080C9CE80E00C8E99F +:1066F0000112C8F80C30BEB30CE000008408002082 +:10670000840A002000000000CC6202009A000020F1 +:10671000FFFF3F005FEA090604D0002140F241601C +:10672000AFF300800098B84312D094F82E002028D0 +:106730000ED126B1002140F24660AFF3008028461A +:10674000FFF7CEFB20B99BF80000D8B3012849D051 +:10675000B9F1000F04D0002140F26360AFF3008074 +:10676000284600F05CFB01265FEA090504D0002101 +:1067700040F26C60AFF30080079800F062FB25B137 +:1067800000214FF4CE60AFF300808EB194F82D005D +:1067900004EB800090F82600202809D025B10021C4 +:1067A00040F27760AFF30080F7484078ECF7ACFB3D +:1067B00025B1002140F27C60AFF3008009B0304683 +:1067C000BDE8F08FFFE7B9F1000F04D0002140F2DF +:1067D0004E60AFF3008094F82D2051460420FFF75F +:1067E00043F9C0E7002E3FF409AF002140F25960A1 +:1067F000AFF3008002E72DE9F84FE44D814695F8AC +:106800002D004FF00008E24C4FF0010B474624B139 +:10681000002140F28A60AFF30080584600F011FB7F +:1068200085F8237024B1002140F28F60AFF300801F +:1068300095F82D00FFF782FD064695F8230028B154 +:10684000002CE4D0002140F295604BE024B10021FF +:1068500040F29960AFF30080CC48803800EB86119D +:1068600011F81900032856D1334605EB830A4A462E +:106870009AF82500904201D1012000E0002000900C +:106880000AF125000021FFF776FC0146009801423D +:1068900003D001228AF82820AF77E1B324B1002188 +:1068A00040F29E60AFF30080324649460120FFF778 +:1068B000DBF89AF828A024B1002140F2A960AFF3D8 +:1068C000008000F0B3FA834624B1002140F2AE60AC +:1068D000AFF3008095F8230038B1002C97D0002149 +:1068E00040F2B260AFF3008091E7BAF1000F07D039 +:1068F00095F82E00202803D13046FFF7F1FAE0B1D9 +:1069000024B1002140F2C660AFF30080304600F0B1 +:1069100086FA4FF0010824B1002140F2CF60AFF3B6 +:106920000080584600F08DFA24B1002140F2D36077 +:10693000AFF300804046BDE8F88F002CF1D0002175 +:1069400040F2C160AFF30080E6E70120ECF750B8F9 +:106950008D48007870472DE9F0418C4C94F82E005A +:1069600020281FD194F82D6004EB860797F8255056 +:10697000202D00D1FFDF8549803901EB861000EB27 +:106980004500407807F8250F0120F87084F82300AF +:10699000294684F82E50324602202234FFF764F84C +:1069A0000020207005E42DE9F0417A4E774C012556 +:1069B00038B1012821D0022879D003287DD0FFDF0B +:1069C00017E400F05FFAFFF7C6FF207E00B1FFDF9B +:1069D00084F821500020ECF732F8A168481C04D05C +:1069E000012300221846ECF77DF814F82E0F2178C9 +:1069F00006EB01110A68012154E0FFF7ACFF01200A +:106A0000ECF71DF894F8210050B1A068401C07D0A5 +:106A100014F82E0F217806EB01110A68062141E0D7 +:106A2000207EDFF86481002708F10208012803D0E6 +:106A300002281ED0FFDFB5E7A777ECF7EEF898F84D +:106A40000000032801D165772577607D524951F810 +:106A5000200094F8201051B948B161680123091A47 +:106A600000221846ECF73EF8022020769AE72776B7 +:106A700098E784F8205000F005FAA07F50B198F80C +:106A8000010061680123091A00221846ECF72AF870 +:106A9000257600E0277614F82E0F217806EB0111F9 +:106AA0000A680021BDE8F041104700E005E03648E3 +:106AB0000078BDE8F041ECF727BAFFF74CFF14F877 +:106AC0002E0F217806EB01110A680521EAE710B5BF +:106AD0002E4C94F82E00202800D1FFDF14F82E0F42 +:106AE00021782C4A02EB01110A68BDE8104004210C +:106AF00010477CB5254C054694F82E00202800D17F +:106B0000FFDFA068401C00D0FFDF94F82E00214971 +:106B100001AA01EB0010694690F90C002844ECF73B +:106B2000ABF89DF904000F2801DD012000E00020F2 +:106B3000009908446168084420F07F41A16094F8FE +:106B40002100002807D002B00123BDE870400022D8 +:106B50001846EBF7C7BF7CBD30B5104A0B1A541C62 +:106B6000B3EB940F1ED3451AB5EB940F1AD393428F +:106B700003D9101A43185B1C14E0954210D9511A1E +:106B80000844401C43420DE098000020040B002004 +:106B90000000000084080020CC620200FF7F841EF9 +:106BA000FFDF0023184630BD0123002201460220EA +:106BB000EBF798BF0220EBF742BFEBF7DEBF2DE902 +:106BC000FE4FEE4C05468A4694F82E00202800D150 +:106BD000FFDFEA4E94F82E10A0462046A6F520725C +:106BE00002EB011420218DF8001090F82D10376968 +:106BF00000EB8101D8F8000091F82590284402AA02 +:106C000001A90C36ECF738F89DF90800002802DDE0 +:106C10000198401C0190A0680199642D084452D34A +:106C2000D74B00225B1B72EB02014CD36168411A07 +:106C300021F07F41B1F5800F45D220F07F40706098 +:106C400086F80AA098F82D1044466B464A4630460E +:106C5000FFF7F3FAB0B3A068401C10D0EBF78DFF3C +:106C6000A168081A0002C11700EB11600012022887 +:106C70002BDD0120EBF7E3FE4FF0FF30A06094F82E +:106C80002D009DF8002020210F34FFF77CFBA17F11 +:106C9000BA4A803A02EB8111E27F01EB420148706F +:106CA00054F80F0C284444F80F0C012020759DF86F +:106CB0000000202803D0B3484078ECF725F90120E4 +:106CC000BDE8FE8F01E00020FAE77760FBE72DE9E1 +:106CD000F047AA4C074694F82D00A4F1800606EB75 +:106CE000801010F8170000B9FFDF94F82D50A0466F +:106CF000A54C24B1002140F6EA00AFF3008040F635 +:106D0000F60940F6FF0A06EB851600BF16F81700D5 +:106D1000012819D0042811D005280FD006280DD03D +:106D20001CB100214846AFF300800FF02DF8002C75 +:106D3000ECD000215046AFF30080E7E72A46394601 +:106D40000120FEF791FEF2E74FF0010A4FF0000933 +:106D5000454624B1002140F60610AFF300805046AE +:106D600000F06FF885F8239024B1002140F60B1055 +:106D7000AFF3008095F82D00FFF7E0FA064695F88E +:106D8000230028B1002CE4D0002140F611101FE0B0 +:106D900024B1002140F61510AFF3008005EB86000A +:106DA00000F1270133463A462630FFF7E4F924B1D3 +:106DB000002140F61910AFF3008000F037F882464A +:106DC00095F8230038B1002CC3D0002140F61F10E5 +:106DD000AFF30080BDE785F82D60012085F8230022 +:106DE000504600F02EF8002C04D0002140F62C1064 +:106DF000AFF30080BDE8F08730B504465F480D462C +:106E000090F82D005D49803901EB801010F81400D6 +:106E100000B9FFDF5D4800EB0410C57330BD574972 +:106E200081F82D00012081F82300704710B55848E3 +:106E300008B1AFF30080EFF3108000F0010072B6EC +:106E400010BD10B5002804D1524808B1AFF300803E +:106E500062B610BD50480068C005C00D10D0103893 +:106E600040B2002804DB00F1E02090F8000405E0C7 +:106E700000F00F0000F1E02090F8140D4009704779 +:106E80000820704710B53D4C94F82400002804D128 +:106E9000F4F712FF012084F8240010BD10B5374C20 +:106EA00094F82400002804D0F4F72FFF002084F881 +:106EB000240010BD10B51C685B68241A181A24F051 +:106EC0007F4420F07F40A14206D8B4F5800F03D262 +:106ED000904201D8012010BD002010BDD0E9003241 +:106EE000D21A21F07F43114421F07F41C0E90031E3 +:106EF00070472DE9FC418446204815468038089C9F +:106F000000EB85160F4616F81400012804D002285D +:106F100002D00020BDE8FC810B46204A01216046DA +:106F2000FFF7C8FFF0B101AB6A4629463846FFF7C4 +:106F3000A6F9B8B19DF804209DF800102846FFF787 +:106F400022FA06EB440148709DF8000020280DD07D +:106F500006EB400044702A4621460320FEF784FDDC +:106F60000120D7E72A4621460420F7E703480121FC +:106F700000EB850000F8254FC170ECE7040B002002 +:106F8000FF1FA107980000200000000084080020D7 +:106F9000000000000000000004ED00E0FFFF3F00E3 +:106FA0002DE9F041044680074FF000054FF001063F +:106FB0000CD56B480560066000F0E8F920B169481F +:106FC000016841F48061016024F00204E0044FF0A4 +:106FD000FF3705D564484660C0F8087324F4805430 +:106FE000600003D56148056024F08044E0050FD5BA +:106FF0005F48C0F80052C0F808735E490D60091D73 +:107000000D605C4A04210C321160066124F4807426 +:10701000A00409D558484660C0F80052C0F808736B +:107020005648056024F40054C4F38030C4F3C031E2 +:10703000884200D0FFDF14F4404F14D0504846601F +:10704000C0F808734F488660C0F80052C0F8087353 +:107050004D490D600A1D16608660C0F808730D600A +:10706000166024F4404420050AD5484846608660EE +:10707000C0F80873C0F848734548056024F40064FC +:107080000DF070FD4348044200D0FFDFBDE8F08101 +:10709000F0B50022202501234FEA020420FA02F174 +:1070A000C9072DD051B2002910DB00BF4FEA51179C +:1070B0004FEA870701F01F0607F1E02703FA06F6FB +:1070C000C7F88061BFF34F8FBFF36F8F0CDB00BF3A +:1070D0004FEA51174FEA870701F01F0607F1E02733 +:1070E00003FA06F6C7F8806204DB01F1E02181F8BB +:1070F000004405E001F00F0101F1E02181F8144D99 +:1071000002F10102AA42C9D3F0BD10B5224C2060A1 +:107110000846F4F7EAFE2068FFF742FF2068FFF711 +:10712000B7FF0DF045F900F092F90DF01BFD0DF0E1 +:1071300058FCEBF7B5FEBDE810400DF0EDB910B509 +:10714000154C2068FFF72CFF2068FFF7A1FF0DF01A +:1071500009FDF4F7C9FF0020206010BD0A20704728 +:10716000FC1F00403C17004000C0004004E5014007 +:10717000008000400485004000D0004004D500405D +:1071800000E0004000F0004000F5004000B000408A +:1071900008B50040FEFF0FFD9C00002070B5264999 +:1071A0000A680AB30022154601244B685B1C4B6039 +:1071B0000C2B00D34D600E7904FA06F30E681E42C4 +:1071C0000FD0EFF3108212F0010272B600D001224C +:1071D0000C689C430C6002B962B6496801600020EB +:1071E00070BD521C0C2AE0D3052070BD4FF0E02189 +:1071F0004FF48000C1F800027047EFF3108111F0E6 +:10720000010F72B64FF0010202FA00F20A48036859 +:1072100042EA0302026000D162B6E7E706480021B5 +:1072200001604160704701218140034800680840C7 +:1072300000D0012070470000A0000020012081073D +:10724000086070470121880741600021C0F80011E3 +:1072500018480170704717490120087070474FF0B7 +:107260008040D0F80001012803D01248007800289F +:1072700000D00120704710480068C00700D00120EE +:1072800070470D480C300068C00700D001207047DF +:107290000948143000687047074910310A68D20362 +:1072A00006D5096801F00301814201D10120704730 +:1072B00000207047A8000020080400404FF08050D4 +:1072C000D0F830010A2801D0002070470120704713 +:1072D00000B5FFF7F3FF20B14FF08050D0F8340134 +:1072E00008B1002000BD012000BD4FF08050D0F853 +:1072F00030010E2801D000207047012070474FF068 +:107300008050D0F83001062803D0401C01D0002066 +:107310007047012070474FF08050D0F830010D28A1 +:1073200001D000207047012070474FF08050D0F806 +:107330003001082801D000207047012070474FF02D +:107340008050D0F83001102801D000207047012073 +:10735000704700B5FFF7F3FF30B9FFF7DCFF18B94E +:10736000FFF7E3FF002800D0012000BD00B5FFF7C4 +:10737000C6FF38B14FF08050D0F83401062803D34F +:10738000401C01D0002000BD012000BD00B5FFF76A +:10739000B6FF48B14FF08050D0F83401062803D32F +:1073A000401C01D0012000BD002000BD0021017063 +:1073B000084670470146002008707047EFF31081BF +:1073C00001F0010172B60278012A01D0012200E029 +:1073D00000220123037001B962B60AB10020704790 +:1073E0004FF400507047E9E7EFF3108111F0010FFF +:1073F00072B64FF00002027000D162B600207047F2 +:10740000F2E700002DE9F04115460E46044600273C +:1074100000F0EBF8A84215D3002341200FE000BF95 +:1074200094F84220A25CF25494F84210491CB1FB3B +:10743000F0F200FB12115B1C84F84210DBB2AB428D +:10744000EED3012700F0DDF83846BDE8F08172493F +:1074500010B5802081F800047049002081F84200B6 +:1074600081F84100433181F8420081F84100433105 +:1074700081F8420081F841006948FFF797FF6848AA +:10748000401CFFF793FFEBF793FCBDE8104000F0C2 +:10749000B8B840207047614800F0A7B80A460146D6 +:1074A0005E48AFE7402070475C48433000F09DB82D +:1074B0000A46014659484330A4E7402101700020A4 +:1074C000704710B504465548863000F08EF820709D +:1074D000002010BD0A460146504810B58630FFF71F +:1074E00091FF08B1002010BD42F2070010BD70B539 +:1074F0000C460646412900D9FFDF4A48006810388B +:1075000040B200F054F8C5B20D2000F050F8C0B2FF +:10751000854201D3012504E0002502E00DB1EBF71F +:107520008AFC224631463D48FFF76CFF0028F5D023 +:1075300070BD2DE9F0413A4F0025064617F10407CA +:1075400057F82540204600F041F810B36D1CEDB20D +:10755000032DF5D33148433000F038F8002825D00A +:107560002E4800F033F8002820D02C48863000F058 +:107570002DF800281AD0EBF734FC2948FFF71EFF3E +:10758000B0F5005F00D0FFDFBDE8F0412448FFF711 +:107590002BBF94F841004121265414F8410F401CA0 +:1075A000B0FBF1F201FB12002070D3E74DE7002899 +:1075B00004DB00F1E02090F8000405E000F00F008B +:1075C00000F1E02090F8140D4009704710F8411FB9 +:1075D0004122491CB1FBF2F302FB131140788142B6 +:1075E00001D1012070470020704710F8411F4078FA +:1075F000814201D3081A02E0C0F141000844C0B240 +:10760000704710B50648FFF7D9FE002803D1BDE842 +:107610001040EBF7D1BB10BD0DE000E0340B0020B3 +:10762000AC00002004ED00E070B5154D2878401C3A +:10763000C4B26878844202D000F0DBFA2C7070BDCE +:107640002DE9F0410E4C4FF0E02600BF00F0C6FAE5 +:107650000EF09AFB40BF20BF677820786070D6F8A4 +:107660000052E9F798FE854305D1D6F8040210B917 +:107670002078B842EAD000F0ACFA0020BDE8F081F2 +:10768000BC0000202DE9F04101264FF0E02231033B +:107690004FF000084046C2F88011BFF34F8FBFF390 +:1076A0006F8F204CC4F800010C2000F02EF81E4D06 +:1076B0002868C04340F30017286840F01000286095 +:1076C000C4F8046326607F1C02E000BF0EF05CFB80 +:1076D000D4F800010028F9D01FB9286820F0100064 +:1076E0002860124805686660C4F80863C4F8008121 +:1076F0000C2000F00AF82846BDE8F08110B50446D9 +:10770000FFF7C0FF2060002010BD002809DB00F05B +:107710001F02012191404009800000F1E020C0F8E3 +:107720008012704700C0004010ED00E008C5004026 +:107730002DE9F047FF4C0646FF21A06800EB06123A +:1077400011702178FF2910D04FF0080909EB0111C1 +:1077500009EB06174158C05900F0F4F9002807DD7D +:10776000A168207801EB061108702670BDE8F0874B +:1077700094F8008045460DE0A06809EB05114158DA +:10778000C05900F0DFF9002806DCA068A84600EB2D +:1077900008100578FF2DEFD1A06800EB061100EB73 +:1077A00008100D700670E1E7F0B5E24B04460020CA +:1077B00001259A680C269B780CE000BF05EB0017AA +:1077C000D75DA74204D106EB0017D7598F4204D0EA +:1077D000401CC0B28342F1D8FF20F0BD70B5FFF766 +:1077E000ECF9D44C08252278A16805EB02128958DF +:1077F00000F0A8F9012808DD2178A06805EB011147 +:107800004058BDE87040FFF7CFB9FFF7A1F8BDE8D9 +:107810007040EBF779BB2DE9F041C64C2578FFF7B6 +:10782000CCF9FF2D6ED04FF00808A26808EB0516C2 +:10783000915900F087F90228A06801DD80595DE0C8 +:1078400000EB051109782170022101EB0511425C62 +:107850005AB1521E4254815901F5800121F07F41F5 +:1078600081512846FFF764FF34E00423012203EB33 +:10787000051302EB051250F803C0875CBCF1000F42 +:1078800010D0BCF5007F10D9CCF3080250F806C028 +:107890000CEB423C2CF07F4C40F806C0C3589A1ABF +:1078A000520A09E0FF2181540AE0825902EB4C326E +:1078B00022F07F428251002242542846FFF738FFCF +:1078C0000C21A06801EB05114158E06850F8272011 +:1078D000384690472078FF2814D0FFF76EF92278B9 +:1078E000A16808EB02124546895800F02BF90128DF +:1078F00093DD2178A06805EB01114058BDE8F04107 +:10790000FFF752B9BDE8F081F0B51D4614460E46AA +:107910000746FF2B00D3FFDFA00700D0FFDF85481D +:10792000FF210022C0E90247C57006710170427054 +:1079300082701046012204E002EB0013401CE15467 +:10794000C0B2A842F8D3F0BD70B57A4C064665784F +:107950002079854200D3FFDFE06840F82560607839 +:10796000401C6070284670BD2DE9FF5F1D468B46A8 +:107970000746FF24FFF721F9DFF8B891064699F88A +:107980000100B84200D8FFDF00214FF001084FF09E +:107990000C0A99F80220D9F808000EE008EB011350 +:1079A000C35CFF2B0ED0BB4205D10AEB011350F88C +:1079B00003C0DC450CD0491CC9B28A42EED8FF2C6A +:1079C00002D00DE00C46F6E799F803108A4203D185 +:1079D000FF2004B0BDE8F09F1446521C89F8022035 +:1079E00008EB04110AEB0412475440F802B00421DA +:1079F000029B0022012B01EB04110CD040F8012066 +:107A00004FF4007808234FF0020C454513D9E905DF +:107A1000C90D02D002E04550F2E7414606EB413283 +:107A200003EB041322F07F42C250691A0CEB0412DC +:107A3000490A81540BE005B9012506EB453103EBFA +:107A4000041321F07F41C1500CEB0411425499F80A +:107A500000502046FFF76CFE99F80000A84201D0C4 +:107A6000FFF7BCFE3846B4E770B50C460546FFF795 +:107A7000A4F8064621462846FFF796FE0446FF284E +:107A80001AD02C4D082101EB0411A868415830464A +:107A900000F058F800F58050C11700EBD1404013BA +:107AA0000221AA6801EB0411515C09B100EB4120ED +:107AB000002800DC012070BD002070BD2DE9F047DA +:107AC00088468146FFF770FE0746FF281BD0194DF8 +:107AD0002E78A8683146344605E0BC4206D02646DA +:107AE00000EB06121478FF2CF7D10CE0FF2C0AD023 +:107AF000A6420CD100EB011000782870FF2804D0BA +:107B0000FFF76CFE03E0002030E6FFF753F8414634 +:107B10004846FFF7A9FF0123A968024603EB0413B7 +:107B2000FF20C854A878401EB84200D1A87001EBCD +:107B3000041001E0000C002001EB06110078087031 +:107B4000104613E6081A0002C11700EB116000127C +:107B50007047000010B5202000F07FF8202000F0D2 +:107B60008DF84D49202081F80004E9F712FC4B49BB +:107B700008604B48D0F8041341F00101C0F8041329 +:107B8000D0F8041341F08071C0F804134249012079 +:107B90001C39C1F8000110BD10B5202000F05DF8BF +:107BA0003E480021C8380160001D01603D4A481E62 +:107BB00010603B4AC2F80803384B1960C2F8000154 +:107BC000C2F8600138490860BDE81040202000F08C +:107BD00055B834493548091F086070473149334862 +:107BE000086070472D48C8380160001D521E0260B1 +:107BF00070472C4901200860BFF34F8F70472DE973 +:107C0000F0412849D0F8188028480860244CD4F85E +:107C100000010025244E6F1E28B14046E9F712FBF3 +:107C200040B9002111E0D4F8600198B14046E9F76D +:107C300009FB48B1C4F80051C4F860513760BDE891 +:107C4000F041202000F01AB831684046BDE8F0410C +:107C50000EF0A4B8FFDFBDE8F08100280DDB00F0D6 +:107C60001F02012191404009800000F1E020C0F88E +:107C70008011BFF34F8FBFF36F8F7047002809DB70 +:107C800000F01F02012191404009800000F1E02036 +:107C9000C0F880127047000020E000E0C8060240F3 +:107CA00000000240180502400004024001000001EB +:107CB0005E4800210170417010218170704770B5DD +:107CC000054616460C460220EAF714FE57490120E5 +:107CD00008705749F01E086056480560001F046090 +:107CE00070BD10B50220EAF705FE5049012008706A +:107CF00051480021C0F80011C0F80411C0F8081163 +:107D00004E494FF40000086010BD48480178D9B1D1 +:107D10004B4A4FF4000111604749D1F8003100226D +:107D2000002B1CBFD1F80431002B02D0D1F8081170 +:107D300019B142704FF0100104E04FF001014170A1 +:107D400040490968817002704FF00000EAF7D2BD27 +:107D500010B50220EAF7CEFD34480122002102705E +:107D60003548C0F80011C0F80411C0F808110260CD +:107D700010BD2E480178002904BF407870472E4876 +:107D8000D0F80011002904BF02207047D0F800117C +:107D900000291CBFD0F80411002905D0D0F8080133 +:107DA000002804BF01207047002070471F4800B51D +:107DB0000278214B4078C821491EC9B282B1D3F85C +:107DC00000C1BCF1000F10D0D3F8000100281CBF87 +:107DD000D3F8040100280BD0D3F8080150B107E014 +:107DE000022802D0012805D002E00029E4D1FFDFFB +:107DF000002000BD012000BD0C480178002904BF0F +:107E0000807870470C48D0F8001100291CBFD0F8CA +:107E10000411002902D0D0F8080110B14FF0100071 +:107E2000704708480068C0B270470000BE000020DC +:107E300010F5004008F5004000F0004004F5014056 +:107E400008F5014000F400405748002101704170DE +:107E5000704770B5064614460D460120EAF74AFD04 +:107E600052480660001D0460001D05605049002056 +:107E7000C1F850014F490320086050494E4808603E +:107E8000091D4F48086070BD2DE9F0410546464880 +:107E90000C46012606704B4945EA024040F08070CE +:107EA0000860FFF72CFA002804BF47480460002749 +:107EB000464CC4F80471474945480860002D02BF8C +:107EC000C4F800622660BDE8F081012D18BFFFDF15 +:107ED000C4F80072266041493F480860BDE8F0815F +:107EE0003148017871B13B4A394911603749D1F8BD +:107EF00004210021002A08BF417002D0384A1268CC +:107F0000427001700020EAF7F5BC2748017800298B +:107F100004BF407870472D48D0F80401002808BFFE +:107F200070472F480068C0B27047002808BF7047EC +:107F30002DE9F0471C480078002808BFFFDF234CDC +:107F4000D4F80401002818BFBDE8F0874FF00209FB +:107F5000C4F80493234F3868C0F30018386840F021 +:107F600010003860D4F80401002804BF4FF4004525 +:107F70004FF0E02608D100BFC6F880520DF004FF94 +:107F8000D4F804010028F7D0B8F1000F03D1386805 +:107F900020F010003860C4F80893BDE8F0870B4962 +:107FA0000120886070470000C100002008F50040F3 +:107FB000001000401CF500405011004098F50140B1 +:107FC0000CF0004004F5004018F5004000F00040BF +:107FD0000000020308F501400000020204F5014020 +:107FE00000F4004010ED00E0012804BF41F6A47049 +:107FF0007047022804BF41F288307047042804BF4C +:1080000046F218007047082804BF47F2A0307047B6 +:1080100000B5FFDF41F6A47000BD10B5FE48002496 +:1080200001214470047044728472C17280F825404A +:10803000C462846380F83C4080F83D40FF2180F8B2 +:108040003E105F2180F83F1018300DF09FFFF3497C +:10805000601E0860091D0860091D0C60091D08608C +:10806000091D0C60091D0860091D0860091D0860D4 +:10807000091D0860091D0860091D0860091D0860C8 +:10808000091D0860091D086010BDE549486070477A +:10809000E448016801F00F01032904BF0120704783 +:1080A000016801F00F01042904BF02207047016834 +:1080B00001F00F01052904D0006800F00F00062828 +:1080C00007D1D948006810F0060F0CBF0820042023 +:1080D000704700B5FFDF012000BD012812BF022854 +:1080E00000207047042812BF08284FF4C87070475A +:1080F00000B5FFDF002000BD012804BF2820704725 +:10810000022804BF18207047042812BF08284FF423 +:10811000A870704700B5FFDF282000BD70B5C148CA +:10812000016801F00F01032908BF012414D0016880 +:1081300001F00F01042904BF022418210DD00168A9 +:1081400001F00F0105294BD0006800F00F00062850 +:108150001CBFFFDF012443D02821AF48C26A806AD8 +:10816000101A0E18082C04BF4EF6981547F2A030CE +:108170002DD02046042C08BF4EF628350BD0012800 +:1081800008BF41F6A47506D0022C1ABFFFDF41F6E6 +:10819000A47541F28835012C08BF41F6A47016D0B1 +:1081A000022C08BF002005D0042C1ABFFFDF0020DE +:1081B0004FF4C8702D1A022C08BF41F2883006D047 +:1081C000042C1ABFFFDF41F6A47046F21800281AEB +:1081D0004FF47A7100F2E730B0FBF1F0304470BD3B +:1081E0009148006810F0060F0CBF082404244FF4D7 +:1081F000A871B2E710B58D49026801F118040A634D +:1082000042684A63007A81F83800207E48B1207FB6 +:10821000F6F781F9A07E011C18BF0121207FF6F737 +:1082200069F9607E002808BF10BD607FF6F773F91A +:10823000E07E011C18BF0121607FBDE81040F6F709 +:1082400059B930B50024054601290AD0022908BFD2 +:108250004FF0807405D0042916BF08294FF0C74499 +:10826000FFDF44F4847040F480107149086045F4E5 +:10827000403001F1040140F00070086030BD30B5BD +:108280000024054601290AD0022908BF4FF0807456 +:1082900005D0042916BF08294FF0C744FFDF44F476 +:1082A000847040F480106249086045F4403001F168 +:1082B000040140F0007008605E48D0F8000100281A +:1082C00018BFFFDF30BD2DE9F04102274FF0E02855 +:1082D00001250024C8F88071BFF34F8FBFF36F8F63 +:1082E000554804600560FFF751F8544E18B13068E6 +:1082F00040F480603060FFF702F838B1306820F059 +:10830000690040F0960040F0004030604D494C4814 +:1083100008604FF01020806CB0F1FF3F04D04A4954 +:108320000A6860F317420A60484940F25B600860DF +:10833000091F40F203100860081F05603949032037 +:10834000086043480560444A42491160444A434931 +:108350001160121F43491160016821F440710160EE +:10836000016841F480710160C8F8807231491020C1 +:10837000C1F80403284880F83140C46228484068A6 +:10838000002808BFBDE8F081BDE8F0410047274A5A +:108390000368C2F81A308088D08302F11800017295 +:1083A00070471D4B10B51A7A8A4208D10146062241 +:1083B000981CF6F715F8002804BF012010BD002016 +:1083C00010BD154890F825007047134A5170107081 +:1083D0007047F0B50546800000F1804000F5805000 +:1083E0008B88C0F820360B78D1F8011043EA0121C0 +:1083F000C0F8001605F10800012707FA00F61A4C2C +:10840000002A04BF2068B04304D0012A18BFFFDF50 +:1084100020683043206029E0280C0020000E004036 +:10842000C40000201015004014140040100C00205F +:108430001415004000100040FC1F00403C17004095 +:108440002C000089781700408C150040381500403A +:108450005016004000000E0408F501404080004026 +:10846000A4F501401011004040160040206807FAB2 +:1084700005F108432060F0BD0CF0C4BCFE4890F844 +:1084800032007047FD4AC17811600068FC49000263 +:1084900008607047252808BF02210ED0262808BF93 +:1084A0001A210AD0272808BF502106D00A2894BFD5 +:1084B0000422062202EB4001C9B2F24A1160F249DD +:1084C000086070472DE9F047EB4CA17A012956D09E +:1084D000022918BFBDE8F087627E002A08BFBDE808 +:1084E000F087012950D0E17E667F0D1C18BF012561 +:1084F0005FF02401DFF894934FF00108C9F84C8035 +:10850000DFF88CA34718DAF80000B84228BFFFDF75 +:108510000020C9F84C01CAF80070300285F0010152 +:1085200040EA015040F0031194F82000820002F16B +:10853000804202F5C042C2F81015D64901EB800115 +:10854000A07FC20002F1804202F5F832C2F8141591 +:10855000D14BC2F81035E27FD30003F1804303F51D +:10856000F833C3F81415CD49C3F8101508FA00F014 +:1085700008FA02F10843CA490860BDE8F087227E84 +:10858000002AAED1BDE8F087A17E267F002914BF66 +:10859000012500251121ADE72DE9F041C14E8046AE +:1085A00003200D46C6F80002BD49BF4808602846B2 +:1085B0000CF02CFCB04F0124B8F1000F04BFBC72CA +:1085C000346026D0B8F1010F23D1B848006860B9F3 +:1085D00015F00C0F09D0C6F80443012000F0DAFEB4 +:1085E000F463346487F83C4002E0002000F0D2FEDF +:1085F00028460CF0F3FC0220B872FEF7B7FE38B93B +:10860000FEF7C4FE20B9AA48016841F4C021016008 +:1086100074609E48C4649E4800682946BDE8F041E5 +:1086200050E72DE9F0479F4E814603200D46C6F8DE +:108630000002DFF86C829C48C8F8000008460CF085 +:10864000E5FB28460CF0CAFC01248B4FB9F1000F62 +:1086500003D0B9F1010F0AD031E0BC72B86B40F41D +:108660008010B8634FF48010C8F8000027E00220A3 +:10867000B872B86B40F40010B8634FF40010C8F83B +:1086800000008A48006860B915F00C0F09D0C6F8E0 +:108690000443012000F07EFEF463346487F83C401C +:1086A00002E0002000F076FEFEF760FE38B9FEF72B +:1086B0006DFE20B97E48016841F4C0210160EAF7EF +:1086C000F7FA2946BDE8F047FCE62DE9F84F754C6E +:1086D0008246032088461746C4F80002DFF8C0919E +:1086E0007148C9F8000010460CF090FBDFF8C4B1E7 +:1086F000614E0125BAF1000F04BFCBF80040B572FE +:1087000004D0BAF1010F18BFFFDF2FD06A48C0F8BC +:1087100000806B4969480860B06B40F40020B0638A +:10872000D4F800321021C4F808130020C4F8000265 +:10873000DFF890C18A03CCF80020C4F80001C4F827 +:108740000C01C4F81001C4F80401C4F81401C4F801 +:1087500018015D4800680090C4F80032C9F8002094 +:10876000C4F80413BAF1010F14D026E038460CF017 +:1087700035FCFEF7FBFD38B9FEF708FE20B94C4882 +:10878000016841F4C02101605048CBF8000002208C +:10879000B072BBE74548006860B917F00C0F09D00C +:1087A000C4F80453012000F0F5FDE563256486F864 +:1087B0003C5002E0002000F0EDFD4FF40020C9F82D +:1087C00000003248C56432480068404528BFFFDFDA +:1087D00039464046BDE8F84F74E62DE9F041264C95 +:1087E0000646002594F8310017468846002808BF41 +:1087F000FFDF16B1012E16D021E094F831000128D8 +:1088000008D094F83020394640460CF014FBE16A59 +:10881000451814E094F830103A4640460CF049FBF5 +:10882000E16A45180BE094F8310094F83010012803 +:108830003A46404609D00CF064FBE16A45183A46D6 +:1088400029463046BDE8F0413FE70CF014FBE16AF1 +:108850004518F4E72DE9F84F124CD4F8000220F047 +:108860000309D4F804034FF0100AC0F30018C4F849 +:1088700008A300262CE00000280C0020241500404E +:108880001C150040081500405415004000800040B1 +:108890004C850040006000404C81004010110040B9 +:1088A00004F5014000100040000004048817004057 +:1088B00068150040ACF50140488500404881004003 +:1088C000A8F5014008F501401811004004100040CF +:1088D000C4F80062FC48FB490160FC4D0127A97AFD +:1088E000012902D0022903D015E0297E11B912E036 +:1088F000697E81B1A97FEA7F07FA01F107FA02F2E6 +:108900001143016095F82000800000F1804000F5DF +:10891000C040C0F81065FF208DF80000C4F8106159 +:10892000276104E09DF80000401E8DF800009DF8CE +:10893000000018B1D4F810010028F3D09DF8000011 +:10894000002808BFFFDFC4F81061002000F022FDFE +:108950006E72AE72EF72C4F80092B8F1000F18BFD9 +:10896000C4F804A3BDE8F88FFF2008B58DF8000017 +:10897000D7480021C0F810110121016105E000BFB6 +:108980009DF80010491E8DF800109DF8001019B1D7 +:10899000D0F810110029F3D09DF80000002808BF7E +:1089A000FFDF08BD0068CB4920F07F4008607047BA +:1089B0004FF0E0200221C0F8801100F5C070BFF335 +:1089C0004F8FBFF36F8FC0F80011704710B490E85D +:1089D0001C10C14981E81C10D0E90420C1E9042021 +:1089E00010BC70474FF0E0210220C1F80001704731 +:1089F000BA4908707047BA490860704770B50546B3 +:108A0000EAF756F9B14C2844E16A884298BFFFDF83 +:108A100001202074EAF74CF9B24A28440021606131 +:108A2000C2F84411B0490860A06BB04940F480001E +:108A3000A063D001086070BD70B5A44C0546AC4A77 +:108A40000220207410680E4600F00F00032808BFB3 +:108A5000012213D0106800F00F00042808BF022282 +:108A60000CD0106800F00F0005281BD0106800F033 +:108A70000F0006281CBFFFDF012213D094F831003D +:108A800094F83010012815D028460CF081FA954949 +:108A900060610020C1F844016169E06A08449249BC +:108AA000086070BD9348006810F0060F0CBF0822E4 +:108AB0000422E3E7334628460CF038FAE7E7824918 +:108AC0004FF4800008608148816B21F4800181634C +:108AD000002101747047C20002F1804202F5F832B1 +:108AE000854BC2F81035C2F81415012181407F482A +:108AF00001607648826B1143816370477948012198 +:108B00004160C1600021C0F84411774801606F489E +:108B1000C1627047794908606D48D0F8001241F091 +:108B20004001C0F8001270476948D0F8001221F0E7 +:108B30004001C0F800127149002008607047644885 +:108B4000D0F8001221F01001C0F80012012181615B +:108B500070475E49FF2081F83E005D480021C0F863 +:108B60001C11D0F8001241F01001C0F8001270473B +:108B7000574981B0D1F81C21012A0DD0534991F8F1 +:108B80003E10FF290DBF00204942017001B008BF0F +:108B90007047012001B07047594A126802F07F0205 +:108BA000524202700020C1F81C0156480068009033 +:108BB000EFE7F0B517460C00064608BFFFDF434D50 +:108BC00014F0010F2F731CBF012CFFDF002E0CBF10 +:108BD000012002206872EC7201281CBF0228FFDF0E +:108BE000F0BD3A4981F83F007047384A136C036082 +:108BF000506C086070472DE9F84F38480078042819 +:108C000028BFFFDF314CDFF8C080314D94F83C00C5 +:108C100000260127E0B1D5F8040110F1000918BFC2 +:108C20004FF00109D5F81001002818BF012050EAC3 +:108C300009014FF4002B17D08021C5F80813C8F89C +:108C400000B084F83C6090F0010F18BFBDE8F88FC9 +:108C5000DFF89090D9F84C01002871D0A07A012853 +:108C60006FD002286ED0D1E0D5F80001DFF890A0D7 +:108C700030B3C5F800616F61FF20009002E0401E34 +:108C8000009005D0D5F81C0100280098F7D000B955 +:108C9000FFDFDAF8000000F07F0A94F83F0050454B +:108CA0003CBF002000F076FB84F83EA0C5F81C61B4 +:108CB000C5F808731348006800902F64AF6326E07E +:108CC00022E0000000000E0408F50140280C0020FE +:108CD000001000403C150040100C0020C400002093 +:108CE00004150040008000404485004004F5014028 +:108CF000101500401414004004110040601500409D +:108D0000481500401C110040B9F1000F03D0B9F123 +:108D1000000F2ED05CE0DAF8000000F07F0084F84D +:108D20003E00C5F81C6194F83D1061B194F83F1005 +:108D300081421BD2002000F02DFB2F64AF6315E0B1 +:108D400064E04CE04EE0FE49096894F83F308AB296 +:108D5000090C984203D30F2A06D9022904D2012014 +:108D600000F018FB2F6401E02F64AF63F548006842 +:108D700000908022C5F80423F3498F64F348036808 +:108D8000A0F1040CDCF800C043F698273B4463458F +:108D900015D2026842F210731A440260C1F84861A9 +:108DA000EC49EB480860091FEB480860EB48C0F845 +:108DB00000B0A06B40F40020A063BDE8F88F06600F +:108DC000C1F84861C5F80823C8F800B0C1F8486187 +:108DD0008020C5F80803C8F800B0BDE8F88F207EF1 +:108DE00010B913E0607E88B1A07FE17F07FA00F040 +:108DF00007FA01F10843C8F8000094F82000800049 +:108E000000F1804000F5C040C0F81065C9F84C7012 +:108E1000D34800682064D34800686064D248A16BDE +:108E20000160A663217C002019B1D9F84411012901 +:108E300000D00021A27A012A6ED0022A74D000BF8D +:108E4000D5F8101101290CBF1021002141EA0008BA +:108E5000C648016811F0FF0F03D0D5F8141101299D +:108E600000D0002184F83210006810F0FF0F03D00A +:108E7000D5F81801012800D0002084F83300BC4840 +:108E8000006884F83400FEF774FF012818BF002042 +:108E900084F83500C5F80061C5F80C61C5F81061AB +:108EA000C5F80461C5F81461C5F81861B1480068D7 +:108EB0000090A548C0F84461AF480068DFF8BC9254 +:108EC0000090D9F80000A062A9F104000068E062F7 +:108ED000AB48016801F00F01032908BF012013D03E +:108EE000016801F00F01042908BF02200CD00168BD +:108EF00001F00F01052926D0006800F00F000628B8 +:108F00001CBFFFDF01201ED084F83000A07A84F857 +:108F1000310002282CD11EE0D5F80C01012814BF25 +:108F2000002008208CE7FFE7D5F80C01012814BFCA +:108F300000200220934A1268012A14BF0422002252 +:108F4000104308437CE79048006810F0060F0CBF00 +:108F500008200420D8E7607850B18C490968097866 +:108F60000840217831EA000008BF84F8247001D05D +:108F700084F82460DFF818A218F0020F06D0E9F791 +:108F800097FEA16A081ADAF81010884718F0010F46 +:108F900018BF4FF0000B0DD0E9F78AFEE16ADAF84E +:108FA0001420081A594690477A48007810F0010FAB +:108FB0002FD10CE018F0020F18BF4FF0010BEBD1CE +:108FC00018F0080F18BF4FF0020BE5D1ECE7DFF8FF +:108FD000BCB1DBF80000007800F00F00072828BFC4 +:108FE00084F8256015D2DBF80000062200F10901A3 +:108FF000A01CF5F7F5F940B9207ADBF800100978E4 +:10900000B0EBD11F08BF012001D04FF0000084F861 +:109010002500E17A4FF0000011F0020F1CBF18F09C +:10902000020F18F0040F19D111F0100F1CBF94F8A3 +:109030003320002A02D094F835207AB111F0080FBD +:109040001CBF94F82420002A08D111F0040F02D08C +:1090500094F8251011B118F0010F01D04FF0010064 +:10906000617A19B168B1FFF7F5FB10E03E484A4953 +:109070000160D5F8000220F00300C5F80002E77295 +:1090800005E001290AD0022918BFFFDF0DD018F032 +:10909000010F14D0DAF80000804745E06672E772ED +:1090A000A7729621227B002006E06672E7720220FA +:1090B000A072227B96210120FFF78FFBE7E718F0D3 +:1090C000020F2AD018F0040F21D1FEF74FF9F0B9A2 +:1090D000FEF75CF9D8B931480168001F0068C0F399 +:1090E000006CC0F3425500F00F03C0F30312C0F34D +:1090F0000320BCF1000F0AD0002B1CBF002A00285F +:1091000005D1002918BF032D38BF48F0040827EA0D +:109110009800DAF80410884706E018F0080F18BF26 +:10912000DAF8080056D08047A07A022818BFBDE8B8 +:10913000F88F207C002808BFBDE8F88F02492FE097 +:10914000741500401C11004000800040488500401C +:1091500014100040ACF501404881004004F5014086 +:1091600004B500404C85004008F501404016004021 +:109170001014004018110040448100404485004014 +:109180001015004000140040141400400415004065 +:10919000100C0020C40000200000040454140040FF +:1091A000C1F8446102281DD0012818BFFFDFE16A21 +:1091B0006069884298BFFFDFD4F81400C9F8000046 +:1091C000A06B4FF4800140F48000A06382480160EE +:1091D000BDE8F88F18F0100F14BFDAF80C00FFDFAD +:1091E000A1D1A1E76169E06A0844E7E738B57B49A6 +:1091F00004460220887201212046FFF763F9784A6D +:1092000004F13D001060774B0020C3F8440176491B +:10921000C1F80001C1F80C01C1F81001C1F8040146 +:10922000C1F81401C1F818017048006800900120CD +:109230009864101D00681168884228BFFFDF38BDA0 +:109240002DE9F843654A88460024917A0125684F44 +:10925000012902D0022903D015E0117E11B912E0D4 +:10926000517E81B1917FD37F05FA01F105FA03F3B5 +:109270001943396092F82010890001F1804101F50D +:10928000C041C1F8104506460220907201213046C7 +:10929000FFF718F9524906F13D000860514AC2F83B +:1092A00044415148C0F80041C0F80C41C0F8104199 +:1092B000C0F80441C0F81441C0F818414B48006898 +:1092C00000909564081D00680968884228BFFFDF88 +:1092D000B8F1000F1CBF4FF400303860BDE8F883D0 +:1092E000022810B50DD0012804BF42F6CE3010BDC3 +:1092F000042817BF082843F6A440FFDF41F66A00A0 +:1093000010BDFDF7E5FF30B9FDF7F9FF002808BFF4 +:1093100041F6583001D041F2643041F29A010844DC +:1093200010BD314910B50020C1F800023049314864 +:109330000860324930480860091D31480860091D3D +:1093400030480860091D30480860091D2F48086032 +:10935000091D2F48086001200BF058FD1E494FF4ED +:109360003810086010BD22494FF43810086070476B +:109370002848016803291BBF00680228012000203B +:109380007047244801680B291BBF00680A28012088 +:109390000020704720490968C9B9204A204913684C +:1093A00070B123F0820343F07D0343F00043136068 +:1093B0000A6822F0100242F0600242F0004205E02A +:1093C00023F0004313600A6822F000420A60034958 +:1093D00081F83D007047000004F50140280C002092 +:1093E00044850040008000400010004018110040FB +:1093F00008F50140000004041011004098F50140F8 +:109400000410004044810040141000401C11004032 +:109410001010004050150040881700403C170040D5 +:109420007C17004010B5404822220021F5F72FF8A4 +:109430003D480024017821F010010170012104F061 +:10944000FFFE3A494FF6FF7081F822408884384980 +:109450000880488010BD704734498A8C824218BF0A +:109460007047002081F822004FF6FF708884704713 +:109470002D49016070472E49088070472B498A8C1E +:10948000A2F57F43FF3B03D00021016008467047EF +:1094900091F822202549012A1ABF016001200020ED +:1094A0007047224901F1220091F82220012A04BFCD +:1094B00000207047012202701D48008888841046F1 +:1094C00070471B49488070471849194B8A8C5B8844 +:1094D0009A4206D191F82220002A1EBF0160012085 +:1094E0007047002070471148114A818C5288914280 +:1094F00009D14FF6FF71818410F8221F19B10021A4 +:10950000017001207047002070470848084A818C8C +:109510005288914205D190F8220000281CBF0020FB +:109520007047012070470000960C0020700C00204E +:10953000CC0000207047584A012340B1012818BFD1 +:1095400070471370086890608888908170475370E6 +:109550000868C2F802008888D08070474E4A10B16F +:10956000012807D00EE0507860B1D2F80200086000 +:10957000D08804E0107828B19068086090898880CD +:109580000120704700207047434910B1012803D0E3 +:1095900006E0487810B903E0087808B10120704768 +:1095A0000020704730B58DB00C4605460D220021D5 +:1095B00004A8F4F76CFFE0788DF81F0020798DF88F +:1095C0001E0060798DF81D00286800906868019081 +:1095D000A8680290E868039068460AF087FF207840 +:1095E0009DF82F1088420CD160789DF82E1088428B +:1095F00007D1A0789DF82D10884202BF01200DB040 +:1096000030BD00200DB030BD30B50C4605468DB0E4 +:109610004FF0030104F1030012B1FDF749FF01E02F +:10962000FDF765FF60790D2220F0C00040F040009A +:109630006071002104A8F4F72AFFE0788DF81F007C +:1096400020798DF81E0060798DF81D002868009043 +:1096500068680190A8680290E868039068460AF07C +:1096600045FF9DF82F0020709DF82E0060709DF83A +:109670002D00A0700DB030BD10B5002904464FF08C +:10968000060102D0FDF714FF01E0FDF730FF60791D +:1096900020F0C000607110BDD0000020FE4840687E +:1096A00070472DE9F0410F46064601461446012059 +:1096B00005F06FF8054696F86500FEF795FC4AF24E +:1096C000B12108444FF47A71B0FBF1F0718840F297 +:1096D00071225143C0EB4100001BA0F55A7402F007 +:1096E0005AFF002818BF1E3CAF4234BF28463846F8 +:1096F000A04203D2AF422CBF3C462C467462BDE868 +:10970000F0812DE9FF4F8BB0044690F86500884644 +:109710000390DDE90D1008430A90E0480027057822 +:109720000C2D28BFFFDFDE4E36F8159094F88851D7 +:109730000C2D28BFFFDFDA4830F81500484480B20E +:10974000009094F87D000D280CBF012000200790A8 +:109750000D98002804BF94F82C0103282BD10798FA +:1097600048B3B4F8AA01404525D1D4F83401C4F86F +:109770002001608840F2E2414843C4F82401B4F873 +:109780007A01B4F806110844C4F82801204602F012 +:109790000CFFB4F8AE01E08294F8AC016075B4F847 +:1097A000B0016080B4F8B201A080B4F8B401E080E8 +:1097B000022084F82C01D4F884010990D4F88001A7 +:1097C0000690B4F80661B4F87801D4F874110191E8 +:1097D0000D9921B194F8401151B100F0D6B804F5BB +:1097E000807104917431059104F5B075091D07E08D +:1097F00004F5AA710491091D059104F5A275091DCE +:109800000891B4F87010A8EB0000A8EB01010FFA62 +:1098100080F90FFA81FBB9F1000F05DAD4F8700175 +:1098200001900120D9460A909C484FF0000A007927 +:10983000A8B3F2F77FFB90B3B4F8180102282ED337 +:1098400094F82C0102282AD094F8430138BB94F8EC +:10985000880100900C2828BFFFDF9148009930F85C +:10986000110000F5C86080B2009094F82C01012826 +:109870007ED0608840F2E2414843009901F0E6F86A +:10988000D4F8342180B206EB0B01A1EB0901821A56 +:1098900001FB02AAC4F83401012084F8430194F8C2 +:1098A0002C01002865D0012800F01482022800F065 +:1098B0007181032818BFFFDF00F04782A7EB0A0180 +:1098C0000198FCF738F90599012640F271220860E9 +:1098D0000898A0F80080002028702E710598006874 +:1098E000A8606188D4F834015143C0EB41006B4952 +:1098F000A0F54E70C8618969814287BF04990860EC +:10990000049801600498616A0068084400F5D47006 +:10991000E86002F040FE10B1E8681E30E8606E7149 +:10992000B4F8F000A0EB080000B20028C4BF032088 +:109930006871079800280E9800F06982E0B100BFB6 +:10994000B4F8181100290CBF0020B4F81A01A4F8CB +:109950001A0194F81C21401C504388420CD26879AB +:10996000401E002808DD6E71B4F81A01401C01E0A9 +:109970000FE05AE0A4F81A010D98002800F06A825E +:1099800094F84001002800F061820FB00220BDE889 +:10999000F08F94F8800003283DD03F4894F865107C +:1099A00090F82C00F7F78DFDE18A40F271225143C7 +:1099B00000EB4100CDF80800D4F82401009901F033 +:1099C00045F8D4F82021D4F82811821A01FB02AA04 +:1099D000C4F820010099029801F038F8D4F8301149 +:1099E000C4F83001411A8A44608840F2E241484399 +:1099F000009901F02BF806EB0B01D4F82821A1EB1C +:109A00000901891AD4F83421C4F83401821A491E94 +:109A100001FB02AA40E7E18A40F27122D4F8240156 +:109A2000514300EB41000290C6E70698002808BFAA +:109A3000FFDF94F86510184890F82C00F7F741FD07 +:109A40000990E08A40F271214143099800EB4100FE +:109A5000009900F0FBFFC4F83001608840F2E24159 +:109A60004843009900F0F2FFC4F8340103A902A8AA +:109A7000FFF7BBF8DDE90160039FE9F7F0F8014665 +:109A80003046FDF769F800F10F06E9F711F9381AC9 +:109A9000801B009006E00000B80C0020E0000020D1 +:109AA000E4620200B4F83401214686B20120D4F801 +:109AB000289004F06EFE074694F86500FEF794FACD +:109AC0004AF2B12108444FF47A7BB0FBFBF0618885 +:109AD00040F271225143C0EB4100801BA0F55A7641 +:109AE00002F059FD002818BF1E3EB94534BF384664 +:109AF0004846B04203D2B9452CBF4E463E46666248 +:109B000094F86500FEF7E9FA00F2E140B0FBFBF1E2 +:109B100006980F1894F86500FEF7DFFA064694F8E9 +:109B20006500FEF761FA30444AF2AB310844B0FBFD +:109B3000FBF1E08A40F2712242430998D4F8306187 +:109B400000EB4200401A801B384400993138471A14 +:109B5000607D40F2E24110FB01F994F8650000904D +:109B600010F00C0F0ABF00984EF62830FEF73CFAB2 +:109B70004AF2B1210844B0FBFBF000EB460000EBD9 +:109B800009060098FEF7B8FA304400F18401FE4857 +:109B9000816193E6E18A40F27122D4F824015143B5 +:109BA00000EB4100009900F051FFC4F830016188DA +:109BB00040F2E2404843009900F048FFC4F8340105 +:109BC00087B221460120D4F828B004F0E2FD814696 +:109BD00094F86500FEF708FA4AF2B12101444FF407 +:109BE0007A70B1FBF0F0618840F271225143C0EB12 +:109BF0004100C01BA0F55A7702F0CDFC002818BF29 +:109C00001E3FCB4534BF48465846B84203D2CB45E9 +:109C10002CBF5F464F4667621EBB0E9808B394F890 +:109C200065603046FEF7E0F94AF2B12101444FF495 +:109C30007A70B1FBF0F0D4F83011E28A084440F2B7 +:109C40007123D4F824115A4301EB42010F1A304614 +:109C5000FEF752FA01460998401A3844A0F120074D +:109C60000AE0E18A40F27122D4F82401514300EB6A +:109C70004100D4F83011471AD4F82821D4F8201123 +:109C8000D4F8300101FB0209607D40F2E24110FB93 +:109C900001FB94F8656016F00C0F0ABF30464EF6D3 +:109CA0002830FEF7A1F94AF2B12101444FF47A704D +:109CB000B1FBF0F000EB490000EB0B093046FEF77A +:109CC0001BFA484400F16001AF488161012084F82B +:109CD0002C01F3E5618840F271225143D4F834013C +:109CE000D4F82821C0EB410101FB09F706EB0B0179 +:109CF000891AD4F820C1D4F83031491E0CFB023245 +:109D000001FB0029607D40F2E24110FB01FB94F869 +:109D1000656016F00C0F0ABF30464EF62830FEF78D +:109D200063F94AF2B12101444FF47A70B1FBF0F0CB +:109D300000EB490000EB0B093046FEF7DDF9484423 +:109D400000F1600190488161B8E5618840F27122BC +:109D5000D4F834015143C0EB410000FB09F794F8FB +:109D60007C0024281CBF94F87D0024280BD1B4F873 +:109D7000AA01A8EB000000B2002804DB94F8AD01B2 +:109D8000002818BF03900A9800B3FEB9099800286C +:109D90001ABF06980028FFDF94F8650010F00C0F3A +:109DA00014BF4EF62830FEF71FF94AF2B1210144E4 +:109DB0004FF47A70B1FBF0F03F1A94F86500FEF7AB +:109DC0009BF90999081A3844A0F12007D4F83411F6 +:109DD00006EB0B0000FB01F6039810F00C0F0ABF16 +:109DE00003984EF62830FEF7FFF84AF2B1210144FD +:109DF0004FF47A70B1FBF0F000EB46060398FEF7E3 +:109E00007BF9304400F160015F48816156E500282C +:109E10007FF496AD94F82C0100283FF4ADAD618835 +:109E200040F27122D4F834015143C0EB410128467D +:109E3000F7F712F90004000C3FF49EAD18990029C1 +:109E400018BF088001200FB0BDE8F08F94F87C01A6 +:109E5000FCF7D1FC94F87C012946FCF7B0FB20B15B +:109E60000D9880F0010084F841010FB00020BDE89A +:109E7000F08F2DE9F843454C0246434F00266168B8 +:109E8000606A052A60D2DFE802F003464B4F5600B5 +:109E9000A07A002560B101216846FDF709FB9DF815 +:109EA000000042F210710002B0FBF1F201FB12055A +:109EB000F4F720FE4119A069FBF73DFEA06126746E +:109EC000032060754FF0010884F81480607AD0B9DF +:109ED000A06A80B1F4F70EFE0544F4F785FC411941 +:109EE000A06A884224BF401BA06204D2C4F8288024 +:109EF000F5F72BFE07E0207B04F11001FCF75FFB78 +:109F0000002808BFFFDF2684FCF739F87879BDE820 +:109F1000F843E8F7F9BFBDE8F843002100F0B3BD0E +:109F2000C1F88001BDE8F883D1F88001BDE8F843AD +:109F3000012100F0A8BD84F83060FCF720F87879A2 +:109F4000BDE8F843E8F7E0BFFFDFBDE8F8832DE99F +:109F5000F04F0E4C824683B020788B4601270025B7 +:109F6000094E4FF00209032804BF207B50457DD1E4 +:109F7000606870612078032818BFFFDF4FF0030886 +:109F8000BBF1080F73D203E0E0000020B80C002002 +:109F9000DFE80BF0040F32322D9999926562F5F7E4 +:109FA000ABF9002818BFFFDF86F8028003B0BDE8D8 +:109FB000F08FF4F77AFF68B9F4F716FC0546E0690C +:109FC000A84228BFE56105D2281A0421FCF7F7FD55 +:109FD000E56138B1F5F723FD002818BFFFDF03B0B6 +:109FE000BDE8F08F03B00020BDE8F04F41E703B0BB +:109FF000BDE8F04FFEF7FFBD2775257494F83000DB +:10A000004FF0010A58B14FF47A71A069FBF793FD44 +:10A01000A061002104F11000F7F71EF80EE0F4F73C +:10A0200069FD82465146A069FBF785FDA061514656 +:10A0300004F11000F7F710F800F1010A208C411C20 +:10A040000A293CBF50442084606830B1208C401CF9 +:10A050000A2828BF84F8159001D284F81580607A08 +:10A06000A8B9A06AE8B1F4F745FD01E02FE02AE0C5 +:10A070008046F4F7B9FB00EB0801A06A884224BFD0 +:10A08000A0EB0800A0620CD2A762F5F75EFD207B72 +:10A09000FCF74BF82570707903B0BDE8F04FE8F796 +:10A0A00033BF207B04F11001FCF789FA002808BFB8 +:10A0B000FFDF03B0BDE8F08F207BFCF736F825709A +:10A0C00003B0BDE8F08FFFDF03B0BDE8F08FBAF159 +:10A0D000200F28BFFFDFDFF8E886072138F81A00D5 +:10A0E000F9F7E4FE040008BFFFDFBAF1200F28BF34 +:10A0F000FFDF38F81A002188884218BFFFDF4FF0D1 +:10A10000200A7461BBF1080F80F06181DFE80BF079 +:10A110000496A0A099FEFDFCC4F88051F580C4F817 +:10A12000845194F8410138B9FCF71EF8D4F84C1169 +:10A13000FCF712FD00281BDCB4F83E11B4F87000E7 +:10A14000814206D1B4F8F410081AA4F8F6002046AB +:10A1500005E0081AA4F8F600B4F83E112046A4F869 +:10A160007010D4F86811C4F84C11C0F870111DE0DB +:10A17000B4F83C11B4F87000081AA4F8F600B4F86A +:10A180003C112046A4F87010D4F84C11C4F86811A2 +:10A19000C4F87011D4F85411C4F80011D4F858114F +:10A1A000C4F87411B4F85C11A4F8781102F008F93D +:10A1B000FBF7B4FF804694F86500FDF715FF4AF2FF +:10A1C000B12108444FF47A71B0FBF1F0D4F83411A6 +:10A1D00040F27122084461885143C0EB4100A0F174 +:10A1E000300AB8F1B70F98BF4FF0B70821460120E9 +:10A1F00004F0CFFA4044AAEB0000A0F21D38A246BA +:10A200002146012004F0C5FADAF824109C3081427E +:10A2100088BF0D1AC6F80C80454528BF4546B56075 +:10A22000D4F86C01A0F5D4703061FCF762FC84F8BE +:10A23000407186F8029003B0BDE8F08F02F0A6F9F5 +:10A2400001E0FEF7D8FC84F8407103B0BDE8F08F60 +:10A25000FBF78AFFD4F8702101461046FCF77CFC1E +:10A2600048B1628840F27123D4F834115A43C1EBEB +:10A270004201B0FBF1F094F87D100D290FD0B4F835 +:10A280007010B4F83E210B189A42AEBF501C401C0F +:10A290000844A4F83E0194F8420178B905E0B4F806 +:10A2A0003E01401CA4F83E0108E0B4F83E01B4F8B9 +:10A2B000F410884204BF401CA4F83E01B4F87A01AF +:10A2C0000DF1040B401CA4F87A01B4F89A00B4F81C +:10A2D0009810401AB4F87010401E08441FFA80F914 +:10A2E0000BE000231A462046CDF800B0FFF709FA2C +:10A2F00068B3012818BFFFDF48D0B4F83E11A9EBBE +:10A30000010000B2002802E053E047E05FE0E8DA35 +:10A31000082084F88D0084F88C70204601F012FE2D +:10A3200084F82C5194F87C514FF6FF77202D00D300 +:10A33000FFDF28F8157094F87C01FBF7F6FE84F82F +:10A340007CA1707903B0BDE8F04FE8F7DDBDA06EE9 +:10A35000002804BF03B0BDE8F08FB4F83E01B4F8A4 +:10A360009420801A01B20029DCBF03B0BDE8F08F51 +:10A37000B4F86C000144491E91FBF0F189B201FB75 +:10A380000020A4F8940003B0BDE8F08FB4F83E01BB +:10A39000BDF804100844A4F83E01AEE7FEF7E4FA65 +:10A3A000FEF729FC4FF0E020C0F8809203B0BDE832 +:10A3B000F08F94F82C01042818BFFFDF84F82C518B +:10A3C00094F87C514FF6FF77202DB2D3B0E7FFDF32 +:10A3D00003B0BDE8F08F10B5FA4C207850B10120E1 +:10A3E0006072F5F7D8FB2078032805D0207A002882 +:10A3F00008BF10BD0C2010BD207BFCF7FCF9207BB2 +:10A40000FCF765FC207BFBF790FE002808BFFFDF10 +:10A410000020207010BD2DE9F04FEA4F83B038784E +:10A4200001244FF0000840B17C720120F5F7B3FB26 +:10A430003878032818BF387A0DD0DFF88C9389F864 +:10A44000034069460720F9F7BAFC002818BFFFDF70 +:10A450004FF6FF7440E0387BFCF7CDF9387BFCF712 +:10A4600036FC387BFBF761FE002808BFFFDF87F86A +:10A470000080E2E7029800281CBF90F82C11002908 +:10A480002AD00088A0421CBFDFF834A34FF0200B75 +:10A490003AD00721F9F70AFD040008BFFFDF94F85E +:10A4A0007C01FCF714FC84F82C8194F87C514FF665 +:10A4B000FF76202D28BFFFDF2AF8156094F87C0175 +:10A4C000FBF733FE84F87CB169460720F9F777FC87 +:10A4D000002818BFFFDF12E06846F9F74EFC00289D +:10A4E000C8D011E0029800281CBF90F82C11002958 +:10A4F00005D00088A0F57F41FF39CAD104E0684645 +:10A50000F9F73BFC0028EDD089F8038087F830800C +:10A5100087F80B8003B00020BDE8F08FAA4948718E +:10A520000020887001220A7048700A71C870A5491D +:10A53000087070E7A449087070472DE9F84FA14CE6 +:10A5400006460F462078002862D1A048FBF792FD0E +:10A55000207320285CD04FF00308666084F80080E8 +:10A56000002565722572AEB1012106F58E70FCF7EB +:10A57000BEFF0620F9F742FC81460720F9F73EFCB2 +:10A5800096F81C114844B1FBF0F200FB1210401C7D +:10A5900086F81C01FBF7C2FD40F2F651884238BF35 +:10A5A00040F2F65000F5A0701FFA80F9F4F7A2FA15 +:10A5B000012680B3A672F4F717F9E061FBF7D4FD2A +:10A5C000824601216846FCF769FF9DF8000042F2CF +:10A5D00010710002B0FBF1F201FB120000EB090167 +:10A5E0005046FBF7A8FAA762A061267584F815808B +:10A5F0002574207B04F11001FBF7E1FF002808BF60 +:10A60000FFDF25840020F5F7C6FA0020BDE8F88FAB +:10A610000C20BDE8F88FFFE7E761FBF7A5FD494691 +:10A62000FBF789FAA061A57284F830600120FDF77C +:10A6300054FD4FF47A7100F2E140B0FBF1F0381AAA +:10A64000A0F5AB60A5626063CFE75F4948707047D3 +:10A650005D49087170475B4810B5417A00291CBFFD +:10A66000002010BD816A51B990F8301039B1416AAB +:10A67000406B814203D9F5F768FA002010BD012034 +:10A6800010BD2DE9F041504C0646E088401CE080AA +:10A69000D4E902516078D6F8807120B13A46284654 +:10A6A000F6F705FD0546A068854205D02169281A00 +:10A6B00008442061FCF71DFAA560AF4209D896F85E +:10A6C0002C01012805D0E078002804BF0120BDE856 +:10A6D000F0810020BDE8F08110B504460846FDF782 +:10A6E00083FC4AF2B12108444FF47A71B0FBF1F0D7 +:10A6F00040F2E241614300F54E7081428CBF081A7E +:10A70000002010BD70B5044682B0002084F84001DE +:10A7100094F8FB00002807BF94F82C01032802B02E +:10A7200070BDFBF721FDD4F8702101461046FCF7FF +:10A7300013FA0028DCBF02B070BD628840F27123BA +:10A74000D4F834115A43C1EB4201B0FBF1F0B4F834 +:10A750007010401C0844A4F83C01B4F8F400B4F8AC +:10A760003C21801A00B20028DCBF02B070BD01207D +:10A7700084F84201B4F89A00B4F8982001AE801A27 +:10A78000401E084485B212E00096B4F83C11002344 +:10A7900001222046FEF7B5FF002804BF02B070BDBD +:10A7A000012815D0022812BFFFDF02B070BDB4F837 +:10A7B0003C01281A00B20028BCBF02B070BDE3E71C +:10A7C000F00C0020B80C0020E00000204F9F01009A +:10A7D000B4F83C01BDF804100844A4F83C01E6E7D5 +:10A7E000F8B50422002506295BD2DFE801F0072630 +:10A7F0000319192A044680F82C2107E00446C948A9 +:10A80000C078002818BF84F82C210AD0FBF7B7FBCA +:10A81000A4F87A51B4F87000A4F83E0184F84251CB +:10A82000F8BD0095B4F8F410012300222046FEF78D +:10A8300068FF002818BFFFDFE8E7032180F82C112C +:10A84000F8BD0646876AB0F83401314685B201206A +:10A8500003F09FFF044696F86500FDF7C5FB4AF23A +:10A86000B12108444FF47A71B0FBF1F0718840F2E5 +:10A8700071225143C0EB4100401BA0F55A7501F015 +:10A880008AFE002818BF1E3DA74234BF2046384626 +:10A89000A84228BF2C4602D2A74228BF3C46746279 +:10A8A000F8BDFFDFF8BD2DE9F05F9E4EB1780229BB +:10A8B00006BFF1880029BDE8F09F7469C4F88401DF +:10A8C00094F86500FDF718FCD4F88411081AB168F3 +:10A8D0000144B160F1680844F060746994F8430180 +:10A8E000002808BFBDE8F09F94F82C01032818BF8A +:10A8F000BDE8F09F94F8655037780C2F28BFFFDF34 +:10A90000894E36F8178094F888710C2F28BFFFDF26 +:10A9100036F81700404494F8888187B2B8F10C0FDC +:10A9200028BFFFDF36F8180000F5C8601FFA80F86E +:10A930002846FDF7E1FBD4F884114FF0000A0E1A07 +:10A9400015F00C0F0ABF28464EF62830FDF74CFBD9 +:10A950004FF47A7900F2E730B0FBF9F0361A284666 +:10A96000FDF7CAFBD4F8001115F00C0FA1EB000B9A +:10A970000ABF28464EF62830FDF736FB4AF2B121D1 +:10A980000844B0FBF9F0ABEB0000A0F160017943A3 +:10A99000B1FBF8F1292202EB50006031A0EB51022B +:10A9A00000EB5100B24201D8B04201D8F1F774FB7C +:10A9B000608840F2E2414843394600F047F8C4F865 +:10A9C000340184F843A1BDE8F09F70B505465548B1 +:10A9D00090F802C0BCF1020F07BF406900F5C074D7 +:10A9E000524800F12404002904BF256070BD4FF4D3 +:10A9F0007A7601290DD002291CBFFFDF70BD1046F9 +:10AA0000FEF76EFC00F2E140B0FBF6F0281A206081 +:10AA100070BD1846FDF761FB00F2E140B0FBF6F0B7 +:10AA2000281A206070BD4148007800281CBF002013 +:10AA3000704710B50720F9F7D3F980F0010010BD79 +:10AA40003A480078002818BF0120704730B5024608 +:10AA50000020002908BF30BDA2FB0110490A41EACD +:10AA6000C051400A4C1C40F100000022D4F1FF31DB +:10AA700040F2A17572EB000038BFFFDF04F5F4600F +:10AA8000B0FBF5F030BD2DE9F843284C0025814698 +:10AA900084F83050D4F8188084F82C10E5722570B2 +:10AAA0000127277239466068F5F792FD6168C1F8A1 +:10AAB0007081267B81F87C61C1F88091C1F8748136 +:10AAC000B1F80080202E28BFFFDF194820F816803B +:10AAD000646884F82C510023A4F878511A4619466A +:10AAE00020460095FEF70DFE002818BFFFDFC4F8D2 +:10AAF0002851C4F8205184F82C71A4F83E51A4F8D0 +:10AB00003C5184F84251B4F87000401EA4F8700023 +:10AB1000A4F87A51FBF733FA02484079BDE8F843CC +:10AB2000E8F7F2B9E0000020E4620200B80C00206F +:10AB3000F00C0020012804D0022805D0032808D1F9 +:10AB400005E0012907D004E0022904D001E004292E +:10AB500001D000207047012070472DE9F0410E46DA +:10AB6000044603F08AFC0546204603F08AFC0446AE +:10AB7000F6F770FBFB4F010015D0386990F86420A0 +:10AB80008A4210D090F8C0311BB190F8C2312342F4 +:10AB90001FD02EB990F85D30234201D18A4218D8D7 +:10ABA00090F8C001A8B12846F6F754FB70B1396996 +:10ABB00091F86520824209D091F8C00118B191F84E +:10ABC000C301284205D091F8C00110B10120BDE8B1 +:10ABD000F0810020FBE730B5E24C85B0E069002849 +:10ABE0005FD0142200216846F3F751FC206990F8E9 +:10ABF0006500FDF7F9F94FF47A7100F5FA70B0FBD2 +:10AC0000F1F5206990F86500FDF776FA2844ADF873 +:10AC1000060020690188ADF80010B0F87010ADF89A +:10AC200004104188ADF8021090F8A20130B1A0697B +:10AC3000C11C039103F002FB8DF81000206990F80D +:10AC4000A1018DF80800E169684688472069002164 +:10AC500080F8A21180F8A1110399002921D090F861 +:10AC6000A01100291DD190F87C10272919D09DF83A +:10AC70001010039A002914D013780124FF2B12D04E +:10AC8000072B0ED102290CD15178FF2909D100BF21 +:10AC900080F8A0410399C0F8A4119DF8101080F825 +:10ACA000A31105B030BD1B29F2D9FAE770B5AD4C40 +:10ACB000206990F87D001B2800D0FFDF2069002567 +:10ACC00080F8A75090F8D40100B1FFDF206990F818 +:10ACD000A81041B180F8A8500188A0F8D81180F8D8 +:10ACE000D6510E2108E00188A0F8D81180F8D6517D +:10ACF000012180F8DA110D2180F8D4110088F9F7CC +:10AD000006FAF8F79FFE2079E8F7FEF8206980F848 +:10AD10007D5070BD70B5934CA07980072CD5A0787C +:10AD2000002829D162692046D37801690D2B01F1F1 +:10AD300070005FD00DDCA3F102034FF001050B2B77 +:10AD400019D2DFE803F01A1844506127182C183A7A +:10AD50006400152B6FD008DC112B4BD0122B5AD06E +:10AD6000132B62D0142B06D166E0162B71D0172B53 +:10AD700070D0FF2B6FD0FFDF70BD91F87F200123D3 +:10AD80001946F6F7FFF80028F6D12169082081F866 +:10AD90007F0070BD1079BDE8704001F090BC91F863 +:10ADA0007E00C00700D1FFDF01F048FC206910F8E9 +:10ADB0007E1F21F00101017070BD91F87D00102807 +:10ADC00000D0FFDF2069112180F8A75008E091F83A +:10ADD0007D00142800D0FFDF2069152180F8A750DE +:10ADE00080F87D1070BD91F87D00152800D0FFDF40 +:10ADF000172005E091F87D00152800D0FFDF19200D +:10AE0000216981F87D0070BDBDE870404EE7BDE866 +:10AE1000704001F028BC91F87C2001230021F6F756 +:10AE2000B1F800B9FFDF0E200FE011F87E0F20F01F +:10AE3000040008701DE00FE091F87C200123002140 +:10AE4000F6F7A0F800B9FFDF1C20216981F87C002B +:10AE500070BD12E01BE022E091F87E00C0F301100B +:10AE6000012800D0FFDF206910F87E1F21F01001BB +:10AE70000170BDE8704001F0E1BB91F87C20012336 +:10AE80000021F6F77FF800B9FFDF1F20DDE791F81A +:10AE90007D00212801D000B1FFDF2220B0E7BDE80E +:10AEA000704001F0D7BB2F48016991F87E2013074D +:10AEB00002D501218170704742F0080281F87E209E +:10AEC0008069C07881F8E10001F0AFBB10B5254C76 +:10AED00021690A88A1F8162281F8140291F8640009 +:10AEE00001F091FB216981F8180291F8650001F0E9 +:10AEF0008AFB216981F81902012081F812020020E1 +:10AF000081F8C0012079BDE81040E7F7FDBF10B51A +:10AF1000144C05212069FFF763FC206990F85A1052 +:10AF2000012908D000F5F57103F001FC2079BDE896 +:10AF30001040E7F7E9BF022180F85A1010BD10B5A4 +:10AF4000084C01230921206990F87C207030F6F725 +:10AF500019F848B12169002001F8960F087301F82B +:10AF60001A0C10BD000100200120A070F9E770B597 +:10AF7000F74D012329462869896990F87C200979D1 +:10AF80000E2A01D1122903D000241C2A03D004E088 +:10AF9000BDE87040D3E7142902D0202A07D008E08A +:10AFA00080F87C4080F8A240BDE87040AFE71629E9 +:10AFB00006D0262A01D1162902D0172909D00CE083 +:10AFC00000F87C4F80F82640407821280CD01A20C9 +:10AFD00017E090F87D20222A07D0EA69002A03D0E2 +:10AFE000FF2901D180F8A23132E780F87D4001F0DD +:10AFF00025FB286980F8974090F8C0010028F3D01D +:10B000000020BDE8704061E710B5D14C216991F88E +:10B010007C10202902D0262902D0A2E7FFF756FF94 +:10B020002169002081F87C0081F8A20099E72DE9D0 +:10B03000F843C74C206990F87C10202908D00027DD +:10B0400090F87D10222905D07FB300F17C0503E044 +:10B050000127F5E700F17D0510F8B01F41F004016C +:10B060000170A06903F015FA4FF00108002608B33B +:10B070003946A069FFF771FDE0B16A46A169206910 +:10B08000F6F7F7F890B3A06903F001FA2169A1F887 +:10B09000AA01B1F8701001F0AAFA40B32069282182 +:10B0A00080F88D1080F88C8058E0FFE70220A070B7 +:10B0B000BDE8F883206990F8C00110B11E20FFF7A9 +:10B0C00005FFAFB1A0692169C07881F8E20008FAF4 +:10B0D00000F1C1F3006000B9FFDF20690A2180F8A8 +:10B0E0007C1090F8A20040B9FFDF06E009E02AE0FA +:10B0F0002E7001F0A3FAFFF7D6FE206980F8976062 +:10B10000D6E7226992F8C00170B1B2F8703092F8B7 +:10B110006410B2F8C40102F5D572F6F79BF968B174 +:10B120002169252081F87C00206900F17D0180F8EB +:10B1300097608D4212D180F87D600FE00020FFF70C +:10B14000C5FE2E70F0E720699DF8001080F8AC1164 +:10B150009DF8011080F8AD1124202870206900F1BD +:10B160007D018D4203D1BDE8F84301F067BA80F854 +:10B17000A2609DE770B5764C01230B21206990F801 +:10B180007D207030F5F7FEFE202650BB206901239C +:10B19000002190F87D207030F5F7F4FE0125F0B124 +:10B1A000206990F87C0024281BD0A06903F04FF997 +:10B1B000C8B1206990F8B01041F0040180F8B010D7 +:10B1C000A1694A7902F0070280F85D20097901F04F +:10B1D000070180F85C1090F8C1311BBB06E0A57038 +:10B1E00036E6A67034E6BDE870405CE690F8C03103 +:10B1F000C3B900F164035E788E4205D1197891429B +:10B2000002D180F897500DE000F503710D700288AF +:10B210004A8090F85C200A7190F85D0048712079AE +:10B22000E7F772FE2169212081F87D00BDE87040BA +:10B2300001F0FBB9F8B5464C206990F87E0010F09B +:10B24000300F04D0A07840F00100A070F8BDA069D4 +:10B2500003F0E2F850B3A06903F0D8F80746A069FC +:10B2600003F0D8F80646A06903F0CEF80546A069B9 +:10B2700003F0CEF801460097206933462A46303065 +:10B2800003F0BFF9A079800703D56069C07814285E +:10B290000FD0216991F87C001C280AD091F85A003F +:10B2A00001280ED091F8B70158B907E0BDE8F84081 +:10B2B000F9E52169012081F85A0002E091F8B60110 +:10B2C00030B1206910F87E1F41F0100101700EE0CE +:10B2D00091F87E0001F5FC7240F0200081F87E00BC +:10B2E00031F8300B03F017FA2079E7F70DFEBDE8CF +:10B2F000F84001F09AB970B5154C206990F87E10AD +:10B30000890707D590F87C20012308217030F5F7D4 +:10B3100039FEF8B1206990F8AA00800712D4A0691C +:10B3200003F056F8216981F8AB00A06930F8052FC9 +:10B33000A1F8AC204088A1F8AE0011F8AA0F40F0A7 +:10B3400002000870206990F8AA10C90705D011E022 +:10B35000000100200120A0707AE590F87E008007AF +:10B3600000D5FFDF206910F87E1F41F00201017057 +:10B3700001F05BF92069002590F87C10062906D1C0 +:10B3800080F87C5080F8A2502079E7F7BDFD206955 +:10B3900090F8A8110429DFD180F8A8512079E7F7A7 +:10B3A000B3FD206990F87C100029D5D180F8A25017 +:10B3B0004EE570B5FB4C01230021206990F87D20FB +:10B3C0007030F5F7DFFD012578B9206990F87D2010 +:10B3D000122A0AD0012305217030F5F7D3FD10B1F0 +:10B3E0000820A07034E5A57032E5206990F8A80027 +:10B3F00008B901F01AF92169A06901F5847102F018 +:10B40000C8FF2169A069D83102F0CEFF206990F809 +:10B41000DC0100B1FFDF21690888A1F8DE0101F538 +:10B42000F071A06902F0A3FF2169A06901F5F47130 +:10B4300002F0A5FF206980F8DC51142180F87D100E +:10B440002079BDE87040E7F75FBD70B5D54C0123AA +:10B450000021206990F87D207030F5F793FD0125DB +:10B46000A8B1A06902F04FFF98B1A0692169B0F8B6 +:10B470000D00A1F8AA01B1F8701001F0B8F858B1A8 +:10B480002069282180F88D1080F88C50E0E4A570A8 +:10B49000DEE4BDE8704006E5A0692169027981F823 +:10B4A000AC21B0F80520A1F8AE2102F01FFF216900 +:10B4B000A1F8B001A06902F01CFF2169A1F8B20156 +:10B4C000A06902F01DFF2169A1F8B4010D2081F8E7 +:10B4D0007D00BDE47CB5B34CA079C00738D0A0692D +:10B4E00001230521C578206990F87D207030F5F79B +:10B4F00049FD68B1AD1E0A2D06D2DFE805F0090945 +:10B500000505090905050909A07840F00800A070A3 +:10B51000A07800281CD1A06902F0BEFE00286ED0E1 +:10B52000A0690226C5781DB1012D01D0162D18D1B4 +:10B53000206990F87C00F5F70DFD90B1216991F834 +:10B540007C001F280DD0202803D0162D16D0A67001 +:10B550007CBD262081F87C00162D02D02A20FFF722 +:10B56000B5FC0C2D5BD00CDC0C2D48D2DFE805F0CF +:10B5700036331F48BEBE4BB55ABE393C2020A070A2 +:10B580007CBD0120142D6ED008DC0D2D6CD0112D4A +:10B590006BD0122D6ED0132D31D168E0152D7FD0D8 +:10B5A000162D6FD0182D6ED0FF2D28D198E0206970 +:10B5B0000123194690F87F207030F5F7E3FC00284E +:10B5C00008D1A06902F0CCFE216981F88E01072024 +:10B5D00081F87F008CE001F0EDF889E0FFF735FF9E +:10B5E00086E001F0C7F883E0206990F87D1011290A +:10B5F00001D0A6707CE0122180F87D1078E075E023 +:10B60000FFF7D7FE74E0206990F87D001728F0D18D +:10B6100001F014F821691B2081F87D0068E0FFF734 +:10B620006AFE65E0206990F87E00C00703D0A0782C +:10B6300040F0010023E06946A06902F0D0FE9DF8C9 +:10B64000000000F02501206900F8B01F9DF80110EE +:10B6500001F04901417000F0E8FF206910F87E1FF9 +:10B6600041F0010117E018E023E025E002E0FFF7D8 +:10B6700066FC3DE0216991F87E10490704D5A07071 +:10B6800036E00DE00FE011E000F0CFFF206910F888 +:10B690007E1F41F0040101702AE0FFF7CBFD27E097 +:10B6A00001F030F824E0FFF765FD21E0FFF7BFFC73 +:10B6B0001EE0A06900790DE0206910F8B01F41F08C +:10B6C00004010170A06902F0F7FE162810D1A069EC +:10B6D00002F0F6FEFFF798FC0AE0FFF748FC07E0EF +:10B6E000E16919B1216981F8A20101E0FFF7DBFBF3 +:10B6F0002169F1E93002401C42F10002C1E9000277 +:10B700007CBD70B5274CA07900074AD5A0780028E9 +:10B7100047D1206990F8E400FE2800D1FFDF2069BE +:10B72000FE21002580F8E41090F87D10192906D13B +:10B7300080F8A75000F082FF206980F87D502069D2 +:10B7400090F87C101F2902D0272921D119E090F808 +:10B750007D00F5F7FFFB78B120692621012380F8F1 +:10B760007C1090F87D200B217030F5F70BFC78B938 +:10B770002A20FFF7ABFB0BE02169202081F87C0039 +:10B7800006E0012180F8A11180F87C5080F8A250D9 +:10B79000206990F87F10082903D10221217080F8D8 +:10B7A000E41021E40001002010B5FD4C216991F85E +:10B7B000AC210AB991F8642081F8642091F8AD2198 +:10B7C0000AB991F8652081F8652010B10020FFF7D3 +:10B7D0007DFB206902F041FF002806D02069BDE80A +:10B7E000104000F5F57102F0A2BF16E470B5EC4C04 +:10B7F00006460D46206990F8E400FE2800D0FFDFE1 +:10B800002269002082F8E46015B1A2F8A400E7E400 +:10B8100022F89E0F01201071E2E470B5E04C012384 +:10B820000021206990F87C207030F5F7ABFB0028F0 +:10B830007BD0206990F8B61111B190F8B71139B1E9 +:10B8400090F8C01100296FD090F8C11119B36BE0C6 +:10B8500090F87D1024291CD090F87C10242918D051 +:10B860005FF0000300F5D67200F5DB7102F096FE82 +:10B870002169002081F8B60101461420FFF7B6FFC8 +:10B88000216901F13000C28A21F8E62F408B4880FF +:10B8900050E00123E6E790F87D2001230B21703072 +:10B8A000F5F770FB68BB206990F8640000F0ABFE10 +:10B8B0000646206990F8650000F0A5FE054620695F +:10B8C00090F8C2113046FFF735F9D8B1206990F8E9 +:10B8D000C3112846FFF72EF9A0B12269B2F87030E3 +:10B8E00092F86410B2F8C40102F5D572F5F7B2FD12 +:10B8F00020B12169252081F87C001BE00020FFF7A2 +:10B90000E5FA11E020690123032190F87D207030D1 +:10B91000F5F738FB40B920690123022190F87D201A +:10B920007030F5F72FFB08B1002059E400211620F4 +:10B93000FFF75CFF012053E410B5E8BB984C206989 +:10B9400090F87E10CA0702D00121092052E08A0730 +:10B950000AD501210C20FFF749FF206910F8AA1F22 +:10B9600041F00101017047E04A0702D5012113208F +:10B9700040E00A0705D510F8E11F417101210720B9 +:10B9800038E011F0300F3BD090F8B711A1B990F822 +:10B99000B611E1B190F87D1024292FD090F87C10D9 +:10B9A00024292BD05FF0000300F5D67200F5DB717F +:10B9B00002F0F4FD216900E022E011F87E0F20F092 +:10B9C000200040F010000870002081F83801206944 +:10B9D00090F87E10C90613D502F03FFEFFF797FAE4 +:10B9E000216901F13000C28A21F8E62F408B48809E +:10B9F00001211520FFF7FAFE0120F6E60123D3E727 +:10BA00000020F2E670B5664C206990F8E410FE293B +:10BA100078D1A178002975D190F87F2001231946AB +:10BA20007030F5F7AFFA00286CD1206990F88C11CE +:10BA300049B10021A0F89C1090F88D1180F8E61013 +:10BA4000002102205BE090F87D200123042170306A +:10BA5000F5F798FA0546FFF76FFF002852D1284600 +:10BA600000F00CFF00284DD120690123002190F83F +:10BA70007C207030F5F786FA78B120690123042123 +:10BA800090F87D207030F5F77DFA30B9206990F894 +:10BA9000960010B10021122031E0206990F87C203E +:10BAA0000A2A0DD0002D2DD1012300217030F5F789 +:10BAB00069FA78B1206990F8A81104290AD105E043 +:10BAC00010F8E21F01710021072018E090F8AA0089 +:10BAD000800718D0FFF7A1FE002813D120690123A9 +:10BAE000002190F87C207030F5F74CFA002809D03E +:10BAF000206990F8A001002804D00021FF20BDE8B3 +:10BB0000704073E609E000210C20FFF76FFE20690A +:10BB100010F8AA1F41F0010101701DE43EB5054671 +:10BB20006846FDF7ABFC00B9FFDF22220021009838 +:10BB3000F2F7ADFC0321009802F096FB0098017823 +:10BB400021F010010170294602F0B3FB144C0D2DB9 +:10BB500043D00BDCA5F102050B2D19D2DFE805F06F +:10BB600022184B191922185718192700152D5FD0C4 +:10BB700008DC112D28D0122D0BD0132D09D0142D37 +:10BB800006D155E0162D2CD0172D6AD0FF2D74D07C +:10BB9000FFDFFDF786FC002800D1FFDF3EBD00007F +:10BBA000000100202169009891F8E61017E0E26892 +:10BBB00000981178017191884171090A8171518849 +:10BBC000C171090A0172E4E70321009802F072FCD6 +:10BBD0000621009802F072FCDBE700980621017153 +:10BBE000D7E70098D4F8101091F8C221027191F8AB +:10BBF000C3114171CDE72169009801F5887102F008 +:10BC0000D7FB21690098DC3102F0DCFBC1E7FA497F +:10BC1000D1E90001CDE90101206901A990F8B00046 +:10BC200000F025008DF80400009802F006FCB0E753 +:10BC30002069B0F84810009802F0D6FB2069B0F8EF +:10BC4000E810009802F0D4FB2069B0F84410009886 +:10BC500002F0D2FB2069B0F8E610009802F0D0FBA9 +:10BC600097E7216991F8C00100280098BCD111F82C +:10BC7000642F02714978BCE7FFE7206990F8A3219F +:10BC8000D0F8A411009802F022FB82E7DB4810B53F +:10BC9000006990F8821041B990F87D2001230621B7 +:10BCA0007030F5F76FF9002800D001209DE570B5E0 +:10BCB000D24D286990F8801039B1012905D00229A8 +:10BCC00006D0032904D0FFDF03E4B0F8F41037E016 +:10BCD00090F87F10082936D0B0F89810B0F89A2064 +:10BCE00000248B1C9A4206D3511A891E0C04240C82 +:10BCF00001D0641EA4B290F8961039B190F87C205F +:10BD0000012309217030F5F73DF940B3FFF7BEFF7D +:10BD100078B129690020B1F89020B1F88E108B1C01 +:10BD20009A4203D3501A801E00D0401EA04200D277 +:10BD300084B20CB1641EA4B22869B0F8F410214496 +:10BD4000A0F8F0102DE5B0F898100329BDD330F815 +:10BD5000701F428D1144491CA0F8801021E5002479 +:10BD6000EAE770B50C4605464FF4087200212046FC +:10BD7000F2F78DFB258014E5F8F7A2B92DE9F04123 +:10BD80000D4607460721F8F791F8041E3CD094F8B9 +:10BD9000C8010026A8B16E70092028700BE0268427 +:10BDA00084F8C861D4F8CA016860D4F8CE01A860EC +:10BDB000B4F8D201A88194F8C8010028EFD12E71FF +:10BDC000AEE094F8D40190B394F8D4010D2813D0C8 +:10BDD0000E2801D0FFDFA3E02088F8F798F9074686 +:10BDE000F7F745FE78B96E700E20287094F8D601EA +:10BDF00028712088E88014E02088F8F788F9074641 +:10BE0000F7F735FE10B10020BDE8F0816E700D200F +:10BE1000287094F8D60128712088E88094F8DA0117 +:10BE2000287284F8D4613846F7F71BFE78E0FFE704 +:10BE300094F80A0230B16E701020287084F80A62FB +:10BE4000AF806DE094F8DC0190B16E700A2028702C +:10BE50002088A880D4F8E011C5F80610D4F8E411C1 +:10BE6000C5F80A10B4F8E801E88184F8DC6157E00D +:10BE700094F8040270B16E701A20287005E000BFBB +:10BE800084F80462D4F80602686094F8040200287A +:10BE9000F6D145E094F8EA0188B16E70152028705B +:10BEA00008E000BF84F8EA6104F5F6702B1D07C8AE +:10BEB00083E8070094F8EA010028F3D130E094F811 +:10BEC000F80170B16E701C20287084F8F861D4F805 +:10BED000FA016860D4F8FE01A860B4F80202A881F3 +:10BEE0001EE094F80C0238B11D20287084F80C6212 +:10BEF000D4F80E02686013E094F81202002883D090 +:10BF00006E701620287007E084F81262D4F81402CC +:10BF10006860B4F81802288194F812020028F3D15E +:10BF2000012071E735480021C16101620846704770 +:10BF300030B5324D0C46E860FFF7F4FF00B1FFDF8B +:10BF40002C7130BD002180F87C1080F87D1080F8C5 +:10BF5000801090F8FB1009B1022100E00321FEF7E8 +:10BF60003FBC2DE9F041254C0546206909B100216F +:10BF700004E0B0F80611B0F8F6201144A0F806115C +:10BF800090F88C1139B990F87F2001231946703050 +:10BF9000F4F7F8FF30B1206930F89C1FB0F85A2050 +:10BFA00011440180206990F8A23033B1B0F89E109E +:10BFB000B0F8F6201144A0F89E1090F9A670002F5A +:10BFC00006DDB0F8A410B0F8F6201144A0F8A410D3 +:10BFD00001213D2615B180F88D6017E02278022AF4 +:10BFE0000ED0012A15D0A2784AB380F88C1012F036 +:10BFF000140F11D01E2117E0FC6202000001002086 +:10C0000090F8E620062A3CD016223AE080F88C1000 +:10C0100044E090F88E2134E0110702D580F88D605D +:10C020003CE0910603D5232180F88D1036E090077F +:10C0300000D1FFDF21692A2081F88D002AE02BB191 +:10C04000B0F89E20B0F8A0309A4210D2002F05DD43 +:10C05000B0F8A420B0F8A0309A4208D2B0F89C30D2 +:10C06000B0F89A20934204D390F88C310BB122227D +:10C0700007E090F880303BB1B0F89830934209D394 +:10C08000082280F88D20C1E7B0F89820062A01D355 +:10C090003E22F6E7206990F88C1019B12069BDE8BE +:10C0A000F0414FE7BDE8F0410021FEF799BB2DE9D3 +:10C0B000F047FF4C81460D4620690088F8F739F8B3 +:10C0C000060000D1FFDFA0782843A070A0794FF0D0 +:10C0D00000058006206904D5A0F8985080F8045126 +:10C0E00003E030F8981F491C0180FFF7CFFD4FF0A7 +:10C0F000010830B3E088000506D5206990F8821069 +:10C1000011B1A0F88E501CE02069B0F88E10491CC7 +:10C1100089B2A0F88E10B0F890208A4201D3531A49 +:10C1200000E0002327897F1DBB4201D880F896805C +:10C13000914206D3A0F88E5080F80A822079E6F763 +:10C14000E3FEA0794FF0020710F0600F0ED02069D7 +:10C1500090F8801011B1032908D102E080F88080A6 +:10C1600001E080F880700121FEF73AFB206990F829 +:10C170008010012904D1E188C90501D580F88070BB +:10C18000B9F1000F72D1E188890502D5A0F81851E4 +:10C1900004E0B0F81811491CA0F8181100F035FBA4 +:10C1A000FEF719FDFFF72EFC2769B7F8F800401CD1 +:10C1B000A7F8F80097F8FC0028B100F01BFFA8B121 +:10C1C000A7F8F85012E000F012FF08B1A7F8F850F5 +:10C1D00000F015FF50B197F80401401CC0B287F879 +:10C1E0000401022802D927F8F85F3D732069012372 +:10C1F000002190F87D207030F4F7C4FE20B920694A +:10C2000090F87D000C2859D120690123002190F875 +:10C210007C207030F4F7B6FE48B32069012300217A +:10C2200090F87F207030F4F7ADFE00B3206990F8ED +:10C230008010022942D190F80401C0B93046F7F7C6 +:10C24000E6F9A0B1216991F8E400FE2836D1B1F8F1 +:10C25000F200012832D981F8FA80B1F89A00B1F8D9 +:10C260009820831E9A4203DB012004E032E025E09F +:10C27000801A401E80B2B1F8F82023899A4201D377 +:10C28000012202E09A1A521C92B2904200D9104642 +:10C29000012801D181F8FA5091F86F2092B98A6E85 +:10C2A00082B1B1F89420B1F87010511A09B2002986 +:10C2B00008DD884200DB084680B203E021690120E6 +:10C2C00081F8FA502169B1F870201044A1F8F40007 +:10C2D000FFF7EDFCE088C0F340214846FFF741FE40 +:10C2E000206980F8FB50BDE8F047FDF7FCB87049C5 +:10C2F00002468878CB78184312D10846006942B1CB +:10C300008979090703D590F87F00082808D0012013 +:10C310007047B0F84C10028E914201D8FEF7B1B9C7 +:10C320000020704770B5624C05460E46E0882843F1 +:10C33000E080A80703D5E80700D0FFDF6661EA07C1 +:10C340004FF000014FF001001AD0A661F278062AE2 +:10C3500002D00B2A14D10AE0226992F87D30172B03 +:10C360000ED10023E2E92E3302F8370C08E02269EF +:10C3700092F87D30112B03D182F8811082F8A80049 +:10C38000AA0718D56269D278052A02D00B2A12D1E1 +:10C390000AE0216991F87D20152A0CD10022E1E9FB +:10C3A000302201F83E0C06E0206990F87D20102A2A +:10C3B00001D180F88210280601D50820E07083E4BE +:10C3C0002DE9F84301273A4C002567F30701E58082 +:10C3D000A570E570257020618946804680F8FB7065 +:10C3E0000088F7F7A6FE00B9FFDF20690088FDF797 +:10C3F00042F820690088FDF764F82069B0F8F2106F +:10C4000071B190F8E410FE290FD190F88C1189B128 +:10C4100090F87F20012319467030F4F7B3FD78B10E +:10C42000206990F8E400FE2804D0206990F8E40028 +:10C43000FFF774FB206990F8FD1089B1258118E0A1 +:10C440002069A0F89C5090F88D1180F8E61000212A +:10C450000220FFF7CBF9206980F8FA500220E7E7C5 +:10C4600090F8C81119B9018C8288914200D881884E +:10C47000218130F8F61F491E8EB230F8021F314478 +:10C4800020F86019018831440180FFF7FFFB20B1DB +:10C49000206930F88E1F314401802069B0F8F21015 +:10C4A000012902D8491CA0F8F2102EB102E00000C8 +:10C4B0000001002080F8045180F8FA5090F87D10B7 +:10C4C0000B2901D00C2916D1B0F87020B0F8AA3190 +:10C4D000D21A12B2002A0EDBD0F8AC11816090F8AB +:10C4E000B01101730321F4F773F8206980F87D50CF +:10C4F00080F8B27026E0242910D1B0F87010B0F89E +:10C50000AA21891A09B2002908DB90F8C001FFF7B7 +:10C510004BF9206900F87D5F857613E090F87C1078 +:10C52000242901D025290DD1B0F87010B0F8AA0146 +:10C53000081A00B2002805DB0120FFF735F9206951 +:10C5400080F87C5020690146B0F8F6207030F4F78E +:10C55000B2FAFC480090FC4BFC4A4146484600F0C9 +:10C560007DFC216A11B16078FCF7B5FA20690123DE +:10C57000052190F87D207030F4F704FD002803D0E9 +:10C58000BDE8F84300F0FDB9BDE8F88300F015BD43 +:10C59000EF49C8617047EE48C069002800D001200B +:10C5A0007047EB4A50701162704710B5044600881E +:10C5B000A4F8CC01B4F8B001A4F8CE01B4F8B201EB +:10C5C000A4F8D001B4F8B401A4F8D201012084F891 +:10C5D000C801DF480079E6F797FC02212046F3F70F +:10C5E000F7FF002004F87D0F0320E07010BD401A13 +:10C5F00000B247F6FE71884201DC002801DC012010 +:10C6000070470020704710B5012808D0022808D0D4 +:10C61000042808D0082806D0FFDF204610BD0124DA +:10C62000FBE70224F9E70324F7E7C9480021006982 +:10C6300020F8A41F8178491C81707047C44800B558 +:10C64000016911F8A60F401E40B20870002800DAF8 +:10C65000FFDF00BDBE482721006980F87C10002163 +:10C6600080F8A011704710B5B94C206990F8A81156 +:10C67000042916D190F87C20012300217030F4F7B2 +:10C6800081FC00B9FFDF206990F8AA10890703D464 +:10C69000062180F87C1004E0002180F8A21080F8C8 +:10C6A000A811206990F87E00800707D5FFF7C6FF24 +:10C6B000206910F87E1F21F00201017010BDA4490D +:10C6C00010B5096991F87C200A2A09D191F8E22075 +:10C6D000824205D1002081F87C0081F8A20010BDC3 +:10C6E00091F87E20130706D522F0080081F87E001D +:10C6F000BDE81040A2E7FF2801D0FFDF10BDBDE874 +:10C700001040A7E7F8B5924C01230A21206990F860 +:10C710007C207030F4F736FC38B3A06901F07CFE61 +:10C72000C8B1A06901F072FE0746A06901F072FE6F +:10C730000646A06901F068FE0546A06901F068FEA2 +:10C7400001460097206933462A46303001F059FFF0 +:10C75000206901F082FF2169002081F8A20081F8A0 +:10C760007C00BDE8F840FEF7D2BBA07840F00100A5 +:10C77000A070F8BD10B5764C01230021206990F817 +:10C780007D207030F4F7FEFB30B1FFF74EFF2169DA +:10C79000102081F87D0010BD20690123052190F84B +:10C7A0007D207030F4F7EEFB08B1082000E0012096 +:10C7B000A07010BD70B5664C01230021206990F86F +:10C7C0007D207030F4F7DEFB012588B1A06901F00F +:10C7D000C4FD2169A1F8AA01B1F87010FFF707FFA5 +:10C7E00040B12069282180F88D1080F88C50E6E552 +:10C7F000A570E4E52169A06901F5D67101F0A8FDF5 +:10C8000021690B2081F87D00D9E510B5FEF779FF8D +:10C81000FEF760FE4E4CA079400708D5A07830B9ED +:10C82000206990F87F00072801D101202070FEF7D1 +:10C8300071FAA079C00609D5A07838B9206990F8B6 +:10C840007D100B2902D10C2180F87D10E0780007C3 +:10C850000ED520690123052190F87D207030F4F772 +:10C8600091FB30B10820A0702169002081F8D4012B +:10C8700010BDBDE81040002000F0C4BB10B5344C22 +:10C88000216991F87D2048B3102A06D0142A07D0D8 +:10C89000152A1AD01B2A2CD11AE001210B2019E0ED +:10C8A000FAF702FE0C2817D32069082100F58870DA +:10C8B000FAF7FEFD28B120690421DC30FAF7F8FD13 +:10C8C00000B9FFDF0121042004E000F017F803E0C5 +:10C8D00001210620FEF78AFF012010BD212A08D180 +:10C8E00091F8970038B991F8C00110B191F8C101E1 +:10C8F00008B1002010BD01211720EBE770B5144CE2 +:10C900000025206990F88F1101290AD002292ED123 +:10C9100090F8A810F1B1062180F8E610012102205C +:10C9200020E090F8D411002921D100F1C80300F5CE +:10C930008471002200F5C870F4F796FA01210520F1 +:10C9400010E00000AFC00100EFC2010025C30100EC +:10C950000001002090F8B000400701D5112000E050 +:10C960000D200121FEF742FF206980F88F5126E556 +:10C9700030B5FB4C05462078002818BFFFDFE57175 +:10C9800030BDF7490120887170472DE9F14FF54D11 +:10C990002846446804F1700794F86510608F94F895 +:10C9A0008280268F082978D0F4F797FBB8F1000F22 +:10C9B00004BF001D80B2864238BF304600F0FF0839 +:10C9C000DFF89C93E848C9F8240009F134006E6848 +:10C9D000406800F1700A90F882B096F86510358FC3 +:10C9E000708F08295DD0F4F778FB00BFBBF1000F12 +:10C9F00004BF001D80B2854238BF2846C0B29AF8F5 +:10CA00001210002918BF04210844C0B296F865101E +:10CA1000FBF735FCB87C002847D007F15801D24815 +:10CA200091E80E1000F5027585E80E10B96EC0F899 +:10CA30002112F96EC0F8251200F58170FBF7DBFFBB +:10CA4000C848007800280CBF0120002080F00101B8 +:10CA5000C6480176D7E91412C0E90412A0F5837222 +:10CA6000D9F82410FBF7F5F994F86500012808BF00 +:10CA700000220CD0022808BF012208D0042808BFD9 +:10CA8000032204D008281ABFFFDF002202224146F9 +:10CA90000120FBF7F9F90EE0FFE70421F4F71DFB95 +:10CAA00084E70421F4F719FBA0E7D9F82400FBF789 +:10CAB000A2FFFBF715FA009850B994F8650094F8B6 +:10CAC000661010F00C0F08BF00219620FBF7B4FF92 +:10CAD00094F8642001210020FCF76BF894F82C00F6 +:10CAE000012808BFFCF735F8022089F80000FCF7A0 +:10CAF0003FFC002818BFFFDFBDE8F88F2DE9F04F9D +:10CB0000DFF860A28BB050469AF800204068AAF186 +:10CB10001401059190F8751000F1700504464FF06E +:10CB200008080127AAF13406A1B3012900F0068103 +:10CB3000022900F00781032918BFFFDF00F01881E8 +:10CB4000306A0423017821F008010170AA7908EA0B +:10CB5000C202114321F004010170EA7903EA820262 +:10CB6000114321F01001017095F80590F06AF6F775 +:10CB70005EFD8046FCF7C9FCB9F1020F00F00081B0 +:10CB8000B9F1010F00F00081B9F1030F00F000814D +:10CB900000F003B9FFE795F80CC04FF002094FF021 +:10CBA000000BBCF1240F1CBF6B7B242B08D0BCF105 +:10CBB0001F0F18BFBCF1200F2AD0222B4DD077E0D9 +:10CBC00094F864109AB190F8AC01002874D0082948 +:10CBD00018BF042969D0082818BF042865D0012986 +:10CBE00018BF012853D000BF4FF0020164E090F855 +:10CBF0001201002860D0082918BF042955D0082840 +:10CC000018BF042851D0012918BF01283FD0EBE7F5 +:10CC1000222B22D0002A4BD090F8C20194F8641045 +:10CC200010F0040F18BF40460CD0082918BF042983 +:10CC30003BD0082818BF042837D0012918BF012885 +:10CC400025D0D1E710F0010F18BF3846EDD110F014 +:10CC5000020F18BF4846E8D12EE04AB390F8C2212F +:10CC600090F85D0094F8641002EA000010F0040FE0 +:10CC700018BF40460ED0082918BF042915D008282F +:10CC800018BF042811D0012918BF0128ACD14FF0DA +:10CC9000010111E010F0010F18BF3846EBD110F080 +:10CCA000020F18BF4846E6D106E04FF0080103E046 +:10CCB00094F864100429F8D0A08E11F00C0F18BF5E +:10CCC0004FF42960F4F709FA218E814238BF0846F3 +:10CCD000ADF80400A4F84C000598FCF7F5FB60B132 +:10CCE0007289316A42F48062728172694FF48060A5 +:10CCF000904703206871EF7022E709AA01A9F06A42 +:10CD0000F6F7CFFB306210B195F8371021B10598D6 +:10CD1000FCF7AEFB6F7113E79DF8241031B9A0F852 +:10CD200000B080F802B0012101F09EFABDF80410B5 +:10CD3000306A01F0C7FB85F8059001E70598FCF71C +:10CD400097FBFDE6B4F84C00ADF8040009AA01A970 +:10CD5000F06AF6F7A6FB3062002808BFFFDFEFE6B7 +:10CD60002401002058010020300D0020380F002041 +:10CD70000598FCF7A9FB002808BFFFDFE0E600BF2D +:10CD800030EA080009D106E030EA080005D102E0E7 +:10CD9000B8F1000F01D0012100E00021306A0278D3 +:10CDA00042EA01110170697C00291CBF69790129DF +:10CDB0003BD005F15801FD4891E80E1000F50278CE +:10CDC00088E80E10A96EC0F82112E96EC0F825128D +:10CDD00000F58170FBF70FFE9AF8000000280CBFE9 +:10CDE00001210021F2480176D5E91212C0E90412AE +:10CDF000A0F58371326AFBF72CF894F864000128DF +:10CE000008BF00220CD0022808BF012208D0042845 +:10CE100008BF032204D008281ABFFFDF0022022225 +:10CE2000FB210020FBF730F803E0FBF7E4FDFBF704 +:10CE300057F8012194F865200846FBF7BAFE3771D0 +:10CE4000306A0188F181807830743770FCF799FA84 +:10CE5000002818BFFFDF0BB0BDE8F08F2DE9F043CD +:10CE6000D44D87B081462878DDF838801E461746B5 +:10CE70000C4628B9002F1CBF002EB8F1000F00D1BE +:10CE8000FFDFC5F81C80C5E90D94C5E905764FF0B4 +:10CE90000000A8716871E870A8702871C64E68819A +:10CEA000A881307804F170072088F7F742F9E8622A +:10CEB0002088F7F72CF92863FBF705FA94F9670047 +:10CEC000FBF7DAFA04F11200FBF76CFD04F10E0037 +:10CED000FBF7D8FA307800280CBF03200120FBF7BD +:10CEE00087FDB64890E80E108DE80E10D0E90410CA +:10CEF000CDE90410307800280CBFB148B148049047 +:10CF00006846FBF763FDF87EFBF7C4FAFBF76AFDA2 +:10CF100094F86F0078B9A06E68B1B88C39888842EF +:10CF200009D1B4F86C1001220844B88494F86E005A +:10CF3000A16EF8F7D8FE3078002804BFFF2094F8DF +:10CF400064401AD094F8651097F81280258F608F8E +:10CF5000082926D0F4F7C1F8B8F1000F04BF001D6E +:10CF600080B2854238BF2846C0B2B97C002918BFBC +:10CF70000421084494F86540C0B22146FBF77FF9CC +:10CF80003078214688B10120FBF74BFB7068D0F860 +:10CF90000001FBF733FD0120FFF7F7FC07B0BDE808 +:10CFA000F0830421F4F799F8D6E70020FBF739FB6A +:10CFB000FFF7A4FD07B0BDE8F0837F4800B5017816 +:10CFC0003438007819B1022818BFFFDF00BD0128EE +:10CFD00018BFFFDF00BD774810B50078022818BFE2 +:10CFE000FFDFBDE8104000F070BA00F06EBA714883 +:10CFF000007970476F488089C0F3002070476D4802 +:10D00000C07870472DE9F04706006B48694D4068CD +:10D0100000F17004686A90F8019018BF012E03D1E6 +:10D02000296B07F0F1FF6870687800274FF001085E +:10D03000A0B101283CD0022860D003281CBFFFDF2C +:10D04000BDE8F087012E08BFBDE8F087286BF6F732 +:10D05000E3FCE879BDE8F047E5F756BF012E14D0B0 +:10D06000A86A002808BFFFDF2889C21CD5E909107B +:10D07000F1F7E3F9A86A686201224946286BF6F7DE +:10D0800047FB022E08BFBDE8F087D4E91401401C1D +:10D0900041F10001C4E91401E079012801D1E771EF +:10D0A00001E084F80780E879BDE8F047E5F72CBF98 +:10D0B000012E14D0A86A002808BFFFDF2889C21CEF +:10D0C000D5E90910F1F7B9F9A86A68620022494662 +:10D0D000286BF6F71DFB022E08BFBDE8F087D4E9E8 +:10D0E0001410491C40F10000C4E91410E079012833 +:10D0F0000CBFE77184F80780BDE8F087012E06D0E9 +:10D10000286BF6F789FC022E08BFBDE8F087D4E94A +:10D110001410491C40F10000C4E91410E079012802 +:10D12000BFD1BCE72DE9F041234D2846A5F13404D9 +:10D13000406800F170062078012818BFFFDFB07842 +:10D140000127002158B1B1706289042042F0040225 +:10D150006281626990472878002818BF3771216A78 +:10D160000322087832EA000009D1628912F4806F44 +:10D1700005D042F002026281626902209047A169F3 +:10D180000020884760B3607950BB287818B30E48F8 +:10D19000007810F0100F04D10449097811F0100F35 +:10D1A0001ED06189E1B9A16AA9B90FE0300D002054 +:10D1B000380F0020240100205801002004630200E1 +:10D1C000BB220200A7A8010032010020218911B171 +:10D1D00010F0100F04D0BDE8F0410020FFF7D5BBE0 +:10D1E000BDE8F04100F071B92DE9F05FCC4E044686 +:10D1F0003046A6F134054068002700F1700A28780F +:10D20000B846022818BFFFDFA889FF2240F400704B +:10D21000A881706890F864101046FBF730F89AF80F +:10D2200012004FF00109002C00F0F080FAF77DFEAB +:10D23000FAF76BFE90B99AF8120078B1686A4178F3 +:10D2400061B100789AF80710C0F3C000884205D198 +:10D2500085F80290BDE8F05F00F037B9686A417860 +:10D260002981002908BFAF6203D0286BF6F70AFABC +:10D27000A862A88940F02000A881EF70706800F1D2 +:10D28000700B044690F82C0001281BD1FBF757FCCB +:10D2900059462046F3F729FEA0B13078002870687F +:10D2A0000CBF00F59A7000F50170218841809BF851 +:10D2B000081001719BF80910417180F80090E8791D +:10D2C000E5F722FE686A9AF806100078C0F380003D +:10D2D00088423AD0706800F1700490F87500002818 +:10D2E0002FD002284AD06771307800281CBF2079DF +:10D2F000002809D027716A89394642F010026A81F4 +:10D300006A694FF010009047E078A0B1E770FCF731 +:10D31000EAF8002808BFFFDF08206A89002142F0F0 +:10D3200008026A816A699047D4E91210491C40F1E9 +:10D330000000C4E91210A07901280CBFA77184F87D +:10D340000690A88940F48070A881696A9AF807302D +:10D350000878C0F3C0029A424DD1726800F0030011 +:10D3600002F17004012818BF02282DD003281CBF29 +:10D37000687940F0040012D068713CE0E86AF6F782 +:10D38000BCF8002808BFFFDFD4E91210491C40F1A7 +:10D390000000C4E91210E879E5F7B6FDA3E784F8C8 +:10D3A0000290AA89484642F40062AA816A8942F042 +:10D3B00001026A816A699047E079012801D1E77129 +:10D3C00019E084F8079016E04878D8B1A98941F4AB +:10D3D0000061A981A96A71B1FB2884BF687940F016 +:10D3E0001000C9D8A879002808BFC84603D08020FB +:10D3F0006A69002190470120A9698847E0B36879EC +:10D40000A0B13AE0E0790128DBD1D8E7002818BFC5 +:10D41000FAF7C5FDA88940F04000A881E97801200D +:10D42000491CC9B2E97001292DD8E5E7307890B9D7 +:10D430003C48007810F0100F04D13B49097811F0F6 +:10D44000100F1AD06989B9B9A96A21B9298911B10E +:10D4500010F0100F11D0B8F1000F1CBF0120FFF722 +:10D46000D1FDFFF74BFBB8F1000F08BFBDE8F09FFF +:10D470000220BDE8F05FC5E5FFE7B8F1000F1CBF73 +:10D480000020FFF7BFFDBDE8F05F00F01EB870B5EB +:10D490000D4606462248224900784C6850B1FAF7FA +:10D4A000F7FD034694F8642029463046BDE87040F5 +:10D4B000FDF78BBAFAF7ECFD034694F86420294691 +:10D4C0003046BDE8704004F0FCBE154910B54C680C +:10D4D000FBF714FBFBF7F3FAFBF7BCF9FBF768FA71 +:10D4E000FAF7FEFC94F82C00012808BFFBF727FB95 +:10D4F00094F86F0038B9A06E28B1002294F86E003D +:10D500001146F8F7F0FB094C00216269A0899047A9 +:10D51000E2696179A07890470020207010BD00007A +:10D520005801002032010020300D0020240100208D +:10D530002DE9F047FA4F894680463D782C0014D0FB +:10D540000126012D11DB601EC4B207EBC40090F868 +:10D550005311414506D10622494600F5AA70F0F75D +:10D560003FFF28B1761CAE42EDDD1020BDE8F0870C +:10D570002046BDE8F087EA498A78824286BF08449F +:10D5800090F843010020704710B540F2D3120021FB +:10D59000E348F0F77CFF0822FF21E248F0F777FF2D +:10D5A000E1480021417081704FF46171818010BDAC +:10D5B0002DE9F0410E460546FFF7BAFFD84C10287A +:10D5C00016D004EBC00191F85A0110F0010F1CBFF6 +:10D5D0000120BDE8F081607808283CBF012081F877 +:10D5E0005A011CD26078401C60700120BDE8F081B7 +:10D5F0006078082813D222780127501C207004EB91 +:10D60000C2083068C8F85401B088A8F85801102A38 +:10D6100028BFFFDF88F8535188F85A71E2E70020ED +:10D62000BDE8F081C04988707047BF488078704776 +:10D630002DE9F041BA4D00272878401E44B2002C55 +:10D6400030DB00BF05EBC40090F85A0110F0010F69 +:10D6500024D06878E6B2401E687005EBC6083046F4 +:10D6600088F85A7100F0E8FA102817D12878401E7F +:10D67000C0B22870B04211D005EBC001D1F85301FF +:10D68000C8F85301D1F85701C8F85701287800F0BD +:10D69000D3FA10281CBF284480F80361601E44B2EE +:10D6A000002CCFDAA0488770BDE8F0819C498A78C9 +:10D6B000824286BF01EB0010C01C002070472DE99C +:10D6C000F0470127994690463D460026FFF730FF78 +:10D6D000102820D0924C04EBC00191F85A1101F0AF +:10D6E000010600F0A9FA102815D0B9F1000F18BFF3 +:10D6F00089F80000A17881420DD904EB001111F1E5 +:10D70000030F08D0204490F84B5190F83B010128BA +:10D710000CBF0127002748EA060047EA0501084038 +:10D72000BDE8F0872DE9F05F1F4690468946064622 +:10D73000FFF7FEFE7A4C054610282ED000F07CFA4A +:10D7400010281CBF1220BDE8F09FA07808283ED208 +:10D75000A6781022701CA07004EB061909F10300D2 +:10D760004146F3F768FB09F1830010223946F3F7CD +:10D7700062FB10213846F3F74BFB3444102184F848 +:10D7800043014046F3F744FB84F84B0184F803510E +:10D79000002084F83B01BDE8F09FA078082816D24D +:10D7A00025784FF0000A681C207004EBC50BD9F8EF +:10D7B0000000CBF85401B9F80400ABF85801102D63 +:10D7C00028BFFFDF8BF853618BF85AA1C0E7072011 +:10D7D000BDE8F09F2DE9F041514CA078401E45B2C4 +:10D7E000002DB8BFBDE8F081EAB2A078401EC1B2FA +:10D7F000A17054FA85F090F803618A423DD004EBA1 +:10D80000011004EB0213D0F803C0C3F803C0D0F832 +:10D8100007C0C3F807C0D0F80BC0C3F80BC0D0F8DE +:10D820000FC0C3F80FC0D0F883C0C3F883C0D0F8CE +:10D8300087C0C3F887C0D0F88BC0C3F88BC0D0F8BE +:10D840008F00C3F88F006318A01801EB410193F813 +:10D8500003C102EB420204EB410180F803C104EB77 +:10D860004202D1F80BC1C2F80BC1B1F80F11A2F8F6 +:10D870000F1193F83B1180F83B1104EBC60797F8A2 +:10D880005A0110F0010F1CD1304600F0D5F91028D4 +:10D8900017D12078401EC0B22070B04211D004EBE6 +:10D8A000C000D0F85311C7F85311D0F85701C7F88A +:10D8B0005701207800F0C0F910281CBF204480F8E0 +:10D8C0000361681E45B2002D8EDABDE8F08116496D +:10D8D0004870704714484078704738B14AF2B81120 +:10D8E000884203D810498880012070470020704783 +:10D8F0000D488088704710B5FFF71AFE102804D035 +:10D9000000F09AF9102818BF10BD082010BD044976 +:10D910008A78824286BF01EB001083300020704776 +:10D92000600F00206C01002060010020FE4B93F886 +:10D9300002C084459CBF00207047184490F8030142 +:10D9400003EBC00090F853310B70D0F85411116004 +:10D95000B0F85801908001207047F34A114491F8C3 +:10D960000321F2490A700268C1F8062080884881C4 +:10D97000704770B516460C460546FBF7D5F8FAF722 +:10D98000C4F9EA48407868B1E748817851B12A196A +:10D99000002E0CBF8330C01CFAF791F9FAF7D8F9C2 +:10D9A000012070BD002070BD10B5FAF7FFF9002806 +:10D9B00004BFFF2010BDBDE81040FAF71DBAFAF70A +:10D9C000F5B9D9498A7882429CBF00207047084443 +:10D9D00090F8030101EBC00090F85A0100F001003B +:10D9E00070472DE9F047D04D00273E4628780028A3 +:10D9F00086BF4FF01009DFF83883BDE8F087AC78B8 +:10DA000021000CD00122012909DB601EC4B22819B3 +:10DA100090F80331B34203D0521C8A42F5DD4C46E4 +:10DA2000A14286BF05EB0410C01C002005EBC60A0E +:10DA30009AF85A1111F0010F16D050B1102C04D0E1 +:10DA4000291991F83B11012903D01021F3F7E0F9CE +:10DA500050B108F8074038467B1C9AF853210AF564 +:10DA6000AA71DFB2FAF7B5FC701CC6B22878B042D2 +:10DA7000C5D8BDE8F0872DE9F041AB4C002635460E +:10DA8000A07800288CBFAA4FBDE8F0816119C0B210 +:10DA900091F80381A84286BF04EB0510C01C00204A +:10DAA00091F83B11012903D01021F3F7B1F958B1D6 +:10DAB00004EBC800BD5590F8532100F5AA7130461B +:10DAC000731CDEB2FAF785FC681CC5B2A078A842C8 +:10DAD000DCD8BDE8F0810144934810B500EB02109A +:10DAE0000A4601218330FAF7EAF8BDE81040FAF758 +:10DAF0002FB90A468D4910B5497841B18A4B9978BA +:10DB000029B10244D81CFAF7DAF8012010BD002030 +:10DB100010BD854A01EB410102EB41010268C1F8E9 +:10DB20000B218088A1F80F0170472DE9F0417E4D4F +:10DB300007460024A878002898BFBDE8F081C0B24D +:10DB4000A04217D905EB041010F1830612D0102162 +:10DB50003046F3F75DF968B904EB440005EB400883 +:10DB600008F20B113A463046FBF74EFDB8F80F01AC +:10DB7000A8F80F01601CC4B2A878A042DFD8BDE8A5 +:10DB8000F081014610226B48F3F755B96948704798 +:10DB900065498A78824203D90A1892F843210AB16A +:10DBA0000020704700EB400001EB400000F20B103A +:10DBB00070475D498A78824206D9084490F83B0153 +:10DBC000002804BF01207047002070472DE9F04174 +:10DBD0000E460746144606213046F3F719F9524D12 +:10DBE00098B1A97871B105F59D7011F0010F18BFBA +:10DBF00000F8014FA978490804D0447000F8024F9A +:10DC0000491EFAD10120BDE8F08138463146FFF7C0 +:10DC10008FFC10280CD000F00FF8102818BF08282F +:10DC200006D0284480F83B414FF00100BDE8F08168 +:10DC30004FF00000BDE8F0813B4B10B4844698786B +:10DC400001000ED0012201290BDB401EC0B21C18BE +:10DC500094F80341644504BF10BC7047521C8A42CB +:10DC6000F3DD10BC1020704770B52F4C01466218D0 +:10DC7000A078401EC0B2A07092F8035181423CD0FF +:10DC800004EB011304EB001C01EB4101DCF8036021 +:10DC9000C3F80360DCF80760C3F80760DCF80B60CA +:10DCA000C3F80B60DCF80F60C3F80F60DCF883602A +:10DCB000C3F88360DCF88760C3F88760DCF88B60AA +:10DCC000C3F88B60DCF88FC0C3F88FC0231800EB5B +:10DCD000400093F803C104EB400082F803C104EB59 +:10DCE0004101D0F80BC1C1F80BC1B0F80F01A1F888 +:10DCF0000F0193F83B0182F83B0104EBC50696F84F +:10DD00005A0110F0010F18BF70BD2846FFF794FFAD +:10DD1000102818BF70BD2078401EC0B22070A842E5 +:10DD200008BF70BD08E00000600F00206001002007 +:10DD30006C0100203311002004EBC000D0F8531117 +:10DD4000C6F85311D0F85701C6F857012078FFF7ED +:10DD500073FF10281CBF204480F8035170BD0000E1 +:10DD60004078704730B50546007801F00F0220F08A +:10DD70000F0010432870092912D2DFE801F00507CF +:10DD800005070509050B0F0006240BE00C2409E02C +:10DD9000222407E001240020E87003E00E2401E0C3 +:10DDA0000024FFDF6C7030BD007800F00F0070477A +:10DDB0000A68C0F803208988A0F807107047D0F8D7 +:10DDC00003200A60B0F80700888070470A68C0F82E +:10DDD00009208988A0F80D107047D0F809200A6042 +:10DDE000B0F80D00888070470278402322F040028E +:10DDF00003EA81111143017070470078C0F380106D +:10DE000070470278802322F0800203EAC111114397 +:10DE1000017070470078C009704770B514460E460F +:10DE200005461F2A88BFFFDF2246314605F109005B +:10DE3000F0F703FBA01D687070BD70B544780E4606 +:10DE40000546062C38BFFFDFA01F84B21F2C88BFF9 +:10DE50001F24224605F109013046F0F7EEFA20466C +:10DE600070BD70B514460E4605461F2A88BFFFDFF9 +:10DE70002246314605F10900F0F7DFFAA01D68706F +:10DE800070BD0968C0F80F1070470A88A0F8132009 +:10DE900089784175704790F8242001F01F0122F025 +:10DEA0001F02114380F824107047072988BF0721FB +:10DEB00090F82420E02322F0E00203EA411111430C +:10DEC00080F8241070471F3008F065B810B504467C +:10DED00000F000FB002818BF204410BDC17811F0ED +:10DEE0003F0F1BBF027912F0010F0022012211F037 +:10DEF0003F0F1BBF037913F0020F002301231A44C5 +:10DF000002EB4202530011F03F0F1BBF027912F0E7 +:10DF1000080F0022012203EB420311F03F0F1BBF49 +:10DF2000027912F0040F00220122134411F03F0F76 +:10DF30001BBF027912F0200F0022012202EBC20265 +:10DF400003EB420311F03F0F1BBF027912F0100FD9 +:10DF50000022012202EB42021A4411F03F0F1BBFC4 +:10DF6000007910F0400F00200120104410F0FF0055 +:10DF700014BF012100210844C0B2704770B5027877 +:10DF8000417802F00F02082A4DD2DFE802F00408BF +:10DF90000B4C4C4C0F14881F1F280AD943E00C2946 +:10DFA00007D040E0881F1F2803D93CE0881F1F28A6 +:10DFB00039D8012070BD4A1EFE2A34D88446C07864 +:10DFC00000258209032A09D000F03F04601C884222 +:10DFD00004D86046FFF782FFA04201D9284670BDF1 +:10DFE0009CF803004FF0010610F03F0F1EBF1CF11C +:10DFF0000400007810F0100F13D06446042160462E +:10E0000000F068FA002818BF14EB0000E6D0017891 +:10E0100001F03F012529E1D280780221B1EB501FA8 +:10E02000DCD3304670BD002070BD70B5017801258D +:10E0300001F00F01002404290AD007290DD0082976 +:10E040001CBF002070BD40780E2836D0204670BD21 +:10E050004078801F1F2830D9F8E7844640789CF824 +:10E0600003108A09032AF1D001F03F06711C814296 +:10E07000ECD86046FFF732FFB042E7D89CF80300C7 +:10E0800010F03F0F1EBF1CF10400007810F0100FBD +:10E0900013D066460421604600F01CFA002818BF21 +:10E0A00016EB0000D2D0017801F03F012529CDD236 +:10E0B00080780221B1EB501FC8D3284670BD10B440 +:10E0C000017801F00F01032920D0052921D14478DE +:10E0D000B0F81910B0F81BC0B0F81730827D222CB0 +:10E0E00017D1062915D3B1F5486F98BFBCF5FA7F53 +:10E0F0000FD272B1082A98BF8A420AD28B429CBFC3 +:10E10000B0F81D00B0F5486F03D805E040780C2842 +:10E1100002D010BC0020704710BC012070472DE9D0 +:10E12000F0411F4614460D00064608BFFFDF21469A +:10E13000304600F0CFF9040008BFFFDF30193A463F +:10E140002946BDE8F041F0F778B9C07800F03F000B +:10E150007047C02202EA8111C27802F03F021143E7 +:10E16000C1707047C07880097047C9B201F00102E0 +:10E17000C1F340031A4402EB4202C1F3800303EBF4 +:10E180004202C1F3C00302EB4302C1F3001303EBED +:10E1900043031A44C1F3401303EBC30302EB4302EE +:10E1A000C1F380131A4412F0FF0202D0521CD2B203 +:10E1B0000171C37802F03F0103F0C0031943C1703D +:10E1C000511C417070472DE9F0410546C078164654 +:10E1D00000F03F041019401C0F46FF2888BFFFDFE6 +:10E1E000281932463946001DF0F727F9A019401CBE +:10E1F0006870BDE8F081C178407801F03F01401AB5 +:10E20000401E80B2704710B590F803C00B460CF06A +:10E210003F0144780CF03F0CA4EB0C0CACF1010C6A +:10E220001FFA8CF4944288BF14462BB10844011D98 +:10E2300022461846F0F701F9204610BD4078704795 +:10E2400000B5027801F0030322F003021A430270C2 +:10E25000012914BF0229002104D0032916BFFFDFC2 +:10E26000012100BD417000BD00B5027801F003033B +:10E2700022F003021A430270012914BF022900216F +:10E2800004D0032916BFFFDF012100BD417000BD8E +:10E29000007800F003007047417841B1C078192838 +:10E2A00003D2BC4A105C884201D101207047002093 +:10E2B000704730B501240546C17019293CBFB548E7 +:10E2C000445C02D3FF2918BFFFDF6C7030BD70B50E +:10E2D00015460E4604461B2A88BFFFDF65702A4696 +:10E2E0003146E01CBDE87040F0F7A7B8B0F8070071 +:10E2F0007047B0F809007047C172090A017370478E +:10E30000B0F80B00704730B4B0F80720B0F809C07F +:10E31000B0F805300179941F40F67A45AC4298BFB9 +:10E32000BCF5FA7F0ED269B1082998BF914209D293 +:10E3300093429FBFB0F80B00B0F5486F012030BC8E +:10E3400098BF7047002030BC7047001D07F023BE07 +:10E35000021D0846114607F01EBEB0F809007047BE +:10E36000007970470A684260496881607047426876 +:10E370000A60806848607047098881817047808999 +:10E38000088070470A68C0F80E204968C0F812106B +:10E390007047D0F80E200A60D0F81200486070472D +:10E3A0000968C0F816107047D0F81600086070476A +:10E3B0000A68426049688160704742680A60806804 +:10E3C000486070470968C1607047C068086070475E +:10E3D000007970470A684260496881607047426806 +:10E3E0000A608068486070470171090A417170478E +:10E3F0008171090AC17170470172090A417270473F +:10E400008172090AC172704780887047C08870475E +:10E41000008970474089704701891B2924BF4189C1 +:10E42000B1F5A47F07D381881B2921BFC088B0F52F +:10E43000A47F01207047002070470A684260496845 +:10E440008160704742680A6080684860704701795F +:10E4500011F0070F1BBF407910F0070F00200120BB +:10E460007047017911F0070F1BBF407910F0070FBB +:10E470000020012070470171704700797047417199 +:10E480007047407970478171090AC1717047C0882F +:10E4900070470179407901F007023F498A5C012AFF +:10E4A00006D800F00700085C01289CBF01207047D7 +:10E4B00000207047017170470079704741717047C3 +:10E4C0004079704730B50C460546FB2988BFFFDF11 +:10E4D0006C7030BDC378024613F03F0008BF704730 +:10E4E0000520127903F03F0312F0010F37D0002905 +:10E4F00014BF0B20704700BF12F0020F32D0012969 +:10E5000014BF801D704700BF12F0040F2DD00229E8 +:10E5100014BF401C704700BF12F0080F28D0032919 +:10E5200014BF801C704700BF12F0100F23D00429C5 +:10E5300014BFC01C704700BF12F0200F1ED0052969 +:10E540001ABF1230C0B2704712F0400F19D006291E +:10E550001ABF401CC0B27047072918D114E0002927 +:10E56000CAD114E00129CFD111E00229D4D10EE0A3 +:10E570000329D9D10BE00429DED108E00529E3D134 +:10E5800005E00629E8D102E0834288BF70470020F9 +:10E5900070470000246302001C63020030B490F84E +:10E5A00064508C88B1F808C015F00C0F1BD000BF68 +:10E5B000B4F5296F98BF4FF4296490F8655015F0B1 +:10E5C0000C0F17D0BCF5296F98BF4FF4296C4A88FF +:10E5D000C988A0F84420A0F84810A0F84640A0F848 +:10E5E0004AC030BC7047002B1CBF157815F00C0FCB +:10E5F000DED1E2E7002B1CBF527812F00C0FE1D104 +:10E60000E5E7DDF800C08181C2810382A0F812C075 +:10E6100070471B2202838282C281828142800281F2 +:10E62000028042848284828359B14FF429614183FC +:10E63000C18241820182C18041818180C184018582 +:10E6400070474FF4A4714183C18241820182C1802D +:10E6500041818180C18401857047F0B4B0F84820C1 +:10E66000818F468EC58E8A4228BF0A4690F8651073 +:10E670004FF0000311F00C0F18BF4FF4296106D1C1 +:10E68000B0F84AC0B0F840108C4538BF61464286A9 +:10E69000C186048FB0F83AC0944238BF14468C4506 +:10E6A00038BF8C460487A0F83AC0B2420ABFA942DC +:10E6B0004FF0010C4FF0000C058EB0F84410C28FE3 +:10E6C000848E914228BF114690F8642012F00C0FFE +:10E6D00018BF4FF4296206D1B0F84660B0F8422066 +:10E6E000964238BF324690F85A60022E0AD0018610 +:10E6F0008286A9420ABFA2420120002040EA0C0003 +:10E70000F0BC70478D4238BF2946944238BF22463C +:10E7100080F85A30EBE7508088899080C889D08093 +:10E72000088A1081488A508101201070704730B4E7 +:10E7300002884A80B0F830C0A1F804C0838ECB8034 +:10E74000428E0A81C48E4C81B0F85650A54204BF57 +:10E75000B0F85240944208D1B0F858409C4202BFF1 +:10E76000B0F854306345002301D04FF001030B7320 +:10E7700000F13003A0F852201A464B89D3848B88CD +:10E780009384CA88A0F858204FF00100087030BC6C +:10E79000704730B404460A46088E91F864104FF46E +:10E7A000747311F00C0F1CBF03EB801080B21ED0ED +:10E7B000918E814238BF0846118F92F865C01CF0D7 +:10E7C0000C0F1CBF03EB811189B218D0538F8B4201 +:10E7D00038BF194692F866301CF00C0F08BF0023B2 +:10E7E000002C0CBF0122002230BCF2F798BC022999 +:10E7F00007BF80003C30C000703080B2D8E7BCF169 +:10E80000020F07BF89003C31C900703189B2DDE7D2 +:10E810002DE9F041044606F099FCC8B9FE4F78682E +:10E8200090F8221001260025012914D00178012931 +:10E830001BD090F8281001291CBF0020BDE8F081F2 +:10E84000657018212170D0F82A10616080F8285076 +:10E850000120BDE8F081657007212170416A616087 +:10E8600080F822500120BDE8F081657014212170EC +:10E87000811C2022201DEFF7E0FD257279680D70C4 +:10E8800081F82850E54882888284C26B527B80F8E8 +:10E89000262080F82260C86B0088F5F738FCF5F771 +:10E8A000E0F8D5E7DC4840680178002914BF80888B +:10E8B0004FF6FF70704730B5D74C83B00D462078C7 +:10E8C0007F2808BFFFDF94F900307F202070D4F844 +:10E8D00004C09CF85000062808BF002205D09CF810 +:10E8E000500008280CBF022201229CF85400CDE9F8 +:10E8F000000302929CF873309CF880200CF13201E6 +:10E90000284606F08FFC03B0BDE8304006F01FBE7D +:10E910002DE9F04106F05FFC002818BF06F0E4FB8B +:10E92000BD4C606800F1840290F87610895C80F834 +:10E930008010002003F07EF828B3FAF753F86068DF +:10E94000B74990F855000D5C2846F9F7A3FD6068BB +:10E950004FF0000680F8735090F8801011F00C0F03 +:10E960000CBF25200F20F9F76CFC606890F8801030 +:10E970000120F9F711FE606890F84010032918BFD4 +:10E9800002290FD103E0BDE8F04101F02FB990F862 +:10E9900076108430085C012804D101221146002041 +:10E9A000FAF707F9FAF7D5F8606890F88050012D6A +:10E9B00007BF0127032100270521A068FFF799F869 +:10E9C000616881F8520040B1002F18BF402521D066 +:10E9D000F9F787F92846FAF79DF86068806DFAF72D +:10E9E0000DF8606890F85410FF291CBF6D30FEF7D9 +:10E9F000B4FFFF21606880F8531080F8541080F84D +:10EA0000626080F8616080F87D60062180F85010B7 +:10EA1000BDE8F08115F00C0F14BF55255025D7E740 +:10EA200070B57D4C0646606800F150052046806850 +:10EA300041B1D0F80510C5F81D10B0F80900A5F8CF +:10EA4000210003E005F11D01FFF7B9F9A068FFF708 +:10EA5000D4F985F82400A0680021032E018002D09B +:10EA6000052E04D03DE00321FFF77CF939E00521B4 +:10EA7000FFF778F96068C06B00F10E01A068FFF73E +:10EA800000FA6068C06B00F11201A068FFF7FDF9A1 +:10EA9000D4E90110CA6B527D8275CA6BD28AC275E5 +:10EAA000120A0276CA6B52884276120A8276CA6BC2 +:10EAB0009288C276120A0277CA6BD2884277120A0B +:10EAC0008277C96B0831FFF7FEF96068C06B017E81 +:10EAD000A068FFF7E0F9606890F88610A068FFF77B +:10EAE000E4F905F11D01A068FFF770F995F824100D +:10EAF000A068FFF786F9606800F1320590F8316090 +:10EB000090F8511091B190F84010032906D190F877 +:10EB10003910002918BF90F8560001D190F8530021 +:10EB2000FFF736F800281CBF012605462946A068D5 +:10EB3000FFF73EF93146A068BDE87040FFF754B9D1 +:10EB40003549496881F84B00704770B5324D002453 +:10EB50000126A8606968A1F8814081F8834081F8A6 +:10EB6000506091F85020022A1FBF91F850100129DF +:10EB7000FFDF70BD06F0CDFA6868047080F82240AF +:10EB800080F8284090F8520030B1F9F7CDFFF9F73E +:10EB9000BCF8686880F852406868072180F84A40ED +:10EBA00080F8396080F8404080F8554080F84B404C +:10EBB00080F87D4080F8381070BD2DE9F041164C8A +:10EBC000054686B0606890F85000012818BF0228FA +:10EBD00005D003281EBF0C2006B0BDE8F081687A7E +:10EBE000022839D0F9F76FFB0220F9F701FF0D4930 +:10EBF00001F10C0090E80D108DE80D10D1E907012E +:10EC0000CDE904016846F9F7E1FE606890F94B0030 +:10EC1000F9F732FCA06807E07401002044110020DD +:10EC20004363020040630200F9F7E5FEFC48F9F790 +:10EC3000B9FEFC48F9F726FC606890F831103230D4 +:10EC4000F9F7A5FB0F210720F9F7BFFB606890F8E3 +:10EC50003900E0B1FEF70FFF6168287A01F1840204 +:10EC600081F87600287A805C81F880006868886581 +:10EC70002A68CA65687A68B1012824D00525022867 +:10EC800008BF81F850506FD0032878D080E0FEF79D +:10EC9000A8FEE1E7E44B91F83850002291F85500C6 +:10ECA000401CA3FB006C4FEA5C0CACEB8C0C60448A +:10ECB00081F8550025FA00F010F0010F03D1501C27 +:10ECC000C2B2032AEAD3002681F87D6091F8490098 +:10ECD000002804BF91F85100002841D0F7F744FA0A +:10ECE000074660683946406CF7F736FFDFF83C832B +:10ECF000054690FBF8F008FB105041423846F6F705 +:10ED00001AFF6168486495FBF8F08A6F10448867C1 +:10ED1000FEF7EEFD01466068826F914220D847649D +:10ED2000866790F8510000281CBF0120FEF7FDFE09 +:10ED30000121606890F84A20002A1CBF90F8492001 +:10ED4000002A0DD090F8313000F13202012B04D1AD +:10ED5000527902F0C002402A08D03230FAF78CFC17 +:10ED60006168042081F8500012E008E00125FEF7F8 +:10ED70000DFF61682A463231FAF746FCF0E7002AB7 +:10ED800018BFFFDF012000F089FF606880F8505055 +:10ED900006B00020BDE8F08170B5A54D686890F818 +:10EDA000501004292ED005291CBF0C2070BD90F8EE +:10EDB0007D100026002990F883104FEA511124D0CD +:10EDC000002908BF012407D0012908BF022403D06D +:10EDD000022914BF00240824C06D00281CBF002095 +:10EDE00000F05CFF6868806DF9F708FE686890F8CD +:10EDF0004010022943D0032904BF90F86C10012968 +:10EE000041D04DE0FFF784FD52E0002908BF012406 +:10EE100007D0012908BF022403D0022914BF00240F +:10EE20000824C06D00281CBF002000F037FF686870 +:10EE3000806DF9F7E3FD686890F84010022906D06C +:10EE4000032904BF90F86C10012904D010E090F859 +:10EE50006C1002290CD1224614F00C0F04D090F84B +:10EE60004C00012808BF042201210020F9F7A1FE6F +:10EE70006868072180F8804080F8616016E090F8AB +:10EE80006C1002290CD1224614F00C0F04D090F81B +:10EE90004C00012808BF042201210020F9F789FE57 +:10EEA0006868082180F8804080F8616080F8501020 +:10EEB000002070BD5E49002210F0010F496802D0A9 +:10EEC000012281F8842010F0080F03D0114408209B +:10EED00081F88400002070475549496881F848004E +:10EEE000704710B5524C636893F83030022B14BF52 +:10EEF000032B00280BD100291ABF02290120002072 +:10EF00001146FEF7F8FC08281CBF012010BD606800 +:10EF100090F83000002816BF022800200120BDE82C +:10EF20001040FAF731BB4248406890F830000028A2 +:10EF300016BF022800200120FAF726BB3C49496889 +:10EF400081F8300070473A49496881F84A007047B3 +:10EF500070B5374C616891F83000002816BF022860 +:10EF60000020012081F8310001F13201FAF7F6FAB0 +:10EF7000606890F83010022916BF03290121002192 +:10EF800080F8511090F8312000F132034FF0000565 +:10EF9000012A04BF5B7913F0C00F0AD000F13203DD +:10EFA000012A04D15A7902F0C002402A01D000227D +:10EFB00000E0012280F84920002A04BF002970BD2A +:10EFC0008567F7F7D1F86168486491F85100002827 +:10EFD0001CBF0020FEF7A9FD0026606890F84A10CB +:10EFE00000291ABF90F84910002970BD90F831200F +:10EFF00000F13201012A04D1497901F0C001402910 +:10F0000005D02946BDE870403230FAF735BBFEF72F +:10F01000BDFD61683246BDE870403231FAF7F4BA9E +:10F020004063020046630200ABAAAAAA40420F0056 +:10F030007401002070B5FF4D0C4600280CBF012361 +:10F040000023696881F8393081F842004FF00800E8 +:10F0500081F856000CD1002C1ABF022C0120002090 +:10F060001146FEF748FC6968082881F8560001D06F +:10F07000002070BD022C14BF032C1220F8D170BDEB +:10F08000002818BF112070470328EA4A526808BFB9 +:10F09000D16382F840000020704710B5E54C6068ED +:10F0A00090F8401003291CBF002180F8601001D0A7 +:10F0B000002010BD0123C16B1A460020F2F738F87A +:10F0C0006168CA6B526A904294BF0120002081F8A7 +:10F0D0006000EDE7D748416891F84000032804D06C +:10F0E000012818BF022807D004E091F84200012847 +:10F0F00008BF70470020704791F84100012814BFF5 +:10F1000003280120F6D1704770B5F9F7F7FCF9F73D +:10F11000D6FCF9F79FFBF9F74BFCC64C002560685D +:10F1200090F8520030B1F9F7FFFCF8F7EEFD606897 +:10F1300080F8525060680121A0F8815080F8835017 +:10F1400080F8501080F82850002070BDB94810B5E4 +:10F150004068643006F0B1FB002010BDB5480121C5 +:10F16000406890F84020032A03BF80F82A10C26B41 +:10F170001288002218BF80F82A20828580F8281083 +:10F180007047AC49496881F88600704701780023D0 +:10F1900011F0010FA749496809D04278032A08BF36 +:10F1A000CB6381F84020012281F884201346027845 +:10F1B00012F0040F0CD082784FF0000C032A08BF25 +:10F1C000C1F83CC081F840200B44082283F8842019 +:10F1D000C27881F830200279002A16BF022A012362 +:10F1E000002381F8393081F84120427981F83820B4 +:10F1F000807981F848004FF0000070478D484068E2 +:10F200008030704770B58B4C06460D46606890F8AC +:10F210005000032818BFFFDF022E1EBF032EFFDFA2 +:10F2200070BD002D18BF06F0A1F900216068A0F89C +:10F23000811080F88310012180F8501070BD00F01B +:10F24000D5BC2DE9F0477B4C0646894660684FF0F7 +:10F250000108072E90F8397038BF032540D3082ED7 +:10F2600084BF0020BDE8F08790F85010062908BF41 +:10F27000002105D090F8501008290CBF022101216F +:10F2800090F8800005F0AEFF002873D1A068C17827 +:10F2900011F03F0F12D0027912F0010F0ED0616809 +:10F2A0004FF0050591F85220002A18BFB9F1000F60 +:10F2B00016D091F88010012909D011E011F03F0F0C +:10F2C0001ABF007910F0100F002F53D14CE04FF00F +:10F2D00001024FF00501FEF74CFB616881F8520016 +:10F2E000A16808782944C0F3801030B1487900F053 +:10F2F000C000402808BF012000D00020616891F8BC +:10F300005210002918BF002807D0FEF74DFB014618 +:10F31000606880F8531080F86180606890F853103E +:10F32000FF292AD080F854100846FEF74AFB40EA2D +:10F330000705606890F85320FF2A18BF002D10D0F1 +:10F34000072E0ED3A068C17811F03F0F09D00179C4 +:10F3500011F0020F05D00B21FEF7BDFB606880F8AD +:10F3600062802846BDE8F087FEF75FF9002808BFF5 +:10F37000BDE8F0870120BDE8F087A36890F8392048 +:10F3800059191B78C3F3801C00F153036046FEF744 +:10F3900096F90546CDE72DE9F043264C87B0A068E5 +:10F3A000FEF7E0FE7F264FF00108002558B1022746 +:10F3B00001287DD0022800F0EF80F9F74BFA07B062 +:10F3C0000620BDE8F083F9F745FA616891F840003E +:10F3D000032800F01581A068C27812F03F0F05D015 +:10F3E000037913F0100F18BF012700D10027002F59 +:10F3F00014BF0823012312F03F0F00F001810079B0 +:10F4000033EA000240F0FC8010F0020F08D091F8BF +:10F410008000002105F064FE002808BF012000D014 +:10F4200000208DF80C508DF810508DF814504FF0CE +:10F43000FF0801E074010020D0B105AA03A904A8C7 +:10F4400000F07AFC606890F831809DF80C0000288C +:10F4500018BF48F002080BD1A068FEF7DBFC81461C +:10F460000121A068FEF732FD4946F8F79AFF28B35C +:10F47000FFB1012000F0DDFB002852D020787F286A +:10F4800008BFFFDF94F900102670606890F85420E0 +:10F49000CDE90021029590F8733090F8802000F1BA +:10F4A0003201404605F0BEFE606880F86C50A3E073 +:10F4B00038E041460020FFF7FEF9A1E0606890F8CF +:10F4C0004100032818BF02282BD19DF81000002806 +:10F4D00027D09DF80C00002823D1F7B1012000F0BF +:10F4E000A8FB00281DD020787F2808BFFFDF94F9F3 +:10F4F00000102670606890F85420CDE90021029534 +:10F5000090F8733090F8802000F13201FE2005F071 +:10F5100089FE606880F86C506EE0FE210020FFF7E5 +:10F52000CAF96DE0F9F796F9A0681821C27812F0CF +:10F530003F0F65D00279914362D10421FEF7C6FCEA +:10F54000616891F84020032A01BF8078B7EB501F13 +:10F5500091F86000002853D04FF0010000F069FBE3 +:10F56000E8B320787F2808BFFFDF94F900102670E9 +:10F57000606890F85420CDE90021029590F873302E +:10F5800090F8802000F13201FF2005F04BFE60680A +:10F5900080F86C8030E000BFF9F75CF9606890F8A3 +:10F5A000400003282CD0A0681821C27812F03F0F29 +:10F5B00026D0007931EA000022D1012000F039FB89 +:10F5C00068B120787F2808BFFFDF94F9001026700B +:10F5D000606890F85420CDE90021029500E00FE02A +:10F5E00090F8733090F8802000F13201FF2005F090 +:10F5F00019FE606880F86C7007B00320BDE8F083E6 +:10F6000007B00620BDE8F083F0B5FE4C074683B096 +:10F6100060686D460078002818BFFFDF002661682B +:10F620008E70C86B02888A8042884A8382888A8367 +:10F63000C088C88381F8206047B10121A068FEF727 +:10F6400045FC0546A0680078C10907E06946A06846 +:10F65000FEF7B5FBA0680078C0F380116068012751 +:10F6600090F85120002A18BF002904D06A7902F0CE +:10F67000C002402A26D090F84A20002A18BF00294C +:10F6800003D0697911F0C00F1CD000F10E00E3F730 +:10F69000B3FC616891F85400FF2819D001F1080209 +:10F6A000C91DFEF743F9002808BFFFDF6068C17974 +:10F6B00041F00201C171D0F86D104161B0F87110D4 +:10F6C00001830FE02968C0F80E10A9884182E0E7A5 +:10F6D000C86B427ECA71D0F81A208A60C08B8881BC +:10F6E0004E610E8360680770C26B90F84B1082F811 +:10F6F0006710C06B0088F4F70AFDF4F7A3F903B0B4 +:10F70000F0BD2DE9F041BF4C0546002760684FF081 +:10F7100001083E4690F84000012818BF022802D098 +:10F72000032818BFFFDF5DB1A068FEF727FC18B9FA +:10F73000A068FEF77AFC18B100F08FFB074645E0A1 +:10F74000606890F850007F25801F06283ED2DFE8D1 +:10F7500000F003191924352FAA48F9F709FA0028EF +:10F7600008BF2570F9F7EBF9606890F8520030B1E6 +:10F77000F9F7DAF9F8F7C9FA606880F85260F9F732 +:10F7800069F830E09F48F9F7F3F9002808BF2570C1 +:10F79000F9F7D5F905F0EAFEC3E09A48F9F7E8F978 +:10F7A000002808BF2570F9F7CAF9F9F753F81AE0ED +:10F7B0009448F9F7DDF930B9257004E09148F9F77C +:10F7C000D7F90028F8D0F9F7BAF9AAE0102F80F09D +:10F7D0003881DFE807F01E9DA6AAF1F108B3F2F127 +:10F7E000F1F10C832051BDE8F041FFF791B80320FF +:10F7F00002F020F9002870D000210320FFF710F953 +:10F80000012211461046F9F7D4F961680C2081F8FD +:10F810005000BDE8F081606800F15005042002F05E +:10F8200009F900287DD00E202870012002F0FDFC8F +:10F83000A06861680078C0F3401081F8750000216D +:10F840000520FFF7EDF87048A1684FF0200CC26B5F +:10F850000B78527B23F020030CEA42121A430A7001 +:10F86000C16B95F825304A7B1A404A73C06B28213A +:10F8700080F86610BDE8F081062002F0DBF8002871 +:10F880004FD0614D0F2085F85000022002F0CDFCD2 +:10F890006068012190F880200846F9F78AF9A0688D +:10F8A00061680078C0F3401081F8750001210520DF +:10F8B000FFF7B6F8E86B80F80D80A068017821F0BA +:10F8C00020010170F9F75DFD002818BFFFDF282037 +:10F8D000E96B81F86600BDE8F08122E0052002F0C6 +:10F8E000A9F8F0B101210320FFF79AF8F9F749FDD3 +:10F8F000002818BFFFDF6068012190F880200846CB +:10F90000F9F757F961680D2081F85000BDE8F081E2 +:10F910006068A0F8816080F8836080F85080BDE85E +:10F92000F081BDE8F04100F061B96168032081F821 +:10F930005000BDE8F041082002F077BC606890F804 +:10F940008310490908BF012507D0012908BF0225F6 +:10F9500003D0022914BF00250825C06D00281CBF54 +:10F96000002000F09BF96068806DF9F747F8606847 +:10F9700090F84010022906D0032904BF90F86C10BB +:10F98000012904D010E090F86C1002290CD12A460D +:10F9900015F00C0F04D090F84C00012808BF042289 +:10F9A00001210020F9F705F96068072180F88050EF +:10F9B00080F8616041E000E043E0606890F8831007 +:10F9C000490908BF012507D0012908BF022503D036 +:10F9D000022914BF00250825C06D00281CBF002087 +:10F9E00000F05CF96068806DF9F708F8606890F8DD +:10F9F000401002290AD0032904BF90F86C10012995 +:10FA000008D014E0740100204411002090F86C101C +:10FA100002290CD12A4615F00C0F04D090F84C00A6 +:10FA2000012808BF042201210020F9F7C2F860680C +:10FA3000082180F8805080F8616080F85010BDE89F +:10FA4000F081FFDFBDE8F08170B5FE4C606890F892 +:10FA5000503000210C2B38D001220D2B40D00E2B22 +:10FA600055D00F2B1CBFFFDF70BD042002F0DDFB63 +:10FA7000606890F880100E20F8F7E3FB606890F85B +:10FA8000800010F00C0F14BF282100219620F8F7F9 +:10FA9000D3FFF9F75EF86068052190F88050A06800 +:10FAA000FEF727F8616881F8520048B115F00C0F95 +:10FAB0000CBF50255525F8F714F92846F9F72AF810 +:10FAC00061680B2081F8500070BDF9F742F8002101 +:10FAD0009620F8F7B1FF6168092081F8500070BDE9 +:10FAE00090F88010FF20F8F7ACFB606890F8800079 +:10FAF00010F00C0F14BF282100219620F8F79CFF6E +:10FB0000F9F727F861680A2081F8500070BDA0F865 +:10FB1000811080F8831080F850200020FFF774FDDA +:10FB2000BDE87040032002F080BB70B5C54C606832 +:10FB300090F850007F25801F062828BF70BDDFE8A1 +:10FB400000F0171F1D032A11BE48F9F711F800280D +:10FB500008BF2570F8F7F3FFF8F77CFEBDE87040AA +:10FB6000FEF7D6BEB748F9F703F8C8B9257017E015 +:10FB7000B448F8F7FDFF40B9257006E005F0F6FC43 +:10FB8000B048F8F7F5FF0028F6D0F8F7D8FFBDE841 +:10FB9000704000F02BB8AB48F8F7EAFF0028E5D03A +:10FBA000F8F7CDFF60680021643005F037FEBDE84E +:10FBB000704000F01BB870B5A24C06460D460129F6 +:10FBC00008D0606890F880203046BDE87040134649 +:10FBD00002F077BBF8F75CFA61680346304691F8AB +:10FBE00080202946BDE8704002F06BBB70B5F8F785 +:10FBF00085FFF8F764FFF8F72DFEF8F7D9FE914C72 +:10FC00000025606890F8520030B1F8F78DFFF8F7E2 +:10FC10007CF8606880F852506068022180F85010CB +:10FC2000A0F8815080F88350BDE87040002002F0B9 +:10FC3000FCBA70B5834D06460421A868FEF746F964 +:10FC4000044605F0C8FA002808BF70BD207800F00F +:10FC50003F00252814D2F8F761FA217811F0800FBF +:10FC60000CBF1E214FF49671B4F80120C2F30C02B0 +:10FC700012FB01F10A1AB2F5877F28BF814201D237 +:10FC8000002070BD68682188A0F88110A17880F8F4 +:10FC900083103046BDE8704001F0CCBE2DE9F04144 +:10FCA000684C0746606800F1810690F883004009BF +:10FCB00008BF012507D0012808BF022503D002286C +:10FCC00014BF00250825F8F78DFE307800F03F06B8 +:10FCD0003046F8F7DFFB606880F8736090F86C00DE +:10FCE00002280CBF4020FF202946F8F7AAFA27B1C6 +:10FCF00029460120F8F795FC05E060682A46C16DA9 +:10FD00000120F8F7E2FCF8F724FF0521A068FDF7D1 +:10FD1000F0FE6168002881F8520008BFBDE8F0815C +:10FD200015F00C0F0CBF50245524F7F7DAFF2046CE +:10FD3000BDE8F041F8F7EEBE2DE9F74F414C002544 +:10FD4000914660688A4690F8510000280CBF4FF039 +:10FD500001084FF00008A0680178CE090121FEF7E4 +:10FD6000B5F836B1407900F0C000402808BF012640 +:10FD700000D00026606890F85210002961D090F8F9 +:10FD800040104FF0000B032906D190F839100029DC +:10FD900018BF90F856700ED1A068C17811F03F0FCF +:10FDA0001CBF007910F0010F02D105F061F940B3DA +:10FDB000606890F85370FF2F18BF082F21D0384685 +:10FDC000FDF7D9FB002818BF4FF00108002E38D0EE +:10FDD000606890F8620030B1FDF7F1FD054660689B +:10FDE00080F862B02DE03846FDF791FD054601210F +:10FDF000A068FEF76BF801462846F9F7D3FB0546E5 +:10FE00001FE0F6B1606890F86100D0B9A068C178D1 +:10FE100011F03F0F05D0017911F0010F18BF0B2130 +:10FE200000D105210022FDF7A4FD616881F8520090 +:10FE300038B1FDF7B9FDFF2803D06168012581F8CD +:10FE4000530001E0740100208AF800500098067009 +:10FE500089F8008003B0BDE8F08F2DE9F04FFF4C2A +:10FE600087B00025606890F850002E46801F4FF044 +:10FE70007F08062880F0D581DFE800F00308088BB2 +:10FE8000FDDB00F0F8FB054600F0CCB9F348F8F7CD +:10FE90006FFE002808BF84F80080F8F750FEA068C5 +:10FEA000FDF782FF0546072861D1A068FEF75AF9E1 +:10FEB0000146606890F86C208A4258D190F8501042 +:10FEC000062908BF002005D090F8500008280CBF74 +:10FED0000220012005F08AF970B90321A068FDF71E +:10FEE000F5FF002843D001884078C1F30B010009D9 +:10FEF00005F07BFC00283AD000212846FFF7A1F945 +:10FF0000A0B38DF80C608DF808608DF8046062680D +:10FF1000FF2592F8500008280CBF02210121A0689B +:10FF2000C37813F03F0F1CBF007910F0020F12D0FE +:10FF300092F8800005F0D4F868B901AA03A902A8D4 +:10FF4000FFF7FAFE606890F831509DF80C00002829 +:10FF500018BF45F002052B469DF804209DF80810B7 +:10FF60009DF80C0000F0D5F9054603E0FFE705F029 +:10FF7000FDFA0225606890F85200002800F05281D6 +:10FF8000F8F7D2FDF7F7C1FE606880F8526000F024 +:10FF900049B9A068FDF708FF0646A1686068CA78FD +:10FFA00090F86D309A4221D10A7990F86E309A42D9 +:10FFB0001CD14A7990F86F309A4217D18A7990F81B +:10FFC00070309A4212D1CA7990F871309A420DD1AC +:10FFD0000A7A90F872309A4208D1097890F8740041 +:10FFE000C1F38011814208BF012500D00025F8F738 +:10FFF00031FC9A48F8F7BCFD002808BF84F800805F +:020000040002F8 +:10000000F8F79DFD042E11D185B120787F2808BF17 +:10001000FFDF94F9003084F80080606890F8732066 +:1000200090F87D1090F8540005F06EFB062500F066 +:10003000F9B802278948F8F79BFD002808BF84F823 +:100040000080F8F77CFDA068FDF7AEFE0546A068CD +:10005000FEF788F8082D08BF00287CD1A0684FF073 +:100060000301C27812F03F0F75D0007931EA000029 +:1000700071D1606800E095E000F1500890F8390017 +:10008000002814BF98F8066098F803604FF0000944 +:1000900098F8020078B1FDF787FC0546FF280AD0E2 +:1000A0000146A068401DFDF758FCB5420CBF4FF05B +:1000B00001094FF000090021A068FDF707FF0622A3 +:1000C00008F11D01EEF78CF940B9A068FDF795FE27 +:1000D00098F82410884208BF012000D0002059EA77 +:1000E00000095DD0606800F1320590F831A098F801 +:1000F000010038B13046FDF74BFD00281CBF054616 +:100100004FF0010A4FF00008A06801784FEAD11BB8 +:100110000121FDF7DBFEBBF1000F07D0407900F0B5 +:10012000C000402808BF4FF0010B01D04FF0000B7A +:100130000121A068FDF7CAFE06222946EEF750F914 +:1001400030B9A068FDF766FE504508BF012501D013 +:100150004FF0000500E023E03BEA050018BFFF2E4A +:100160000DD03046FDF7D3FB060008D00121A06872 +:10017000FDF7ACFE01463046F9F714FA804645EA31 +:10018000080019EA000F0BD060680121643005F007 +:1001900045FB01273846FFF737FA052002F045F8FE +:1001A0003D463FE002252D48F8F7E2FC002808BF55 +:1001B00084F80080F8F7C3FCA068FDF7F5FD06465B +:1001C000A068FDF7CFFF072E08BF00282AD1A0683E +:1001D0004FF00101C27812F03F0F23D00279914312 +:1001E00020D1616801F150060021FDF76FFE062263 +:1001F00006F11D01EEF7F4F8A0B9A068FDF7FDFDCA +:1002000096F8241088420DD160680121643005F011 +:1002100005FBFF21022000F009F8002818BF032584 +:1002200000E0FFDF07B02846BDE8F08F2DE9F0437E +:100230000A4C0F4601466068002683B090F87D2086 +:10024000002A35D090F8500008280CBF022501255F +:10025000A168C87810F03F0F02E000007401002090 +:10026000FD484FF000084FF07F0990F900001CBFD7 +:10027000097911F0100F22D07F2808BFFFDF94F911 +:10028000001084F80090606890F85420CDE90021B7 +:10029000029590F8733090F8802000F132013846D2 +:1002A00004F0C0FF05F0AAFA10B305F050F92CE0F5 +:1002B000002914BF0221012180F87D10C2E77F28A8 +:1002C00008BFFFDF94F9001084F80090606890F890 +:1002D0005420CDE90021029590F8733090F88020E9 +:1002E00000F13201384604F09DFF05F030F90CE0D2 +:1002F0000220FFF79EFC30B16068012680F86C8018 +:10030000F8F7A8FA01E005F031F903B03046BDE88E +:10031000F0832DE9F047D04C054684B09A46174645 +:100320000E46A068FDF71EFF4FF00109002800F0FF +:10033000CF804FF00208012808D0022800F00E817B +:1003400005F014F904B04046BDE8F087A068092123 +:10035000C27812F03F0F00F059810279914340F0CA +:100360005581616891F84010032906D012F0020F00 +:1003700008BFFF2118D05DB115E00021FDF7A6FDF3 +:1003800061680622C96B1A31EEF72AF848BB1EE0F5 +:10039000FDF740FD05460121A068FDF797FD2946C0 +:1003A000F7F7FFFF18B15146012000F051B960681E +:1003B00090F84100032818BF022840F02781002E42 +:1003C0001CBFFE21012040F0438100F01FB9A0684E +:1003D000FDF713FD6168C96B497E884208BF01269D +:1003E00000D00026A068C17811F03F0F05D0017938 +:1003F00011F0020F01D06DB338E0616891F842202E +:10040000012A01D096B11BE0D6B90021FDF75EFDAF +:1004100061680268C96BC1F81A208088C883A06827 +:10042000FDF7EBFC6168C96B487609E091F8530071 +:1004300091F85610884203D004B04046BDE8F087DA +:100440006068643005F02EFA002840D004B00F2018 +:10045000BDE8F08767B1FDF7DDFC05460121A06826 +:10046000FDF734FD2946F7F79CFF08B1012200E0B3 +:100470000022616891F84200012807D040B92EB9E6 +:1004800091F8533091F856108B4201D1012100E0D0 +:1004900000210A421BD0012808BF002E11D14FF0C5 +:1004A0000001A068FDF712FD61680268C96BC1F820 +:1004B0001A208088C883A068FDF79FFC6168C96B1B +:1004C00048766068643005F0EDF90028BED19DE003 +:1004D00060682F46554690F840104FF002080329F7 +:1004E000AAD0A168CA7812F03F0F1BBF097911F09A +:1004F000020F002201224FF0FF0A90F85010082945 +:100500000CBF0221012192B190F8800004F0E8FDB7 +:1005100068B95FB9A068FDF77DFC07460121A068B6 +:10052000FDF7D4FC3946F7F73CFF48B1AA465146DF +:100530000020FFF77BFE002818BF4FF003087BE781 +:10054000606890F84100032818BF02287FF474AF58 +:10055000002E18BF4FF0FE0AE9D16DE7616891F8EF +:100560004030032B52D0A0684FF0090CC27812F033 +:100570003F0F4BD002793CEA020C47D1022B06D048 +:1005800012F0020F08BFFF2161D0E5B35EE012F068 +:10059000020F4FF07F0801D04DB114E001F164006B +:1005A00005F080F980B320787F2842D013E067B34C +:1005B000FDF730FC05460121A068FDF787FC2946C0 +:1005C000F7F7EFFE08B36068643005F06BF9D8B157 +:1005D00020787F282DD094F9001084F8008060687E +:1005E00090F85420CDE90021CDF8089090F87330B0 +:1005F00090F8802000F13201504604F013FE0D20E7 +:1006000004B0BDE8F08716E000E001E00220F7E763 +:10061000606890F84100032818BF0228F6D1002E28 +:10062000F4D04FF0FE014FF00200FEF744F9022033 +:10063000E6E7FFDFCFE7FDF7EDFB05460121A06808 +:10064000FDF744FC2946F7F7ACFE38B151460220CD +:10065000FEF731F9DAE7000074010020606890F8D5 +:100660004100032818BF0228D0D1002E1CBFFE2154 +:100670000220EDD1CAE72DE9F84F4FF00008F74806 +:10068000F8F776FA7F27F54C002808BF2770F8F7AF +:1006900056FAA068FDF788FB81460121FEF7D1FDDF +:1006A000616891F88020012A14D0042A1CBF082A0E +:1006B000FFDF00F0D781606890F8520038B1F8F79A +:1006C00033FAF7F722FB6168002081F852004046B8 +:1006D000BDE8F88F0125E24EB9F1080F3AD2DFE804 +:1006E00009F03EC00439393914FC0546F8F7B2F870 +:1006F000002D72D0606890F84000012818BF0228D1 +:100700006BD120787F2869D122E018B391F840009E +:10071000022802D0012818D01CE020787F2808BFCA +:10072000FFDF94F90000277000906068FF2190F8C7 +:10073000733090F85420323004F02FFF61680020AD +:100740004FF00C0881F87D00B5E720787F2860D154 +:10075000FFDF5EE0F8F77EF84FF00608ABE74FF0FA +:100760000008002800F0508191F84000022836D09F +:1007700001284BD003289ED1A068CA6BC37892F899 +:100780001AC0634521D1037992F81BC063451CD17F +:10079000437992F81CC0634517D1837992F81DC044 +:1007A000634512D1C37992F81EC063450DD1037A17 +:1007B00092F81FC0634508D1037892F819C0C3F3BB +:1007C0008013634508BF012300D0002391F8421035 +:1007D00001292CD0C3B300F013B93FE019E0207811 +:1007E0007F2808BFFFDF94F9000027700090606841 +:1007F000FF2190F8733090F85420323004F0CDFE91 +:1008000060684FF00C0880F87D5054E720787F280E +:100810009ED094F90000277000906068FF2190F846 +:10082000733090F85420323004F0B7FE16E0002BFD +:100830007ED102F11A01FDF7C2FAA068FDF7DDFAD8 +:100840006168C96B4876DBE0FFE796F85600082838 +:1008500070D096F8531081426AD0D5E04FF0060868 +:1008600029E7054691F8510000280CBF4FF0010B15 +:100870004FF0000B4FF00008A06810F8092BD209C8 +:1008800007D0407900F0C000402808BF4FF0010AAF +:1008900001D04FF0000A91F84000032806D191F8EA +:1008A0003900002818BF91F8569001D191F8539063 +:1008B0004846FDF72CF80090D8B34846FCF75BFE9D +:1008C000002818BF4FF0010BBAF1000F37D0A06815 +:1008D000A14600F10901009800E0B6E0F8F762FED9 +:1008E0005FEA0008D9F8040090F8319018BF49F089 +:1008F0000209606890F84010032924D0F7F7AAFF96 +:10090000002DABD0F7F75DFD002808BFB8F1000F50 +:100910007DD020787F2808BFFFDF94F90000277082 +:1009200000906068494690F8733090F8542002E0D7 +:1009300066E004E068E0323004F02FFE8EE7606885 +:1009400090F83190D5E7A168C06BCA78837E9A424F +:100950001BD10A79C37E9A4217D14A79037F9A4202 +:1009600013D18A79437F9A420FD1CA79837F9A4201 +:100970000BD10A7AC37F9A4207D10978407EC1F32E +:100980008011814208BF012700D0002796F853004C +:10099000082806D096F85610884208BF4FF0010983 +:1009A00001D04FF00009B8F1000F05D1BBF1000FE5 +:1009B00004D0F7F706FD08B1012000E000204DB19A +:1009C00096F84210012903D021B957EA090101D054 +:1009D000012100E00021084216D0606890F8421022 +:1009E000012908BF002F0BD1C06B00F11A01A068CC +:1009F000FDF7E5F9A068FDF700FA6168C96B487674 +:100A00004FF00E0857E602E0F7F724FF26E760688C +:100A100090F84100032818BF02287FF41FAFBAF1F5 +:100A2000000F3FF41BAF20787F2808BFFFDF94F949 +:100A30000000277000906068FE2190F8733090F8F5 +:100A40005420323004F0A9FD08E791F8481000293D +:100A500018BF00283FF47EAE0BE0000074010020B8 +:100A600044110020B9F1070F7FF474AE00283FF461 +:100A700071AEFEF790FC80461DE60000D0F8001134 +:100A800049B1D0E941231A448B691A448A61D0E9FB +:100A90003F12D16003E0FE4AD0F8FC101162D0E9A9 +:100AA0003F1009B1086170470028FCD00021816126 +:100AB00070472DE9FF4F06460C46488883B040F248 +:100AC000E24148430190E08A002500FB01FA94F8D6 +:100AD0007C0090460D2822D00C2820D024281ED03F +:100AE00094F87D0024281AD000208346069818B177 +:100AF0000121204603F0C0F894F8641094F86500D2 +:100B0000009094F8F0200F464FF47A794AB1012A08 +:100B100061D0022A44D0032A5DD0FFDFB5E0012076 +:100B2000E3E7B8F1000F00D1FFDFD94814F8641FE4 +:100B3000243090F83400F0F7C4FC01902078F8F7E6 +:100B4000CFFB4D4600F2E730B0FBF5F1DFF8409304 +:100B5000D9F80C0001EB00082078F8F7C1FB01463A +:100B600014F86409022816D0012816D040F6340083 +:100B700008444AF2EF010844B0FBF5F10198D9F8B6 +:100B80001C20411A514402EB08000D18012084F882 +:100B9000F0002D1D78E02846EAE74FF4C860E7E74B +:100BA000DFF8EC92A8F10100D9F80810014300D158 +:100BB000FFDFB848B8F1000F016801EB0A0506D065 +:100BC000D9F8080000F22630A84200D9FFDF032040 +:100BD00084F8F00058E094F87C20019D242A05D088 +:100BE00094F87D30242B01D0252A3AD1B4F8702016 +:100BF000B4F81031D21A521C12B2002A31DB94F828 +:100C0000122172B3174694F8132102B110460090D6 +:100C1000022916D0012916D040F6340049F60852B0 +:100C20008118022F12D0012F12D040F63400104448 +:100C3000814210D9081A00F5FA70B0FBF9F00544AA +:100C40000FE04846EAE74FF4C860E7E74846EEE7BA +:100C50004FF4C860EBE7401A00F5FA70B0FBF9F00A +:100C60002D1AB8F1000F0FD0DFF82482D8F8080051 +:100C700018B9B8F8020000B1FFDFD8F8080000F298 +:100C80002630A84200D9FFDF05B9FFDF2946D4F896 +:100C9000F400F4F750FFC4F8F400B06000203070A6 +:100CA0004FF0010886F80480204603F040F8ABF1CD +:100CB0000101084202D186F8058005E094F8F000B1 +:100CC000012844D003207071606A3946009A01F00F +:100CD00042FBF060069830EA0B0035D029463046DA +:100CE000F0F7BAF987B2204603F021F8B8420FD8DE +:100CF000074686F8058005FB07F1D4F8F400F4F701 +:100D00001AFFB06029463046F0F7A6F9384487B29A +:100D10003946204602F0B0FFB068C4F8F400A06E77 +:100D2000002811D0B4F87000B4F89420801A01B2F1 +:100D3000002909DD34F86C0F0144491E91FBF0F1E4 +:100D400089B201FB0020208507B0BDE8F08F0220AA +:100D5000B9E72DE9F04106460C46012001F0DBFA27 +:100D6000C5B20B2001F0D7FAC0B2854200D0FFDF38 +:100D70000025082C7ED2DFE804F00461696965C6AD +:100D80008293304601F0DDFA0621F3F78FF8040074 +:100D900000D1FFDF304601F0D4FA2188884200D02C +:100DA000FFDF94F8F00000B9FFDF204602F00FFEED +:100DB000374E21460020B5607580F561FDF7E9FCEE +:100DC00000F19807606AB84217D994F86500F7F700 +:100DD0000BF9014694F864004FF47A72022828D087 +:100DE000012828D040F6340008444AF2473108442C +:100DF000B0FBF2F1606A0844C51B21460020356152 +:100E0000FDF7C7FC618840F2E24251439830081A6E +:100E1000A0F22630706194F8652094F86410606A3E +:100E200001F099FAA0F5CB70B061BDE8F041F5F79B +:100E300060BE1046D8E74FF4C860D5E7BDE8F04182 +:100E400002F02FBEBDE8F041F7F7D5BE304601F005 +:100E500078FA0621F3F72AF8040000D1FFDF3046C4 +:100E600001F06FFA2188884200D0FFDF01220021C3 +:100E7000204600E047E0BDE8F04101F089BAF7F70D +:100E800073FDF7F7B8FE02204FF0E02104E0000008 +:100E9000CC11002084010020C1F88002BDE8F0815F +:100EA000304601F04EFA0621F3F700F8040000D1B5 +:100EB000FFDF304601F045FA2188884200D0FFDF8D +:100EC00094F8F000042800D0FFDF84F8F05094F884 +:100ED000FA504FF6FF76202D00D3FFDFFA4820F8B6 +:100EE000156094F8FA00F5F720F900B9FFDF20202B +:100EF00084F8FA002046FFF7C1FDF4480078BDE809 +:100F0000F041E2F701B8FFDFC8E770B5EE4C00250D +:100F1000443C84F82850E07868B1E570FEF71EF98B +:100F20002078042803D0606AFFF7A8FD6562E748CF +:100F30000078E1F7E9FFBDE8704001F03ABA70B51A +:100F4000E14C0146443CE069F5F706FE6568A2788D +:100F500090FBF5F172B140F27122B5FBF2F292B260 +:100F6000A36B01FB02F6B34202D901FB123200E08F +:100F70000022A2634D43002800DAFFDF2946E06922 +:100F8000F4F7D9FDE06170BD2DE9F05FFEF736F9A9 +:100F90008246CD48683800F1240881684646D8F872 +:100FA0001800F4F7C8FD0146F069F5F7D5FD4FF0DC +:100FB0000009074686F835903C4640F28F254E469C +:100FC0001EE000BF0AEB06000079F7F70DF80146B6 +:100FD0004AF2B12001444FF47A70B1FBF0F008EB13 +:100FE0008602414692681044844207D3241A91F83D +:100FF0003500A4F28F24401C88F83500761CF6B228 +:1010000098F83600B042DDD8002C10DD98F8351085 +:10101000404608EB81018968A14208D24168C91B9A +:10102000B1F5247F00D30D466C4288F8359098F8CE +:101030003560C3460AEB060898F80400F6F7D4FFBB +:101040004AF2B12101444FF47A7AB1FBFAF298F8EE +:101050000410082909D0042909D0002013180429F4 +:101060000AD0082908D0252207E0082000E0022045 +:1010700000EB40002830F1E70F22521D4FF4A8701A +:10108000082914D0042915D0022916D04FF0080CD5 +:101090005FF0280012FB0C00184462190BEB86036A +:1010A00010449A68D84690420BD8791925E04FF041 +:1010B000400CEFE74FF0100CECE74FF0040C182059 +:1010C000E8E798F8352098F836604046B24210D2EA +:1010D000521C88F835203C1B986862198418084611 +:1010E000F6F782FF4AF2B1210144B1FBFAF001198F +:1010F00003E080F83590D8F80410D8F81C00BDE85B +:10110000F05FF4F718BD2DE9FE4F14460546FEF7D3 +:1011100075F8DFF8B4A10290AAF1440A50469AF893 +:1011200035604FF0000B0AEB86018968CAF83C1065 +:10113000F4B3044600780027042825D005283ED0C3 +:10114000FFDFA04639466069F4F7F5FC0746F5F77E +:101150000BF881463946D8F80440F5F7FDFC401EEF +:1011600090FBF4F0C14361433846F4F7E4FC0146D8 +:10117000C8F81C004846F5F7EFFC002800DDFFDF4B +:10118000012188F813108DE0D4F81490D4F804806D +:1011900001F07AF9070010D0387800B9FFDF7969DB +:1011A00078684A460844414601F05AF9074600E08B +:1011B0000BE04045C5D9FFDFC3E75F46C1E7606A82 +:1011C00001F004F940F6B837BBE7C1690AEB460005 +:1011D0000191408D10B35446DAF81400FFF7AFFECA +:1011E0006168E069F4F7A7FC074684F835B0019C14 +:1011F000D0462046DAF81410F5F7AEFC81463946A1 +:101200002046F5F7A9FCD8F804200146B9FBF2F016 +:10121000B1FBF2F1884242D0012041E0F4F7A4FF93 +:10122000FFF78DFEFFF7B0FE9AF83510DAF804905C +:101230000AEB81010746896800913946DAF81C00FB +:10124000F5F78AFC00248046484504DB98FBF9F456 +:1012500004FB09F41AE0002052469AF8351007E022 +:1012600002EB800304F28F249B68401C1C44C0B234 +:101270008142F5D851B10120F6F7B6FE4AF2B1210C +:1012800001444FF47A70B1FBF0F004440099A8EBEC +:1012900004000C1A00D5FFDFCAF83C40A7E7002085 +:1012A00088F813009AF802005446B8B13946E0694C +:1012B000F5F752FC0146A26B40F2712042438A428C +:1012C00006D2C4F83CB009E03412002080010020AE +:1012D000E06B511A884200D30846E063AF6085F89E +:1012E00000B001202871029F94F835003F1DC05DB9 +:1012F000F6F77AFE4AF23B5101444FF47A70B1FBA3 +:10130000F0F0E16BFE300844E8602078042808D152 +:1013100094F8350004EB4000408D0A2801D20320E8 +:1013200000E00220687104EB4600408DC0B1284601 +:101330006168EFF791FE82B20020761C0CE000BFDE +:1013400004EB4001B0424B8D13449BB24B8501D35B +:101350005B1C4B85401CC0B294F836108142EFD222 +:10136000A8686061A06194F8350004EB4001488DE5 +:10137000401C488594F83500C05D082803D0042837 +:1013800003D000210BE0082100E0022101EB410124 +:1013900028314FF4A872082804D0042802D002286B +:1013A00007D028220A44042805D0082803D0252184 +:1013B00002E01822F6E70F21491D08280CD0042866 +:1013C0000CD002280CD0082011FB0020E16B8842D1 +:1013D00008D20120BDE8FE8F4020F5E71020F3E79A +:1013E0000420F1E70020F5E770B5FE4C061D14F867 +:1013F000352F905DF6F7F8FD4FF47A7100F2E73083 +:10140000B0FBF1F0D4F8071045182078805DF6F7AE +:1014100073FE2178895D082903D0042903D00022B6 +:101420000BE0082200E0022202EB420228324FF4D5 +:10143000A873082904D0042902D0022907D0282340 +:101440001344042905D0082903D0252202E01823DB +:10145000F6E70F22521D08290AD004290AD00229D2 +:101460000AD0082112FB0131081A281A293070BD50 +:101470004021F7E71021F5E70421F3E72DE9FF41CB +:1014800007460C46012000F046FFC5B20B2000F0D5 +:1014900042FFC0B2854200D0FFDF20460126002572 +:1014A000D04C082869D2DFE800F004304646426894 +:1014B0006865667426746078002819D1FDF79EFE71 +:1014C000009594F835108DF808104188C90411D0A2 +:1014D000206C019003208DF80900C24824388560F3 +:1014E000C56125746846FDF768FB002800D0FFDF62 +:1014F000BDE8FF81FFF778FF0190E07C10B18DF827 +:101500000950EAE78DF80960E7E7607840B1207C90 +:1015100008B9FDF7F9FD6574BDE8FF41F4F72FBD8B +:10152000A674FDF739FC0028E2D0FFDFE0E7BDE854 +:10153000FF41F7F760BBFDF761FE4088C00407D0AC +:1015400001210320FDF75EFEA7480078E1F7DCFCEF +:10155000002239466846FFF7D6FD38B1694638465D +:1015600000F0EDFE0028C3D1FFDFC1E7E670FFF712 +:10157000CCFCBDE7BDE8FF41C7E4FFDFB8E7994910 +:1015800050B101228A704A6840F27123B2FBF3F233 +:1015900002EB0010886370470020887070472DE9C7 +:1015A000F05F894640F271218E4E48430025044683 +:1015B000706090462F46D0074AF2B12A4FF47A7BEA +:1015C0000FD0B9F800004843B0600120F6F70CFDD9 +:1015D00000EB0A01B1FBFBF0241AB7680125A4F265 +:1015E0008F245FEA087016D539F8151040F2712083 +:1015F000414306EB85080820C8F80810F6F7F4FC0C +:1016000000EB0A01B1FBFBF0241AD8F80800A4F2A1 +:101610008F2407446D1CA74219D9002D17D0391B00 +:10162000B1FBF5F0B268101AB1FBF5F205FB12122E +:10163000801AB060012008E0B1FBF5F306EB8002F0 +:101640009468E31A401CC0B29360A842F4D3BDE88A +:10165000F09F2DE9F041634C00262078042804D047 +:101660002078052801D00C2018E401206070607CEF +:10167000002538B1EFF3108010F0010F72B610D0D2 +:1016800001270FE0FDF7BAFD074694F82000F5F7B3 +:10169000B2F87888C00411D000210320FDF7B2FD14 +:1016A0000CE00027607C38B1A07C28B1FDF72CFD50 +:1016B0006574A574F4F763FC07B962B694F820006A +:1016C000F5F705FB94F8280030B184F8285020780D +:1016D000052800D0FFDF0C26657000F06AFE30465A +:1016E00012E4404810B5007808B1FFF7B2FF00F0EF +:1016F000D4FE3C4900202439086210BD10B53A4C94 +:1017000058B1012807D0FFDFA06841F66A0188427E +:1017100000D3FFDF10BD40F6C410A060F4E73249EB +:1017200008B508702F4900200870487081F828001B +:10173000C8700874487488742022486281F8202098 +:10174000243948704FF6FF7211F1680121F810201A +:10175000401CC0B22028F9D30020FFF7CFFFFFF7CD +:10176000C0FF1020ADF80000012269460420FFF7F9 +:1017700016FF08BD7FB51B4C05460E46207810B1FC +:101780000C2004B070BD95F8652095F86410686A67 +:1017900000F0C5FEC5F80401656295F8F00000B1DF +:1017A000FFDF104900202439C861052121706070D5 +:1017B00084F82800014604E004EB4102491C5085EE +:1017C000C9B294F836208A42F6D284F83500304601 +:1017D000FFF7D5FE0548F4F74DFC84F820002028DB +:1017E00007D105E0F0110020800100207D140200E7 +:1017F000FFDFF4F7B9FC606194F82010012268461D +:10180000FFF781FC00B9FFDF94F82000694600F083 +:1018100096FD00B9FFDF0020B3E7F94810B5007866 +:1018200008B1002010BD0620F2F7DAFA80F00100BE +:1018300010BDF8B5F24D0446287800B1FFDF002056 +:10184000009023780246DE0701466B4605D060888B +:10185000A188ADF80010012211462678760706D53A +:10186000E088248923F8114042F00802491C491EEF +:1018700085F836101946FFF792FE0020F8BD1FB517 +:1018800011B1112004B010BDDD4C217809B10C203C +:10189000F8E70022627004212170114605E000BFC4 +:1018A00004EB4103491C5A85C9B294F836308B4287 +:1018B000F6D284F83520FFF762FED248F4F7DAFB5F +:1018C00084F82000202800D1FFDF00F0DDFD10B1FA +:1018D000F4F74AFC05E0F4F747FC40F6B831F4F7BA +:1018E0002AF9606194F8201001226846FFF70BFC8A +:1018F00000B9FFDF94F82000694600F020FD00B930 +:10190000FFDF0020BEE770B5BD4C616A0160FFF7E4 +:10191000A0FE050002D1606AFFF7B0F80020606207 +:10192000284670BD7FB5B64C2178052901D00C2022 +:1019300027E7B3492439C860606A00B9FFDF606AED +:1019400090F8F00000B1FFDF606A90F8FA002028FC +:1019500000D0FFDFAC48F4F78DFB616A0546202814 +:1019600081F8FA000E8800D3FFDFA548443020F844 +:101970001560606A90F8FA00202800D1FFDF00238C +:1019800001226846616AFFF794F8606A694690F838 +:10199000FA0000F0D4FC00B9FFDF00206062F0E63E +:1019A000974924394870704710B540F2E24300FB74 +:1019B00003F4002000F0B3FD844201D9201A10BDC9 +:1019C000002010BD70B50D46064601460020FCF70C +:1019D000E0FE044696F86500F6F706FB014696F829 +:1019E00064004FF47A72022815D0012815D040F611 +:1019F000340008444AF247310844B0FBF2F17088E1 +:101A000040F271225043C1EB4000A0F22630A542C3 +:101A100006D2214605E01046EBE74FF4C860E8E740 +:101A20002946814204D2A54201D2204600E0284640 +:101A3000706270BD70B50546FDF7E0FB7049007837 +:101A400024398C689834072D30D2DFE805F004344F +:101A500034252C34340014214FF4A873042810D0FA +:101A60000822082809D02A2102280FD011FB0240A1 +:101A700000222823D118441819E0402211FB02400B +:101A8000F8E7102211FB02402E22F3E7042211FB9B +:101A9000024000221823EDE7282100F04BFC04440B +:101AA00004F5317403E004F5B07400E0FFDF54483E +:101AB000C06BA04201D9012070BD002070BD70B57F +:101AC0004F4C243C607870B1D4E904512846A26898 +:101AD000EFF7EDFA2061A84205D0A169401B084448 +:101AE000A061F5F706F82169A068884201D820783E +:101AF00008B1002070BD012070BD2DE9F04F0546F2 +:101B000085B016460F461C461846F6F7F5FA05EB63 +:101B100047014718204600F0F5FB4AF2C5714FF423 +:101B20007A7908444D46B0FBF5F0384400F160087E +:101B30003348761C24388068304404902046F6F7F9 +:101B4000DBFAA8EB0007204600F0DCFB0646204647 +:101B5000F6F74AFA301AB0FBF5F03A1A18252820A1 +:101B60004FF4C8764FF4BF774FF0020B082C30D0FB +:101B7000042C2BD00021022C2ED0082311F1280197 +:101B800003EB830C0CEB831319440A444FF0000A57 +:101B9000082C29D0042C22D00021022C29D0054663 +:101BA000082001F5B07100BF00EB0010284481420D +:101BB00032D2082C2AD0042C1ED00020022C28D08F +:101BC0000821283001EB0111084434E03946102384 +:101BD000D6E731464023D3E704231831D0E73D460A +:101BE00040F2EE311020DFE735464FF435614020FA +:101BF000DAE70420B431D7E738461021E2E70000E5 +:101C0000F01100207D140200530D020030464021E7 +:101C1000D8E704211830D5E7082C4FD0042C4AD03F +:101C20000021022C4DD0082311F12801C3EBC30081 +:101C300000EB4310084415182821204600F07AFBD9 +:101C400005EB4001082C42D0042C3DD00026022C8C +:101C50003FD0082016F1280600EB801006EB80002C +:101C60000E180120FA4D8DF804008DF800A08DF8B3 +:101C700005B0A86906F22A260499F3F75CFFCDE9BE +:101C800002062046F6F7B0F94AF23B510144B1FB97 +:101C9000F9F0301AFE38E8630298C5F84080A86170 +:101CA00095F82000694600F04AFB002800D1FFDFCC +:101CB00005B0BDE8F08F39461023B7E73146402321 +:101CC000B4E704231831B1E73E461020C4E74020B2 +:101CD000C2E704201836BFE72DE9FE4F06461C4632 +:101CE000174688464FF0010A1846F6F705FAD84D10 +:101CF000243DA9688A1907EB48011144471820467A +:101D000000F000FB4FF47A7BD84600F6FB00B0FBF6 +:101D1000F8F0384400F120092046F6F7EDF9A968FB +:101D20000246A9EB0100801B871A204600F0EAFA60 +:101D300005462046F6F758F9281AB0FBF8F03A1A8B +:101D4000182528204FF4C8774FF4BF78082C2DD0E1 +:101D5000042C28D00021022C2BD0082311F12801BB +:101D600003EB830C0CEB831319440A44082C28D092 +:101D7000042C21D00021022C28D00546082001F592 +:101D8000B07100BF00EB0010284481422AD2082C19 +:101D900022D0042C1DD00020022C20D00821283075 +:101DA00001EB01112CE041461023D9E739464023CD +:101DB000D6E704231831D3E7454640F2EE31102030 +:101DC000E0E73D464FF435614020DBE70420B431C5 +:101DD000D8E740461021E3E738464021E0E70421F8 +:101DE0001830DDE7082C48D0042C43D00020022C0A +:101DF00046D0082110F12800C1EBC10303EB4111CB +:101E0000084415182821204600F094FA05EB4001FB +:101E1000082C3BD0042C36D00027022C38D00820C8 +:101E200017F1280700EB801007EB80000C1804F571 +:101E300096740C98F6F7D8F84AF23B510144B1FB7E +:101E4000FBF0834DFE30A5F12407E96B06F1FE029D +:101E50000844B9680B191A44824224D93219114432 +:101E60000C1AFE342044B0F1807F37D2642C12D299 +:101E7000642011E040461021BEE738464021BBE710 +:101E800004211830B8E747461020CBE74020C9E7C7 +:101E900004201837C6E720460421F4F790FEE8B185 +:101EA000E86B2044E863E0F703FFB9682938314460 +:101EB0000844CDE9000995F835008DF808000220A6 +:101EC0008DF809006846FCF778FE00B1FFDFFCF7EB +:101ED00063FF00B1FFDF5046BDE8FE8F4FF0000A00 +:101EE000F9E71FB500F021FB594C607880B994F8F0 +:101EF000201000226846FFF706F938B194F8200058 +:101F0000694600F01CFA18B9FFDF01E00120E0701B +:101F1000F4F735F800206074A0741FBD2DE9F84F68 +:101F2000FDF76CF90646451CC07840090CD0012825 +:101F30000CD002280CD000202978824608064FF4E5 +:101F4000967407D41E2006E00120F5E70220F3E78F +:101F50000820F1E72046B5F80120C2F30C0212FB7D +:101F600000F7C80901D010B103E01E2401E0FFDF33 +:101F70000024F6F7D3F8A7EB00092878B77909EB26 +:101F80000408C0F3801010B120B1322504E04FF4F2 +:101F9000FA7501E0FFDF00250C2F00D3FFDF2D488D +:101FA0002D4A30F81700291801FB0821501CB1FBFD +:101FB000F0F5F6F76DF8F6F717F84FF47A7100F2CE +:101FC0007160B0FBF1F1A9EB0100471BA7F15900CB +:101FD000103FB0F5247F11D31D4E717829B9024608 +:101FE000534629462046FFF788FD00F09EFAF3F796 +:101FF000C6FF00207074B074BDE8F88F3078009090 +:102000005346224629463846FFF766FE0028F3D19C +:1020100001210220FDF7F6F8BDE8F84F61E710B5A1 +:102020000446012903D10A482438007830B104203D +:1020300084F8F000BDE81040F3F7A1BF00220121B1 +:10204000204600F0A5F934F8700F401C2080F1E71D +:10205000F0110020646302003F420F002DE9F041BF +:102060000746FDF7CBF8050000D1FFDF287810F018 +:102070000C0F01D0012100E00021F74C606A3030E4 +:10208000FCF7C7FA29783846EFF71BFAA4F12406C3 +:102090000146A069B26802446FB32878082803D0CB +:1020A000042803D000230BE0082300E0022303EB05 +:1020B000430328334FF4A877082804D0042802D01B +:1020C000022810D028273B4408280ED004280ED020 +:1020D00002280ED05FF00800C0EBC00707EB4010ED +:1020E0001844983009E01827EDE74020F4E7102065 +:1020F000F2E70420F0E74FF4FC701044471828780A +:102100003F1DF5F771FF014628784FF47A720228D7 +:102110001DD001281DD040F6340008444AF2EF01DA +:102120000844B0FBF2F03A1A606A40F2E241B0466D +:102130004788F0304F43316A81420DD03946206BD9 +:1021400000F08EF90646B84207D9FFDF05E01046D9 +:10215000E3E74FF4C860E0E70026C04880688642A5 +:1021600007D2616A40F271224888424306EB420678 +:1021700004E040F2E240B6FBF0F0616AC882606AB7 +:10218000297880F86410297880F865100521417558 +:10219000C08A6FF41C71484306EB400040F635419D +:1021A000C8F81C00B0EB410F00D3FFDFBDE8F081A1 +:1021B00010B5052937D2DFE801F00509030D31001C +:1021C000002100E00121BDE8104028E7032180F84C +:1021D000F01010BD0446408840F2E24148439F4958 +:1021E000091D0860D4F818010089E082D4F81801AC +:1021F00080796075D4F8180140896080D4F818019E +:102200008089A080D4F81801C089E0802046A16AA6 +:10221000FFF7D8FB022084F8F00010BD816ABDE80A +:102220001040FFF7CFBBFFDF10BD70B58A4C243CD8 +:102230000928A1683FD2DFE800F0050B0B15131544 +:1022400038380800BDE870404BE6BDE8704065E6F0 +:10225000022803D00020BDE87040FFE60120FAE725 +:10226000E16070BD032802D005281CD000E0E160C9 +:102270005FF0000600F059F9774D012085F828003D +:1022800085F83460686AA9690026C0F8F41080F8FF +:10229000F060E068FFF746FB00B1FFDFF3F76FFE89 +:1022A0006E74AE7470BD0126E4E76C480078BDE83A +:1022B0007040E0F729BEFFDF70BD674924394860F0 +:1022C000704770B5644D0446243DB1B14FF47A7641 +:1022D000012903D0022905D0FFDF70BD1846F5F7AC +:1022E000FCFE05E06888401C68801046F6F7F8FFA1 +:1022F00000F2E730B0FBF6F0201AA86070BD564837 +:1023000000787047082803D0042801D0F5F76CBE88 +:102310004EF628307047002804DB00F1E02090F8EA +:10232000000405E000F00F0000F1E02090F8140D2B +:102330004009704710F00C0000D008467047F4F7D1 +:102340003EB910B50446202800D3FFDF4248443090 +:1023500030F8140010BD70B505460C461046F5F770 +:1023600043FE4FF47A71022C0DD0012C0DD040F6B3 +:10237000340210444AF247321044B0FBF1F02844D2 +:1023800000F5CB7070BD0A46F3E74FF4C862F0E782 +:102390001FB513460A46044601466846FEF789FB08 +:1023A00094F8FA006946FFF7CAFF002800D1FFDF62 +:1023B0001FBD70B5284C0025257094F82000F3F758 +:1023C000B4FE00B9FFDF84F8205070BD2DE9F04164 +:1023D000050000D1FFDF204A0024243AD5F804612B +:1023E0002046631E116A08E08869B04203D3984210 +:1023F00001D203460C460846C9680029F4D104B945 +:1024000004460021C5F80041F035C4B1E068E5603C +:10241000E86000B105612E698846A96156B1B069CE +:1024200030B16F69B84200D2FFDFB069C01BA8614C +:10243000C6F81880084D5CB1207820B902E0E96048 +:102440001562E8E7FFDF6169606808442863ADE66C +:10245000C5F83080AAE60000F011002080010020BD +:1024600010B50C4601461046F4F776FB002806DA54 +:10247000211A491EB1FBF4F101FB040010BD90FBD1 +:10248000F4F101FB140010BD2E48016A002001E0A8 +:102490000846C9680029FBD170472DE9FE43294D44 +:1024A0000120287000264FF6FF7420E00621F1F786 +:1024B000FDFC070000D1FFDF97F8FA00F037F4F7D2 +:1024C00006FC07F80A6BA14617F8FA89B8F1200F45 +:1024D00000D3FFDF1B4A683222F8189097F8FA0001 +:1024E000F3F723FE00B9FFDF202087F8FA006946E2 +:1024F0000620F1F764FC50B1FFDF08E0029830B12C +:1025000090F8F01019B10088A042CFD104E06846DD +:10251000F1F733FC0028F1D02E70BDE8FE8310B532 +:10252000FFF719FF00F5C87010BD064800212430E0 +:1025300090F8352000EB4200418503480078E0F731 +:10254000E3BC0000CC11002080010020012804D051 +:10255000022805D0032808D105E0012907D004E0AE +:10256000022904D001E0042901D000207047012095 +:102570007047F748806890F8A21029B1B0F89E1013 +:10258000B0F8A020914215D290F8A61029B1B0F869 +:10259000A410B0F8A02091420CD2B0F89C20B0F862 +:1025A0009A108A4206D290F88020B0F898001AB1AA +:1025B000884203D3012070470628FBD200207047D1 +:1025C0002DE9F041E24D0746A86800F1700490F84B +:1025D000140130B9E27B002301212046EEF7D2FC42 +:1025E00010B1A08D401CA08501263D21AFB92878EF +:1025F000022808D001280AD06878C8B110F0140F5A +:1026000009D01E2039E0162037E026773EE0A86882 +:1026100090F8160131E0020701D56177F5E78107EF +:1026200001D02A2029E0800600D4FFDF232024E007 +:1026300094F8320028B1E08D411CE185218E88425A +:1026400013D294F8360028B1A08E411CA186218EA9 +:1026500088420AD2A18D608D814203D3AA6892F884 +:10266000142112B9228E914201D3222005E0217C4F +:1026700029B1218D814207D308206077C5E7208DDD +:10268000062801D33E20F8E7207FB0B10020207358 +:10269000607320740221A868FFF78AFDA86890F88B +:1026A000E410012904D1D0F81C110878401E0870EC +:1026B000E878BDE8F041E0F727BCA868BDE8F04144 +:1026C0000021FFF775BDA2490C28896881F8E40054 +:1026D00014D0132812D0182810D0002211280ED0A0 +:1026E00007280BD015280AD0012807D0002805D0CC +:1026F000022803D021F89E2F012008717047A1F80D +:10270000A420704710B5924CA1680A88A1F86021F6 +:1027100081F85E0191F8640001F046FBA16881F840 +:10272000620191F8650001F03FFBA16881F8630147 +:10273000012081F85C01002081F82E01E078BDE8DD +:102740001040E0F7E1BB70B5814C00231946A0684A +:1027500090F87C207030EEF715FC00283DD0A06882 +:1027600090F820110025C9B3A1690978B1BB90F890 +:102770007D00EEF7EFFB88BBA168B1F870000A2876 +:102780002DD905220831E069EBF72AFE10B3A068C5 +:10279000D0F81C11087858B10522491CE069EBF704 +:1027A0001FFE002819D1A068D0F81C01007840B99C +:1027B000A068E169D0F81C010A68C0F80120097915 +:1027C0004171A068D0F81C110878401C08700120E5 +:1027D000FFF779FFA06880F8205170BDFFE7A0687F +:1027E00090F8241111B190F82511C1B390F82E1171 +:1027F0000029F2D090F82F110029EED190F87D0039 +:10280000EEF7A8FB0028E8D1A06890F8640001F07A +:10281000CBFA0646A06890F8650001F0C5FA0546B7 +:10282000A06890F830113046FFF790FEA0B3A06882 +:1028300090F831112846FFF789FE68B3A268B2F814 +:10284000703092F86410B2F8320102F58872EEF737 +:1028500001FE20B3A168252081F87C00BDE7FFE7D9 +:1028600090F87D10242918D090F87C10242914D0D9 +:102870005FF0000300F5897200F59271FBF78EFEA0 +:10288000A16881F8245101F13000C28A21F8E62FB5 +:10289000408B4880142007E005E00123EAE7BDE80B +:1028A000704000202EE71620BDE870400BE710B501 +:1028B000F4F7FAFD0C2813D3254C0821A068D0F8B2 +:1028C00018011E30F4F7F4FD28B1A0680421D830B7 +:1028D000F4F7EEFD00B9FFDFBDE810400320F2E69B +:1028E00010BD10B51A4CA068D0F818110A78002A4B +:1028F0001FD04988028891421BD190F87C20002388 +:1029000019467030EEF73EFB002812D0A068D0F8D0 +:1029100018110978022907D003290BD0042919D0EE +:10292000052906D108200DE090F87D00EEF712FB96 +:1029300040B110BD90F8811039B190F8820000B913 +:10294000FFDF0A20BDE81040BDE6BDE81040AEE75D +:102950008C01002090F8AA008007EAD10C20FFF734 +:10296000B2FEA068002120F89E1F01210171017BA9 +:1029700041F00101017310BD70B5F74CA268556EAE +:10298000EEF702FDEBB2C1B200228B4203D0A36886 +:1029900083F8121102E0A16881F81221C5F3072122 +:1029A000C0F30720814203D0A16881F8130114E726 +:1029B000A06880F8132110E710B5E74C0421A06847 +:1029C000FFF7F6FBA06890F85A10012908D000F52F +:1029D0009E71FBF7ACFEE078BDE81040E0F794BADA +:1029E000022180F85A1010BD70B5DB4CA06890F839 +:1029F000E410FE2955D16178002952D190F87F204A +:102A0000002301217030EEF7BDFA002849D1A068FB +:102A100090F8141109B1022037E090F87C200023CF +:102A200019467030EEF7AEFA28B1A06890F896001B +:102A300008B1122029E0A068002590F87C20122A15 +:102A40001DD004DC032A23D0112A04D119E0182A4E +:102A50001AD0232A26D0002304217030EEF792FAF0 +:102A600000281ED1A06890F87D10192971D020DCB3 +:102A700001292AD0022935D0032932D120E00B20A8 +:102A800003E0BDE8704012E70620BDE870401AE69A +:102A900010F8E21F01710720FFF715FEA06880F80B +:102AA0007C509AE61820FFF70EFEA068A0F89E5012 +:102AB00093E61D2918D01E2916D0212966D149E098 +:102AC00010F8E11F4171072070E00C20FFF7FBFDBB +:102AD000A06820F8A45F817941F00101817100F8BC +:102AE000275C53E013202CE090F8252182BB90F85E +:102AF0002421B2B1242912D090F87C1024290ED0C0 +:102B00005FF0000300F5897200F59271FBF746FD56 +:102B1000A0681E2180F87D1080F8245103E0012375 +:102B2000F0E71E2932D1A068FBF797FDFFF744FFBD +:102B3000A16801F13000C28A21F8E62F408B48805D +:102B40001520FFF7C0FDA068A0F8A45080F87D50C4 +:102B50001CE02AE090F8971051B180F8125180F8EB +:102B600013511820FFF7AFFDA068A0F8A4500DE0A6 +:102B700090F82F1151B990F82E1139B1C16DD0F8DC +:102B80003001FFF7F9FE1820FFF79DFDA06890F8CF +:102B9000E400FE2885D1FFF7A4FEA06890F8E400C9 +:102BA000FE2885D1BDE87040CDE51120FFF78BFDF3 +:102BB000A068CBE7684A0129926819D0002302294E +:102BC0000FD003291ED010B301282BD0032807D122 +:102BD00092F87C00132803D0162801D0182804D1BD +:102BE000704792F8E4000028FAD0D2F8180117E0F4 +:102BF00092F8E4000128F3D0D2F81C110878401EA6 +:102C00000870704792F8E4000328EED17047D2F8BC +:102C10001801B2F870108288891A09B20029F5DB10 +:102C200003707047B2F87000B2F82211401A00B277 +:102C30000028F6DBD2F81C010178491E01707047AC +:102C400070B5044690F87C0000250C2810D00D28A3 +:102C50002ED1D4F81811B4F870008988401C88422D +:102C600026D1D4F864013C4E017811B3FFDF42E075 +:102C7000B4F87000B4F82211401C884218D1D4F87E +:102C80001C01D0F80110A160407920730321204677 +:102C9000EDF7F1FDD4F81C01007800B9FFDF012148 +:102CA000FE20FFF787FF84F87C50012084F8B200F3 +:102CB00093E52188C180D4F81801D4F864114089C3 +:102CC0000881D4F81801D4F8641180894881D4F8B7 +:102CD0001801D4F86411C0898881D4F864010571A1 +:102CE000D4F8641109200870D4F864112088488051 +:102CF000F078E0F709F902212046EDF7BCFD032149 +:102D00002046FFF755FAB068D0F81801007802287D +:102D100000D0FFDF0221FE20FFF74CFF84F87C503B +:102D20005BE52DE9F0410C4C00260327D4F808C0E0 +:102D3000012598B12069C0788CF8E20005FA00F00E +:102D4000C0F3C05000B9FFDFA06800F87C7F468464 +:102D500080F82650BDE8F0818C01002000239CF80B +:102D60007D2019460CF17000EEF70CF970B1607817 +:102D70000028EFD12069C178A06880F8E11080F8C0 +:102D80007D70A0F8A46080F8A650E3E76570E1E7E5 +:102D9000F0B5F74C002385B0A068194690F87D2067 +:102DA0007030EEF7EFF8012580B1A06890F87C0054 +:102DB00023280ED024280CD06846F6F785FB68B18E +:102DC000009801A9C0788DF8040008E0657005B08E +:102DD000F0BD607840F020006070F8E70021A06846 +:102DE00003AB162290F87C00EEF74FFB002648B1AB +:102DF000A0689DF80C20162180F80C2180F80D1198 +:102E0000192136E02069FBF722FB78B121690879A6 +:102E100000F00702A06880F85C20497901F0070102 +:102E200080F85D1090F82F310BBB03E00020FFF716 +:102E300078FFCCE790F82E31CBB900F164035F78CE +:102E4000974205D11A788A4202D180F897500EE055 +:102E500000F5AC71028821F8022990F85C200A7113 +:102E600090F85D0048710D70E078E0F74DF8A068CB +:102E7000212180F87D1080F8A650A0F8A460A6E774 +:102E8000F8B5BB4C00231946A06890F87D2070303F +:102E9000EEF778F840B32069FBF7BEFA48B3206933 +:102EA000FBF7B4FA07462069FBF7B4FA0646206937 +:102EB000FBF7AAFA05462069FBF7AAFA0146009734 +:102EC000A06833462A463030FBF79BFBA1680125FA +:102ED00091F87C001C2810D091F85A00012812D0DB +:102EE00091F8250178B90BE0607840F0010060703E +:102EF000F8BDBDE8F840002013E781F85A5002E021 +:102F000091F8240118B11E2081F87D000BE01D20EE +:102F100081F87D0001F5A57231F8300BFBF7FBFB62 +:102F2000E078DFF7F1FFA068002120F8A41F85708A +:102F3000F8BD10B58E4C00230921A06890F87C20C4 +:102F40007030EEF71FF848B16078002805D1A1680D +:102F500001F8960F087301F81A0C10BD012060707B +:102F600010BD7CB5824C00230721A06890F87C201E +:102F70007030EEF707F838B36078002826D169463C +:102F80002069FBF75FFA9DF80000002500F025019D +:102F9000A06880F8B0109DF8011001F0490180F898 +:102FA000B11080F8A250D0F81811008849888142E9 +:102FB00000D0FFDFA068D0F818110D70D0F86411B0 +:102FC0000A7822B1FFDF16E0012060707CBD30F886 +:102FD000E82BCA80C16F0D71C16F009A8A60019A97 +:102FE000CA60C26F0821117030F8E81CC06F4180C0 +:102FF000E078DFF789FFA06880F87C507CBD70B571 +:103000005B4C00231946A06890F87D207030EDF7E6 +:10301000B9FF012540B9A0680023082190F87C2061 +:103020007030EDF7AFFF10B36078002820D1A068B2 +:1030300090F8AA00800712D42069FBF7C9F9A168AB +:1030400081F8AB00206930F8052FA1F8AC2040884A +:10305000A1F8AE0011F8AA0F40F002000870A068B5 +:103060004FF0000690F8AA10C90702D011E0657071 +:103070009DE490F87D20002319467030EDF782FF23 +:1030800000B9FFDFA06880F87D5080F8A650A0F856 +:10309000A460A06890F87C10012906D180F87C60BB +:1030A00080F8A260E078DFF72FFFA168D1F818015F +:1030B000098842888A42DBD101780429D8D1067078 +:1030C000E078DFF721FFA06890F87C100029CFD1CD +:1030D00080F8A2606BE470B5254DA86890F87C106C +:1030E0001A2902D00220687061E469780029FBD1B6 +:1030F000002480F8A74080F8A240D0F8181100887A +:103100004988814200D0FFDFA868D0F818110C7000 +:10311000D0F864110A780AB1FFDF25E090F8A82002 +:1031200072B180F8A8400288CA80D0F864110C718E +:10313000D0F864210E2111700188D0F864010DE0EF +:1031400030F8E82BCA80C16F0C71C26F0121117277 +:10315000C26F0D21117030F8E81CC06F418000F083 +:10316000A1FEE878DFF7D0FEA86880F87C401EE476 +:103170008C01002070B5FA4CA16891F87C20162AC9 +:1031800001D0132A02D191F8A82012B10220607058 +:103190000DE46278002AFBD181F8E000002581F877 +:1031A000A75081F8A250D1F81801098840888842B8 +:1031B00000D0FFDFA068D0F818010078032800D005 +:1031C000FFDF0321FE20FFF7F5FCA068D0F86411B3 +:1031D0000A780AB1FFDF14E030F8E02BCA8010F85B +:1031E000081BC26F1171C16F0D72C26F0D2111707A +:1031F00030F8E81CC06F418000F054FEE078DFF743 +:1032000083FEA06880F87C504BE470B5D44C092153 +:103210000023A06890F87C207030EDF7B3FE002505 +:1032200018B12069007912281ED0A0680A21002355 +:1032300090F87C207030EDF7A5FE18B12069007978 +:10324000142814D02069007916281AD1A06890F8A3 +:103250007C101F2915D180F87C5080F8A250BDE861 +:1032600070401A20FFF74EBABDE8704061E6A068D2 +:1032700000F87C5F458480F82650BDE87040FFF779 +:103280009BBB0EE470B5B64C2079C00773D02069A3 +:1032900000230521C578A06890F87C207030EDF7F8 +:1032A00071FE98B1062D11D006DC022D0ED0042D32 +:1032B0000CD0052D06D109E00B2D07D00D2D05D022 +:1032C000112D03D0607840F008006070607800280D +:1032D00051D12069FAF7E0FF00287ED0206900254F +:1032E0000226C178891E162977D2DFE801F00B7615 +:1032F00034374722764D76254A457676763A5350CE +:103300006A6D7073A0680023012190F87F207030EF +:10331000EDF738FE08BB2069FBF722F8A16881F8B9 +:103320001601072081F87F0081F8A65081F8A2508D +:1033300056E0FFF76AFF53E0A06890F87C100F2971 +:1033400001D066704CE0617839B980F88150122163 +:1033500080F87C1044E000F0D0FD41E000F0ACFDCE +:103360003EE0FBF7A9F803283AD12069FBF7A8F85B +:10337000FFF700FF34E03BE00079F9E7FFF7ABFE31 +:103380002EE0FFF73CFE2BE0FFF7EBFD28E0FFF718 +:10339000D0FD25E0A0680023194690F87D2070300C +:1033A000EDF7F0FD012110B16078C8B901E061705E +:1033B00016E0A06820F8A45F817000F8276C0FE089 +:1033C0000BE0FFF75DFD0BE000F034FD08E0FFF7D8 +:1033D000DFFC05E000F0FAFC02E00020FFF7A1FCB2 +:1033E000A268F2E93001401C41F10001C2E900018C +:1033F0005EE42DE9F0415A4C2079800741D5607890 +:1034000000283ED1E06801270026C178204619290E +:10341000856805F170006FD2DFE801F04B3E0D6F5B +:10342000C1C1801C34C1556287C1C1C1C1BE8B9569 +:1034300098A4B0C1BA0095F87F2000230121EDF7D0 +:10344000A1FD00281DD1A068082180F87F1080F818 +:10345000A26090E0002395F87D201946EDF792FDDB +:1034600010B1A06880F8A660A0680023194690F803 +:103470007C207030EDF786FD002802D0A06880F82F +:10348000A26067E4002395F87C201946EDF77AFDE9 +:1034900000B9FFDF042008E0002395F87C201946DE +:1034A000EDF770FD00B9FFDF0C20A16881F87C000A +:1034B00050E4002395F87C201946EDF763FD00B930 +:1034C000FFDF0D20F1E7002395F87C201946EDF78A +:1034D00059FD00B9FFDFA0680F2180F8A77008E050 +:1034E00095F87C00122800D0FFDFA068112180F839 +:1034F000A87080F87C102DE451E0002395F87C2022 +:103500001946EDF73FFD20B9A06890F8A80000B972 +:10351000FFDFA068132180F8A770EAE795F87C0028 +:10352000182800D0FFDF1A20BFE7BDE8F04100F007 +:1035300063BD002395F87C201946EDF723FD00B903 +:10354000FFDF0520B1E785F8A66003E4002395F8C6 +:103550007C201946EDF716FD00B9FFDF1C20A4E71B +:103560008C010020002395F87D201946EDF70AFD17 +:1035700000B9FFDFA06880F8A66006E4002395F894 +:103580007C201946EDF7FEFC00B9FFDF1F208CE719 +:10359000BDE8F04100F0F8BC85F87D60D3E7FFDFBF +:1035A0006FE710B5F74C6078002837D120794007D5 +:1035B0000FD5A06890F87C00032800D1FFDFA06839 +:1035C00090F87F10072904D101212170002180F893 +:1035D0007F10FFF70EFF00F0B5FCFFF753FEA07859 +:1035E000000716D5A0680023052190F87C207030D4 +:1035F000EDF7C8FC50B108206070A068D0F86411E5 +:1036000008780D2800D10020087002E00020F9F7AA +:10361000F9FCA068BDE81040FFF712BB10BD2DE912 +:10362000F041D84C07464FF00005607808436070C1 +:10363000207981062046806802D5A0F8985004E0E1 +:10364000B0F89810491CA0F8981000F018FD012659 +:10365000F8B1A088000506D5A06890F8821011B1D5 +:10366000A0F88E5015E0A068B0F88E10491CA0F8A4 +:103670008E1000F0F3FCA068B0F88E10B0F8902027 +:10368000914206D3A0F88E5080F83A61E078DFF7D7 +:103690003BFC207910F0600F08D0A06890F88010F3 +:1036A00021B980F880600121FEF782FD1FB9FFF784 +:1036B00078FFFFF799F93846FEF782FFBDE8F04141 +:1036C000F5F711BFAF4A51789378194313D11146DA +:1036D0000128896808D01079400703D591F87F0048 +:1036E000072808D001207047B1F84C00098E8842A5 +:1036F00001D8FEF7E4B900207047A249C278896872 +:10370000012A06D05AB1182A08D1B1F81011FAF7D7 +:10371000BABEB1F822114172090A81727047D1F81C +:10372000181189884173090A8173704770B5954CE7 +:1037300005460E46A0882843A080A80703D5E807C1 +:1037400000D0FFDFE660E80700D02661A80719D5A2 +:10375000F078062802D00B2814D10BE0A06890F86E +:103760007C1018290ED10021E0E93011012100F868 +:103770003E1C07E0A06890F87C10122902D10021BD +:1037800080F88210280601D50820A07068050AD5A7 +:10379000A0688288B0F87010304600F07FFC304698 +:1037A000BDE87040A9E763E43EB505466846F5F715 +:1037B00065FE00B9FFDF222200210098EAF767FECC +:1037C00003210098FAF750FD0098017821F01001CC +:1037D00001702946FAF76DFD6A4C192D71D2DFE8A8 +:1037E00005F020180D3EC8C8C91266C8C9C959C815 +:1037F000C8C8C8BBC9C971718AC89300A1680098BC +:1038000091F8151103E0A168009891F8E610017194 +:10381000B0E0A068D0F81C110098491CFAF795FD9B +:10382000A8E0A1680098D1F8182192790271D1F826 +:10383000182112894271120A8271D1F81821528915 +:10384000C271120A0272D1F8182192894272120AC8 +:103850008272D1F81811C989FAF74EFD8AE0A06882 +:10386000D0F818110098091DFAF77CFDA068D0F86F +:10387000181100980C31FAF77FFDA068D0F81811E4 +:1038800000981E31FAF77EFDA1680098D831FAF74A +:1038900087FD6FE06269009811780171918841712C +:1038A000090A81715188C171090A017262E03649C1 +:1038B000D1E90001CDE9010101A90098FAF78AFDDB +:1038C00058E056E0A068B0F844100098FAF794FD6C +:1038D000A068B0F8E6100098FAF792FDA068B0F87A +:1038E00048100098FAF780FDA068B0F8E81000983A +:1038F000FAF77EFD3EE0A168009891F83021027150 +:1039000091F83111417135E0A06890F81301EDF79D +:1039100032FD01460098FAF7B2FDA06890F8120156 +:1039200000F03DFA70B1A06890F8640000F037FA3A +:1039300040B1A06890F8121190F86400814201D063 +:10394000002002E0A06890F81201EDF714FD014696 +:103950000098FAF790FD0DE0A06890F80D1100981E +:10396000FAF7A8FDA06890F80C110098FAF7A6FDE8 +:1039700000E0FFDFF5F795FD00B9FFDF0098FFF7E6 +:10398000BCFE3EBD8C0100207C63020010B5F94CEA +:10399000A06890F8121109B990F8641080F86410CA +:1039A00090F8131109B990F8651080F8651000209F +:1039B000FEF7A8FEA068FAF750FE002806D0A0681F +:1039C000BDE8104000F59E71FAF7B1BE10BDF8B524 +:1039D000E84E00250446B060B5807570B57035704E +:1039E0000088F5F748FDB0680088F5F76AFDB4F87F +:1039F000F800B168401C82B201F17000EDF75BF88D +:103A000000B1FFDF94F87D00242809D1B4F87010CC +:103A1000B4F81001081A00B2002801DB707830B148 +:103A200094F87C0024280AD0252808D015E0FFF758 +:103A3000ADFF84F87D50B16881F897500DE0B4F87F +:103A40007010B4F81001081A00B2002805DB707875 +:103A500018B9FFF79BFF84F87C50A4F8F850FEF7E4 +:103A600088FD00281CD1B06890F8E400FE2801D041 +:103A7000FFF79AFEC0480090C04BC14A2146284635 +:103A8000F9F7ECF9B0680023052190F87C2070303C +:103A9000EDF778FA002803D0BDE8F840F8F771BFD9 +:103AA000F8BD10B5FEF765FD20B10020BDE810405F +:103AB0000146B4E5BDE81040F9F77FBA70B50C4691 +:103AC000154606464FF4B47200212046EAF7DFFCA3 +:103AD000268005B9FFDF2868C4F818016868C4F8B3 +:103AE0001C01A868C4F8640182E4F0F7E9BA2DE982 +:103AF000F0410D4607460621F0F7D8F9041E3DD0E7 +:103B0000D4F864110026087858B14A8821888A427E +:103B100007D109280FD00E2819D00D2826D0082843 +:103B20003ED094F83A01D0B36E701020287084F81B +:103B30003A61AF809AE06E7009202870D4F8640171 +:103B4000416869608168A9608089A88133E008467E +:103B5000F0F7DDFA0746EFF78AFF70B96E700E20B6 +:103B60002870D4F864014068686011E00846F0F7F6 +:103B7000CEFA0746EFF77BFF08B1002081E46E70B4 +:103B80000D202870D4F8640141686960008928819B +:103B9000D4F8640106703846EFF763FF66E00EE084 +:103BA0006E7008202870D4F86401416869608168EB +:103BB000A960C068E860D4F86401067056E094F823 +:103BC0003C0198B16E70152028700AE084F83C61C1 +:103BD000D4F83E016860D4F84201A860D4F84601E8 +:103BE000E86094F83C010028F0D13FE094F84A01E5 +:103BF00058B16E701C20287084F84A610A2204F5BE +:103C0000A671281DEAF719FC30E094F8560140B17E +:103C10006E701D20287084F85661D4F858016860D1 +:103C200024E094F8340168B16E701A20287004E022 +:103C300084F83461D4F83601686094F834010028BF +:103C4000F6D113E094F85C01002897D06E7016202E +:103C5000287007E084F85C61D4F85E016860B4F80D +:103C60006201288194F85C010028F3D1012008E466 +:103C7000404A5061D170704770B50D4604464EE021 +:103C8000B4F8F800401CA4F8F800B4F89800401C00 +:103C9000A4F89800204600F0F2F9B8B1B4F88E000C +:103CA000401CA4F88E00204600F0D8F9B4F88E002D +:103CB000B4F89010884209D30020A4F88E000120A7 +:103CC00084F83A012B48C078DFF71EF994F8A20077 +:103CD00020B1B4F89E00401CA4F89E0094F8A60001 +:103CE00020B1B4F8A400401CA4F8A40094F8140176 +:103CF00040B994F87F200023012104F17000EDF712 +:103D000041F920B1B4F89C00401CA4F89C00204666 +:103D1000FEF796FFB4F87000401CA4F870006D1E0A +:103D2000ADB2ADD23FE5134AC2E90601704770B5A6 +:103D30000446B0F8980094F88010D1B1B4F89A1005 +:103D40000D1A2D1F94F8960040B194F87C200023A2 +:103D5000092104F17000EDF715F9B8B1B4F88E60DF +:103D6000204600F08CF980B1B4F89000801B001F51 +:103D70000CE007E08C0100201F360200C53602006F +:103D80002D370200C0F10205DCE72846A84200DA20 +:103D90000546002D01DC002005E5A8B203E510F082 +:103DA0000C0000D00120704710B5012808D002286F +:103DB00008D0042808D0082806D0FFDF204610BD10 +:103DC0000124FBE70224F9E70324F7E770B5CC4CA4 +:103DD000A06890F87C001F2804D0607840F00100B3 +:103DE0006070E0E42069FAF73CFBD8B12069012259 +:103DF0000179407901F0070161F30705294600F0D8 +:103E0000070060F30F21A06880F8A2200022A0F82C +:103E10009E20232200F87C2FD0F8B400BDE870402B +:103E2000FEF7AABD0120FEF77CFFBDE870401E2012 +:103E3000FEF768BCF8B5B24C00230A21A06890F8E0 +:103E40007C207030EDF79EF838B32069FAF7E4FA79 +:103E5000C8B12069FAF7DAFA07462069FAF7DAFA00 +:103E600006462069FAF7D0FA05462069FAF7D0FA33 +:103E700001460097A06833462A463030FAF7C1FB66 +:103E8000A068FAF7EAFBA168002081F8A20081F897 +:103E90007C00BDE8F840FEF78FBD607840F001007F +:103EA0006070F8BD964810B580680088F0F72FF96B +:103EB000BDE81040EFF7C6BD10B5914CA36893F86C +:103EC0007C00162802D00220607010BD60780028A7 +:103ED000FBD1D3F81801002200F11E010E30C833C7 +:103EE000ECF7C2FFA0680021C0E92E11012180F883 +:103EF0008110182180F87C1010BD10B5804CA0688E +:103F000090F87C10132902D00220607010BD6178F7 +:103F10000029FBD1D0F8181100884988814200D0CF +:103F2000FFDFA068D0F8181120692631FAF745FAAA +:103F3000A1682069DC31FAF748FAA168162081F8F7 +:103F40007C0010BD10B56E4C207900071BD5607841 +:103F5000002818D1A068002190F8E400FEF72AFE9E +:103F6000A06890F8E400FE2800D1FFDFA068FE21E1 +:103F700080F8E41090F87F10082904D10221217004 +:103F8000002180F87F1010BD70B55D4D2421002404 +:103F9000A86890F87D20212A05D090F87C20232A5B +:103FA00018D0FFDFA0E590F8122112B990F8132184 +:103FB0002AB180F87D10A86880F8A64094E500F842 +:103FC0007D4F847690F8B1000028F4D00020FEF7F1 +:103FD00099FBF0E790F8122112B990F813212AB159 +:103FE00080F87C10A86880F8A2407DE580F87C40CD +:103FF0000020FEF787FBF5E770B5414C0025A0686F +:10400000D0F8181103884A889A4219D109780429EE +:1040100016D190F87C20002319467030ECF7B2FFDF +:1040200000B9FFDFA06890F8AA10890703D4012126 +:1040300080F87C1004E080F8A250D0F818010570D8 +:10404000A0680023194690F87D207030ECF79AFFA5 +:10405000002802D0A06880F8A65045E5B0F890206E +:10406000B0F88E108A4201D3511A00E000218288F4 +:10407000521D8A4202D3012180F89610704710B574 +:1040800090F8821041B990F87C200023062170300E +:10409000ECF778FF002800D0012010BD70B5114466 +:1040A000174D891D8CB2C078A968012806D040B18F +:1040B000182805D191F8120138B109E0A1F8224180 +:1040C00012E5D1F8180184800EE591F8131191B131 +:1040D000FFF765FE80B1A86890F86400FFF75FFE07 +:1040E00050B1A86890F8121190F86420914203D062 +:1040F00090F8130100B90024A868A0F81041F3E477 +:104100008C01002070B58F4C0829207A6CD2DFE832 +:1041100001F004176464276B6B6458B1F4F7D3F8AB +:10412000F5F7FFF80020A072F4F7B4F9BDE870408D +:10413000F4F758BCF5F717F9BDE87040F1F71FBF69 +:10414000DEF7B6FDF5F752F8D4E90001F1F7F3FC1C +:104150002060A07A401CC0B2A072282824D370BD71 +:10416000A07A0025401EC6B2E0683044F4F700FD96 +:1041700010B9E1687F208855A07A272828BF01253B +:10418000DEF796FDA17A01EB4102C2EB81110844F2 +:104190002946F5F755F8A07A282809D2401CC0B264 +:1041A000A072282828BF70BDBDE87040F4F772B92E +:1041B000207A002818BF00F086F8F4F74BFBF4F7DC +:1041C000F7FBF5F7D0F80120E0725F480078DEF7E2 +:1041D0009BFEBDE87040F1F7D2BE002808BF70BD5D +:1041E000BDE8704000F06FB8FFDF70BD10B5554CF2 +:1041F000207A002804BF0C2010BD00202072E0723D +:10420000607AF2F7F8FA607AF2F761FD607AF1F716 +:104210008CFF00280CBF1F20002010BD002270B5AD +:10422000484C06460D46207A68B12272E272607AE6 +:10423000F2F7E1FA607AF2F74AFD607AF1F775FF7A +:10424000002808BFFFDF4048E560067070BD70B50C +:10425000050007D0A5F5E8503C494C3881429CBF89 +:10426000122070BD374CE068002804BF092070BDE3 +:10427000207A00281CBF0C2070BD3548F1F7FAFEEB +:104280006072202804BF1F2070BDF1F76DFF206011 +:104290001DB12946F1F74FFC2060012065602072B6 +:1042A00000F011F8002070BD2649CA7A002A04BF28 +:1042B000002070471E22027000224270CB684360CB +:1042C000CA7201207047F0B585B0F1F74DFF1D4D62 +:1042D0000746394668682C6800EB80004600204697 +:1042E000F2F73AFCB04206DB6868811B3846F1F70A +:1042F00022FC0446286040F2367621463846F2F722 +:104300002BFCB04204DA31463846F1F714FC04467F +:1043100000208DF8000040F6E210039004208DF894 +:10432000050001208DF8040068460294F2F7CAF8EF +:10433000687A6946F2F743F9002808BFFFDF05B045 +:10434000F0BD000074120020AC010020B5EB3C0071 +:10435000054102002DE9F0410C4612490D68114A51 +:10436000114908321160A0F12001312901D3012047 +:104370000CE0412810D040CC0C4F94E80E0007EB25 +:104380008000241F50F8807C3046B84720600548E4 +:10439000001D0560BDE8F081204601F0EBFCF5E76B +:1043A00006207047100502400100000184630200EE +:1043B00010B55548F2F7FAFF00B1FFDF5248401C34 +:1043C000F2F7F4FF002800D0FFDF10BD2DE9F14F18 +:1043D0004E4E82B0D6F800B001274B48F2F7EEFF00 +:1043E000DFF8248120B9002708F10100F2F7FCFF73 +:1043F000474C00254FF0030901206060C4F80051CC +:10440000C4F80451029931602060DFF808A11BE074 +:10441000DAF80000C00617D50E2000F068F8EFF3B8 +:10442000108010F0010072B600D001200090C4F896 +:104430000493D4F8000120B9D4F8040108B901F0BC +:10444000A3FC009800B962B6D4F8000118B9D4F8FA +:1044500004010028DCD0D4F804010028CCD137B105 +:10446000C6F800B008F10100F2F7A8FF11E008F16A +:104470000100F2F7A3FF0028B6D1C4F80893C4F8EE +:104480000451C4F800510E2000F031F81E48F2F734 +:10449000ABFF0020BDE8FE8F2DE9F0438DB00D4647 +:1044A000064600240DF110090DF1200818E000BFA8 +:1044B00004EB4407102255F827106846E9F7BDFFC2 +:1044C00005EB8707102248467968E9F7B6FF68468A +:1044D000FFF77CFF10224146B868E9F7AEFF641C85 +:1044E000B442E5DB0DB00020BDE8F0836EE70028A4 +:1044F00009DB00F01F02012191404009800000F11A +:10450000E020C0F880127047AD01002004E50040B3 +:1045100000E0004010ED00E0B54900200870704751 +:1045200070B5B44D01232B60B34B1C68002CFCD03C +:10453000002407E00E6806601E68002EFCD0001DF7 +:10454000091D641C9442F5D30020286018680028D7 +:10455000FCD070BD70B5A64E0446A84D3078022838 +:1045600000D0FFDFAC4200D3FFDF7169A44801290E +:1045700003D847F23052944201DD03224271491CB4 +:104580007161291BC1609E49707800F02EF90028E6 +:1045900000D1FFDF70BD70B5954C0D466178884243 +:1045A00000D0FFDF954E082D4BD2DFE805F04A041E +:1045B0001E2D4A4A4A382078022800D0FFDF032007 +:1045C0002070A078012801D020B108E0A06801F097 +:1045D00085F904E004F1080007C8FFF7A1FF0520F2 +:1045E0002070BDE87040F1F7CABCF1F7BDFD01468F +:1045F0006068F2F7B1FAB04202D2616902290BD3C6 +:104600000320F2F7FAFD12E0F1F7AEFD0146606813 +:10461000F2F7A2FAB042F3D2BDE870409AE72078F0 +:1046200002280AD0052806D0FFDF04202070BDE84C +:10463000704000F0D0B8022000E00320F2F7DDFD6A +:10464000F3E7FFDF70BD70B50546F1F78DFD684CEF +:1046500060602078012800D0FFDF694901200870E0 +:104660000020087104208D6048716448C8600220F1 +:104670002070607800F0B9F8002800D1FFDF70BD2D +:1046800010B55B4C207838B90220F2F7CCFD18B990 +:104690000320F2F7C8FD08B1112010BD5948F1F709 +:1046A000E9FC6070202804D00120207000206061A7 +:1046B00010BD032010BD2DE9F0471446054600EB60 +:1046C00084000E46A0F1040801F01BF907464FF0E4 +:1046D000805001694F4306EB8401091FB14201D2AA +:1046E000012100E0002189461CB10069B4EB900F64 +:1046F00002D90920BDE8F0872846DCF7A3FD90B970 +:10470000A84510D3BD4205D2B84503D245EA0600FC +:10471000800701D01020EDE73046DCF793FD10B99B +:10472000B9F1000F01D00F20E4E73748374900689E +:10473000884205D0224631462846FFF7F1FE1AE0AE +:10474000FFF79EFF0028D5D1294800218560C0E9E8 +:1047500003648170F2F7D3FD08B12D4801E04AF2FD +:10476000F87060434FF47A7100F2E730B0FBF1F07B +:104770001830FFF768FF0020BCE770B505464FF022 +:10478000805004696C432046DCF75CFD08B10F20C3 +:1047900070BD01F0B6F8A84201D8102070BD1A48CB +:1047A0001A490068884203D0204601F097F810E0CB +:1047B000FFF766FF0028F1D10D4801218460817068 +:1047C000F2F79DFD08B1134800E013481830FFF7D9 +:1047D0003AFF002070BD10B5054C6078F1F7A5FCDC +:1047E00000B9FFDF0020207010BDF1F7E8BE000027 +:1047F000B001002004E5014000E40140105C0C0021 +:1048000084120020974502005C000020BEBAFECA58 +:1048100050280500645E0100A85B01007E4909681C +:104820000160002070477C4908600020704701212A +:104830008A0720B1012804D042F204007047916732 +:1048400000E0D1670020704774490120086042F2FF +:104850000600704708B50423704A1907103230B1BA +:10486000C1F80433106840F0010010600BE01068DC +:1048700020F001001060C1F808330020C1F80801E1 +:10488000674800680090002008BD011F0B2909D867 +:10489000624910310A6822F01E0242EA40000860B4 +:1048A0000020704742F2050070470F2809D85B4985 +:1048B00010310A6822F4706242EA00200860002089 +:1048C000704742F205007047000100F18040C0F8D7 +:1048D000041900207047000100F18040C0F8081959 +:1048E00000207047000100F18040D0F80009086006 +:1048F00000207047012801D907207047494A52F823 +:10490000200002680A43026000207047012801D994 +:1049100007207047434A52F8200002688A43026029 +:1049200000207047012801D9072070473D4A52F8FE +:104930002000006808600020704702003A494FF0EC +:10494000000003D0012A01D0072070470A60704799 +:10495000020036494FF0000003D0012A01D00720A1 +:1049600070470A60704708B54FF40072510510B1E6 +:10497000C1F8042308E0C1F808230020C1F824018D +:1049800027481C3000680090002008BD08B5802230 +:10499000D10510B1C1F8042308E0C1F808230020B4 +:1049A000C1F81C011E48143000680090002008BDAA +:1049B00008B54FF48072910510B1C1F8042308E0E6 +:1049C000C1F808230020C1F82001154818300068FC +:1049D0000090002008BD10493831096801600020AE +:1049E000704770B54FF080450024C5F80841F2F7D4 +:1049F00092FC10B9F2F799FC28B1C5F82441C5F82A +:104A00001C41C5F820414FF0E020802180F80014BF +:104A10000121C0F8001170BD0004004000050040F5 +:104A2000080100404864020078050040800500400D +:104A30006249634B0A6863499A42096801D1C1F32C +:104A400010010160002070475C495D4B0A685D49B8 +:104A5000091D9A4201D1C0F3100008600020704780 +:104A60005649574B0A68574908319A4201D1C0F359 +:104A7000100008600020704730B5504B504D1C6846 +:104A800042F20803AC4202D0142802D203E01128FB +:104A900001D3184630BDC3004B481844C0F8101568 +:104AA000C0F81425002030BD4449454B0A6842F245 +:104AB00009019A4202D0062802D203E0042801D359 +:104AC00008467047404A012142F8301000207047E4 +:104AD0003A493B4B0A6842F209019A4202D0062841 +:104AE00002D203E0042801D308467047364A012168 +:104AF00002EBC00041600020704770B52F4A304E75 +:104B0000314C156842F2090304EB8002B54204D02F +:104B1000062804D2C2F8001807E0042801D318467A +:104B200070BDC1F31000C2F80008002070BD70B560 +:104B3000224A234E244C156842F2090304EB8002FA +:104B4000B54204D0062804D2D2F8000807E00428B1 +:104B500001D3184670BDD2F80008C0F310000860F9 +:104B6000002070BD174910B50831184808601120A1 +:104B7000154A002102EBC003C3F81015C3F8141541 +:104B8000401C1428F6D3002006E0042804D302EBCE +:104B90008003C3F8001807E002EB8003D3F8004855 +:104BA000C4F31004C3F80048401C0628EDD310BD20 +:104BB0000449064808310860704700005C00002086 +:104BC000BEBAFECA00F5014000F001400000FEFF41 +:104BD000814B1B6803B19847BFF34F8F7F48016833 +:104BE0007F4A01F4E06111430160BFF34F8F00BFC2 +:104BF000FDE710B5EFF3108010F0010F72B601D091 +:104C0000012400E0002400F0DDF850B1DCF7BFFB28 +:104C1000F1F755F8F2F793FAF2F7BEFF7149002069 +:104C2000086004B962B6002010BD2DE9F0410C46C1 +:104C30000546EFF3108010F0010F72B601D0012687 +:104C400000E0002600F0BEF820B106B962B60820E8 +:104C5000BDE8F08101F01EF9DCF79DFB0246002063 +:104C600001234709BF0007F1E02700F01F01D7F833 +:104C70000071CF40F9071BD0202803D222FA00F19F +:104C8000C90727D141B2002904DB01F1E02191F8E5 +:104C9000001405E001F00F0101F1E02191F8141D6D +:104CA0004909082916D203FA01F717F0EC0F11D0C1 +:104CB000401C6428D5D3F2F74DFF4B4A4B490020E6 +:104CC000F2F790FF47494A4808602046DCF7C1FAEE +:104CD00060B904E006B962B641F20100B8E73E48A7 +:104CE00004602DB12846DCF701FB18B1102428E040 +:104CF000404D19E02878022802D94FF4805420E072 +:104D000007240028687801D0D8B908E0C8B1202865 +:104D100017D8A878212814D8012812D001E0A87843 +:104D200078B9E8780B280CD8DCF735FB2946F2F780 +:104D3000ECF9F0F783FF00F017FE2846DCF7F4FAF1 +:104D4000044606B962B61CB1FFF753FF20467FE761 +:104D500000207DE710B5044600F034F800B10120D2 +:104D60002070002010BD244908600020704770B5F5 +:104D70000C4622490D682149214E08310E60102849 +:104D800007D011280CD012280FD0132811D00120E1 +:104D900013E0D4E90001FFF748FF354620600DE03D +:104DA000FFF727FF0025206008E02068FFF7D2FF0B +:104DB00003E0114920680860002020600F48001DB2 +:104DC000056070BD07480A490068884201D101208A +:104DD0007047002070470000C80100200CED00E083 +:104DE0000400FA055C0000204814002000000020A8 +:104DF000BEBAFECA50640200040000201005024042 +:104E0000010000017D49C0B20860704700B57C49CF +:104E1000012808BF03200CD0022808BF042008D0B6 +:104E2000042808BF062004D0082816BFFFDF05208D +:104E300000BD086000BD70B505460C46164610461C +:104E4000F3F7D2F8022C08BF4FF47A7105D0012C89 +:104E50000CBF4FF4C86140F6340144183046F3F7F4 +:104E60003CF9204449F6797108444FF47A71B0FB5B +:104E7000F1F0281A70BD70B505460C460846F4F7E7 +:104E80002FFA022C08BF40F24C4105D0012C0CBF78 +:104E900040F634014FF4AF5149F6CA62511A084442 +:104EA0004FF47A7100F2E140B0FBF1F0281A801E55 +:104EB00070BD70B5064615460C460846F4F710FA64 +:104EC000022D08BF4FF47A7105D0012D0CBF4FF4AD +:104ED000C86140F63401022C08BF40F24C4205D0B4 +:104EE000012C0CBF40F634024FF4AF52891A08442B +:104EF00049F6FC6108444FF47A71B0FBF1F0301AC6 +:104F000070BD70B504460E460846F3F76DF80546C9 +:104F10003046F3F7E2F828444AF2AB3108444FF444 +:104F20007A71B0FBF1F0201A801E70BD2DE9F041BE +:104F300007461E460D4614461046082A16BF04288A +:104F40004EF62830F3F750F807EB4701C1EBC711D5 +:104F500000EBC100022D08BF40F24C4105D0012DED +:104F60000CBF40F634014FF4AF5147182846F4F710 +:104F7000B7F9381A4FF47A7100F6B730B0FBF1F593 +:104F80002046F3F7B9F828443044401DBDE8F081CD +:104F900070B5054614460E460846F3F725F805EBAE +:104FA0004502C2EBC512C0EBC2053046F3F795F8D7 +:104FB0002D1A2046082C16BF04284EF62830F3F789 +:104FC00013F828444FF47A7100F6B730B0FBF1F5CE +:104FD0002046F3F791F82844401D70BD0949082880 +:104FE00018BF0428086803BF20F46C5040F4444004 +:104FF00040F0004020F00040086070470C15004071 +:105000001015004040170040F0B585B00C4605462D +:10501000F9F73EF907466E78204603A96A46EEF78F +:1050200002FD81198EB258B1012F02D0032005B0C4 +:10503000F0BD204604AA0399EEF717FC049D01E099 +:10504000022F0FD1ED1C042E0FD32888BDF80010BD +:10505000001D80B2884201D8864202D14FF0000084 +:10506000E5E702D34FF00200E1E74FF00100DEE791 +:10507000FA48C078FF2814BF0120002070472DE9AE +:10508000F041F74C0746160060680D4603D0F9F76B +:1050900069F8A0B121E0F9F765F8D8B96068F9F7C7 +:1050A00061F8D0B915F00C0F17D06068C17811F015 +:1050B0003F0F1CBF007910F0100F0ED00AE0022E37 +:1050C00008D0E6481FB1807DFF2806D002E0C078F6 +:1050D000FF2802D00120BDE8F0810020BDE8F0816A +:1050E0000A4601460120CAE710B5DC4C1D2200210A +:1050F000A01CE9F7CCF97F206077FF202074E070D6 +:10510000A075A08920F060002030A08100202070D0 +:1051100010BD70B5D249486001200870D248D1490D +:10512000002541600570CD4C1D222946A01CE9F7E1 +:10513000AEF97F206077FF202074E070A075A08911 +:1051400020F060002030A081257070BD2DE9F0476F +:10515000C24C06462078C24F4FF0010907F10808FB +:10516000002520B13878D0B998F80000B8B198F887 +:10517000000068B387F80090D8F804103C2239B3D7 +:105180007570301DE9F759F90520307086F80490E4 +:105190003878002818BF88F8005005D015E03D7019 +:1051A000A11C4FF48E72EAE71D220021A01CE9F732 +:1051B0006EF97F206077FF202074E070A075A089D1 +:1051C00020F060002030A08125700120BDE8F0872C +:1051D0000020BDE8F087A148007800280CBF01201E +:1051E000002070470A460146002048E710B510B17C +:1051F000022810D014E09A4C6068F8F7B3FF78B931 +:105200006068C17811F03F0F1CBF007910F0100FDB +:1052100006D1012010BD9148007B10F0080FF8D195 +:10522000002010BD2DE9FF4F81B08C4D8346DDE994 +:105230000F042978DDF838A09846164600291CBFCF +:1052400005B0BDE8F08F8849097800291CBF05B07A +:10525000BDE8F08FE872B4B1012E08BF012708D075 +:10526000022E08BF022704D0042E16BF082E0327E3 +:10527000FFDFEF7385F81E804FF00008784F8CB188 +:10528000022C1DD020E0012E08BF012708D0022EDD +:1052900008BF022704D0042E16BF082E0327FFDF05 +:1052A000AF73E7E77868F8F75DFF68B97868C178A9 +:1052B00011F03F0F1CBF007910F0100F04D110E067 +:1052C000287B10F0080F0CD14FF003017868F8F735 +:1052D000FDFD30B14178090929740088C0F30B0045 +:1052E0006882CDF800807868F8F73CFF0146012815 +:1052F000BDF8000005F102090CBF40F0010020F0EC +:105300000100ADF8000099F80A2012F0020F4ED10A +:10531000022918BF20F0020049D000BFADF80000FC +:1053200010F0020F04D0002908BF40F0080801D097 +:1053300020F00808ADF800807868C17811F03F0FC0 +:105340001CBF007910F0020F0CD0314622464FF0FE +:105350000100FFF794FE002804BF48F00400ADF8F8 +:10536000000006D099F80A00800860F38208ADF8C2 +:10537000008099F80A004109BDF8000061F3461069 +:10538000ADF8000080B20090BDF80000A8810421B3 +:105390007868F8F79BFD002804BFA88920F060001A +:1053A0000CD0B0F80100C004C00C03D007E040F0FE +:1053B0000200B3E7A88920F060004030A8815CB902 +:1053C00016F00C0F08D07868C17811F03F0F1CBFA1 +:1053D000007910F0100F0DD17868C17811F03F0FEF +:1053E00008D0017911F0400F04D00621F8F76EFDC6 +:1053F00000786877314622460020FFF740FE60BB08 +:105400007968C87810F03F0F3FD0087910F0010F8D +:105410003BD0504605F1040905F10308BAF1FF0F2E +:105420000DD04A464146F8F781FA002808BFFFDF51 +:1054300098F8000040F0020088F8000025E00846D7 +:10544000F8F7DBFC88F800007868F8F7ADFC07286F +:105450000CD249467868F8F7B2FC16E094120020A6 +:10546000CC010020D2120020D40100207868F8F787 +:105470009BFC072809D100217868F8F727FD01680F +:10548000C9F800108088A9F804003146224601209E +:10549000FFF7F5FD80BB7868C17811F03F0F2BD086 +:1054A000017911F0020F27D005F1170605F1160852 +:1054B000BBF1020F18BFBBF1030F08D0F8F774FC63 +:1054C00007280AD231467868F8F787FC12E002987C +:1054D000016831608088B0800CE07868F8F764FC7F +:1054E000072807D101217868F8F7F0FC01683160DE +:1054F0008088B08088F800B0002C04BF05B0BDE8FB +:10550000F08F7868F8F72EFE022804BF05B0BDE8DA +:10551000F08F05F11F047868F8F76DFEAB7AC3F1E0 +:10552000FF01884228BF084605D9A98921F06001FA +:1055300001F14001A981C2B203EB04017868F8F7D8 +:1055400062FEA97A0844A87205B0BDE8F08FB048A1 +:105550000178002918BF704701220270007B10F00B +:10556000080F14BF07200620FCF75FBEA848C17BC8 +:10557000002908BF70470122818921F06001403174 +:1055800081810378002B18BF7047027011F0080F5B +:1055900014BF07200620FCF748BE2DE9FF5F9C4F93 +:1055A000DDF838B0914638780E4600281CBF04B0AC +:1055B000BDE8F09FBC1C1D2200212046E8F767FFD4 +:1055C000944D4FF0010A84F800A06868F8F7ECFBEE +:1055D00018B3012826D0022829D0062818BFFFDFDB +:1055E0002AD000BF04F11D016868F8F726FC20727C +:1055F000484604F1020904F10108FF2821D04A4677 +:105600004146F8F793F9002808BFFFDF98F800003B +:1056100040F0020088F8000031E0608940F013009B +:105620006081DFE7608940F015006081E0E7608914 +:1056300040F010006081D5E7608940F01200608181 +:10564000D0E76868F8F7D9FB88F800006868F8F7D1 +:10565000ABFB072804D249466868F8F7B0FB0EE0B8 +:105660006868F8F7A1FB072809D100216868F8F7F6 +:105670002DFC0168C9F800108088A9F8040084F89E +:1056800009B084F80CA000206073FF20A073A17AF9 +:1056900011F0040F08BF20752AD004F1150804F199 +:1056A0001409022E18BF032E09D06868F8F77CFB96 +:1056B00007280CD241466868F8F78FFB16E000987F +:1056C0000168C8F800108088A8F804000EE0686837 +:1056D000F8F76AFB072809D101216868F8F7F6FB9B +:1056E0000168C8F800108088A8F8040089F80060F4 +:1056F0007F20E0760398207787F800A004B006208A +:10570000BDE8F05FFCF791BD2DE9FF5F424F814698 +:105710009A4638788B4600281CBF04B0BDE8F09F3D +:105720003B48017831B1007B10F0100F04BF04B08A +:10573000BDE8F09F1D227C6800212046E8F7A7FE07 +:1057400048464FF00108661C324D84F8008004F191 +:105750000209FF280BD04A463146F8F7E7F800283F +:1057600008BFFFDF307840F0020030701CE068684E +:10577000F8F743FB30706868F8F716FB072804D287 +:1057800049466868F8F71BFB0EE06868F8F70CFB01 +:10579000072809D100216868F8F798FB0168C9F863 +:1057A00000108088A9F8040004F11D016868F8F76A +:1057B00044FB207284F809A060896BF3000040F07C +:1057C0001A00608184F80C8000206073FF20A073B1 +:1057D00020757F20E0760298207787F8008004B05B +:1057E0000720BDE8F05FFCF720BD094A137C834227 +:1057F00005BF508A88420020012070470448007B82 +:10580000C0F3411002280CBF0120002070470000A7 +:1058100094120020CC010020D4010020C2790D2375 +:1058200041B342BB8188012904D94908818004BF62 +:10583000012282800168012918BF002930D0016847 +:105840006FEA0101C1EBC10202EB011281796FEA3B +:10585000010101EB8103C3EB811111444FEA914235 +:1058600001608188B2FBF1F301FB132181714FF0DC +:10587000010102E01AB14FF00001C1717047818847 +:10588000FF2908D24FF6FF7202EA41018180FF2909 +:1058900084BFFF2282800168012918BF0029CED170 +:1058A0000360CCE7817931B1491E11F0FF018171AC +:1058B0001CBF002070470120704710B50121C17145 +:1058C0008171818004460421F1F7E8FD002818BFAA +:1058D00010BD2068401C206010BD00000B4A022152 +:1058E00011600B490B68002BFCD0084B1B1D186086 +:1058F00008680028FCD00020106008680028FCD050 +:1059000070474FF0805040697047000004E5014047 +:1059100000E4014002000B464FF00000014620D099 +:10592000012A04D0022A04D0032A0DD103E0012069 +:1059300002E0022015E00320072B05D2DFE803F088 +:105940000406080A0C0E100007207047012108E029 +:10595000022106E0032104E0042102E0052100E029 +:105960000621F0F7A4BB0000E24805218170002168 +:10597000017041707047E0490A78012A05D0CA6871 +:105980001044C8604038F1F7B4B88A6810448860A1 +:10599000F8E7002819D00378D849D94A13B1012B68 +:1059A0000ED011E00379012B00D06BB943790BB114 +:1059B000012B09D18368643B8B4205D2C0680EE09D +:1059C0000379012B02D00BB10020704743790BB152 +:1059D000012BF9D1C368643B8B42F5D280689042B9 +:1059E000F2D801207047C44901220A70027972B1CD +:1059F00000220A71427962B104224A7182685232ED +:105A00008A60C068C860BB49022088707047032262 +:105A1000EFE70322F1E770B5B74D04460020287088 +:105A2000207988B100202871607978B10420B14EC6 +:105A30006871A168F068F0F77EF8A860E0685230FD +:105A4000E8600320B07070BD0120ECE70320EEE7B2 +:105A50002DE9F04105460226F0F777FF006800B116 +:105A6000FFDFA44C01273DB12878B8B1012805D04B +:105A7000022811D0032814D027710DE06868C828C7 +:105A800008D30421F1F79BF820B16868FFF773FF92 +:105A9000012603E0002601E000F014F93046BDE8DD +:105AA000F08120780028F7D16868FFF772FF00289E +:105AB000E2D06868017879B1A078042800D0FFDFCF +:105AC00001216868FFF7A7FF8B49E07800F003F930 +:105AD0000028E1D1FFDFDFE7FFF785FF6770DBE735 +:105AE0002DE9F041834C0F46E178884200D0FFDF7A +:105AF00000250126082F7DD2DFE807F0040B2828B7 +:105B00003D434F57A0780328C9D00228C7D0FFDFF4 +:105B1000C5E7A078032802D0022800D0FFDF0420C8 +:105B2000A07025712078B8BB0020FFF724FF7248D1 +:105B30000178012906D08068E06000F0EDF820616E +:105B4000002023E0E078F0F734FCF5E7A0780328A4 +:105B500002D0022800D0FFDF207880BB022F08D0BF +:105B60005FF00500F1F749FBA078032840D0A5704D +:105B700095E70420F6E7A078042800D0FFDF022094 +:105B800004E0A078042800D0FFDF0120A168884746 +:105B9000FFF75EFF054633E003E0A078042800D05D +:105BA000FFDFBDE8F04100F08DB8A078042804D0F4 +:105BB000617809B1022800D0FFDF207818B1BDE874 +:105BC000F04100F08AB8207920B10620F1F715FBEA +:105BD00025710DE0607840B14749E07800F07BF82E +:105BE00000B9FFDF65705AE704E00720F1F705FB15 +:105BF000A67054E7FFDF52E73DB1012D03D0FFDF70 +:105C0000022DF9D14BE70420C0E70320BEE770B5B1 +:105C1000050004D0374CA078052806D101E01020FB +:105C200070BD0820F1F7FFFA08B1112070BD3548AA +:105C3000F0F720FAE070202806D00121F1F7DCF817 +:105C40000020A560A07070BD032070BD294810B56C +:105C5000017809B1112010BD8178052906D00129EC +:105C600006D029B101210170002010BD0F2010BD08 +:105C700000F033F8F8E770B51E4C0546A07808B17F +:105C8000012809D155B12846FFF783FE40B1287895 +:105C900040B1A078012809D00F2070BD102070BD40 +:105CA000072070BD2846FFF79EFE03E0002128462E +:105CB000FFF7B1FE1049E07800F00DF800B9FFDF02 +:105CC000002070BD0B4810B5006900F01DF8BDE85C +:105CD0001040F0F754B9F0F772BC064810B5C07820 +:105CE000F0F723FA00B9FFDF0820F1F786FABDE8E4 +:105CF000104039E6DC010020B41300203D8601008D +:105D0000FF1FA107E15A02000C490A6848F202137A +:105D10009A4302430A607047084A116848F2021326 +:105D200001EA03009943116070470246044B1020BA +:105D30001344FC2B01D8116000207047C8060240B4 +:105D40000018FEBF1EF0040F0CBFEFF30880EFF346 +:105D50000980014A10470000FF7B010001B41EB416 +:105D600000B5F1F76DFC01B40198864601BC01B0A5 +:105D70001EBD00008269034981614FF0010010449B +:105D8000704700005D5D02000FF20C0000F10000A2 +:105D9000694641F8080C20BF70470000FEDF184933 +:105DA0000978F9B90420714608421BD10699154AB1 +:105DB000914217DC0699022914DB02394878DF2862 +:105DC00010D10878FE2807D0FF280BD14FF0010032 +:105DD0004FF000020C4B184741F201000099019A64 +:105DE000094B1847094B002B02D01B68DB6818478A +:105DF0004FF0FF3071464FF00002034B1847000090 +:105E000028ED00E000700200D14B020004000020E9 +:105E1000174818497047FFF7FBFFDBF7CFF900BDC4 +:105E2000154816490968884203D1154A13605B6812 +:105E3000184700BD20BFFDE70F4810490968884298 +:105E400010D1104B18684FF0FF318842F2D080F328 +:105E500008884FF02021884204DD0B4802680321A6 +:105E60000A4302600948804709488047FFDF000075 +:105E7000C8130020C81300200010000000000020FC +:105E8000040000200070020014090040B92F000037 +:105E9000215E0200F0B44046494652465B460FB4CC +:105EA00002A0013001B50648004700BF01BC86468C +:105EB0000FBC8046894692469B46F0BC7047000066 +:105EC0000911000004207146084202D0EFF3098155 +:105ED00001E0EFF30881886902380078102813DBAD +:105EE00020280FDB2C280BDB0A4A12680A4B9A4247 +:105EF00003D1602804DB094A10470220086070477C +:105F0000074A1047074A1047074A12682C3212689E +:105F1000104700005C000020BEBAFECA9B130000C0 +:105F2000554302006F4D0200040000200D4B0E4946 +:105F300008470E4B0C4908470D4B0B4908470D4BC2 +:105F4000094908470C4B084908470C4B06490847C4 +:105F50000B4B054908470B4B034908470A4B0249BD +:105F60000847000041BF000079C10000792D000002 +:105F7000F32B0000812B0000012E0000B71300005E +:105F80003F2900007D2F0000455D020000210160D7 +:105F90004160017270470A6802600B7903717047B3 +:105FA00089970000FF9800005B9A0000C59A0000E6 +:105FB000FF9A0000339B0000659B00009D9B000042 +:105FC0003D9C00007D980000859A0000331200007F +:105FD0000744000053440000B94400004745000056 +:105FE0006146000037470000694700004148000053 +:105FF000DB4800002F490000154A0000354A000028 +:10600000AD160000D1160000F11500004D1600007D +:10601000031700009717000003610000C36200002F +:10602000A1660000BB67000043680000C168000073 +:10603000256900004D6A00001D6B0000896B00009F +:10604000574A00005D4A0000674A0000CF4A00003E +:10605000FB4A0000B74C0000E14C0000194D000065 +:10606000834D00006D4E0000834E00007744000019 +:10607000974E0000B94E0000FF4E000033120000A2 +:10608000331200003312000033120000C12500005B +:1060900047260000632600007F2600000D28000030 +:1060A000A9260000B3260000F526000017270000EF +:1060B000F3270000352800003312000033120000DF +:1060C00097840000B7840000B9840000FD840000BC +:1060D0002B8500001B860000A7860000BB86000001 +:1060E000098700001F880000C1890000E98A0000BC +:1060F0003D740000018B00003312000033120000D9 +:10610000EBB700004DB90000A7B9000021BA0000AC +:10611000CDBA0000010000000000000010011001D5 +:106120003A0200001A020000020004050600000006 +:1061300007111102FFFFFFFF0000FFFFF3B3000094 +:10614000273D0000532100008774000001900000EB +:1061500000000000BF9200009B920000AD92000082 +:10616000000002000000000000020000000000002B +:1061700000010000000000004382000023820000B4 +:10618000918200002D250000EF2400000F25000063 +:10619000DBAA000007AB00000FAD0000FD590000B6 +:1061A000B182000000000000E18200007B250000B9 +:1061B000000000000000000000000000F1AB000043 +:1061C00000000000915A00000300000001555555E1 +:1061D000D6BE898E00006606660C661200000A03B1 +:1061E000AE055208000056044608360CC7FD0000F4 +:1061F0005BFF0000A1FB0000C3FD0000A7A8010099 +:106200009B040100AAAED7AB15412010000000008E +:10621000900A0000900A00007B5700007B570000A6 +:10622000E143000053B200000B7700006320000040 +:10623000BD3A020063BD0100BD570000BD5700001C +:1062400005440000E5B2000093770000D72000006D +:10625000EB3A020079BD0100700170014000380086 +:106260005C0024006801200200000300656C746279 +:10627000000000000000000000000000000000001E +:106280008700000000000000000000000000000087 +:10629000BE83605ADB0B376038A5F5AA9183886C02 +:1062A000010000007746010049550100000000018F +:1062B0000206030405000000070000FB349B5F801A +:1062C000000080001000000000000000000000003E +:1062D000060000000A000000320000007300000009 +:1062E000B4000000F401FA00960064004B00320094 +:1062F0001E0014000A000500020001000049000011 +:1063000000000000D7CF0100E9D1010025D1010034 +:10631000EBCF0100000000008FD40100000101025A +:10632000010202030C0802170D0101020909010113 +:1063300006020918180301010909030305000000FA +:10634000555555252627D6BE898E00002BFB01000A +:1063500003F7010049FA01003FF20100BB220200ED +:10636000B7FB0100F401FA00960064004B00320014 +:106370001E0014000A00050002000100254900006B +:1063800000000000314A0200494A0200614A02004E +:10639000794A0200A94A0200D14A0200FB4A0200DF +:1063A0002F4B02007B470200B7460200A1430200C8 +:1063B0002B5D0200AD730100BD730100E9730100A4 +:1063C000BB740100C3740100D57401002F480200A2 +:1063D000494802001D4802002748020055480200B3 +:1063E0008B480200AB480200C9480200D7480200AF +:1063F000E5480200F54802000D4902002549020067 +:106400003B4902005149020000000000DFBC0000CF +:1064100035BD00004BBD000015590200CD43020000 +:10642000994402000F5C02004D5C0200775C0200A0 +:106430009D710100FD760100674902008D4902004F +:10644000B1490200D74902001C0500402005004068 +:10645000001002007464020008000020E80100003F +:106460004411000098640200F0010020D8110000DF +:10647000A011000001181348140244200B440C061C +:106480004813770B1B2034041ABA0401A40213101A +:08649000327F0B744411C000BF +:02000004000FEB +:1040000000000420CDB20F00F5B20F00F7B20F0090 +:10401000F9B20F00FBB20F00FDB20F00000000006C +:10402000000000000000000000000000C1450F007B +:1040300001B30F000000000003B30F00214D0F007B +:10404000354E0F0007B30F0007B30F0007B30F0083 +:1040500007B30F0007B30F0007B30F0007B30F003C +:1040600007B30F0007B30F0007B30F0007B30F002C +:1040700007B30F0007B30F0007B30F0007B30F001C +:1040800007B30F0085720F0007B30F0007B30F00CF +:1040900041730F0007B30F00814B0F0007B30F00F0 +:1040A00007B30F0007B30F0007B30F0007B30F00EC +:1040B00007B30F0007B30F0000000000000000006E +:1040C00007B30F0007B30F0007B30F0007B30F00CC +:1040D00007B30F0007B30F0007B30F0005850F00EC +:1040E00007B30F0007B30F0007B30F000000000075 +:1040F0000000000007B30F000000000007B30F002E +:1041000000000000000000000000000000000000AF +:10411000000000000000000000000000000000009F +:10412000000000000000000000000000000000008F +:10413000000000000000000000000000000000007F +:10414000000000000000000000000000000000006F +:10415000000000000000000000000000000000005F +:10416000000000000000000000000000000000004F +:10417000000000000000000000000000000000003F +:10418000000000000000000000000000000000002F +:10419000000000000000000000000000000000001F +:1041A000000000000000000000000000000000000F +:1041B00000000000000000000000000000000000FF +:1041C00000000000000000000000000000000000EF +:1041D00000000000000000000000000000000000DF +:1041E00000000000000000000000000000000000CF +:1041F00000000000000000000000000000000000BF +:104200000348044B834202D0034B03B11847704765 +:10421000C8860020C8860020000000000548064926 +:104220000B1AD90F01EBA301491002D0034B03B1C4 +:1042300018477047C8860020C8860020000000008C +:1042400010B5064C237843B9FFF7DAFF044B13B1DE +:104250000448AFF300800123237010BDC8860020FE +:10426000000000005CBD0F0008B5044B1BB1044901 +:104270000448AFF30080BDE80840CFE7000000002D +:10428000CC8600205CBD0F00A3F5803A704700BFCC +:10429000154B002B08BF114B9D46FFF7F5FF002182 +:1042A0008B460F461148124A121A00F075F80C4B53 +:1042B000002B00D098470B4B002B00D098470020D4 +:1042C000002104000D000B4800F016F800F040F843 +:1042D0002000290000F074FA00F014F80000080033 +:1042E000000000000000000000000420C88600203C +:1042F000A4CE002025430F00002301461A4618468D +:1043000000F09CB808B50021044600F0CBF8044B3F +:104310001868C36B03B19847204600F029F900BF25 +:1043200058BB0F0038B5084B084D5B1B9C1007D0DD +:10433000043B1D44013C55F804399847002CF9D141 +:10434000BDE8384007F002BCC8860020C4860020C3 +:1043500070B50D4E0D4D761BB61006D0002455F8E5 +:10436000043B01349847A642F9D1094E094D761B0A +:1043700007F0E6FBB61006D0002455F8043B0134E4 +:104380009847A642F9D170BDBC860020BC860020AB +:10439000C4860020BC860020830730B548D0541E58 +:1043A000002A3FD0CAB2034601E0013C3AD303F8E9 +:1043B000012B9D07F9D1032C2DD9CDB245EA052556 +:1043C0000F2C45EA054536D9A4F1100222F00F0C56 +:1043D00003F1200EE6444FEA121C03F1100242E9F9 +:1043E000045542E9025510327245F8D10CF1010230 +:1043F00014F00C0F03EB021204F00F0C13D0ACF10D +:10440000040323F003030433134442F8045B934290 +:10441000FBD10CF003042CB1CAB21C4403F8012BED +:104420009C42FBD130BD64461346002CF4D1F9E721 +:1044300003461446BFE71A46A446E0E770B4184C9A +:104440002568D5F848411CB365681F2D25DC38B9AF +:10445000AB1C0135656044F82310002070BC704728 +:1044600004EB850C0228CCF88820D4F888614FF042 +:10447000010202FA05F246EA0206C4F88861CCF8A5 +:104480000831E5D1D4F88C311343C4F88C31DFE71F +:1044900005F5A674C5F84841D6E74FF0FF30DDE7D3 +:1044A00058BB0F002DE9F84F2B4B1F68D7F8486118 +:1044B0002DED028BC6B108EE100A8B464FF00108B5 +:1044C0004FF000097468651E0ED4013406EB8404B5 +:1044D000BBF1000F0CD0D4F800315B4508D0013D92 +:1044E0006B1CA4F10404F3D1BDEC028BBDE8F88F82 +:1044F00073682268013BAB420CBF7560C4F8009042 +:10450000002AECD0D6F88801D6F804A008FA05F104 +:1045100001420BD190477268524513D1D7F8483108 +:10452000B342DCD01E46002ECCD1DDE7D6F88C019C +:1045300001420CD1D4F8801018EE100A904772682E +:104540005245EBD0D7F84861002EBBD1CCE7D4F868 +:1045500080009047DFE700BF58BB0F00024B13B14C +:104560000248FFF7C9BE70470000000025430F0056 +:10457000FEE700BF38B50C46E8B90968C9B10F4D70 +:10458000A9420BD06B1A3B2B11D93C22284606F0CE +:10459000EDFE03E0CA5CEA54013BFBD2074800226F +:1045A0003C2103F0D3F90023A887236038BD3D23C5 +:1045B000F2E70E23F9E70123F7E700BF807F002031 +:1045C000074A6FF002039E4502D1EFF3098101E033 +:1045D000EFF308818869A0F102000078104700BF5E +:1045E00075450F0038B50446A8B10D4D00223C2199 +:1045F000284603F0ABF9AB8F83420ED12A4605F172 +:104600003C0152F8040B44F8040B8A42F9D10133FF +:10461000AB87002038BD0E20FCE70B20FAE700BF77 +:10462000807F00200B2970B50446154608462FD917 +:104630002389053304EB43012044431ADAB2012AEB +:1046400026D9814224D8134806F090FE2388522BA5 +:1046500006D1AB0711D062884CF668639A420CD041 +:104660000F2014E034F8022B824204D0AE89964227 +:1046700003F1010308D1002009E0218900230A3455 +:104680004FF6FE704FF440559942EBD80B2070BDA9 +:104690000920FCE7E486002008B5002203F056F963 +:1046A000034B1B88834214BF0B20002008BD00BFB2 +:1046B000E486002038B50C4C21684B1C054612D00E +:1046C0000A484FF4805206F01FFE48B115B1206829 +:1046D00000F00CFC054920684FF4806200F026FCD5 +:1046E0004FF0FF33236038BD30840020F086002077 +:1046F0002DE9F041DFF84480044624F47F65184634 +:10470000D8F8003025F00F05AB420E46174609D009 +:10471000FFF7D0FF0848C8F800504FF480522946F0 +:1047200006F024FE0448C4F30B043A463146204404 +:10473000BDE8F04106F01ABEF0860020308400206B +:10474000BFF34F8F0549064BCA6802F4E06213437A +:10475000CB60BFF34F8F00BFFDE700BF00ED00E06F +:104760000400FA054BDF704710DF704711DF704718 +:1047700013DF704718DF704760DF704769DF7047ED +:1047800061DF70471FB50023CDE90133039368460D +:1047900002230093FFF7EEFF05B05DF804FB08B5B8 +:1047A0004FF0E023D3F8F03DDB0700D500BEFFF764 +:1047B000C7FF0000014B1878704700BFF19600203A +:1047C0002DE9FF484C4B40F60212C3F8402500F09B +:1047D00005FA00F021FE002000F0AAFA00F09CFE8D +:1047E00048B1052000F0A4FA00F0A8FE00F0CCFECD +:1047F000062000F09DFA4FF08043DFF81081D3F8D7 +:104800001C55B12D0CBF0123002388F800304AD07D +:10481000A5F1A8014C424C41384EDFF8F49004F069 +:10482000010333704FF08043D3F8007407F00107A1 +:10483000002C3BD14E2D38D0572D36D0304B1B6835 +:104840001A68304B9A4200D17FBB6D2D2ED01220BA +:1048500000F0B0F9044633789BBB122000F0AAF9AF +:1048600010B1122000F0A6F900F00100307000F045 +:1048700005FDDFF8A0B0824630B12CB9204B1B6893 +:104880001A68214B9A4257D04FF440535A684A4510 +:104890000ABF9B684FF4905303F500731B685B4598 +:1048A0003AD1012448E00124B6E701244FF08043C7 +:1048B00000226D2DC3F81C2500F0E480002CCAD125 +:1048C000C5E70120D0E7544636E03C4634E04FF4DB +:1048D00080030B6071E0022000F02AFAA5F14E037C +:1048E0005842584103F012FEAEE000221146C1E0EA +:1048F00003F05CFEC6E000BF00A00040F19600207F +:1049000034840020D51A5A007E67E54EF2960020C6 +:10491000DBE5B1517CB0EE87002CC2D1BAF1000FBB +:10492000D1D0002FD1D0654B654A1B6865481A600D +:10493000654B43F0010398474FF440535A684A458A +:1049400008BF9B685D4A0CBF03F500734FF490539A +:1049500012681B685B450CBF5C4B002313601CB9DD +:10496000BAF1000F40F08E803378002BB3D00820CE +:1049700000F0DEF998F800300BB9FFF703FF0123D0 +:104980004FF4742088F80030FFF7F2FE504B514985 +:104990001868019001A8FFF7E7FE4F4991F8163318 +:1049A0005A09EC231341DA0707D54C4B9A68002AC1 +:1049B0008DD01A6842F480021A600C22484B029390 +:1049C00000210DEB0200FFF7E7FC40F20113029A11 +:1049D000039303A94020FFF7D1FE0C2200210DEB29 +:1049E0000200FFF7D9FC9DF80C30029A43F0010356 +:1049F00003A9A0208DF80C30FFF7C0FE0C22002187 +:104A00000DEB0200FFF7C8FC01241723029AADF852 +:104A10000E3003A923208DF80C40FFF7AFFE0C22C7 +:104A200000210DEB0200FFF7B7FC0623029A8DF878 +:104A30000C4003A920208DF80E40ADF81030FFF790 +:104A40009DFE02A8FFF798FE4FF4405330785A6855 +:104A50004A450ABF9B684FF4905303F500731B68E7 +:104A60005B4504D0572D02D04E2D7FF43EAF01227E +:104A700040F6B83100F0E8FC3378002B3FF438AF53 +:104A8000FFF774FE00F0EEF800F0F8FBA0B100F0C4 +:104A900043FD88B94FF440535B684B4506D198F805 +:104AA00000300BB9FFF76EFEFFF760FE034B1B688B +:104AB00000221A6000F004FDFFF742FE348400205B +:104AC000D51A5A000048E80160BB0F007E67E54E2A +:104AD0005CBB0F009F470F0000E100E0409D0020FD +:104AE0000080002010B58EB03423ADF802300DF1F7 +:104AF0000201002301A8ADF80430FFF741FE04468F +:104B000040B9BDF80430102B07D0112B0CD001A8F0 +:104B100000F0CCFF20460EB010BD054B01221A70EC +:104B2000072000F005F9F2E7014B18700820F8E7BC +:104B3000F096002013B5002301A80193FFF712FEA1 +:104B4000044660B9019802F053FA019B0A2B09D080 +:104B5000092B09D00B2B02D1012004F09BFB20462E +:104B600002B010BD2046F8E70220F6E708B5FFF7CF +:104B7000B9FF0528FBD1FFF7DDFF0528F7D108BDF8 +:104B80000021024A084602F003BE00BF6D4B0F0031 +:104B90001F2884BF00F01F00044B054A98BF4FF048 +:104BA000A04300F5E07043F8202070470003005058 +:104BB0000C0003001F288ABF064B4FF0A04300F0F3 +:104BC0001F00D3F8103523FA00F0C04300F00100B5 +:104BD000704700BF000300507047000008B54FF059 +:104BE000804301220021DA601220C3F818159A6070 +:104BF000FFF7CEFF1220FFF7CBFF154B4FF4C85045 +:104C000043F001039847FFF7E7FF124A1E210820EF +:104C100002F09AFD08B102F051FE02F0FBFC0E49D1 +:104C20000E4BE02081F823001B684FF47A72B3FB2F +:104C3000F2F3013BB3F1807F08D24FF0E0225361E1 +:104C4000002381F8230093610723136108BD00BF8F +:104C500070BB0F00F496002000ED00E038840020C7 +:104C6000704700004FF0E0224FF40031002310B5F0 +:104C70001361C2F8801102F1C04202F540524FF4B4 +:104C80008031C2F84813C2F80813012151609160C5 +:104C90004FF080420A4CD16002201F2B1A46C6BF3B +:104CA00003F01F0221464FF0A04102F5E0720133EC +:104CB000302B41F82200F0D1FFF7D2FF10BD00BF2A +:104CC00000030050074B23F81010074B002282B05E +:104CD000C3F81021D3F810210192019A01229A60A1 +:104CE00002B07047E898002000C001400A4A0B4B10 +:104CF00011681B68B1FBF3F203FB1211B1EB530F08 +:104D00004FEA530288BF591A4F2359430020B1FB81 +:104D1000F2F189B2FFF7D6BFE4980020F0980020A6 +:104D2000024A136801331360FFF7E0BFE4980020E4 +:104D30001B490A68082823D8DFE800F0130513226E +:104D4000221A1E242A00174B40F6B83018604FF480 +:104D50007F4303F0103323F080539A4218BF0B6057 +:104D60007047104B4FF4967018604FF47F03F0E7D4 +:104D70000C4B64221A6070470A4B40F6B83018603A +:104D80001346E6E7074B40F6B8301860FF23E0E72C +:104D9000044B4FF4967018604FF0FF13D9E700BF33 +:104DA000F4980020F098002000F1804382B01A6847 +:104DB000002A14BF0120002004D000221A601B68C2 +:104DC0000193019B02B070470F4A1378D3B903785F +:104DD0004FF08041C3F34003C1F88035037803F0FE +:104DE0000103C1F87835094B1968C90706D4E021D9 +:104DF00083F800130121C3F88011196001230448CE +:104E000013707047034870470499002000E100E0E8 +:104E10000000AD0B0C00AD0B014B02681A6070472F +:104E2000009900204FF080434FF46072C3F80423D0 +:104E30007047000010B54FF08043D3F80443620779 +:104E400007D54FF48470FFF7AFFF10B11E4B1B68FE +:104E50009847A30608D54FF48A70FFF7A5FF18B14D +:104E60001A4B00201B689847600608D54FF48C70D9 +:104E7000FFF79AFF18B1154B01201B6898472106D0 +:104E800008D54FF48E70FFF78FFF18B1104B00203C +:104E90001B689847E20508D54FF49070FFF784FF30 +:104EA00018B10B4B01201B689847A3050AD54FF496 +:104EB0009270FFF779FF28B1054BBDE810401B68E1 +:104EC0000220184710BD00BFF8980020FC98002071 +:104ED00000990020044AD2F80034DB07FBD50160BA +:104EE000BFF35F8F704700BF00E001404FF0805379 +:104EF0001A69B0FBF2F302FB130373B9084B0222E9 +:104F0000C3F80425C3F80805D3F80024D207FBD55D +:104F100000220448C3F8042570470348704700BFC7 +:104F200000E001400000AD0B0A00AD0BF8B50B4BE3 +:104F30001546012206460F46C3F804250024A54263 +:104F400004D1064B0022C3F80425F8BD57F82410FD +:104F500006EB8400FFF7BEFF0134F0E700E00140FC +:104F6000BFF34F8F0549064BCA6802F4E062134352 +:104F7000CB60BFF34F8F00BFFDE700BF00ED00E047 +:104F80000400FA054FF08053D3F83021082A06D1E7 +:104F9000D3F83431032B02D8024AD05C704700208A +:104FA000704700BF76BB0F0008B54FF08053D3F8B1 +:104FB0003021082A4ED14FF080420021C2F80C1156 +:104FC000C2F81011C2F8381502F54042D3F80414A3 +:104FD000C2F82015D3F80814C2F82415D3F80C141D +:104FE000C2F82815D3F81014C2F82C15D3F81414ED +:104FF000C2F83015D3F81814C2F83415D3F81C14BD +:10500000C2F84015D3F82014C2F84415D3F824147C +:10501000C2F84815D3F82814C2F84C15D3F82C144C +:10502000C2F85015D3F83014C2F85415D3F834141C +:10503000C2F86015D3F83814C2F86415D3F83C14DC +:10504000C2F86815D3F84014C2F86C15D3F844348C +:10505000C2F87035FFF796FF18B1494B494AC3F8BB +:105060008C26FFF78FFF18B1474BFB22C3F818259A +:10507000FFF788FF70B14FF080414FF08053D1F8B7 +:10508000E42ED3F8583222F00F0203F00F0313433B +:10509000C1F8E43EFFF776FF20B13C4B4FF40072BD +:1050A000C3F840264FF08053D3F83031082B09D194 +:1050B0004FF08043D3F80024D10744BF6FF00102C2 +:1050C000C3F80024324AD2F8883043F47003C2F89F +:1050D0008830BFF34F8FBFF36F8F4FF01023D3F89B +:1050E0000C22D2071DD52B4B0122C3F80425D3F87F +:1050F0000024002AFBD04FF01022D2F80C3223F00B +:105100000103C2F80C32234BD3F80024002AFBD051 +:105110000022C3F80425D3F80024002AFBD0FFF7AF +:105120001FFFD3F80022002A03DBD3F80432002B40 +:1051300022DA184B0122C3F80425D3F80024002AF0 +:10514000FBD04FF010221221C2F80012D3F8002435 +:10515000002AFBD04FF010231222C3F804220D4B7B +:10516000D3F80024002AFBD00022C3F80425D3F88A +:105170000024002AFBD0D2E7074B084A1A6008BD7A +:10518000005000404881030000F0004000900240C1 +:1051900000ED00E000E00140388400200090D003E2 +:1051A00013DF704718DF7047064B1878012803D1CA +:1051B000012904BF0221197012B1104602F07EBB12 +:1051C000704700BF3599002008B5FFF7F3FA88B1A2 +:1051D00011481C2101F098FF08B102F06FFB0F4944 +:1051E0000D4800231C2201F07FFF98B1BDE8084064 +:1051F00002F064BB4FF47F20FFF778FE07220749D7 +:105200004FF47F20FFF792FE054B1A78012A04BF66 +:1052100002221A7008BD00BF2C9900203899002086 +:105220003599002070B5124C124D134ED4F800344D +:105230007BB1C4F80056C4F80456C4F80856C4F844 +:105240000C56C4F81056C4F81456C4F81856C4F8CE +:105250001C5602F005FB05F0F4FF20B104F0DAFD66 +:10526000002005F003FA3378023B022BDED870BD34 +:10527000000001403546526E3599002013B54FF4B9 +:105280004053124A596891420CBF9C684FF48054B5 +:1052900001A800F0A7F92368013302D16368013344 +:1052A00011D0019B1A88012A0DD1588820B1996824 +:1052B0000022204602F04AFB019B5B881B1A5842E1 +:1052C000584102B010BD0020FBE700BFDBE5B15143 +:1052D00084B02DE9F34108AC84E80F009DF820402C +:1052E000BDF8228001A80F4616461D4600F07AF947 +:1052F00054B9384B0122FF21A3F802809D601A8027 +:105300009980354B1A7012E0012C17D1314BBA1924 +:105310002A449A60A5221A80FF229A800C9AA3F848 +:105320000280C3E903765D619A612B4B1C70FFF725 +:105330004BFF02B0BDE8F04104B07047032C0FD121 +:10534000019A244B11881980518892689A60C3E9A8 +:105350000376AA2259809A805D611F4B0122D1E712 +:10536000022C15D1019A1B4B1188A5290AD10022C4 +:105370009A60FF221A60FF229A800022C3E903226A +:105380005A61EAE719805188926859809A60F2E779 +:10539000052C0ED1FFF70EFA40B100F097FD08B1D1 +:1053A00002F08CFA0C4B03221A70C2E700F010FADC +:1053B000F5E7042C08D1074B00229A60FF221A60FF +:1053C000019A92889A80B2E7062CB2D1024B04224D +:1053D000EAE700BF389900203599002000B50C4B52 +:1053E0001B7889B063B90B4B1B786BB905238DF81B +:1053F0000C30079B009303AB0FCBFFF769FF03E073 +:1054000004F000FB0028EED009B05DF804FB00BFFB +:1054100034990020289900201FB50023CDE90233DC +:10542000074B019301F030FE30B906494FF47F235A +:1054300001A84B6001F046FE05B05DF804FB00BF1B +:10544000A9510F002C99002070B505460E460AB1EF +:1054500080F00102154B02F001021A7000F0B0FF5B +:10546000044628B935B100F08BFC0446FFF7DAFE9C +:10547000204670BDBEB10E4B0E4A0F481D70294626 +:1054800002F0F8F84FF400444FF4FA7029464FF454 +:105490007A720023E6FB040106F0D0F92A460146A1 +:1054A000064802F0F9F800F06FF9DEE734990020C1 +:1054B00028990020DD530F007CBB0F0008990020C5 +:1054C0001FB5134B4FF0FF32C3F88020C3F8802183 +:1054D0004FF440530F4A596891420DD19C682046C1 +:1054E000FFF75EFE10B14FF000531C60204604B081 +:1054F000BDE8104000F09AB80023CDE902334FF424 +:10550000805406236846CDE90034FFF74BFEE9E7F7 +:1055100000E100E0DBE5B15107B501A800F062F859 +:10552000019B1A88A52A07D09888A0F1AA0358429F +:10553000584103B05DF804FB0120FAE710B501F013 +:105540005DF9A8B10E4B0F4843F00103984701F0F5 +:10555000BFF808B102F0B2F901F050F908B102F059 +:10556000ADF901F001F9044638B102F0A7F904E001 +:1055700001F01EF904460028E4D1204610BD00BF0A +:1055800080BB0F0000A8610000B589B003AB1422F6 +:1055900000211846FEF700FF02228DF80C200022A1 +:1055A00000920FC8FFF794FEFFF73CFE002009B001 +:1055B0005DF804FB13B5044601A800F013F8019B45 +:1055C0001A8822805A8862809A68A2609A88A2808B +:1055D000DA68E2601A6922615A6962619B69A361B3 +:1055E00002B010BD014B0360704700BF00F00F0018 +:1055F000F0B50346186880F308885868FF2464B241 +:10560000EFF30585002D01D1A64600472546064645 +:1056100021273FBAF0B40024002500260027F0B46B +:10562000F92040B2004700BFF0BD00BFFFF7E0BF68 +:1056300073B500230DF1020101A8ADF8023001930A +:1056400002F0C4FDF8B9019C25785DB3174B93F8BF +:105650003020032A28D00C2606FB00F29958E9B91D +:1056600098189D5093F830200132D2B283F8302040 +:10567000BDF802300E4A9B08013B0434436084604D +:10568000084602F085F8019B33B128B1184602F0B4 +:10569000B9FD08B102F012F902B070BD0130042862 +:1056A000DAD1F0E70720EEE70420ECE75499002078 +:1056B000F9560F00084609B102F000B97047000022 +:1056C00010B50C220B4B504319181A5882B193F89D +:1056D00030208C68013AD2B283F8302000221A5070 +:1056E000C1E90122201F02F08DFD08B102F0E6F8A9 +:1056F000002010BD54990020214B70B50122214E8D +:105700001A7096F8303003B970BD1E4C002523681E +:1057100083B1013B042B07D8DFE803F01C0612031A +:105720002800204600F0DEFEE8B2FFF7C9FF08B10E +:1057300002F0C4F80135042D04F10C04E7D1E0E7D0 +:10574000A3686360204600F073FE0028ECD002F0EE +:10575000B5F8E9E7204600F053FF00F02FFF08B14D +:1057600002F0ACF80520FFF7E3FADDE700F074FF84 +:1057700000F092FFBDE870400620FFF7D9BA00BFE5 +:10578000289900205499002008B50E4B002283F878 +:10579000302004210139C3E901221A6003F10C030E +:1057A000F8D1094800F03EFE02F0A8FC08B102F072 +:1057B00085F8064802F08EFC08B102F07FF8002060 +:1057C00008BD00BF54990020B5560F0031560F0098 +:1057D00008B50020FFF774FF0120FFF771FF0220DA +:1057E000FFF76EFF0320FFF76BFFBDE8084002F0F4 +:1057F000CDBC006870476CDF70476DDF70476EDFAF +:1058000070476FDF704772DF704773DF704774DF78 +:10581000704776DF704777DF70477ADF70477CDF4D +:1058200070477FDF704786DF70478FDF704790DFFC +:105830007047AFDF7047B0DF7047B1DF7047B2DF4E +:105840007047B5DF704764DF704766DF70470C282C +:1058500013D8DFE800F01412121212120912071204 +:10586000120D0B0002207047032070470420704780 +:10587000042914BF06200520704706207047012028 +:10588000704702F01BB810B5044608460321FFF725 +:10589000DEFF0246204601F075F918B1BDE8104060 +:1058A00002F00CB810BD00000346032B10B50846EB +:1058B000144620D0042B23D169B1124B18884FF61F +:1058C000FF7398421CD01321FFF7A3FFC0B1BDE8BE +:1058D000104001F0F3BF104602F094F808B101F057 +:1058E000EDFF094B1B689C420AD1012203210748A6 +:1058F00001F048F9EAE70121FFF7A9FF0246F6E7C0 +:1059000010BD00BF3E840020489A0020F899002076 +:10591000F8B50A4DAB889E181D2E14460DDC2F6875 +:10592000FE1802F1010C07F803C07070B01C05F0FE +:105930001DFDAB8802331A19AA80F8BDA899002072 +:10594000F0B54E4E317895B0002940F092804C4C25 +:10595000019110222046FEF71FFD4A4B019923605A +:1059600018220EA8FEF718FD01238DF838302823E1 +:105970001093454B1B78002B7DD0444B04AC03F1B6 +:10598000100518685968224603C20833AB42144612 +:10599000F7D13F4C10220DEB0201E01D05F0B4FCE5 +:1059A000002868D03B4801210460FFF728FF08B1B8 +:1059B00001F084FF384B08AA03F1100C1746186851 +:1059C0005968154603C5083363452A46F7D1206850 +:1059D0000C903248A288A379ADF8342007600122E8 +:1059E00000218DF83630FFF70CFF08B101F066FF9B +:1059F00003238DF84C3004238DF80E3041F23053E0 +:105A0000ADF81030264B08AA9B798DF812300DF1B5 +:105A10000F0104A8FFF717FF012210460DF10E0138 +:105A2000FFF776FF1F4805F04BFE1E49C2B2092062 +:105A3000FFF76EFF102208A90620FFF769FF104943 +:105A400019480EAAFFF7DFFE08B101F037FF164C28 +:105A5000042221780120FFF7DEFE08B101F02EFFBD +:105A600020780121FFF7D1FE08B101F027FF0123C3 +:105A7000337015B0F0BD0623BEE700BF2C9A00209E +:105A8000A899002088990020F4990020A9BB0F0054 +:105A9000B8990020449A0020BF990020289A00203D +:105AA000F899002086BB0F003C840020F0B5044626 +:105AB0000146B1B0A84801F099F82388262B3BD8BD +:105AC0000F2B04D8012B00F0CC8031B0F0BD103B7F +:105AD000162BFAD801A252F823F000BF655B0F0025 +:105AE000735B0F00CB5A0F00A55B0F009F5C0F008C +:105AF000CB5A0F00CB5A0F00CB5A0F00CB5A0F00D6 +:105B0000CB5A0F00BB5C0F00CB5A0F00CB5A0F00D3 +:105B1000CB5A0F00CB5A0F00CB5A0F00CB5A0F00B5 +:105B2000315D0F00CB5A0F00235D0F00CB5A0F00E1 +:105B3000CB5A0F00255C0F00513B9AB2052AC4D8FE +:105B4000052BC2D801A252F823F000BF6F5C0F00F2 +:105B5000BB5C0F00CB5A0F00CB5A0F00475D0F0004 +:105B60000B5C0F007D4BA2881A807D4B00221A70BF +:105B7000ABE78023794CADF824307A4B2088322271 +:105B80001A6010A9012309AAFFF759FE08B101F014 +:105B900095FE754B1B780BB9FFF7D2FE4FF6FF73DE +:105BA000238092E7714B03AC9A79186899888DF835 +:105BB00022200790DA1DADF8201017332646106812 +:105BC0005168254603C508329A422C46F7D1684BE6 +:105BD00009AA03F11807154618685968144603C442 +:105BE0000833BB422246F7D1186820605B48614AFF +:105BF000008810AB8521CDE91456FFF712FE00286E +:105C00003FF463AF01F05AFE5FE7A379002B7FF406 +:105C10005CAF524B13211888FFF7FBFD00283FF4BF +:105C200054AF79E0237A012B7FF44FAF4C4B002225 +:105C30001A704C4B19680139196069B910AB1422FC +:105C40001846FEF7A9FB05228DF84020149A009211 +:105C50000FC8FFF73DFB38E731B0BDE8F040FFF774 +:105C60006FBE3E4B00211888FFF7EFFDD6E7A37902 +:105C7000002B3FF42AAFA27B043A022A3FF625AF5D +:105C8000022B18BF01238DF840304FF4C173ADF8DB +:105C90004430324B10A91888FFF7CDFDAFE7334AE7 +:105CA000258A508D02F118010023854218BF19463C +:105CB0000732A088FFF7B7FDB0E7284B1C884FF6E6 +:105CC000FF75AC4227D02C4B1B78F3B12B49012335 +:105CD00008222046FFF7B1FDF0B902460146022333 +:105CE0002046FFF7AAFDB8B92A460C212046FFF747 +:105CF000A0FDA0F54053023B012B7FF6E6AE08283D +:105D00003FF4E3AE112889D1DFE61A461946204652 +:105D1000FFF793FD82E7082031B0BDE8F04001F0C5 +:105D2000CDBD0E4B002218881146FFF780FD75E7A8 +:105D300000238DF840308DF84130084B10A91888A9 +:105D4000FFF773FDC1E6E188044B1729188828BFC7 +:105D50001721FFF776FD61E7F89900203E840020C7 +:105D60002C9A0020408400203F9A0020B8990020FF +:105D7000D09900203A9A0020F4990020EC99002054 +:105D800030B5464A464800231370464A95B0137012 +:105D900000F048FB01F0F6FD0446002868D14248B7 +:105DA000FEF720FC002866D1404B01221A70112317 +:105DB0003F488DF8043005F083FC3D4982B201A8CC +:105DC000FFF72DFD08B101F079FD0822002104A89C +:105DD000FEF7E2FA0823ADF810301823ADF81230C0 +:105DE0000023ADF8143004A84FF4C873ADF8163092 +:105DF000FFF713FD08B101F061FD00210C2201A89D +:105E0000FEF7CAFA0823ADF804302A4B02932A4859 +:105E10002A4B039301A900F02FFD08B101F04EFDBC +:105E2000274D4022002104A8FEF7B6FA284605F0C7 +:105E300047FCADF810002846059505F041FC079594 +:105E4000204DADF81800284605F03AFC1123ADF8B6 +:105E5000300004A8ADF84C300D9500F0DBFF1A4B74 +:105E600030221A7007225A7010229A70FFF768FDCC +:105E7000204615B030BD04A8FFF7BFFC08B101F003 +:105E80001DFD9DF8113004A801338DF81130FFF786 +:105E9000B2FC00288BD001F011FD88E73F9A00206A +:105EA000A9580F00399A0020B8990020F4990020D1 +:105EB00086BB0F001D5F0F00F899002083580F006C +:105EC0008DBB0F0098BB0F003A9A002010B50F4B06 +:105ED00001221A700E4B18884FF6FF73984207D0B4 +:105EE0001321FFF796FC08B101F0E8FC002010BD7B +:105EF000084C2378002BF9D0074B1878FFF787FC64 +:105F000008B101F0DBFC00232370EFE73F9A00208B +:105F10003E8400202C9A00203C840020F0B50B78B1 +:105F200089B005460C46092B35D8DFE813F02D0063 +:105F3000360041000A001900260007011001440044 +:105F4000150100F089FB0421FFF781FC0246284679 +:105F500000F018FEF8B109B0BDE8F04001F0AEBCA9 +:105F6000FFF7B4FF08B101F0A9FC00F095FB90B178 +:105F700009B0BDE8F04000F09DBBFFF7A7FF002887 +:105F8000F6D001F09BFCF3E7764B01221A704B68C8 +:105F90001A78754B1A7009B0F0BD724B02261E704C +:105FA0004B681B78012BF6D100F008FB3146CBE79C +:105FB0006C4B0322EEE70520FEF7BAFE694B1E7814 +:105FC000022E37D0032E59D0012EE4D104AB10227B +:105FD00018460021FEF7E0F9634A237A12788DF81B +:105FE0001020002203920C2B4FF00302CDE9012078 +:105FF00008D03146284600F0C5FD0028CBD001F07E +:106000005DFCC8E763681846FFF7F3FB0590181DB1 +:10601000FFF7EFFB069003F10800FFF7EAFB07909C +:1060200001A800F005FA0028B5D03146FFF70FFCB3 +:106030000246DFE7237A13F0030011D0C0F1040217 +:106040001A44D2B219464FF0000C0E46013167686F +:10605000C9B2914207F806C0F7D11B1A0433237264 +:106060000123049363680693237A04A89B0805938D +:1060700000F0C6FA00288ED00221D7E7207A8307E5 +:1060800002D03246314662E7384E0190314601F087 +:106090008FFC014618B12846FFF7F5FB7BE76168E6 +:1060A000019A306805F062F9019801F0E7FC0146B9 +:1060B0000028F0D101A9304601F0F0FC014600288B +:1060C000E9D104230493019B9B08059304A833683A +:1060D000069300F007FA074640B9254A237A11686B +:1060E0000B441360234B32681A6054E709281BD114 +:1060F0001F4B217A1A68114419601F4B1B78002B23 +:106100003FF449AF1D4C2388013B9BB22380002BF9 +:106110007FF441AF284600F0FBFC08B101F0CEFB54 +:10612000174B1B88238036E7306801F06BFC014673 +:1061300010B12846FFF7A7FB3946ACE70E4B01220A +:106140001A700F4A8B8813800C4A138023E70A4A7F +:10615000002313700A4AF8E7054B196800F09CFC0D +:10616000F8E600BF399A0020409A00204C9A00209F +:10617000309A0020489A0020389A0020369A002051 +:10618000349A002018DF7047012973B514460D4674 +:106190001A4608D0032912D014B3204602B0BDE835 +:1061A000704001F08BBB0F4B1B78052BF4D10E4BCD +:1061B0001B68002BF0D0214604209847ECE7094EDD +:1061C0003378022BE8D1094B01925B689847064B64 +:1061D00035701B68002BDFD0019A21462846ECE77A +:1061E00002B070BD589A0020509A00205C9A00209E +:1061F00030B589B003AC142200212046FEF7CCF85C +:10620000094B1B88ADF80E30084BDB680693002560 +:10621000079B8DF80C50009394E80F00FFF758F897 +:10622000284609B030BD00BF689A0020B49A00200B +:1062300000B589B003238DF80C300A4B1B88ADF8EC +:106240000E30094B5A6804929A68DB680693079BE4 +:106250000093059203AB0FCBFFF73AF8002009B08B +:106260005DF804FB689A0020B49A002000B589B05C +:1062700001238DF80C300F4B0F4A1B88ADF80E3000 +:106280004FF440535968914208BF9A680B4B5968C4 +:10629000049118BF4FF480529968DB68069305910A +:1062A000009203AB0FCBFFF713F8002009B05DF8A5 +:1062B00004FB00BF689A0020DBE5B151B49A0020CE +:1062C00000B589B003AB142200211846FEF764F82C +:1062D00004228DF80C20002200920FC8FEF7F8FF70 +:1062E00009B05DF804FB0000194BF7B5194C1C60B0 +:1062F000194B02221A70FEF75DFA184B48B1196863 +:10630000204600F001FF00B303B0BDE8F04001F00B +:10631000D5BA1D68124F013D2D0B013504464FF4CF +:1063200040567368BB420CBFB0684FF4805000EB1E +:1063300004300134FEF7DAFDA542F2D80023054807 +:1063400000931A460321FFF71FFF03B0F0BD00BF03 +:10635000CC9A0020C49A0020589A00206C9A002001 +:10636000DBE5B15170B50C4686B00321CDE90210D2 +:106370000546960802A8019304940596FFF702FFCC +:10638000E0B1B4F5805F019B11D8012302A8CDE9EB +:106390000235CDE90446FFF7F5FE78B9032302A8DC +:1063A000CDE90235CDE90446FFF7ECFE06E01A46DA +:1063B000E11AE81AFFF7D6FF0028E6D006B070BD54 +:1063C0001FB5114B0193114B114900241C70114B47 +:1063D00001A81C80CDE9024400F074FE0E4B10B100 +:1063E0001C7004B010BD4FF440520C4954688C42EC +:1063F00007490CBF92684FF480524A60084A002156 +:10640000116001221A70ECE789610F00B09A002038 +:10641000C49A0020689A0020589A0020DBE5B15108 +:10642000549A0020014B1860704700BF509A00201A +:1064300038B54368214C0FCB84E80F002278500711 +:1064400001D5910733D16068830730D1A3689D07D8 +:106450002DD1E16811F0030429D1184408441849EA +:10646000B3F5204F086024D84FF4405315495D68B8 +:106470008D420ABF9B684FF46923C3F56A23984293 +:1064800017D8114B1149196011495960D10709D525 +:10649000104A9A60104B1B78012B0CD1FFF724FF98 +:1064A000204638BD92074CBF0C4A0D4AF1E706243E +:1064B000F6E70C24F4E70824F2E700BFB49A0020C2 +:1064C0006C9A0020DBE5B1515C9A0020E9620F0074 +:1064D000C1620F006D620F00589A002031620F00F8 +:1064E000F1610F002DE9F04385B0002853D0816899 +:1064F00011F0030451D12C4B1B78052B4FD12B4E9F +:1065000042683368DFF8AC9003EB82039500D9F85A +:106510000020934207D94FF0FF3333600C2420460C +:1065200005B0BDE8F0830391FEF744F9DFF88880F9 +:10653000039940B13368D8F800002A4600F0D4FD32 +:10654000D8B10446EBE74FF44053194A586837680E +:10655000904208BF9868039118BF4FF48050002301 +:106560002A463844FEF7C4F80399D8F8000000958D +:106570000B4600220121FFF707FE33681D44D9F8BE +:10658000003035609D420CD1FEF714F90028C6D1C9 +:10659000FEF790F8C3E70E24C1E71024BFE70824F4 +:1065A000BDE70924BBE700BF589A0020549A002099 +:1065B000DBE5B1516C9A0020CC9A002070B50B4BF2 +:1065C0001D6885B90A4E3378042B0CD1094C0A4B4F +:1065D00021781A780948FEF725F810B90523337099 +:1065E00070BD2570FCE70820FAE700BF549A002030 +:1065F000589A0020B09A0020B49A0020709A002087 +:10660000F8B5114B1A78032A03D0042A03D00824C2 +:1066100016E004221A700D4B1C68002CF7D10C4DAB +:1066200043682F789E0007EB8303402B0AD88168CC +:1066300008483246384404F099FE2B7833442B70D6 +:106640002046F8BD0924FBE7589A0020549A002000 +:10665000B09A0020709A002010B50B4C2378052BBF +:1066600010D10A4B0A4A1B68116899420AD10623C5 +:106670002370084B1B685868FEF70EF808B907230B +:10668000237010BD0820FCE7589A00206C9A002067 +:10669000549A0020CC9A0020044B1B78072B02D17F +:1066A000034B9B6818470820704700BF589A00208A +:1066B0005C9A002000B589B006238DF80C30079B4A +:1066C000009303AB0FCBFEF703FE09B05DF804FBAC +:1066D000F0B58DB005A8FEF76DFF089C002C3ED0EC +:1066E0000B9E04F58053B3422DD91E4BA6F5805561 +:1066F00003EA55054FF440539B689C420BD8690050 +:106700002B46A4EB450201F5805106EB4500FFF74F +:1067100029FE0DB0F0BD05F58053012701A8CDE994 +:106720000173CDE90337FFF72DFD0028F1D14FF4B8 +:10673000805301A8CDE9023301970497FFF722FDAA +:106740000028E6D1DBE70123CDE90136A4084FF4A8 +:10675000805301A803930494FFF714FDD9E7204662 +:10676000D7E700BF00F0FFFF00B58DB005A8FEF72A +:1067700021FF099880B1089B8BB94FF440530B4A15 +:10678000596891420ED19B6880080022039001A8AD +:10679000CDE90123FFF7F6FC0DB05DF804FB0B9A81 +:1067A0001344F1E74FF48053EEE700BFDBE5B1514E +:1067B00000B58DB005A8FEF7FDFE099898B1089BBD +:1067C000A3B94FF440530C4A5968914211D19B68C8 +:1067D0000393800803214FF47422049001A8CDE9AB +:1067E0000112FFF7CFFC0DB05DF804FB0B9A1344C8 +:1067F000EEE74FF48053EBE7DBE5B15110B58CB019 +:1068000005A8FEF7D7FE0898B8B10B9C00F5805399 +:10681000A34214D94FF440539B6898421BD80F4BA6 +:10682000A4F5805203EA52035900A0EB430201F59C +:10683000805104EB4300FFF795FD0CB010BD8008BC +:1068400003224FF48053049001A8CDE9012303945F +:10685000FFF798FCF1E70E20EFE700BF00F0FFFF25 +:10686000A8DF7047AADF7047ADDF7047AEDF704723 +:10687000B0DF704762DF70472DE9F0470E4694B0F5 +:106880000546002800F00181002900F0FE804B68D9 +:10689000002B00F0FA804FF6FF7303800023ADF861 +:1068A0000A307B4B04AA03F1100C1746186859688C +:1068B000144603C4083363452246F7D141F23053EE +:1068C0000DF10A013846ADF80830FFF7D3FF044652 +:1068D000002840F0D6802A1D02A90120FFF7C0FF42 +:1068E0000446002840F0CD809DF80A30AB71014687 +:1068F0001C220DA8FDF750FD9DF834300E9443F096 +:1069000004038DF8343001AFAB798DF80E30214699 +:1069100041F2325303223846CDE91044CDE9124406 +:10692000ADF80C30FDF738FD9DF806308DF80440C9 +:1069300023F01F0343F00303214614224FF0110AF2 +:1069400008A88DF806308DF805A00DF10C08FDF7AC +:1069500023FD4FF01409A8880A9405F1080308AA3A +:106960000DA90C94CDE90887ADF82C90FFF77AFFBC +:106970000446002840F0858001461C220DA8FDF742 +:106980000BFD9DF834300E9423F0180343F01803E8 +:106990008DF83430AB798DF80E30214641F2315309 +:1069A00003223846CDE91044CDE91244ADF80C304D +:1069B000FDF7F2FC9DF806308DF8044023F01F032C +:1069C00043F0130321464A4608A88DF806308DF897 +:1069D00005A0FDF7E1FC1723ADF82C30A8880A9438 +:1069E00005F1100308AA0DA90C94CDE90887FFF75B +:1069F00039FF0446002844D101461C220DA8FDF7AA +:106A0000CBFC9DF834300E9443F002038DF8343003 +:106A1000AB798DF80E30214641F2345303223846CB +:106A2000CDE91044CDE91244ADF80C30FDF7B4FCCB +:106A30009DF806308DF8054023F01F0343F0030353 +:106A400021464A4608A88DF806308DF804A0FDF7C7 +:106A5000A3FC02230A93ADF82C30A8880C9605F10C +:106A6000200308AA0DA9CDE90887FFF7FBFE04461D +:106A700038B97368AB62B36803B1EB62054B0122AE +:106A80001A70204614B0BDE8F0870E24F9E700BF65 +:106A9000B9BB0F00D09A002070B5054686B070B320 +:106AA00002884FF6FF739A422BD0174B1B7843B3E3 +:106AB000164C1022080AE170207121FA02F0090E2A +:106AC000072301266071A17102A800216370ADF84F +:106AD00006302270A670FDF75FFC2B8AADF80830F7 +:106AE0000023ADF80C3028888DF80A600DF10603FC +:106AF00002A9CDE90434FFF7B9FE06B070BD0E203F +:106B0000FBE70820F9E700BFD09A0020D19A0020C7 +:106B100030B5044687B060B302884FF6FF739A42DF +:106B200029D0164B1B7833B3154D11232B700B0A4C +:106B30006970AB700B0C090EEB70297105230021F5 +:106B4000102202A8ADF80630FDF726FC238AADF826 +:106B5000083001238DF80A300023ADF80C3020886E +:106B60000DF1060302A9CDE90435FFF77FFE07B05A +:106B700030BD0E20FBE70820F9E700BFD09A0020C7 +:106B8000D19A002030B5044687B038B300884FF65C +:106B9000FF73984224D0134B1B780BB3124D102374 +:106BA00069700321ADF80610AA7000211A4602A8E8 +:106BB0002B70FDF7F1FB238AADF8083001238DF827 +:106BC0000A300023ADF80C3020880DF1060302A92D +:106BD000CDE90435FFF74AFE07B030BD0E20FBE7D4 +:106BE0000820F9E7D09A0020D19A002070B50D4610 +:106BF00088B0044650B149B1826A3AB10B88502B33 +:106C000049D005D8102B43D0112B54D008B070BDFB +:106C1000512BFBD18E79022EF8D10A89038A9A4230 +:106C2000F4D18B7B043B022BF0D99DF816308DF804 +:106C3000106043F001038DF816300B8AADF8183060 +:106C40004B8AADF81A30082201F1140301A8002183 +:106C50000793FDF7A1FBA18A2088019601AACDF830 +:106C600008D0FFF701FE48B3E36A03B1984740F24A +:106C7000FD132088ADF8143004A9FFF7F9FD0028B2 +:106C8000C4D0E36A002BC1D008B0BDE870401847FB +:106C90008B882380BAE7C98803899942B6D1082333 +:106CA0008DF81030123535F8023C8DF81830059506 +:106CB00004A99047AAE74FF6FF73EAE7BDF8003052 +:106CC0002088DB07D3D5002604A9ADF81460FFF7B0 +:106CD000CFFD0028D5D1297D4B1E072B3ED8DFE8FC +:106CE00003F004192226282A3B2C6B8A8DF80460B5 +:106CF000012B05D8062201212046FFF743FFBEE7FE +:106D0000012315358DF80C300295A36A01A92046A0 +:106D100098477BE76A8A01239A428DF80430F0D8BD +:106D200006220221E8E702238DF80430EDE7032371 +:106D3000FAE70423F8E70523F6E76B8A022B02D86B +:106D400003220821D8E7B5F81530ADF80830002B3C +:106D50000CBF07230623E7E70923E5E70322CBE778 +:106D6000A8DF7047AADF70472DE9F04180468EB05A +:106D700015461F460E4611B9084600F09FFD15B98D +:106D8000284600F09BFD1C220DEB02000021FDF7C0 +:106D900003FB9DF81C30ADF80480002443F002038F +:106DA0008DF81C3021460123032268468DF80630F9 +:106DB000CDE90A44CDE90C440894FDF7EDFA3B789F +:106DC0008DF800307B788DF801309DF8023023F08B +:106DD0001F0343F002032146142202A88DF802305B +:106DE000FDF7DAFA0A48CDF80CD001AB029302AAFB +:106DF000149B0088ADF8105007A9ADF81240ADF80B +:106E000014500696FFF7AEFF0EB0BDE8F08100BF4C +:106E1000F89A002030B587B041F60A032B4AADF846 +:106E20000C30044603A901208DF80E00FFF798FFEF +:106E30000546D8B92288E2B922894AB1244B009389 +:106E4000E16804F13C0342F62420FFF78DFFD8B936 +:106E5000228C4AB11F4B0093616A04F13C0342F655 +:106E60002620FFF781FF78B9A36B7BB9284607B0CE +:106E700030BD194B0093616804F13C0342F62920B0 +:106E8000FFF772FF0028D7D00546EFE71A788DF894 +:106E900010205A888DF81120120A8DF812209A8835 +:106EA000DB888DF815301B0A8DF813208DF816300D +:106EB000120A0A4B8DF814200093072204F13C03B8 +:106EC00004A942F65020FFF74FFFDDE7F89A0020B3 +:106ED000E89A0020D89A0020E09A0020F09A00203A +:106EE00029DF704728DF7047064B182202FB00306D +:106EF00000230422C0E90423037183608361C3601B +:106F0000704700BF0C9B002023B502460846C968A5 +:106F100043680093044B53F82150436910F80C1B4D +:106F2000A84702B020BD00BFFC9A002038B5194C1C +:106F30002378182202FB03431A795869012A03D0E7 +:106F4000032A1AD00F2038BD134A996915689A6828 +:106F5000DB68A2EB0532B2F5805F184401EB053126 +:106F600000EB053034BF92084FF48062FFF7B8FFA2 +:106F70000028E8D10123A370E5E74FF080531B6997 +:106F80009BB2B0FBF3F0044B1B681844FFF7AAFF59 +:106F9000EEE700BF0C9B0020049C002070B5134D51 +:106FA0006C780A2C1FD02E783444E4B2092C84BFAC +:106FB0000A3CE4B2182606FB0454A261207103C9FE +:106FC000A360049BE360AB7804F1100282E8030045 +:106FD00023B100206B7801336B7070BDFFF7A6FF03 +:106FE0001128F7D1F5E70420F7E700BF0C9B00203C +:106FF00070B5234CA3782BB100260228A67002D0CE +:10700000032833D070BD25781E4A182101FB0541A5 +:10701000136889680133B1EB033F136014D86378B8 +:107020001660013B63706B1CDBB21821092B01FB5E +:10703000054188BFA5F10903002004312370FFF743 +:1070400063FF2846FFF750FF6378002BDAD0A37860 +:10705000002BD7D1FFF76AFF0028D3D01128D1D059 +:107060002178182303FB0141043105E0217818231E +:1070700003FB014104310D20BDE87040FFF744BF20 +:107080000C9B0020049C002008B50A4B00211960CD +:10709000094B1980997008460131FFF725FF0A292D +:1070A000F9D1064B00201860054BC3E90000C3E985 +:1070B000020008BD049C00200C9B0020009C0020C6 +:1070C000FC9A0020064A03461068042807D008608E +:1070D000411C11601A68034B43F8202000207047C0 +:1070E000009C0020FC9A002013B5CC180C43A40788 +:1070F00008D1009313460A4601460120FFF74EFFD0 +:1071000002B010BD1020FBE707B500220B4600922D +:1071100001460320FFF742FF03B05DF804FB0000C7 +:10712000094B5A7899780132D2B2914208BF0022B5 +:10713000197891421FBF02705878182202FB003064 +:1071400014BF043000207047089C0020082910B5A7 +:10715000044602D0002000F0B1FBD4E90030BDE8C5 +:107160001040184773B5054600240DF107000E4680 +:107170008DF8074000F0B0FB0DF10600FFF7D0FFDF +:1071800090B10670094B9DF8062045605A709DF835 +:10719000070000F0C5FB24B9054B4FF48012C3F87B +:1071A0000021204602B070BD0424F0E7089C0020B6 +:1071B00000E100E0204B21491A682F2300BF00BFE7 +:1071C00000BF00BF00BF00BF00BF00BF8A422FD07A +:1071D00000BF00BF00BF00BF00BF00BF00BF00BFB7 +:1071E00000BF00BF00BF00BF00BF00BF00BF00BFA7 +:1071F00000BF00BF00BF00BF00BF00BF00BF00BF97 +:1072000000BF00BF00BF00BF00BF00BF00BF00BF86 +:1072100000BF00BF00BF00BF00BF00BF00BF00BF76 +:1072200000BF00BF00BF00BF00BF00BF00BF00BF66 +:10723000013BC3D1704700BF388400200024F40014 +:107240000C4B0D484FF4003210B5C3F880200124D8 +:107250004FF48033C0F84833C0F808334460FFF778 +:10726000A9FF064B846000201860FFF7A3FF044BC2 +:10727000187010BD00E100E000100140249D0020C6 +:10728000159D00202DE9F3412549264B0025C1F825 +:107290004051C1F84451C1F84851C1F84C51C1F8AE +:1072A0000051C1F804511B68002B34D0D1F80445BB +:1072B0001D49DFF888800968641A24F07F442F464E +:1072C0001968A14212D81A7CDE69641A0D4462B1B1 +:1072D0005A691F7400929B690193424608216846CF +:1072E00000F056FA08B100F0E9FABEB90F4A104BA7 +:1072F00011781B788B4205D10133DBB2022B08BF1A +:107300000023137012780B4B43F822500A4B4FF4B2 +:107310008012C3F8002102B0BDE8F0813346CFE708 +:1073200000100140289D0020249D0020219D002068 +:10733000209D0020189D002000E100E04D710F000D +:107340002DE9F74FA84AA94913780978A84C994222 +:107350003BD00133DBB2022B08BF00231370A549D9 +:107360001278A54B0F6853F822003B1823F07F4397 +:1073700000220B60236815461646944613B942B1A5 +:10738000236006E0196881420DD902B12360091A11 +:10739000196001262368DFF8689200930027BDB9C1 +:1073A000DFF868A268E0401A0E44D968D3F81CE000 +:1073B000C3F800C031B1BA1922F07F42C3E90121FC +:1073C000DD611D4673460122D8E700252E46E1E720 +:1073D0002846ED69874BD0F804C01B68DFF830E21F +:1073E0008168ACEB030222F07F42724500F2AD806F +:1073F0000A4402600122027422680023C0E90133BA +:10740000C361002A40F0AB802060C8E75A1C9AF89C +:107410000210D4F800B0D2B291428AF8002004BF22 +:1074200000228AF80020182202FB03A31A79986828 +:10743000022A77D0032A00F08580012A1CD190F817 +:1074400010C0BCF1000F17D1D96841601A69826081 +:107450005A69C2609B698361684B1B78002B18BF17 +:1074600061464160B6E7904200F09E809046D26946 +:10747000002AF8D1002303749AF800309AF801200A +:107480009A42C3D1236827B9009A9A4201D1002EAB +:1074900042D0002B00F08580D3F80090584C554B1B +:1074A000D4F804651868574F351A3B7825F07F45A6 +:1074B00003359BB94FF48033C4F84433C4F8043324 +:1074C000514B4FF400324FF00108C3F880211A608D +:1074D000C4F80080FFF76EFE87F80080A9452CBF36 +:1074E0004844401920F07F40C4F84005D4F80435E2 +:1074F0009B1B23F07F43801B033320F07F4083429C +:107500000AD9D4F80435C4F84035FFF753FE3E4B92 +:107510004FF40032C3F80021384B00221A7003B038 +:10752000BDE8F08F5A46D846A2E78BF81020DBF86A +:107530001CB00123BBF1000FF7D1002B9CD0C4F885 +:1075400000B099E700231A46F4E7A3EB0C0323F0FD +:107550007F438B4234BFCB1A002303604AE70168A4 +:10756000136899421BD85B1A1360C2614CE7A1EB08 +:107570000C01D3F81CC01A46BCF1000F0AD06346B8 +:10758000D3F800C08C45F2D3ACEB010CC3F800C0BB +:107590009C4613460160C0F81CC0D861FFE6134644 +:1075A000EEE7FFF74DFEB7E7404510D1DBF81C30A2 +:1075B000236063B9DFF83CE001920121C9F80810AB +:1075C000CEF800300D4B1970FFF7F4FD019A1368E7 +:1075D000D269C8F81C2012B111680B4413602368EB +:1075E0005B4518BF012745E7209D0020219D002015 +:1075F000289D0020249D0020189D0020149D00201F +:1076000000100140159D002000E100E0089C0020D2 +:10761000FEFF7F0008B5FFF713FE104B00200B2282 +:1076200018809A700E4B18600E4B18700E4B187025 +:107630000E4B4FF48012E021C3F8802183F814131D +:107640001A6002F18042A2F56F22C2F8080583F8A1 +:107650001113074BD2F804251A6008BD089C0020BE +:10766000289D0020209D0020219D002000E100E0B9 +:10767000249D0020074B9B784BB132B128B10368A1 +:10768000187C20B959745A61704707207047082048 +:10769000704700BF089C00202DE9F743DFF8848085 +:1076A00098F8023005460E461746ABB3A0B304293E +:1076B00030D9436983B3437C0024012B0DF10700CB +:1076C0000CBF8946A1468DF8074000F005F90DF181 +:1076D0000600FFF725FDD8B1012303700F4B45606D +:1076E000D3F80435C0E90497C0E902369DF80630A6 +:1076F00088F801309DF8070000F012F924B9084B12 +:107700004FF48012C3F80021204603B0BDE8F08397 +:107710000424EFE70724F7E70824F5E70010014009 +:1077200000E100E0089C0020064A92783AB130B1AE +:10773000426922B1002202740221FFF713BD082022 +:10774000704700BF089C00204B1C30B5DB0004468E +:1077500012F003009BB20DD1074D2A601A44074B6B +:107760001A60074B1870074B1870074B1C80074BAB +:10777000198030BD0720FCE7349D0020309D00209B +:107780002C9D00203C9D0020389D00203A9D00202B +:107790002DE9F347DFF8C080B8F800308B42064689 +:1077A0000D4617464CD300240DF107008DF8074015 +:1077B00000F092F8244B254A25481B78008892F85F +:1077C00000C084455FFA8CF138BF4C1CDBB238BF77 +:1077D000E4B2A3422ED014781178CBB2884286BF8F +:1077E0000133DBB2002313709DF8070000F098F816 +:1077F0004FF6FF739C4225D0DFF86090D9F8002047 +:107800004FEAC40A42F8347002EBC403AEB1A5B12A +:10781000104BB8F800001B682A4604FB00303146C4 +:1078200003F0A4FDD9F80030534400209D8002B03D +:10783000BDE8F0874FF6FF74D6E700209880F6E7A2 +:107840000920F4E70420F2E73C9D00202C9D002055 +:107850003A9D0020309D0020389D0020349D00205E +:1078600070B5104C104D22782B789A4200D170BD23 +:107870000E480F4A2378126806880E4802EBC301AF +:10788000006852F83320898803FB060090470A49B4 +:1078900022780988D3B2914286BF0133DBB200233C +:1078A0002370E0E73C9D00202C9D0020389D0020A7 +:1078B000349D0020309D00203A9D00201FB50021FE +:1078C000CDE9021001AA44F20100ADF80410FCF762 +:1078D00066FF05B05DF804FB70B5EFF3108672B675 +:1078E0000C4A946801239CB993600B4B0B4DD3F861 +:1078F000801029401160C3F88050D3F88410516083 +:107900004FF0FF32C3F88420047006B962B670BD30 +:107910000370FAE7409D002000E100E0FC06FFBD97 +:1079200010B5084B9A685AB150B9EFF3108172B68E +:10793000054A1C6814605C685460986001B962B6BE +:1079400010BD00BF409D002000E100E003462AB1C9 +:1079500010881A4619448A4203D170474FF6FF70C7 +:10796000F7E712F8013B40BA80B25840C0F3031366 +:10797000584080EA0033580100F4FF509BB2584051 +:10798000E9E70000064B074A00201870064B1A6012 +:107990000822C3E90120C3E90300C3E905007047D9 +:1079A0004C9D0020509D002030B0002000207047EA +:1079B00030B5F9B1124B5C6800220A60E4B1B0F551 +:1079C000167F1BD8D868013C01305C60D8601C6809 +:1079D00018694FF4177505FB00440C60012101FA8A +:1079E00000F49969013000F00700214318619961A2 +:1079F000104630BD0E20FCE70420FAE70C20F8E723 +:107A000030B00020F0B51C498A689AB34D690E6801 +:107A1000AC1A04F0070423464FF4177707FB036CF6 +:107A2000604511D1012000FA03F58869684088613A +:107A300000204E68D1F818C04FF0010E73440025A5 +:107A4000164403F007030AE0013303F007039D42E5 +:107A5000E4D11020EDE74AB1013A1C4601250EFAA7 +:107A600004F414EA0C0FA6EB0207F4D00DB1C1E93F +:107A70000172F0BD0420FCE730B00020064A136913 +:107A80001268013B4FF4177103F0070301FB032356 +:107A9000C3F858020020704730B0002030B5C0B1A4 +:107AA000B9B10E4BDA68B2B1013ADA609A681C6873 +:107AB00001329A605A694FF4177505FB024404605D +:107AC0000132D4F85802086002F007025A6100201F +:107AD00030BD0E20FCE70420FAE700BF30B00020E4 +:107AE0003FB40C49086890B10B4B1C687CB10B4A41 +:107AF0001568CDE9025000238DF804300B60136047 +:107B000004AB13E90700234604B030BC184704B0A7 +:107B100030BC704754B0002058B0002064B0002042 +:107B2000DC2810B509D0DD2810D0C02816D1FFF709 +:107B3000D7FF0E4B0E4A1A6010BD0E4A0E4B196845 +:107B40001368581C1060C022CA54F2E7094A0A4B55 +:107B500019681368581C1060DB22F5E7064B054ACC +:107B6000196813685C1CC8541460E2E74484002060 +:107B7000917B0F0054B0002064B00020C02802BFE9 +:107B8000014B024A1A60704744840020917B0F0029 +:107B9000C02810B409D0DB280BD0094B094A19685A +:107BA00013685C1CC854146006E05DF8044BFFF7D2 +:107BB00097BF054B054A1A605DF8044B704700BF3C +:107BC00064B0002054B0002044840020217B0F00CA +:107BD00007B501228DF807000DF10701002002F022 +:107BE00085FD00280CBF0420002003B05DF804FBD5 +:107BF00010B5064A064C12682368D05CFFF7E8FF10 +:107C000010B923680133236010BD00BF68B00020A5 +:107C10005CB0002008B5C020FFF7DAFF28B9034B9D +:107C20001B6813B9024B034A1A6008BD5CB0002000 +:107C300048840020F17B0F0008B5DB20FFF7C8FF68 +:107C400010B9024B024A1A6008BD00BF48840020E8 +:107C5000557C0F0010B50C4A0C4C12682368D35C9D +:107C6000C02B03D0DB2B0DD0042010BDDC20FFF790 +:107C7000AFFF0028F9D12368054A01332360054B83 +:107C80001A60F2E7DD20F2E768B000205CB0002067 +:107C9000F17B0F00488400207FB5184C184D194E19 +:107CA000002002F0C3FC30B322689AB1296833681F +:107CB00099420FD2012201A9002002F0C3FC10B9A1 +:107CC0004FF0FF3001E09DF804000F4BC0B21B687D +:107CD0009847E5E70D4B1B686BB10292084A0221F9 +:107CE000126803928DF8041004AA12E9070004B088 +:107CF000BDE87040184704B070BD00BF64B00020FC +:107D000054B0002050B000204484002058B000201F +:107D1000014B18600020704758B00020034B1A78C0 +:107D20000AB901221A700020704700BF4CB0002031 +:107D3000014B0020187070474CB000202DE9F04F27 +:107D400085B0002851D02A4F3B78012B07D0022B59 +:107D500014BF08240424204605B0BDE8F08F254D4B +:107D6000DFF8A090244E254CDFF89C80DFF89CA023 +:107D7000DFF89CB0C9F8001000232B6002233060AC +:107D80003B70C4F800802A68D9F800309A4215D3B5 +:107D9000C4F80080FFF73EFF044608BB184B1B6881 +:107DA00001223A70E3B18DF80420326802922A6809 +:107DB000039204AA12E907009847CCE733682A68BF +:107DC0009A5CC02A03D02A689B5CDB2B04D1236811 +:107DD000534508BFC4F800B023689847042801D170 +:107DE0000024B8E71128CED1FAE71024B3E700BF8A +:107DF0004CB000205CB0002068B000204884002017 +:107E000058B0002060B00020157C0F00F17B0F00FF +:107E1000397C0F00054B064A1860064B1960064B6B +:107E200000201860054B1A60704700BF64B0002046 +:107E30007D7B0F0050B0002054B00020448400200F +:107E4000064B07481B68DB00DBB2002203705B4275 +:107E500042708270C3700421FFF770BF94B000209D +:107E60006CB0002070B52B4C2B4D02462378012BB3 +:107E700014D0022B21D0002B4BD1002A49D1274806 +:107E8000FFF752FC08B1FFF719FD254B1B68002BCB +:107E90003FD0244ABDE8704010781847012A38D1F5 +:107EA0002968214B06311868FFF748FF08B1FFF732 +:107EB00005FD022323700022D8E7022A16D0032AE8 +:107EC0000CD032BB194B15481A6041F67F21FFF7E1 +:107ED000E3FBF0B1BDE87040FFF7F0BC144A136853 +:107EE000013303F0070313600023E3E70F4A13682D +:107EF000052B0AD001331360074B19680A4BBDE804 +:107F0000704018680631FFF719BF064B01221A703E +:107F1000EAE770BDB8B00020ACB0002070B000201F +:107F2000A8B00020B0B00020C0B00020B4B0002045 +:107F300098B00020F0B585B004AB03E907009DF8C8 +:107F40000400032874D8DFE800F00802A3A601208B +:107F500005B0BDE8F040FFF785BF039E544C032EEB +:107F600040F28280029D6B7813F00F0265D00E2ADA +:107F70007AD1042E55D02A78500652D5110650D504 +:107F80001A44AB781A44EB781A4412F0FF0248D135 +:107F9000B71E39462846FFF7D9FCEB5B834240D138 +:107FA00044492A780B6802F00702D8B282422BD1EA +:107FB000013303F007030B60FFF742FF3E4B012242 +:107FC00030461A70FFF75AFD08B1FFF777FC3849C1 +:107FD0004FF41670FFF7ECFC002862D0042802D0A2 +:107FE0000020FFF76BFC35480521FFF713FF08B1B0 +:107FF000FFF764FC324B1B68002B56D04FF000009B +:1080000005B0BDE8F040184720684FF41671FFF73F +:1080100001FF08B1FFF752FC05B0BDE8F040FFF7E3 +:108020000FBF20684FF41671FFF7F4FE00283CD014 +:1080300005B0BDE8F040FFF741BC2978AA780B44B1 +:108040001344EA78134413F0FF030DD11D4A12685C +:108050000132C1F3C20102F00702914204D11A4A6F +:1080600003201370FFF7FEFE25681DB14FF4167153 +:108070002846D9E70E494FF41670FFF799FC60B116 +:10808000042802D02846FFF719FC0C480521CBE74D +:108090000A480521C8E70320CAE720684FF4167193 +:1080A000C2E720684FF416719FE705B0F0BD00BF2E +:1080B000BCB0002094B0002090B000209CB0002004 +:1080C000A4B0002098B00020B0B000200220FFF73C +:1080D000C9BE0000074B10B5044618600648FFF7FC +:1080E00017FE08B1FFF7EAFB002C0CBF0E200020A2 +:1080F00010BD00BFA4B00020357F0F00184A1948FA +:10810000002310B51360184A1360184A1360184A08 +:108110001370184A1370184B184A01211960184B34 +:108120001960184B1970FFF7A5FA08B1032010BDAC +:10813000FFF728FC0028FAD1FFF7F0FD0028F6D160 +:10814000114C4FF416702146FFF732FC0028EDD198 +:1081500020684FF41671BDE81040FFF75BBE00BF0A +:10816000C0B00020CCBB0F00ACB00020B4B00020E9 +:1081700090B00020B8B0002094B00020CD800F0057 +:1081800098B00020B0B00020BCB000200C4A08B568 +:10819000002313600B4A1360FFF708FC08B1FFF7D8 +:1081A0008DFBFFF7C5FD08B1FFF788FB0648FFF719 +:1081B000BBFA042802D10020FFF780FB002008BD95 +:1081C000A8B00020A4B0002070B0002037B50D4644 +:1081D000044698B191B10A4B19780022019259B125 +:1081E00001A91A70FFF75AFC019B063B2B802368FC +:1081F0000433236003B030BD0420FBE70E20F9E711 +:1082000090B000200438FFF7FDBB18DF7047000076 +:10821000F0B51D46154B87B018680F4659681B7A94 +:1082200003AC03C42370124B18685968114B0093B8 +:1082300001AC03C42046164603F042FA214602462A +:10824000384603F093F801A803F03AFA01A9024670 +:10825000304603F08BF8684603F032FA694602466E +:10826000284603F083F807B0F0BD00BFD0BB0F0075 +:10827000D9BB0F00312E30000120704710B51C46CD +:108280000B781E2B0AD000232022052102F07AFC55 +:108290004FF6FF70A04228BF204610BD0020F9E72E +:1082A000F8B5069F14460D463A46002118461E466C +:1082B000FCF772F87CB14FF0E023D3F8F03DDB0718 +:1082C00000D500BE4FF0FF300AE0284600F0F8F974 +:1082D000013504F50074BC4206EB0401F5D32046D9 +:1082E000F8BD0000F8B50A4F0D461E460024069B57 +:1082F0009C4206EB040101D32046F8BD3A462846CD +:1083000000F0B4FA0028F7D0013504F50074EEE768 +:10831000C4B0002030B5264D2A7A8DB09AB107AC92 +:108320001422002120460625FCF736F88DF81C5053 +:108330000B9B009394E80F00FCF7CAFF0620FCF7A4 +:10834000F7FC0DB030BD2B68002BFAD0194B197813 +:1083500019B105201A70FCF7EBFCD5E900329A42FE +:10836000EFD307AC142200212046FCF715F86B7AF6 +:10837000DBB106234FF420424FF474214FF4602008 +:108380008DF81C3002F0C0FF0028D1D0102200214F +:1083900003A8FCF701F84FF460224FF4205303A820 +:1083A000CDE90423FFF731FFC2E78DF81C30BFE7AA +:1083B000C4B000204C840020024B0B604FF40073CB +:1083C0001380704709010100012070470048704781 +:1083D000FA840020044B054A1878054B002814BF86 +:1083E00010461846704700BFE0B20020AF8400205E +:1083F0004D8400202DE9FF411E4B187020B11E4B0B +:108400002A229A720022DA721C4A1D4DDFF878C0C7 +:1084100017461C4BEE4603F110067446186859685F +:10842000F046A8E803000833B342C646F6D12B78DD +:1084300003F00F0310336B4413F8103CD373114B4C +:1084400018685968A646AEE803000833B34274467C +:10845000F6D115F8013B04A901EB1313654513F898 +:10846000103C9373A2F10202D3D100233B7404B0F9 +:10847000BDE8F081E0B20020FA84002064B300205F +:1084800060000010E1BB0F006800001010B570B96B +:10849000134B14481968022202F068FF01230133CC +:1084A000DBB211485B0043F44073038010BD052824 +:1084B00014D80B4B53F82040204603F001F9C3B207 +:1084C0001F2B28BF1F23084A2046E118884202F1CB +:1084D0000202E4D010F8014B1480F7E70020E5E732 +:1084E0000C850020E4B20020E2B200204DDF70478E +:1084F0004EDF70474FDF704750DF704712DF704725 +:1085000000F0C8BE002000F033BD00001FB5244BB2 +:10851000402283F8272300238DF807304FF440537F +:1085200004465A681F4B9A4227D10DF10700FFF706 +:10853000E5FF9DF8073003B30120FFF7D9FF0120C5 +:10854000FFF7D4FF0120FFF7D5FF02A8FFF7D4FF04 +:10855000029BDA0702D5002000F09CFE029B9B07DD +:1085600002D5022000F096FE2046FFF743FF00F000 +:1085700027F802F059FE04B010BD002301A88DF8C1 +:108580000430FCF721FC084B039303A8FCF744FCE0 +:10859000FCF748FC4FF08043D3F838340293D7E718 +:1085A00000E100E0DBE5B15101850F00012000F0A2 +:1085B00071BE0120FCF7BCBB0220FCF7B9BB000078 +:1085C0007FB52F492F4802F0E7FF4FF440532E4A62 +:1085D000596891424CD11A78102A46D9142A186940 +:1085E00044D95B69294CB3FBF4F50A2201A904FBC9 +:1085F000153403F021F92649224802F0CDFF01A9E4 +:10860000204802F0C9FF23491E4802F0C5FF0A2294 +:1086100001A9284603F010F901A91A4802F0BCFF8D +:108620001D49184802F0B8FF4FF47A760A2201A9D2 +:10863000B4FBF6F5284603F0FFF801A9114802F053 +:10864000ABFF15490F4802F0A7FF0A2201A906FB5C +:10865000154003F0F1F801A90A4802F09DFF0F4907 +:10866000084802F099FF04B070BD00200023B9E76C +:108670000B49044804B0BDE8704002F08DBF00BF54 +:10868000FFBB0F0024850020DBE5B15140420F0005 +:108690000CBC0F000ABC0F000EBC0F0019BC0F0071 +:1086A00010BC0F0010B503461C1A944200DB10BD2D +:1086B0000C781CB1013103F8014BF5E72024FAE7EF +:1086C0002DE9F3410C4605464FF400720021204687 +:1086D000FBF762FE6DB95E493E22204602F046FE7F +:1086E000552384F8FE31AA2384F8FF3102B0BDE897 +:1086F000F081B5F5017F2DD8691EB1F5817F24BFCA +:108700006FF4817805EB0801C9B10B02C1EBC151CF +:1087100003F5807004EB412440F693651A1FB2F50F +:10872000696F03F1010206D2AB4214BF91B24FF65A +:10873000FF7124F8131090421346EFD1D6E7F823C7 +:10874000237004F109022346FF2003F8010F93422E +:10875000FBD1DAE7B5F5027F3BD86FF4017C6544C5 +:108760003DB920463B490B22FFF79CFF2823E372CB +:10877000203439492E014FF0000801EB05256FF038 +:108780001907022EB2D80B2229462046FFF78AFF8E +:108790005923212269216374E3746376B31C84F83E +:1087A0000D80A773E1732274A27484F8148084F896 +:1087B0001580A775E17522766383E86830B102F011 +:1087C0007FFFE061013620341035DAE74FF4E9101D +:1087D000F7E7224B9D4289D86FF40277EA19012A04 +:1087E0000FD81D4B03EB0213D9680191084602F024 +:1087F00067FF01990246204602B0BDE8F04102F051 +:10880000B5BD6FF4FD76A9190902B1F5801FBFF45B +:108810006DAF134B236003F1144303F52C1303F6E0 +:10882000023363600F4BC4F8FC314FF46963A361FA +:108830004FF40053A5F20B254FF48072A3600A4B4E +:108840006561E1602261E36104F12000D4E700BFCB +:108850001CBC0F0047BC0F00D4BC0F000801010076 +:108860005546320A306FB10A29009A23F7B5654B95 +:1088700014460A689A420D4639D103F114434A68F6 +:1088800003F52C1303F602339A4230D1D1F8FC21C0 +:108890005D4B9A422BD18B6823F4FF5323F01E03C8 +:1088A0009B049B0CB3F5005F21D10B69B3F5807F6E +:1088B0001DD1C86810F0FF0619D1CB69534A934205 +:1088C00005D0534A934215D0524A93420FD1A0F596 +:1088D0008053B3F5692F07D201234FF4807205F15D +:1088E0002001FBF705FF22E0B0F5805F1FD34FF0BA +:1088F000FF3021E001276772CB68B3F1102F1DD143 +:1089000004223431684602F031FD042205F13801B9 +:108910000DEB020002F02AFD009BB3F5742F03D18A +:10892000019BB3F57E2F01D02772E0E7A772AB69F8 +:10893000002B37D14FF4007003B0F0BDA3F57422C3 +:10894000B2F5204F28D2E27A01F12007DAB9324A93 +:10895000934218D32B69B34215D90422B91968463A +:1089600002F004FD009BD02B14D10422311D0DEB2D +:108970000200394402F0FAFC264B019A9A424FF069 +:1089800001030DD1E372E8682A6901233946A0F595 +:10899000A030A6E70836DDE7B3F5805FC7D3012333 +:1089A0002372A4E72268934207D041F263018B420D +:1089B00000D80AB14FF0FF3323606B6941F26302C4 +:1089C0009342B7D803F0070204EBD303012191408F +:1089D0001A7B1142C8B204D16168024301311A7393 +:1089E0006160D4E900329A42A4D30120FBF762FE11 +:1089F000637A002B9ED0A37A002B9BD10123237294 +:108A000098E700BF5546320A306FB10A4028A5AD3D +:108A10003C8263D629009A2300D80F004FF0805380 +:108A2000D3F83001082802D1D3F8343123B9A0F1AA +:108A30000D0358425841704701207047094B0122ED +:108A400083F8D8200260BFF36F8FBFF34F8F064AC1 +:108A5000904202D0043A904202D1002283F8D820FA +:108A6000704700BF78B300205070024042DF70476B +:108A700043DF704744DF704712DF704710B5134B78 +:108A8000134A5B68C3F3080373B9EFF310835BB950 +:108A900010494B681B0607D58024C1F8844092F822 +:108AA000D8307BB14C60F8E792F8D83033B101464A +:108AB000BDE810400848012201F094B8BDE810401C +:108AC000FFF7BCBFFFF7BAFF4C6010BD00ED00E040 +:108AD00078B3002000E100E07D8A0F000D4B1822E2 +:108AE00002FB0033598A1A8A521A998A92B28A4230 +:108AF00028BF0A46D9681423434303F1804303F592 +:108B00001C33C3F80016C3F80426034B03EB8000A4 +:108B1000FFF7B4BF78B300200470024007B54FF4EC +:108B2000405300205A68084B9A420AD18DF807003A +:108B30000DF10700FFF7A0FF9DF80700003818BFF0 +:108B4000012003B05DF804FBDBE5B15107B5FFF789 +:108B5000E5FF58B1002301A80193FFF78BFF0198AF +:108B6000003818BF012003B05DF804FB4FF08043CC +:108B7000D3F80C0400F00110A0F101135842584141 +:108B8000F1E70000074BD3F8C024D10309D406490C +:108B90000648D1F8C010C3F8A017C3F8A427FFF700 +:108BA0006DBF70470070024078B3002048700240EB +:108BB0000828F0B402D1F0BCFFF7E4BF0F4D104A13 +:108BC000182141436E1800F59473695852F82340F8 +:108BD000F788B388DB1B9BB2A4B2A34228BF23460D +:108BE000142404FB0022C2F80017C2F80437054B16 +:108BF000F0BC03EB8000FFF741BF00BF78B300205B +:108C0000007002402870024070470000014B802233 +:108C10005A60704700E100E0024B8022C3F88420D4 +:108C2000704700BF00E100E0074BD3F80014D3F811 +:108C300000240A43C3F800240022C3F858214FF44B +:108C40008002C3F8042370470070024070B5887832 +:108C5000404D00F07F0318220C26C4095A4306FB3E +:108C600004228E882A44C6F30A061681CA7802F0C6 +:108C70000302012A29D00121374A01FA03F5D4B9A8 +:108C800003F10C06B140C2F80413D2F8141503F531 +:108C900094732943C2F8141542F823402E4BC3F8AD +:108CA000180540F48070C3F80C05BFF36F8FBFF355 +:108CB0004F8F012014E002339940C2F80413D2F818 +:108CC00010352B43C2F81035E8E7082B09D04FF0D8 +:108CD000E023D3F8F00D10F0010001D000BE002019 +:108CE00070BD1D4BDCB9B5F8D42012B18022C3F899 +:108CF0001C250022C3F85021D3F8002312F40012DF +:108D000008BFC3F85421144B4FF44012C3F8042396 +:108D1000D3F8142542F48072C3F81425BEE700226C +:108D2000C3F82C21B5F8C82012B18022C3F81C2545 +:108D3000094BD3F8002312F4001208BFC3F85421E2 +:108D4000064AC3F80423D3F8102542F48072C3F80E +:108D50001025A3E778B3002000700240000820002F +:108D60001D4B2DE9F041802201241C4DDFF8788055 +:108D7000C3F88420274604F10C03A21C07FA02F270 +:108D800007FA03F31343C5F80833A30003F1804344 +:108D900003F51C330026182202FB04805E60314676 +:108DA0009E620134FBF7F8FA082C4FF01802E2D16A +:108DB0000B4BC5F808330B48C5F81C6531466E628D +:108DC000AE64FBF7E9FA044BC5F814758022C5F8C8 +:108DD00010755A60BDE8F08100E100E000700240CB +:108DE0000008300038B4002078B30020F7B51F46E3 +:108DF00001F07F0018231F4CCD090E4643430C2180 +:108E000001FB053104EB010C62500022ACF8047048 +:108E1000ACF8062018B1F5B1FFF760FE18E017BBFB +:108E2000154BD3F88034C3F3C0139D421BD01348B5 +:108E3000FFF724FE124B5B68C3F30803003B18BF27 +:108E4000012300933A463B463146384600F0B5FED2 +:108E5000012003B0F0BD1C44A37A002BF8D0A5720A +:108E6000FFF7A6FEF4E7002DD6D10648FFF706FE71 +:108E7000EEE700BF78B3002000700240507002405F +:108E800000ED00E04C70024011F07F0008B507D102 +:108E90000D4B01225A65BFF36F8FBFF34F8F08BD93 +:108EA0000828F8D0084B41F48072C909C3F8182586 +:108EB000F1D1064B182202FB00339A7A002AEAD03D +:108EC0009972FFF775FEE6E70070024078B3002064 +:108ED00011F0770F12D00A4B41F48072C3F80C15D1 +:108EE000C3F80C25CA09C3F8181504BF01F594711D +:108EF00043F82120BFF36F8FBFF34F8F704700BF40 +:108F000000700240174B0122002110B5C3F8142550 +:108F1000C3F810250A468B0003F1804303F51C3388 +:108F2000013108295A609A62F5D10E4B0E4C5A62F3 +:108F30009A64C3F85821D3F80014D3F800240A43E4 +:108F4000C3F80024D3F80023C3F80823074AC3F862 +:108F500004230021DC222046FBF71EFA4023A382D3 +:108F6000238110BD0070024078B300200514C001B9 +:108F70002DE9F04FB24BB34AD3F80013002385B06C +:108F80001C4601201D4621FA03F6F6070BD552F8C0 +:108F9000236046B100FA03F6344342F82350BFF38E +:108FA0006F8FBFF34F8F0133192BECD1E20706D53A +:108FB000FFF7A8FF00210122084600F0D5FD14F4B8 +:108FC000006FA14D08D09E4BD3F8A8369BB2A5F8F0 +:108FD000D230012385F8D730A30221D5984ED6F898 +:108FE000143513F4807302D0FFF7CCFD0123D6F8BB +:108FF0001025D70540F1188195F8D7305BB1B5F849 +:10900000D2200023012185F8D73092B20091184672 +:10901000882100F0D2FD01220321002000F094FD00 +:10902000660228D5864BD3F8006406F4E062F005AA +:10903000C3F8002406D50122C3F82C250421002002 +:1090400000F082FD71050FD57D4B0122C3F8082584 +:109050009A65D3F8002312F4001208BFC3F8542114 +:109060004FF40012C3F80423B20504D501220521F0 +:10907000002000F069FD23022BD5714BD3F880143A +:10908000C9B28DF80810D3F88424D2B28DF8092023 +:10909000D3F888048DF80A00D3F88C048DF80B00FF +:1090A000D3F890048DF80C00D3F894048DF80D00DB +:1090B000D3F898048DF80E00D3F89C348DF80F3057 +:1090C0004F0601D1052A04D0012202A9002000F098 +:1090D0005EFD5E4B23405BB195F8D830002B40F02D +:1090E000AB804FF0E023D3F8F03DDE0700D500BEA3 +:1090F000DFF85481DFF84891DFF858B14746002681 +:109100004FF0140A06F10C0324FA03F3D807F1B266 +:1091100022D50AFB0693082ED3F808273B6853FA9A +:1091200082F33B604FF0180303FB0653D2B2D8889A +:1091300012FA80F080B2D88000F08E803889904298 +:1091400040F08A80DB88BA889BB29A4240F28480E1 +:1091500011B95846FFF792FC0136092E07F118079E +:10916000D0D13B4B2340002B5BD0354BD3F86C94D4 +:10917000C3F86C94BFF36F8FBFF34F8F14F4806408 +:1091800007D0D3F88044D3F880341906C4F3C01450 +:1091900070D54FF0000A2C4F00264FF0180B29FA1B +:1091A00006F3DA07F0B201D4CEB9C4B1244B1422CD +:1091B00002FB0633FA68D3F8083652FA83F2FA60F3 +:1091C0000BFB0652518A89B251FA83F39BB2538248 +:1091D000538A398A9BB299424FD9FFF77FFC0136F7 +:1091E000082E07F11807DAD100241826012704F108 +:1091F000100329FA03F3DB07E0B203D464B9BAF130 +:10920000000F09D006FB0452B8F80410D3889BB2B3 +:1092100099423DD9FFF7CCFC0134082C08F118081D +:10922000E5D105B0BDE8F08F002B7FF4F4AE4FF42C +:109230000013C6F80833EEE6002385F8D83057E768 +:10924000007002400071024078B30020FCFB1F0058 +:10925000000400014C700240182303FB0653DA8817 +:10926000BA80DA8801230093002392B2184600F0F6 +:10927000A4FC71E74FF0010A8DE7528A01230093A5 +:10928000002340F0800192B2184600F096FCA6E759 +:109290009772C1E7012813B5044600F0C380022885 +:1092A00059D0002855D1784BD3F80025002A50D149 +:1092B0004FF48002C3F808234FF40062C3F800247F +:1092C000BFF36F8FBFF34F8FFFF7A8FB60B16F4BFA +:1092D000D3F8001C032269BB49F27531C3F8001CA6 +:1092E000C3F8142DC3F8001C4FF08053D3F830316D +:1092F000082B0CD1654BD3F8001CC022E9B949F208 +:109300007531C3F8001CC3F8142CC3F8001C5E4B65 +:109310000124C3F80045BFF36F8FBFF34F8FFFF7F2 +:1093200015FCB0B9FFF7FAFB50B102B0BDE8104030 +:10933000FFF79CBBC3F8142DD6E7C3F8142CE6E75F +:109340004FF08043C3F80001D3F800210192019A45 +:109350001C6002B010BD4C4CD4F804351BB1FFF7B3 +:10936000F5FB0028F5D1D4F800341B05FBD54FF4EC +:109370000063C4F80034BFF36F8FBFF34F8F4FF01B +:109380008053D3F83031082B0CD1404BD3F8001C5C +:1093900000293FD149F27532C3F8002CC3F8141CE0 +:1093A000C3F8002CFFF73AFB58B1384BD3F8001C38 +:1093B000A1BB49F27532C3F8002CC3F8141DC3F8E1 +:1093C000002C4FF08053D3F83031082B2E4B0AD1AC +:1093D00040F2E372C3F800284022C3F80428BFF328 +:1093E0006F8FBFF34F8F80220121C3F81C25C3F874 +:1093F0000413274BC3F884215A60FFF7A7FB00280A +:10940000FBD0214B0122C3F80425BFF36F8FBFF3BC +:109410004F8F9EE70022C3F8142CC3E70022C3F845 +:10942000142DCEE7184BD3F80025002A91D0002246 +:10943000C3F80425BFF36F8FBFF34F8F144980200B +:10944000C1F88400D3F80013C3F80813C3F800254B +:10945000BFF36F8FBFF34F8FFFF760FB78B1FFF75C +:1094600007FB0C4B5A68C2F30802003A18BF0122EE +:109470000221002002B0BDE8104000F065BB4FF0B3 +:1094800080435C60EDE700BF0070024000E00640F2 +:1094900000E100E000ED00E00A44034690B288429B +:1094A00002D39A89824202D25A89104480B270470C +:1094B00082888A4210B504D884898B1A9BB29C4258 +:1094C00003D243891A44891A8BB2038210BD9308D0 +:1094D00013B501EB8303044699420BD112F003024A +:1094E00006D0002301A8019301F040FF019B2360F7 +:1094F00002B010BD51F8040B2060EDE72DE9F043F8 +:1095000085B01446BDF83050AB4238BF4289A3EB5A +:1095100005091FFA89F938BFA9EB0209828838BF0B +:109520001FFA89F94A4588460746194605D2FFF7CA +:10953000BFFF058AB0F80490ADB2B9F1000F1FD09B +:10954000A14528BFA146BC88AC421DD9FA889DF828 +:1095500034003968661BA9EB04042C44B3B214FB35 +:1095600002F416FB02F60128B6B2A4B202FB051102 +:1095700016D099450BD802FB09F2404601F0F6FEE1 +:10958000484605B0BDE8F0832D1BADB2DCE732469E +:10959000404601F0EBFE3968224608EB0600EDE795 +:1095A000994506D819FB02F292B24046FFF78FFFA9 +:1095B000E6E726F00305ADB22A4640460191FFF7E3 +:1095C00086FF16F0030628D001990D44C6F1040168 +:1095D00089B2A1424FF0000328BF2146641A0393C9 +:1095E00003ABA4B2A8191A4685420CD13B68013ED0 +:1095F000164419448B420BD1039BC8F80030002C51 +:10960000BED02246D1E715F801CB03F801CBEBE73A +:1096100013F8012B06F8012FECE73968EFE713B5D3 +:10962000930800EB8303984209D112F0030204D09F +:109630000B68019301A901F099FE02B010BD0C68FE +:1096400040F8044BEFE770B59A42A2EB03041D46C5 +:1096500038BF4389A4B238BFE41A838838BFA4B2A4 +:10966000A3420E46114602D2FFF722FF848874B14E +:109670008288AA4208D9C2880168304602FB0511D7 +:1096800001F074FE012070BDAD1AADB2F1E72046C5 +:10969000F9E72DE9F0430746B0F80E908588048A73 +:1096A0001646C288007A85B01FFA89F9A4B288BB31 +:1096B000A145A9EB040038BF7C8980B23CBF001BE8 +:1096C00080B2281A80B2864228BF06464C46AC4279 +:1096D00028D2A5EB0408751B386825441FFA88FCBE +:1096E00015FB02F518FB02F8012B1FFA88F8ADB242 +:1096F00002FB040022D0664517D8724301F036FE03 +:10970000324649463846FFF7C7FEF881304605B075 +:10971000BDE8F083AE4221BF761B02FB0611A146D5 +:109720002E46D3E7641BA4B2D1E74246009101F074 +:109730001DFE009938682A464144DFE7664505D892 +:1097400016FB02F292B2FFF76AFFD9E728F0030492 +:10975000A4B22246CDE90001FFF761FF18F003082B +:10976000019929D0C8F104039BB2AB4200980A6862 +:10977000039228BF2B46013CED1A0DF10C0C20443E +:10978000ADB244466246013C1CF801EB00F801EF23 +:1097900014F0FF04F7D13868904408EB0304421E2C +:1097A000A04504D11844002DAAD02A46CBE718F8CA +:1097B00001CB02F801CFF3E73868F4E7B2F5004FC8 +:1097C00010D882805200C38092B29DF8003003729C +:1097D000531E838152420023C38101604281038270 +:1097E0000120704700207047C189028A89B292B275 +:1097F0009142A1EB020338BF428980889BB23CBFF3 +:109800009B1A9BB2984228BF18467047C289038AA8 +:1098100092B29BB2D31A58425841704710B5C189D1 +:10982000028A848889B292B2914238BF4089A1EB02 +:1098300002039BB23CBF1B1A9BB2E01A80B210BD60 +:1098400038B5C289038A04469BB292B2FFF7FBFE89 +:10985000218A054682B289B22046FFF71DFE20828A +:10986000284638BD73B5C389058A0026ADB20446C3 +:10987000CDE900569BB2FFF741FE218A054602461C +:1098800089B22046FFF708FE2082284602B070BD4C +:1098900038B5C589028AADB292B2AA42A5EB0203DD +:1098A00088BF42899BB288BF9B1A828888BF9BB2BF +:1098B0009A42044614D1007A90B938BD9B1A9BB2E3 +:1098C0009342FBD2E288206802FB030001F04EFDC8 +:1098D000012229462046FFF7DFFDE0810120ECE769 +:1098E0002B46EDE712B10023FFF7D3BE10467047B9 +:1098F0000023C381038283885B009BB25A1E5B42B4 +:10990000828143810120704701720120704700006D +:109910000B4B63B10B4B1B78834206D90A4B1B6878 +:1099200000EB400003EBC0007047C01AC0B2012832 +:1099300003D8064B00EB4000F4E70020704700BF5F +:109940000000000058B4002054B4002004BD0F00F3 +:1099500070B5104E054600242046FFF7D9FF436836 +:109960002846984733780134E4B20133A342F3DA4E +:10997000372200210848FAF70FFD1022FF2107487F +:10998000FAF70AFDBDE8704005481222FF21FAF7F8 +:1099900003BD00BF58B4002059B400205CB40020BF +:1099A0006CB4002037B50C460546C868019200F03B +:1099B000A5FDE368019A0021284603B0BDE83040C8 +:1099C000184773B5054614463AB90378012B04D1FC +:1099D00010460191FFF720F90199281DFFF758FF64 +:1099E00006462CB92B78012B02D12046FFF70EF941 +:1099F00036B94FF0E023D3F8F03DDB0700D500BEC9 +:109A000002B070BD024B5878003818BF0120704773 +:109A100059B40020024B1878C0F38000704700BF93 +:109A200059B40020014B1878704700BF90B4002053 +:109A3000F8B5164E3178054629BB154C1548372226 +:109A4000FAF7AAFC201DFFF753FF134B1C60134BC2 +:109A500023B11348AFF30080124B1860104F00245D +:109A60002046FFF755FF036898473B780134E4B27E +:109A70000133A342F4DA2846FFF7C6F82846FFF779 +:109A8000C5F8012333700120F8BD00BF90B4002059 +:109A9000A486002059B4002094B4002000000000E7 +:109AA00058B4002054B400201FB54378023B0A4646 +:109AB000032B12D8DFE803F0022A1921204B197872 +:109AC0006FF300011970197800246FF341011970C8 +:109AD0005C70197864F3820119701A4B014618689A +:109AE00004B0BDE81040FFF76CBF154B1978C907EB +:109AF00023D5197841F00401EEE711490B78DC0712 +:109B00001BD50B786FF382030B70E6E70C490B78DB +:109B10005B0712D50B786FF382030B700023CDE93E +:109B20000133039303788DF8043005238DF8053055 +:109B3000044B01A91868FFF744FF04B010BD00BF33 +:109B400059B4002094B400201FB50023CDE901339F +:109B50008DF804008DF8051001A811460393FFF756 +:109B6000A3FF05B05DF804FB1FB50023CDE9013369 +:109B700003938DF8040001238DF8081001A8114605 +:109B80008DF80530FFF790FF05B05DF804FB1FB5B9 +:109B9000144600230822CDE9013303938DF8040015 +:109BA00006230DEB02008DF8053001F0DFFB2146A6 +:109BB00001A8FFF779FF04B010BD1FB50024CDE95F +:109BC00001448DF8040007208DF805008DF8081079 +:109BD00001A89DF8181003928DF80930FFF764FF73 +:109BE00004B010BD1FB54FF40063CDE901300391FF +:109BF00001A81146FFF758FF05B05DF804FB00000F +:109C000038B58B7803F07F03082B05460C4608D93E +:109C10004FF0E023D3F8F03DDB0700D500BE002075 +:109C200038BD064B2046997801F00DFB0028EFD097 +:109C300021462846BDE83840FFF708B859B400204F +:109C40002DE9F047DDE9085681460C4690469A46D4 +:109C50000027B84501DC01200EE06378052B04D114 +:109C6000E37803F0030353450AD04FF0E023D3F821 +:109C7000F03DDA0702D40020BDE8F08700BEFAE725 +:109C800021464846FFF7BCFF38B94FF0E023D3F830 +:109C9000F03DDB07EFD500BEEEE7A378DA0914BF8D +:109CA00033702B70237801371C44D2E70B4B01F043 +:109CB0007F0203EB420303EBD1112031487910F00E +:109CC000010008D14B795B0706D44B7943F00403BC +:109CD0004B71012070470020704700BF59B400202D +:109CE0000B4B01F07F0203EB420303EBD111203158 +:109CF0004B7913F0010209D14B79C3F380005B0764 +:109D000005D54B7962F382034B7170470020704791 +:109D100059B4002070B5164D01F07F0605EB4605DD +:109D200005EBD11420346579ED0709D54FF0E02318 +:109D3000D3F8F03DDA0701D4002070BD00BEFBE788 +:109D4000657945F001056571FFF750F80028F4D1F9 +:109D5000637960F300036371637960F38203637175 +:109D60004FF0E023D3F8F03DDB07E5D500BEE4E794 +:109D700059B40020054B01F07F0203EB420303EBD3 +:109D8000D11191F8250000F00100704759B400206E +:109D900010B50B4B01F07F0203EB420303EBD11430 +:109DA000203463799B0709D4FFF76EF8637943F099 +:109DB00002036371637943F00103637110BD00BF57 +:109DC00059B4002010B50B4B01F07F0203EB4203A6 +:109DD00003EBD114203463799B0709D5FFF778F89A +:109DE00063796FF34103637163796FF30003637108 +:109DF00010BD00BF59B40020054B01F07F0203EBFA +:109E0000420303EBD11191F82500C0F340007047E5 +:109E100059B400202DE9F04F87B001F012FA002864 +:109E200000F09182AF4B1D682B78012B02D10020EE +:109E3000FEF7F2FE03A9281DFFF702FD2B78012B88 +:109E4000044602D10020FEF7E1FE002C00F07B82E8 +:109E50009DF80D30013B072B00F2B382DFE813F0D1 +:109E600008001300A8027E028D021F004A02AA0207 +:109E70009DF80C00FFF76CFD00F038FB9A4B9DF845 +:109E800010209A70CEE79DF80C00FFF761FD00F0FE +:109E90002DFB964B002BC5D0FEF78EFBC2E7924CF4 +:109EA0009DF80C50237843F00103237094F825307B +:109EB0006FF3000384F8253094F825306FF38203A4 +:109EC00084F8253094F826306FF3000384F82630A8 +:109ED00094F826306FF3820384F82630002000F0D7 +:109EE0000DFB9DF8106006F06002602A11D14FF062 +:109EF000E023D3F8F03DDC0700D500BE9DF80C0050 +:109F00000021FEF7C1FF9DF80C008021FEF7BCFF89 +:109F100088E7402A0DD176480028EFD000F0EEFA0D +:109F200004AA00212846AFF3008000287FF47AAF0E +:109F3000E4E706F01F06012E00F07181022E00F00A +:109F40009881002ED3D1202A0FD19DF814300F2BE9 +:109F5000D4D82344D878FFF7DBFC01460028CDD0C5 +:109F600004AA2846FFF71EFDDFE7002ABFD19DF8AF +:109F70001130092BBBD801A252F823F009A20F001F +:109F8000F7A10F00EF9E0F00E3A10F00EF9E0F005F +:109F9000A59F0F0021A10F00EF9E0F00BF9F0F0094 +:109FA000D59F0F0004A800F0AFFA9DF812102846C4 +:109FB000FEF73AFE237843F00203237032E763781A +:109FC0008DF80A3001230DF10A0204A9284600F099 +:109FD00059FA27E79DF812906378994537D063784E +:109FE0003BB12846FEF7BCFEA6782846FFF7B0FC3A +:109FF000A670B9F1000F2AD009F1FF30C0B2FEF708 +:10A00000E9F910B14378022B08D04FF0E023D3F8E0 +:10A01000F03DDF077FF56BAF00BE68E7C379C3F3A0 +:10A020008012C3F340131B0143EA4213227822F04B +:10A030003002134323704388C31800F109060093CC +:10A04000009BB3420AD82B4B0BB1FEF7B2FA84F84F +:10A05000019004A9284600F003FAE3E673780B2B7D +:10A0600003BF337896F80380F6184FF00108737831 +:10A07000042BCAD1009B9A1B93B201934FF0000BA3 +:10A080001D4B1B785FFA8BF70133BB42BDDB3846B3 +:10A09000FFF73EFC31468368019A8246284698477E +:10A0A0000828024639D9019B834236D3B8F1010F03 +:10A0B00006D1DAF8083011498B4208BF4FF0020888 +:10A0C0000021CBB298451DD83B4631460C48019241 +:10A0D00001F0E9F8084B019A1B7801339F421644BE +:10A0E000AEDD92E794B4002059B40020B9850F008A +:10A0F00000000000B3850F0058B40020A9A70F008E +:10A100006CB40020B078034454FA83F30131D8785A +:10A11000FF287FF47AAFDF70D3E70BF1010BAFE7D5 +:10A12000BDF81200030A5A1EC0B20E2A3FF6E6AE70 +:10A1300001A151F822F000BF75A10F009FA10F00EF +:10A14000C1A10F00FD9E0F00FD9E0F00D5A10F00C5 +:10A150009FA10F00FD9E0F00FD9E0F00FD9E0F00B2 +:10A16000FD9E0F00FD9E0F00FD9E0F00FD9E0F0047 +:10A1700087A10F00FEF72AF91223024604A92846F8 +:10A1800000F080F9D1E6934B002B3FF4B7AEAFF36C +:10A190000080024600283FF4AAAE4388EEE7022B77 +:10A1A00007D1FEF717F900283FF4A1AE4388024615 +:10A1B000E4E7894B002B3FF4A1AEAFF30080F2E758 +:10A1C000BDF81410FEF762F9024600283FF496AE7F +:10A1D0000378D3E7814B002B3FF490AEAFF30080C0 +:10A1E000F2E7BDF81230012B7FF488AE237843F0FC +:10A1F000080323702DE7BDF81230012B7FF47EAEEB +:10A2000023786FF3C303F4E72378C3F340129B086A +:10A2100003F002031343ADF80A300223D3E69DF89E +:10A2200014300F2B3FF66AAE2344D878FFF770FB4B +:10A23000014600283FF462AE04AA2846FFF7B2FBAD +:10A2400000287FF4EFAD9DF8103013F060047FF428 +:10A2500055AE9DF811300A3B012B3FF64FAE00F092 +:10A260004DF99DF811300A2B7FF4F3AE8DF80A40BA +:10A27000A8E69DF8141001F07F03082B3FF637AED7 +:10A2800004EB430303EBD113D87CFFF741FB0746F4 +:10A290002AB100283FF432AE04AA014661E69DF8D7 +:10A2A000113003F0FD02012A08D0002B7FF41FAE0D +:10A2B0002846FFF7A1FDADF80A00AEE7BDF8122071 +:10A2C00022B9012B284612D1FFF77CFD002F3FF465 +:10A2D000A9AD04AA39462846FFF764FB002000F028 +:10A2E0000DF994F82630DE073FF59CADB1E6FFF797 +:10A2F0004FFDEBE79DF81010394B01F07F0403EBA5 +:10A30000440303EBD11393F825006FF3000083F8A7 +:10A31000250093F825006FF3820083F825003CB9EF +:10A32000059B9DF811209DF80C0000F0FBF879E5E5 +:10A33000D87CFFF7EDFA48B94FF0E023D3F8F03DB1 +:10A34000D80700D500BE07B0BDE8F08F0469059BB3 +:10A350009DF811209DF80C00A04763E5204B1A786A +:10A36000D1077FF55FAD1F4A002A3FF45BAD187837 +:10A37000C0F3C000AFF3008054E5194B1B78DA0737 +:10A380007FF550AD184B002B3FF44CADAFF3008080 +:10A3900048E5FFF7BDFA436913B19DF80C009847F3 +:10A3A0000134124B1B78E0B201338342F1DA39E514 +:10A3B0000024F6E7049B002B3FF434AD0598984742 +:10A3C00030E54FF0E023D3F8F03DDB077FF52AAD11 +:10A3D00000BE27E5000000000000000000000000B3 +:10A3E00059B40020000000000000000058B4002014 +:10A3F00037B514490446CA89888991F90050831AEF +:10A400009BB2402B28BF4023002D10DA904214D07D +:10A410001A4689680C48019300F0A8FF0A4A019B7C +:10A420008021204603B0BDE83040FFF773BC904266 +:10A430004FF0000103D10022F3E78021FBE7024A3D +:10A44000EFE700BF58B500206CB5002011F0800F79 +:10A450004FF000031A460CBF80211946FFF75ABC83 +:10A4600030B4074C05460B4608684968224603C2CB +:10A470000022C4E902222846197830BCFFF7E6BF63 +:10A4800058B50020F8B5184E0C46054608684968CE +:10A49000B260374603C70021F181E1888B4228BFB3 +:10A4A0000B46B381E18889B153B14AB94FF0E0233B +:10A4B000D3F8F03DDA0701D40020F8BD00BEFBE779 +:10A4C0002846FFF795FF30B10120F6E721782846AE +:10A4D000FFF7BCFFF7E74FF0E023D3F8F03DDB07D1 +:10A4E000EAD500BEE9E700BF58B5002002481422B3 +:10A4F0000021F9F751BF00BF58B50020014B18618A +:10A50000704700BF58B5002010B50246044C0068E3 +:10A510005168234603C30023C4E9023310BD00BFC2 +:10A5200058B5002070B52D4C1E462378C909B1EBF3 +:10A53000D31F054618D04EB14FF0E023D3F8F03DBD +:10A54000DA0701D4002070BD00BEFBE7244B13B135 +:10A550002146AFF3008023690BB90120F3E71F4ABE +:10A56000022128469847F8E794F90030002B06DBD3 +:10A57000A0680028E6D01B49324600F0F7FEA2682A +:10A58000E38932443344A260E2889BB29A42E38179 +:10A5900001D03F2E1ED823696BB921782846FFF7DA +:10A5A00055FF0028D9D14FF0E023D3F8F03DDB0769 +:10A5B000C8D500BEC7E70121084A2846984701468A +:10A5C0000028EAD12846FEF75FFC80212846FEF7E6 +:10A5D0005BFCC2E72846FFF70BFFE2E758B5002017 +:10A5E000000000006CB5002070B500F110050446B5 +:10A5F0002846FFF713F93F2817D9E1780020FFF725 +:10A6000055FB90B12846FFF709F93F28E17807D9B3 +:10A6100004F638024023BDE870400020FFF77ABB03 +:10A62000BDE870400020FFF75BBB70BD08B5044B70 +:10A6300040F6B80202FB00301030FFF7D5F808BD35 +:10A64000ACB5002070B540F6B804074E444304F1A1 +:10A65000100092B23044FFF705F905463019FFF7B4 +:10A66000C3FF284670BD00BFACB500202DE9F04106 +:10A670000446FFF7C7F910B90020BDE8F081FFF7E5 +:10A68000C9F906460028F7D140F6B801164D4C43EB +:10A6900004F12408A8444046FFF7A6F80028EBD0B0 +:10A6A0002F193046B978FFF701FB0028E4D004F6F3 +:10A6B00078042544294640224046FFF7D3F8B9786C +:10A6C000044668B103462A463046FFF723FB48B9E3 +:10A6D0004FF0E023D3F8F03DDB07CDD500BECCE74B +:10A6E000FFF7FEFA2046C8E7ACB5002070B50B4C6A +:10A6F00040F6B80303FB0044243492B205462046DA +:10A70000FFF7F0F806462046FFF76EF83F2802D91B +:10A710002846FFF7ABFF304670BD00BFACB5002048 +:10A7200037B5144C40F6B80200212046F9F734FE44 +:10A73000FF234FF4424201256371E2800023082287 +:10A7400063812273009304F138012B464FF4806239 +:10A7500004F110002581FFF731F800952B464FF4E6 +:10A76000806204F5876104F12400FFF727F803B045 +:10A7700030BD00BFACB5002010B50A4C0021052249 +:10A780002046F9F709FE04F110002434FFF7B0F871 +:10A790002046FFF7ADF820460121BDE81040FFF745 +:10A7A000B3B800BFACB50020F7B54B79022B064615 +:10A7B00003D00025284603B0F0BD8B79022BF8D1D9 +:10A7C000204FBB787BBB8B783B700C7809250C4401 +:10A7D00003E023781D44ADB21C446378242B1BD1C5 +:10A7E0009542F6D96378042B12D163790A2B0FD1E5 +:10A7F000154B277801930133009302231A46E11980 +:10A800003046FFF71DFA70B10E3517FA85F5ADB277 +:10A810000C48FFF7E9FECDE7052BE3D12146304692 +:10A82000FFF7EEF938B94FF0E023D3F8F03DDB073E +:10A83000BFD500BEBDE7A3787B7023781D44ADB2C1 +:10A840001C44CFE7ACB50020AEB5002070B50B4678 +:10A850001146127802F06002202A45D1234E8A88E0 +:10A860003478944240D14A78203A032A3CD8DFE831 +:10A8700002F00213162F2BB91D4A0723FFF702FE21 +:10A88000012070BD022BFBD11A4B002BF8D01849C8 +:10A890000020AFF30080F3E7002BF1D1ECE713B910 +:10A8A000FFF7DEFDECE7022BEAD14C881248347149 +:10A8B00004F0010585F00101FFF726F80F4B002B8E +:10A8C000DED0C4F3400229460020AFF30080D7E772 +:10A8D000002BE5D0022BD3D1094B002BD0D04988D7 +:10A8E0000020AFF30080CBE70020CAE7ACB5002022 +:10A8F000B2B5002000000000D0B50020000000002C +:10A90000000000002DE9F347374D1C46EB788B42E1 +:10A9100007460E4607D0AB788B4258D1AB78B3428E +:10A9200032D001245CE0A2B205F6380105F1100036 +:10A93000FEF7D8FF2D4B2BB92D4BEBB92A48FFF76B +:10A9400053FEEBE76B79FF2BF6D005F638094FF095 +:10A95000000805F1100AA045EED019F8013B6A790C +:10A960009A4206D15046FEF751FF10B96979AFF30C +:10A97000008008F10108EEE71E48FEF747FF0028B7 +:10A98000DCD1FDF789F9D9E71B4B13B10020AFF3F8 +:10A9900000800020FFF76AFE0028C2D11748FEF7AA +:10A9A00023FF0028BDD1002CBBD014F03F03B8D149 +:10A9B000A97801933846FFF779F9019B04460028EE +:10A9C000AFD0A9781A463846FFF7A4F908E04FF04F +:10A9D000E023D3F8F04D14F0010401D000BE0024B0 +:10A9E000204602B0BDE8F087ACB5002000000000B2 +:10A9F000997C0F00BCB5002000000000D0B50020FD +:10AA000030B4104B02249A6B83F82C10996883F8A9 +:10AA1000304093F83C408A1A9A6224B942F2050504 +:10AA20009D8783F83E4051B14AB11A7BD20930BCB0 +:10AA300014BF93F82E1093F82F10FFF7A9B930BC6C +:10AA4000704700BF64CE002038B5154B154C054645 +:10AA500073B1607BAFF3008050B942F2077384F8A2 +:10AA60003E00A38728460121BDE83840FFF7C8BF54 +:10AA7000A26BA36894F82F109B1AB3F5805F28BFD0 +:10AA80004FF48053084A9BB22846FFF743F930B988 +:10AA90004FF0E023D3F8F03DDB0700D500BE38BD12 +:10AAA0000000000064CE002064BE002073B5234C7B +:10AAB000E28AA36852BA92B2054612B1B3FBF2F22F +:10AAC00092B2A06BD4F81110B0FBF2F61B1AB3F5DA +:10AAD000805F28BF4FF4805309BA009302FB16022F +:10AAE000174B607B3144FDF7DBFB031E0CDA43F2AE +:10AAF0000333A38701210023284684F83E3002B0A7 +:10AB0000BDE87040FFF77CBF94F82E1006D100938B +:10AB10001A462846FFF751F802B070BD084A9BB2AA +:10AB20002846FFF7F7F80028F6D14FF0E023D3F8D6 +:10AB3000F03DDB07F0D500BEEEE700BF64CE00209D +:10AB400064BE0020C28A836852BA92B223B9002A36 +:10AB50000CBF002002207047C17B282904D1017B53 +:10AB6000C90906D1022070472A2902D1017BC909EF +:10AB7000F8D122B1934234BF022000207047012057 +:10AB800070470000044880F83C1080F83D2080F8B1 +:10AB90003E300120704700BF64CE002002484022B2 +:10ABA0000021F9F7F9BB00BF64CE00200248402223 +:10ABB0000021F9F7F1BB00BF64CE002073B54B79DB +:10ABC000082B054602D0002002B070BD8B79062B01 +:10ABD000F9D1CB79502BF6D1162A07D84FF0E023C4 +:10ABE000D3F8F03DD907EED500BEECE7164C8B78D4 +:10ABF00084F82D3004F12E030E78019304F12F0315 +:10AC0000009302231A463144FFF71AF838B94FF07F +:10AC1000E023D3F8F03DDA07D5D500BED4E7002312 +:10AC200084F8303094F82F101F2322462846FFF76F +:10AC300071F830B94FF0E023D3F8F03DDB0700D5D1 +:10AC400000BE1720C0E700BF64CE00207FB50646D7 +:10AC50001546A1B9137803F07F02022A48D16C7817 +:10AC6000012C45D16A88002A42D1AB883C4D95F829 +:10AC70003020042ADBB204D11946FFF789F80120FD +:10AC80001AE095F82E10994218D1022AF7D1AA6B32 +:10AC9000AB689B1AAB62032385F8303005F12002C4 +:10ACA0000D23FFF737F80028E9D14FF0E023D3F860 +:10ACB000F03DDB0752D500BE04B070BD95F82F10F3 +:10ACC0009942DCD1002ADAD10191FFF753F80199BA +:10ACD0000028D4D13046FFF78FF80028CFD10023C9 +:10ACE00085F830301E4A95F82F101F233046D8E7DC +:10ACF00003F06003202B31D16B78FE2B13D0FF2B98 +:10AD00002CD16B8853BBE9888AB23ABB144B3046CE +:10AD100099872946C3E90D2283F8302083F83E2025 +:10AD2000FFF79EFBABE76B88C3B9EB88012B15D10E +:10AD30008DF80F300B4B1BB1AFF300808DF80F0077 +:10AD40009DF80F3053B1013B8DF80F300DF10F021C +:10AD5000012329463046FFF795FB90E70020ABE73B +:10AD600064CE0020000000002DE9F041AB4C94F8C7 +:10AD70003070012F90B005461E4600F0A181032FD0 +:10AD800000F00D82002F41D194F82F308B4201D07A +:10AD9000012013E01F2E03D12268A14B9A4210D04C +:10ADA00094F82E100423284684F83030FEF7F0FF84 +:10ADB00094F82F102846FEF7EBFF002010B0BDE8F6 +:10ADC000F081984B23626368E67BD4F8088084F8AE +:10ADD0002C70C4E90937012384F8303006F0FD03F4 +:10ADE000282BC4E90D872BD12046FFF7ABFE014687 +:10ADF00018B12846FFF704FE08E0B8F1000F56D05E +:10AE0000282E40F0A8812846FFF750FE94F83030F5 +:10AE1000022BBDD194F82E102846FEF7EDFF002836 +:10AE2000B6D1A368A26B94F82E10934240F2E08151 +:10AE3000207BC00900F0DC812846FEF7A9FFA7E7C8 +:10AE4000B8F1000F17D0237BDB0914D1B8F5805F70 +:10AE500001D90121CDE7744A1FFA88F32846FEF78D +:10AE600059FF0028D2D14FF0E023D3F8F03DDA07A4 +:10AE7000A3D500BEA2E7252E677B08D8192E1AD8C5 +:10AE8000032E00F0F780122E00F09F8096B394F806 +:10AE90003C30002BDDD1A38E634A6449607BFDF713 +:10AEA000EDF9031ED5DB60D1A368002BD1D10223BD +:10AEB00084F83030AAE71A3E0B2EE8D801A353F8E5 +:10AEC00026F000BF35B00F0015AF0F008FAE0F009A +:10AED0008FAE0F008FAE0F008FAE0F008FAE0F0042 +:10AEE0008FAE0F008FAE0F0085AF0F008FAE0F003B +:10AEF0002FAF0F003846FDF7BFF90028D4D194F8E2 +:10AF00003C30002BC3D140F20243A387002384F8D6 +:10AF10003E30BCE7464B002BC6D0E17C3846C1F33F +:10AF2000400301F001020909FDF74EFAE5E70DF1D2 +:10AF3000160206A93846FDF73FFA069B13B1BDF885 +:10AF400016203AB994F83C30002BA0D140F20242CE +:10AF5000A287DCE712BA013B1BBA0892324807937A +:10AF6000082207A900F002FA0823A06800283FF48D +:10AF700070AF834228BF034663632B4A94F82E10B8 +:10AF80009BB26BE70023CDE90733099308238DF8C3 +:10AF90001F300DF11602022306A938468DF8243021 +:10AFA000FDF70AFA069A002ACCD0BDF81630002B1D +:10AFB000C8D012BA5BBA08921B48ADF826300C22F2 +:10AFC00007A900F0D3F90C23CFE72422002107A81A +:10AFD000F9F7E2F980238DF81D30082202232021A1 +:10AFE00009A88DF81E308DF81F30F9F7D5F9102219 +:10AFF00020210BA8F9F7D0F9042220210FA8F9F796 +:10B00000CBF90FAB0BAA09A93846FDF701F90648A1 +:10B01000242207A900F0AAF92423A6E764CE002081 +:10B02000555342435553425364BE002073CE002013 +:10B03000C9830F0003238DF81C3000238DF81D30C9 +:10B040008DF81E308DF81F30704B8BB13846AFF342 +:10B0500000809DF81E3080F0010060F3C7130422C9 +:10B060006B488DF81E3007A900F080F904237CE7B7 +:10B070000120EEE71222002107A8F9F78DF9F0234D +:10B0800094F83C208DF81C300A238DF823304FF0C3 +:10B09000000362F303038DF81E3094F83D308DF801 +:10B0A00028305B4894F83E308DF82930122207A9E9 +:10B0B00000F05CF90023A38784F83E30122354E7A4 +:10B0C000E37BA06B282B06D1636B30449842A063CE +:10B0D000BFF4EDAE97E62A2B41D1E28A52BA92B282 +:10B0E0001AB1A368B3FBF2F292B2D4F81110484F30 +:10B0F000B0FBF2FC09BA02FB1C020096607B3B46E7 +:10B100006144FDF7EFF8002809DAA36B3344A36329 +:10B1100043F20333A387002384F83E3099E6864246 +:10B1200012D9321A40B1A36B03920344391838463E +:10B13000A36300F0B5F9039A94F82F10002300934D +:10B140002846FEF73AFD61E6A36B1E44636BA663D7 +:10B150009E42BFF4ACAE2846FFF776FC56E6237B52 +:10B160003044DB09A0630CD1A38E294A607B04F133 +:10B170000F01FDF783F8002803DA39462846FFF768 +:10B180003FFCD4E90D329A42BFF491AE4FF0E02378 +:10B19000D3F8F03DDB077FF539AE00BE36E694F814 +:10B1A0002E308B427FF432AE0D2E7FF42FAEE37B38 +:10B1B000282B09D02A2B14D0164B53B1607B04F1F5 +:10B1C0000F01AFF3008004E0134B13B1607BAFF3CA +:10B1D0000080002384F83030104A94F82F101F2389 +:10B1E0003CE60F4B002BF4D0607BFDF793F8F0E7C3 +:10B1F0009B1AA362032384F830300A4A0D232846A1 +:10B20000FEF788FD00287FF4C3AD2CE600000000A7 +:10B2100064BE0020000000000000000064CE00209A +:10B2200015830F0084CE002008B50020FEF700FC37 +:10B2300030B94FF0E023D3F8F03DDB0700D500BE76 +:10B2400008BDFEF7EFBB8388C07800F0030002283A +:10B25000C3F30A0315D003281DD001280FD10229FA +:10B2600040F2FF3208BF4FF480629A420FD24FF093 +:10B27000E023D3F8F00D10F0010008D000BE00204C +:10B280007047022904D1B3F5007FF0D10120704747 +:10B29000402BFBD9EBE702290CBF4FF48062402220 +:10B2A0009A42F3D2E3E730B50A44914200D330BD6D +:10B2B0004C78052C06D18C7804F07F0500EB450511 +:10B2C000E4092B550C782144EFE700000649074AB2 +:10B2D000074B9B1A03DD043BC858D050FBDCF9F741 +:10B2E00063FEF8F7D5FF000068BD0F000080002066 +:10B2F000C8860020FEE7FEE7FEE7FEE7FEE7FEE782 +:10B30000FEE7FEE7FEE7FEE7032A70B515D940EA3F +:10B31000010C1CF0030F04460B4621D119462046B0 +:10B320000E680568B54204F1040403F1040317D163 +:10B33000043A032A20461946F0D8541EA2B100F15F +:10B34000FF3C013901E0C3180CD01CF801EF11F8E3 +:10B35000012F9645A4EB0C03F5D0AEEB020070BDB7 +:10B36000541EECE7184670BD104670BD844641EA95 +:10B37000000313F003036DD1403A41D351F8043B6D +:10B3800040F8043B51F8043B40F8043B51F8043BBF +:10B3900040F8043B51F8043B40F8043B51F8043BAF +:10B3A00040F8043B51F8043B40F8043B51F8043B9F +:10B3B00040F8043B51F8043B40F8043B51F8043B8F +:10B3C00040F8043B51F8043B40F8043B51F8043B7F +:10B3D00040F8043B51F8043B40F8043B51F8043B6F +:10B3E00040F8043B51F8043B40F8043B51F8043B5F +:10B3F00040F8043B51F8043B40F8043B403ABDD2CE +:10B40000303211D351F8043B40F8043B51F8043B6F +:10B4100040F8043B51F8043B40F8043B51F8043B2E +:10B4200040F8043B103AEDD20C3205D351F8043BFE +:10B4300040F8043B043AF9D2043208D0D2071CBFCA +:10B4400011F8013B00F8013B01D30B8803806046F3 +:10B45000704700BF082A13D38B078DD010F0030369 +:10B460008AD0C3F10403D21ADB071CBF11F8013BD9 +:10B4700000F8013B80D331F8023B20F8023B7BE728 +:10B48000043AD9D3013A11F8013B00F8013BF9D253 +:10B490000B7803704B7843708B78837060467047ED +:10B4A00088420DD98B1883420AD900EB020CBAB13D +:10B4B000624613F801CD02F801CD9942F9D17047E7 +:10B4C0000F2A0ED8034602F1FF3C4AB10CF1010CE1 +:10B4D000013B8C4411F8012B03F8012F6145F9D190 +:10B4E000704740EA01039B0750D1A2F1100370B5E9 +:10B4F00001F1200C23F00F0501F1100E00F11004F2 +:10B50000AC441B095EF8105C44F8105C5EF80C5CFF +:10B5100044F80C5C5EF8085C44F8085C5EF8045C77 +:10B5200044F8045C0EF1100EE64504F11004E9D174 +:10B53000013312F00C0F01EB031102F00F0400EBCA +:10B54000031327D0043C24F003064FEA940C1E4456 +:10B550001C1F8E465EF8045B44F8045FB442F9D1C8 +:10B560000CF1010402F0030203EB840301EB8401FC +:10B5700002F1FF3C4AB10CF1010C013B8C4411F883 +:10B58000012B03F8012F6145F9D170BD02F1FF3C99 +:10B5900003469BE72246EBE7830710B5044610D12C +:10B5A0000268A2F1013323EA020313F0803F08D1BD +:10B5B00050F8042FA2F1013323EA020313F0803F75 +:10B5C000F6D003781BB110F8013F002BFBD100F03F +:10B5D00003F8204610BD00BF80EA0102844612F045 +:10B5E000030F4FD111F0030F32D14DF8044D11F07C +:10B5F000040F51F8043B0BD0A3F101329A4312F02F +:10B60000803F04BF4CF8043B51F8043B16D100BF07 +:10B6100051F8044BA3F101329A4312F0803FA4F198 +:10B6200001320BD14CF8043BA24312F0803F04BF1F +:10B6300051F8043B4CF8044BEAD023460CF8013B8C +:10B6400013F0FF0F4FEA3323F8D15DF8044B704736 +:10B6500011F0010F06D011F8012B0CF8012B002A74 +:10B6600008BF704711F0020FBFD031F8022B12F063 +:10B67000FF0F16BF2CF8022B8CF8002012F47F4F1E +:10B68000B3D1704711F8012B0CF8012B002AF9D126 +:10B69000704700BF00000000000000000000000034 +:10B6A000000000000000000000000000000000009A +:10B6B000000000000000000000000000000000008A +:10B6C00090F800F06DE9024520F007016FF0000CE2 +:10B6D00010F0070491F820F040F049804FF000048A +:10B6E0006FF00700D1E9002391F840F000F1080065 +:10B6F00082FA4CF2A4FA8CF283FA4CF3A2FA8CF39D +:10B700004BBBD1E9022382FA4CF200F10800A4FA03 +:10B710008CF283FA4CF3A2FA8CF3E3B9D1E9042357 +:10B7200082FA4CF200F10800A4FA8CF283FA4CF38E +:10B73000A2FA8CF37BB9D1E9062301F1200182FA48 +:10B740004CF200F10800A4FA8CF283FA4CF3A2FA4E +:10B750008CF3002BC6D0002A04BF04301A4612BA5C +:10B76000B2FA82F2FDE8024500EBD2007047D1E95F +:10B77000002304F00305C4F100004FEAC50514F0EE +:10B78000040F91F840F00CFA05F562EA05021CBFBF +:10B7900063EA050362464FF00004A9E7F0B5254FC0 +:10B7A000A2F1020E164605460C460FCF8BB0EC46B2 +:10B7B000ACE80F000FCFACE80F0097E803004CF89F +:10B7C000040BBEF1220F8CF800102ED804F1FF3EBE +:10B7D00070464FF0000CB5FBF6F206FB125328330F +:10B7E0006B44614613F828CC00F801CF2B469E42EB +:10B7F00001F1010C1546EED9002304F80C3089B193 +:10B80000A44472461EF8010F1CF8015D8EF800502A +:10B810006FEA0E0302322344121B0B449A428CF847 +:10B820000000EEDB20460BB0F0BD002020700BB016 +:10B83000F0BD00BF34BD0F00FFF7B0BF53B94AB928 +:10B84000002908BF00281CBF4FF0FF314FF0FF3028 +:10B8500000F074B9ADF1080C6DE904CE00F006F803 +:10B86000DDF804E0DDE9022304B070472DE9F0477C +:10B87000089D04468E46002B4DD18A42944669D9D4 +:10B88000B2FA82F252B101FA02F3C2F1200120FAB7 +:10B8900001F10CFA02FC41EA030E94404FEA1C4805 +:10B8A000210CBEFBF8F61FFA8CF708FB16E341EA01 +:10B8B000034306FB07F199420AD91CEB030306F187 +:10B8C000FF3080F01F81994240F21C81023E6344A8 +:10B8D0005B1AA4B2B3FBF8F008FB103344EA03444C +:10B8E00000FB07F7A7420AD91CEB040400F1FF3361 +:10B8F00080F00A81A74240F207816444023840EA9E +:10B900000640E41B00261DB1D4400023C5E90043D6 +:10B910003146BDE8F0878B4209D9002D00F0EF8059 +:10B920000026C5E9000130463146BDE8F087B3FA8C +:10B9300083F6002E4AD18B4202D3824200F2F98074 +:10B94000841A61EB030301209E46002DE0D0C5E977 +:10B95000004EDDE702B9FFDEB2FA82F2002A40F0C3 +:10B960009280A1EB0C014FEA1C471FFA8CFE0126C6 +:10B97000200CB1FBF7F307FB131140EA01410EFB6A +:10B9800003F0884208D91CEB010103F1FF3802D211 +:10B99000884200F2CB804346091AA4B2B1FBF7F00B +:10B9A00007FB101144EA01440EFB00FEA64508D92E +:10B9B0001CEB040400F1FF3102D2A64500F2BB806B +:10B9C0000846A4EB0E0440EA03409CE7C6F12007BA +:10B9D000B34022FA07FC4CEA030C20FA07F401FA00 +:10B9E00006F31C43F9404FEA1C4900FA06F3B1FB89 +:10B9F000F9F8200C1FFA8CFE09FB181140EA0141EE +:10BA000008FB0EF0884202FA06F20BD91CEB01018A +:10BA100008F1FF3A80F08880884240F28580A8F1E2 +:10BA200002086144091AA4B2B1FBF9F009FB101134 +:10BA300044EA014100FB0EFE8E4508D91CEB0101D2 +:10BA400000F1FF346CD28E456AD90238614440EA75 +:10BA50000840A0FB0294A1EB0E01A142C846A646F5 +:10BA600056D353D05DB1B3EB080261EB0E0101FA7E +:10BA700007F722FA06F3F1401F43C5E900710026DB +:10BA80003146BDE8F087C2F12003D8400CFA02FC31 +:10BA900021FA03F3914001434FEA1C471FFA8CFE41 +:10BAA000B3FBF7F007FB10360B0C43EA064300FB31 +:10BAB0000EF69E4204FA02F408D91CEB030300F1CF +:10BAC000FF382FD29E422DD9023863449B1B89B286 +:10BAD000B3FBF7F607FB163341EA034106FB0EF30F +:10BAE0008B4208D91CEB010106F1FF3816D28B42BC +:10BAF00014D9023E6144C91A46EA004638E72E4688 +:10BB0000284605E70646E3E61846F8E64B45A9D27F +:10BB1000B9EB020864EB0C0E0138A3E74646EAE7EE +:10BB2000204694E74046D1E7D0467BE7023B61449C +:10BB300032E7304609E76444023842E7704700BF05 +:10BB4000F8B500BFF8BC08BC9E467047F8B500BF0A +:10BB5000F8BC08BC9E467047088000200010020018 +:10BB60000338FDD87047000000000000000000000E +:10BB70000338FDD87047010000000000089900203C +:10BB80000338FDD87047416461444655004D6573E4 +:10BB90006874617374696300302E392E32207331FA +:10BBA000343020372E332E3000000000000000001B +:10BBB00000000000000000000023D1BCEA5F7823F1 +:10BBC00015DEEF12120000000000000070B000202F +:10BBD0004164616672756974006E52462055463242 +:10BBE00000303132333435363738394142434445F9 +:10BBF00046006E52462053657269616C0009045319 +:10BC00006F66744465766963653A200053002E00C0 +:10BC10006E6F7420666F756E640D0A00EB3C905574 +:10BC20004632205546322000020101000240000049 +:10BC300000F80201010001000000000009010100FC +:10BC4000800029420042004D455348544153544915 +:10BC5000430046415431362020203C21646F6374F8 +:10BC60007970652068746D6C3E0A3C68746D6C3E3A +:10BC70003C626F64793E3C7363726970743E0A6C17 +:10BC80006F636174696F6E2E7265706C6163652895 +:10BC90002268747470733A2F2F6275796D656163D1 +:10BCA0006F666665652E636F6D2F6D61726B2E62B8 +:10BCB0006972737322293B0A3C2F73637269707433 +:10BCC0003E3C2F626F64793E3C2F68746D6C3E0A77 +:10BCD00000000000494E464F5F554632545854000C +:10BCE00024850020494E44455820202048544D00CA +:10BCF0005ABC0F0043555252454E5420554632000F +:10BD00000000000021A70F0079A70F00A9A70F00CE +:10BD10004DA80F0005A90F00000000009DAB0F000B +:10BD2000ADAB0F00BDAB0F004DAC0F0069AD0F0008 +:10BD30000000000030313233343536373839616233 +:10BD4000636465666768696A6B6C6D6E6F7071724B +:10BD5000737475767778797A00000000000000002F +:10BD60002885FF7F010000000880002000000000FF +:10BD700000000000F48200205C830020C4830020C7 +:10BD800000000000000000000000000000000000B3 +:10BD900000000000000000000000000000000000A3 +:10BDA0000000000000000000000000000000000093 +:10BDB0000000000000000000000000000000000083 +:10BDC0000000000000000000000000000000000073 +:10BDD0000000000000000000000000000000000063 +:10BDE0000000000000000000000000000000000053 +:10BDF0000000000000000000000000000000000043 +:10BE00000000000000000000000000000000000032 +:10BE10000000000000000000010000000000000021 +:10BE20000E33CDAB34126DE6ECDE05000B000000E6 +:10BE30000000000000000000000000000000000002 +:10BE400000000000000000000000000000000000F2 +:10BE500000000000000000000000000000000000E2 +:10BE600000000000000000000000000000000000D2 +:10BE700000000000000000000000000000000000C2 +:10BE800000000000000000000000000000000000B2 +:10BE900000000000000000000000000000000000A2 +:10BEA0000000000000000000000000000000000092 +:10BEB0000000000000000000000000000000000082 +:10BEC0000000000000000000000000000000000072 +:10BED0000000000000000000000000000000000062 +:10BEE0000000000000000000000000000000000052 +:10BEF0000000000000000000000000000000000042 +:10BF00000000000000000000000000000000000031 +:10BF10000000000000000000000000000000000021 +:10BF20000000000000000000000000000000000011 +:10BF30000000000000000000000000000000000001 +:10BF400000000000000000000000000000000000F1 +:10BF500000000000000000000000000000000000E1 +:10BF600000000000000000000000000000000000D1 +:10BF700000000000000000000000000000000000C1 +:10BF800000000000000000000000000000000000B1 +:10BF900000000000000000000000000000000000A1 +:10BFA0000000000000000000000000000000000091 +:10BFB0000000000000000000000000000000000081 +:10BFC0000000000000000000000000000000000071 +:10BFD0000000000000000000000000000000000061 +:10BFE0000000000000000000000000000000000051 +:10BFF0000000000000000000000000000000000041 +:10C000000000000000000000000000000000000030 +:10C010000000000000000000000000000000000020 +:10C020000000000000000000000000000000000010 +:10C030000000000000000000000000000000000000 +:10C0400000000000000000000000000000000000F0 +:10C0500000000000000000000000000000000000E0 +:10C0600000000000000000000000000000000000D0 +:10C0700000000000000000000000000000000000C0 +:10C0800000000000000000000000000000000000B0 +:10C0900000000000000000000000000000000000A0 +:10C0A0000000000000000000000000000000000090 +:10C0B0000000000000000000000000000000000080 +:10C0C0000000000000000000000000000000000070 +:10C0D0000000000000000000000000000000000060 +:10C0E0000000000000000000000000000000000050 +:10C0F0000000000000000000000000000000000040 +:10C10000000000000000000000000000000000002F +:10C11000000000000000000000000000000000001F +:10C12000000000000000000000000000000000000F +:10C1300000000000000000000000000000000000FF +:10C1400000000000000000000000000000000000EF +:10C1500000000000000000000000000000000000DF +:10C1600000000000000000000000000000000000CF +:10C1700000000000000000000000000000000000BF +:10C1800000000000000000000000000000000000AF +:10C190000000000000000000FFFFFFFF7C7F002088 +:10C1A0000090D003FF00FFFF320000007D7B0F00F6 +:10C1B000F17B0F0001090262000301008032080BCD +:10C1C000000202020000090400000102020004054E +:10C1D0002400200105240100010424020205240694 +:10C1E00000010705810308001009040100020A008C +:10C1F000000007050202400000070582024000001F +:10C200000904020002080650050705030240000069 +:10C210000705830240000009024B00020100803242 +:10C22000080B0002020200000904000001020200E3 +:10C230000405240020010524010001042402020554 +:10C24000240600010705810308001009040100020B +:10C250000A000000070502024000000705820240B4 +:10C26000000012010002EF0201409A2329000001A0 +:10C2700001020301FDBB0F008DBB0F008DBB0F0042 +:10C2800064B30020F2BB0F00D9BB0F00554632202B +:10C29000426F6F746C6F6164657220302E392E327C +:10C2A000206C69622F6E726678202876322E302ECE +:10C2B0003029206C69622F74696E79757362202849 +:10C2C000302E31322E302D3134352D673937373518 +:10C2D000653736393129206C69622F75663220281E +:10C2E00072656D6F7465732F6F726967696E2F6306 +:10C2F0006F6E6669677570646174652D392D67614D +:10C30000646262386337290D0A4D6F64656C3A20A8 +:10C3100068747470733A2F2F6275796D6561636FFD +:10C32000666665652E636F6D2F6D61726B2E626937 +:10C330007273730D0A426F6172642D49443A204D45 +:10C340006573687461737469630D0A446174653A56 +:10C350002053657020203120323032340D0A000025 +:10C3600000000000000000000000000000000000CD +:10C3700000000000000000000000000000000000BD +:10C3800000000000000000000000000000000000AD +:10C39000000000000000000000000000000000009D +:10C3A000000000000000000000000000000000008D +:10C3B000000000000000000000000000000000007D +:10C3C000000000000000000000000000000000006D +:10C3D000000000000000000000000000000000005D +:10C3E000000000000000000000000000000000004D +:10C3F000000000000000000000000000000000003D +:10C40000000000000000000000000000010000002B +:10C4100098B4002010000C000000E0FF1F00000096 +:10C42000000000005D450F0069420F0041420F000F +:10D80000F1109E1E797A22200500000064000000BD +:10D81000CC00000000001000CD000000000004005B +:10D82000D000000029009A23D10000004028A5ADB7 +:10D83000D2000000200000000000000000000000F6 +:10D8400000000000000000000000000000000000D8 +:08D850000000000000000000D0 +:020000041000EA +:0810140000400F0000E00F0096 +:00000001FF diff --git a/bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.zip b/bin/generic/Meshtastic_7.3.0_bootloader-0.9.2_s140_7.3.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..bccd58625239b212c8db2bb00cab841b1b7c2129 GIT binary patch literal 192586 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⩔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%0D58=Ao1&aE=w9%(l}&h-bA!8ch@p?Q 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 literal 0 HcmV?d00001 diff --git a/bin/generic/update-Meshtastic_6.1.0_bootloader-0.9.2_nosd.uf2 b/bin/generic/update-Meshtastic_6.1.0_bootloader-0.9.2_nosd.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..65032ce66e57cb31c7de84ba2dec18760aaaddba GIT binary patch literal 74752 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 a>mmHVvlK?Olgr=t_3x~kf1yB%|NjC8yB})+ literal 0 HcmV?d00001 diff --git a/bin/generic/update-Meshtastic_7.3.0_bootloader-0.9.2_nosd.uf2 b/bin/generic/update-Meshtastic_7.3.0_bootloader-0.9.2_nosd.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..beffb3206f141e73c51046df8e0cce83d883bce9 GIT binary patch literal 74752 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

e*CXWQvIA-1uuNLnY*RYK-+ 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 Y#Q!@>VMIH*{C!{l&bs**3Z(e|FF42_X8-^I literal 0 HcmV?d00001 From d83f8edd54135e208a51f525deaf29700167a837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 12:09:53 +0200 Subject: [PATCH 303/305] include radiolib CRC fix --- platformio.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index bd7ab4f5a..31274ec90 100644 --- a/platformio.ini +++ b/platformio.ini @@ -87,7 +87,8 @@ monitor_speed = 115200 monitor_filters = direct lib_deps = - jgromes/RadioLib@~6.6.0 +; jgromes/RadioLib@~6.6.0 + https://github.com/jgromes/RadioLib.git#eda4ec22ae0039f3b9a2844d68ac2023ac0076a5 https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 From 6fc4a1754bd3aa9c01e640617cdde2f84610f41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 13:12:39 +0200 Subject: [PATCH 304/305] Radiolib API Changes --- src/mesh/LR11x0Interface.h | 2 +- src/mesh/SX126xInterface.cpp | 5 ++++- src/mesh/SX126xInterface.h | 2 +- src/mesh/SX128xInterface.cpp | 4 ++++ src/mesh/SX128xInterface.h | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/mesh/LR11x0Interface.h b/src/mesh/LR11x0Interface.h index 11a389d25..9272f43f0 100644 --- a/src/mesh/LR11x0Interface.h +++ b/src/mesh/LR11x0Interface.h @@ -25,7 +25,7 @@ template class LR11x0Interface : public RadioLibInterface /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. virtual bool sleep() override; - bool isIRQPending() override { return lora.getIrqStatus() != 0; } + bool isIRQPending() override { return lora.getIrqFlags() != 0; } protected: /** diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 39ffb0ac9..47ac5da07 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -12,6 +12,9 @@ #define SX126X_MAX_POWER 22 #endif +#define RADIOLIB_SX126X_IRQ_RX_DEFAULT \ + RADIOLIB_SX126X_IRQ_RX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT | RADIOLIB_SX126X_IRQ_CRC_ERR | RADIOLIB_SX126X_IRQ_HEADER_ERR + template SX126xInterface::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) @@ -301,7 +304,7 @@ template bool SX126xInterface::isActivelyReceiving() // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet // received and handled the interrupt for reading the packet/handling errors. - uint16_t irq = lora.getIrqStatus(); + uint16_t irq = lora.getIrqFlags(); bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED)); // Handle false detections if (detected) { diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h index f2c861743..b392cd3e4 100644 --- a/src/mesh/SX126xInterface.h +++ b/src/mesh/SX126xInterface.h @@ -25,7 +25,7 @@ template class SX126xInterface : public RadioLibInterface /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. virtual bool sleep() override; - bool isIRQPending() override { return lora.getIrqStatus() != 0; } + bool isIRQPending() override { return lora.getIrqFlags() != 0; } protected: float currentLimit = 140; // Higher OCP limit for SX126x PA diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index fdb2b9a39..ae5fd6941 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -12,6 +12,10 @@ #define SX128X_MAX_POWER 13 #endif +#define RADIOLIB_SX128X_IRQ_RX_DEFAULT \ + RADIOLIB_SX128X_IRQ_RX_DONE | RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT | RADIOLIB_SX128X_IRQ_CRC_ERROR | \ + RADIOLIB_SX128X_IRQ_HEADER_ERROR + template SX128xInterface::SX128xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h index 3aaf31b1c..f7fd35b25 100644 --- a/src/mesh/SX128xInterface.h +++ b/src/mesh/SX128xInterface.h @@ -27,7 +27,7 @@ template class SX128xInterface : public RadioLibInterface /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. virtual bool sleep() override; - bool isIRQPending() override { return lora.getIrqStatus() != 0; } + bool isIRQPending() override { return lora.getIrqFlags() != 0; } protected: /** From ff40a3f120c453f6f45e5bc6a7085022982f04ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Sep 2024 21:21:40 +0200 Subject: [PATCH 305/305] fix RF switch for Tracker E (#4621) * fix RF switch for Tracker E * fix all flags for Radiolib * hopefully fix LR1110 * update to latest radiolib master - thanks @GUVWAF --- platformio.ini | 2 +- src/mesh/LR11x0Interface.cpp | 24 ++++++++++++++++++------ src/mesh/SX126xInterface.cpp | 7 +------ src/mesh/SX128xInterface.cpp | 8 +------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/platformio.ini b/platformio.ini index 31274ec90..87eb0afb7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -88,7 +88,7 @@ monitor_filters = direct lib_deps = ; jgromes/RadioLib@~6.6.0 - https://github.com/jgromes/RadioLib.git#eda4ec22ae0039f3b9a2844d68ac2023ac0076a5 + https://github.com/jgromes/RadioLib.git#3115fc2d6700a9aee05888791ac930a910f2628f https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 0201282db..8f0dc062e 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -50,11 +50,23 @@ template bool LR11x0Interface::init() limitPower(); +#ifdef TRACKER_T1000_E // Tracker T1000E uses DIO5, DIO6, DIO7, DIO8 for RF switching + + static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7, + RADIOLIB_LR11X0_DIO8, RADIOLIB_NC}; + + static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 DIO7 DIO8 + {LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH}}, + {LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH}}, + {LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW}}, + {LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW}}, END_OF_MODE_TABLE, + }; + +#else + // set RF switch configuration for Wio WM1110 // Wio WM1110 uses DIO5 and DIO6 for RF switching - // NOTE: other boards may be different. If you are - // using a different board, you may need to wrap - // this in a conditional. static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; @@ -67,6 +79,8 @@ template bool LR11x0Interface::init() {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE, }; +#endif + // We need to do this before begin() call #ifdef LR11X0_DIO_AS_RF_SWITCH LOG_DEBUG("Setting DIO RF switch\n"); @@ -218,9 +232,7 @@ template void LR11x0Interface::startReceive() // We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly. // Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving - int err = lora.startReceive( - RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_LR11X0_IRQ_RX_DONE, - 0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving + int err = lora.startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0); assert(err == RADIOLIB_ERR_NONE); RadioLibInterface::startReceive(); diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 47ac5da07..fcb3e4edb 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -12,9 +12,6 @@ #define SX126X_MAX_POWER 22 #endif -#define RADIOLIB_SX126X_IRQ_RX_DEFAULT \ - RADIOLIB_SX126X_IRQ_RX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT | RADIOLIB_SX126X_IRQ_CRC_ERR | RADIOLIB_SX126X_IRQ_HEADER_ERR - template SX126xInterface::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) @@ -267,9 +264,7 @@ template void SX126xInterface::startReceive() // We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly. // Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving - int err = lora.startReceiveDutyCycleAuto(preambleLength, 8, - RADIOLIB_SX126X_IRQ_RX_DEFAULT | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED | - RADIOLIB_SX126X_IRQ_HEADER_VALID); + int err = lora.startReceiveDutyCycleAuto(preambleLength, 8, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); if (err != RADIOLIB_ERR_NONE) LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err); assert(err == RADIOLIB_ERR_NONE); diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index ae5fd6941..9ff9ac2d7 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -12,10 +12,6 @@ #define SX128X_MAX_POWER 13 #endif -#define RADIOLIB_SX128X_IRQ_RX_DEFAULT \ - RADIOLIB_SX128X_IRQ_RX_DONE | RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT | RADIOLIB_SX128X_IRQ_CRC_ERROR | \ - RADIOLIB_SX128X_IRQ_HEADER_ERROR - template SX128xInterface::SX128xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) @@ -260,9 +256,7 @@ template void SX128xInterface::startReceive() #endif // We use the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving - int err = - lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_SX128X_IRQ_RX_DEFAULT | RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED | - RADIOLIB_SX128X_IRQ_HEADER_VALID); + int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); if (err != RADIOLIB_ERR_NONE) LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err);