diff --git a/.github/workflows/arduino.yml b/.github/workflows/arduino.yml index 2d635224..5a5953a2 100644 --- a/.github/workflows/arduino.yml +++ b/.github/workflows/arduino.yml @@ -188,7 +188,9 @@ jobs: #- name: List files in dir 2 # run: | # ls ${{ github.workspace }} - + - name: Edit version + run: sed -i "s/\(AssemblyVersion(""\([0-9]\+\.\)\{3\}\)\([0-9]\+\)/\1${{github.run_number}}/" "SimHubPlugin/Properties/AssemblyInfo.cs" + - name: Restore Packages run: nuget restore "SimHubPlugin/User.PluginSdkDemo.sln" diff --git a/.github/workflows/platformio.yml b/.github/workflows/platformio.yml new file mode 100644 index 00000000..0e355957 --- /dev/null +++ b/.github/workflows/platformio.yml @@ -0,0 +1,103 @@ +name: build_firmware + +on: [push] + + +# see e.g. https://cgrotz.github.io/2020-08-24-esp32_ci_cd_part2/ + +jobs: + buildFirmware: + #runs-on: ubuntu-latest + runs-on: windows-latest + + steps: + + # Prepare environment + - uses: actions/checkout@main + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + + + + + # Build ESP code + - name: Build PlatformIO Project ESP32 + working-directory: ./ESP32 + run: | + pio system info + pio run --environment esp32 + pio run --environment esp32s3usbotg + pio run --environment esp32-speedcrafter + + + + + + + + + # copy files to dir + - name: Copy files to packing dir + run: | + copy ${{ github.workspace }}/ESP32/.pio/build/esp32/firmware.bin ${{ github.workspace }}/Helper/bins/esp32 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32/bootloader.bin ${{ github.workspace }}/Helper/bins/esp32 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32/partitions.bin ${{ github.workspace }}/Helper/bins/esp32 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32s3usbotg/firmware.bin ${{ github.workspace }}/Helper/bins/esp32s3 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32s3usbotg/bootloader.bin ${{ github.workspace }}/Helper/bins/esp32s3 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32s3usbotg/partitions.bin ${{ github.workspace }}/Helper/bins/esp32s3 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32-speedcrafter/firmware.bin ${{ github.workspace }}/Helper/bins/esp32_speedcrafter_PCB_V1p4 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32-speedcrafter/bootloader.bin ${{ github.workspace }}/Helper/bins/esp32_speedcrafter_PCB_V1p4 + copy ${{ github.workspace }}/ESP32/.pio/build/esp32-speedcrafter/partitions.bin ${{ github.workspace }}/Helper/bins/esp32_speedcrafter_PCB_V1p4 + + + + + + + + # copy files to dir + - name: List files in dir + run: | + dir ${{ github.workspace }}/Helper/bins/esp32 + + + + + + + # zip plugin binaries + - name: ZIP files + run: | + 7z a ${{ github.workspace }}/Helper/zip/esp32.zip ${{ github.workspace }}/Helper/bins/esp32/ + 7z a ${{ github.workspace }}/Helper/zip/esp32s3.zip ${{ github.workspace }}/Helper/bins/esp32s3/ + 7z a ${{ github.workspace }}/Helper/zip/esp32_speedcrafter.zip ${{ github.workspace }}/Helper/bins/esp32_speedcrafter_PCB_V1p4/ + # 7z a ./Helper/zip/esp32.zip @./Helper/zip/filelist_esp32.txt + # 7z a ./Helper/zip/esp32s3.zip @Helper/zip/filelist_esp32s3.txt + # 7z a./Helper/zip/esp32_speedcrafter.zip @Helper/zip/filelist_esp32_speedcrafter.txt + + + + + + # Publish as build artifact + - name: Attach artifact + uses: actions/upload-artifact@v2 + with: + name: firmware_esp32 + path: | + ./Helper/zip/esp32.zip + ./Helper/zip/esp32s3.zip + ./Helper/zip/esp32_speedcrafter.zip + + + diff --git a/.github/workflows/publishRelease.yml b/.github/workflows/publishRelease.yml new file mode 100644 index 00000000..5a0d671f --- /dev/null +++ b/.github/workflows/publishRelease.yml @@ -0,0 +1,29 @@ +name: build_release + + +# Only trigger, when the build workflow succeeded, see https://stackoverflow.com/questions/62750603/github-actions-trigger-another-action-after-one-action-is-completed +on: + workflow_run: + workflows: ["build_firmware","build_simhub_plugin"] + types: + - completed + +#on: [push] + +# This is the list of jobs that will be run concurrently. +# Since we use a build matrix, the actual number of jobs +# started depends on how many configurations the matrix +# will produce. + +# https://docs.github.com/de/actions/using-workflows/storing-workflow-data-as-artifacts + +jobs: + generateRelase: + runs-on: ubuntu-latest + steps: + - name: Download math result for job 2 + uses: actions/download-artifact@v4 + - name: Print the final result + shell: bash + run: | + echo The result is good diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml new file mode 100644 index 00000000..97919782 --- /dev/null +++ b/.github/workflows/publish_release.yml @@ -0,0 +1,32 @@ +name: build_release + + +# Only trigger, when the build workflow succeeded, see https://stackoverflow.com/questions/62750603/github-actions-trigger-another-action-after-one-action-is-completed +on: + workflow_run: + workflows: ["build_firmware","build_simhub_plugin"] + types: + - completed + + + +# This is the list of jobs that will be run concurrently. +# Since we use a build matrix, the actual number of jobs +# started depends on how many configurations the matrix +# will produce. + +# https://docs.github.com/de/actions/using-workflows/storing-workflow-data-as-artifacts + +jobs: + deploy: + name: deploy + runs-on: ubuntu-latest + steps: + - name: Download math result for job 2 + uses: actions/download-artifact@v4 + with: + name: ["simhub_plugin","firmware_esp32"] + - name: Print the final result + shell: bash + run: | + echo The result is good diff --git a/.github/workflows/simhubPlugin.yml b/.github/workflows/simhubPlugin.yml new file mode 100644 index 00000000..0338a3b7 --- /dev/null +++ b/.github/workflows/simhubPlugin.yml @@ -0,0 +1,86 @@ +name: build_simhub_plugin + +on: [push] + + +# see e.g. https://cgrotz.github.io/2020-08-24-esp32_ci_cd_part2/ + +jobs: +######################################################################## +# Build SimHub plugin +######################################################################## + buildSimhubPlugin: + runs-on: windows-2019 + + steps: + - uses: actions/checkout@v2 + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v1.0.5 + + - name: Setup MS Build Systems + uses: microsoft/setup-msbuild@v1.1 + + - uses: actions/cache@v3 + name: Restore Caches + id: cache + with: + path: | + innounp050.rar + SimHub.8.01.2.zip + key: SimHub.8.01.2 + + - name: Download Requirements # Used to download the SimHub DLLs - only if not in cache. + if: steps.cache.outputs.cache-hit != 'true' + run: | + aria2c -j1 -o innounp050.rar "https://sourceforge.net/projects/innounp/files/innounp/innounp%200.50/innounp050.rar/download" + aria2c -j1 -o SimHub.8.01.2.zip "https://github.com/SHWotever/SimHub/releases/download/9.1.22/SimHub.9.01.22.zip" + + - name: Extract Requirements # Used to extract the SimHub DLLs + run: | + 7z x innounp050.rar + 7z x SimHub.8.01.2.zip + dir + mkdir "C:\Program Files (x86)\SimHub\" + ${{ github.workspace }}\innounp.exe -v -x -b -e -d"C:\Program Files (x86)\SimHub\" SimHubSetup_9.1.22.exe + - name: List files in dir + run: | + ls "C:\Program Files (x86)\SimHub\" + + #- name: List files in dir 2 + # run: | + # ls ${{ github.workspace }} + - name: Edit version + run: sed -i "s/\(AssemblyVersion(""\([0-9]\+\.\)\{3\}\)\([0-9]\+\)/\1${{github.run_number}}/" "SimHubPlugin/Properties/AssemblyInfo.cs" + + - name: Restore Packages + run: nuget restore "SimHubPlugin/User.PluginSdkDemo.sln" + + - name: Build solution + run: msbuild "SimHubPlugin/User.PluginSdkDemo.sln" -t:rebuild -property:Configuration=Release + + + + + + + # zip plugin binaries + - name: ZIP files + run: | + 7z a ./SimHubPlugin/bin/SimHub_plugin.zip ./SimHubPlugin/bin/* + + + + + + + + # Publish as build artifact + - name: Attach artifact + uses: actions/upload-artifact@v2 + with: + name: simhub_plugin + path: | + ./SimHubPlugin/bin/SimHub_plugin.zip \ No newline at end of file diff --git a/Arduino/Esp32/Main/Main.h b/Arduino/Esp32/Main/Main.h index ba86b2ea..a73588b7 100644 --- a/Arduino/Esp32/Main/Main.h +++ b/Arduino/Esp32/Main/Main.h @@ -87,6 +87,7 @@ #define BLUETOOTH_GAMEPAD //#define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 #endif @@ -113,6 +114,7 @@ //#define BLUETOOTH_GAMEPAD #define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 #endif @@ -146,7 +148,7 @@ #define BLUETOOTH_GAMEPAD //#define USB_JOYSTICK - + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 #endif @@ -177,6 +179,7 @@ #define BLUETOOTH_GAMEPAD //#define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 #endif @@ -207,6 +210,7 @@ #define BLUETOOTH_GAMEPAD //#define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 #endif @@ -248,5 +252,7 @@ //#define BLUETOOTH_GAMEPAD #define USB_JOYSTICK + + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 15 #endif diff --git a/Arduino/Esp32/Main/Main.ino b/Arduino/Esp32/Main/Main.ino index 30615d45..638e7b42 100644 --- a/Arduino/Esp32/Main/Main.ino +++ b/Arduino/Esp32/Main/Main.ino @@ -231,11 +231,16 @@ TaskHandle_t Task4; void setup() { //Serial.begin(115200); - Serial.begin(921600); + //Serial.begin(921600); //Serial.begin(512000); - Serial.setTimeout(5); - //Serial.setTxTimeoutMs(0); - + // + #ifdef SERIAL_TIMEOUT + //Serial.setTxTimeoutMs(0); + Serial.begin(921600); + #else + Serial.begin(921600); + Serial.setTimeout(5); + #endif Serial.println(" "); Serial.println(" "); Serial.println(" "); @@ -525,15 +530,16 @@ void setup() //MCP setup #ifdef Using_analog_output_ESP32_S3 - Wire.begin(MCP_SDA,MCP_SCL,400000); + //Wire.begin(MCP_SDA,MCP_SCL,400000); + MCP4725_I2C.begin(MCP_SDA,MCP_SCL,400000); uint8_t i2c_address[8]={0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67}; int index_address=0; int found_address=0; int error; for(index_address=0;index_address<8;index_address++) { - Wire.beginTransmission(i2c_address[index_address]); - error = Wire.endTransmission(); + MCP4725_I2C.beginTransmission(i2c_address[index_address]); + error = MCP4725_I2C.endTransmission(); if (error == 0) { Serial.print("I2C device found at address"); @@ -549,6 +555,7 @@ void setup() Serial.println(i2c_address[index_address]); } } + if(dac.begin(i2c_address[found_address], &MCP4725_I2C)==false) { Serial.println("Couldn't find MCP, will not have analog output"); @@ -1211,9 +1218,13 @@ void serialCommunicationTask( void * pvParameters ) - delay(1); - // read serial input - byte n = Serial.available(); + delay( SERIAL_COOMUNICATION_TASK_DELAY_IN_MS ); + + + //if (Serial) + { + // read serial input + byte n = Serial.available(); bool structChecker = true; @@ -1427,6 +1438,7 @@ void serialCommunicationTask( void * pvParameters ) Serial.flush(); //Serial.flush(true); + } // transmit controller output if (IsControllerReady()) @@ -1442,8 +1454,10 @@ void serialCommunicationTask( void * pvParameters ) SetControllerOutputValue(joystickNormalizedToInt32_local); } - - + /*#ifdef SERIAL_TIMEOUT + delay(10); + #endif +*/ } } diff --git a/ESP32/.gitignore b/ESP32/.gitignore new file mode 100644 index 00000000..89cc49cb --- /dev/null +++ b/ESP32/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/ESP32/.vscode/extensions.json b/ESP32/.vscode/extensions.json new file mode 100644 index 00000000..080e70d0 --- /dev/null +++ b/ESP32/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/ESP32/include/ABSOscillation.h b/ESP32/include/ABSOscillation.h new file mode 100644 index 00000000..d822f5ae --- /dev/null +++ b/ESP32/include/ABSOscillation.h @@ -0,0 +1,385 @@ +#pragma once + +#include "DiyActivePedal_types.h" + + +static const long ABS_ACTIVE_TIME_PER_TRIGGER_MILLIS = 100; +static const long RPM_ACTIVE_TIME_PER_TRIGGER_MILLIS = 100; +static const long BP_ACTIVE_TIME_PER_TRIGGER_MILLIS = 100; +static const long WS_ACTIVE_TIME_PER_TRIGGER_MILLIS = 100; +static int RPM_VALUE_LAST = 0; + +class ABSOscillation { +private: + long _timeLastTriggerMillis; + long _absTimeMillis; + long _lastCallTimeMillis = 0; + +public: + ABSOscillation() + : _timeLastTriggerMillis(0) + {} + +public: + void trigger() { + _timeLastTriggerMillis = millis(); + } + + void forceOffset(DAP_calculationVariables_st* calcVars_st, uint8_t absPattern, uint8_t absForceOrTarvelBit, float * absForceOffset, float * absPosOffset) { + + + long timeNowMillis = millis(); + float timeSinceTrigger = (timeNowMillis - _timeLastTriggerMillis); + float absForceOffset_local = 0; + + if (timeSinceTrigger > ABS_ACTIVE_TIME_PER_TRIGGER_MILLIS) + { + _absTimeMillis = 0; + absForceOffset = 0; + } + else + { + _absTimeMillis += timeNowMillis - _lastCallTimeMillis; + float absTimeSeconds = _absTimeMillis / 1000.0f; + + // abs amplitude + float absAmp_fl32 = 0; + switch (absForceOrTarvelBit) { + case 0: + absAmp_fl32 = calcVars_st->absAmplitude; + break; + case 1: + absAmp_fl32 = calcVars_st->stepperPosRange * calcVars_st->absAmplitude / 100.; + break; + default: + break; + } + + + + switch (absPattern) { + case 0: + // sine wave pattern + absForceOffset_local = absAmp_fl32 * sin(2 * PI * calcVars_st->absFrequency * absTimeSeconds); + break; + case 1: + // sawtooth pattern + if (calcVars_st->absFrequency > 0) + { + absForceOffset_local = absAmp_fl32 * fmod(absTimeSeconds, 1.0 / (float)calcVars_st->absFrequency) * (float)calcVars_st->absFrequency; + } + break; + default: + break; + } + + switch (absForceOrTarvelBit) { + case 0: + *absForceOffset = absForceOffset_local; + *absPosOffset = 0; + break; + case 1: + *absForceOffset = 0; + *absPosOffset = absForceOffset_local; + break; + default: + break; + } + + } + + _lastCallTimeMillis = timeNowMillis; + + return; + + } +}; + +class RPMOscillation { +private: + long _timeLastTriggerMillis; + long _RPMTimeMillis; + long _lastCallTimeMillis = 0; + + +public: + RPMOscillation() + : _timeLastTriggerMillis(0) + {} + float RPM_value =0; + int32_t RPM_position_offset = 0; +public: + void trigger() { + _timeLastTriggerMillis = millis(); + } + + void forceOffset(DAP_calculationVariables_st* calcVars_st) { + + + long timeNowMillis = millis(); + float timeSinceTrigger = (timeNowMillis - _timeLastTriggerMillis); + float RPMForceOffset = 0; + float RPM_max_freq = calcVars_st->RPM_max_freq; + float RPM_min_freq = calcVars_st->RPM_min_freq; + //float RPM_max =10; + float RPM_amp_base = calcVars_st->RPM_AMP; + float RPM_amp=0; + if(RPM_value==0) + { + RPM_min_freq=0; + } + RPM_amp=RPM_amp_base*(1+0.3*RPM_value/100); + + + float RPM_freq=constrain(RPM_value*(RPM_max_freq-RPM_min_freq)/100, RPM_min_freq, RPM_max_freq); + + + + if (timeSinceTrigger > RPM_ACTIVE_TIME_PER_TRIGGER_MILLIS) + { + _RPMTimeMillis = 0; + RPMForceOffset = RPM_VALUE_LAST; + } + else + { + _RPMTimeMillis += timeNowMillis - _lastCallTimeMillis; + float RPMTimeSeconds = _RPMTimeMillis / 1000.0f; + + //RPMForceOffset = calcVars_st->absAmplitude * sin(calcVars_st->absFrequency * RPMTimeSeconds); + RPMForceOffset = RPM_amp * sin( 2*PI* RPM_freq* RPMTimeSeconds); + } + + _lastCallTimeMillis = timeNowMillis; + RPM_VALUE_LAST=RPMForceOffset; + if (calcVars_st->Force_Range > 0) + { + RPM_position_offset = calcVars_st->stepperPosRange*(RPMForceOffset/calcVars_st->Force_Range); + } + //return RPMForceOffset; + + + } +}; + +class BitePointOscillation { +private: + long _timeLastTriggerMillis; + long _BiteTimeMillis; + long _lastCallTimeMillis = 0; + + +public: + BitePointOscillation() + : _timeLastTriggerMillis(0) + {} + //float RPM_value =0; + float BitePoint_Force_offset = 0; +public: + void trigger() { + _timeLastTriggerMillis = millis(); + } + + void forceOffset(DAP_calculationVariables_st* calcVars_st) { + + + long timeNowMillis = millis(); + float timeSinceTrigger = (timeNowMillis - _timeLastTriggerMillis); + float BitePointForceOffset = 0; + float BP_freq = calcVars_st->BP_freq; + //float BP_freq = 15; + float BP_amp = calcVars_st->BP_amp; + //float BP_amp = 2; + + if (timeSinceTrigger > BP_ACTIVE_TIME_PER_TRIGGER_MILLIS) + { + _BiteTimeMillis = 0; + BitePointForceOffset = 0; + } + else + { + _BiteTimeMillis += timeNowMillis - _lastCallTimeMillis; + float BPTimeSeconds = _BiteTimeMillis / 1000.0f; + + //RPMForceOffset = calcVars_st->absAmplitude * sin(calcVars_st->absFrequency * RPMTimeSeconds); + BitePointForceOffset = BP_amp * sin( 2*PI* BP_freq* BPTimeSeconds); + } + BitePoint_Force_offset=BitePointForceOffset; + _lastCallTimeMillis = timeNowMillis; + //RPM_VALUE_LAST=RPMForceOffset; + + //return RPMForceOffset; + + + } +}; + +// moving average filter:https://github.com/sebnil/Moving-Avarage-Filter--Arduino-Library-/tree/master + +#define MAX_DATA_POINTS 100 +class MovingAverageFilter +{ +public: + //construct without coefs + MovingAverageFilter(unsigned int newDataPointsCount); + int dataPointsCount; + float process(float in); + + +private: + float values[MAX_DATA_POINTS]; + int k; // k stores the index of the current array read to create a circular memory through the array + + float out; + int i; // just a loop counter +}; + +MovingAverageFilter::MovingAverageFilter(unsigned int newDataPointsCount) +{ + k = 0; //initialize so that we start to write at index 0 + if (newDataPointsCount < MAX_DATA_POINTS) + dataPointsCount = newDataPointsCount; + else + dataPointsCount = MAX_DATA_POINTS; + + for (i = 0; i < dataPointsCount; i++) + { + values[i] = 0; // fill the array with 0's + } +} + +float MovingAverageFilter::process(float in) +{ + out = 0; + + values[k] = in; + k = (k + 1) % dataPointsCount; + + for (i = 0; i < dataPointsCount; i++) + { + out += values[i]; + } + + float retValue= 0; + if (dataPointsCount> 0) + { + retValue= out / dataPointsCount; + } + + return retValue; +} + +MovingAverageFilter movingAverageFilter(100); +// G force effect +class G_force_effect +{ + public: + float G_value=0; + float G_force_raw=0; + float G_force=0; + + + void forceOffset(DAP_calculationVariables_st* calcVars_st, uint8_t G_multi) + { + uint32_t Force_Range; + float G_multiplier=((float)G_multi)/100; + Force_Range=calcVars_st->Force_Range; + if(G_value==-128) + { + G_force_raw=0; + + } + else + { + G_force_raw=10*(G_value)*G_multiplier/9.8; + //G_force_raw=constrain(G_force_raw,-1*Force_Range*0.25,Force_Range*0.25); + } + + //apply filter + G_force=movingAverageFilter.process(G_force_raw); + //G_force=G_force_raw; + + } +}; +//Wheel slip +class WSOscillation { +private: + long _timeLastTriggerMillis; + long _WSTimeMillis; + long _lastCallTimeMillis = 0; + + +public: + WSOscillation() + : _timeLastTriggerMillis(0) + {} + //float RPM_value =0; + float WS_Force_offset = 0; +public: + void trigger() { + _timeLastTriggerMillis = millis(); + } + + void forceOffset(DAP_calculationVariables_st* calcVars_st) { + + + long timeNowMillis = millis(); + float timeSinceTrigger = (timeNowMillis - _timeLastTriggerMillis); + float WSForceOffset = 0; + float WS_freq = calcVars_st->WS_freq; + //float BP_freq = 15; + float WS_amp = calcVars_st->WS_amp; + //float BP_amp = 2; + + if (timeSinceTrigger > WS_ACTIVE_TIME_PER_TRIGGER_MILLIS) + { + _WSTimeMillis = 0; + WSForceOffset = 0; + } + else + { + _WSTimeMillis += timeNowMillis - _lastCallTimeMillis; + float WSTimeSeconds = _WSTimeMillis / 1000.0f; + + //RPMForceOffset = calcVars_st->absAmplitude * sin(calcVars_st->absFrequency * RPMTimeSeconds); + WSForceOffset = WS_amp * sin( 2*PI* WS_freq* WSTimeSeconds); + /*if (WS_freq > 0) + { + //WSForceOffset = WS_amp * fmod(WSTimeSeconds, 1.0 / (float)WS_freq) * WS_freq; + //WSForceOffset = WS_amp * (2*fmod(WSTimeSeconds, 1.0 / (float)WS_freq) * WS_freq-1); + } + */ + + + } + WS_Force_offset=WSForceOffset; + _lastCallTimeMillis = timeNowMillis; + //RPM_VALUE_LAST=RPMForceOffset; + + //return RPMForceOffset; + + + } +}; +//Road impact +MovingAverageFilter movingAverageFilter_roadimpact(100); +class Road_impact_effect +{ + public: + float Road_Impact_force=0; + float Road_Impact_force_raw=0; + uint8_t Road_Impact_value=0; + + void forceOffset(DAP_calculationVariables_st* calcVars_st, uint8_t Road_impact_multi) + { + uint32_t Force_Range; + float Road_multiplier=((float)Road_impact_multi)/100; + Force_Range=calcVars_st->Force_Range; + //Road_multiplier=0.1; + Road_Impact_force_raw=0.3*Road_multiplier*((float)Force_Range)*((float)Road_Impact_value)/100; + + //apply filter + Road_Impact_force=movingAverageFilter_roadimpact.process(Road_Impact_force_raw); + + + } +}; diff --git a/ESP32/include/Controller.h b/ESP32/include/Controller.h new file mode 100644 index 00000000..b5ac1f4e --- /dev/null +++ b/ESP32/include/Controller.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Arduino.h" +#include "Main.h" + +/*#ifdef CONFIG_IDF_TARGET_ESP32S2 ||ARDUINO_ESP32S3_DEV || CONFIG_IDF_TARGET_ESP32S3 + #define USB_JOYSTICK +#elif CONFIG_IDF_TARGET_ESP32 + #define BLUETOOTH_GAMEPAD +#endif +*/ + + + + +void SetupController(); +bool IsControllerReady(); + +void SetControllerOutputValue(int32_t value); +int32_t NormalizeControllerOutputValue(float value, float minVal, float maxVal, float maxGameOutput); diff --git a/ESP32/include/CycleTimer.h b/ESP32/include/CycleTimer.h new file mode 100644 index 00000000..12fee68e --- /dev/null +++ b/ESP32/include/CycleTimer.h @@ -0,0 +1,38 @@ +#pragma once + +#include "freertos/timers.h" +#include "RTDebugOutput.h" + +static const int MAX_CYCLES = 1000; + +class CycleTimer { +private: + RTDebugOutput _rtOutput; + int64_t _timeFirst; + unsigned int _cycleCount; + +public: + CycleTimer(String timerName) + : _rtOutput({ timerName }) + { + ResetTimer(); + } + + void ResetTimer() { + _timeFirst = esp_timer_get_time(); + _cycleCount = 0; + } + + void Bump() { + _cycleCount++; + if (_cycleCount > MAX_CYCLES) { + int64_t timeEnd = esp_timer_get_time(); + int64_t timeElapsed = timeEnd - _timeFirst; + + float averageCycleTime = float(timeElapsed) / MAX_CYCLES; + _rtOutput.offerData({ averageCycleTime }); + + ResetTimer(); + } + } +}; diff --git a/ESP32/include/DiyActivePedal_types.h b/ESP32/include/DiyActivePedal_types.h new file mode 100644 index 00000000..25cec8e3 --- /dev/null +++ b/ESP32/include/DiyActivePedal_types.h @@ -0,0 +1,246 @@ +#pragma once + +#include + +// define the payload revision +#define DAP_VERSION_CONFIG 136 + +// define the payload types +#define DAP_PAYLOAD_TYPE_CONFIG 100 +#define DAP_PAYLOAD_TYPE_ACTION 110 +#define DAP_PAYLOAD_TYPE_STATE_BASIC 120 +#define DAP_PAYLOAD_TYPE_STATE_EXTENDED 130 + +struct payloadHeader { + + // structure identification via payload + uint8_t payloadType; + + // variable to check if structure at receiver matched version from transmitter + uint8_t version; + + // store to EEPROM flag + uint8_t storeToEeprom; + +}; + +struct payloadPedalAction { + uint8_t triggerAbs_u8; + uint8_t resetPedalPos_u8; + uint8_t startSystemIdentification_u8; + uint8_t returnPedalConfig_u8; + uint8_t RPM_u8; + uint8_t G_value; + uint8_t WS_u8; + uint8_t impact_value_u8; +}; + + +struct payloadPedalState_Basic { + uint16_t pedalPosition_u16; + uint16_t pedalForce_u16; + uint16_t joystickOutput_u16; +}; + +struct payloadPedalState_Extended { + + unsigned long timeInMs_u32; + float pedalForce_raw_fl32; + float pedalForce_filtered_fl32; + float forceVel_est_fl32; + + // register values from servo + int16_t servoPosition_i16; + int16_t servoPositionTarget_i16; + int16_t servo_voltage_0p1V; + int16_t servo_current_percent_i16; +}; + +struct payloadPedalConfig { + // configure pedal start and endpoint + // In percent + uint8_t pedalStartPosition; + uint8_t pedalEndPosition; + + // configure pedal forces + uint8_t maxForce; + uint8_t preloadForce; + + // design force vs travel curve + // In percent + uint8_t relativeForce_p000; + uint8_t relativeForce_p020; + uint8_t relativeForce_p040; + uint8_t relativeForce_p060; + uint8_t relativeForce_p080; + uint8_t relativeForce_p100; + + // parameter to configure damping + uint8_t dampingPress; + uint8_t dampingPull; + + // configure ABS effect + uint8_t absFrequency; // In Hz + uint8_t absAmplitude; // In kg/20 + uint8_t absPattern; // 0: sinewave, 1: sawtooth + uint8_t absForceOrTarvelBit; // 0: Force, 1: travel + + + // geometric properties of the pedal + // in mm + int16_t lengthPedal_a; + int16_t lengthPedal_b; + int16_t lengthPedal_d; + int16_t lengthPedal_c_horizontal; + int16_t lengthPedal_c_vertical; + int16_t lengthPedal_travel; + + + //Simulate ABS trigger + uint8_t Simulate_ABS_trigger; + uint8_t Simulate_ABS_value; + // configure for RPM effect + uint8_t RPM_max_freq; //In HZ + uint8_t RPM_min_freq; //In HZ + uint8_t RPM_AMP; //In Kg + + //configure for bite point + uint8_t BP_trigger_value; + uint8_t BP_amp; + uint8_t BP_freq; + uint8_t BP_trigger; + //G force effect + uint8_t G_multi; + uint8_t G_window; + //wheel slip + uint8_t WS_amp; + uint8_t WS_freq; + //Road impact effect + uint8_t Road_multi; + uint8_t Road_window; + // cubic spline parameters + float cubic_spline_param_a_array[5]; + float cubic_spline_param_b_array[5]; + + // PID parameters + float PID_p_gain; + float PID_i_gain; + float PID_d_gain; + float PID_velocity_feedforward_gain; + + // MPC settings + float MPC_0th_order_gain; + float MPC_1st_order_gain; + float MPC_2nd_order_gain; + + uint8_t control_strategy_b; + + // controller settings + uint8_t maxGameOutput; + + // Kalman filter model noise + uint8_t kf_modelNoise; + uint8_t kf_modelOrder; + + // debug flags, sued to enable debug output + uint8_t debug_flags_0; + + // loadcell rating in kg / 2 --> to get value in kg, muiltiply by 2 + uint8_t loadcell_rating; + + // use loadcell or travel as joystick output + uint8_t travelAsJoystickOutput_u8; + + // invert loadcell sign + uint8_t invertLoadcellReading_u8; + + // invert motor direction + uint8_t invertMotorDirection_u8; + + // spindle pitch in mm/rev + uint8_t spindlePitch_mmPerRev_u8; + + //pedal type, 0= clutch, 1= brake, 2= gas + uint8_t pedal_type; + //OTA flag + uint8_t OTA_flag; + + uint8_t enableReboot_u8; + + +}; + +struct payloadFooter { + // To check if structure is valid + uint16_t checkSum; +}; + + +struct DAP_actions_st { + payloadHeader payLoadHeader_; + payloadPedalAction payloadPedalAction_; + payloadFooter payloadFooter_; +}; + +struct DAP_state_basic_st { + payloadHeader payLoadHeader_; + payloadPedalState_Basic payloadPedalState_Basic_; + payloadFooter payloadFooter_; +}; + +struct DAP_state_extended_st { + payloadHeader payLoadHeader_; + payloadPedalState_Extended payloadPedalState_Extended_; + payloadFooter payloadFooter_; +}; + + +struct DAP_config_st { + + payloadHeader payLoadHeader_; + payloadPedalConfig payLoadPedalConfig_; + payloadFooter payloadFooter_; + + + void initialiseDefaults(); + void initialiseDefaults_Accelerator(); + void loadConfigFromEprom(DAP_config_st& config_st); + void storeConfigToEprom(DAP_config_st& config_st); +}; + + +struct DAP_calculationVariables_st +{ + float springStiffnesss; + float springStiffnesssInv; + float Force_Min; + float Force_Max; + float Force_Range; + long stepperPosMinEndstop; + long stepperPosMaxEndstop; + long stepperPosEndstopRange; + float RPM_max_freq; + float RPM_min_freq; + float RPM_AMP; + long stepperPosMin; + long stepperPosMax; + float stepperPosRange; + float startPosRel; + float endPosRel; + float absFrequency; + float absAmplitude; + float rpm_value; + float BP_trigger_value; + float BP_amp; + float BP_freq; + float dampingPress; + float Force_Max_default; + float WS_amp; + float WS_freq; + + void updateFromConfig(DAP_config_st& config_st); + void updateEndstops(long newMinEndstop, long newMaxEndstop); + void updateStiffness(); + void dynamic_update(); + void reset_maxforce(); +}; diff --git a/ESP32/include/ForceCurve.h b/ESP32/include/ForceCurve.h new file mode 100644 index 00000000..c2251d97 --- /dev/null +++ b/ESP32/include/ForceCurve.h @@ -0,0 +1,14 @@ +#pragma once + +#include "DiyActivePedal_types.h" + +// The number of segments, which are defined for the spline +#define NUMBER_OF_SPLINE_SEGMENTS 5 + +class ForceCurve_Interpolated { + +public: + float EvalForceCubicSpline(const DAP_config_st* config_st, const DAP_calculationVariables_st* calc_st, float fractionalPos); + float EvalForceGradientCubicSpline(const DAP_config_st* config_st, const DAP_calculationVariables_st* calc_st, float fractionalPos, bool normalized_b); + +}; diff --git a/ESP32/include/LoadCell.h b/ESP32/include/LoadCell.h new file mode 100644 index 00000000..5e010ae7 --- /dev/null +++ b/ESP32/include/LoadCell.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "Main.h" + +class LoadCell_ADS1256 { +private: + float _zeroPoint = 0.0; + float _varianceEstimate = 0.0; + float _standardDeviationEstimate = 0.0; + +public: + LoadCell_ADS1256(uint8_t channel0=0, uint8_t channel1=1); + float getReadingKg() const; + void setLoadcellRating(uint8_t loadcellRating_u8) const; + +public: + void setZeroPoint(); + void estimateVariance(); + +public: + float getVarianceEstimate() const { return _varianceEstimate; } +}; diff --git a/ESP32/include/Main.h b/ESP32/include/Main.h new file mode 100644 index 00000000..e243a1d0 --- /dev/null +++ b/ESP32/include/Main.h @@ -0,0 +1,259 @@ +#pragma once + +//#include + +/********************************************************************/ +/* Select the PCB */ +/********************************************************************/ +#ifndef PCB_VERSION + //#define PCB_VERSION 1 // V1 for regular ESP32 + //#define PCB_VERSION 2 // V1 for ESP32 S2 mini + #define PCB_VERSION 3 // V3 for regular ESP32 + //#define PCB_VERSION 4 // speedcrafter PCB V1.3 + //#define PCB_VERSION 5 // speedcrafter PCB V1.4 + //#define PCB_VERSION 6 // V1 for ESP32 S3 +#endif + + + +/********************************************************************/ +/* Other defines */ +/********************************************************************/ + +// target cycle time for pedal update task, to get constant cycle times, required for FIR filtering +#define DAP_MICROSECONDS_PER_SECOND 1000000 + +// 15kHz +//#define ADC_SAMPLE_RATE ADS1256_DRATE_15000SPS +//#define PUT_TARGET_CYCLE_TIME_IN_US DAP_MICROSECONDS_PER_SECOND / 15000 + +// 7.5kHz +//#define ADC_SAMPLE_RATE ADS1256_DRATE_7500SPS +//#define PUT_TARGET_CYCLE_TIME_IN_US DAP_MICROSECONDS_PER_SECOND / 7500 + +// 3.75kHz +//#define ADC_SAMPLE_RATE ADS1256_DRATE_3750SPS +//#define PUT_TARGET_CYCLE_TIME_IN_US DAP_MICROSECONDS_PER_SECOND / 3750 + +// 2.0kHz +//#define ADC_SAMPLE_RATE ADS1256_DRATE_2000SPS +//#define PUT_TARGET_CYCLE_TIME_IN_US DAP_MICROSECONDS_PER_SECOND / 2000 + +// 1.0kHz +#define ADC_SAMPLE_RATE ADS1256_DRATE_1000SPS +#define PUT_TARGET_CYCLE_TIME_IN_US DAP_MICROSECONDS_PER_SECOND / 1000 + + + +/********************************************************************/ +/* Loadcell defines */ +/********************************************************************/ +#define LOADCELL_WEIGHT_RATING_KG 300.0 +#define LOADCELL_EXCITATION_V 5.0 +#define LOADCELL_SENSITIVITY_MV_V 2.0 + + +/********************************************************************/ +/* Motor defines */ +/********************************************************************/ +//#define MOTOR_INVERT_MOTOR_DIR false + + + + +/********************************************************************/ +/* PIN defines */ +/********************************************************************/ +// initial version of dev PCB for regular ESP32 +#if PCB_VERSION == 1 + // ADC defines + #define PIN_DRDY 17// 17 --> DRDY + #define PIN_RST 16 // X --> X + #define PIN_SCK 18 // 18 -->SCLK + #define PIN_MISO 19 // 19 --> DOUT + #define PIN_MOSI 23 // 23 --> DIN + #define PIN_CS 5 // 5 --> CS + + // stepper pins + #define dirPinStepper 0 + #define stepPinStepper 4 + + // endstop pins + #define minPin 34 + #define maxPin 35 + + // level shifter not present on this PCB design + #define SENSORLESS_HOMING false + + #define BLUETOOTH_GAMEPAD + //#define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 +#endif + + +// initial version of dev PCB for ESP32 S2 mini +#if PCB_VERSION == 2 + // ADC defines + #define PIN_DRDY 37// 37 --> DRDY + #define PIN_RST 16 // X --> X + #define PIN_SCK 18 // 18 -->SCLK + #define PIN_MISO 35 // 35 --> DOUT + #define PIN_MOSI 33 // 33 --> DIN + #define PIN_CS 39 // 39 --> CS + + // stepper pins + #define dirPinStepper 8 + #define stepPinStepper 9 + + // endstop pins + #define minPin 11 + #define maxPin 10 + + // level shifter not present on this PCB design + #define SENSORLESS_HOMING false + + //#define BLUETOOTH_GAMEPAD + #define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 +#endif + + + +// V3 version of dev PCB for regular ESP32 +#if PCB_VERSION == 3 + // ADC defines + #define PIN_DRDY 19// 19 --> DRDY + #define PIN_RST 15 // X --> X + #define PIN_SCK 16 // 16 -->SCLK + #define PIN_MISO 18 // 18 --> DOUT + #define PIN_MOSI 17 // 17 --> DIN + #define PIN_CS 21 // 21 --> CS + + // stepper pins + #define dirPinStepper 22 + #define stepPinStepper 23 + //analog output pin + #define D_O 25 + + // endstop pins + #define minPin 12 + #define maxPin 13 + + // level shifter is present on this PCB design + #define SENSORLESS_HOMING true + #define ISV57_TXPIN 27 //17 + #define ISV57_RXPIN 26 // 16 + + #define Using_analog_output + + #define BLUETOOTH_GAMEPAD + //#define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 +#endif + + + +// speedcrafter PCB V1.3 +#if PCB_VERSION == 4 + // ADC defines + #define PIN_DRDY 27// 19 --> DRDY + #define PIN_RST 5 // X --> X + #define PIN_SCK 14 // 16 -->SCLK + #define PIN_MISO 12 // 18 --> DOUT + #define PIN_MOSI 13 // 17 --> DIN + #define PIN_CS 15 // 21 --> CS + + // stepper pins + #define dirPinStepper 32 + #define stepPinStepper 33 + + // endstop pins + #define minPin 35 + #define maxPin 34 + + // level shifter is present on this PCB design + #define SENSORLESS_HOMING true + #define ISV57_TXPIN 27 //17 + #define ISV57_RXPIN 26 // 16 + + #define BLUETOOTH_GAMEPAD + //#define USB_JOYSTICK + + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 +#endif + + + + +// Speedcrafters PCB V1.4 +#if PCB_VERSION == 5 + // ADC defines + #define PIN_DRDY 19// 19 --> DRDY + #define PIN_RST 34 // X --> X + #define PIN_SCK 15 // 16 -->SCLK + #define PIN_MISO 18 // 18 --> DOUT + #define PIN_MOSI 13 // 17 --> DIN + #define PIN_CS 21 // 21 --> CS + + // stepper pins + #define dirPinStepper 22 + #define stepPinStepper 23 + + // endstop pins + #define minPin 35 + #define maxPin 34 + + // level shifter is present on this PCB design + #define SENSORLESS_HOMING true + #define ISV57_TXPIN 26 //17 + #define ISV57_RXPIN 27 // 16 + + #define BLUETOOTH_GAMEPAD + //#define USB_JOYSTICK + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 1 +#endif + + + + +// V3 version of dev PCB for ESP32 S3 +// flash instructions, see https://hutscape.com/tutorials/hello-arduino-esp32s3 +// 1. ESP32S3 Dev Module +// 2. USB CDC On Boot Enabled +#if PCB_VERSION == 6 + // ADC defines + #define PIN_DRDY 15//19// 19 --> DRDY + #define PIN_RST 6 // X --> X + #define PIN_SCK 16//16 // 16 -->SCLK + #define PIN_MISO 18 // 18 --> DOUT + #define PIN_MOSI 17 // 17 --> DIN + #define PIN_CS 7//21 // 21 --> CS + + // stepper pins + #define dirPinStepper 37//22 + #define stepPinStepper 38//23 + + //analog output pin + //#define D_O 25 + //MCP4725 SDA SCL + #define MCP_SDA 48 + #define MCP_SCL 47 + + // endstop pins + #define minPin 12 + #define maxPin 13 + + // level shifter is present on this PCB design + #define SENSORLESS_HOMING true + #define ISV57_TXPIN 10//27 //17 + #define ISV57_RXPIN 9//26 // 16 + + #define Using_analog_output_ESP32_S3 + + //#define BLUETOOTH_GAMEPAD + #define USB_JOYSTICK + + #define SERIAL_COOMUNICATION_TASK_DELAY_IN_MS 5 +#endif + + diff --git a/ESP32/include/Modbus.h b/ESP32/include/Modbus.h new file mode 100644 index 00000000..eaa07bdf --- /dev/null +++ b/ESP32/include/Modbus.h @@ -0,0 +1,103 @@ +#ifndef MODBUS_H +#define MODBUS_H + +// #ifdef MODEBUS_LOG +// #define MODEBUS_LOG + + +#include +#include +#include +using namespace std; + +class Modbus +{ +private: + /* data */ + bool log = false; + int mode_ = -1; + uint32_t timeout_ = 20; + HardwareSerial* s ; + uint8_t rawRx[512]; + int lenRx = 0; + uint8_t dataRx[512]; + int datalen = 0; + int SlaveID = 0x01; + uint8_t txout[9] = {0,0,0,0,0,0,0,0,0}; + #define Coil_Register 0x01 + #define Discret_Register 0x02 + #define Holding_Register 0x03 + #define Input_Register 0x04 + #define Write_Holding_Register 0x06 + // vector txbuff; + // vector rxbuff; + +public: + + Modbus(); + Modbus(HardwareSerial &st); + + bool init(int mode, bool en_log = false); + void setTimeout(uint16_t timeout); + + + uint8_t byteRead(int nb); + int blockRead(int index); + int coilRead(int address); //Return 1 byte = 8 bit coil + int coilRead(int id, int address); + int discreteInputRead(int address); + int discreteInputRead(int id, int address); + long holdingRegisterRead(int address); + long holdingRegisterRead(int id, int address, int block); + long inputRegisterRead(int address); + long inputRegisterRead(int id, int address, int block); + + int coilWrite(int address, uint8_t value); + int coilWrite(int id, int address, uint8_t value); + int holdingRegisterWrite(int address, uint16_t value); + int holdingRegisterWrite(int id, int address, uint16_t value); + void RxRaw(uint8_t *raw, uint8_t &rlen); + void TxRaw(uint8_t *raw, uint8_t &rlen); + //Read multiple coils, discrete inputs, holding registers, or input register values. + //int requestFrom(int type, int address, int nb, byte *ret,int len); + int requestFrom(int slaveId, int type, int address,int nb); + // ~Modbus(); + bool checkAndReplaceParameter(uint16_t slaveId_local_u16, uint16_t parameterAdress, long value); + void readParameter(uint16_t slaveId_local_u16, uint16_t parameterAdress); + + + // Read Coil Register 0x01 + int ReadCoilReg(int add); + int ReadCoilReg(int slaveId, int add); + int ReadCoilReg(int slaveId, int add, int nbit); + + // Read Discret Register 0x02 + int ReadDiscretReg(int add); + int ReadDiscretReg(int slaveId, int add); + int ReadDiscretReg(int slaveId, int add, int nbit); + + // Read Holding Register 0x03 + int ReadHoldingReg(int add); + int ReadHoldingReg(int slaveId, int add); + int ReadHoldingReg(int slaveId, int add, int nbyte); + + // Read Input Register 0x04 + int ReadInputReg(int add); + int ReadInputReg(int slaveId, int add); + int ReadInputReg(int slaveId, int add, int nbyte); + + + int8_t uint8(int add); + uint16_t uint16(int add); + uint32_t uint32(int add, bool byteHL = true); + + + + int CheckCRC(uint8_t *buf, int len); +}; + +// #else +// #error "Log not defined" +// #endif + +#endif diff --git a/ESP32/include/PedalGeometry.h b/ESP32/include/PedalGeometry.h new file mode 100644 index 00000000..c5ab5100 --- /dev/null +++ b/ESP32/include/PedalGeometry.h @@ -0,0 +1,17 @@ +#pragma once + +#include "DiyActivePedal_types.h" +#include + +static const float TRAVEL_PER_ROTATION_IN_MM = 5.0; // determined by lead screw pitch & starts + + +class StepperWithLimits; + +float sledPositionInMM(StepperWithLimits* stepper, DAP_config_st& config_st); +float pedalInclineAngleDeg(float sledPositionMM, DAP_config_st& config_st); +float pedalInclineAngleAccel(float pedalInclineAngleDeg_global); +float convertToPedalForce(float F_l, float sledPositionMM, DAP_config_st& config_st); +float convertToPedalForceGain(float sledPositionMM, DAP_config_st& config_st); + + diff --git a/ESP32/include/README b/ESP32/include/README new file mode 100644 index 00000000..194dcd43 --- /dev/null +++ b/ESP32/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/ESP32/include/RTDebugOutput.h b/ESP32/include/RTDebugOutput.h new file mode 100644 index 00000000..df770083 --- /dev/null +++ b/ESP32/include/RTDebugOutput.h @@ -0,0 +1,61 @@ +#pragma once + +#include + + +template +class RTDebugOutput { +private: + QueueHandle_t _queue_data; + std::array _outNames; + +public: + RTDebugOutput(std::array outNames = {}) + : _outNames(outNames) + { + _queue_data = xQueueCreate(1, sizeof(std::array)); + xTaskCreatePinnedToCore(this->debugOutputTask, "debugOutputTask", 5000, this, 1, NULL, 1); + } + + void offerData(std::array values) { + xQueueSend(_queue_data, &values, /*xTicksToWait=*/0); + } + + + template + void printValue(String name, T value) { + if (name.length() > 0) { + Serial.print(name); Serial.print(":"); + } + Serial.print(value); Serial.print(","); + } + void printValue(String name, float value) { + if (name.length() > 0) { + Serial.print(name); Serial.print(":"); + } + Serial.print(value, FLOAT_PRECISION); Serial.print(","); + } + + void printData() { + std::array values; + if (pdTRUE == xQueueReceive(_queue_data, &values, /*xTicksToWait=*/0)) { + static SemaphoreHandle_t semaphore_print = xSemaphoreCreateMutex(); + if (xSemaphoreTake(semaphore_print, /*xTicksToWait=*/10) == pdTRUE) { + for (int i=0; iprintData(); + taskYIELD(); + } + } +}; diff --git a/ESP32/include/SignalFilter.h b/ESP32/include/SignalFilter.h new file mode 100644 index 00000000..70cc2a95 --- /dev/null +++ b/ESP32/include/SignalFilter.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +static const int Nobs = 1; // 1 filter input: observed value +static const int Nstate = 2; // 2 filter outputs: change & velocity +static const int Ncom = 1; // Number of commands, u vector + + +class KalmanFilter { +private: + KALMAN _K; + unsigned long _timeLastObservation; + +public: + KalmanFilter(float varianceEstimate); + + float filteredValue(float observation, float command, uint8_t modelNoiseScaling_u8); + float changeVelocity(); +}; diff --git a/ESP32/include/SignalFilter_2nd_order.h b/ESP32/include/SignalFilter_2nd_order.h new file mode 100644 index 00000000..791cc92e --- /dev/null +++ b/ESP32/include/SignalFilter_2nd_order.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +static const int Nobs_2nd_order = 1; // 1 filter input: observed value +static const int Nstate_2nd_order = 3; // 2 filter outputs: change, velocity & acceleration +static const int Ncom_2nd_order = 1; // Number of commands, u vector + + +class KalmanFilter_2nd_order { +private: + KALMAN _K; + unsigned long _timeLastObservation; + +public: + KalmanFilter_2nd_order(float varianceEstimate); + + float filteredValue(float observation, float command, uint8_t modelNoiseScaling_u8); + float changeVelocity(); + float changeAccel(); +}; diff --git a/ESP32/include/StepperMovementStrategy.h b/ESP32/include/StepperMovementStrategy.h new file mode 100644 index 00000000..e3abeb58 --- /dev/null +++ b/ESP32/include/StepperMovementStrategy.h @@ -0,0 +1,488 @@ +#pragma once + +#include "DiyActivePedal_types.h" +#include "Main.h" + + + + + +// see https://github.com/Dlloydev/QuickPID/blob/master/examples/PID_Basic/PID_Basic.ino +#include + +//Define Variables we'll be connecting to +float Setpoint, Input, Output; + +//Specify the links and initial tuning parameters +double Kp=0.3, Ki=50.0, Kd=0.000; +uint8_t control_strategy_u8 = 0; +QuickPID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, /* OPTIONS */ + myPID.pMode::pOnError, /* pOnError, pOnMeas, pOnErrorMeas */ + myPID.dMode::dOnMeas, /* dOnError, dOnMeas */ + myPID.iAwMode::iAwOff, /* iAwCondition, iAwClamp, iAwOff */ + myPID.Action::direct); /* direct, reverse */ +bool pidWasInitialized = false; + +#define PID_OUTPUT_LIMIT_FL32 0.5f + +/**********************************************************************************************/ +/* */ +/* Movement strategy: spring stiffness */ +/* */ +/**********************************************************************************************/ + +int32_t MoveByInterpolatedStrategy(float filteredLoadReadingKg, float stepperPosFraction, ForceCurve_Interpolated* forceCurve, const DAP_calculationVariables_st* calc_st, const DAP_config_st* config_st) { + float spingStiffnessInv_lcl = calc_st->springStiffnesssInv; + //float springStiffnessInterp = forceCurve->stiffnessAtPosition(stepperPosFraction); + float springStiffnessInterp = forceCurve->EvalForceGradientCubicSpline(config_st, calc_st, stepperPosFraction, false); + + + if (springStiffnessInterp > 0) { + spingStiffnessInv_lcl = (1.0f / springStiffnessInterp); + } + + // caclulate pedal position + float pedalForceInterp = forceCurve->EvalForceCubicSpline(config_st, calc_st, stepperPosFraction); + float stepperPosInterp = (calc_st->stepperPosMax - calc_st->stepperPosMin) * stepperPosFraction; + return spingStiffnessInv_lcl * (filteredLoadReadingKg - pedalForceInterp) + stepperPosInterp; +} + + + + +/**********************************************************************************************/ +/* */ +/* Movement strategy: PID */ +/* */ +/**********************************************************************************************/ + +void tunePidValues(DAP_config_st& config_st) +{ + Kp=config_st.payLoadPedalConfig_.PID_p_gain; + Ki=config_st.payLoadPedalConfig_.PID_i_gain; + Kd=config_st.payLoadPedalConfig_.PID_d_gain; + + control_strategy_u8 = config_st.payLoadPedalConfig_.control_strategy_b; + + myPID.SetTunings(config_st.payLoadPedalConfig_.PID_p_gain, config_st.payLoadPedalConfig_.PID_i_gain, config_st.payLoadPedalConfig_.PID_d_gain); +} + +int32_t MoveByPidStrategy(float loadCellReadingKg, float stepperPosFraction, StepperWithLimits* stepper, ForceCurve_Interpolated* forceCurve, const DAP_calculationVariables_st* calc_st, DAP_config_st* config_st, float absForceOffset_fl32, float changeVelocity) { + + if (pidWasInitialized == false) + { + //turn the PID on + myPID.SetTunings(Kp, Ki, Kd); + myPID.SetMode(myPID.Control::automatic); + //myPID.SetAntiWindupMode(myPID.iAwMode::iAwCondition); + myPID.SetAntiWindupMode(myPID.iAwMode::iAwClamp); + //myPID.SetAntiWindupMode(myPID.iAwMode::iAwOff); + + + pidWasInitialized = true; + myPID.SetSampleTimeUs(PUT_TARGET_CYCLE_TIME_IN_US); + //myPID.SetOutputLimits(-1.0,0.0); + myPID.SetOutputLimits(-PID_OUTPUT_LIMIT_FL32, PID_OUTPUT_LIMIT_FL32); // allow the PID to only change the position a certain amount per cycle + + myPID.SetTunings(config_st->payLoadPedalConfig_.PID_p_gain, config_st->payLoadPedalConfig_.PID_i_gain, config_st->payLoadPedalConfig_.PID_d_gain); + } + + + // clamp the stepper position to prevent problems with the spline + float stepperPosFraction_constrained = constrain(stepperPosFraction, 0, 1); + + // constrain the output to the correct positioning interval to prevent PID windup + float neg_output_limit_fl32 = 1.0 - stepperPosFraction_constrained; + float pos_output_limit_fl32 = stepperPosFraction_constrained; + if (pos_output_limit_fl32 < PID_OUTPUT_LIMIT_FL32) + { + myPID.SetOutputLimits(-PID_OUTPUT_LIMIT_FL32, pos_output_limit_fl32); + } + else if (neg_output_limit_fl32 < PID_OUTPUT_LIMIT_FL32) + { + myPID.SetOutputLimits(-neg_output_limit_fl32, PID_OUTPUT_LIMIT_FL32); + } + else + { + myPID.SetOutputLimits(-PID_OUTPUT_LIMIT_FL32, PID_OUTPUT_LIMIT_FL32); + } + + + + // read target force at spline position + float loadCellTargetKg = forceCurve->EvalForceCubicSpline(config_st, calc_st, stepperPosFraction_constrained); + + // apply effect force offset + loadCellTargetKg -=absForceOffset_fl32; + + // clip to min & max force to prevent Ki to overflow + float loadCellReadingKg_clip = constrain(loadCellReadingKg, calc_st->Force_Min, calc_st->Force_Max); + float loadCellTargetKg_clip = constrain(loadCellTargetKg, calc_st->Force_Min, calc_st->Force_Max); + + + // dynamically scale the PID values depending on the force curve gradient + if (control_strategy_u8 == 1) + { + float gradient_orig_fl32 = forceCurve->EvalForceGradientCubicSpline(config_st, calc_st, stepperPosFraction_constrained, true); // determine gradient to modify the PID gain. The steeper the gradient, the less gain should be used + + // normalize gradient + float gradient_fl32 = gradient_orig_fl32; + float gradient_abs_fl32 = fabs(gradient_fl32); + + float gain_modifier_fl32 = 10.0; + if (gradient_abs_fl32 > 1e-5) + { + gain_modifier_fl32 = 1.0 / pow( gradient_abs_fl32 , 1); + } + gain_modifier_fl32 = constrain( gain_modifier_fl32, 0.1, 10); + + myPID.SetTunings(gain_modifier_fl32 * Kp, gain_modifier_fl32 * Ki, gain_modifier_fl32 * Kd); + } + else + { + myPID.SetTunings(Kp, Ki, Kd); + } + + + + + + + + + + // ToDO + // - Min and Max force need to be identified from forceCurve->forceAtPosition() as they migh differ from calc_st.Force_Min & calc_st.Force_Max + // - model predictive control, see e.g. https://www.researchgate.net/profile/Mohamed-Mourad-Lafifi/post/Model-Predictive-Control-examples/attachment/60202ac761fb570001029f61/AS%3A988637009301508%401612720839656/download/An+Introduction+to+Model-based+Predictive+Control+%28MPC%29.pdf + // https://www.youtube.com/watch?v=XaD8Lngfkzk + // https://github.com/pronenewbits/Arduino_Constrained_MPC_Library + + if (calc_st->Force_Range > 0.001) + { + Input = ( loadCellReadingKg_clip - calc_st->Force_Min) / calc_st->Force_Range; + Setpoint = ( loadCellTargetKg_clip - calc_st->Force_Min) / calc_st->Force_Range; + } + else + { + Input = 0; + Setpoint= 0; + } + // compute PID output + myPID.Compute(); + + // integrate the position update + // The setpoint comes from the force curve. The input comes from the loadcell. When the loadcell reading is below the force curve, the difference becomes positive. + // Thus, the stepper has to move towards the foot to increase the loadcell reading. + // Since the QuickPID has some filtering applied on the input, both variables are changed for performance reasons. + float posStepperNew_fl32 = stepperPosFraction - Output; + posStepperNew_fl32 *= (float)(calc_st->stepperPosMax - calc_st->stepperPosMin); + posStepperNew_fl32 += calc_st->stepperPosMin; + + // add velocity feedforward + posStepperNew_fl32 += changeVelocity * config_st->payLoadPedalConfig_.PID_velocity_feedforward_gain; + + // convert position to integer + int32_t posStepperNew = floor(posStepperNew_fl32); + + // clamp target position to range + posStepperNew=constrain(posStepperNew,calc_st->stepperPosMin,calc_st->stepperPosMax ); + + //#define PLOT_PID_VALUES + #ifdef PLOT_PID_VALUES + static RTDebugOutput rtDebugFilter({ "stepperPosFraction", "loadCellTargetKg", "loadCellReadingKg", "loadCellReadingKg_clip", "Setpoint", "Input", "Output", "posStepperNew"}); + rtDebugFilter.offerData({ stepperPosFraction, loadCellTargetKg, loadCellReadingKg, loadCellReadingKg_clip, Setpoint, Input, Output, posStepperNew}); + #endif + + + return posStepperNew; +} + + + + +int32_t MoveByForceTargetingStrategy(float loadCellReadingKg, StepperWithLimits* stepper, ForceCurve_Interpolated* forceCurve, const DAP_calculationVariables_st* calc_st, DAP_config_st* config_st, float absForceOffset_fl32, float changeVelocity, float stepper_vel_filtered_fl32, float stepper_accel_filtered_fl32, float d_phi_d_x, float d_x_hor_d_phi) { + // see https://github.com/ChrGri/DIY-Sim-Racing-FFB-Pedal/wiki/Movement-control-strategies#mpc + + + /* + This closed-loop control strategy models the foot as a spring with a certain stiffness k1. + The force resulting from that model is F1 = k1 * x. + To find the servo target position: + 1) A line with the slope -k1 at the point of the loadcell reading & current position is drawn. + 2) The intersection with the force-travel curve gives the target position + + Since the force-travel curve might be non-linear, Newtons method is used to numerically find the intersection point. + f(x_n) = -k1 * x + b - forceCurve(x) = 0 + x_n+1 = x_n - f(x_n) / f'(x_n) + whereas x_n is the servo position at iteration n + f(x_n) = -k1 * x + b - forceCurve(x) + f'(x_n) = -k1 - d(forceCurve(x)) / dx + */ + + // get current stepper position + float stepperPos = stepper->getCurrentPosition(); + + // add velocity feedforward + stepperPos += changeVelocity * config_st->payLoadPedalConfig_.PID_velocity_feedforward_gain; + + // motion corrected loadcell reading + float loadCellReadingKg_corrected = loadCellReadingKg; + //loadCellReadingKg_corrected += config_st->payLoadPedalConfig_.MPC_1st_order_gain * stepper_vel_filtered_fl32 / STEPS_PER_MOTOR_REVOLUTION / 10;// + stepper_accel_filtered_fl32; + //loadCellReadingKg_corrected += config_st->payLoadPedalConfig_.MPC_1st_order_gain * stepper_accel_filtered_fl32 / STEPS_PER_MOTOR_REVOLUTION / 10;// + stepper_accel_filtered_fl32; + + + // set initial guess + float stepperPos_initial = stepperPos; + + // Find the intersections of the force curve and the foot model via Newtons-method + #define MAX_NUMBER_OF_NEWTON_STEPS 5 + for (uint8_t iterationIdx = 0; iterationIdx < MAX_NUMBER_OF_NEWTON_STEPS; iterationIdx++) + { + //float stepperPosFraction = stepper->getCurrentPositionFraction(); + float stepperPosFraction = stepper->getCurrentPositionFractionFromExternalPos( stepperPos ); + + // clamp the stepper position to prevent problems with the spline + float x_0 = constrain(stepperPosFraction, 0, 1); + + // get force and force gradient of force vs travel curve + float loadCellTargetKg = forceCurve->EvalForceCubicSpline(config_st, calc_st, x_0); + float gradient_force_curve_fl32 = forceCurve->EvalForceGradientCubicSpline(config_st, calc_st, x_0, false); + + // apply effect force offset + loadCellTargetKg -=absForceOffset_fl32; + + // how many mm movement to order if 1kg of error force is detected + // this can be tuned for responsiveness vs oscillation + float mm_per_motor_rev = config_st->payLoadPedalConfig_.spindlePitch_mmPerRev_u8;//TRAVEL_PER_ROTATION_IN_MM; + float steps_per_motor_rev = STEPS_PER_MOTOR_REVOLUTION; + + // foot spring stiffness + float d_f_d_phi = -config_st->payLoadPedalConfig_.MPC_0th_order_gain; + + float move_mm_per_kg = config_st->payLoadPedalConfig_.MPC_0th_order_gain; + float MOVE_STEPS_FOR_1KG = (move_mm_per_kg / mm_per_motor_rev) * steps_per_motor_rev; + + + + // make stiffness dependent on force curve gradient + // less steps per kg --> steeper line + float gradient_normalized_force_curve_fl32 = forceCurve->EvalForceGradientCubicSpline(config_st, calc_st, x_0, true); + gradient_normalized_force_curve_fl32 = constrain(gradient_normalized_force_curve_fl32, 0.05, 1); + + float mmPerStep = 0; + if (steps_per_motor_rev > 0) + { + mmPerStep = mm_per_motor_rev / steps_per_motor_rev ; + } + + + + // The foot is modeled to be of proportional resistance with respect to deflection. Since the deflection depends on the pedal kinematics, the kinematic must be respected here + // This is accomplished with the forceGain variable + /*float forceGain_abs = fabs( d_phi_d_x ); + if (forceGain_abs > 0) + { + MOVE_STEPS_FOR_1KG *= fabs( forceGain ); + }*/ + + + + // angular foot model + // m1 = d_f_d_x dForce / dx + //float m1 = d_f_d_phi * (-d_phi_d_x); + + + // Translational foot model + // given in kg/step + float m1 = d_f_d_phi * (-d_x_hor_d_phi) * (-d_phi_d_x) * mmPerStep; + + // smoothen gradient with force curve gradient since it had better results w/ clutch pedal characteristic + //m1 /= gradient_normalized_force_curve_fl32; + + // gradient of the force curve + // given in kg/step + float m2 = gradient_force_curve_fl32; + + // Newton update + float denom = m1 - m2; + if ( fabs(denom) > 0 ) + { + float stepUpdate = ( loadCellReadingKg_corrected - loadCellTargetKg) / ( denom ); + + // smoothen update with force curve gradient since it had better results w/ clutch pedal characteristic + stepUpdate *= gradient_normalized_force_curve_fl32; + + stepperPos -= stepUpdate; + } + } + + // limit the position update + float deltaMax = 0.5 * (float)(calc_st->stepperPosMax - calc_st->stepperPosMin); + int32_t posStepperNew = constrain(stepperPos, stepperPos_initial-deltaMax, stepperPos_initial+deltaMax ); + + // clamp target position to range + posStepperNew = constrain(posStepperNew,calc_st->stepperPosMin,calc_st->stepperPosMax ); + + return posStepperNew; +} + + +int32_t mpcBasedMove(float loadCellReadingKg, float stepperPosFraction, StepperWithLimits* stepper, ForceCurve_Interpolated* forceCurve, const DAP_calculationVariables_st* calc_st, DAP_config_st* config_st, float absForceOffset_fl32) +{ + + + + static const float MOVE_MM_FOR_1KG = 3.0; + static const float MOVE_STEPS_FOR_1KG = (MOVE_MM_FOR_1KG / TRAVEL_PER_ROTATION_IN_MM) * STEPS_PER_MOTOR_REVOLUTION; + + + + // get target force at current location + float loadCellTargetKg = forceCurve->EvalForceCubicSpline(config_st, calc_st, stepperPosFraction); + loadCellTargetKg -=absForceOffset_fl32; + + + + // get loadcell reading + float loadCellReadingKg_clip = constrain(loadCellReadingKg, calc_st->Force_Min, calc_st->Force_Max); + + + + // if target force at location is lower than loadcell reading --> move towards the foot k_f * n_steps + + // Take into account system constraints like stepper rpm & acceleration + + + + // if target force at location is lower than loadcell reading --> move away from the foot -k_f * n_steps + + + + // predict target force at new location and compare to predicted force --> compute cost matrix + + + + + + + + + // e_k = r^2 = (F_lc - k * (delta_x_0) - F_t(x_0 + delta_x_0))^2 + + // r: force residue + + // e: cost + + // F_lc: current loadcell measurement + + // k: sping stiffness of the foot + + // x_0: current stepper position + + // x_1: next stepper pos + + // delta_x_0 = x_1 - x_0: step update at time step 0 + + // F_t(x): target force at location + + + + + + // minimize e with x_1 + + // d[e(delta_x_0)] / d[delta_x_0] == 0 + + + + // d[e] / d[delta_x_0] = d[e] / d[r] * d[r] / d[delta_x_0] + + // d[e] / d[r] = 2 * r + + + + // d[r] / d[delta_x_0] = d[F_lc - k * (delta_x_0) - F_t(x_0 + delta_x_0)] = -k - d[F_t]/d[delta_x_0] + + + + + + // MPC: sum up over planing horizon and optimize costs + + // take only the first control value & repeat in the next cycle + + // constraint |delta_x_0| < max step rate + + + + // l = sum_k( e_k(delta_x_k, x_0) ) + + // where k = [0, 1, ..., N] + + +} + + + +// see https://pidtuner.com +void measureStepResponse(StepperWithLimits* stepper, const DAP_calculationVariables_st* calc_st, const DAP_config_st* config_st, const LoadCell_ADS1256* loadcell) +{ + + int32_t currentPos = stepper->getCurrentPositionFromMin(); + int32_t minPos = currentPos - dap_calculationVariables_st.stepperPosRange * 0.05; + int32_t maxPos = currentPos + dap_calculationVariables_st.stepperPosRange * 0.05; + + stepper->moveTo(minPos, true); + + Serial.println("======================================"); + Serial.println("Start system identification data:"); + + unsigned long initialTime = micros(); + unsigned long t = micros(); + bool targetPosHasBeenSet_b = false; + float loadcellReading; + + int32_t targetPos; + + for (uint32_t cycleIdx = 0; cycleIdx < 5; cycleIdx++) + { + // toogle target position + if (cycleIdx % 2 == 0) + { + targetPos = maxPos; + } + else + { + targetPos = minPos; + } + + targetPos = (int32_t)constrain(targetPos, dap_calculationVariables_st.stepperPosMin, dap_calculationVariables_st.stepperPosMax); + + // execute move to target position and meaure system response + float currentPos; + for (uint32_t sampleIdx_u32 = 0; sampleIdx_u32 < 2000; sampleIdx_u32++) + { + // get loadcell reading + loadcellReading = loadcell->getReadingKg(); + + // update time + t = micros() - initialTime; + + // after some time, set target position + if (sampleIdx_u32 == 50) + { + stepper->moveTo(targetPos, false); + } + + // get current position + currentPos = stepper->getCurrentPositionFraction(); + loadcellReading = (loadcellReading - calc_st->Force_Min) / calc_st->Force_Range; + + static RTDebugOutput rtDebugFilter; + rtDebugFilter.offerData({ ((float)t) *1e-6 , currentPos, loadcellReading}); + } + } + + Serial.println("======================================"); + Serial.println("End system identification data"); +} diff --git a/ESP32/include/StepperWithLimits.h b/ESP32/include/StepperWithLimits.h new file mode 100644 index 00000000..c3f1a2dc --- /dev/null +++ b/ESP32/include/StepperWithLimits.h @@ -0,0 +1,48 @@ +#include +#include "isv57communication.h" +#include "DiyActivePedal_types.h" + +// these are physical properties of the stepper +static const uint32_t MAXIMUM_STEPPER_RPM = 4000; +static const uint32_t STEPS_PER_MOTOR_REVOLUTION = 1600; +static const uint32_t MAXIMUM_STEPPER_SPEED = (MAXIMUM_STEPPER_RPM * STEPS_PER_MOTOR_REVOLUTION) / 60; // steps/s +static const int32_t MAXIMUM_STEPPER_ACCELERATION = 1e10; // steps/s² + + +class StepperWithLimits { +private: + FastAccelStepper* _stepper; + uint8_t _pinMin, _pinMax; // pins that limit switches attach to + int32_t _limitMin, _limitMax; // stepper position at limit switches + int32_t _posMin, _posMax; // stepper position at min/max of travel + +public: + StepperWithLimits(uint8_t pinStep, uint8_t pinDirection, uint8_t pinMin, uint8_t pinMax, bool invertMotorDir_b); + bool hasValidStepper() const { return NULL != _stepper; } + +public: + void findMinMaxEndstops(); + void refindMinLimit(); + void checkLimitsAndResetIfNecessary(); + void updatePedalMinMaxPos(uint8_t pedalStartPosPct, uint8_t pedalEndPosPct); + bool isAtMinPos(); + bool correctPos(int32_t posOffset); + void findMinMaxSensorless(isv57communication * isv57, DAP_config_st dap_config_st); + void refindMinLimitSensorless(isv57communication * isv57); +public: + int8_t moveTo(int32_t position, bool blocking = false); + void printStates(); + +public: + int32_t getCurrentPositionFromMin() const; + int32_t getCurrentPosition() const; + double getCurrentPositionFraction() const; + double getCurrentPositionFractionFromExternalPos(int32_t extPos_i32) const; + int32_t getTargetPositionSteps() const; + +public: + int32_t getLimitMin() const { return _limitMin; } + int32_t getLimitMax() const { return _limitMax; } + int32_t getTravelSteps() const { return _posMax - _posMin; } + void setSpeed(uint32_t speedInStepsPerSecond); +}; diff --git a/ESP32/include/isv57communication.h b/ESP32/include/isv57communication.h new file mode 100644 index 00000000..e7c48519 --- /dev/null +++ b/ESP32/include/isv57communication.h @@ -0,0 +1,79 @@ +#ifndef ISV57_COMMUNICATION_H +#define ISV57_COMMUNICATION_H + +//#include +#include "Modbus.h" + + +// define modbus stuff +#define MODE 5 + + + + + +// servo states register addresses +#define reg_add_position_given_p 0x0001 // checked +#define reg_add_position_feedback_p 0x0002 // checked +#define reg_add_position_error_p 0x0003 // checked +#define reg_add_command_position_given_p 0x0004 // checked +#define reg_add_position_relative_error_p 0x0005 // checked +#define reg_add_velocity_given_rpm 0x0040 // checked +#define reg_add_velocity_feedback_rpm 0x0041 // checked +#define reg_add_velocity_error_rpm 0x0042 // checked +#define reg_add_velocity_feedback_no_filt_rpm 0x0048 // checked +#define reg_add_position_command_velocity_rpm 0x0049 // checked +#define reg_add_velocity_current_given_percent 0x0080 // checked +#define reg_add_velocity_current_feedback_percent 0x0081 // checked +#define reg_add_voltage_0p1V 0x0140 // checked + +#define ref_cyclic_read_0 0x01F3 +#define ref_cyclic_read_1 0x01F4 +#define ref_cyclic_read_2 0x01F5 +#define ref_cyclic_read_3 0x01F6 + +// servo parameter addresses +#define pr_0_00 0x0000 // reserved parameter +#define pr_1_00 0x0000 + 25 // 1st position gain +#define pr_2_00 pr_1_00 + 40 // adaptive filter mode setup +#define pr_3_00 pr_2_00 + 30 // velocity control +#define pr_4_00 pr_3_00 + 30 // velocity torque control +#define pr_5_00 pr_4_00 + 50 // extension settings +#define pr_6_00 pr_5_00 + 40 // special settings + + + +class isv57communication { + + public: + isv57communication(); + void setupServoStateReading(); + void sendTunedServoParameters(); + void readAllServoParameters(); + void readServoStates(); + bool checkCommunication(); + bool findServosSlaveId(); + + + + void setZeroPos(); + int16_t getZeroPos(); + int16_t regArray[4]; + + int16_t slaveId = 63; + + int16_t servo_pos_given_p = 0; + int16_t servo_pos_error_p = 0; + int16_t servo_current_percent = 0; + int16_t servo_voltage_0p1V = 0; + + private: + // declare variables + uint8_t raw[200]; + uint8_t len; + int16_t zeroPos; + //Modbus modbus; + +}; + +#endif diff --git a/ESP32/lib/README b/ESP32/lib/README new file mode 100644 index 00000000..2593a33f --- /dev/null +++ b/ESP32/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/ESP32/platformio.ini b/ESP32/platformio.ini new file mode 100644 index 00000000..7597fc72 --- /dev/null +++ b/ESP32/platformio.ini @@ -0,0 +1,92 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +default_envs = esp32s3usbotg +#default_envs = esp32 +#default_envs = esp32-speedcrafter + +[env] +framework = arduino +#platform = espressif32 @ 6.5.0 +#platform = espressif32 @ 6.6.0 +platform = espressif32 +#platform = https://github.com/platformio/platform-espressif32.git +# see https://github.com/platformio/platform-espressif32/issues/1225 +platform_packages = + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.17 + platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 +# platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.0-alpha3 +# platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 + +monitor_speed = 921600 + +[common] +lib_deps_external = + rfetick/Kalman @ ^1.1.0 + lemmingdev/ESP32-BLE-Gamepad @ ^0.5.5 + gin66/FastAccelStepper @ ^0.30.12 + dlloydev/QuickPID @ ^3.1.9 + tomstewart89/BasicLinearAlgebra @ ^3.2 + #schnoog/Joystick_ESP32S2 @ ^0.9.4 + https://github.com/ChrGri/ADS1255-ADS1256.git + +[env:esp32s3usbotg] +board = esp32-s3-devkitc-1 +board_build.f_cpu = 240000000L +# https://community.platformio.org/t/platformio-is-it-possible-to-flash-esp32-s3-when-its-in-tinyusb-mode/34015/4 +#build_unflags = +# '-D PCB_VERSION' +build_flags = + -DARDUINO_RUNNING_CORE=1 + #-DARDUINO_EVENT_RUNNING_CORE=1 + -DCORE_DEBUG_LEVEL=1 + -DARDUINO_USB_MODE=0 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MSC_ON_BOOT=0 + -DARDUINO_USB_DFU_ON_BOOT=0 + -DPCB_VERSION=6 + -DUSB_VID=0xF011 + -DUSB_PID=0xF011 + '-DUSB_PRODUCT="DiyFfbPedal"' + '-DUSB_MANUFACTURER="OpenSource"' + +# https://community.platformio.org/t/platformio-is-it-possible-to-flash-esp32-s3-when-its-in-tinyusb-mode/34015/7 +board_upload.use_1200bps_touch = yes +board_upload.wait_for_upload_port = yes +board_upload.require_upload_port = yes + +lib_deps = + ${common.lib_deps_external} + schnoog/Joystick_ESP32S2 @ ^0.9.4 + adafruit/Adafruit MCP4725 @ ^2.0.2 + + + + +[env:esp32] +board = esp32dev +lib_deps = + ${common.lib_deps_external} +build_unflags = + '-D PCB_VERSION=6' +build_flags = + '-D PCB_VERSION=3' + +[env:esp32-speedcrafter] +board = esp-wrover-kit +lib_deps = + ${common.lib_deps_external} +build_unflags = + '-D PCB_VERSION=6' +build_flags = + '-D PCB_VERSION=5' + + diff --git a/ESP32/src/.DS_Store b/ESP32/src/.DS_Store new file mode 100644 index 00000000..c6179262 Binary files /dev/null and b/ESP32/src/.DS_Store differ diff --git a/ESP32/src/.last-used b/ESP32/src/.last-used new file mode 100644 index 00000000..e69de29b diff --git a/ESP32/src/.theia/launch.json b/ESP32/src/.theia/launch.json new file mode 100644 index 00000000..a2ea02c4 --- /dev/null +++ b/ESP32/src/.theia/launch.json @@ -0,0 +1,6 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + "version": "0.2.0", + "configurations": [] +} diff --git a/ESP32/src/Controller.cpp b/ESP32/src/Controller.cpp new file mode 100644 index 00000000..39cf6ad2 --- /dev/null +++ b/ESP32/src/Controller.cpp @@ -0,0 +1,108 @@ +#include +//#include +#include "Controller.h" + +static const int16_t JOYSTICK_MIN_VALUE = 0; +static const int16_t JOYSTICK_MAX_VALUE = 10000; +static const int16_t JOYSTICK_RANGE = JOYSTICK_MAX_VALUE - JOYSTICK_MIN_VALUE; + +#ifdef USB_JOYSTICK + #include + + Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, + 0, 0, // Button Count, Hat Switch Count + false, false, false, // X and Y, but no Z Axis + false, false, false, // No Rx, Ry, or Rz + false, false, // No rudder or throttle + false, true, false); // No accelerator, brake, or steering + + void SetupController() { + Joystick.setBrakeRange(JOYSTICK_MIN_VALUE, JOYSTICK_MAX_VALUE); + delay(100); + + Joystick.begin(); + + // rename HID device name, see e.g. https://github.com/schnoog/Joystick_ESP32S2/issues/8 + //USB.PID(0x8211); + //USB.VID(0x303b); + //USB.productName("DIY FFB pedal"); + //USB.manufacturerName("Open source"); + //USB.begin(); + + } + bool IsControllerReady() { return true; } + void SetControllerOutputValue(int32_t value) { + Joystick.setBrake(value); + } + + + + +#elif defined BLUETOOTH_GAMEPAD + #include + + + + // get the max address + // see https://arduino.stackexchange.com/questions/58677/get-esp32-chip-id-into-a-string-variable-arduino-c-newbie-here + char ssid[23]; + uint64_t chipid = ESP.getEfuseMac(); // The chip ID is essentially its MAC address(length: 6 bytes). + unsigned int chip = (unsigned int)(chipid >> 32); + std::string bluetoothName_lcl = "DiyFfbPedal_" + std::to_string( chip ); + BleGamepad bleGamepad(bluetoothName_lcl, bluetoothName_lcl, 100); + + + void SetupController() { + BleGamepadConfiguration bleGamepadConfig; + bleGamepadConfig.setControllerType(CONTROLLER_TYPE_MULTI_AXIS); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS + bleGamepadConfig.setAxesMin(JOYSTICK_MIN_VALUE); // 0 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal + bleGamepadConfig.setAxesMax(JOYSTICK_MAX_VALUE); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal + //bleGamepadConfig.setWhichSpecialButtons(false, false, false, false, false, false, false, false); + bleGamepadConfig.setWhichAxes(true, true, true, true, true, true, true, true); + //bleGamepadConfig.setWhichSimulationControls(true, true, true, true, true); // only brake active + bleGamepadConfig.setButtonCount(0); + bleGamepadConfig.setHatSwitchCount(0); + bleGamepadConfig.setAutoReport(false); + bleGamepadConfig.setPid(chip); // product id + + bleGamepad.begin(&bleGamepadConfig); + + //bleGamepad.deviceManufacturer = "DiyFfbPedal"; + //bleGamepad.deviceName = chip; + } + + bool IsControllerReady() { return bleGamepad.isConnected(); } + + void SetControllerOutputValue(int32_t value) { + //bleGamepad.setBrake(value); + + if (bleGamepad.isConnected() ) + { + //bleGamepad.setAxes(value, 0, 0, 0, 0, 0, 0, 0); + bleGamepad.setX(value); + //bleGamepad.setSimulationControls(value, 0, 0, 0, 0); + //bleGamepad.setSliders(value,0); + bleGamepad.sendReport(); + } + else + { + Serial.println("BLE not connected!"); + delay(500); + } + + + } + +#endif + + +int32_t NormalizeControllerOutputValue(float value, float minVal, float maxVal, float maxGameOutput) { + float valRange = (maxVal - minVal); + if (abs(valRange) < 0.01) { + return JOYSTICK_MIN_VALUE; // avoid div-by-zero + } + + float fractional = (value - minVal) / valRange; + int32_t controller = JOYSTICK_MIN_VALUE + (fractional * JOYSTICK_RANGE); + return constrain(controller, JOYSTICK_MIN_VALUE, (maxGameOutput/100.) * JOYSTICK_MAX_VALUE); +} diff --git a/ESP32/src/DiyActivePedal_types.cpp b/ESP32/src/DiyActivePedal_types.cpp new file mode 100644 index 00000000..58fa00c6 --- /dev/null +++ b/ESP32/src/DiyActivePedal_types.cpp @@ -0,0 +1,227 @@ +#include "DiyActivePedal_types.h" +#include "Arduino.h" + +#include "PedalGeometry.h" +#include "StepperWithLimits.h" + +#include + +static const float ABS_SCALING = 50; + +const uint32_t EEPROM_OFFSET = (DAP_VERSION_CONFIG-128) * sizeof(DAP_config_st) % (2048-sizeof(DAP_config_st)); + +void DAP_config_st::initialiseDefaults() { + payLoadHeader_.payloadType = DAP_PAYLOAD_TYPE_CONFIG; + payLoadHeader_.version = DAP_VERSION_CONFIG; + payLoadHeader_.storeToEeprom = false; + + payLoadPedalConfig_.pedalStartPosition = 10; + payLoadPedalConfig_.pedalEndPosition = 85; + + payLoadPedalConfig_.maxForce = 60; + payLoadPedalConfig_.preloadForce = 2; + + payLoadPedalConfig_.relativeForce_p000 = 0; + payLoadPedalConfig_.relativeForce_p020 = 20; + payLoadPedalConfig_.relativeForce_p040 = 40; + payLoadPedalConfig_.relativeForce_p060 = 60; + payLoadPedalConfig_.relativeForce_p080 = 80; + payLoadPedalConfig_.relativeForce_p100 = 100; + + payLoadPedalConfig_.dampingPress = 0; + payLoadPedalConfig_.dampingPull = 0; + + payLoadPedalConfig_.absFrequency = 15; + payLoadPedalConfig_.absAmplitude = 0; + payLoadPedalConfig_.absPattern = 0; + payLoadPedalConfig_.absForceOrTarvelBit = 0; + + payLoadPedalConfig_.lengthPedal_a = 205; + payLoadPedalConfig_.lengthPedal_b = 220; + payLoadPedalConfig_.lengthPedal_d = 60; + payLoadPedalConfig_.lengthPedal_c_horizontal = 215; + payLoadPedalConfig_.lengthPedal_c_vertical = 60; + payLoadPedalConfig_.lengthPedal_travel = 100; + + + payLoadPedalConfig_.Simulate_ABS_trigger = 0;// add for abs trigger + payLoadPedalConfig_.Simulate_ABS_value = 80;// add for abs trigger + payLoadPedalConfig_.RPM_max_freq = 40; + payLoadPedalConfig_.RPM_min_freq = 10; + payLoadPedalConfig_.RPM_AMP = 5; + payLoadPedalConfig_.BP_trigger_value =50; + payLoadPedalConfig_.BP_amp=1; + payLoadPedalConfig_.BP_freq=15; + payLoadPedalConfig_.BP_trigger=0; + payLoadPedalConfig_.G_multi = 50; + payLoadPedalConfig_.G_window=60; + payLoadPedalConfig_.WS_amp=1; + payLoadPedalConfig_.WS_freq=15; + payLoadPedalConfig_.Road_multi = 50; + payLoadPedalConfig_.Road_window=60; + payLoadPedalConfig_.cubic_spline_param_a_array[0] = 0; + payLoadPedalConfig_.cubic_spline_param_a_array[1] = 0; + payLoadPedalConfig_.cubic_spline_param_a_array[2] = 0; + payLoadPedalConfig_.cubic_spline_param_a_array[3] = 0; + payLoadPedalConfig_.cubic_spline_param_a_array[4] = 0; + + payLoadPedalConfig_.cubic_spline_param_b_array[0] = 0; + payLoadPedalConfig_.cubic_spline_param_b_array[1] = 0; + payLoadPedalConfig_.cubic_spline_param_b_array[2] = 0; + payLoadPedalConfig_.cubic_spline_param_b_array[3] = 0; + payLoadPedalConfig_.cubic_spline_param_b_array[4] = 0; + + payLoadPedalConfig_.PID_p_gain = 0.3; + payLoadPedalConfig_.PID_i_gain = 50.0; + payLoadPedalConfig_.PID_d_gain = 0.0; + payLoadPedalConfig_.PID_velocity_feedforward_gain = 0.0; + + + payLoadPedalConfig_.MPC_0th_order_gain = 1.0; + payLoadPedalConfig_.MPC_1st_order_gain = 0.0; + payLoadPedalConfig_.MPC_2nd_order_gain = 0.0; + + payLoadPedalConfig_.control_strategy_b = 0; + + payLoadPedalConfig_.maxGameOutput = 100; + + payLoadPedalConfig_.kf_modelNoise = 128; + payLoadPedalConfig_.kf_modelOrder = 1; + + payLoadPedalConfig_.debug_flags_0 = 0; + + payLoadPedalConfig_.loadcell_rating = 150; + + payLoadPedalConfig_.travelAsJoystickOutput_u8 = 0; + + payLoadPedalConfig_.invertLoadcellReading_u8 = 0; + + payLoadPedalConfig_.invertMotorDirection_u8 = 0; + payLoadPedalConfig_.pedal_type=0; + payLoadPedalConfig_.OTA_flag=0; + payLoadPedalConfig_.enableReboot_u8=1; +} + + + + +void DAP_config_st::storeConfigToEprom(DAP_config_st& config_st) +{ + + EEPROM.put(EEPROM_OFFSET, config_st); + EEPROM.commit(); + Serial.println("Successfully stored config in EPROM"); + + /*if (true == config_st.payLoadHeader_.storeToEeprom) + { + config_st.payLoadHeader_.storeToEeprom = false; // set to false, thus at restart existing EEPROM config isn't restored to EEPROM + EEPROM.put(0, config_st); + EEPROM.commit(); + Serial.println("Successfully stored config in EPROM"); + }*/ +} + +void DAP_config_st::loadConfigFromEprom(DAP_config_st& config_st) +{ + DAP_config_st local_config_st; + + EEPROM.get(EEPROM_OFFSET, local_config_st); + //EEPROM.commit(); + + config_st = local_config_st; + + // check if version matches revision, in case, update the default config + /*if (local_config_st.payLoadHeader_.version == DAP_VERSION_CONFIG) + { + config_st = local_config_st; + Serial.println("Successfully loaded config from EPROM"); + } + else + { + Serial.println("Couldn't load config from EPROM due to version mismatch"); + Serial.print("Target version: "); + Serial.println(DAP_VERSION_CONFIG); + Serial.print("Source version: "); + Serial.println(local_config_st.payLoadHeader_.version); + + }*/ + +} + + + + + +void DAP_calculationVariables_st::updateFromConfig(DAP_config_st& config_st) { + startPosRel = ((float)config_st.payLoadPedalConfig_.pedalStartPosition) / 100.0f; + endPosRel = ((float)config_st.payLoadPedalConfig_.pedalEndPosition) / 100.0f; + + + if (startPosRel == endPosRel) + { + endPosRel = startPosRel + 1 / 100; + } + + absFrequency = ((float)config_st.payLoadPedalConfig_.absFrequency); + absAmplitude = ((float)config_st.payLoadPedalConfig_.absAmplitude) / 20.0f; // in kg or percent + + dampingPress = ((float)config_st.payLoadPedalConfig_.dampingPress) / 400.0f; + RPM_max_freq = ((float)config_st.payLoadPedalConfig_.RPM_max_freq); + RPM_min_freq = ((float)config_st.payLoadPedalConfig_.RPM_min_freq); + RPM_AMP = ((float)config_st.payLoadPedalConfig_.RPM_AMP) / 100.0f; + //Bite point effect; + + BP_trigger_value=(float)config_st.payLoadPedalConfig_.BP_trigger_value; + BP_amp=((float)config_st.payLoadPedalConfig_.BP_amp) / 100.0f; + BP_freq=(float)config_st.payLoadPedalConfig_.BP_freq; + WS_amp=((float)config_st.payLoadPedalConfig_.WS_amp) / 20.0f; + WS_freq=(float)config_st.payLoadPedalConfig_.WS_freq; + // update force variables + Force_Min = ((float)config_st.payLoadPedalConfig_.preloadForce); + Force_Max = ((float)config_st.payLoadPedalConfig_.maxForce); + Force_Range = Force_Max - Force_Min; + Force_Max_default=((float)config_st.payLoadPedalConfig_.maxForce); +} + +void DAP_calculationVariables_st::dynamic_update() +{ + Force_Range = Force_Max - Force_Min; +} + +void DAP_calculationVariables_st::reset_maxforce() +{ + Force_Max = Force_Max_default; +} + +void DAP_calculationVariables_st::updateEndstops(long newMinEndstop, long newMaxEndstop) { + + if ( newMinEndstop == newMaxEndstop ) + { + newMaxEndstop = newMinEndstop + 10; + } + + stepperPosMinEndstop = newMinEndstop; + stepperPosMaxEndstop = newMaxEndstop; + stepperPosEndstopRange = stepperPosMaxEndstop - stepperPosMinEndstop; + + stepperPosMin = stepperPosEndstopRange * startPosRel; + stepperPosMax = stepperPosEndstopRange * endPosRel; + + stepperPosRange = stepperPosMax - stepperPosMin; +} + +void DAP_calculationVariables_st::updateStiffness() { + springStiffnesss = Force_Range / stepperPosRange; + if ( fabs(springStiffnesss) > 0.0001 ) + { + springStiffnesssInv = 1.0 / springStiffnesss; + } + else + { + springStiffnesssInv = 1000000; + } + + } + + + diff --git a/ESP32/src/ForceCurve.cpp b/ESP32/src/ForceCurve.cpp new file mode 100644 index 00000000..bca92f08 --- /dev/null +++ b/ESP32/src/ForceCurve.cpp @@ -0,0 +1,108 @@ +#include "ForceCurve.h" +//#include "InterpolationLib.h" +#include "Arduino.h" + + + +/**********************************************************************************************/ +/* */ +/* Spline interpolation: force computation */ +/* */ +/**********************************************************************************************/ + +// see https://swharden.com/blog/2022-01-22-spline-interpolation/ +float ForceCurve_Interpolated::EvalForceCubicSpline(const DAP_config_st* config_st, const DAP_calculationVariables_st* calc_st, float fractionalPos) +{ + + float fractionalPos_lcl = constrain(fractionalPos, 0, 1); + + float splineSegment_fl32 = fractionalPos_lcl * 5; + uint8_t splineSegment_u8 = (uint8_t)floor(splineSegment_fl32); + + if (splineSegment_u8 < 0){splineSegment_u8 = 0;} + if (splineSegment_u8 > (NUMBER_OF_SPLINE_SEGMENTS-1) ){splineSegment_u8 = NUMBER_OF_SPLINE_SEGMENTS-1;} + float a = config_st->payLoadPedalConfig_.cubic_spline_param_a_array[splineSegment_u8]; + float b = config_st->payLoadPedalConfig_.cubic_spline_param_b_array[splineSegment_u8]; + + float yOrig[ NUMBER_OF_SPLINE_SEGMENTS + 1 ]; + yOrig[0] = config_st->payLoadPedalConfig_.relativeForce_p000; + yOrig[1] = config_st->payLoadPedalConfig_.relativeForce_p020; + yOrig[2] = config_st->payLoadPedalConfig_.relativeForce_p040; + yOrig[3] = config_st->payLoadPedalConfig_.relativeForce_p060; + yOrig[4] = config_st->payLoadPedalConfig_.relativeForce_p080; + yOrig[5] = config_st->payLoadPedalConfig_.relativeForce_p100; + + //double dx = 1.0f; + double t = (splineSegment_fl32 - (float)splineSegment_u8);// / dx; + double y = (1 - t) * yOrig[splineSegment_u8] + t * yOrig[splineSegment_u8 + 1] + t * (1 - t) * (a * (1 - t) + b * t); + + if (calc_st->Force_Range> 0) + { + y = calc_st->Force_Min + y / 100.0f * calc_st->Force_Range; + } + else + { + y = calc_st->Force_Min; + } + + + return y; +} + + +/**********************************************************************************************/ +/* */ +/* Spline interpolation: gradient computation */ +/* */ +/**********************************************************************************************/ + +float ForceCurve_Interpolated::EvalForceGradientCubicSpline(const DAP_config_st* config_st, const DAP_calculationVariables_st* calc_st, float fractionalPos, bool normalized_b) +{ + + float fractionalPos_lcl = constrain(fractionalPos, 0, 1); + + float splineSegment_fl32 = fractionalPos_lcl * 5; + uint8_t splineSegment_u8 = (uint8_t)floor(splineSegment_fl32); + + if (splineSegment_u8 < 0){splineSegment_u8 = 0;} + if (splineSegment_u8 > 4){splineSegment_u8 = 4;} + float a = config_st->payLoadPedalConfig_.cubic_spline_param_a_array[splineSegment_u8]; + float b = config_st->payLoadPedalConfig_.cubic_spline_param_b_array[splineSegment_u8]; + + float yOrig[NUMBER_OF_SPLINE_SEGMENTS + 1]; + yOrig[0] = config_st->payLoadPedalConfig_.relativeForce_p000; + yOrig[1] = config_st->payLoadPedalConfig_.relativeForce_p020; + yOrig[2] = config_st->payLoadPedalConfig_.relativeForce_p040; + yOrig[3] = config_st->payLoadPedalConfig_.relativeForce_p060; + yOrig[4] = config_st->payLoadPedalConfig_.relativeForce_p080; + yOrig[5] = config_st->payLoadPedalConfig_.relativeForce_p100; + + + + double Delta_x_orig = 100; // total horizontal range [0,100] + double dx = Delta_x_orig / NUMBER_OF_SPLINE_SEGMENTS; // spline segment horizontal range + double t = (splineSegment_fl32 - (float)splineSegment_u8); // relative position in spline segment [0, 1] + double dy = yOrig[splineSegment_u8 + 1] - yOrig[splineSegment_u8]; // spline segment vertical range + double y_prime = 0; + if (fabs(dx) > 0) + { + y_prime = dy / dx + (1 - 2 * t) * (a * (1 - t) + b * t) / dx + t * (1 - t) * (b - a) / dx; + } + // when the spline was identified, x and y were givin in the unit of percent --> 0-100 + // --> conversion of the gradient to the proper axis scaling is performed + if (normalized_b == false) + { + double d_y_scale = calc_st->Force_Range / 100.0; + double d_x_scale=0; + if (fabs(calc_st->stepperPosRange) > 0.01) + { + d_x_scale = 100.0 / calc_st->stepperPosRange; + } + + y_prime *= d_x_scale * d_y_scale; + } + + return y_prime; +} + + diff --git a/ESP32/src/LoadCell.cpp b/ESP32/src/LoadCell.cpp new file mode 100644 index 00000000..a2540368 --- /dev/null +++ b/ESP32/src/LoadCell.cpp @@ -0,0 +1,140 @@ +#include "LoadCell.h" + +#include +#include +#include "Main.h" + +static const float ADC_CLOCK_MHZ = 7.68; // crystal frequency used on ADS1256 +static const float ADC_VREF = 2.5; // voltage reference + +static const int NUMBER_OF_SAMPLES_FOR_LOADCELL_OFFFSET_ESTIMATION = 1000; +static const float DEFAULT_VARIANCE_ESTIMATE = 0.2f * 0.2f; +static const float LOADCELL_VARIANCE_MIN = 0.001f; +//static const float CONVERSION_FACTOR = LOADCELL_WEIGHT_RATING_KG / (LOADCELL_EXCITATION_V * (LOADCELL_SENSITIVITY_MV_V/1000)); + +#define CONVERSION_FACTOR LOADCELL_WEIGHT_RATING_KG / (LOADCELL_EXCITATION_V * (LOADCELL_SENSITIVITY_MV_V/1000)) + + +ADS1256& ADC() { + static ADS1256 adc(ADC_CLOCK_MHZ, ADC_VREF, /*useresetpin=*/false + , PIN_DRDY, PIN_SCK, PIN_MISO, PIN_MOSI, PIN_CS); // RESETPIN is permanently tied to 3.3v + + + static bool firstTime = true; + if (firstTime) { + Serial.println("Starting ADC"); + adc.initSpi(ADC_CLOCK_MHZ); + delay(1000); + + Serial.println("ADS: send SDATAC command"); + //adc.sendCommand(ADS1256_CMD_SDATAC); + + // start the ADS1256 with data rate of 15kSPS SPS and gain x64 + //adc.begin(ADS1256_DRATE_15000SPS,ADS1256_GAIN_64,false); + //adc.begin(ADS1256_DRATE_1000SPS,ADS1256_GAIN_64,false); + adc.begin(ADC_SAMPLE_RATE, ADS1256_GAIN_64, false); + + + Serial.println("ADC Started"); + + adc.waitDRDY(); // wait for DRDY to go low before changing multiplexer register + if ( fabs(CONVERSION_FACTOR) > 0.01) + { + adc.setConversionFactor(CONVERSION_FACTOR); + } + else + { + adc.setConversionFactor(1); + } + firstTime = false; + } + + return adc; +} + + +void LoadCell_ADS1256::setLoadcellRating(uint8_t loadcellRating_u8) const { + ADS1256& adc = ADC(); + double originalConversionFactor_f64 = CONVERSION_FACTOR; + + double updatedConversionFactor_f64 = 1; + if (LOADCELL_WEIGHT_RATING_KG>0) + { + updatedConversionFactor_f64 = 2 * ((double)loadcellRating_u8) * (CONVERSION_FACTOR/LOADCELL_WEIGHT_RATING_KG); + } + Serial.print("OrigConversionFactor: "); + Serial.print(originalConversionFactor_f64); + Serial.print(", NewConversionFactor:"); + Serial.println(updatedConversionFactor_f64); + + + adc.setConversionFactor( updatedConversionFactor_f64 ); +} + + + + +LoadCell_ADS1256::LoadCell_ADS1256(uint8_t channel0, uint8_t channel1) + : _zeroPoint(0.0), _varianceEstimate(DEFAULT_VARIANCE_ESTIMATE) +{ + ADC().setChannel(channel0,channel1); // Set the MUX for differential between ch0 and ch1 + //ADC().setChannel(channel1, channel0); // Set the MUX for differential between ch0 and ch1 +} + +float LoadCell_ADS1256::getReadingKg() const { + ADS1256& adc = ADC(); + adc.waitDRDY(); // wait for DRDY to go low before next register read + + // correct bias, assume AWGN --> 3 * sigma is 99.9 % + return adc.readCurrentChannel() - ( _zeroPoint + 3.0 * _standardDeviationEstimate ); +} + +void LoadCell_ADS1256::setZeroPoint() { + Serial.println("ADC: Identify loadcell offset"); + + // Due to construction and gravity, the loadcell measures an initial voltage difference. + // To compensate this difference, the difference is estimated by moving average filter. + float loadcellOffset = 0.0f; + for (long i = 0; i < NUMBER_OF_SAMPLES_FOR_LOADCELL_OFFFSET_ESTIMATION; i++) { + loadcellOffset += getReadingKg(); // DOUT arriving here are from MUX AIN0 and + } + loadcellOffset /= NUMBER_OF_SAMPLES_FOR_LOADCELL_OFFFSET_ESTIMATION; + + Serial.print("Offset "); + Serial.println(loadcellOffset,10); + + _zeroPoint = loadcellOffset; +} + +void LoadCell_ADS1256::estimateVariance() { + ADS1256& adc = ADC(); + + + Serial.println("ADC: Identify loadcell variance"); + float varNormalizer = 1. / (float)(NUMBER_OF_SAMPLES_FOR_LOADCELL_OFFFSET_ESTIMATION - 1); + float varEstimate = 0.0f; + for (long i = 0; i < NUMBER_OF_SAMPLES_FOR_LOADCELL_OFFFSET_ESTIMATION; i++){ + float loadcellReading = getReadingKg(); + //Serial.println(loadcellReading); + varEstimate += sq(loadcellReading) * varNormalizer; + } + + _standardDeviationEstimate = sqrt(varEstimate); + + Serial.println("Variance est.:"); + Serial.println(varEstimate); + + Serial.println("Stddev est.:"); + Serial.println(_standardDeviationEstimate); + + // make sure estimate is nonzero + if (varEstimate < LOADCELL_VARIANCE_MIN) { + varEstimate = LOADCELL_VARIANCE_MIN; + } + varEstimate *= 9; // The variance is 1*sigma --> to make it 3*sigma, we have to multiply by 3*3 + + _varianceEstimate = varEstimate; + + + +} diff --git a/ESP32/src/Main.cpp b/ESP32/src/Main.cpp new file mode 100644 index 00000000..118118ae --- /dev/null +++ b/ESP32/src/Main.cpp @@ -0,0 +1,1651 @@ + +/* Todo*/ +// https://github.com/espressif/arduino-esp32/issues/7779 + + +#define ESTIMATE_LOADCELL_VARIANCE +#define ISV_COMMUNICATION +//#define PRINT_SERVO_STATES + +#define DEBUG_INFO_0_CYCLE_TIMER 1 +#define DEBUG_INFO_0_STEPPER_POS 2 +#define DEBUG_INFO_0_LOADCELL_READING 4 +#define DEBUG_INFO_0_SERVO_READINGS 8 +#define DEBUG_INFO_0_PRINT_ALL_SERVO_REGISTERS 16 +#define DEBUG_INFO_0_STATE_BASIC_INFO_STRUCT 32 +#define DEBUG_INFO_0_STATE_EXTENDED_INFO_STRUCT 64 +#define DEBUG_INFO_0_CONTROL_LOOP_ALGO 128 + +bool resetServoEncoder = true; +bool isv57LifeSignal_b = false; +#ifdef ISV_COMMUNICATION + #include "isv57communication.h" + int32_t servo_offset_compensation_steps_i32 = 0; +#endif + +#define OTA_update + +#define PI 3.14159267 +#define DEG_TO_RAD PI / 180 + +#include "Arduino.h" +#include "Main.h" + +#ifdef Using_analog_output_ESP32_S3 +#include +#include + TwoWire MCP4725_I2C= TwoWire(1); + //MCP4725 MCP(0x60, &MCP4725_I2C); + Adafruit_MCP4725 dac; + int current_use_mcp_index; + bool MCP_status =false; +#endif + +//#define ALLOW_SYSTEM_IDENTIFICATION + +/**********************************************************************************************/ +/* */ +/* function declarations */ +/* */ +/**********************************************************************************************/ +void updatePedalCalcParameters(); +void pedalUpdateTask( void * pvParameters ); +void serialCommunicationTask( void * pvParameters ); +void servoCommunicationTask( void * pvParameters ); +void OTATask( void * pvParameters ); + +// https://www.tutorialspoint.com/cyclic-redundancy-check-crc-in-arduino +uint16_t checksumCalculator(uint8_t * data, uint16_t length) +{ + uint16_t curr_crc = 0x0000; + uint8_t sum1 = (uint8_t) curr_crc; + uint8_t sum2 = (uint8_t) (curr_crc >> 8); + int index; + for(index = 0; index < length; index = index+1) + { + sum1 = (sum1 + data[index]) % 255; + sum2 = (sum2 + sum1) % 255; + } + return (sum2 << 8) | sum1; +} + + +bool systemIdentificationMode_b = false; + +int16_t servoPos_i16 = 0; + + + +bool splineDebug_b = false; + + + +#include + + + +#include "ABSOscillation.h" +ABSOscillation absOscillation; +RPMOscillation _RPMOscillation; +BitePointOscillation _BitePointOscillation; +G_force_effect _G_force_effect; +WSOscillation _WSOscillation; +Road_impact_effect _Road_impact_effect; +#define ABS_OSCILLATION + + + +#include "DiyActivePedal_types.h" +DAP_config_st dap_config_st; +DAP_calculationVariables_st dap_calculationVariables_st; +DAP_state_basic_st dap_state_basic_st; +DAP_state_extended_st dap_state_extended_st; + + +#include "CycleTimer.h" + + + + + +#include "RTDebugOutput.h" + + +/**********************************************************************************************/ +/* */ +/* iterpolation definitions */ +/* */ +/**********************************************************************************************/ + +#include "ForceCurve.h" +ForceCurve_Interpolated forceCurve; + + + +/**********************************************************************************************/ +/* */ +/* multitasking definitions */ +/* */ +/**********************************************************************************************/ +#ifndef CONFIG_IDF_TARGET_ESP32S3 + #include "soc/rtc_wdt.h" +#endif + +//#define PRINT_USED_STACK_SIZE +// https://stackoverflow.com/questions/55998078/freertos-task-priority-and-stack-size +#define STACK_SIZE_FOR_TASK_1 0.2 * (configTOTAL_HEAP_SIZE / 4) +#define STACK_SIZE_FOR_TASK_2 0.2 * (configTOTAL_HEAP_SIZE / 4) + + +TaskHandle_t Task1; +TaskHandle_t Task2; +#ifdef ISV_COMMUNICATION + isv57communication isv57; + #define STACK_SIZE_FOR_TASK_3 0.2 * (configTOTAL_HEAP_SIZE / 4) + TaskHandle_t Task3; +#endif + +static SemaphoreHandle_t semaphore_updateConfig=NULL; + bool configUpdateAvailable = false; // semaphore protected data + DAP_config_st dap_config_st_local; + +static SemaphoreHandle_t semaphore_updateJoystick=NULL; + int32_t joystickNormalizedToInt32 = 0; // semaphore protected data + +static SemaphoreHandle_t semaphore_resetServoPos=NULL; +bool resetPedalPosition = false; + +static SemaphoreHandle_t semaphore_readServoValues=NULL; + +static SemaphoreHandle_t semaphore_updatePedalStates=NULL; + +/**********************************************************************************************/ +/* */ +/* target-specific definitions */ +/* */ +/**********************************************************************************************/ + + + + +/**********************************************************************************************/ +/* */ +/* controller definitions */ +/* */ +/**********************************************************************************************/ + +#include "Controller.h" + + + + +/**********************************************************************************************/ +/* */ +/* pedal mechanics definitions */ +/* */ +/**********************************************************************************************/ + +#include "PedalGeometry.h" + + +/**********************************************************************************************/ +/* */ +/* Kalman filter definitions */ +/* */ +/**********************************************************************************************/ + +#include "SignalFilter.h" +KalmanFilter* kalman = NULL; + + +#include "SignalFilter_2nd_order.h" +KalmanFilter_2nd_order* kalman_2nd_order = NULL; + + + + +/**********************************************************************************************/ +/* */ +/* loadcell definitions */ +/* */ +/**********************************************************************************************/ + +#include "LoadCell.h" +LoadCell_ADS1256* loadcell = NULL; + + + +/**********************************************************************************************/ +/* */ +/* stepper motor definitions */ +/* */ +/**********************************************************************************************/ + +#include "StepperWithLimits.h" +StepperWithLimits* stepper = NULL; +//static const int32_t MIN_STEPS = 5; + +#include "StepperMovementStrategy.h" +/**********************************************************************************************/ +/* */ +/* OTA */ +/* */ +/**********************************************************************************************/ +//OTA update +#ifdef OTA_update +#include "ota.h" +TaskHandle_t Task4; +#endif + + + +/**********************************************************************************************/ +/* */ +/* setup function */ +/* */ +/**********************************************************************************************/ +void setup() +{ + //Serial.begin(115200); + //Serial.begin(921600); + //Serial.begin(512000); + // + + + #if PCB_VERSION == 6 + Serial.setTxTimeoutMs(0); + #else + Serial.begin(921600); + Serial.setTimeout(5); + #endif + Serial.println(" "); + Serial.println(" "); + Serial.println(" "); + + // init controller + SetupController(); + delay(3000); + Serial.println("This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License."); + Serial.println("Please check github repo for more detail: https://github.com/ChrGri/DIY-Sim-Racing-FFB-Pedal"); + +// check whether iSV57 communication can be established +// and in case, (a) send tuned servo parameters and (b) prepare the servo for signal read +#ifdef ISV_COMMUNICATION + + bool isv57slaveIdFound_b = isv57.findServosSlaveId(); + Serial.print("iSV57 slaveId found: "); + Serial.println( isv57slaveIdFound_b ); + + if (!isv57slaveIdFound_b) + { + Serial.println( "Restarting ESP" ); + ESP.restart(); + } + + + // check whether iSV57 is connected + isv57LifeSignal_b = isv57.checkCommunication(); + if (!isv57LifeSignal_b) + { + Serial.println( "Restarting ESP" ); + ESP.restart(); + } + + + Serial.print("iSV57 communication state: "); + Serial.println(isv57LifeSignal_b); + + if (isv57LifeSignal_b) + { + isv57.setupServoStateReading(); + isv57.sendTunedServoParameters(); + } + delay(200); +#endif + + +// initialize configuration and update local variables + dap_config_st.initialiseDefaults(); + + // Load config from EEPROM, if valid, overwrite initial config + EEPROM.begin(2048); + dap_config_st.loadConfigFromEprom(dap_config_st_local); + + + // check validity of data from EEPROM + bool structChecker = true; + uint16_t crc; + if ( dap_config_st_local.payLoadHeader_.payloadType != DAP_PAYLOAD_TYPE_CONFIG ){ + structChecker = false; + /*Serial.print("Payload type expected: "); + Serial.print(DAP_PAYLOAD_TYPE_CONFIG); + Serial.print(", Payload type received: "); + Serial.println(dap_config_st_local.payLoadHeader_.payloadType);*/ + } + if ( dap_config_st_local.payLoadHeader_.version != DAP_VERSION_CONFIG ){ + structChecker = false; + /*Serial.print("Config version expected: "); + Serial.print(DAP_VERSION_CONFIG); + Serial.print(", Config version received: "); + Serial.println(dap_config_st_local.payLoadHeader_.version);*/ + } + // checksum validation + crc = checksumCalculator((uint8_t*)(&(dap_config_st_local.payLoadHeader_)), sizeof(dap_config_st_local.payLoadHeader_) + sizeof(dap_config_st_local.payLoadPedalConfig_)); + if (crc != dap_config_st_local.payloadFooter_.checkSum){ + structChecker = false; + /*Serial.print("CRC expected: "); + Serial.print(crc); + Serial.print(", CRC received: "); + Serial.println(dap_config_st_local.payloadFooter_.checkSum);*/ + } + + + + + + + // if checks are successfull, overwrite global configuration struct + if (structChecker == true) + { + Serial.println("Updating pedal config from EEPROM"); + dap_config_st = dap_config_st_local; + } + else + { + + Serial.println("Couldn't load config from EPROM due to mismatch: "); + + Serial.print("Payload type expected: "); + Serial.print(DAP_PAYLOAD_TYPE_CONFIG); + Serial.print(", Payload type received: "); + Serial.println(dap_config_st_local.payLoadHeader_.payloadType); + + + Serial.print("Target version: "); + Serial.print(DAP_VERSION_CONFIG); + Serial.print(", Source version: "); + Serial.println(dap_config_st_local.payLoadHeader_.version); + + Serial.print("CRC expected: "); + Serial.print(crc); + Serial.print(", CRC received: "); + Serial.println(dap_config_st_local.payloadFooter_.checkSum); + + } + + + // interprete config values + dap_calculationVariables_st.updateFromConfig(dap_config_st); + + + + bool invMotorDir = dap_config_st.payLoadPedalConfig_.invertMotorDirection_u8 > 0; + stepper = new StepperWithLimits(stepPinStepper, dirPinStepper, minPin, maxPin, invMotorDir); + loadcell = new LoadCell_ADS1256(); + + loadcell->setLoadcellRating(dap_config_st.payLoadPedalConfig_.loadcell_rating); + + loadcell->setZeroPoint(); + #ifdef ESTIMATE_LOADCELL_VARIANCE + loadcell->estimateVariance(); // automatically identify sensor noise for KF parameterization + #endif + + // find the min & max endstops + Serial.print("Start homing"); + if (isv57LifeSignal_b && SENSORLESS_HOMING) + { + + stepper->findMinMaxSensorless(&isv57, dap_config_st); + } + else + { + stepper->findMinMaxEndstops(); + } + + + Serial.print("Min Position is "); Serial.println(stepper->getLimitMin()); + Serial.print("Max Position is "); Serial.println(stepper->getLimitMax()); + + + // setup Kalman filter + Serial.print("Given loadcell variance: "); + Serial.println(loadcell->getVarianceEstimate()); + kalman = new KalmanFilter(loadcell->getVarianceEstimate()); + + kalman_2nd_order = new KalmanFilter_2nd_order(1); + + + + + + + + + + + + + + // activate parameter update in first cycle + configUpdateAvailable = true; + // equalize pedal config for both tasks + dap_config_st_local = dap_config_st; + + + + + + // setup multi tasking + semaphore_updateJoystick = xSemaphoreCreateMutex(); + semaphore_updateConfig = xSemaphoreCreateMutex(); + semaphore_resetServoPos = xSemaphoreCreateMutex(); + semaphore_updatePedalStates = xSemaphoreCreateMutex(); + delay(10); + + + if(semaphore_updateJoystick==NULL) + { + Serial.println("Could not create semaphore"); + ESP.restart(); + } + if(semaphore_updateConfig==NULL) + { + Serial.println("Could not create semaphore"); + ESP.restart(); + } + + + + // print all servo registers + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_PRINT_ALL_SERVO_REGISTERS) + { + if (isv57LifeSignal_b) + { + isv57.readAllServoParameters(); + } + } + + + + + disableCore0WDT(); + + //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1 + xTaskCreatePinnedToCore( + pedalUpdateTask, /* Task function. */ + "pedalUpdateTask", /* name of task. */ + 10000, /* Stack size of task */ + //STACK_SIZE_FOR_TASK_1, + NULL, /* parameter of the task */ + 1, /* priority of the task */ + &Task1, /* Task handle to keep track of created task */ + 0); /* pin task to core 1 */ + delay(500); + + xTaskCreatePinnedToCore( + serialCommunicationTask, + "serialCommunicationTask", + 10000, + //STACK_SIZE_FOR_TASK_2, + NULL, + 1, + &Task2, + 1); + delay(500); + + #ifdef ISV_COMMUNICATION + + xTaskCreatePinnedToCore( + servoCommunicationTask, + "servoCommunicationTask", + 10000, + //STACK_SIZE_FOR_TASK_2, + NULL, + 1, + &Task3, + 1); + delay(500); +#endif + + + + //Serial.begin(115200); + #ifdef OTA_update + char* APhost; + switch(dap_config_st.payLoadPedalConfig_.pedal_type) + { + case 0: + APhost="FFBPedalClutch"; + break; + case 1: + APhost="FFBPedalBrake"; + break; + case 2: + APhost="FFBPedalGas"; + break; + default: + APhost="FFBPedal"; + break; + + } + if(dap_config_st.payLoadPedalConfig_.OTA_flag==1) + { + ota_wifi_initialize(APhost); + xTaskCreatePinnedToCore( + OTATask, + "OTATask", + 16000, + //STACK_SIZE_FOR_TASK_2, + NULL, + 1, + &Task4, + 1); + delay(500); + } + #endif + + //MCP setup + #ifdef Using_analog_output_ESP32_S3 + //Wire.begin(MCP_SDA,MCP_SCL,400000); + MCP4725_I2C.begin(MCP_SDA,MCP_SCL,400000); + uint8_t i2c_address[8]={0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67}; + int index_address=0; + int found_address=0; + int error; + for(index_address=0;index_address<8;index_address++) + { + MCP4725_I2C.beginTransmission(i2c_address[index_address]); + error = MCP4725_I2C.endTransmission(); + if (error == 0) + { + Serial.print("I2C device found at address"); + Serial.print(i2c_address[index_address]); + Serial.println(" !"); + found_address=index_address; + break; + + } + else + { + Serial.print("try address"); + Serial.println(i2c_address[index_address]); + } + } + + if(dac.begin(i2c_address[found_address], &MCP4725_I2C)==false) + { + Serial.println("Couldn't find MCP, will not have analog output"); + MCP_status=false; + } + else + { + Serial.println("MCP founded"); + MCP_status=true; + //MCP.begin(); + } + #endif + + + Serial.println("Setup end"); + +} + + + + +/**********************************************************************************************/ +/* */ +/* Calc update function */ +/* */ +/**********************************************************************************************/ +void updatePedalCalcParameters() +{ + dap_calculationVariables_st.updateFromConfig(dap_config_st); + dap_calculationVariables_st.updateEndstops(stepper->getLimitMin(), stepper->getLimitMax()); + stepper->updatePedalMinMaxPos(dap_config_st.payLoadPedalConfig_.pedalStartPosition, dap_config_st.payLoadPedalConfig_.pedalEndPosition); + //stepper->findMinMaxLimits(dap_config_st.payLoadPedalConfig_.pedalStartPosition, dap_config_st.payLoadPedalConfig_.pedalEndPosition); + dap_calculationVariables_st.updateStiffness(); + + // tune the PID settings + tunePidValues(dap_config_st); + + // equalize pedal config for both tasks + dap_config_st_local = dap_config_st; +} + + + +/**********************************************************************************************/ +/* */ +/* Main function */ +/* */ +/**********************************************************************************************/ +void loop() { + taskYIELD(); + /* + #ifdef OTA_update + server.handleClient(); + //delay(1); + #endif + */ + + +} + + +/**********************************************************************************************/ +/* */ +/* pedal update task */ +/* */ +/**********************************************************************************************/ + + +//long lastCallTime = micros(); +unsigned long cycleTimeLastCall = micros(); +unsigned long minCyclesForFirToInit = 1000; +unsigned long firCycleIncrementer = 0; + +float filteredReading_exp_filter = 0; +unsigned long printCycleCounter = 0; + + +uint printCntr = 0; +//void loop() +void pedalUpdateTask( void * pvParameters ) +{ + + for(;;){ + + + // system identification mode + #ifdef ALLOW_SYSTEM_IDENTIFICATION + if (systemIdentificationMode_b == true) + { + measureStepResponse(stepper, &dap_calculationVariables_st, &dap_config_st, loadcell); + systemIdentificationMode_b = false; + } + #endif + + + // controll cycle time. Delay did not work with the multi tasking, thus this workaround was integrated + unsigned long now = micros(); + if (now - cycleTimeLastCall < PUT_TARGET_CYCLE_TIME_IN_US) // 100us = 10kHz + { + // skip + continue; + } + { + // if target cycle time is reached, update last time + cycleTimeLastCall = now; + } + + + + // print the execution time averaged over multiple cycles + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_CYCLE_TIMER) + { + static CycleTimer timerPU("PU cycle time"); + timerPU.Bump(); + } + + + // if a config update was received over serial, update the variables required for further computation + if (configUpdateAvailable == true) + { + if(semaphore_updateConfig!=NULL) + { + + bool configWasUpdated_b = false; + // Take the semaphore and just update the config file, then release the semaphore + if(xSemaphoreTake(semaphore_updateConfig, (TickType_t)1)==pdTRUE) + { + Serial.println("Updating pedal config"); + configUpdateAvailable = false; + dap_config_st = dap_config_st_local; + configWasUpdated_b = true; + xSemaphoreGive(semaphore_updateConfig); + } + + // update the calc params + if (true == configWasUpdated_b) + { + Serial.println("Updating the calc params"); + configWasUpdated_b = false; + + if (true == dap_config_st.payLoadHeader_.storeToEeprom) + { + dap_config_st.payLoadHeader_.storeToEeprom = false; // set to false, thus at restart existing EEPROM config isn't restored to EEPROM + uint16_t crc = checksumCalculator((uint8_t*)(&(dap_config_st.payLoadHeader_)), sizeof(dap_config_st.payLoadHeader_) + sizeof(dap_config_st.payLoadPedalConfig_)); + dap_config_st.payloadFooter_.checkSum = crc; + dap_config_st.storeConfigToEprom(dap_config_st); // store config to EEPROM + } + + updatePedalCalcParameters(); // update the calc parameters + } + + } + else + { + semaphore_updateConfig = xSemaphoreCreateMutex(); + //Serial.println("semaphore_updateConfig == 0"); + } + } + + + + // if reset pedal position was requested, reset pedal now + // This function is implemented, so that in case of lost steps, the user can request a reset of the pedal psotion + if (resetPedalPosition) { + + if (isv57LifeSignal_b && SENSORLESS_HOMING) + { + stepper->refindMinLimitSensorless(&isv57); + } + else + { + stepper->refindMinLimit(); + } + + resetPedalPosition = false; + resetServoEncoder = true; + } + + + //#define RECALIBRATE_POSITION + #ifdef RECALIBRATE_POSITION + stepper->checkLimitsAndResetIfNecessary(); + #endif + + + // compute pedal oscillation, when ABS is active + float absForceOffset_fl32 = 0.0f; + + float absForceOffset = 0; + float absPosOffset = 0; + #ifdef ABS_OSCILLATION + absOscillation.forceOffset(&dap_calculationVariables_st, dap_config_st.payLoadPedalConfig_.absPattern, dap_config_st.payLoadPedalConfig_.absForceOrTarvelBit, &absForceOffset, &absPosOffset); + _RPMOscillation.trigger(); + _RPMOscillation.forceOffset(&dap_calculationVariables_st); + _BitePointOscillation.forceOffset(&dap_calculationVariables_st); + _G_force_effect.forceOffset(&dap_calculationVariables_st, dap_config_st.payLoadPedalConfig_.G_multi); + _WSOscillation.forceOffset(&dap_calculationVariables_st); + _Road_impact_effect.forceOffset(&dap_calculationVariables_st, dap_config_st.payLoadPedalConfig_.Road_multi); + #endif + + //update max force with G force effect + movingAverageFilter.dataPointsCount=dap_config_st.payLoadPedalConfig_.G_window; + movingAverageFilter_roadimpact.dataPointsCount=dap_config_st.payLoadPedalConfig_.Road_window; + dap_calculationVariables_st.reset_maxforce(); + dap_calculationVariables_st.Force_Max+=_G_force_effect.G_force; + dap_calculationVariables_st.Force_Max+=_Road_impact_effect.Road_Impact_force; + dap_calculationVariables_st.dynamic_update(); + dap_calculationVariables_st.updateStiffness(); + + // compute the pedal incline angle + //#define COMPUTE_PEDAL_INCLINE_ANGLE + #ifdef COMPUTE_PEDAL_INCLINE_ANGLE + float sledPosition = sledPositionInMM(stepper); + float pedalInclineAngle = pedalInclineAngleDeg(sledPosition, dap_config_st); + + // compute angular velocity & acceleration of incline angke + float pedalInclineAngle_Accel = pedalInclineAngleAccel(pedalInclineAngle); + + //float legRotationalMoment = 0.0000001; + //float forceCorrection = pedalInclineAngle_Accel * legRotationalMoment; + + //Serial.print(pedalInclineAngle_Accel); + //Serial.println(" "); + + #endif + + + // Get the loadcell reading + float loadcellReading = loadcell->getReadingKg(); + + // Invert the loadcell reading digitally if desired + if (dap_config_st.payLoadPedalConfig_.invertLoadcellReading_u8 == 1) + { + loadcellReading *= -1; + } + + + // Convert loadcell reading to pedal force + float sledPosition = sledPositionInMM(stepper, dap_config_st); + float pedalInclineAngleInDeg_fl32 = pedalInclineAngleDeg(sledPosition, dap_config_st); + float pedalForce_fl32 = convertToPedalForce(loadcellReading, sledPosition, dap_config_st); + float d_phi_d_x = convertToPedalForceGain(sledPosition, dap_config_st); + + // compute gain for horizontal foot model + float b = dap_config_st.payLoadPedalConfig_.lengthPedal_b; + float d = dap_config_st.payLoadPedalConfig_.lengthPedal_d; + float d_x_hor_d_phi = -(b+d) * sinf(pedalInclineAngleInDeg_fl32 * DEG_TO_RAD); + + /*printCntr++; + if (printCntr >= 100) + { + + Serial.print("MPC_0th_order_gain: "); + Serial.print(dap_config_st.payLoadPedalConfig_.MPC_0th_order_gain); + printCntr = 0; + }*/ + + + /*printCntr++; + if (printCntr >= 100) + { + Serial.print("Angle: "); + Serial.print(pedalInclineAngleInDeg_fl32); + + Serial.print(", sin(angle): "); + Serial.print( sinf(pedalInclineAngleInDeg_fl32 * DEG_TO_RAD) ); + + Serial.print(", d_phi_d_x: "); + Serial.print( d_phi_d_x ); + + Serial.print(", d_x_hor_d_phi: "); + Serial.print( d_x_hor_d_phi ); + + //Serial.print(", a: "); + //Serial.print( dap_config_st.payLoadPedalConfig_.lengthPedal_a ); + + //Serial.print(", b: "); + //Serial.print( dap_config_st.payLoadPedalConfig_.lengthPedal_b ); + + //Serial.print(", c_ver: "); + //Serial.print( dap_config_st.payLoadPedalConfig_.lengthPedal_c_vertical ); + + //Serial.print(", c_ver: "); + //Serial.print( dap_config_st.payLoadPedalConfig_.lengthPedal_c_horizontal ); + + //Serial.print(", d: "); + //Serial.print( dap_config_st.payLoadPedalConfig_.lengthPedal_d ); + + Serial.println(); + printCntr = 0; + }*/ + + + + + + // Do the loadcell signal filtering + float filteredReading = 0; + float changeVelocity = 0; + + // const velocity model denoising filter + if (dap_config_st.payLoadPedalConfig_.kf_modelOrder == 0) + { + filteredReading = kalman->filteredValue(pedalForce_fl32, 0, dap_config_st.payLoadPedalConfig_.kf_modelNoise); + changeVelocity = kalman->changeVelocity(); + } + + // const acceleration model denoising filter + if (dap_config_st.payLoadPedalConfig_.kf_modelOrder == 1) + { + filteredReading = kalman_2nd_order->filteredValue(pedalForce_fl32, 0, dap_config_st.payLoadPedalConfig_.kf_modelNoise); + changeVelocity = kalman->changeVelocity(); + } + + // exponential denoising filter + if (dap_config_st.payLoadPedalConfig_.kf_modelOrder == 2) + { + float alpha_exp_filter = 1.0f - ( (float)dap_config_st.payLoadPedalConfig_.kf_modelNoise) / 5000.0f; + float filteredReading_exp_filter = filteredReading_exp_filter * alpha_exp_filter + pedalForce_fl32 * (1.0-alpha_exp_filter); + filteredReading = filteredReading_exp_filter; + } + + //filteredReading=constrain(filteredReading, dap_calculationVariables_st.Force_Min, dap_calculationVariables_st.Force_Max_default); + + + + + // Do position state estimate + float stepper_pos_filtered_fl32 = 0;//kalman_2nd_order->filteredValue(stepper->getCurrentPosition(), 0, dap_config_st.payLoadPedalConfig_.kf_modelNoise); + float stepper_vel_filtered_fl32 = 0;//kalman_2nd_order->changeVelocity(); + float stepper_accel_filtered_fl32 = 0;//kalman_2nd_order->changeAccel(); + + + /*Serial.print(stepper->getCurrentPosition()); + Serial.print(", "); + Serial.print(stepper_pos_filtered_fl32); + Serial.print(", "); + Serial.print(stepper_vel_filtered_fl32); + Serial.println(" ");*/ + + + + + + //#define DEBUG_FILTER + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_LOADCELL_READING) + { + static RTDebugOutput rtDebugFilter({ "rawReading_g", "pedalForce_fl32", "filtered_g"}); + rtDebugFilter.offerData({ loadcellReading * 1000, pedalForce_fl32*1000, filteredReading * 1000}); + } + + + /*#ifdef ABS_OSCILLATION + filteredReading += forceAbsOffset; + #endif*/ + + + //Add effect by force + float effect_force=absForceOffset+ _BitePointOscillation.BitePoint_Force_offset+_WSOscillation.WS_Force_offset; + + // use interpolation to determine local linearized spring stiffness + double stepperPosFraction = stepper->getCurrentPositionFraction(); + int32_t Position_Next = 0; + + // select control loop algo + if (dap_config_st.payLoadPedalConfig_.control_strategy_b <= 1) + { + Position_Next = MoveByPidStrategy(filteredReading, stepperPosFraction, stepper, &forceCurve, &dap_calculationVariables_st, &dap_config_st, effect_force, changeVelocity); + } + + if (dap_config_st.payLoadPedalConfig_.control_strategy_b == 2) + { + Position_Next = MoveByForceTargetingStrategy(filteredReading, stepper, &forceCurve, &dap_calculationVariables_st, &dap_config_st, effect_force, changeVelocity, stepper_vel_filtered_fl32, stepper_accel_filtered_fl32, d_phi_d_x, d_x_hor_d_phi); + } + + + + + + //#define DEBUG_STEPPER_POS + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_STEPPER_POS) + { + static RTDebugOutput rtDebugFilter({ "ESP_pos", "ESP_tar_pos", "ISV_pos", "frac1"}); + rtDebugFilter.offerData({ stepper->getCurrentPositionFromMin(), Position_Next, -(int32_t)(isv57.servo_pos_given_p + isv57.servo_pos_error_p - isv57.getZeroPos()), (int32_t)(stepperPosFraction * 10000.)}); + } + + + //stepper->printStates(); + + + // add dampening + if (dap_calculationVariables_st.dampingPress > 0.0001) + { + // dampening is proportional to velocity --> D-gain for stability + Position_Next -= dap_calculationVariables_st.dampingPress * changeVelocity * dap_calculationVariables_st.springStiffnesssInv; + } + + + + // clip target position to configured target interval with RPM effect movement in the endstop + Position_Next = (int32_t)constrain(Position_Next, dap_calculationVariables_st.stepperPosMin, dap_calculationVariables_st.stepperPosMax); + + + //Adding effects + Position_Next +=_RPMOscillation.RPM_position_offset; + Position_Next +=absPosOffset; + Position_Next = (int32_t)constrain(Position_Next, dap_calculationVariables_st.stepperPosMinEndstop, dap_calculationVariables_st.stepperPosMaxEndstop); + + + //bitepoint trigger + + int32_t BP_trigger_value=dap_config_st.payLoadPedalConfig_.BP_trigger_value; + int32_t BP_trigger_min=(BP_trigger_value-4); + int32_t BP_trigger_max=(BP_trigger_value+4); + int32_t Position_check=100*((Position_Next-dap_calculationVariables_st.stepperPosMin) / dap_calculationVariables_st.stepperPosRange); + //Serial.println(Position_check); + if(dap_config_st.payLoadPedalConfig_.BP_trigger==1) + { + if(Position_check > BP_trigger_min) + { + if(Position_check < BP_trigger_max) + { + _BitePointOscillation.trigger(); + } + } + } + + // if pedal in min position, recalibrate position + #ifdef ISV_COMMUNICATION + // Take the semaphore and just update the config file, then release the semaphore + if(xSemaphoreTake(semaphore_resetServoPos, (TickType_t)1)==pdTRUE) + { + if (stepper->isAtMinPos()) + { + stepper->correctPos(servo_offset_compensation_steps_i32); + servo_offset_compensation_steps_i32 = 0; + } + xSemaphoreGive(semaphore_resetServoPos); + } + #endif + + + + // get current stepper position right before sheduling a new move + //int32_t stepperPosCurrent = stepper->getCurrentPositionFromMin(); + //int32_t stepperPosCurrent = stepper->getTargetPositionSteps(); + //int32_t movement = abs(stepperPosCurrent - Position_Next); + //if (movement > MIN_STEPS) + { + stepper->moveTo(Position_Next, false); + } + + + + // compute controller output + dap_calculationVariables_st.reset_maxforce(); + dap_calculationVariables_st.dynamic_update(); + dap_calculationVariables_st.updateStiffness(); + if(semaphore_updateJoystick!=NULL) + { + if(xSemaphoreTake(semaphore_updateJoystick, (TickType_t)1)==pdTRUE) { + + if (1 == dap_config_st.payLoadPedalConfig_.travelAsJoystickOutput_u8) + { + joystickNormalizedToInt32 = NormalizeControllerOutputValue(Position_Next, dap_calculationVariables_st.stepperPosMin, dap_calculationVariables_st.stepperPosMax, dap_config_st.payLoadPedalConfig_.maxGameOutput); + } + else + { + //joystickNormalizedToInt32 = NormalizeControllerOutputValue(loadcellReading, dap_calculationVariables_st.Force_Min, dap_calculationVariables_st.Force_Max, dap_config_st.payLoadPedalConfig_.maxGameOutput); + + joystickNormalizedToInt32 = NormalizeControllerOutputValue(filteredReading, dap_calculationVariables_st.Force_Min, dap_calculationVariables_st.Force_Max, dap_config_st.payLoadPedalConfig_.maxGameOutput); + } + + xSemaphoreGive(semaphore_updateJoystick); + } + } + else + { + semaphore_updateJoystick = xSemaphoreCreateMutex(); + //Serial.println("semaphore_updateJoystick == 0"); + } + + // provide joystick output on PIN + #ifdef Using_analog_output + int dac_value=(int)(joystickNormalizedToInt32*255/10000); + dacWrite(D_O,dac_value); + #endif + + #ifdef Using_analog_output_ESP32_S3 + if(MCP_status) + { + int dac_value=(int)(joystickNormalizedToInt32*4096*0.9/10000);//limit the max to 5V*0.9=4.5V to prevent the overvolatage + dac.setVoltage(dac_value, false); + } + #endif + + + float normalizedPedalReading_fl32 = 0; + if ( fabs(dap_calculationVariables_st.Force_Range) > 0.01) + { + normalizedPedalReading_fl32 = constrain((filteredReading - dap_calculationVariables_st.Force_Min) / dap_calculationVariables_st.Force_Range, 0, 1); + } + + // simulate ABS trigger + if(dap_config_st.payLoadPedalConfig_.Simulate_ABS_trigger==1) + { + int32_t ABS_trigger_value=dap_config_st.payLoadPedalConfig_.Simulate_ABS_value; + if( (normalizedPedalReading_fl32*100) > ABS_trigger_value) + { + absOscillation.trigger(); + } + } + + + + // update pedal states + if(semaphore_updatePedalStates!=NULL) + { + if(xSemaphoreTake(semaphore_updatePedalStates, (TickType_t)1)==pdTRUE) + { + + // update basic pedal state struct + dap_state_basic_st.payloadPedalState_Basic_.pedalForce_u16 = normalizedPedalReading_fl32 * 65535; + dap_state_basic_st.payloadPedalState_Basic_.pedalPosition_u16 = constrain(stepperPosFraction, 0, 1) * 65535; + dap_state_basic_st.payloadPedalState_Basic_.joystickOutput_u16 = (float)joystickNormalizedToInt32 / 10000. * 32767.0;//65535; + + dap_state_basic_st.payLoadHeader_.payloadType = DAP_PAYLOAD_TYPE_STATE_BASIC; + dap_state_basic_st.payLoadHeader_.version = DAP_VERSION_CONFIG; + dap_state_basic_st.payloadFooter_.checkSum = checksumCalculator((uint8_t*)(&(dap_state_basic_st.payLoadHeader_)), sizeof(dap_state_basic_st.payLoadHeader_) + sizeof(dap_state_basic_st.payloadPedalState_Basic_)); + + + // update extended struct + dap_state_extended_st.payloadPedalState_Extended_.timeInMs_u32 = millis(); + dap_state_extended_st.payloadPedalState_Extended_.pedalForce_raw_fl32 = loadcellReading; + dap_state_extended_st.payloadPedalState_Extended_.pedalForce_filtered_fl32 = filteredReading; + dap_state_extended_st.payloadPedalState_Extended_.forceVel_est_fl32 = changeVelocity; + + if(semaphore_readServoValues!=NULL) + { + if(xSemaphoreTake(semaphore_readServoValues, (TickType_t)1)==pdTRUE) { + dap_state_extended_st.payloadPedalState_Extended_.servoPosition_i16 = servoPos_i16; + dap_state_extended_st.payloadPedalState_Extended_.servo_voltage_0p1V = isv57.servo_voltage_0p1V; + dap_state_extended_st.payloadPedalState_Extended_.servo_current_percent_i16 = isv57.servo_current_percent; + + xSemaphoreGive(semaphore_readServoValues); + } + } + else + { + semaphore_readServoValues = xSemaphoreCreateMutex(); + } + + dap_state_extended_st.payloadPedalState_Extended_.servoPositionTarget_i16 = stepper->getCurrentPositionFromMin(); + dap_state_extended_st.payLoadHeader_.payloadType = DAP_PAYLOAD_TYPE_STATE_EXTENDED; + dap_state_extended_st.payLoadHeader_.version = DAP_VERSION_CONFIG; + dap_state_extended_st.payloadFooter_.checkSum = checksumCalculator((uint8_t*)(&(dap_state_extended_st.payLoadHeader_)), sizeof(dap_state_extended_st.payLoadHeader_) + sizeof(dap_state_extended_st.payloadPedalState_Extended_)); + + // release semaphore + xSemaphoreGive(semaphore_updatePedalStates); + } + } + else + { + semaphore_updatePedalStates = xSemaphoreCreateMutex(); + } + + + + + + + + + + #ifdef PRINT_USED_STACK_SIZE + unsigned int temp2 = uxTaskGetStackHighWaterMark(nullptr); + Serial.print("PU task stack size="); Serial.println(temp2); + #endif + /* + #ifdef OTA_update + server.handleClient(); + //delay(1); + #endif + */ + + } +} + + + + + + + + + + +/**********************************************************************************************/ +/* */ +/* communication task */ +/* */ +/**********************************************************************************************/ + + + + + +int32_t joystickNormalizedToInt32_local = 0; +void serialCommunicationTask( void * pvParameters ) +{ + + for(;;){ + + // average cycle time averaged over multiple cycles + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_CYCLE_TIMER) + { + static CycleTimer timerSC("SC cycle time"); + timerSC.Bump(); + } + + uint16_t crc; + + + + + delay( SERIAL_COOMUNICATION_TASK_DELAY_IN_MS ); + + + { + // read serial input + uint8_t n = Serial.available(); + + bool structChecker = true; + + if (n > 0) + { + switch (n) { + + // likely config structure + case sizeof(DAP_config_st): + + if(semaphore_updateConfig!=NULL) + { + if(xSemaphoreTake(semaphore_updateConfig, (TickType_t)1)==pdTRUE) + { + DAP_config_st * dap_config_st_local_ptr; + dap_config_st_local_ptr = &dap_config_st_local; + Serial.readBytes((char*)dap_config_st_local_ptr, sizeof(DAP_config_st)); + + + + // check if data is plausible + + if ( dap_config_st_local.payLoadHeader_.payloadType != DAP_PAYLOAD_TYPE_CONFIG ){ + structChecker = false; + Serial.print("Payload type expected: "); + Serial.print(DAP_PAYLOAD_TYPE_CONFIG); + Serial.print(", Payload type received: "); + Serial.println(dap_config_st_local.payLoadHeader_.payloadType); + } + if ( dap_config_st_local.payLoadHeader_.version != DAP_VERSION_CONFIG ){ + structChecker = false; + Serial.print("Config version expected: "); + Serial.print(DAP_VERSION_CONFIG); + Serial.print(", Config version received: "); + Serial.println(dap_config_st_local.payLoadHeader_.version); + } + // checksum validation + crc = checksumCalculator((uint8_t*)(&(dap_config_st_local.payLoadHeader_)), sizeof(dap_config_st_local.payLoadHeader_) + sizeof(dap_config_st_local.payLoadPedalConfig_)); + if (crc != dap_config_st_local.payloadFooter_.checkSum){ + structChecker = false; + Serial.print("CRC expected: "); + Serial.print(crc); + Serial.print(", CRC received: "); + Serial.println(dap_config_st_local.payloadFooter_.checkSum); + } + + + // if checks are successfull, overwrite global configuration struct + if (structChecker == true) + { + Serial.println("Updating pedal config"); + configUpdateAvailable = true; + } + xSemaphoreGive(semaphore_updateConfig); + } + } + break; + + // likely action structure + case sizeof(DAP_actions_st) : + + DAP_actions_st dap_actions_st; + Serial.readBytes((char*)&dap_actions_st, sizeof(DAP_actions_st)); + + if ( dap_actions_st.payLoadHeader_.payloadType != DAP_PAYLOAD_TYPE_ACTION ){ + structChecker = false; + Serial.print("Payload type expected: "); + Serial.print(DAP_PAYLOAD_TYPE_ACTION); + Serial.print(", Payload type received: "); + Serial.println(dap_config_st_local.payLoadHeader_.payloadType); + } + if ( dap_actions_st.payLoadHeader_.version != DAP_VERSION_CONFIG ){ + structChecker = false; + Serial.print("Config version expected: "); + Serial.print(DAP_VERSION_CONFIG); + Serial.print(", Config version received: "); + Serial.println(dap_config_st_local.payLoadHeader_.version); + } + crc = checksumCalculator((uint8_t*)(&(dap_actions_st.payLoadHeader_)), sizeof(dap_actions_st.payLoadHeader_) + sizeof(dap_actions_st.payloadPedalAction_)); + if (crc != dap_actions_st.payloadFooter_.checkSum){ + structChecker = false; + Serial.print("CRC expected: "); + Serial.print(crc); + Serial.print(", CRC received: "); + Serial.println(dap_actions_st.payloadFooter_.checkSum); + } + + + + if (structChecker == true) + { + + // trigger reset pedal position + if (dap_actions_st.payloadPedalAction_.resetPedalPos_u8) + { + resetPedalPosition = true; + } + + // trigger ABS effect + if (dap_actions_st.payloadPedalAction_.triggerAbs_u8) + { + absOscillation.trigger(); + } + //RPM effect + _RPMOscillation.RPM_value=dap_actions_st.payloadPedalAction_.RPM_u8; + //G force effect + _G_force_effect.G_value=dap_actions_st.payloadPedalAction_.G_value-128; + //wheel slip + if (dap_actions_st.payloadPedalAction_.WS_u8) + { + _WSOscillation.trigger(); + } + //Road impact + _Road_impact_effect.Road_Impact_value=dap_actions_st.payloadPedalAction_.impact_value_u8; + // trigger system identification + if (dap_actions_st.payloadPedalAction_.startSystemIdentification_u8) + { + systemIdentificationMode_b = true; + } + + // trigger return pedal position + if (dap_actions_st.payloadPedalAction_.returnPedalConfig_u8) + { + DAP_config_st * dap_config_st_local_ptr; + dap_config_st_local_ptr = &dap_config_st; + //uint16_t crc = checksumCalculator((uint8_t*)(&(dap_config_st.payLoadHeader_)), sizeof(dap_config_st.payLoadHeader_) + sizeof(dap_config_st.payLoadPedalConfig_)); + crc = checksumCalculator((uint8_t*)(&(dap_config_st.payLoadHeader_)), sizeof(dap_config_st.payLoadHeader_) + sizeof(dap_config_st.payLoadPedalConfig_)); + dap_config_st_local_ptr->payloadFooter_.checkSum = crc; + Serial.write((char*)dap_config_st_local_ptr, sizeof(DAP_config_st)); + Serial.print("\r\n"); + } + + + } + + break; + + default: + + // flush the input buffer + while (Serial.available()) Serial.read(); + //Serial.flush(); + + Serial.println("\nIn byte size: "); + Serial.println(n); + Serial.println(" Exp config size: "); + Serial.println(sizeof(DAP_config_st) ); + Serial.println(" Exp action size: "); + Serial.println(sizeof(DAP_actions_st) ); + + break; + + + + + } + } + + + // send pedal state structs + // update pedal states + printCycleCounter++; + DAP_state_basic_st dap_state_basic_st_lcl; + DAP_state_extended_st dap_state_extended_st_lcl; + + if(semaphore_updatePedalStates!=NULL) + { + + if(xSemaphoreTake(semaphore_updatePedalStates, (TickType_t)1)==pdTRUE) + { + + // UPDATE basic pedal state struct + dap_state_basic_st_lcl = dap_state_basic_st; + + // UPDATE extended pedal state struct + dap_state_extended_st_lcl = dap_state_extended_st; + + // release semaphore + xSemaphoreGive(semaphore_updatePedalStates); + + } + } + else + { + semaphore_updatePedalStates = xSemaphoreCreateMutex(); + } + + + + // send the pedal state structs + // send basic pedal state struct + if ( !(dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_STATE_BASIC_INFO_STRUCT) ) + { + if (printCycleCounter >= 2) + { + printCycleCounter = 0; + Serial.write((char*)&dap_state_basic_st_lcl, sizeof(DAP_state_basic_st)); + Serial.print("\r\n"); + } + } + + if ( (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_STATE_EXTENDED_INFO_STRUCT) ) + { + Serial.write((char*)&dap_state_extended_st_lcl, sizeof(DAP_state_extended_st)); + Serial.print("\r\n"); + } + + + // wait until transmission is finished + // flush argument = true, do not clear Rx buffer + //Serial.flush(); + //Serial.flush(true); + + } + + // transmit controller output + //Serial.print("Joy 1"); + delay( SERIAL_COOMUNICATION_TASK_DELAY_IN_MS ); + if (IsControllerReady()) + { + //Serial.print(" 2"); + if(semaphore_updateJoystick!=NULL) + { + if(xSemaphoreTake(semaphore_updateJoystick, (TickType_t)1)==pdTRUE) + { + //Serial.print(" 3"); + joystickNormalizedToInt32_local = joystickNormalizedToInt32; + xSemaphoreGive(semaphore_updateJoystick); + } + } + //Serial.print(" 4"); + //Serial.print("\r\n"); + SetControllerOutputValue(joystickNormalizedToInt32_local); + } + + /*#ifdef SERIAL_TIMEOUT + delay(10); + #endif +*/ + + } +} +//OTA multitask +void OTATask( void * pvParameters ) +{ + + for(;;) + { + #ifdef OTA_update + server.handleClient(); + //delay(1); + #endif + } +} + + + +#ifdef ISV_COMMUNICATION + + +int16_t servoPos_last_i16 = 0; +int64_t timeSinceLastServoPosChange_l = 0; +int64_t timeNow_l = 0; +int64_t timeDiff = 0; + +#define TIME_SINCE_SERVO_POS_CHANGE_TO_DETECT_STANDSTILL_IN_MS 200 + + + +uint64_t print_cycle_counter_u64 = 0; +unsigned long cycleTimeLastCall_lifelineCheck = 0;//micros(); +void servoCommunicationTask( void * pvParameters ) +{ + + for(;;){ + + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_CYCLE_TIMER) + { + static CycleTimer timerServoCommunication("Servo Com. cycle time"); + timerServoCommunication.Bump(); + } + + // check if servo communication is still there every N milliseconds + unsigned long now = millis(); + if ( (now - cycleTimeLastCall_lifelineCheck) > 5000) + { + // if target cycle time is reached, update last time + cycleTimeLastCall_lifelineCheck = now; + + isv57LifeSignal_b = isv57.checkCommunication(); + //Serial.println("Lifeline check"); + } + + + + if (isv57LifeSignal_b) + { + + //delay(5); + isv57.readServoStates(); + + if(semaphore_readServoValues!=NULL) + { + if(xSemaphoreTake(semaphore_readServoValues, (TickType_t)1)==pdTRUE) { + servoPos_i16 = -( isv57.servo_pos_given_p - isv57.getZeroPos() ); + xSemaphoreGive(semaphore_readServoValues); + } + } + else + { + semaphore_readServoValues = xSemaphoreCreateMutex(); + } + + + + int32_t servo_offset_compensation_steps_local_i32 = 0; + + + // condition 1: servo must be at halt + // condition 2: the esp accel lib must be at halt + bool cond_1 = false;; + bool cond_2 = false; + + // check whether target position from ESP hasn't changed and is at min endstop position + cond_2 = stepper->isAtMinPos(); + + if (cond_2 == true) + { + //isv57.readServoStates(); + int16_t servoPos_now_i16 = isv57.servo_pos_given_p; + timeNow_l = millis(); + +//#define PRINT_SERVO_POS_EVERY_N_CYCLES +#ifdef PRINT_SERVO_POS_EVERY_N_CYCLES + print_cycle_counter_u64++; + // print servo pos every N cycles + if ( (print_cycle_counter_u64 % 2000) == 0 ) + { + Serial.println(servoPos_now_i16); + print_cycle_counter_u64 = 0; + } +#endif + + + // check whether servo position has changed, in case, update the halt detection variable + if (servoPos_last_i16 != servoPos_now_i16) + { + servoPos_last_i16 = servoPos_now_i16; + timeSinceLastServoPosChange_l = timeNow_l; + } + + // compute the time difference since last servo position change + timeDiff = timeNow_l - timeSinceLastServoPosChange_l; + + // if time between last servo position is larger than a threshold, detect servo standstill + if ( (timeDiff > TIME_SINCE_SERVO_POS_CHANGE_TO_DETECT_STANDSTILL_IN_MS) + && (timeNow_l > 0) ) + { + cond_1 = true; + } + else + { + cond_1 = false; + } + } + + + + + // calculate zero position offset + if (cond_1 && cond_2) + { + + // reset encoder position, when pedal is at min position + if (resetServoEncoder == true) + { + isv57.setZeroPos(); + resetServoEncoder = false; + } + + // calculate encoder offset + // movement to the back will reduce encoder value + servo_offset_compensation_steps_local_i32 = (int32_t)isv57.getZeroPos() - (int32_t)isv57.servo_pos_given_p; + // when pedal has moved to the back due to step losses --> offset will be positive + + // since the encoder positions are defined in int16 space, they wrap at multiturn + // to correct overflow, we apply modulo to take smallest possible deviation + if (servo_offset_compensation_steps_local_i32 > pow(2,15)-1) + { + servo_offset_compensation_steps_local_i32 -= pow(2,16); + } + + if (servo_offset_compensation_steps_local_i32 < -pow(2,15)) + { + servo_offset_compensation_steps_local_i32 += pow(2,16); + } + } + + + // invert the compensation wrt the motor direction + if (dap_config_st.payLoadPedalConfig_.invertMotorDirection_u8 == 1) + { + servo_offset_compensation_steps_local_i32 *= -1; + } + + + if(semaphore_resetServoPos!=NULL) + { + + // Take the semaphore and just update the config file, then release the semaphore + if(xSemaphoreTake(semaphore_resetServoPos, (TickType_t)1)==pdTRUE) + { + servo_offset_compensation_steps_i32 = servo_offset_compensation_steps_local_i32; + xSemaphoreGive(semaphore_resetServoPos); + } + + } + else + { + semaphore_resetServoPos = xSemaphoreCreateMutex(); + //Serial.println("semaphore_resetServoPos == 0"); + } + + + + if (dap_config_st.payLoadPedalConfig_.debug_flags_0 & DEBUG_INFO_0_SERVO_READINGS) + { + static RTDebugOutput rtDebugFilter({ "pos_p", "pos_error_p", "curr_per", "offset"}); + rtDebugFilter.offerData({ isv57.servo_pos_given_p, isv57.servo_pos_error_p, isv57.servo_current_percent, (int16_t)servo_offset_compensation_steps_i32}); + } + + + + + } + else + { + Serial.println("Servo communication lost!"); + delay(1000); + } + + + } +} + +#endif diff --git a/ESP32/src/Modbus.cpp b/ESP32/src/Modbus.cpp new file mode 100644 index 00000000..cb6ac7d0 --- /dev/null +++ b/ESP32/src/Modbus.cpp @@ -0,0 +1,523 @@ +#include "Modbus.h" +#include + +Modbus::Modbus() +{ + this->s = NULL; + this->mode_ = -1; +} +Modbus::Modbus(HardwareSerial &st) +{ + this->s = &st; +} + + +bool Modbus::init(int mode, bool en_log) +{ + this->mode_ = mode; + this->log = en_log; + //pinMode(mode_,OUTPUT); + //digitalWrite(mode_, 0); + + return true; +} + +void Modbus::setTimeout(uint16_t timeout) +{ + timeout_ = timeout; +} + +uint8_t Modbus::byteRead(int index) +{ + return rawRx[index+3]; +} + +int Modbus::blockRead(int index) +{ + return ((dataRx[index*2] << 8) | dataRx[index*2+1]); +} + +int Modbus::coilRead(int address){ + + return coilRead(SlaveID,address); +} + +int Modbus::coilRead(int id, int address){ + if(requestFrom(id,Coil_Register,address,1)) + { + uint8_t x = byteRead(0); + return bitRead(x,0); + }else + { + return -1; + } +} + +int Modbus::discreteInputRead(int address) +{ + return discreteInputRead(SlaveID,address); +} + +int Modbus::discreteInputRead(int id, int address) +{ + if(requestFrom(id,Discret_Register,address,1)) + { + uint8_t x = byteRead(0); + return bitRead(x,0); + }else + { + return -1; + } +} + + + +// check target values at register address. If target value was already present, return 0. If target value has to be set, return 1. +void Modbus::readParameter(uint16_t slaveId_local_u16, uint16_t parameterAdress) { + + bool retValue_b = false; + + // check if value at address is already target value + uint8_t raw2[2]; + uint8_t len; + int16_t regArray[4]; + + // read the four registers simultaneously + if(requestFrom(slaveId_local_u16, 0x03, parameterAdress, 2) > 0) + { + RxRaw(raw2, len); + regArray[0] = uint16(0); + } + + // write to public variables + int16_t returnValue = regArray[0]; + + + + // if value is not target value --> overwrite value + Serial.print("Parameter address: "); + Serial.print(parameterAdress); + Serial.print(", actual:"); + Serial.println(returnValue); + + + delay(50); +} + + +// check target values at register address. If target value was already present, return 0. If target value has to be set, return 1. +bool Modbus::checkAndReplaceParameter(uint16_t slaveId_local_u16, uint16_t parameterAdress, long value) { + + bool retValue_b = false; + + // check if value at address is already target value + uint8_t raw2[2]; + uint8_t len; + int16_t regArray[4]; + + // read the four registers simultaneously + if(requestFrom(slaveId_local_u16, 0x03, parameterAdress, 2) > 0) + { + RxRaw(raw2, len); + regArray[0] = uint16(0); + } + + // write to public variables + int16_t returnValue = regArray[0]; + + + + // if value is not target value --> overwrite value + if(returnValue!= value) + { + Serial.print("Parameter adresse: "); + Serial.print(parameterAdress); + Serial.print(", actual:"); + Serial.print(returnValue); + Serial.print(", target:"); + Serial.println(value); + + + + holdingRegisterWrite(slaveId_local_u16, parameterAdress, value); // deactivate auto gain + delay(50); + retValue_b = true; + } + + return retValue_b; +} + + +long Modbus::holdingRegisterRead(int address) +{ + return holdingRegisterRead(SlaveID, address, 1); +} + +long Modbus::holdingRegisterRead(int id, int address, int block) +{ + if(block > 2){block = 2;} + if(requestFrom(SlaveID, Holding_Register, address, block)) + { + if(block == 2) + { + return (blockRead(0) << 16 | blockRead(1)); + } + else{ + return blockRead(0); + } + } + else{ + return -1; + } + +} + +long Modbus::inputRegisterRead(int address) +{ + return inputRegisterRead(SlaveID , address, 1); +} + +long Modbus::inputRegisterRead(int id, int address, int block) +{ + if(block > 2){block = 2;} + if(requestFrom(id, Input_Register,address,block)) + { + if(block == 2) + { + return (blockRead(0) << 16 | blockRead(1)); + } + else{ + return blockRead(0); + } + } + else + { + return -1; + } +} + + + + + + +int Modbus::requestFrom(int slaveId, int type, int address, int nb) +{ + + // address = address - 1; + int crc ; + txout[0] = slaveId; + txout[1] = type; + txout[2] = address >> 8; + txout[3] = address; + txout[4] = nb >> 8; + txout[5] = nb; + crc = this->CheckCRC(txout,6); + txout[6] = crc ; + txout[7] = crc >> 8; + + + if(log){ + Serial.print("TX: "); + for(int i =0; i < 8; i++) + { + Serial.printf("%02X ",txout[i] ); + } + Serial.print("\t"); + } + + //digitalWrite(mode_,1); + //delay(1); + this->s->write(txout,8); + this->s->flush(); + //digitalWrite(mode_,0); + //delay(1); + uint32_t t = millis(); + lenRx = 0; + datalen = 0; + int ll = 0; + int rx; + uint8_t found = 0; + + while((millis() - t) < timeout_){ + if(this->s->available()) + { + rx = this->s->read(); + t = millis(); + + if(found == 0) + { + if(txout[ll] == rx){ll++;}else{ll = 0;} + if(ll == 2) + { + found = 1; + } + } + else if(found == 1){ + + rawRx[0] = txout[0]; // Slave ID + rawRx[1] = txout[1]; // Function code + rawRx[2] = rx; // Bytes count to follow + 2 Byte CRC + lenRx = 3; + found = 2; + } + else if(found == 2) + { + this->rawRx[lenRx++] = rx; + + // the receive message looks like this + // Byte 1: SalveId e.g. 0x3F + // Byte 2: Function code e.g. 0x03 + // Byte 3: Bytes to read e.g. m=8 to read 4 consecutive registers + // Byte 4-(N-2): Register values + // Byte N-1: CRC MSB + // Byte N: CRC LSB + + // The total message length is thus N = 5+m + if(lenRx >= rawRx[2] + 5) { break; } + } + + } + + + } + + if(log){ + Serial.print("RX: "); + for(int i =0; i < lenRx; i++) + { + Serial.printf("%02X ",rawRx[i] ); + } + Serial.println(); + } + + /*Serial.print(lenRx); + Serial.println();*/ + + + if(lenRx > 2){ + int crc1 = rawRx[lenRx - 1] <<8 | rawRx[lenRx - 2]; + int crc2 = CheckCRC(rawRx, lenRx - 2); + //Serial.printf("CRC1: %04X CRC2: %04X\n",crc1, crc2); + + + /*Serial.print("CRC1: "); + Serial.print(crc1); + Serial.print(", CRC2: "); + Serial.print(crc2); + Serial.println();*/ + + if(crc1 == crc2) + { + datalen = rawRx[2]; + /*Serial.print("Datalen: "); + Serial.print(datalen); + Serial.println();*/ + return datalen; + } + else + { + return -1; + } + }else{ + return -1; + } +} + + + + + + + +int Modbus::ReadCoilReg(int add) +{ + return ReadCoilReg(1, add, 1); +} + +int Modbus::ReadCoilReg(int slaveId, int add) +{ + return ReadCoilReg( slaveId, add, 1); +} + +int Modbus::ReadCoilReg(int slaveId, int add, int nbit) +{ + if(requestFrom(slaveId,Coil_Register,add,nbit)) + { + return byteRead(0); + }else + { + return -1; + } + +} + +int Modbus::ReadDiscretReg(int add) +{ + return ReadDiscretReg(1,add,1); +} + +int Modbus::ReadDiscretReg(int slaveId, int add) +{ + return ReadDiscretReg(slaveId,add,1); +} + +int Modbus::ReadDiscretReg(int slaveId, int add, int nbit) +{ + if(requestFrom(slaveId,Discret_Register,add,nbit)) { + return byteRead(0); + } + else { + return -1; + } +} + +int Modbus::ReadHoldingReg(int add) +{ + return 0; +} + +int Modbus::ReadHoldingReg(int slaveId, int add) +{ + return 0; +} + +int Modbus::ReadHoldingReg(int slaveId, int add, int nbyte) +{ + return 0; +} + +int Modbus::ReadInputReg(int add) +{ + return 0; +} + +int Modbus::ReadInputReg(int slaveId, int add) +{ + return 0; +} + +int Modbus::ReadInputReg(int slaveId, int add, int nbyte) +{ + return 0; +} + +int8_t Modbus::uint8(int add) +{ + return rawRx[add*2+3]; +} + + + + +uint16_t Modbus::uint16(int add) +{ + int add_ = (add)*2 + 3; + + return (rawRx[add_] << 8 | rawRx[add_+1]); +} + +uint32_t Modbus::uint32(int add, bool byteHL) +{ + uint32_t val ; + if (byteHL) + { + val = uint16(add) << 16 | uint16(add+1); + } + else + { + val = uint16(add+1)<< 16 | uint16(add); + } + return val; +} + +void Modbus::RxRaw(uint8_t*raw, uint8_t &rlen) +{ + + + for(int i =0; i < lenRx; i++) + { + raw[i] = rawRx[i]; + // if(rawRx[i] < 16) + // { + // Serial.print("0"); + // } + // Serial.print(rawRx[i],HEX); + } + rlen = this->lenRx; + // Serial.println(rlen); +} + + +void Modbus::TxRaw(uint8_t*raw, uint8_t &rlen) +{ + + + for(int i =0; i < 8; i++) + { + raw[i] = txout[i]; + // if(rawRx[i] < 16) + // { + // Serial.print("0"); + // } + // Serial.print(rawRx[i],HEX); + } + rlen = 8; + // Serial.println(rlen); +} + + +int Modbus::CheckCRC(uint8_t*buf, int len) +{ + int nominal = 0xA001; + int crc = 0xFFFF; + unsigned char pos,i; + + for ( pos = 0; pos < len; pos++) { + crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc + + for (i = 8; i != 0; i--) { // Loop over each bit + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= nominal; + } + else // Else LSB is not set + crc >>= 1; // Just shift right + } + } + // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes) + return crc; +} + + + +int Modbus::holdingRegisterWrite(int id, int address, uint16_t value) +{ + int crc ; + + // form signal + txout[0] = id; + txout[1] = Write_Holding_Register; + txout[2] = address >> 8; + txout[3] = address; + txout[4] = value >> 8; + txout[5] = value; + crc = this->CheckCRC(txout,6); + txout[6] = crc ; + txout[7] = crc >> 8; + + // send signal + digitalWrite(mode_,1); + delay(1); + this->s->write(txout,8); + this->s->flush(); + digitalWrite(mode_,0); + delay(1); + + + return 1; + + + + +} \ No newline at end of file diff --git a/ESP32/src/PedalGeometry.cpp b/ESP32/src/PedalGeometry.cpp new file mode 100644 index 00000000..8dcc3837 --- /dev/null +++ b/ESP32/src/PedalGeometry.cpp @@ -0,0 +1,277 @@ +#include "PedalGeometry.h" + +#include "StepperWithLimits.h" + +KALMAN<3,1> K_pedal_geometry; +unsigned long _timeLastObservation; + +static const float KF_MODEL_NOISE_FORCE_ACCELERATION = ( 10000000000. ); + + +// evolution matrix. Size is + /*K_pedal_geometry.F = {1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0};*/ + + + + + + + + +float sledPositionInMM(StepperWithLimits* stepper, DAP_config_st& config_st) { + float currentPos = stepper->getCurrentPositionFromMin(); + //return (currentPos / STEPS_PER_MOTOR_REVOLUTION) * TRAVEL_PER_ROTATION_IN_MM; + return (currentPos / STEPS_PER_MOTOR_REVOLUTION) * config_st.payLoadPedalConfig_.spindlePitch_mmPerRev_u8; + +} + +float pedalInclineAngleDeg(float sledPositionMM, DAP_config_st& config_st) { + // see https://de.wikipedia.org/wiki/Kosinussatz + // A: is lower pedal pivot + // C: is upper pedal pivot + // B: is rear pedal pivot + float a = config_st.payLoadPedalConfig_.lengthPedal_a; + float b = config_st.payLoadPedalConfig_.lengthPedal_b; + float c_ver = config_st.payLoadPedalConfig_.lengthPedal_c_vertical; + float c_hor = config_st.payLoadPedalConfig_.lengthPedal_c_horizontal + sledPositionMM; + float c = sqrtf(c_ver * c_ver + c_hor * c_hor); + +//#define DEBUG_PEDAL_INCLINE +#ifdef DEBUG_PEDAL_INCLINE + Serial.print("a: "); Serial.print(a); + Serial.print(", b: "); Serial.print(b); + Serial.print(", c: "); Serial.print(c); + + Serial.print(", sledPositionMM: "); Serial.print(sledPositionMM); +#endif + + float nom = b*b + c*c - a*a; + float den = 2 * b * c; + + float alpha = 0; + if (abs(den) > 0.01) { + alpha = acos( nom / den ); + } + +#ifdef DEBUG_PEDAL_INCLINE + Serial.print(", alpha1: "); Serial.print(alpha * RAD_TO_DEG); +#endif + + // add incline due to AB incline --> result is incline realtive to horizontal + if (abs(c_hor)>0.01) { + //alpha += atan(c_ver / c_hor); + alpha += atan2(c_ver, c_hor); // y, x + } + + + + + +#ifdef DEBUG_PEDAL_INCLINE + Serial.print(", alpha2: "); Serial.print(alpha * RAD_TO_DEG); + Serial.println(" "); +#endif + + + return alpha * RAD_TO_DEG; +} + + + +float convertToPedalForce(float F_l, float sledPositionMM, DAP_config_st& config_st) { + // see https://de.wikipedia.org/wiki/Kosinussatz + // A: is lower pedal pivot + // B: is rear pedal pivot + // C: is upper pedal pivot + // D: is foot rest + // + // a: is loadcell rod (connection CB) + // b: is lower pedal plate (connection AC) + // c: is sled line (connection AC) + // d: is upper pedal plate (connection AC) + + float a = config_st.payLoadPedalConfig_.lengthPedal_a; + float b = config_st.payLoadPedalConfig_.lengthPedal_b; + float d = config_st.payLoadPedalConfig_.lengthPedal_d; + + float c_ver = config_st.payLoadPedalConfig_.lengthPedal_c_vertical; + float c_hor = config_st.payLoadPedalConfig_.lengthPedal_c_horizontal + sledPositionMM; + float c = sqrtf(c_ver * c_ver + c_hor * c_hor); + + + //Serial.print("a: "); Serial.print(a); + //Serial.print(", b: "); Serial.print(b); + //Serial.print(", c: "); Serial.print(c); + //Serial.print(", d: "); Serial.print(d); + //Serial.print(", sled: "); Serial.print(sledPositionMM); + //Serial.print(", b_hor: "); Serial.print(b_hor); + //Serial.println(); + + + // lower plus upper pedal plate length + float b_plus_d = fabs(b + d); + + // compute gamma angle, see https://de.wikipedia.org/wiki/Kosinussatz + float nom = a*a + b*b - c*c; + float den = 2 * a * b; + + float arg = 0; + if (abs(den) > 0.01) { + arg = nom / den; + arg *= arg; + } + + // apply conversion factor to loadcell reading + float one_minus_arg = 1 - arg; + float F_b = F_l; + if ( (b_plus_d > 0) && (one_minus_arg > 0) ) + { + F_b *= b / (b_plus_d) * sqrt( one_minus_arg ); + } + + + return F_b; +} + + + + +// Calculate gradient of phi with respect to sled position. +// This is done by taking the derivative of the force with respect to the sled position. +float convertToPedalForceGain(float sledPositionMM, DAP_config_st& config_st) { + // see https://de.wikipedia.org/wiki/Kosinussatz + // A: is lower pedal pivot + // B: is rear pedal pivot + // C: is upper pedal pivot + // D: is foot rest + // + // a: is loadcell rod (connection CB) + + // b: is lower pedal plate (connection AC) + // c: is sled line (connection AC) + // d: is upper pedal plate (connection AC) + + float a = config_st.payLoadPedalConfig_.lengthPedal_a; + float b = config_st.payLoadPedalConfig_.lengthPedal_b; + float d = config_st.payLoadPedalConfig_.lengthPedal_d; + + float c_ver = config_st.payLoadPedalConfig_.lengthPedal_c_vertical; + float c_hor = config_st.payLoadPedalConfig_.lengthPedal_c_horizontal + sledPositionMM; + float c = sqrtf(c_ver * c_ver + c_hor * c_hor); + + + float alpha = acos( (b*b + c*c - a*a) / (2*b*c) ); + float alphaPlus = atan2(c_ver, c_hor); // y, x + + float sinAlpha = sin(alpha); + float cosAlpha = cos(alpha); + float sinAlphaPlus = sin(alphaPlus); + float cosAlphaPlus = cos(alphaPlus); + + // d_alpha_d_x + float d_alpha_d_x = - 1.0f / fabs( sinAlpha ) * ( 1.0f / b - cosAlpha / c) * cosAlphaPlus; + + // d_alphaPlus_d_x + float d_alphaPlus_d_x = - c_ver / (c * c); + + float d_phi_d_x = d_alpha_d_x + d_alphaPlus_d_x; + + // return in deg/mm + return d_phi_d_x * RAD_TO_DEG; +} + + + + + +float pedalInclineAngleAccel(float pedalInclineAngleDeg_global) { + + + // estimate pedals angular velocity and acceleration + // obtain time + unsigned long currentTime = micros(); + unsigned long elapsedTime = currentTime - _timeLastObservation; + if (elapsedTime < 1) { elapsedTime=1; } + _timeLastObservation = currentTime; + + // update state transition and system covariance matrices + float delta_t = (float)elapsedTime / 1000000.0f; // convert to seconds + float delta_t_pow2 = delta_t * delta_t; + float delta_t_pow3 = delta_t_pow2 * delta_t; + float delta_t_pow4 = delta_t_pow2 * delta_t_pow2; + + K_pedal_geometry.F = {1.0, delta_t, 0.5 * delta_t * delta_t, + 0.0, 1.0, delta_t, + 0.0, 0.0, 1.0}; + + // measurement matrix. Size is + K_pedal_geometry.H = {1.0, 0.0, 0.0}; + + // model covariance matrix. Size is + /*K_pedal_geometry.Q = {1000, 0.0, 0.0, + 0.0, 1000, 0.0, + 0.0, 0.0, 1000};*/ + + // measurement covariance matrix. Size is + K_pedal_geometry.R = { 0.0001 }; + + /* + float K_Q_11 = KF_MODEL_NOISE_FORCE_ACCELERATION * 0.5f * delta_t_pow3; + float K_Q_12 = KF_MODEL_NOISE_FORCE_ACCELERATION * 0.5f * delta_t_pow2; + K_pedal_geometry.Q = { KF_MODEL_NOISE_FORCE_ACCELERATION * 0.25f * delta_t_pow4, K_Q_11, K_Q_12, + K_Q_11, KF_MODEL_NOISE_FORCE_ACCELERATION * delta_t_pow2, delta_t, + K_Q_12, delta_t, 1.0}; +*/ + + + // 1 * x + deltaT * x_d + 0.5 * deltaT^2 * x_dd + 1/6 * deltaT^3 * x_ddd + // 1 / 6 * delta_t * delta_t * delta_t + // 1 / 2 * delta_t * delta_t + // delta_t + + float Q11 = KF_MODEL_NOISE_FORCE_ACCELERATION * (1. / 6. * delta_t * delta_t * delta_t) * (1. / 6. * delta_t * delta_t * delta_t); + float Q12 = KF_MODEL_NOISE_FORCE_ACCELERATION * (1. / 6. * delta_t * delta_t * delta_t) * (1. / 2. * delta_t * delta_t); + float Q13 = KF_MODEL_NOISE_FORCE_ACCELERATION * (1. / 6. * delta_t * delta_t * delta_t) * (delta_t); + + float Q21 = Q12; + float Q22 = KF_MODEL_NOISE_FORCE_ACCELERATION * (1. / 2. * delta_t * delta_t) * (1. / 2. * delta_t * delta_t); + float Q23 = KF_MODEL_NOISE_FORCE_ACCELERATION * (1. / 2. * delta_t * delta_t) * (delta_t); + + float Q31 = Q13; + float Q32 = Q23; + float Q33 = KF_MODEL_NOISE_FORCE_ACCELERATION * (delta_t) * (delta_t); + + + + + + K_pedal_geometry.Q = { Q11, Q12, Q12, + Q21, Q21, Q21, + Q31, Q32, Q33}; + + + + // APPLY KALMAN FILTER + K_pedal_geometry.update({pedalInclineAngleDeg_global}); + float pedalPos = K_pedal_geometry.x(0,0); + float pedalVel = K_pedal_geometry.x(0,1); + float pedalAccel = K_pedal_geometry.x(0,2); + + + //Serial.print("alpha2: "); + //Serial.print(pedalInclineAngleDeg_global * RAD_TO_DEG); + //Serial.print(", pedalPos: "); + //Serial.print(pedalPos * RAD_TO_DEG); + //Serial.print(", pedalVel: "); + //Serial.print(pedalVel); + //Serial.print(", pedalAccel: "); + + //Serial.print(", "); + //Serial.print(pedalAccel); + //Serial.println(" "); + + return pedalAccel * RAD_TO_DEG; + + } diff --git a/ESP32/src/SignalFilter.cpp b/ESP32/src/SignalFilter.cpp new file mode 100644 index 00000000..154e0915 --- /dev/null +++ b/ESP32/src/SignalFilter.cpp @@ -0,0 +1,76 @@ +#include "SignalFilter.h" + + + + + +// v = s / t +// a = v/t +// a = s / t^2 +// a = 300 / delta_t^2 +// adjust model noise here s = 0.5 * a * delta_t^2 --> a = 2 * s / delta_t^2 +//static const float KF_MODEL_NOISE_FORCE_ACCELERATION = ( 2.0f * 1000.0f / 0.05f/ 0.05f ); +static const double KF_MODEL_NOISE_FORCE_ACCELERATION = ( 8.0f * 4.0f / 0.1f/ 0.1f ); + + +KalmanFilter::KalmanFilter(float varianceEstimate) + : _timeLastObservation(micros()) +{ + // evolution matrix. Size is + _K.F = {(double)1.0, 0.0, + 0.0, (double)1.0}; + + // command matrix. Size is + _K.B = {1.0, + 0.0}; + + // measurement matrix. Size is + _K.H = {1.0, 0.0}; + + // model covariance matrix. Size is + _K.Q = {1.0, 0.0, + 0.0, 1.0}; + + // measurement covariance matrix. Size is + _K.R = { varianceEstimate }; +} + +float KalmanFilter::filteredValue(float observation, float command, uint8_t modelNoiseScaling_u8) { + // obtain time + unsigned long currentTime = micros(); + unsigned long elapsedTime = currentTime - _timeLastObservation; + double modelNoiseScaling_fl32 = modelNoiseScaling_u8; + modelNoiseScaling_fl32 /= 255.0; + + if (modelNoiseScaling_fl32 < 0.001) + { + modelNoiseScaling_fl32 = 0.001; + } + if (elapsedTime < 1) { elapsedTime=1; } + _timeLastObservation = currentTime; + + // update state transition and system covariance matrices + double delta_t = ((double)elapsedTime) / 1000000.0f;/// 1000000.0f; // convert to seconds + double delta_t_pow2 = delta_t * delta_t; + double delta_t_pow3 = delta_t_pow2 * delta_t; + double delta_t_pow4 = delta_t_pow2 * delta_t_pow2; + + _K.F = {(double)1.0, delta_t, + 0.0, (double)1.0}; + + _K.B = {1.0, + 0.0}; + + double K_Q_11 = modelNoiseScaling_fl32 * KF_MODEL_NOISE_FORCE_ACCELERATION * (double)0.5f * delta_t_pow3; + _K.Q = {modelNoiseScaling_fl32 * KF_MODEL_NOISE_FORCE_ACCELERATION * (double)0.25f * delta_t_pow4, K_Q_11, + K_Q_11, modelNoiseScaling_fl32 * KF_MODEL_NOISE_FORCE_ACCELERATION * delta_t_pow2}; + + + // APPLY KALMAN FILTER + _K.update({observation}, {command}); + return _K.x(0,0); +} + +float KalmanFilter::changeVelocity() { + return _K.x(0,1) / 1.0f; +} diff --git a/ESP32/src/SignalFilter_2nd_order.cpp b/ESP32/src/SignalFilter_2nd_order.cpp new file mode 100644 index 00000000..1347cd7a --- /dev/null +++ b/ESP32/src/SignalFilter_2nd_order.cpp @@ -0,0 +1,100 @@ +#include "SignalFilter_2nd_order.h" + + + + + +// v = s / t +// a = v/t +// a = s / t^2 +// a = 300 / delta_t^2 +// adjust model noise here s = 1/6 * j * delta_t^3 --> j = 6 * s / delta_t^3 +//static const float KF_MODEL_NOISE_FORCE_ACCELERATION = ( 2.0f * 1000.0f / 0.05f/ 0.05f ); +static const float KF_MODEL_NOISE_FORCE_JERK = 2000 * ( 2.0f * 4.0f / 0.1f/ 0.1f ); + +//static const float KF_MODEL_NOISE_FORCE_ACCELERATION = 180 * 1e6;//( 2.0f * 4.0f / 0.1f/ 0.1f ); + + +KalmanFilter_2nd_order::KalmanFilter_2nd_order(float varianceEstimate) + : _timeLastObservation(micros()) +{ + // evolution matrix. Size is + _K.F = {1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0}; + + // command matrix. Size is + _K.B = {1.0, + 0.0, + 0.0 }; + + // measurement matrix. Size is + _K.H = {1.0, 0.0, 0.0}; + + // model covariance matrix. Size is + _K.Q = {1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0}; + + // measurement covariance matrix. Size is + _K.R = { varianceEstimate }; +} + +float KalmanFilter_2nd_order::filteredValue(float observation, float command, uint8_t modelNoiseScaling_u8) { + // obtain time + unsigned long currentTime = micros(); + unsigned long elapsedTime = currentTime - _timeLastObservation; + float modelNoiseScaling_fl32 = modelNoiseScaling_u8; + modelNoiseScaling_fl32 /= 255.0; + + if (modelNoiseScaling_fl32< 0.001) + { + modelNoiseScaling_fl32 = 0.001; + } + if (elapsedTime < 1) { elapsedTime=1; } + _timeLastObservation = currentTime; + + // update state transition and system covariance matrices + float delta_t = (float)elapsedTime / 1000000.0f; // convert to seconds + float delta_t_pow2 = delta_t * delta_t; + float delta_t_pow3 = delta_t_pow2 * delta_t; + float delta_t_pow4 = delta_t_pow2 * delta_t_pow2; + + _K.F = {1.0, delta_t, 0.5f * delta_t * delta_t, + 0.0, 1.0, delta_t, + 0.0, 0.0, 1.0}; + + _K.B = {1.0, + 0.0, + 0.0}; + + float Q11 = KF_MODEL_NOISE_FORCE_JERK * (1. / 6. * delta_t * delta_t * delta_t) * (1. / 6. * delta_t * delta_t * delta_t); + float Q12 = KF_MODEL_NOISE_FORCE_JERK * (1. / 6. * delta_t * delta_t * delta_t) * (1. / 2. * delta_t * delta_t); + float Q13 = KF_MODEL_NOISE_FORCE_JERK * (1. / 6. * delta_t * delta_t * delta_t) * (delta_t); + + float Q21 = Q12; + float Q22 = KF_MODEL_NOISE_FORCE_JERK * (1. / 2. * delta_t * delta_t) * (1. / 2. * delta_t * delta_t); + float Q23 = KF_MODEL_NOISE_FORCE_JERK * (1. / 2. * delta_t * delta_t) * (delta_t); + + float Q31 = Q13; + float Q32 = Q23; + float Q33 = KF_MODEL_NOISE_FORCE_JERK * (delta_t) * (delta_t); + + _K.Q = { Q11, Q12, Q13, + Q21, Q22, Q23, + Q31, Q32, Q33}; + + + + // APPLY KALMAN FILTER + _K.update({observation}, {command}); + return _K.x(0,0); +} + +float KalmanFilter_2nd_order::changeVelocity() { + return _K.x(0,1); +} + +float KalmanFilter_2nd_order::changeAccel() { + return _K.x(0,2); +} diff --git a/ESP32/src/StepperWithLimits.cpp b/ESP32/src/StepperWithLimits.cpp new file mode 100644 index 00000000..8262d1ec --- /dev/null +++ b/ESP32/src/StepperWithLimits.cpp @@ -0,0 +1,303 @@ +#include "StepperWithLimits.h" +#include "RTDebugOutput.h" +#include "Main.h" +#include "Math.h" + + +#define STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT 20 +#define MIN_POS_MAX_ENDSTOP STEPS_PER_MOTOR_REVOLUTION * 3 // servo has to drive minimum N steps before it allows the detection of the max endstop + +static const uint8_t LIMIT_TRIGGER_VALUE = LOW; // does endstop trigger high or low +static const int32_t ENDSTOP_MOVEMENT = STEPS_PER_MOTOR_REVOLUTION / 100; // how much to move between trigger checks +static const int32_t ENDSTOP_MOVEMENT_SENSORLESS = ENDSTOP_MOVEMENT * 5; + + +FastAccelStepperEngine& stepperEngine() { + static FastAccelStepperEngine myEngine = FastAccelStepperEngine(); // this is a factory and manager for all stepper instances + + static bool firstTime = true; + if (firstTime) { + myEngine.init(); + firstTime = false; + } + + return myEngine; +} + + + +StepperWithLimits::StepperWithLimits(uint8_t pinStep, uint8_t pinDirection, uint8_t pinMin, uint8_t pinMax, bool invertMotorDir_b) + : _pinMin(pinMin), _pinMax(pinMax) + , _limitMin(0), _limitMax(0) + , _posMin(0), _posMax(0) +{ + + + pinMode(pinMin, INPUT); + pinMode(pinMax, INPUT); + + + _stepper = stepperEngine().stepperConnectToPin(pinStep); + + + + // Stepper Parameters + if (_stepper) { + _stepper->setDirectionPin(pinDirection, invertMotorDir_b); + _stepper->setAutoEnable(true); + _stepper->setSpeedInHz(MAXIMUM_STEPPER_SPEED); // steps/s + _stepper->setAcceleration(MAXIMUM_STEPPER_ACCELERATION); // steps/s² + +//#if defined(SUPPORT_ESP32_PULSE_COUNTER) +// _stepper->attachToPulseCounter(1, 0, 0); +//#endif + } +} + + +void StepperWithLimits::findMinMaxSensorless(isv57communication * isv57, DAP_config_st dap_config_st) +{ + + if (! hasValidStepper()) return; + + // obtain servo states + isv57->readServoStates(); + bool endPosDetected = abs( isv57->servo_current_percent) > STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT; + + + int32_t setPosition = _stepper->getCurrentPosition(); + while(!endPosDetected){ + delay(10); + isv57->readServoStates(); + endPosDetected = abs( isv57->servo_current_percent) > STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT; + + setPosition = setPosition - ENDSTOP_MOVEMENT_SENSORLESS; + _stepper->moveTo(setPosition, true); + + //Serial.print("Min_DetValue: "); + //Serial.println(isv57->servo_current_percent); + } + + // move away from min position to reduce servos current reading + _stepper->forceStop(); + setPosition = setPosition + 5 * ENDSTOP_MOVEMENT_SENSORLESS; + _stepper->moveTo(setPosition, true); + _stepper->forceStopAndNewPosition(0); + _stepper->moveTo(0); + _limitMin = 0; + + // wait N ms to let the endPosDetected become 0 again + //delay(300); + + // read servo states again + //isv57->readServoStates(); + endPosDetected = 0;//abs( isv57->servo_current_percent) > STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT; + + setPosition = _stepper->getCurrentPosition(); + + // calculate max steps for endstop limit + float spindlePitch = max( dap_config_st.payLoadPedalConfig_.spindlePitch_mmPerRev_u8, (uint8_t)1 ); + float maxRevToReachEndPos = (float)dap_config_st.payLoadPedalConfig_.lengthPedal_travel / spindlePitch; + float maxStepsToReachEndPos = maxRevToReachEndPos * STEPS_PER_MOTOR_REVOLUTION; + + Serial.print("Max travel steps: "); + Serial.println(maxStepsToReachEndPos); + + while (!endPosDetected) { + delay(10); + isv57->readServoStates(); + + // only trigger when difference is significant + if (setPosition > MIN_POS_MAX_ENDSTOP) + { + endPosDetected = abs( isv57->servo_current_percent) > STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT; + } + + // trigger endstop if configured max travel is reached + if (setPosition > maxStepsToReachEndPos) + { + endPosDetected = true; + } + + + setPosition = setPosition + ENDSTOP_MOVEMENT_SENSORLESS; + _stepper->moveTo(setPosition, true); + + //Serial.print("Max_DetValue: "); + //Serial.println(isv57->servo_current_percent); + } + + //_stepper->forceStop(); + //setPosition = setPosition - 5 * ENDSTOP_MOVEMENT; + + _limitMax = _stepper->getCurrentPosition(); + + // reduce speed and accelerartion + _stepper->setSpeedInHz(MAXIMUM_STEPPER_SPEED / 4); + _stepper->setAcceleration(MAXIMUM_STEPPER_ACCELERATION / 4); + + // move to min + _stepper->moveTo(_posMin, true); + + // increase speed and accelerartion + _stepper->setAcceleration(MAXIMUM_STEPPER_ACCELERATION); + _stepper->setSpeedInHz(MAXIMUM_STEPPER_SPEED); + + + + +#if defined(SUPPORT_ESP32_PULSE_COUNTER) + _stepper->clearPulseCounter(); +#endif + + +} + + +void StepperWithLimits::findMinMaxEndstops() { + if (! hasValidStepper()) return; + + int32_t setPosition = _stepper->getCurrentPosition(); + while(! (LIMIT_TRIGGER_VALUE == digitalRead(_pinMin))){ + setPosition = setPosition - ENDSTOP_MOVEMENT; + _stepper->moveTo(setPosition, true); + } + + + _stepper->forceStopAndNewPosition(0); + _stepper->moveTo(0); + _limitMin = 0; + + setPosition = _stepper->getCurrentPosition(); + while(! (LIMIT_TRIGGER_VALUE == digitalRead(_pinMax))){ + setPosition = setPosition + ENDSTOP_MOVEMENT; + _stepper->moveTo(setPosition, true); + } + + _limitMax = _stepper->getCurrentPosition(); + + _stepper->moveTo(_posMin, true); +#if defined(SUPPORT_ESP32_PULSE_COUNTER) + _stepper->clearPulseCounter(); +#endif +} + +void StepperWithLimits::updatePedalMinMaxPos(uint8_t pedalStartPosPct, uint8_t pedalEndPosPct) { + int32_t limitRange = _limitMax - _limitMin; + _posMin = _limitMin + ((limitRange * pedalStartPosPct) / 100); + _posMax = _limitMin + ((limitRange * pedalEndPosPct) / 100); +} + +void StepperWithLimits::refindMinLimit() { + int32_t setPosition = _stepper->getCurrentPosition(); + while(! (LIMIT_TRIGGER_VALUE == digitalRead(_pinMin))){ + setPosition = setPosition - ENDSTOP_MOVEMENT; + _stepper->moveTo(setPosition, true); + } + _stepper->forceStopAndNewPosition(_limitMin); +} + +void StepperWithLimits::refindMinLimitSensorless(isv57communication * isv57) { + + // obtain servo states + isv57->readServoStates(); + bool endPosDetected = abs( isv57->servo_current_percent) > STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT; + + + int32_t setPosition = _stepper->getCurrentPosition(); + while(!endPosDetected){ + delay(10); + isv57->readServoStates(); + endPosDetected = abs( isv57->servo_current_percent) > STEPPER_WITH_LIMITS_SENSORLESS_CURRENT_THRESHOLD_IN_PERCENT; + + setPosition = setPosition - ENDSTOP_MOVEMENT_SENSORLESS; + _stepper->moveTo(setPosition, true); + + //Serial.print("Min_DetValue: "); + //Serial.println(isv57->servo_current_percent); + } + + // move away from min position to reduce servos current reading + _stepper->forceStop(); + setPosition = setPosition + 5 * ENDSTOP_MOVEMENT_SENSORLESS; + _stepper->moveTo(setPosition, true); + _stepper->forceStopAndNewPosition(_limitMin); +} + +void StepperWithLimits::checkLimitsAndResetIfNecessary() { + // in case the stepper loses its position and therefore an endstop is triggered reset position + if (LIMIT_TRIGGER_VALUE == digitalRead(_pinMin)) { + _stepper->forceStopAndNewPosition(_limitMin); + _stepper->moveTo(_posMin, true); + } + if (LIMIT_TRIGGER_VALUE == digitalRead(_pinMax)) { + _stepper->forceStopAndNewPosition(_limitMin); + _stepper->moveTo(_posMax, true); + } +} + +int8_t StepperWithLimits::moveTo(int32_t position, bool blocking) { + return _stepper->moveTo(position, blocking); +} + +int32_t StepperWithLimits::getCurrentPositionFromMin() const { + return _stepper->getCurrentPosition() - _posMin; +} + +int32_t StepperWithLimits::getCurrentPosition() const { + return _stepper->getCurrentPosition(); +} + + +double StepperWithLimits::getCurrentPositionFraction() const { + return double(getCurrentPositionFromMin()) / getTravelSteps(); +} + +double StepperWithLimits::getCurrentPositionFractionFromExternalPos(int32_t extPos_i32) const { + return (double(extPos_i32) - _posMin)/ getTravelSteps(); +} + +int32_t StepperWithLimits::getTargetPositionSteps() const { + return _stepper->getPositionAfterCommandsCompleted(); +} + + +void StepperWithLimits::printStates() +{ + int32_t currentStepperPos = _stepper->getCurrentPosition(); + int32_t currentStepperVel = _stepper->getCurrentSpeedInUs(); + int32_t currentStepperVel2 = _stepper->getCurrentSpeedInMilliHz(); + + + //Serial.println(currentStepperVel); + + int32_t currentStepperAccel = _stepper->getCurrentAcceleration(); + + static RTDebugOutput rtDebugFilter({ "Pos", "Vel", "Vel2", "Accel"}); + rtDebugFilter.offerData({ currentStepperPos, currentStepperVel, currentStepperVel2, currentStepperAccel}); +} + + +void StepperWithLimits::setSpeed(uint32_t speedInStepsPerSecond) +{ + _stepper->setSpeedInHz(speedInStepsPerSecond); // steps/s +} + +bool StepperWithLimits::isAtMinPos() +{ + + bool isNotRunning = !_stepper->isRunning(); + bool isAtMinPos = getCurrentPositionFromMin() == 0; + + return isAtMinPos && isNotRunning; +} +bool StepperWithLimits::correctPos(int32_t posOffset) +{ + // + int32_t stepOffset =(int32_t)constrain(posOffset, -10, 10); + + // correct pos + _stepper->setCurrentPosition(_stepper->getCurrentPosition() + stepOffset); + return 1; +} + diff --git a/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.bin b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.bin new file mode 100644 index 00000000..1955c178 Binary files /dev/null and b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.bin differ diff --git a/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.bootloader.bin b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.bootloader.bin new file mode 100644 index 00000000..cf090f35 Binary files /dev/null and b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.bootloader.bin differ diff --git a/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.elf b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.elf new file mode 100644 index 00000000..e512fa31 Binary files /dev/null and b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.elf differ diff --git a/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.partitions.bin b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.partitions.bin new file mode 100644 index 00000000..2108af95 Binary files /dev/null and b/ESP32/src/build/esp32.esp32.lolin_s2_mini/Main.ino.partitions.bin differ diff --git a/ESP32/src/compile_commands.json b/ESP32/src/compile_commands.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/ESP32/src/compile_commands.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/ESP32/src/isv57communication.cpp b/ESP32/src/isv57communication.cpp new file mode 100644 index 00000000..ab5ae7ed --- /dev/null +++ b/ESP32/src/isv57communication.cpp @@ -0,0 +1,209 @@ +#include "isv57communication.h" +#include "Main.h" + +Modbus modbus(Serial1); + + +// initialize the communication +isv57communication::isv57communication() +{ + Serial1.begin(38400, SERIAL_8N1, ISV57_RXPIN, ISV57_TXPIN, true); // Modbus serial + modbus.init(MODE); +} + + + + +// send tuned servo parameters +void isv57communication::setupServoStateReading() { + + + // The iSV57 has four registers (0x0191, 0x0192, 0x0193, 0x0194) in which we can write, which values we want to obtain cyclicly + // These registers can be obtained by sending e.g. the command: 0x63, 0x03, 0x0191, target_sate, CRC + // tell the modbus slave, which registers will be read cyclicly + modbus.holdingRegisterWrite(slaveId, 0x0191, reg_add_position_given_p); + delay(50); + modbus.holdingRegisterWrite(slaveId, 0x0192, reg_add_velocity_current_feedback_percent); + delay(50); + modbus.holdingRegisterWrite(slaveId, 0x0193, reg_add_position_error_p); + delay(50); + modbus.holdingRegisterWrite(slaveId, 0x0194, reg_add_voltage_0p1V); + delay(50); + + +} + + +void isv57communication::readAllServoParameters() { + for (uint16_t reg_sub_add_u16 = 0; reg_sub_add_u16 < pr_6_00; reg_sub_add_u16++) + { + modbus.readParameter(slaveId, pr_0_00 + reg_sub_add_u16); + } +} + +// send tuned servo parameters +void isv57communication::sendTunedServoParameters() { + + bool retValue_b = false; + + + // Pr0 register + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+1, 0); // control mode + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+2, 0); // deactivate auto gain + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+3, 10); // machine stiffness + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+4, 80); // ratio of inertia + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+8, 1600); // microsteps + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+9, 1); // 1st numerator + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+10, 1); // & denominator + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+13, 500); // 1st torque limit + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+14, 500); // position deviation setup + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+16, 50); // regenerative braking resitor + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+17, 50); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+18, 0); // vibration suppression + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_0_00+19, 0); + + // Pr1 register + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+0, 600); // 1st position gain + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+1, 300); // 1st velocity loop gain + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+2, 300); // 1st time constant of velocity loop + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+3, 15); // 1st filter of velocity detection + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+4, 150); // 1st torque filter + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+10, 200); // velocity feed forward gain + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+11, 6000); // velocity feed forward filter + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+12, 0); // torque feed forward gain + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+13, 0); // torque feed forward filter + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+15, 0); // control switching mode + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+33, 0); // speed given filter + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+35, 0); // position command filter + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+36, 0); // encoder feedback + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_1_00+37, 1052); // special function register + // see https://www.oyostepper.com/images/upload/File/ISV57T-180.pdf + // 0x01 = 1: velocity feedforward disabled + // 0x02 = 2: torque feedforward disabled + // 0x04 = 4: motor overspeed alarm disabled + // 0x08 = 8: position following alarm disabled + // 0x10 = 16: overload alarm disabled + // 0x400 = 1024: undervoltage disabled + + // Pr2 register + // vibration suppression + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+1, 50); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+2, 20); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+3, 99); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+4, 90); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+5, 20); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+6, 99); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+22, 0); + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+23, 0); + + // Pr3 register + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_3_00+24, 5000); // maximum rpm + + // Pr5 register + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_5_00+13, 5000); // overspeed level + retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_5_00+20, 1); // encoder output resolution + + + + // store the settings to servos NVM if necesssary + if (retValue_b) + { + Serial.println("Servo registered in NVM have been updated! Please power cycle the servo and the ESP!"); + modbus.holdingRegisterWrite(slaveId, 0x019A, 0x5555); // store the settings to servos NVM + delay(2000); + } + +} + +bool isv57communication::findServosSlaveId() +{ + bool slaveIdFound = false; + for (int slaveIdTest = 0; slaveIdTest<256; slaveIdTest++) + { + if(modbus.requestFrom(slaveIdTest, 0x03, 0x0000, 2) > 0) + { + slaveId = slaveIdTest; + slaveIdFound = true; + Serial.print("Found servo slave ID:"); + Serial.print(slaveId); + Serial.print("\r\n"); + break; + } + } + return slaveIdFound; +} + + + + +bool isv57communication::checkCommunication() +{ + if(modbus.requestFrom(slaveId, 0x03, 0x0000, 2) > 0) + { + //Serial.println("Lifeline check: true"); + return true; + } + else + { + //Serial.println("Lifeline check: false"); + return false; + } +} + + + +void isv57communication::setZeroPos() +{ + zeroPos = servo_pos_given_p; +} + +int16_t isv57communication::getZeroPos() +{ + return zeroPos; +} + + +// read servo states +void isv57communication::readServoStates() { + + // read the four registers simultaneously + int8_t numberOfRegistersToRead_u8 = 4; + int bytesReceived_i = modbus.requestFrom(slaveId, 0x03, ref_cyclic_read_0, numberOfRegistersToRead_u8); + if(bytesReceived_i == (numberOfRegistersToRead_u8*2)) + { + modbus.RxRaw(raw, len); + for (uint8_t regIdx = 0; regIdx < numberOfRegistersToRead_u8; regIdx++) + { + regArray[regIdx] = modbus.uint16(regIdx); + } + + // write to public variables + servo_pos_given_p = regArray[0]; + servo_current_percent = regArray[1]; + servo_pos_error_p = regArray[2]; + servo_voltage_0p1V = regArray[3]; + } + //Serial.print("Bytes :"); + //Serial.println(bytesReceived_i); + + + + // print registers + if (0) + { + Serial.print("Pos_given:"); + Serial.print(servo_pos_given_p); + + Serial.print(",Pos_error:"); + Serial.print(servo_pos_error_p); + + Serial.print(",Cur_given:"); + Serial.print(servo_current_percent); + + Serial.print(",Voltage:"); + Serial.print(servo_voltage_0p1V); + + Serial.println(" "); + } + +} diff --git a/ESP32/src/ota.h b/ESP32/src/ota.h new file mode 100644 index 00000000..06679378 --- /dev/null +++ b/ESP32/src/ota.h @@ -0,0 +1,222 @@ +#pragma once +//OTA update +//source:https://lastminuteengineers.com/esp32-ota-web-updater-arduino-ide/ +//https://github.com/italocjs/ESP32_OTA_APMODE +#include +//#include +#include +//#include +#include + + + +//const char *host = "esp32"; +//const char *new_ssid = "ADD_WANTED_SSID_HERE"; +const char *password = "pedaladmin"; +WebServer server(80); +//OTA update page +#define jquery_min_js_v3_2_1_gz_len 30178 +PROGMEM const char jquery_min_js_v3_2_1_gz[] = { +0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0xcc,0xbd,0x69,0x77,0x1b,0x47,0x92,0x36,0xfa,0xfd,0xfd,0x15,0x44,0xb5,0x86,0x5d,0x25,0x24,0x41,0x50,0xb6,0xfb,0x9d,0x2e,0xa8,0x84,0x63,0x5b,0x56,0x5b,0x3d,0xde,0xda,0x92,0x97,0x1e,0x10,0xf6,0xa9,0x0d,0x40,0x81,0xd8,0x08,0x80,0xa2,0x64,0x02,0xf3,0xdb,0x6f,0x3c,0x11,0x99,0x59,0x59,0x0b,0x68,0xf7,0xcc,0x9d,0x73,0xaf,0x17,0xa2,0x96,0xac,0x5c,0x23,0x23,0x63,0x8f,0xcb,0xa7,0x9d,0xb3,0xf9,0x3f,0xee,0xf2,0xed,0x87,0xb3,0x77,0x1f,0xf5,0x9e,0xf5,0xae,0xce,0x0e,0x67,0x7e,0x1a,0x9c,0xfd,0xfd,0xcd,0xd9,0xab,0xf5,0xdd,0x2a,0x8b,0xf7,0xc5,0x7a,0x75,0x16,0xaf,0xb2,0xb3,0xf5,0x7e,0x96,0x6f,0xcf,0xd2,0xf5,0x6a,0xbf,0x2d,0x92,0xbb,0xfd,0x7a,0xbb,0xa3,0xa2,0xf3,0x5b,0x7c,0xda,0x5b,0x6f,0xa7,0x97,0x8b,0x22,0xcd,0x57,0xbb,0xfc,0xec,0xe9,0xe5,0xff,0xe9,0x4c,0xee,0x56,0x29,0x3e,0xf4,0x63,0x95,0x04,0x0f,0xde,0x1d,0x3d,0xde,0xd1,0x67,0xe9,0xde,0x1b,0x78,0xeb,0x64,0x9e,0xd3,0x45,0x14,0xed,0x3f,0x6c,0xf2,0xf5,0xe4,0x6c,0xb9,0xce,0xee,0x16,0xf9,0xf9,0xf9,0x89,0x17,0xbd,0xfc,0xfd,0x66,0xbd,0xdd,0xef,0x86,0xd5,0xdb,0x28,0xee,0x65,0xeb,0xf4,0x6e,0x99,0xaf,0xf6,0xc3,0x84,0x9a,0xe9,0xf4,0x83,0xb0,0x6c,0x35,0x78,0x28,0x26,0x7e,0xa7,0x2c,0x12,0xec,0x67,0xdb,0xf5,0xfd,0xd9,0x2a,0xbf,0x3f,0xfb,0x62,0xbb,0x5d,0x6f,0x7d,0x4f,0x8f,0x79,0x9b,0xdf,0xde,0x15,0xdb,0x7c,0x77,0x16,0x9f,0xdd,0x17,0xab,0x8c,0xca,0xdc,0x17,0xfb,0x19,0xdd,0x99,0x2f,0xbd,0x60,0xb0,0xcd,0xf7,0x77,0xdb,0xd5,0x19,0xb5,0x12,0x1c,0x43,0xfe,0xeb,0x7b,0x34,0x33,0xf9,0xa4,0x58,0xe5,0x99,0xd7,0x31,0xdd,0x95,0xef,0x87,0xf2,0x13,0xee,0x67,0xc5,0x4e,0x3d,0x32,0x0d,0xef,0x62,0x9a,0xcb,0x68,0x34,0x56,0x99,0x33,0x12,0x95,0x47,0xdf,0xf2,0x24,0xf4,0xa6,0xf9,0xfe,0xbb,0xed,0x7a,0xbf,0x46,0xdd,0xdf,0x4e,0xd4,0x24,0x4a,0x7b,0x3b,0x4c,0xb0,0x9a,0xd2,0x15,0xad,0x41,0x1a,0xef,0xd5,0x8c,0x2e,0x37,0x77,0xbb,0x99,0x2a,0xe8,0x82,0x5a,0xcd,0xdf,0x53,0xc9,0x79,0xf4,0x70,0x54,0x37,0xd1,0xbc,0xb7,0x5f,0xbf,0xa1,0xa6,0x56,0x53,0xb5,0xa0,0x9b,0x59,0xbc,0xfb,0xf6,0x7e,0x45,0x35,0x6e,0xf2,0xed,0xfe,0x83,0x5a,0x46,0x8b,0xf2,0xfd,0x2a,0x5a,0xf6,0xd2,0x78,0xb1,0xf0,0xa5,0xe9,0x40,0xad,0xa9,0x8a,0x81,0xe9,0xfa,0xd9,0x46,0x3a,0x9f,0x44,0xc9,0xe1,0x90,0xe9,0x6e,0x27,0xbd,0x74,0x9b,0xc7,0xfb,0xfc,0x8b,0x45,0x8e,0x6e,0xfb,0xde,0x2e,0xdd,0x16,0x1b,0xcc,0x55,0xda,0xdb,0xe7,0xef,0xf7,0x11,0x7d,0xd2,0x9b,0xe5,0x71,0xd6,0x8b,0x37,0x9b,0x7c,0x95,0x7d,0x3e,0x2b,0x16,0x19,0x41,0x55,0x6f,0x13,0x6f,0xa9,0xfc,0x37,0xeb,0x2c,0xef,0x6d,0xf3,0xe5,0xfa,0x5d,0x6e,0xde,0x1c,0x51,0xf1,0x6d,0xe4,0x31,0x08,0x7a,0x6a,0x1b,0x55,0xa7,0x4e,0x2f,0x01,0xd6,0x6f,0xdb,0x9b,0xac,0x68,0xb4,0xc5,0x9e,0xdf,0x1c,0xd5,0x2e,0xba,0xfc,0x65,0x74,0xbd,0xbb,0xbe,0x7b,0xf5,0xc5,0xab,0x57,0xd7,0xef,0x3f,0xed,0x8f,0xbb,0x87,0xda,0xfd,0x93,0xcb,0xa9,0xda,0x53,0xb1,0x8b,0xe5,0xee,0xe2,0x52,0xdd,0x45,0x97,0x17,0xfe,0x28,0xbe,0xf8,0x6d,0x1c,0xd0,0xf3,0x77,0xed,0x2d,0x25,0x34,0x3f,0x3f,0x50,0xdf,0xb7,0x9f,0xc7,0xbb,0xdc,0x0f,0x8e,0x03,0x34,0x1b,0x6d,0x7b,0x1b,0xb3,0x28,0xd1,0x83,0x80,0x7e,0x78,0xab,0x68,0x39,0x68,0x55,0xef,0x52,0xda,0x12,0xe1,0x56,0x2d,0xf2,0xd5,0x74,0x3f,0x0b,0xfb,0x6a,0xbf,0xfe,0x74,0xbb,0x8d,0x3f,0x94,0x50,0x69,0x2b,0x9f,0xc8,0x7c,0x03,0x44,0x68,0x00,0xb4,0xd4,0x15,0xc8,0x35,0x63,0xbd,0x5b,0x2c,0xa2,0x28,0x1e,0xba,0x85,0xc3,0xf8,0x79,0x7f,0x88,0xab,0x51,0xdc,0xc5,0x4f,0x4f,0x1a,0x1b,0x87,0xf2,0x6c,0x7c,0x54,0x80,0x87,0x37,0xfb,0x38,0xbd,0xa9,0x54,0x89,0xc9,0x4d,0xa8,0xf7,0xcb,0x7c,0x3b,0xcd,0xb9,0xaa,0x9e,0xd3,0x69,0x3f,0x50,0x71,0x09,0xe5,0x34,0xc4,0xfc,0x9d,0x80,0x42,0xc4,0x40,0x9c,0x1c,0x55,0x1e,0xa7,0xb3,0xb6,0x3e,0x6e,0x7b,0x78,0xc3,0x15,0x52,0x15,0x47,0xb5,0x8c,0x37,0x6d,0xc5,0xb8,0x41,0xdb,0x33,0x9f,0xfa,0x11,0x6f,0xfc,0xea,0x06,0x49,0x54,0x6a,0x8b,0xc7,0x32,0x62,0x7a,0x84,0x05,0x0e,0xb0,0xc6,0x00,0xfd,0x96,0x89,0xac,0x55,0x3c,0x01,0xb8,0x2d,0x3e,0xe8,0xfe,0x6c,0xa7,0xbc,0xa7,0x76,0xa8,0x60,0x52,0x6c,0x77,0xfb,0x53,0x15,0xe4,0xb7,0x7e,0x9f,0xca,0x2c,0xe2,0x47,0x8b,0x5c,0x5c,0x51,0x99,0xfc,0xb6,0x65,0x5e,0x9d,0x95,0x50,0x69,0xd4,0x8d,0xbb,0x3e,0x96,0x29,0x09,0xfb,0x76,0x52,0x6b,0xfd,0x4c,0x5f,0x44,0xfd,0xf3,0xf3,0xf4,0x79,0x32,0x1c,0xf1,0xc2,0xa5,0xe3,0x71,0x38,0x1a,0xa3,0xfa,0x55,0x76,0x72,0x94,0x76,0x55,0x0e,0x87,0xe6,0x02,0xca,0xc2,0x87,0x33,0xb5,0x23,0xdc,0x18,0x12,0xae,0xa0,0x1f,0xb5,0xdb,0xf0,0xb4,0xd1,0x1d,0x5f,0x1c,0x15,0x2d,0xd7,0xfb,0x3d,0xb5,0x11,0xf1,0x1e,0xd2,0xd7,0x4e,0x7b,0x18,0x0e,0x6d,0x02,0x9a,0xf7,0x4c,0xe5,0x6a,0x42,0xb8,0xc6,0x4e,0xe2,0xa8,0x3f,0x3e,0x1c,0x08,0xb3,0xcc,0xa2,0x2b,0x42,0x37,0xf6,0xb1,0x19,0xf6,0x3c,0xea,0x5c,0x0d,0x26,0xc0,0xab,0xc9,0x7a,0xbd,0xc8,0xe3,0x55,0x89,0xc5,0xa7,0xe7,0xe7,0xfe,0x3c,0x9a,0x56,0x2a,0x9b,0xe9,0xca,0xba,0xdd,0x40,0x35,0xd0,0xfe,0xf4,0x70,0xd8,0xf6,0x8a,0xdd,0x2b,0xd3,0xaf,0x69,0x70,0x38,0xf8,0x53,0xc2,0x49,0x01,0xb5,0x1e,0x45,0x05,0xd5,0x37,0x15,0xc8,0x9c,0x5d,0x5c,0x04,0x83,0xd9,0xf3,0x62,0x80,0x8a,0x08,0xe1,0x63,0xcb,0x74,0x22,0x3f,0xae,0xb4,0x14,0x04,0xe8,0x57,0x72,0x56,0x10,0x60,0x05,0x69,0x34,0x1d,0x25,0x8c,0x73,0xf1,0x33,0xed,0x44,0x51,0x86,0xee,0x9d,0x9f,0xe3,0x07,0xad,0x7e,0xb7,0x88,0x8b,0x95,0xcc,0xb3,0x9f,0xa1,0xe1,0x3c, +0xe2,0x5d,0x4c,0xaf,0xf8,0x97,0x1e,0x06,0xc1,0xd0,0xcf,0xe9,0x3f,0x1a,0x32,0xf0,0xf2,0xf9,0x79,0xb5,0x40,0x1a,0x0c,0x53,0xac,0x66,0xc8,0xef,0xea,0x75,0xf2,0x5b,0x1a,0x3a,0xba,0x11,0x99,0xf5,0xf0,0xe7,0x34,0xd9,0x54,0x71,0xf8,0x6e,0x5d,0x64,0x67,0x7d,0xdd,0x2b,0x2e,0x42,0x4f,0x0d,0x10,0x4d,0xcb,0x05,0xf4,0x1f,0xe8,0x14,0xa4,0x33,0x79,0x1d,0xea,0x73,0xcc,0xeb,0xfa,0xb7,0xdd,0xaf,0xe3,0xfd,0xac,0xb7,0xc5,0xe3,0xa5,0x1f,0x04,0x84,0x60,0x37,0x8b,0x38,0xcd,0xfd,0xcb,0xeb,0x97,0x84,0xe7,0x3c,0x2f,0x50,0xc5,0xee,0x7b,0xc2,0xc9,0x1f,0xc2,0x4e,0x5f,0xe5,0x38,0x05,0x2b,0xb0,0x5c,0x3f,0x21,0xb1,0x9f,0x57,0xeb,0xf5,0xc6,0x05,0xc8,0xa3,0x2a,0xd7,0xa5,0x65,0xa3,0x7b,0xe6,0x11,0x2d,0x26,0x0d,0x0e,0xeb,0xc9,0xd5,0x14,0xbb,0x9f,0xe4,0x50,0x3c,0x81,0xe6,0x3a,0x51,0x7c,0x7e,0x1e,0xd3,0x37,0x71,0x4f,0x4e,0x4f,0x7c,0xf2,0x0d,0xad,0x20,0x1d,0x95,0xad,0x78,0x4c,0xd7,0xac,0x67,0xc6,0xf7,0x56,0x77,0xcb,0x24,0xdf,0xa2,0x55,0x3a,0xa4,0xbc,0x1d,0x9f,0x6b,0x7c,0x17,0x9c,0x9f,0x77,0xa8,0xaa,0xf8,0x1b,0x3f,0xbe,0xa0,0xb3,0x67,0x97,0xbf,0x5a,0xac,0x63,0x3a,0x39,0x02,0xee,0x95,0xb3,0x30,0xcd,0x66,0x54,0xaa,0xab,0xef,0x10,0x21,0x41,0xb5,0x8e,0x04,0x4e,0xcf,0xa4,0xfc,0x98,0xce,0xfd,0xe8,0x46,0x10,0x15,0xd5,0x46,0xcb,0xd5,0xf1,0x93,0x08,0x9d,0x02,0xd0,0xa4,0x74,0xbe,0x6a,0x1c,0xe6,0x39,0xfb,0xd4,0xa3,0x72,0x89,0xbb,0x71,0x95,0x3b,0x63,0x1a,0xfc,0x09,0x68,0xf4,0x79,0x9c,0x06,0x34,0x84,0x95,0x74,0xf5,0x8b,0xe5,0x66,0xff,0xe1,0x54,0x57,0x07,0x0e,0x84,0xeb,0x3e,0x5f,0x99,0xce,0xf7,0x8f,0x0a,0x15,0x3f,0x76,0xc2,0xc4,0x5d,0xcf,0x0b,0x1b,0xdb,0x10,0x63,0x6e,0xf6,0x2e,0x1e,0xce,0x47,0x76,0xd8,0xb4,0x8b,0xcd,0x67,0xa1,0x79,0x4f,0xa0,0xbd,0x58,0x27,0xf1,0xe2,0x8b,0x77,0xf1,0xa2,0xd2,0xe8,0x86,0x41,0x21,0x8d,0x97,0xf9,0x02,0x67,0x6a,0x5b,0x87,0x62,0x0b,0xb3,0x7b,0xe5,0xd1,0x59,0xed,0x95,0x40,0x7c,0xa7,0xde,0x05,0x8d,0x73,0x08,0xe7,0x35,0x13,0x24,0xb4,0xa1,0xfb,0x03,0x42,0x00,0xf7,0x98,0xff,0x07,0xcc,0x46,0x4a,0xb0,0x24,0xb8,0x69,0x90,0x3d,0x4f,0x07,0x99,0x20,0x88,0x44,0xf7,0x7c,0x94,0x11,0x0e,0x50,0xf8,0xc1,0x14,0x77,0xae,0x82,0x84,0xc8,0x99,0x9b,0x63,0xbe,0x20,0x02,0x0d,0x5f,0x67,0x32,0x97,0xbf,0xfb,0x85,0xd9,0x9a,0x34,0x6a,0x82,0xb9,0xe5,0x63,0x93,0x4c,0x53,0xec,0x63,0xa2,0xcb,0x21,0xed,0xb0,0x27,0x71,0x66,0xde,0xe4,0x35,0x42,0xa1,0x1c,0x18,0xe0,0x79,0x34,0x1e,0xd4,0xf7,0x0a,0x0d,0x54,0xe3,0x13,0x1a,0xef,0xd0,0x9c,0xeb,0xa9,0x2a,0x41,0xdf,0x2e,0x17,0x51,0x05,0x61,0x1c,0x84,0x33,0x0d,0x52,0x74,0x4c,0x07,0x2a,0x25,0x88,0x5a,0x35,0xdb,0x74,0x8e,0x60,0xe9,0x75,0x32,0xbc,0xb8,0x0a,0x0b,0x03,0xc9,0x31,0xbd,0xa7,0xee,0xa2,0xa9,0x5a,0x57,0x31,0x65,0xd2,0xdd,0x6e,0x62,0x4e,0x04,0x5a,0x11,0xa2,0x66,0x1b,0x8b,0x10,0x8f,0xf2,0x6e,0x77,0x1c,0x25,0x34,0x91,0x76,0xf2,0x74,0x99,0x28,0x57,0x80,0x1e,0x9a,0x9e,0x46,0xaf,0x4c,0x03,0x74,0x2c,0x81,0x64,0x9e,0x50,0xdd,0x53,0x5b,0x37,0x9d,0x0a,0x9d,0x74,0x30,0x79,0x3e,0x1d,0x4c,0xa8,0x81,0x2c,0xea,0x10,0x89,0x3e,0x9a,0x50,0xa9,0x40,0x65,0xb4,0x49,0x67,0xe7,0xe7,0x39,0x9f,0xbd,0xfc,0xd4,0x62,0xd3,0xbc,0x4e,0xad,0x48,0x4b,0xba,0x15,0x6e,0x62,0x46,0x6d,0x59,0xa8,0x62,0xb0,0x28,0xc7,0x33,0x79,0x9e,0x71,0x73,0x79,0x64,0x5a,0xa3,0xaf,0x95,0x2c,0x10,0xf1,0x32,0x33,0x69,0x31,0x0f,0x06,0x16,0xa6,0x26,0x02,0x53,0xbf,0xfb,0x81,0xc1,0xf6,0x9a,0x96,0xa1,0xe1,0xce,0x40,0x24,0xde,0x15,0x59,0x78,0xa5,0x88,0xfc,0x7c,0xdf,0x0a,0x28,0x38,0xb1,0xd1,0xd7,0x06,0x04,0x24,0x04,0x2d,0xa9,0x1c,0x77,0x09,0x91,0xe5,0x71,0x44,0xad,0x56,0x8e,0x57,0x1a,0x9b,0x6e,0x92,0x68,0x01,0x0d,0xed,0xe6,0xf8,0x54,0xcf,0x02,0x9a,0xf1,0x26,0x45,0x12,0xeb,0xce,0x25,0x42,0x8b,0xa8,0x4c,0xb3,0x24,0x7e,0xbd,0x82,0x00,0xd8,0x2b,0xef,0xa1,0xf7,0x34,0x77,0xce,0x0f,0xce,0x78,0xfc,0x76,0xbb,0x2a,0xc7,0x39,0x73,0x1f,0xbe,0x24,0x7e,0xa2,0x47,0x17,0x6a,0x77,0xb7,0x01,0x87,0x17,0xae,0xe9,0xc0,0x6f,0xc1,0x40,0x6f,0x3e,0x2c,0x93,0xf5,0x82,0x8f,0xeb,0xc9,0x6a,0x24,0x77,0xbd,0x62,0x9f,0x6f,0x63,0x42,0xa8,0xe3,0x28,0x6d,0x3c,0xc2,0x78,0x99,0x4c,0xf5,0x3e,0x13,0xda,0xe4,0xec,0x1b,0x3e,0x2e,0xce,0x84,0xff,0x39,0x33,0x33,0x71,0xc6,0x3b,0xe2,0x0c,0xfd,0x38,0xfb,0x3e,0x9f,0x7e,0xf1,0x7e,0xa3,0x71,0xbd,0x1c,0x88,0xba,0x61,0x8f,0xa9,0x29,0x62,0x79,0xce,0xe8,0x40,0xad,0x2e,0xc4,0x7c,0x64,0x0f,0x09,0xaf,0x9b,0x74,0xbd,0xb1,0x47,0x80,0x4e,0x7c,0xc4,0x57,0xeb,0x7b,0xcb,0x47,0x04,0x25,0x63,0x75,0x5f,0x9e,0x68,0x9d,0x0e,0x6d,0x6a,0x4f,0x40,0xcb,0x03,0x98,0xd0,0x71,0x58,0x52,0x94,0xf5,0xf3,0xae,0x9c,0x14,0x02,0x6f,0x3a,0x2e,0x3a,0x58,0x4f,0x39,0x61,0xa9,0x10,0xcd,0x8c,0x17,0x63,0x24,0x38,0x00,0xd3,0xc3,0xa1,0xaf,0x4f,0x45,0x7b,0x46,0x96,0x80,0x91,0xbc,0x20,0x32,0x34,0xb9,0xb8,0x12,0xc8,0x64,0x16,0xec,0x7d,0xd4,0x72,0x10,0x1a,0x72,0x50,0x11,0xab,0xa9,0xe6,0xea,0x46,0x2d,0xd4,0x52,0xad,0xd4,0x5a,0x6d,0xd4,0xad,0xda,0xaa,0x9d,0xda,0x13,0x53,0xe5,0xed,0x8a,0xdf,0x7e,0x5b,0xe4,0x5e,0xf7,0xea,0x29,0x88,0x08,0x4c,0x23,0x31,0x57,0x0e,0x6f,0x7b,0x4f,0x5b,0xea,0x3d,0xfd,0xff,0x21,0x9a,0xc5,0xc4,0x72,0xfc,0x26,0x3f,0x9f,0xca,0xcf,0x67,0xed,0x6c,0x18,0x88,0x02,0x40,0xf0,0x22,0x22,0xfe,0x5e,0xd1,0x59,0xf6,0x39,0xd1,0x81,0x75,0x6e,0xf6,0x25,0x70,0xc2,0x17,0xd1,0xcb,0xde,0x66,0xbd,0x51,0xaf,0xf0,0x0b,0xa6,0xf8,0x6f, +0xe6,0xe2,0x4b,0xba,0x10,0xde,0xf9,0x75,0x74,0x0a,0x6f,0xf5,0x95,0xb3,0xb7,0x53,0xda,0xdb,0xa9,0x1c,0x18,0x31,0xd1,0xe6,0x4c,0x47,0xe8,0xfe,0x18,0x9a,0xe0,0xe2,0xea,0xa8,0xfe,0x1e,0x79,0xe9,0x2c,0x4f,0x6f,0xf2,0xec,0xb0,0xcb,0x17,0xb4,0xea,0x74,0x11,0xef,0x3e,0xac,0xd2,0x43,0x7c,0xb7,0x5f,0x4f,0x68,0xd8,0x3b,0xbe,0x22,0x6c,0xff,0xe1,0xc0,0x62,0x93,0xf5,0x62,0x77,0xc8,0xf2,0x49,0xbe,0x3d,0x64,0xc5,0x2e,0x4e,0x16,0xf4,0xc1,0xac,0xc8,0xb2,0x7c,0x75,0x28,0x76,0x84,0x8c,0x0e,0x0b,0x22,0xb7,0x0e,0xcb,0xbb,0xc5,0xbe,0xd8,0x2c,0xf2,0x03,0x8d,0x6e,0x75,0xa0,0x53,0x26,0x5b,0xaf,0x16,0x1f,0x0e,0x5a,0x50,0x41,0x6d,0xa5,0xf4,0x22,0xf3,0xd4,0x7f,0x44,0xde,0xe8,0xfa,0xfa,0xfd,0xb3,0xfe,0xf5,0xf5,0xfe,0xfa,0x7a,0x7b,0x7d,0xbd,0xba,0xbe,0x9e,0x8c,0x3d,0xf5,0x55,0xe4,0xf9,0xc3,0xf0,0x9a,0xfe,0xe9,0x11,0x1f,0x7c,0x7d,0x7f,0x31,0x3e,0x8c,0x7e,0xb9,0xee,0x5f,0x50,0xd9,0xb8,0x3f,0x0e,0xba,0x9e,0xfa,0x3a,0xf2,0xae,0xaf,0x47,0x5e,0xf7,0x3f,0xba,0xde,0x53,0xdf,0xeb,0x7e,0xd5,0xf5,0x02,0xfa,0x42,0xdf,0x8f,0x9e,0xfe,0xf2,0xe4,0xd0,0xf9,0xaf,0xf1,0x30,0x0a,0xf4,0x93,0x61,0xf8,0x67,0xbf,0xac,0xf1,0x17,0xfc,0xfe,0x79,0x1c,0x3c,0x0d,0xfe,0x7c,0xb8,0xf6,0xea,0x2f,0xae,0x3d,0xbc,0xb9,0xf6,0x0e,0xba,0xde,0xe0,0xa0,0x6b,0xb9,0xbe,0xa6,0xae,0x7d,0x13,0xd1,0x21,0x68,0x1b,0xbc,0xbe,0xf6,0x7d,0xff,0x5f,0xaf,0x3a,0x38,0xd4,0xdf,0xf8,0x01,0x8d,0x73,0x3c,0x3e,0x78,0xdd,0xaf,0xa9,0xe6,0xa7,0xc1,0xa1,0x47,0xe5,0xae,0xd1,0xb4,0xfa,0x36,0x02,0x2c,0xca,0x6e,0xf6,0xa9,0x1f,0x34,0x7a,0x6f,0x4a,0x1b,0xf7,0x3b,0xf7,0xb9,0xf7,0x0b,0xf7,0xb1,0xcb,0x15,0xff,0xa2,0x2b,0x1d,0x07,0xa6,0x15,0xaa,0x51,0xde,0x3f,0xd1,0x1f,0xff,0xa3,0xe5,0xe3,0xa7,0x4a,0x7e,0xe8,0xf5,0xf7,0x6d,0xaf,0xfd,0xd1,0x8b,0xee,0x7f,0xa1,0x8b,0x74,0x13,0xd8,0xa2,0x6f,0x2a,0x45,0x23,0x53,0x94,0x3a,0x30,0xfe,0x33,0x8d,0xf7,0xe9,0xd0,0x9d,0x3d,0x6e,0xfb,0xad,0xfb,0xc5,0x37,0x81,0xfa,0xa1,0xde,0x18,0x4d,0xee,0x13,0x2a,0xf7,0x63,0xf4,0xf0,0xfa,0x65,0x58,0x79,0xf7,0x27,0x3d,0xf5,0xf4,0xf6,0xf3,0xaf,0x3e,0x7d,0xf3,0xa6,0xfa,0x96,0x06,0x5a,0xbe,0x7f,0xfb,0xe9,0xdf,0xaa,0x6f,0xe5,0xd5,0x61,0xf4,0x74,0x8c,0xd7,0x9f,0xbe,0x7d,0xfb,0x7d,0x58,0x6b,0xf7,0x6b,0x9a,0xd4,0x37,0x5f,0xfc,0xf0,0xf2,0xdb,0xfa,0x0b,0xea,0xe4,0xe7,0x5f,0xbe,0xfe,0xaa,0xd6,0x99,0xd0,0x67,0xa8,0x66,0xbe,0xfc,0x00,0xce,0xfb,0xb0,0xda,0xcf,0xf0,0xff,0x05,0x6e,0x82,0x0b,0x3f,0x85,0x60,0xe8,0xb0,0x9e,0x5c,0x00,0x5d,0x69,0x70,0xd1,0xf3,0x93,0xbf,0xa3,0x8d,0xb1,0xce,0x32,0x5a,0xaf,0x51,0x97,0xc0,0x3b,0xf0,0xaf,0xaf,0xb3,0xa7,0xc1,0xea,0x50,0x42,0xac,0x7e,0xa1,0xef,0xe9,0x75,0x97,0xc0,0xc1,0x4e,0x26,0x83,0x86,0x57,0xd0,0x48,0xc0,0xad,0xd6,0x46,0x8a,0x9d,0xf0,0x77,0x9a,0x86,0x27,0xba,0xc8,0x2a,0xcf,0xb3,0xdd,0xe7,0xb4,0x7d,0x89,0xf5,0xaa,0x8f,0x0d,0xd5,0xc9,0xc2,0x86,0x65,0xaf,0xf2,0xdb,0xc3,0x94,0xc6,0x24,0x23,0x2a,0x07,0x58,0x1d,0x03,0xdd,0xd0,0x8e,0xcc,0x82,0x21,0x77,0xdd,0xe9,0x98,0x3f,0x8c,0x46,0xbf,0x50,0xdf,0x9f,0xe8,0x2e,0x1e,0xd5,0x4f,0xd1,0x25,0x7a,0x55,0xac,0x36,0x77,0x7b,0x8d,0x69,0x0e,0xe8,0x4c,0x4c,0xb8,0xe1,0x90,0xdc,0xed,0xf7,0xeb,0x55,0xf0,0xe4,0xb2,0x50,0x3f,0x53,0xb9,0xd9,0x75,0x86,0xcb,0x7f,0x42,0x26,0xf6,0xcb,0xc3,0xb8,0x7b,0xfd,0x70,0xbd,0x7b,0x7a,0x3d,0x5a,0xc5,0xfb,0xe2,0x5d,0x7e,0x76,0x7d,0x7f,0xa9,0xfe,0x53,0x6a,0xfb,0x93,0x3f,0x02,0x6a,0xa0,0x69,0xf1,0xaf,0xef,0xe9,0x2f,0xad,0xbe,0x7e,0x40,0x75,0xa9,0x27,0xd1,0xe5,0x88,0x46,0x75,0xa9,0x7e,0xad,0x80,0x17,0x6f,0x36,0xda,0x6b,0x59,0x7c,0x31,0x19,0x3f,0x5c,0xa9,0xbf,0x1c,0xb9,0xe3,0xc3,0x83,0x8c,0x8a,0x36,0x1e,0x77,0x1a,0x70,0x1a,0xc7,0x51,0x2b,0x69,0x15,0x79,0xfd,0xf7,0x74,0x38,0x5e,0xfc,0xe5,0x93,0x4f,0x3e,0xfa,0x8b,0xa1,0x75,0x40,0xa6,0x11,0x45,0x90,0x0e,0x93,0x30,0x7b,0xde,0x1f,0xca,0xb9,0xdc,0x9b,0x6c,0xd7,0xcb,0xcf,0x67,0xf1,0xf6,0xf3,0x75,0x96,0xfb,0x59,0x97,0xbf,0x08,0xc2,0xd6,0x97,0x2f,0x5e,0x5c,0xf5,0x0f,0x9f,0x7c,0xf2,0xec,0xaf,0x7f,0x51,0x57,0xfd,0x67,0x1f,0x9d,0x67,0x87,0x4f,0xfe,0xf2,0xd1,0x33,0x08,0x75,0x92,0x38,0xba,0xa4,0x2e,0x13,0xfa,0x7b,0x7f,0x35,0xb9,0x7e,0xff,0x7f,0x27,0xe3,0xc3,0x2f,0x17,0x43,0x9a,0x78,0xfa,0x79,0xa2,0x11,0xa3,0x7e,0x73,0x71,0x7d,0xf7,0x8a,0xfe,0xc1,0x2c,0x10,0xbf,0x9c,0xc6,0x27,0x04,0x83,0x43,0xef,0xba,0x8f,0xa3,0x95,0x48,0xfa,0xeb,0xbb,0xc9,0x64,0x92,0x79,0x61,0x2c,0xe7,0x8b,0xdf,0x57,0x17,0x57,0x84,0x60,0x09,0x55,0x75,0xe3,0x5e,0xaa,0x7b,0xf7,0x29,0x11,0xe9,0xfa,0x64,0xa1,0xb7,0x56,0xec,0xea,0x5f,0xfd,0x85,0x8a,0x9e,0x11,0xe3,0xc5,0xc5,0x8f,0x2a,0x8b,0x5d,0x42,0x6b,0xe9,0x33,0xbb,0x13,0xed,0x63,0xbf,0x95,0x51,0x32,0x07,0x08,0x98,0x91,0x3e,0x8e,0x7c,0x3a,0xcb,0x96,0x4c,0x3a,0xd0,0x51,0xbf,0x88,0x93,0x7c,0xe1,0xc9,0xa1,0xae,0x1e,0xb2,0x62,0x1b,0x7a,0xa5,0xe0,0xd5,0x23,0x88,0x26,0x48,0x26,0x6a,0x63,0x9a,0xaf,0x32,0x8f,0x28,0x92,0xfd,0xf6,0xc3,0xc3,0xdf,0x34,0x45,0xf7,0x32,0xfa,0x52,0x48,0xb8,0x77,0x3d,0xde,0x81,0xf8,0x62,0x17,0xa8,0xea,0xdd,0xcb,0x91,0x7b,0x6f,0x84,0x90,0x44,0xbc,0x65,0xf9,0x5b,0xda,0xad,0x47,0xa2,0x04,0x89,0xdc,0x9a,0x50,0x7f,0xff,0x16,0x3d,0x70,0xbd,0xe1,0x4b,0x5d,0x6a,0x58,0x9d,0xd4,0x57,0xba,0xd9,0x58,0xe9,0x66,0x13,0x22,0x18,0x5b,0x19,0xa1,0xd8,0x61,0x2c,0x06,0xf7,0xd4,0x78,0x8e,0xc3,0x59,0xf3,0x12,0xf4,0x13, +0x0c,0x2c,0x1f,0x91,0xd2,0xe9,0x7c,0x3c,0x5a,0x3a,0x6b,0x1a,0x33,0x04,0x12,0x0d,0x23,0x75,0x4d,0x88,0x8c,0x11,0x22,0x66,0x0d,0xe2,0x05,0xf4,0x45,0xd2,0x5b,0xdf,0xaf,0xf2,0xed,0xcb,0x92,0x54,0x49,0x86,0x89,0x1d,0x4f,0xf8,0x57,0x50,0xd6,0x19,0x40,0x94,0x08,0x0d,0x43,0x61,0x77,0x1c,0x46,0x19,0x02,0x82,0x2b,0x02,0xe2,0xfb,0xf3,0xf3,0xbf,0xca,0xcf,0x15,0xdf,0x5a,0xca,0x1a,0x15,0x74,0x88,0xcc,0xf7,0x7d,0x54,0x5c,0x69,0xec,0x70,0x48,0xc2,0x77,0x01,0x95,0x5e,0x11,0xe3,0x4f,0xe3,0x57,0x2c,0x69,0x5f,0xa9,0x4d,0xc0,0x4a,0x8c,0x2b,0x5d,0x2f,0x91,0x40,0xff,0xd9,0xcb,0xdf,0xe7,0x29,0x08,0x76,0x90,0x26,0x93,0x68,0x31,0xba,0x1a,0x73,0x99,0xbf,0x46,0x68,0x8b,0x55,0x1e,0xfe,0x9c,0x48,0xce,0x69,0xbe,0xd7,0x92,0xf9,0xcf,0x3e,0xbc,0xce,0xfc,0x49,0x10,0x54,0x3a,0x32,0xef,0x15,0x80,0x9b,0x89,0x7d,0x28,0xbc,0xc7,0x9c,0xf8,0x25,0x61,0x81,0xa9,0xcc,0x8e,0xa5,0x76,0xbb,0x96,0xaa,0xce,0xcf,0xf7,0xc4,0x0c,0xce,0xe9,0xf7,0xf7,0xea,0x41,0x87,0x16,0xa3,0x67,0x63,0xf3,0xde,0xc0,0x58,0xa6,0xdc,0x2e,0xee,0x3e,0xfb,0xf0,0x36,0x9e,0x7e,0x13,0x2f,0x59,0x78,0xa2,0xb8,0x87,0x3c,0xb8,0x8f,0xc6,0xd4,0x46,0x5a,0x2d,0xf9,0x39,0x61,0xd2,0x1d,0xca,0x62,0xcd,0xda,0xdf,0xfc,0x6e,0x6b,0xb6,0x24,0x46,0x43,0x5d,0xa5,0xf6,0xd2,0xde,0xed,0x8e,0x68,0xee,0xce,0xa7,0xa3,0x18,0x7b,0x72,0x0c,0x71,0xce,0x2d,0x2d,0xeb,0x6d,0x6f,0x9f,0xef,0x98,0xc9,0x96,0xb5,0xe0,0x35,0x25,0x88,0x51,0xdb,0x28,0x1e,0x98,0xa9,0x32,0x22,0x10,0x7a,0x29,0x30,0x83,0xba,0xab,0x64,0x7f,0xf0,0xe0,0xdf,0xc8,0xba,0x7c,0xba,0x17,0xed,0x59,0xee,0x7b,0x45,0x46,0xf4,0xd1,0xf0,0x26,0xba,0xb1,0x12,0x81,0x84,0x58,0x6c,0xe2,0xd5,0x93,0xde,0xae,0x5e,0x50,0xdd,0x44,0x77,0x50,0xcc,0x4c,0xa9,0x2f,0xc4,0x97,0xae,0x0d,0xa9,0x2a,0x1b,0x01,0x72,0xd0,0xf5,0x68,0x36,0x8e,0xbc,0x3f,0x79,0xdd,0x1b,0x8c,0xa0,0xbb,0x8b,0x7d,0x3c,0x21,0x96,0x81,0x0a,0xcf,0xd7,0xc5,0xca,0x27,0xcc,0x1c,0x10,0xb4,0x3f,0x31,0x43,0x3a,0x3f,0xbf,0x8d,0xfd,0xc4,0xd1,0xc8,0x04,0x04,0x89,0x98,0x8c,0x6d,0x00,0x84,0xd0,0x98,0xc5,0x5d,0x8f,0xf5,0x1d,0x6f,0xf8,0x00,0x5a,0x6f,0x3f,0xa5,0xcd,0xba,0xe5,0xf9,0x93,0xdd,0xfe,0x3e,0x78,0x38,0x4e,0x8a,0x15,0xed,0xe1,0x0f,0x0f,0x37,0x04,0x15,0x77,0x58,0x21,0xd1,0xf1,0xd4,0xc6,0x4c,0x7b,0x53,0x57,0x5e,0xf8,0xa5,0xc4,0xe7,0x3b,0xe5,0x3d,0xb9,0xc2,0x89,0xcc,0x1b,0xb5,0xdc,0xbd,0xe0,0x0e,0x44,0x30,0x0d,0x6e,0xdc,0x3e,0x4e,0xfc,0x14,0xfb,0xd9,0xe2,0x43,0x86,0xbc,0x14,0x43,0x0f,0x5e,0x10,0x37,0x4a,0xec,0x5e,0xfe,0x15,0x4f,0xd1,0xf9,0x79,0x46,0x3d,0x26,0x96,0x2e,0x19,0x11,0x9a,0x9e,0x15,0x93,0xbd,0x1f,0x10,0x2b,0x3c,0xe2,0xb2,0xe3,0x28,0x37,0x7d,0x49,0xca,0x26,0x8b,0xd8,0x45,0xb5,0xa3,0xbb,0x31,0xa1,0x58,0x15,0x97,0xef,0xe7,0x71,0xc9,0xb9,0xad,0xea,0x9a,0xb0,0x49,0x91,0x2f,0x32,0x5a,0x3f,0x4f,0xf0,0xaa,0x96,0xc2,0x75,0x68,0xaa,0x03,0x3d,0x53,0x56,0xca,0xd2,0xb9,0xb2,0x33,0xe6,0xae,0x03,0x26,0xee,0x84,0x9e,0x8c,0xb1,0x03,0x64,0x06,0x0e,0x7a,0xbb,0x89,0xab,0x68,0x52,0xb3,0xa7,0x07,0x0f,0xac,0x7b,0x5a,0x05,0x94,0x9c,0x00,0x25,0xeb,0xc5,0xb4,0x1e,0x5f,0xc6,0xab,0x6c,0x91,0x8f,0xd2,0x51,0x3e,0x26,0x0c,0x5a,0xd6,0xb6,0xa8,0xd4,0x46,0xa8,0x31,0x26,0x84,0x4b,0x3c,0xe6,0x15,0x4b,0x67,0x0d,0x46,0x94,0xfb,0xc4,0xb9,0xa7,0x76,0xd7,0x77,0xdb,0x34,0x7f,0x0d,0x35,0xe4,0x45,0xe2,0xde,0x31,0xf2,0xac,0x20,0xa0,0x34,0x90,0xee,0xa4,0xd4,0x3f,0x1c,0x46,0x6f,0x8a,0x64,0x41,0x28,0x15,0x58,0x2d,0x75,0xb8,0xad,0x8b,0x2b,0x2b,0x22,0x1a,0x5e,0x85,0x84,0xd4,0x6d,0x2f,0x97,0xee,0x0a,0x95,0x3a,0x24,0xdb,0xed,0x13,0xdb,0xd0,0xf0,0xcf,0x4c,0x4a,0x31,0x7b,0x8c,0xb9,0x66,0x6d,0x1e,0x8d,0xce,0x99,0xd3,0xd5,0xff,0xa8,0x7e,0xdf,0x69,0x80,0x4e,0x63,0x21,0xd1,0xf8,0x2e,0x38,0xd1,0xde,0xfa,0x54,0x7b,0x86,0xe1,0xd7,0x87,0x3b,0x0e,0x8f,0x2a,0xa0,0xb8,0x74,0xc0,0xd5,0xb0,0x3c,0xf9,0x13,0xf7,0xda,0xf9,0xa6,0x52,0x81,0xfb,0x75,0x1c,0x26,0xf5,0xdb,0x62,0xf7,0xd2,0x79,0x40,0xd8,0xc1,0x79,0x42,0xc8,0x0e,0x32,0x8b,0x1c,0x70,0xdd,0xf6,0xb5,0xd3,0x7a,0xad,0x9b,0xee,0xb8,0x37,0xee,0xb8,0x0b,0x87,0xd4,0x71,0xe8,0xad,0xa8,0x9b,0x28,0xf7,0x55,0xaa,0x32,0x59,0x07,0x08,0xe9,0x62,0xc8,0xc7,0x0c,0x90,0x13,0xdc,0xaa,0x69,0x34,0xa9,0x82,0xfc,0x94,0x40,0x9e,0xa0,0x3c,0x9a,0x8c,0xa6,0x63,0xe0,0x75,0x40,0x7c,0xd4,0xf1,0x33,0xfc,0xe0,0x9a,0xe8,0x0d,0xfc,0x6b,0xbb,0x74,0x5b,0xd9,0xfc,0xe7,0xe7,0x6d,0x9a,0xfe,0xb8,0xf5,0xf8,0xa2,0x4d,0x70,0x4c,0xa3,0x29,0xed,0x04,0x11,0x60,0x41,0x11,0x3f,0xc1,0x7d,0xb1,0xfb,0xf9,0xeb,0xaf,0x9a,0x72,0x15,0x16,0xe4,0xc6,0x75,0x62,0x20,0x0e,0xac,0xc4,0x44,0xb7,0x60,0x24,0xf9,0x1d,0x9a,0x49,0xef,0xcb,0xb7,0x5f,0x7f,0x55,0x3d,0x69,0x8e,0x6a,0xc9,0x8d,0xe6,0x7b,0x53,0x49,0x8b,0x08,0x07,0x76,0x03,0xf1,0xb0,0xd9,0x58,0xf8,0xce,0xca,0x1b,0x85,0x02,0x01,0x29,0x31,0x75,0xf6,0xf5,0xb4,0xde,0x9b,0xa1,0xbf,0x8a,0xa6,0x74,0x12,0xad,0xea,0x2f,0xd4,0x26,0xea,0x4c,0xfc,0x15,0xd1,0x8a,0x52,0x93,0x9f,0xa3,0x4c,0x3e,0x89,0xef,0x16,0xfb,0x1f,0x8b,0xfc,0x3e,0x80,0x1c,0x76,0xbf,0xde,0xd0,0x5b,0x10,0x42,0x79,0x2f,0xce,0xb2,0x2f,0x88,0x67,0xda,0x7f,0x55,0xec,0xf6,0x39,0x75,0x6b,0xd8,0x7c,0x04,0x3b,0x8b,0xc5,0x3a,0xa6,0xf3,0x2f,0x8b,0x55,0xe7,0x2a,0x08,0x73,0x20,0x2f,0x42,0xee,0x5c,0x0a,0x15,0x3a,0xb7,0x74,0x10,0xaf,0xca,0xe2,0x10,0x73,0x33,0xa6,0xe3,0x93,0x67,0x17,0xcd, +0x4f,0x10,0xd2,0xa9,0xa1,0x06,0x22,0xe2,0xb6,0x54,0x27,0xae,0x1d,0xd2,0xf6,0x35,0x9d,0x5b,0xa8,0xb1,0x6d,0xe1,0x4f,0xd6,0xed,0x1a,0x3f,0x98,0x83,0xe2,0xf3,0xf5,0x52,0x0e,0x0a,0x3a,0xfc,0x75,0x73,0x4d,0x42,0x08,0xc2,0x01,0x0d,0xc6,0xcd,0x56,0x2d,0xfd,0x12,0xfd,0x53,0xce,0xf2,0xd5,0x29,0x4a,0x48,0xbe,0x04,0xe1,0x76,0xa2,0x8b,0xeb,0x4a,0x17,0x09,0xf4,0x88,0xa0,0xbb,0x53,0x9d,0x5a,0x85,0xa8,0x8b,0x88,0xa1,0x96,0xa7,0xfe,0x5d,0xbd,0x9b,0x68,0x6c,0xe8,0x67,0xbd,0x49,0xb1,0xd8,0xe7,0xdb,0xde,0xeb,0x97,0x6d,0x70,0x6f,0x0f,0xfd,0x5f,0x89,0x4f,0xb4,0x12,0xef,0xd6,0x29,0x6c,0x92,0x4c,0x38,0x20,0x8e,0xc4,0x2e,0x51,0x13,0xab,0xac,0xda,0x00,0x0e,0x2e,0x10,0x64,0x2d,0x5b,0xb6,0x4e,0x14,0x9f,0x9f,0x6f,0x4a,0x6c,0x5e,0x23,0x72,0xcb,0x2e,0xa5,0xc3,0x51,0x0a,0xd5,0xfc,0xf1,0x18,0x84,0xff,0xf3,0x41,0x49,0x73,0x27,0x11,0x8a,0x1d,0xa7,0xe0,0xf6,0xe6,0x33,0x19,0xbf,0xed,0x1b,0x88,0xe3,0x77,0xf1,0xe2,0x2e,0xff,0x7f,0x7b,0x46,0x44,0xdc,0xdb,0x3a,0x2f,0xe0,0x3b,0xb8,0xc6,0x94,0xf0,0x6c,0x7b,0xff,0x54,0xa5,0x63,0x46,0x21,0x39,0x9a,0x8c,0x07,0x79,0x94,0xb4,0x80,0x10,0x11,0xb4,0x25,0x4b,0x37,0x89,0x72,0xe1,0xe6,0xfe,0x7b,0x4d,0x68,0x62,0x8e,0x17,0xcc,0xcc,0xc7,0xdb,0x4f,0xff,0x16,0xb5,0xef,0xdb,0x1a,0x2f,0xaa,0xcf,0xda,0xdf,0x99,0x2a,0xe7,0xf3,0x93,0x5c,0x4c,0xc8,0xfc,0x04,0xbd,0x6f,0x50,0xcc,0xb1,0xd1,0xf1,0xb7,0x32,0xb9,0x34,0x11,0x74,0x9c,0xe5,0x51,0xbf,0x36,0xfb,0x95,0xca,0x59,0xcb,0xf3,0x94,0x25,0x10,0xc1,0x83,0xa1,0xa3,0x26,0xac,0x56,0x0b,0x40,0x95,0xa5,0x0e,0xf6,0xd6,0x1c,0x59,0x6a,0x81,0x26,0x33,0xe4,0xee,0xc4,0xc2,0x0b,0xcb,0x02,0xeb,0x33,0xe4,0xf0,0x59,0xff,0x32,0x30,0x55,0x3f,0xdf,0x18,0xca,0xef,0x24,0x17,0x06,0x21,0xc5,0x16,0x03,0xbf,0xc5,0x1f,0xe1,0xc5,0x4a,0xd4,0x56,0x9f,0x42,0xd6,0xb5,0xd7,0xb0,0x59,0x13,0x8d,0xad,0xe8,0xf0,0xc0,0x51,0x19,0x79,0xcf,0xe3,0x33,0x42,0x6a,0x7f,0xf6,0xba,0x77,0x5d,0xef,0xcf,0x2f,0x9e,0x5f,0xc6,0x2f,0x9e,0x8b,0x0c,0xad,0x7c,0x7c,0x01,0xe9,0xf9,0x9f,0xcf,0x96,0x3b,0x22,0xc5,0xd7,0xf7,0x69,0xbc,0xa1,0xfe,0xe6,0xd1,0x9f,0xa9,0xf4,0x7a,0xc3,0x34,0x81,0x11,0xef,0xf3,0xb3,0x4b,0x79,0x48,0x17,0xf2,0xf8,0x85,0xa7,0xe2,0xe6,0x42,0x7b,0xa3,0x6a,0x75,0xbf,0xd0,0xb7,0x63,0x8b,0xd4,0x89,0xf7,0x92,0x95,0xf1,0x20,0x6e,0x1f,0x47,0xa5,0xa4,0x1d,0x92,0xef,0x6b,0x96,0xc1,0xb6,0x56,0x6a,0x7a,0x52,0x56,0x75,0x38,0x98,0xaa,0x4a,0x99,0xfe,0x30,0xe4,0xbd,0x71,0x10,0x41,0xe6,0xa9,0xba,0x8a,0xec,0xbf,0x22,0x19,0x7f,0x5b,0x6d,0xf4,0xae,0xfd,0xbb,0x50,0x2b,0x3d,0x5a,0xbe,0x29,0x5f,0xb5,0x7e,0x19,0xff,0x89,0x9b,0xeb,0x3e,0x6d,0xf9,0xb4,0xf7,0xa7,0x5e,0x17,0x22,0x47,0x3e,0x66,0x6b,0xcb,0x1b,0xd7,0xd6,0x73,0xb6,0xcd,0x27,0x34,0x9d,0x67,0x96,0xa8,0xfc,0xb3,0xb9,0xaa,0x2e,0x70,0xeb,0x7b,0x59,0xbd,0x4b,0x67,0xf9,0x06,0x27,0x38,0x39,0x21,0xe2,0x83,0x41,0x9d,0x1d,0x07,0xc8,0x13,0x33,0x2d,0x9a,0x1b,0x1e,0xaa,0x0b,0x7d,0x49,0x50,0x2b,0xbe,0x02,0xf1,0xa0,0xbc,0x97,0xa7,0x96,0x01,0xef,0xa3,0xac,0x0d,0x36,0xf8,0x4b,0x91,0x31,0x5b,0xa5,0x0c,0xd5,0xf2,0xac,0x03,0x56,0xac,0x65,0x61,0xf2,0x15,0x0f,0xb2,0xa5,0x26,0xfb,0x4a,0x79,0xa1,0x99,0x0b,0xaa,0xa9,0xb1,0x6f,0xec,0x8c,0x11,0xc3,0x7b,0xb2,0x99,0xb2,0x82,0x3f,0xda,0x4e,0x5b,0x35,0x4f,0x55,0xf8,0x9e,0x5e,0x99,0x2f,0x55,0xef,0x69,0x88,0xb5,0x0f,0x80,0x00,0x96,0xe0,0x93,0xf3,0x9d,0x29,0x6f,0x90,0xc1,0x2e,0x5a,0x9b,0x57,0x87,0xc3,0xba,0x77,0x9f,0x27,0x37,0xc5,0xfe,0xeb,0x6a,0x59,0xbc,0x58,0xae,0x7f,0x6b,0x79,0xba,0x6e,0x2b,0xb9,0xab,0x3d,0x04,0x76,0xa9,0x41,0x5f,0x8a,0x59,0x49,0xd7,0x04,0x81,0xd8,0x78,0x5c,0x3e,0xda,0x69,0x35,0xb7,0x62,0x2d,0x4e,0x79,0x37,0xda,0x75,0xb0,0xcf,0x79,0x64,0x5b,0x3d,0xb2,0x4e,0xe4,0xa9,0x6f,0x00,0xd5,0xb7,0xd1,0xad,0x9d,0x30,0x47,0xaa,0x7e,0xab,0xc5,0x33,0x07,0x10,0x84,0xdb,0x68,0xdb,0x56,0x66,0xeb,0x96,0x49,0xcc,0x7c,0xac,0x7b,0xe9,0x7a,0x09,0x6e,0xce,0x10,0xf4,0xdf,0xad,0x77,0x05,0xba,0x1d,0xa8,0x3d,0x64,0x88,0x4e,0xb1,0xd5,0x3e,0x2e,0x56,0xbb,0x60,0xd8,0x72,0xe8,0x44,0x7f,0xad,0xb0,0xf6,0xc3,0xb8,0x4e,0xd8,0x87,0x10,0x01,0x24,0x55,0xa9,0x84,0x65,0xcc,0x23,0x96,0xdc,0x77,0xfc,0x4e,0x26,0x12,0xd0,0xcc,0x56,0x84,0xa7,0xa9,0x6d,0x7a,0x58,0x5e,0xfa,0x59,0x10,0xc6,0xa7,0xba,0x7e,0x7e,0x7e,0xf5,0x97,0xf3,0x93,0x6f,0xd9,0xfa,0xae,0x7e,0x74,0xc2,0x4a,0x47,0x0b,0x14,0x92,0xa8,0x22,0xd0,0xc2,0x1b,0x87,0x40,0xe8,0xf4,0x07,0x56,0xf0,0xa2,0x3e,0x8b,0x92,0x61,0xa3,0x9e,0xd8,0xd5,0xf5,0x42,0xe7,0xac,0xfa,0x03,0x51,0x5b,0x74,0x4e,0xf6,0xe9,0xa2,0x93,0x9c,0x7a,0x65,0x4f,0xdd,0x61,0x46,0xe4,0x63,0xd4,0xc6,0xec,0x51,0x83,0x7e,0x53,0x20,0x1c,0x0c,0x4f,0x4f,0x41,0x12,0x84,0x57,0xea,0xea,0x1c,0xb3,0x2e,0x76,0x9e,0x2f,0x73,0xb0,0x40,0x79,0x26,0xb6,0x65,0xed,0x1f,0x71,0x43,0xd9,0x10,0xe3,0x5b,0x51,0xb3,0xd5,0x06,0xe9,0xe1,0x3b,0x48,0x75,0xdf,0xa9,0x38,0x80,0xc5,0x4f,0x22,0xa5,0x92,0x53,0xa5,0xa8,0x7b,0x57,0xe1,0xcd,0xf0,0xb5,0x7f,0x43,0x1f,0x5c,0xe0,0x87,0xfa,0xd4,0x0f,0x3f,0x3e,0xcf,0xf0,0xf5,0x55,0xdb,0x02,0x9d,0x9a,0xd8,0xd4,0xda,0x09,0x95,0xcb,0xc6,0xc4,0x8f,0x73,0x3b,0x8d,0x46,0xf1,0x18,0xc6,0x38,0xc9,0x58,0xc4,0xe8,0x34,0x72,0x2b,0x73,0xe6,0x11,0xd9,0x4e,0x53,0xbf,0x72,0xdc,0x4c,0xda,0x3a,0x88,0x8f,0x73,0x57,0x5e,0xad,0x65,0x5d,0x83,0x34,0x8a,0x07,0xa5,0x40,0xca,0x81,0x9f,0x69, +0xef,0x6e,0x25,0x22,0xc3,0x14,0xa5,0x92,0xf6,0x52,0x33,0xb7,0x94,0x16,0x3a,0x8c,0x32,0x98,0x0d,0xcc,0x60,0x2e,0x46,0xe4,0x6c,0x09,0x07,0xd4,0x24,0xde,0x29,0x7e,0x13,0xea,0x62,0xef,0xd0,0xe5,0x99,0xb9,0xbe,0x0a,0xfb,0x47,0xb5,0x0a,0xc2,0xd5,0x51,0x11,0x5b,0xaf,0xf1,0x5d,0xbb,0x2e,0x8a,0x35,0x1b,0x10,0x07,0xca,0x1f,0x58,0xc3,0x97,0x9f,0x58,0xec,0xd9,0x58,0x8c,0x36,0x38,0x34,0xfa,0x87,0x98,0xf5,0x0f,0x96,0x97,0x79,0xa3,0xbc,0xe8,0xcf,0x4f,0xae,0x40,0xba,0xa8,0x06,0x5e,0x26,0xe2,0x8e,0x65,0xe6,0x89,0x95,0x99,0x13,0x52,0xed,0x6c,0x05,0xeb,0x24,0x62,0x14,0xe9,0x48,0xd1,0xe9,0x09,0x4b,0x96,0x65,0x43,0x59,0xa4,0x99,0x30,0x45,0x0b,0xf5,0x5f,0x13,0xd1,0x02,0x54,0x0d,0x36,0xd2,0x3a,0x95,0xf2,0x81,0xc5,0x34,0x56,0xb6,0xa8,0x05,0xac,0x79,0xf0,0x70,0x2c,0x67,0x28,0x51,0x2b,0x99,0x1e,0x82,0x22,0x73,0x62,0xbd,0xe8,0xf3,0x4c,0x19,0x8c,0xd4,0x3a,0xbb,0xbf,0x33,0x4b,0xc6,0xfd,0x80,0xaa,0x81,0x94,0xa1,0x56,0xc5,0xe3,0x1f,0x33,0xe8,0xd3,0xa1,0xef,0x4a,0x62,0x6b,0x86,0x42,0x30,0x72,0x23,0xea,0xf9,0x73,0x99,0x25,0xb7,0xa4,0xaa,0x95,0x0c,0x86,0x39,0xeb,0xb7,0x3a,0x1b,0xc3,0x5b,0x18,0x70,0xb3,0xd6,0xc4,0x93,0xe1,0x24,0x74,0x85,0x21,0xb4,0x26,0x9b,0x61,0x8d,0xb7,0xa6,0x1d,0x42,0xdc,0x57,0x0b,0xd3,0xc9,0x0b,0x39,0xe9,0xed,0x36,0x79,0x5a,0x4c,0x8a,0x3c,0x1b,0x4e,0x84,0xf3,0x0a,0x59,0x0e,0x8d,0xf1,0xe7,0x3b,0x22,0x72,0xf3,0xa8,0xc9,0xbf,0xd7,0x0c,0x1e,0x45,0xbd,0x21,0x9f,0xc0,0xa2,0xaa,0xf2,0x45,0xc3,0x41,0xe7,0xcd,0x07,0x5a,0x9c,0xf7,0x67,0x5c,0x52,0x9d,0xdd,0xad,0xb6,0x79,0xba,0x9e,0xae,0x8a,0xdf,0xf2,0xec,0x2c,0x7f,0xbf,0xd9,0xe6,0xbb,0x1d,0x8c,0x90,0xcf,0xbc,0xae,0xae,0xf2,0x6e,0x55,0x10,0x95,0xf1,0x06,0x82,0xb7,0xa6,0xfc,0xcb,0x61,0xac,0x18,0x0f,0x10,0x0a,0x22,0x70,0xcb,0xf7,0x04,0x6a,0x2f,0xef,0x60,0x1d,0x4f,0x84,0xdf,0x4e,0xdd,0x44,0x1a,0xa5,0xbe,0xd9,0x83,0x74,0x61,0xa9,0xb6,0x28,0x76,0x41,0xc3,0xe0,0x85,0xff,0x59,0xa0,0x16,0x86,0xed,0x22,0xa6,0x7f,0x34,0x01,0xdb,0xc5,0x87,0x0c,0xb1,0x9f,0x2c,0xf4,0xd2,0x3c,0xd7,0x24,0x08,0x1c,0xa1,0x7b,0xac,0x6d,0xf0,0x59,0xee,0xa8,0x08,0x45,0xea,0x25,0xba,0x61,0x69,0x3e,0xec,0x1e,0x73,0x08,0xf1,0x68,0xf2,0xdf,0xc2,0x7d,0xa6,0xc5,0x06,0x2b,0xf2,0x3c,0xc6,0x95,0x13,0xe7,0xac,0x2e,0xd9,0x71,0x30,0x7e,0x93,0xc3,0xe1,0xaf,0xf2,0x73,0xc5,0xb7,0xc2,0xac,0x35,0x2c,0x42,0xd9,0x41,0x87,0xed,0x19,0x56,0x7b,0x8b,0x45,0xdd,0x87,0x6c,0x4d,0x1c,0x53,0x33,0x6c,0xbb,0xc0,0xb4,0xe1,0x20,0x1e,0xe0,0x81,0x2b,0xab,0x4f,0xbb,0x6c,0xed,0x6c,0x15,0x86,0x1f,0x49,0xd3,0x1f,0xbb,0x08,0x56,0x7a,0xfa,0x23,0xa0,0x45,0xca,0x95,0xf3,0xc6,0x4c,0x3e,0xd7,0x91,0x94,0x12,0x0d,0xe2,0x49,0x45,0x94,0x29,0xf8,0x65,0x17,0x3d,0x38,0x8a,0x9c,0xf0,0x93,0xbe,0x12,0x0a,0xfd,0xbb,0x5d,0x7e,0x97,0xad,0xc3,0x22,0x56,0x8c,0x90,0xc2,0x1f,0x55,0xb9,0x3b,0x60,0x5b,0x0f,0xb6,0x16,0xbf,0xdb,0x7c,0xc1,0x76,0x0f,0xe1,0x83,0xf7,0xc2,0x0b,0x9b,0x7a,0x70,0x71,0x0a,0x81,0x85,0x34,0xf4,0xf0,0x8d,0xf7,0xf4,0xb8,0x6b,0x1f,0x6f,0xf3,0x77,0xc5,0xfa,0x6e,0xa7,0x47,0x5f,0xf9,0xf6,0xbf,0x4e,0x15,0x3a,0x1e,0x15,0x3d,0x7a,0xc5,0x42,0xa3,0xf0,0x81,0x8d,0x66,0xda,0x64,0x5c,0xa3,0xab,0x71,0x84,0x3f,0x55,0x01,0x92,0x8a,0x47,0x1f,0x8d,0x89,0x64,0xa0,0xbf,0x84,0x3c,0x46,0x1f,0xf3,0xdf,0x4f,0x60,0x71,0xed,0xec,0x28,0x29,0x09,0x5e,0x8d,0x21,0xf0,0x19,0x20,0x90,0xbf,0x83,0x0a,0x10,0x17,0xac,0x10,0x53,0xa5,0x7d,0xc2,0xc7,0xb4,0x57,0xc4,0x1a,0xe7,0xd1,0x9e,0x54,0x10,0x8c,0xf2,0x56,0xfb,0x99,0x34,0x40,0xaf,0x4c,0x4d,0x1f,0x05,0x43,0xdd,0x39,0xb3,0x9d,0xe9,0xb6,0x3f,0x46,0xbf,0x3f,0x1e,0x47,0x5d,0x1f,0x3f,0x43,0xf4,0x18,0x97,0x7f,0xa1,0x62,0x57,0x41,0xf8,0xec,0xa9,0xef,0xc1,0x4c,0x46,0x2a,0xfb,0x88,0xcd,0xc7,0xb3,0xcc,0xdc,0x05,0xf8,0xf6,0x13,0xf9,0xf6,0xff,0x8e,0xa9,0xfb,0xff,0xde,0x28,0x10,0xe2,0xe7,0xfc,0xbc,0xde,0xe2,0xd1,0x98,0x1e,0xb5,0xed,0x9b,0x0e,0x9a,0xa7,0xad,0x4c,0xb3,0x63,0x00,0xed,0xc7,0x1e,0xcf,0x81,0xd6,0x84,0xa2,0x8e,0x21,0xb6,0x61,0xc8,0x03,0x1a,0xa2,0x64,0x54,0x9d,0xf1,0x30,0x3d,0x3f,0x7f,0x2b,0xc5,0xa1,0xc3,0x21,0x08,0x9e,0xfa,0x29,0x5c,0x0e,0xe5,0xc6,0x7a,0xdf,0xf9,0xc4,0x6d,0x5b,0x75,0xc4,0x45,0x12,0x5c,0x98,0xeb,0x80,0x17,0xa6,0x8f,0x7a,0xfb,0xe5,0x1c,0x26,0x18,0x31,0x35,0x96,0x3a,0x4f,0xdc,0xd5,0xfa,0x88,0x88,0x5d,0x80,0xb3,0x00,0x10,0x8c,0xb2,0x7e,0x57,0xee,0xd8,0xae,0xf0,0x12,0x71,0x51,0x49,0xef,0x5a,0x0d,0x64,0x45,0x0e,0xe5,0x88,0x5d,0x8d,0x72,0x01,0x28,0xb0,0x5d,0xd7,0xa5,0x85,0x8e,0x62,0x49,0xd6,0xec,0xd6,0x07,0xad,0x49,0xb7,0x6e,0x65,0x87,0x03,0x4d,0x94,0x6b,0x4c,0xe4,0xff,0x62,0xad,0xe1,0xa8,0xa8,0x58,0x10,0xc1,0xea,0x09,0x73,0x0a,0x83,0x90,0x96,0x7e,0x25,0xb2,0x04,0x2d,0x38,0x2d,0x2d,0x05,0x4e,0xce,0x0d,0x2d,0xdd,0xef,0x0a,0x5b,0xeb,0x82,0x56,0x2d,0xf2,0xf7,0x02,0xde,0x69,0x47,0xda,0x2f,0xb5,0x8d,0x5b,0xb1,0x93,0xb7,0x8f,0x8d,0x4e,0x2a,0xd2,0xf4,0x80,0x9f,0x39,0x3e,0x75,0x62,0x4d,0x9f,0x0f,0xc1,0x0b,0x62,0xde,0xc2,0x0e,0xa6,0x23,0xef,0x02,0xa7,0x7b,0xf2,0x68,0x08,0xca,0x34,0x0d,0x4d,0x89,0x61,0xde,0xe1,0xdb,0x5f,0xf4,0x2d,0xc1,0x1f,0xec,0x78,0x73,0x0b,0x68,0x69,0x10,0x7a,0x4f,0xcb,0x97,0xee,0x8b,0x17,0x44,0x4d,0x7a,0x4f,0xdc,0x77,0x02,0x4f,0x25,0x30,0x4a,0x53,0xff,0xa5,0x8b,0xc0,0x94,0xb9,0x9b,0x5b,0x30,0xfa,0x16,0xd8,0x90,0x4d,0x93,0x82,0x7a, +0xa5,0x07,0xf9,0x82,0x8f,0x39,0xd6,0x6d,0xe6,0x16,0x54,0x4d,0xdd,0xdd,0x2b,0xae,0xbd,0xeb,0x5d,0x78,0x0c,0xbc,0x75,0x64,0x63,0xbc,0xcb,0xb4,0x29,0x4e,0xc4,0xb8,0x85,0xc9,0xba,0x12,0xe8,0x89,0xe2,0xf7,0x60,0x31,0xe7,0x3e,0xbf,0xf8,0x18,0x76,0x0f,0x9e,0xb6,0x07,0xe4,0x6e,0x98,0xd9,0xc5,0x61,0x97,0xe9,0xf9,0x19,0x36,0xa1,0xa6,0xd3,0x71,0x99,0x0b,0x07,0xde,0xd1,0x93,0x42,0xfa,0x51,0xb1,0x69,0x8e,0x26,0xd4,0xee,0x74,0xe8,0x39,0xa7,0x9d,0xd7,0x72,0x02,0xdc,0x56,0xb9,0x94,0x2d,0x1c,0x0d,0x4e,0x29,0x86,0xd5,0x2e,0xea,0x14,0x44,0x2b,0xcf,0x88,0x31,0xef,0x5c,0xe1,0xd8,0xbe,0xe5,0xd3,0x79,0x62,0x48,0x89,0x4d,0xf0,0xb0,0xb4,0xfc,0xc5,0x32,0x5a,0x8e,0x36,0x2c,0x02,0x9f,0x0d,0x97,0xa7,0xb7,0xdf,0x36,0xc4,0xc8,0x97,0x75,0x32,0x98,0xea,0x5f,0x47,0x1b,0x9a,0xaa,0xd5,0x82,0x4d,0xc0,0x61,0xd7,0xb2,0x3e,0x3f,0xaf,0x0c,0xe7,0x68,0xb7,0x3f,0x35,0xb2,0x8e,0x46,0xd3,0xe1,0xad,0x73,0xda,0x87,0xb7,0x3d,0x4c,0x3f,0x5f,0x8f,0xd5,0xf4,0xfc,0x7c,0x87,0xde,0xdd,0xaa,0x05,0xf5,0xeb,0x8e,0xf0,0xa1,0x8f,0x1f,0xf6,0xc7,0xbb,0x89,0x16,0xa3,0xa5,0xa6,0xbc,0x5e,0xbf,0xc4,0xab,0xca,0x3d,0x97,0x99,0x47,0x37,0x44,0x79,0xb3,0xe9,0xd4,0x2a,0x9a,0x03,0x07,0xb2,0x3d,0xd3,0x9c,0x0e,0x13,0x9a,0x8d,0x15,0xae,0x9e,0x8d,0xd5,0x12,0x57,0xb7,0x8e,0x55,0xd9,0x68,0x35,0xb6,0xd3,0xd1,0xed,0x82,0x70,0xa6,0xff,0x68,0x5a,0xa8,0x0d,0xfa,0x2a,0xea,0x07,0x90,0xdf,0x6c,0xd6,0x1b,0x9f,0x8d,0xa1,0xaa,0x33,0x71,0x7e,0xde,0xed,0x12,0xbf,0xb0,0x64,0xa6,0xf3,0x01,0xcd,0x47,0xa3,0x7b,0x5a,0xdc,0xfd,0x78,0x20,0x8e,0x3e,0x15,0x33,0x27,0x9a,0xf8,0xff,0x9d,0xa1,0x41,0x0c,0xc3,0xbe,0x42,0x7f,0x7c,0x1c,0xff,0xe2,0x8a,0xeb,0x81,0xf2,0x30,0xfe,0x7b,0x43,0x30,0x93,0xb3,0xa7,0x73,0x54,0xe6,0xab,0xea,0xd9,0xb4,0xbf,0x88,0x72,0x1e,0x06,0x31,0x68,0xfb,0x7f,0x83,0xfe,0xbe,0x7f,0x7e,0xbe,0xbf,0xcc,0x5e,0x44,0xfd,0xe3,0xb1,0xe5,0xdc,0x2d,0x75,0x19,0x4c,0x09,0x33,0xa5,0xb6,0xe3,0x49,0xca,0x20,0x17,0x15,0x62,0x88,0x1e,0xd4,0xb8,0x1d,0x87,0x8a,0x20,0x94,0xad,0x15,0xe8,0x44,0xe9,0x4b,0x05,0x42,0xe5,0x5b,0xcf,0x1d,0x1a,0xdf,0x90,0x19,0x96,0xdc,0xb0,0x73,0x57,0x43,0x62,0xca,0x47,0xb1,0x8a,0x15,0x61,0x54,0xf8,0x77,0x3a,0x6d,0xd5,0xbc,0x05,0xfc,0xb8,0xce,0x3e,0xb9,0xc6,0x05,0x71,0xe9,0x03,0x34,0x89,0x98,0xaf,0x3a,0x61,0x52,0x90,0x45,0xaf,0x71,0x46,0x8d,0xa6,0x4c,0xf1,0x64,0x30,0x29,0x48,0xf1,0xc3,0x4f,0x8e,0x41,0xdb,0xa9,0x8a,0xea,0xfa,0xf0,0xa1,0xa2,0x8e,0x83,0x34,0x94,0xb9,0x09,0x1f,0x56,0xeb,0x7d,0x58,0x54,0x65,0x8e,0x72,0x86,0xc2,0xb4,0x41,0x22,0x04,0xcc,0x9a,0x86,0x52,0xa5,0x4a,0x07,0xd3,0x51,0x1d,0x03,0x30,0x9b,0x35,0x76,0x9c,0x46,0x99,0x91,0x12,0xe4,0x6a,0x34,0x06,0x1a,0x8d,0x9b,0xe6,0x63,0xc4,0xfb,0x4d,0x61,0x2f,0xc6,0x74,0xca,0x0c,0xc3,0x49,0xf0,0x33,0x09,0xaa,0x83,0x81,0x62,0xb0,0x3c,0x8e,0x99,0xa0,0x51,0x19,0x58,0x6c,0x54,0xcf,0x1e,0x4d,0xfc,0x90,0x6f,0x89,0x97,0x62,0xb8,0x86,0x2e,0x8e,0xd6,0xa0,0x3e,0xc6,0x93,0xd6,0x2e,0xda,0x60,0xd3,0xe1,0xd5,0xa1,0x5c,0xd6,0xac,0xfa,0x89,0x5a,0xe2,0x3a,0x1d,0xa4,0x9a,0xf5,0xfa,0x89,0xcb,0xe5,0xb0,0x39,0x0b,0xd4,0x0a,0x60,0xb8,0xe8,0x24,0x03,0x9f,0x6b,0xcf,0xbb,0x18,0xe7,0x1d,0x9a,0x5d,0xc4,0xab,0xe9,0x89,0x26,0x7f,0xd0,0xe4,0x23,0xd3,0x09,0xa7,0xe0,0x97,0xbf,0x67,0xe8,0x55,0x8d,0x2e,0xd6,0x8e,0x88,0x86,0x99,0xd1,0x20,0x5b,0x9f,0xb1,0x1a,0x74,0x33,0x4c,0x7a,0x5c,0x51,0xdd,0x82,0xf0,0xfd,0x72,0x11,0xe2,0x05,0xda,0xaf,0xbf,0x93,0xe7,0xd6,0x6f,0x85,0x68,0xcc,0x6a,0x73,0xa9,0xd8,0xf4,0xf4,0x59,0x63,0x68,0xc7,0xcd,0xe7,0xb6,0x06,0x8b,0xba,0x34,0xb5,0x6e,0xf4,0x15,0x94,0xd2,0x54,0x9a,0xa9,0x7d,0xbc,0xad,0x04,0x32,0x70,0x0d,0x77,0xd7,0x69,0x2c,0xb2,0xdd,0xf2,0x1a,0xbb,0x72,0x56,0x51,0x65,0xcb,0x39,0xcf,0xe4,0x03,0xad,0x4c,0x46,0xec,0xdb,0x7a,0xdd,0x1a,0x18,0x01,0x22,0xbf,0x35,0x91,0xc5,0x70,0xb0,0x39,0xf5,0x7e,0xd5,0x8b,0x53,0xb0,0x7e,0x5a,0x84,0x0d,0x49,0x14,0x37,0xf9,0x8a,0xbd,0x72,0x0e,0xe5,0xb5,0x0f,0x72,0xb3,0xd3,0x01,0x56,0x60,0x99,0x75,0xdc,0x83,0x76,0xe9,0x70,0xf8,0x2f,0x7a,0x10,0x27,0x6c,0xbf,0xc6,0x7e,0xf9,0xac,0xca,0x08,0xd7,0xb1,0x4f,0xe8,0x5c,0x19,0xd5,0x06,0xdf,0xf7,0x69,0x2e,0x45,0xed,0xd5,0x4a,0x9d,0xff,0x41,0x4b,0xb4,0x04,0xbd,0x80,0xd1,0xb8,0x38,0x14,0x11,0xdf,0xb3,0x31,0x8e,0xd4,0xfa,0x95,0x51,0xfd,0x1d,0x95,0xb9,0x6a,0x27,0xdd,0x5d,0xfb,0x30,0xf7,0xce,0x56,0xc0,0x83,0x52,0x65,0x85,0x6c,0x42,0x4e,0x63,0x84,0x87,0x71,0xa5,0xca,0x3f,0x24,0x07,0x80,0x70,0xd7,0x82,0xc4,0xf3,0xbf,0xb4,0xf9,0x1f,0x4b,0x1f,0x5a,0x7a,0xdb,0xb1,0x27,0x44,0x8f,0x5b,0x67,0x55,0x30,0xc2,0x86,0xe4,0xdb,0xb6,0xb1,0xfd,0xac,0x77,0x9c,0x9d,0x53,0x78,0x46,0x63,0x02,0xdb,0x0a,0xff,0xd4,0x52,0x58,0x2c,0xf1,0xfe,0x87,0xcb,0xe4,0xd8,0xf3,0x19,0xa0,0x71,0x1e,0x25,0x47,0xc5,0x8e,0x22,0x4d,0x1f,0xed,0x7a,0x55,0xa7,0xda,0xa4,0x16,0x50,0x43,0x59,0x3f,0x01,0xaf,0x30,0x10,0x10,0x9c,0xd4,0x76,0x39,0x13,0xc3,0x70,0x39,0xb7,0xdf,0xd4,0xa5,0x83,0x26,0xf6,0xc5,0xc6,0x41,0x62,0x66,0x92,0x08,0x55,0x33,0x8e,0xab,0xbd,0x76,0x84,0xa0,0xa3,0xe4,0xe2,0x0a,0x65,0xf2,0xdb,0x7a,0x89,0x92,0x09,0x1a,0xa5,0xcf,0xfb,0xc3,0xb4,0x9b,0x84,0x29,0x97,0x24,0xfe,0xbe,0x59,0x9b,0xe3,0x69,0x37,0x48,0x9f,0x27,0x83,0xb4,0x1b,0x3d,0x0b,0xe2,0xba,0x75,0x42,0x4c,0xdf,0x13,0xbf,0xff,0xd8,0xe7,0x57,0xbf,0xf3,0xf9,0xa2, +0x31,0x94,0x8a,0xff,0x70,0x64,0xfb,0x3a,0xb8,0xb8,0x00,0x11,0x33,0x30,0xd5,0x64,0x95,0x6a,0xa6,0x7f,0xb8,0x9a,0x6e,0x37,0xa3,0x0e,0xb5,0xd6,0xc2,0x36,0x39,0x06,0xc0,0x89,0xcf,0x89,0x1c,0x70,0xbf,0xb5,0x3e,0xfb,0x0f,0xdb,0x38,0x2b,0xd6,0x88,0xc9,0xc0,0x9b,0x3f,0x59,0xbf,0xc7,0x35,0x31,0xfd,0x39,0x7e,0x37,0xc4,0x8b,0xde,0xaf,0xb7,0x19,0xae,0x8b,0x65,0x3c,0xc5,0xc3,0x63,0x50,0x52,0x56,0xc9,0x38,0x5a,0xc2,0x06,0xb3,0xac,0x6e,0x77,0x97,0x2c,0x0b,0x88,0xa6,0xd4,0x36,0x27,0x2a,0xa8,0x59,0x7e,0x25,0xe5,0x8d,0xc1,0xe3,0x16,0x06,0xd6,0xc7,0x6d,0xec,0x84,0xb6,0x31,0x96,0x4e,0xbb,0xb2,0xc7,0x15,0x92,0x8a,0xf9,0xf8,0x6d,0x4c,0xa4,0xc5,0x14,0xd4,0xd4,0x4d,0x0e,0xb1,0x6c,0xd4,0x4a,0x04,0xba,0x6e,0xa3,0xd1,0x6f,0x46,0x28,0x40,0x48,0xe3,0xc6,0xda,0x85,0x0c,0xfb,0xe1,0x8d,0x95,0xb3,0x0e,0x88,0x3a,0x51,0x05,0xa8,0x9e,0x39,0x1a,0x37,0xe2,0x33,0x43,0xa9,0x04,0x0f,0xf0,0x77,0x25,0xf6,0xf3,0x1f,0xe2,0x0b,0x31,0xe3,0x70,0x0b,0xd8,0x22,0xb3,0x68,0xa6,0x2b,0xc9,0x21,0x62,0xd1,0x9c,0xee,0xe1,0x30,0x0b,0x54,0xa1,0x25,0xb2,0x54,0x2b,0xac,0x02,0x11,0xb7,0x83,0x6a,0xf8,0xde,0xd6,0xc0,0x1e,0xd2,0xb9,0x31,0x0e,0x57,0x13,0x29,0xfe,0x20,0x52,0xee,0x54,0xc2,0x27,0x70,0xa5,0x0e,0x09,0x76,0xc6,0x86,0x0a,0x65,0xa3,0x96,0xb7,0x96,0x95,0x98,0xc2,0xa5,0xd6,0x4c,0x63,0x80,0x0e,0xff,0x48,0x84,0xa1,0xd3,0xe7,0x39,0xdd,0xca,0x48,0x70,0xe5,0xe7,0x3a,0x6c,0xc4,0xef,0xf4,0x62,0xaa,0xb4,0x0a,0x86,0x28,0xc8,0x13,0xad,0x43,0x43,0x96,0xea,0x78,0x06,0x76,0x86,0x67,0xfa,0x7d,0x38,0x1b,0x96,0xb2,0xb3,0x20,0xfc,0x8d,0x96,0xaa,0x08,0xec,0xdc,0x3b,0x41,0x9f,0x76,0xb1,0x39,0x00,0x04,0x41,0x12,0x70,0xba,0x1e,0x38,0x9e,0x37,0x48,0x9e,0xa7,0x83,0x04,0xfe,0xf6,0x5d,0x76,0x2e,0x17,0x9d,0x40,0x69,0x66,0x64,0x6b,0xda,0xc7,0x55,0xff,0x2f,0xd8,0x02,0x6f,0x15,0x4c,0xc1,0x70,0x8a,0x80,0xca,0x26,0xd6,0x00,0x51,0xad,0x08,0xef,0x39,0xc2,0x57,0x88,0x91,0x69,0x80,0xef,0x4b,0xe5,0x59,0x22,0xe7,0x50,0xc9,0xe1,0x6b,0x2a,0xd7,0x6a,0x7e,0xa1,0x52,0xd3,0xac,0x60,0xe2,0x68,0xa1,0xa7,0x56,0x1a,0xad,0xbf,0x28,0xc9,0x96,0xc7,0x64,0x01,0x60,0x8a,0x66,0x0c,0xa9,0x45,0xb3,0x8d,0x96,0x46,0xe8,0xb0,0xd5,0xb5,0x58,0x6d,0x73,0x55,0xe6,0x7d,0xba,0x7f,0xac,0x8f,0x48,0x84,0x79,0x4b,0x5c,0xe6,0x2d,0xa9,0x31,0x6f,0x49,0x8d,0x79,0x23,0xb8,0xcf,0xa3,0x47,0xbc,0x4e,0xa4,0x51,0xa2,0x0d,0x07,0xc6,0x1d,0xc7,0x07,0xcb,0x3a,0x01,0x99,0x5f,0x65,0x57,0xa1,0xac,0x34,0xf3,0xb4,0x84,0x48,0x12,0x4c,0x39,0x6f,0x53,0x2a,0x1d,0x2d,0x15,0x3f,0x6b,0x0e,0xb0,0x24,0x00,0xed,0x7a,0xdf,0x55,0xec,0xa7,0x4b,0x06,0xad,0x32,0xd5,0x56,0x5a,0x16,0x37,0xfd,0x13,0x38,0x70,0xdb,0x28,0x1f,0xeb,0x82,0x2d,0x04,0x45,0x08,0x21,0x6a,0xd9,0xe2,0xbb,0xb8,0x89,0x9f,0xfb,0x0c,0x61,0x36,0xf6,0x44,0xce,0xb1,0x27,0x84,0xaf,0x80,0xfa,0x35,0x75,0x74,0x0e,0xa5,0x3f,0x7e,0xec,0x8a,0xa7,0x4c,0x5d,0x60,0xa0,0x10,0x83,0x81,0xaa,0x2c,0xca,0x2d,0x30,0x8f,0x24,0x72,0x43,0x62,0xc3,0x0f,0x41,0x79,0xa6,0x19,0x28,0xa0,0xa8,0xd4,0x9f,0x70,0x3d,0x08,0x5d,0x64,0x14,0x42,0x6a,0xce,0x46,0x14,0xb8,0xa1,0x9d,0xea,0xc4,0xf5,0xb1,0x5d,0x78,0xef,0x74,0xc1,0x61,0xb4,0x32,0xaa,0x10,0x6c,0x1e,0xd5,0x9d,0x45,0x54,0x86,0x66,0x05,0x6b,0xdf,0xc9,0xe5,0x59,0x8e,0x67,0x28,0x1f,0x54,0x0c,0xe5,0x35,0xe6,0xad,0x41,0x35,0x44,0x15,0xf4,0x67,0x1d,0x4d,0xcd,0x58,0x36,0xd0,0xdb,0xd0,0x24,0x22,0x48,0xc0,0x53,0x4f,0xcd,0x4a,0x53,0x10,0x1a,0x4f,0x38,0x63,0x9e,0xf1,0x36,0x82,0x8b,0x5b,0x67,0x42,0x03,0x18,0x6e,0x42,0x9a,0xa9,0x8d,0x5a,0x12,0xb7,0x8d,0xea,0xd5,0x36,0x4a,0x87,0x04,0xc7,0xfe,0x64,0x18,0x87,0x6b,0xda,0xcc,0xc1,0x70,0x34,0x0e,0xa7,0xe1,0x2d,0x7b,0x7a,0x10,0x65,0xef,0x23,0x46,0x00,0x97,0xa4,0x65,0x9f,0x47,0xf4,0xf1,0x56,0xad,0xe8,0xc6,0x9f,0x2b,0x4c,0x2c,0x5e,0x20,0x5e,0x5d,0x05,0x10,0x6e,0xc0,0x92,0x2e,0x08,0x0a,0x6f,0x78,0x46,0xb7,0xa3,0x15,0x5d,0x81,0x2b,0xbd,0xd5,0x57,0x0b,0xa2,0x6c,0xac,0xf6,0x0b,0x74,0xbb,0x5c,0xa0,0x01,0xaa,0xf4,0xc6,0x9a,0xd1,0x54,0xeb,0xdb,0x4a,0x7d,0x73,0x59,0x84,0x5b,0xba,0xa3,0x8a,0x06,0x39,0xd3,0x57,0x62,0x83,0x38,0xa7,0xee,0x1c,0x7f,0xe7,0x73,0xda,0x44,0xf9,0xf0,0x35,0xcd,0xef,0x22,0x08,0x97,0x78,0x44,0xcc,0x22,0x3d,0x9d,0x8c,0xe6,0xe8,0xe1,0x14,0x3f,0xe8,0x9e,0x6c,0xff,0x2d,0x0f,0x18,0x56,0xf5,0xc3,0xad,0x51,0x02,0xae,0x95,0xa9,0x3f,0x08,0xb7,0xb4,0x94,0x43,0xdd,0x83,0x29,0x4d,0x54,0x11,0x84,0xc6,0xe7,0x8a,0x6e,0x2b,0xae,0x09,0x1f,0xaa,0x48,0x59,0xf1,0xf1,0x5a,0xc2,0x24,0x31,0xfa,0x3d,0xa3,0xf2,0x1a,0xb1,0x9e,0x01,0x67,0x06,0x40,0x77,0x0a,0xe9,0x8b,0x7d,0x85,0x13,0x98,0x60,0x79,0x0a,0x4b,0x03,0x9a,0xa9,0x13,0x1e,0xa6,0x42,0xcb,0xce,0xa0,0xe4,0x50,0x8b,0x13,0x85,0x5e,0x23,0x2a,0x0c,0xb3,0xca,0xba,0x20,0xc1,0x97,0x2b,0x4a,0x29,0xf7,0x79,0x07,0x51,0xc0,0xa0,0xe3,0xef,0x44,0xd1,0x1c,0x9b,0x02,0x5a,0x93,0xa0,0x84,0xb4,0x1b,0x5d,0x3c,0x5c,0xe8,0x8b,0x32,0x1e,0x9d,0x08,0x13,0xf2,0x23,0xa1,0xa3,0xe7,0x93,0x41,0x21,0x71,0x19,0xd2,0xea,0x58,0x0b,0x3d,0xd6,0x80,0x3a,0x40,0x5d,0x25,0x1c,0xb4,0xa4,0x83,0x3e,0x18,0x5b,0xbc,0x97,0x5a,0x9a,0xc6,0x29,0xad,0xa7,0x59,0x34,0xb0,0x78,0xaa,0x0f,0x57,0xfa,0x94,0x36,0x96,0xcc,0x74,0x1e,0x75,0xbb,0xc5,0x20,0xa7,0x96,0x73,0x69,0xb9,0xd2,0x6e,0x6e,0xda,0xad,0xc8,0xcc,0x68,0x37,0x16,0x2f,0x08,0x22,0xa4,0x1b,0x7c,0x89,0xf3,0xd4, +0x4a,0xb8,0x0b,0xf8,0x01,0xeb,0x80,0x29,0xfa,0x7c,0xa7,0x35,0x61,0x25,0x57,0x71,0xf1,0x4c,0xaa,0x1c,0xd2,0x56,0x0c,0x3d,0xef,0xe8,0xc4,0xf1,0x32,0x1e,0x72,0x84,0x82,0x9f,0xd3,0xee,0xff,0x50,0x56,0x59,0x10,0xa2,0x21,0x30,0x7a,0x3e,0x91,0xa7,0x56,0x68,0x6e,0x9f,0xf2,0x71,0x1e,0x1c,0x97,0x86,0x62,0x36,0x94,0x01,0xf7,0xb0,0x04,0xb0,0xdf,0xaa,0xce,0x5f,0x56,0x00,0xe3,0x04,0xf2,0x79,0x01,0x0d,0x74,0x1d,0xc5,0xa8,0x1b,0xf9,0x06,0x1e,0xb5,0xb4,0xd9,0x09,0x55,0xee,0x22,0xaf,0xef,0xa9,0x7d,0x44,0x6d,0xd3,0xa6,0xba,0xc3,0xce,0x7a,0x17,0xcd,0xd5,0x7b,0xa0,0x1a,0xb6,0x4b,0x36,0xa6,0xd9,0x30,0x65,0xa6,0xcf,0xd5,0x87,0xe8,0xbe,0x1b,0x09,0x9b,0x03,0x13,0x98,0x4a,0x28,0xb3,0xc3,0xa1,0x77,0xa5,0x7e,0x8b,0xde,0xdb,0xd8,0x3b,0xb4,0x2e,0x37,0x12,0x55,0x4e,0x2c,0x95,0x08,0xbe,0x6f,0x82,0xc1,0x8e,0x60,0xeb,0xb7,0xf3,0x73,0x1d,0x04,0x6e,0x11,0xbd,0x1f,0xed,0xc6,0xf4,0x94,0x96,0x8d,0x51,0xc3,0xf9,0xf9,0x22,0x78,0x58,0x23,0x72,0xd0,0xe1,0xb0,0x68,0x98,0x36,0xad,0x20,0x38,0xf5,0x17,0x20,0xaa,0x3a,0x1b,0x23,0x01,0xb9,0xa5,0x15,0x59,0x6b,0x6b,0xf4,0x5b,0x7f,0x81,0x2f,0x57,0x6a,0x06,0x5f,0x50,0x99,0x48,0xc2,0x1d,0x42,0x68,0xa1,0x37,0xf7,0xd1,0x87,0xe0,0x98,0xc2,0xc5,0x77,0x11,0x75,0x6e,0xd1,0xda,0xf9,0xf9,0xf6,0xe2,0x42,0xd1,0x14,0xec,0x4d,0x71,0xc6,0x59,0xdb,0x6e,0xb4,0x83,0x05,0x3b,0xfa,0xbb,0xe5,0x2e,0xd9,0xe6,0x12,0x69,0xee,0xd6,0xdf,0xab,0x3b,0x4c,0xad,0x63,0x70,0xbf,0x7d,0xd1,0xd7,0x72,0xe4,0x1d,0x21,0xa3,0x3d,0x8d,0xed,0x70,0xb8,0xe3,0xbf,0x3e,0x7e,0xa2,0x2f,0xc4,0x52,0x84,0x4e,0xe7,0xc1,0x1d,0x50,0xce,0x5d,0x70,0x34,0x78,0xa4,0x50,0x77,0x84,0x62,0xe9,0xbc,0xa0,0xae,0xdc,0xd9,0x75,0xa4,0xde,0xd9,0x68,0x4d,0x00,0xd1,0x8a,0x09,0x05,0xd5,0x63,0x4d,0x14,0x64,0x6c,0x74,0xe4,0xbd,0x0b,0xd4,0xfe,0x58,0xba,0x46,0xe0,0xac,0x09,0xc2,0x89,0x29,0x37,0x8b,0xa6,0x62,0xb4,0x46,0x7d,0x6c,0xe5,0x05,0xb4,0x0d,0x06,0xc7,0x6f,0xfa,0xd4,0xe1,0x04,0x3a,0x34,0x40,0x51,0x0f,0x4e,0xd9,0x03,0xb8,0x84,0x3c,0x63,0x6f,0x45,0x03,0x9e,0x44,0x04,0xd9,0xc9,0x28,0xa5,0xb3,0x68,0x02,0x81,0xa7,0xb5,0xb2,0x08,0x73,0x73,0x35,0xa0,0x6a,0xa9,0xbd,0xdf,0x70,0x16,0xe2,0x84,0x9c,0x58,0x43,0x82,0x28,0x76,0xcc,0xde,0x8b,0xd2,0xc4,0xa0,0x16,0xa2,0xa0,0x94,0x99,0xda,0x28,0x37,0x51,0x5b,0x08,0x34,0x38,0x45,0xae,0x22,0xb8,0x73,0x53,0x87,0xa3,0xa5,0x6d,0x06,0xa7,0x10,0x1f,0x7a,0x50,0x8d,0xd1,0x30,0xaf,0x58,0x10,0xa5,0x11,0x3d,0xd6,0xb0,0x88,0x56,0x2c,0x1d,0x75,0x74,0xc0,0xe0,0x4e,0xf4,0x1a,0x3c,0x23,0x0a,0xf8,0xf5,0x4b,0xe0,0x01,0x82,0xec,0x02,0x5a,0x69,0xcd,0xfe,0xff,0xb5,0xe6,0x6b,0xb9,0xc1,0x06,0xb2,0x58,0xa8,0x60,0x85,0x3d,0x63,0x21,0x36,0x85,0x8c,0x7c,0xeb,0x08,0xe2,0xcf,0x0d,0x4a,0x73,0xb9,0x17,0x91,0x8f,0x26,0x01,0xfa,0x18,0xd0,0x0b,0xd5,0x71,0xc2,0xdb,0x2c,0x59,0xa1,0x5d,0x91,0xfb,0xa9,0x12,0xa1,0x14,0x86,0x3d,0x11,0x72,0xdf,0x8c,0xed,0x38,0x89,0x7e,0xec,0xb9,0x71,0x32,0x8c,0x8f,0x31,0xb1,0x76,0x45,0x75,0x2d,0x27,0xb4,0x96,0xe8,0x26,0x46,0x38,0x81,0xa0,0xde,0x8e,0x83,0x63,0xd6,0x3a,0xc8,0x14,0xd4,0xea,0x22,0x92,0xb1,0xe8,0x73,0x37,0x8f,0x16,0x8f,0x0d,0x49,0xbb,0x36,0x17,0xe6,0xe8,0x6b,0x77,0x71,0xd6,0xbe,0xdc,0x85,0x39,0x8c,0x27,0xea,0x0a,0x43,0x34,0x83,0x61,0x64,0x59,0xc0,0x35,0xab,0xee,0x48,0x0e,0xf8,0x50,0xa9,0xd1,0x20,0x69,0x88,0xf2,0x97,0xc4,0x62,0x42,0xbe,0x1e,0x04,0x04,0x76,0xb0,0xc2,0x22,0x38,0x82,0x7a,0xf7,0x71,0x3f,0x6b,0x8e,0xc1,0xe6,0xda,0x1a,0x45,0x77,0xc6,0x71,0xd7,0x0b,0x8c,0xa5,0x91,0x36,0xfa,0x65,0x97,0xa7,0x3b,0xd5,0xb4,0x57,0x8a,0x3a,0x1d,0x02,0x51,0xc8,0x72,0x2b,0x76,0xa0,0x27,0x7c,0xbd,0xae,0x1e,0x31,0xab,0x7d,0xc4,0x8b,0xb9,0xc5,0x1e,0xdf,0x12,0xf7,0x6d,0x66,0xf9,0x7f,0x12,0x1b,0x7c,0x4f,0x79,0x7f,0x12,0x11,0x56,0x29,0x3d,0xac,0xc9,0xae,0x50,0x1e,0x5c,0x34,0x21,0xef,0x58,0x24,0x59,0x07,0x96,0xbd,0xce,0xf2,0x62,0x3a,0xdb,0x1f,0xee,0x8b,0x6c,0x3f,0xf3,0x54,0x5d,0x06,0x23,0xfc,0x6d,0xbb,0x7f,0x58,0xa2,0x3c,0xab,0x1d,0xae,0xb2,0x42,0x74,0xa4,0x3c,0x13,0x07,0xbe,0xd2,0x0a,0xae,0x61,0xe9,0xdd,0x3a,0x2e,0x96,0xd7,0x5d,0xb2,0x93,0x87,0x33,0x92,0xaa,0x89,0x3f,0x6f,0x06,0x8f,0x43,0x6c,0x7a,0xbf,0x33,0x68,0x29,0x6a,0x47,0xad,0xbf,0x6c,0x1d,0x24,0xa1,0x83,0xdf,0x15,0x16,0x96,0x13,0x61,0x3c,0x2d,0xd9,0xd8,0xea,0xd4,0x92,0xe9,0x60,0x84,0xb5,0x3e,0x95,0x96,0xfa,0xba,0x5b,0x7f,0x6f,0x74,0x88,0xb9,0xaa,0x41,0x75,0xee,0x21,0x4b,0x82,0xe8,0x78,0x58,0x9b,0xeb,0x90,0xe3,0xe4,0xb5,0xdb,0x11,0x66,0x8e,0x1d,0x61,0xe6,0xda,0x11,0x06,0x6a,0x1a,0x1f,0xd9,0xf3,0x8e,0x37,0x7c,0xf4,0x9e,0x63,0x9d,0x6e,0xb6,0x74,0xea,0x5b,0x73,0x30,0xfd,0x68,0x44,0x84,0x91,0xc4,0x4e,0xdd,0x6c,0xad,0x78,0x6a,0xeb,0xda,0xff,0x99,0x1b,0xfa,0xb8,0x7c,0xaa,0xb6,0x12,0x9a,0xfa,0xbd,0xb1,0xb2,0xe3,0x30,0x78,0x3f,0x7f,0xfd,0x15,0xed,0x06,0x7a,0xc8,0x97,0xf4,0xc8,0xda,0x84,0xbe,0xb7,0x97,0x68,0x97,0x4d,0x1c,0xad,0x49,0xed,0x7b,0xfd,0x80,0x0d,0x39,0x3f,0x9c,0x88,0x74,0xc3,0xa7,0x9d,0xb5,0xc1,0x4c,0x8d,0x56,0x25,0x66,0x79,0x49,0x20,0x91,0x42,0x4a,0xd1,0xb9,0x11,0x0d,0x38,0x4f,0x34,0xd9,0xb2,0x65,0x8f,0xa6,0x1d,0x91,0x6d,0x1a,0x33,0xea,0xb3,0x2f,0xb6,0x47,0x74,0x76,0x24,0xf2,0xe8,0x94,0xc4,0x74,0x34,0x6e,0x91,0xda,0xd7,0xdd,0xf9,0xe3,0x0e,0xcb,0xb6,0x53,0x53,0xb3,0x63,0x92,0xf7,0xa9,0x99,0x6a,0xc6,0xbb,0x15, +0x34,0x5f,0xca,0x8b,0x3e,0xab,0x46,0x6d,0xfb,0x83,0xf6,0x43,0xb5,0xd8,0x78,0xe8,0xef,0xe7,0xd1,0xe5,0x2f,0xcf,0x25,0x3a,0xf7,0xe8,0x97,0xeb,0xcb,0xeb,0xfe,0x8b,0x90,0x63,0x9c,0xed,0xaf,0xb7,0xd7,0xab,0xeb,0xc9,0xf8,0x69,0x30,0xaa,0xde,0x5f,0x5f,0x0e,0x5f,0xf8,0xc3,0xf0,0x39,0x95,0xbd,0x7a,0x71,0xe0,0x40,0x48,0x2f,0xa9,0x8e,0xde,0xe8,0x97,0xf0,0x4f,0xd7,0xa3,0xeb,0x9e,0x1a,0x3f,0x7d,0x72,0x59,0x76,0xf4,0x8b,0x9a,0x01,0x4f,0x25,0x14,0x62,0x82,0x58,0x9a,0x08,0x40,0x59,0x31,0x3f,0x02,0x17,0x63,0x5d,0xb0,0x8d,0xd5,0x32,0xcc,0x7b,0xb0,0xaa,0x47,0x84,0xeb,0xb0,0x6c,0x4c,0xcb,0xd7,0x55,0x8e,0x4a,0x7f,0xd2,0x88,0x21,0x93,0x3c,0xfa,0x69,0x19,0x82,0x13,0xcc,0x96,0xae,0xe3,0xa5,0xb1,0xaa,0x1e,0x6e,0x35,0x6b,0xa3,0x63,0x74,0x86,0x3e,0x42,0xe4,0x96,0x8f,0xe0,0x8a,0xf2,0xaf,0xd4,0x5d,0x0f,0xf6,0x80,0x08,0xdc,0xa6,0xbe,0x13,0xa0,0x0e,0xa5,0xaf,0xa3,0xcc,0x23,0x18,0xf7,0xc2,0xd5,0x9a,0xce,0x11,0x98,0x76,0xc1,0x01,0x4d,0x84,0x5e,0xe6,0x84,0x65,0x8b,0x1d,0x77,0xd6,0x98,0x5e,0xa9,0x59,0x97,0xb3,0x05,0xd5,0x70,0x94,0xc1,0xff,0x36,0xac,0x16,0xa1,0xc6,0xf5,0x90,0x92,0xb6,0x21,0x35,0xbb,0xaf,0x9c,0x80,0xd7,0xfe,0x03,0xdb,0x87,0xb6,0x98,0x0a,0x12,0x85,0xea,0x86,0xf2,0xce,0xf9,0xce,0x0d,0xaa,0x59,0x9a,0x90,0x19,0x24,0x58,0x8f,0x6c,0x8e,0xad,0xaa,0x67,0xde,0xd1,0xb6,0xb0,0x6c,0x9e,0xc8,0xfc,0xe4,0x79,0xc6,0x32,0x53,0x90,0xf3,0xa5,0x3b,0x4c,0x8e,0xd8,0x9c,0x1c,0xde,0xbd,0x14,0xac,0x69,0x31,0x72,0x1a,0xd5,0x1a,0x80,0x2c,0xc6,0xad,0x49,0xe6,0x05,0x7a,0x79,0x54,0xe2,0xf8,0x79,0xbe,0xb8,0x1a,0xba,0x28,0x11,0xf6,0x62,0xa9,0x35,0x25,0x6c,0x99,0xb3,0x5a,0x3b,0x5f,0xe8,0x18,0xea,0x4c,0xc7,0x76,0xae,0x02,0x8e,0x05,0xdd,0xaa,0x67,0x7b,0xf4,0xc3,0xbe,0x04,0x2f,0x6e,0x53,0xfd,0x75,0x74,0xc9,0xa6,0x31,0xdf,0xf9,0xf9,0xa7,0x96,0x7e,0x64,0x31,0xb5,0xed,0x85,0xf1,0x35,0x3f,0x8a,0x05,0xfd,0x2b,0xf5,0x37,0x09,0x66,0x76,0xbd,0x7b,0xea,0x3f,0x1f,0x5d,0xdf,0x5f,0xff,0x34,0xee,0xbe,0x08,0x46,0xbf,0xbc,0x18,0x3f,0x3d,0xfc,0xc9,0x8d,0x67,0xf6,0x65,0x64,0xb3,0x06,0xb4,0x42,0x70,0xae,0x26,0x7c,0xb8,0x55,0xd6,0xd5,0x92,0xf2,0xaf,0x5a,0x3a,0x29,0xc8,0x99,0xe8,0x03,0xe1,0xdb,0xfb,0x63,0x3a,0xad,0x5f,0xc8,0x75,0x19,0xed,0x6b,0x5c,0x86,0xf1,0x7c,0x11,0x7d,0x34,0x1c,0x89,0xa4,0x81,0x8d,0x25,0xc6,0xe1,0xdf,0x4c,0x34,0x27,0xc5,0x1e,0x2d,0xf9,0x08,0xc5,0x0d,0x25,0x0e,0x0a,0x32,0xe9,0x49,0x86,0x81,0x21,0x24,0x73,0x69,0xa0,0x57,0x3a,0x08,0x1b,0xd1,0xdf,0x13,0xfb,0x8e,0x5d,0x5c,0x4c,0x54,0x28,0x22,0xe3,0xcf,0x08,0xbe,0xf6,0xf1,0x2a,0x45,0x97,0xb7,0x43,0xec,0xd2,0x30,0x51,0x6e,0x1a,0x00,0x78,0xa9,0x21,0x2c,0x36,0x88,0x1d,0xfe,0x52,0x25,0xc6,0x8a,0x8d,0xb7,0x66,0x4b,0xac,0xaa,0x8c,0x97,0x55,0x7d,0x2e,0x6b,0xc4,0xad,0x35,0xc3,0x9c,0x27,0x12,0x30,0x37,0x87,0x1a,0x84,0xc6,0xe4,0x22,0x59,0x8e,0x78,0x4f,0x54,0xfe,0x50,0x5f,0x10,0x57,0x47,0x77,0x32,0x2a,0xb6,0x9c,0x24,0x32,0x1a,0x4f,0x06,0xce,0x52,0x58,0xf6,0x2d,0xca,0xea,0x4e,0xe9,0x39,0x02,0x4c,0x81,0xc9,0x96,0x8a,0x89,0xbd,0x9a,0x28,0x67,0x17,0x47,0x44,0xdc,0xbb,0x35,0x38,0x8e,0x6e,0xf6,0x83,0xb8,0xfa,0x81,0xec,0xc5,0xb0,0x16,0x24,0x77,0x58,0x9e,0xe4,0x3d,0x04,0xe5,0xfc,0x30,0xd4,0xbf,0x0c,0xa1,0xfe,0x16,0x1f,0xd8,0x50,0xce,0xbe,0x54,0x19,0x1c,0x07,0x5f,0x3a,0x6a,0x35,0xc0,0xa0,0x7a,0x15,0x6d,0xa1,0x25,0x04,0xd4,0xbd,0x16,0xe8,0x15,0xce,0x60,0x77,0x80,0x85,0x21,0xdd,0xfe,0xb0,0xda,0x17,0x8b,0x03,0x7b,0x3a,0x5f,0xaa,0xbf,0x47,0x0f,0x6c,0x12,0x47,0x25,0x58,0x43,0x28,0xb6,0x2b,0x3b,0x5c,0x73,0x14,0x37,0x68,0x08,0xe9,0x33,0x28,0xf9,0x06,0x15,0x1c,0x07,0x83,0x9b,0xa6,0x9e,0x7b,0x6b,0x3a,0xe6,0x32,0xd9,0xee,0x46,0x6e,0xc7,0x5d,0x12,0x7e,0xa9,0x3f,0x88,0x9f,0xa7,0x83,0xb8,0x81,0xbf,0x24,0x49,0x04,0xbc,0x6b,0x1c,0xfc,0x45,0xdc,0xcd,0x62,0xbd,0xcb,0xdd,0x8c,0x0a,0xd5,0x70,0xdd,0x1a,0xbd,0x1a,0x64,0x0b,0xe5,0x1c,0xec,0x3e,0x1b,0x98,0x56,0xe8,0x1f,0xde,0x9d,0x16,0x2d,0x30,0x6c,0x59,0xb1,0x7e,0x89,0x28,0x11,0x54,0x1a,0x12,0x67,0x10,0x33,0x83,0x9a,0x97,0x16,0x47,0xf9,0xb2,0x76,0x0a,0x57,0x10,0xd8,0x4e,0x87,0x53,0xb1,0x7c,0xd1,0xf6,0xad,0x75,0xef,0xf9,0x13,0xe7,0x12,0xc7,0xd0,0xa6,0x89,0xb1,0xea,0xe7,0x8a,0xb2,0xad,0x91,0x92,0xc2,0x6a,0x40,0x2a,0xf8,0x18,0x92,0x13,0x36,0x5f,0xa0,0xe6,0x5b,0xed,0x38,0x86,0x2d,0xb1,0xbc,0xf5,0x59,0xbd,0x65,0xff,0x23,0x01,0xde,0xc0,0xc4,0xe7,0x16,0xb4,0x6b,0x50,0x06,0x30,0x52,0xa8,0x71,0x05,0x23,0x27,0x7d,0xe1,0x46,0x02,0xd2,0x0b,0x4e,0x8c,0x0a,0xf1,0xf3,0x80,0x22,0xf8,0xe5,0x1a,0x1c,0x8b,0x70,0x4b,0x2a,0xce,0xb2,0xda,0xf2,0x9d,0x38,0xf6,0xdc,0x91,0x55,0xb2,0x8c,0xd0,0x66,0x25,0xfe,0x74,0xcb,0x1f,0x73,0x2c,0x68,0xaa,0xf2,0xb3,0x7a,0x76,0x12,0xb7,0x52,0x7a,0xef,0x9b,0x90,0xe9,0xb5,0xbc,0x17,0x61,0xed,0xde,0x00,0x2b,0x24,0x9b,0x6e,0x5c,0xe5,0xff,0x90,0xae,0xd6,0xc9,0xed,0xab,0x2a,0xb9,0x5d,0xea,0xe9,0x75,0x74,0xe8,0x87,0x16,0x33,0x15,0x63,0x1d,0xd2,0x74,0x7c,0x4d,0xb4,0x3b,0x9a,0x8b,0x2b,0xb5,0x47,0x94,0xde,0xcf,0x6d,0x43,0x04,0x62,0x70,0xb5,0x9c,0x81,0x2d,0xcd,0xbb,0xfe,0x94,0x95,0x79,0xfd,0x33,0x0e,0xc4,0xbe,0xaa,0x9b,0x99,0xe8,0xc2,0x18,0x7e,0xc5,0xd8,0x37,0x38,0x0a,0x8e,0x38,0x55,0xb6,0x6e,0xd9,0xac,0xeb,0x26,0x68,0x38,0x39,0x84,0x96,0xea,0x1f,0x2b,0x7e,0xa2,0x85,0xdf,0x1f,0xb3,0xdb,0x0e,0x0f,0x1a,0x35,0xfd, +0x81,0xa9,0xaa,0xdb,0x6a,0xe3,0xd3,0x9d,0xdc,0xb4,0xae,0xcb,0x6f,0xf0,0x89,0x2c,0x27,0x18,0xc9,0x49,0x02,0x87,0x87,0xe7,0xdc,0x36,0x16,0x0f,0xb7,0x7e,0xef,0xb2,0xfc,0x28,0x6c,0x10,0x75,0x4b,0x61,0xf0,0x4a,0x5e,0x31,0xd9,0x72,0x50,0xa1,0xa1,0x78,0x21,0x52,0x59,0x73,0xc6,0x86,0x3e,0x17,0xd8,0xe7,0xcb,0xcd,0x22,0xde,0xe7,0x1e,0x9b,0x64,0x46,0xb6,0x18,0x84,0x8c,0xf6,0x08,0x27,0x9c,0x19,0xbb,0x91,0x39,0xd9,0x5d,0xa4,0xba,0x61,0x11,0x24,0x3d,0x1e,0x97,0x54,0x8f,0xa3,0x94,0x71,0xf2,0xf0,0x24,0x25,0xed,0xe8,0xf1,0xfc,0x56,0x6c,0xef,0x3f,0x09,0x58,0xf3,0x98,0x06,0x2a,0x23,0x32,0xa7,0x8e,0x97,0x32,0x96,0xcc,0x59,0x8e,0x23,0x63,0x65,0x84,0x83,0xdb,0x21,0x5c,0xf6,0xff,0xce,0x36,0xc0,0x15,0x44,0x91,0x07,0xea,0x75,0x29,0x24,0x83,0x03,0xc2,0xbb,0x7c,0xcb,0x42,0x0d,0x55,0xc3,0x2f,0x79,0x60,0x48,0xbe,0xaf,0xa2,0x4b,0x62,0x0b,0x5d,0x06,0xb0,0x7b,0x39,0x2d,0xb7,0xfe,0xd7,0xe5,0xa6,0x7d,0xb0,0x92,0x6a,0xbd,0xbf,0xb5,0x63,0xac,0xff,0x15,0x0b,0x3e,0xdd,0x69,0x22,0xe8,0x81,0x64,0x19,0x56,0x70,0x44,0x5d,0x13,0x3e,0xf8,0x9c,0x10,0x6a,0x42,0x0d,0xef,0x2a,0x8e,0x78,0x71,0xd4,0x82,0x92,0xd1,0x62,0x58,0xa6,0x65,0x39,0x2a,0xed,0xdd,0x69,0x95,0xb8,0x72,0xae,0xb1,0x06,0xf9,0x02,0xa9,0x73,0x6a,0x47,0x6b,0x1e,0xb1,0xd1,0xe1,0x9a,0x68,0x34,0x78,0xd9,0x53,0x1f,0x06,0x46,0x31,0x3b,0xc0,0x17,0xc1,0x43,0x1a,0x4d,0x8d,0xd4,0x55,0x8b,0x10,0xba,0xdd,0xd9,0x73,0x73,0xae,0x04,0x93,0xd1,0xcc,0x28,0xb2,0x52,0x08,0x73,0x53,0x10,0x64,0x6c,0x76,0xce,0xee,0x8b,0xfb,0xf5,0xe6,0xdb,0xd5,0xab,0x78,0xb1,0x13,0x1b,0x19,0xf3,0x19,0x5b,0xc1,0x04,0x47,0x9a,0x93,0x7c,0xb9,0xde,0x7e,0x60,0xdb,0x13,0x58,0x36,0x26,0x30,0x8e,0x41,0xd1,0x49,0x94,0x42,0x8d,0xcb,0x19,0x2a,0xe6,0xd1,0x43,0xe5,0x24,0x28,0x6d,0x77,0xb5,0xb2,0x3b,0xa9,0xd4,0x4d,0xc3,0x9c,0x9a,0xb3,0xb1,0x34,0x6b,0x3d,0xcb,0xd8,0x12,0x57,0xd6,0x22,0xa9,0x27,0x7e,0x72,0x69,0xad,0x14,0x9b,0x42,0xe0,0x04,0xaa,0x5a,0xa2,0x64,0xe8,0xd1,0xe1,0x60,0xcf,0xdb,0x90,0xcd,0x45,0x0d,0x2f,0x59,0x52,0x0c,0x26,0x4e,0x3e,0x3c,0xb4,0x38,0xa5,0x58,0x70,0x74,0x12,0x11,0x28,0xdd,0xd1,0xc2,0xc0,0x17,0xfc,0x03,0x11,0x59,0xb1,0x65,0x5c,0x06,0x62,0x6c,0x1a,0x84,0x16,0x1a,0xc6,0x88,0x73,0x10,0x9e,0x5f,0x27,0xd3,0xc0,0xb0,0x68,0x34,0xa0,0x25,0x82,0x89,0x11,0x2f,0xa7,0x10,0x2f,0xa7,0xcf,0xe1,0x1d,0x32,0xbb,0xb8,0x38,0x9a,0xb6,0xeb,0xf4,0x99,0x3d,0xf6,0xcb,0xda,0x88,0x5d,0x67,0xb2,0xc4,0x92,0x10,0x4d,0x23,0xcd,0xca,0x4a,0xb0,0x7d,0x93,0xae,0x5e,0x8b,0xf4,0x5a,0x4a,0xe6,0xd1,0x54,0x54,0x30,0xec,0x51,0x5a,0x29,0xdd,0xb2,0xc4,0x9d,0xc9,0x51,0x2d,0xd6,0xee,0x79,0x5d,0xaf,0x88,0xf8,0x23,0x28,0x6f,0xa4,0x3e,0xd3,0x3c,0x3e,0x69,0xad,0xae,0x93,0xb3,0x3d,0x62,0xfe,0x53,0xb1,0xaf,0x24,0x72,0x29,0x11,0x78,0xce,0xb0,0x28,0x0a,0x14,0xb6,0xe9,0xd7,0x96,0xc1,0x43,0x63,0x21,0x4c,0xeb,0x3f,0x2e,0x01,0x4c,0x51,0xe3,0xce,0x92,0xa2,0xee,0x96,0xbe,0xce,0x7b,0xa6,0xd1,0x7a,0x6e,0x30,0xe7,0xbb,0xd6,0xfe,0x66,0x47,0x8b,0x47,0xe6,0x8e,0x7d,0xd4,0x37,0xee,0x9a,0x95,0x0a,0xd4,0x6f,0x4b,0x4f,0x66,0xe7,0xe9,0x77,0xc6,0xa8,0x43,0x23,0x5e,0x0e,0x0c,0x1a,0x0b,0xd3,0x64,0xa1,0x9e,0x63,0x20,0x6c,0xd7,0xcb,0x62,0x97,0x07,0xc3,0xdc,0x64,0xd8,0xe9,0x65,0xeb,0x55,0xce,0x0c,0x5e,0x5c,0x20,0x21,0x50,0xd8,0xf6,0xd9,0x7e,0x96,0xaf,0xca,0x6f,0x18,0x97,0x87,0x89,0xc6,0x09,0xc2,0xb5,0xc0,0xf7,0x5d,0x4f,0x5f,0x16,0x98,0x38,0xa4,0x1c,0xdb,0xa4,0x5e,0x8a,0x30,0x6d,0x89,0xcd,0x5e,0x22,0x57,0x41,0x65,0x62,0xac,0x72,0x78,0x34,0xf2,0x56,0xeb,0x7d,0x31,0xf9,0xe0,0xe1,0xb8,0x5d,0x4f,0xe1,0x8f,0xed,0x29,0x07,0x75,0xfa,0x9e,0xa0,0x16,0x8e,0x84,0xd2,0xf6,0xf4,0xd9,0x58,0x8d,0x3c,0xfa,0x6c,0xbd,0x78,0x07,0xd9,0x39,0x06,0x5a,0xab,0x00,0x48,0xf1,0xac,0xbd,0x96,0xea,0xab,0xbe,0x32,0x15,0x65,0x9e,0xd4,0xca,0x01,0x79,0x95,0x87,0x59,0xfb,0xef,0x56,0x7a,0xa5,0x74,0x3d,0xa8,0x14,0x4e,0x18,0x1e,0x42,0xe5,0x30,0x39,0x91,0x47,0x0f,0xc4,0x56,0xef,0xdb,0x20,0x2d,0xa3,0x13,0x60,0x71,0x1f,0x7f,0xd8,0xb5,0x6d,0x52,0x59,0xcd,0x12,0xf8,0x64,0x55,0x1b,0xc0,0xe8,0xf1,0xfa,0x78,0xad,0x3e,0x24,0xbc,0xd8,0xda,0x64,0x01,0xf4,0x50,0xb1,0xa9,0xf4,0x42,0x38,0x35,0x5b,0x63,0x79,0x04,0x9a,0xa5,0xac,0x86,0xd8,0x14,0x3c,0x97,0xaa,0x76,0xea,0xc0,0xe5,0x7f,0x47,0xd9,0xe8,0xe3,0x31,0xc8,0x68,0x7d,0x35,0x98,0xd0,0xef,0xd5,0x78,0xec,0x37,0x1a,0xcf,0x39,0x2c,0x63,0x5b,0x1e,0xbe,0x41,0x1d,0x78,0x1d,0x88,0xb7,0x97,0xcc,0x8b,0x08,0x3c,0xf9,0x20,0xae,0x01,0x63,0x66,0x1b,0xf4,0xf4,0x2a,0xeb,0x89,0xc3,0x3d,0x67,0xac,0x0c,0x13,0xea,0x4c,0x7f,0xdc,0xf5,0xb0,0xc3,0xbd,0xb1,0xb4,0x9b,0x4b,0xea,0x23,0xdb,0x3a,0xc2,0x7c,0xaa,0x58,0xa2,0xe7,0x06,0x65,0x6b,0x47,0x85,0x19,0x75,0x2d,0x01,0x1d,0xef,0xc4,0x7e,0xb9,0xe3,0xa7,0xbe,0x35,0x0d,0xab,0x7b,0xae,0x48,0xe9,0x99,0xe4,0xa4,0x73,0x92,0xe3,0xd1,0xc1,0x59,0x9f,0x1d,0x35,0x67,0x4e,0xd6,0x4f,0x9e,0x4f,0x02,0x1d,0x18,0x25,0xd3,0x93,0xc5,0x86,0x50,0x31,0xb3,0xa0,0xb6,0x73,0x4e,0xda,0x52,0xb0,0x18,0x3a,0x32,0xc2,0xdb,0x99,0xf8,0x23,0x20,0x2e,0xd9,0xe4,0x82,0xe7,0xe4,0x8e,0x55,0xd4,0xc1,0x60,0xce,0x41,0x45,0xff,0x68,0x2e,0x2f,0x2c,0x27,0x03,0x54,0x35,0x23,0xd0,0x9c,0x90,0xc9,0x70,0x6e,0xd0,0xc9,0xd4,0xa7,0x43,0x4d,0x7d,0x03,0xe5,0xa7,0x5c,0x7e,0x0b,0x02,0x2f,0xf4,0x27,0xdd,0xae,0x7a,0xbc,0x90,0x7d,0x9a, +0xea,0x75,0xc4,0xf2,0x04,0xf8,0x16,0xe1,0x63,0xbf,0x61,0xa2,0x41,0x63,0x9e,0x02,0x31,0x5e,0x02,0x05,0x4b,0xae,0xd4,0xac,0x32,0x97,0xe6,0x69,0x01,0x59,0x7b,0x13,0x51,0x9f,0x5c,0x50,0x07,0x12,0x9d,0xfb,0x0e,0x32,0x2b,0x81,0x9c,0x10,0x58,0x9a,0xb3,0xff,0xc4,0x97,0xeb,0xf5,0x0d,0x80,0xae,0xfd,0x0d,0x75,0xfc,0x86,0x48,0x24,0xda,0xfd,0x6f,0xb7,0x71,0x4a,0x1d,0x4e,0xba,0x57,0x2f,0x60,0xb3,0xc2,0x1d,0xfc,0xb6,0xa5,0x83,0xa9,0x06,0x39,0x3e,0x4a,0x74,0xd7,0x06,0xc9,0xf0,0x06,0x5a,0x2f,0xa7,0x15,0x62,0x7b,0x99,0x72,0x95,0xe6,0x7d,0xb7,0x91,0xe8,0x44,0x31,0x5f,0x3c,0xbf,0xf3,0xfd,0xdb,0x62,0x99,0xaf,0xef,0xf6,0xfe,0x0d,0xea,0x7e,0x64,0xff,0x02,0x7d,0x13,0xd0,0x8f,0x3e,0x1a,0x33,0xd3,0x3c,0xf5,0xfb,0x2a,0xae,0xae,0x23,0x0e,0x92,0xf0,0x1b,0x15,0x57,0x66,0x9f,0x09,0xc4,0x47,0x3e,0x4a,0x02,0x62,0x64,0xbf,0xe1,0x72,0xcf,0x1e,0x2b,0x97,0x05,0xc3,0x2c,0xfc,0x16,0xea,0x61,0x77,0x2f,0xe9,0xcb,0x47,0x12,0xfd,0x0d,0xed,0xf1,0x12,0x13,0x88,0xb0,0x27,0xdc,0xa4,0x49,0xac,0xa7,0x2d,0x24,0xd7,0x34,0x4a,0xe0,0x29,0x3a,0xa3,0x9f,0x4f,0xc6,0x83,0x7c,0x94,0x00,0xff,0x10,0x71,0x4c,0x1d,0x54,0x33,0x44,0xaa,0x45,0x4f,0x1d,0x00,0xc9,0xa2,0x19,0x31,0x62,0xa3,0x8f,0x2e,0xe2,0x31,0x7d,0x67,0x62,0x9b,0x29,0x9e,0x34,0xba,0x07,0x8d,0x42,0x20,0xca,0x5f,0x25,0x18,0x28,0x68,0x00,0x58,0x97,0x40,0x4c,0x3a,0x8e,0x5a,0xd0,0x37,0xbf,0xa9,0x20,0x19,0x58,0x29,0x6b,0xc1,0x60,0x78,0x82,0xb0,0xa8,0x7c,0x44,0xbd,0x35,0x84,0x08,0xcc,0x77,0xed,0xcc,0x4d,0x02,0x91,0xbb,0xf2,0x6e,0x9a,0x20,0x73,0x19,0xd1,0x5d,0xf7,0xb3,0xbc,0xd5,0x57,0xa5,0x9e,0x7b,0x33,0x8d,0x10,0xdf,0xc4,0x24,0x9f,0x44,0xbe,0xae,0x7a,0x12,0x2e,0x62,0x40,0x1c,0x08,0x82,0x8d,0x53,0xcb,0xfa,0x94,0x67,0x00,0x4d,0x1d,0x58,0x46,0xc1,0xa2,0xb8,0xaa,0x37,0x09,0x73,0xde,0x7a,0x1b,0x61,0xaa,0x2e,0x2e,0x88,0x18,0x9b,0xba,0xdb,0x97,0x99,0x41,0xda,0x21,0x90,0x46,0x3f,0x8f,0xc0,0x07,0x82,0x16,0x9a,0x0a,0x3a,0x67,0xfe,0xc0,0x94,0x56,0x53,0xbd,0xb3,0x60,0x8d,0xa2,0xec,0x51,0xcb,0xd1,0x88,0xf9,0xac,0x85,0x19,0x58,0x15,0xba,0x89,0x65,0xa3,0xa3,0x86,0xfe,0x0a,0x01,0x64,0x75,0xe1,0x53,0x39,0x23,0x03,0xc7,0x7a,0xe8,0x3b,0x2e,0xad,0x98,0x60,0x34,0x0d,0x39,0x99,0xd6,0x4a,0x00,0xd6,0x6c,0xe6,0x3f,0x20,0x99,0x45,0x06,0xc3,0xc3,0x6b,0x62,0xba,0xb7,0x2b,0xba,0xf8,0x3e,0x5e,0x4d,0xf3,0xc3,0xf7,0x98,0xc4,0x9c,0xe8,0x84,0x83,0x84,0xa7,0x39,0xb0,0x0d,0xf7,0x0f,0xdf,0xbf,0x0e,0x18,0x37,0x3f,0xb9,0x1c,0x9c,0xc2,0x35,0x91,0x73,0xc2,0xa4,0x88,0x69,0x08,0x09,0xfe,0x5a,0x82,0xcc,0xe8,0xcb,0xde,0x7d,0xbc,0x5d,0x11,0x18,0x9c,0x9f,0xff,0x43,0xeb,0xf6,0x7a,0x08,0x01,0x18,0xd4,0x8b,0x98,0xe4,0xd5,0xb6,0xa5,0x33,0xdb,0x12,0x7c,0x0d,0x13,0x62,0xea,0x76,0xbb,0x78,0x8a,0x48,0x41,0x8c,0x77,0x58,0xfc,0xb1,0x15,0x69,0xf5,0x17,0xa6,0x64,0xe4,0x12,0x02,0x15,0xc4,0xe3,0xe2,0x58,0x3e,0x7e,0x12,0x3a,0x3a,0x79,0x5a,0xbe,0xaf,0x80,0x91,0x08,0x9c,0xb9,0xd6,0x36,0x78,0xfa,0x5e,0x96,0x21,0x0e,0x46,0x9a,0x9e,0x19,0x57,0x2d,0x12,0x6a,0xfd,0x81,0x3a,0xdb,0x32,0x66,0x96,0x02,0xb5,0x19,0x4b,0xaf,0x14,0x17,0xff,0x29,0x2e,0xf6,0xa1,0xbe,0xae,0xec,0x0d,0x8e,0x30,0xd6,0xe9,0x0f,0x2f,0x2e,0x74,0xc5,0x5c,0x12,0x00,0xc3,0x15,0xc0,0xe4,0xd4,0xde,0x70,0x38,0xff,0x8e,0x24,0x4e,0xa9,0x94,0x7f,0xd1,0x3f,0x1c,0xbe,0xaf,0xc1,0xef,0x68,0x3b,0x66,0xf9,0xa2,0x99,0x40,0x1e,0x55,0x24,0x83,0x2b,0xa9,0x83,0x37,0xc0,0x37,0x3a,0x54,0x7f,0x2d,0x82,0xf5,0xcb,0x6f,0xbf,0xd6,0x2e,0xa7,0x5f,0xad,0xe3,0x0c,0xb1,0x11,0xdf,0x04,0xea,0xff,0xc4,0xed,0x85,0x25,0x7a,0xf5,0x1b,0xdb,0x1a,0x81,0xa4,0x07,0xa3,0x1d,0x64,0x30,0xf0,0x58,0x6b,0xca,0x8f,0xdf,0x60,0x47,0x20,0xd1,0x0b,0x15,0xd7,0xcc,0xaf,0xfb,0x06,0xe6,0xe2,0xf5,0x28,0x7e,0x74,0xff,0x26,0xdd,0xae,0x17,0x8b,0x61,0x65,0xb1,0x75,0x3b,0x1c,0xde,0xb8,0x19,0x7d,0xbb,0xb5,0xef,0x71,0x4b,0x41,0xd3,0x6f,0xd9,0x3e,0x6f,0xeb,0x86,0x75,0x3a,0x7f,0x9c,0x21,0x94,0x5a,0x4c,0xe9,0x61,0x20,0xe1,0xe4,0xd2,0x88,0x1c,0x6e,0x3e,0x78,0xc8,0x21,0x16,0x81,0xb4,0x64,0x06,0x7d,0x52,0x1a,0xbc,0xe5,0x6a,0x09,0xf3,0x8d,0x66,0x50,0x32,0x72,0xd5,0xd6,0xa5,0xbf,0x92,0x43,0x17,0x5f,0xd6,0xcf,0x2d,0xce,0x26,0x0c,0x6b,0xe5,0x39,0xeb,0x00,0xfc,0x52,0xa7,0x6f,0xb2,0x2b,0xd0,0x6c,0xcc,0x09,0xa9,0x26,0x0d,0x5d,0xa1,0x65,0x27,0x4b,0x39,0x7c,0xca,0x41,0x2b,0xb5,0xbe,0xcb,0x7a,0x03,0x24,0xec,0x45,0x4d,0x23,0x9f,0xd2,0x29,0x99,0x99,0x1c,0xa2,0x33,0x98,0xbb,0x9b,0x57,0x8e,0x07,0x40,0x3e,0x8c,0xc3,0xf9,0xd0,0xf4,0x23,0x08,0x0b,0x64,0xb4,0x67,0x61,0x0e,0xec,0x2b,0xd5,0x0f,0x6d,0xdb,0xab,0xaa,0xe4,0x96,0xd0,0x4f,0xee,0x7d,0xa7,0xeb,0xa8,0xc0,0x4b,0x40,0xfd,0x91,0x77,0x35,0x32,0x5a,0x4b,0x02,0x61,0x31,0xee,0xc0,0x55,0xf7,0xc7,0x1e,0xe7,0x63,0x3c,0xf2,0x6f,0x74,0xa5,0x7e,0x74,0x93,0x9d,0x73,0x20,0xa6,0xb6,0xd3,0x68,0xe4,0xd6,0x56,0x0b,0xf7,0xf2,0x40,0x7d,0x67,0xe1,0x9e,0xef,0xc6,0x97,0xac,0x7e,0x11,0x25,0xa1,0x16,0xe7,0x4b,0xa0,0x96,0xd2,0x4f,0x5f,0xb9,0xe5,0x94,0x36,0x94,0x4e,0x20,0x5a,0x9d,0x14,0xd3,0xbb,0x2d,0xcb,0x37,0x58,0x55,0x0e,0xa1,0x9d,0xda,0xe5,0xfb,0x86,0x4c,0xd8,0xa4,0x0c,0x15,0x8d,0x29,0x46,0x60,0xe2,0x27,0x37,0xb2,0x64,0x06,0xf9,0x68,0xdb,0xb3,0xc9,0x69,0x09,0x27,0x8e,0x09,0x20,0xab,0xf9,0x60,0xeb,0x65,0xb2,0xa0,0x9a,0xc7,0x34,0xaf,0xa7,0x72,0x77,0x14,0x26,0x02,0x94,0x1c, +0x6e,0xa5,0xd2,0x99,0xb0,0x36,0x1b,0xe0,0xaf,0x2a,0x0f,0xea,0xbd,0x22,0xf6,0x32,0x4d,0x09,0xbd,0x9f,0x92,0x7f,0x97,0x0d,0x1d,0x0e,0x49,0x8b,0xb0,0x96,0x9e,0xd9,0x22,0xe9,0xd0,0xea,0x68,0xd0,0xd7,0x50,0x54,0x36,0x3b,0xb9,0x85,0x2f,0x7f,0xa9,0xf2,0x1c,0xa6,0x21,0x82,0xdd,0xd5,0x25,0x66,0x15,0x95,0x5e,0x1d,0x14,0x2a,0x7b,0x91,0x99,0x1d,0x7b,0x4b,0x5f,0x55,0x93,0x66,0x83,0xea,0x8c,0x12,0x96,0x47,0x3b,0x03,0xd6,0xa6,0x2c,0xee,0x04,0x28,0xd1,0x6a,0x9f,0x65,0xc3,0x51,0x32,0x0e,0x93,0x8a,0x4c,0xf7,0x84,0xe9,0xb0,0xce,0xff,0x92,0x8d,0x20,0xe4,0x1d,0x1f,0x7d,0x77,0x8a,0x80,0x1a,0x9c,0xd4,0xca,0x90,0x8e,0x3c,0x0e,0xad,0x9a,0xd8,0xd3,0x75,0xd6,0xde,0x82,0x4f,0x99,0xc5,0xbb,0x97,0xf1,0x3e,0xfe,0xe3,0x5b,0xa5,0x9c,0x14,0x9d,0xc9,0xd3,0xed,0x4f,0x02,0xea,0x09,0x9f,0xff,0xc4,0x3e,0x97,0x3f,0xaa,0x9f,0xf5,0xef,0x3f,0xb5,0xe5,0xc3,0x83,0x98,0x3d,0x3c,0xbd,0x3e,0x1e,0xae,0x47,0xe6,0x7a,0x0c,0x9b,0x87,0xff,0x8c,0x2e,0x47,0x9f,0x5e,0xfc,0xe7,0xd8,0x95,0x8f,0x3f,0x29,0x11,0x88,0xb7,0xdf,0xde,0xf1,0x91,0xc2,0xbc,0x21,0x04,0xc4,0x2c,0xf2,0x07,0xf7,0x08,0x14,0x28,0x01,0xa3,0x38,0x2a,0x16,0x4e,0x56,0xd8,0xf1,0x78,0xc3,0x6e,0x1c,0xfe,0xd3,0x9a,0x66,0xfc,0xfd,0xcd,0xb7,0xdf,0x88,0xf1,0x00,0xc3,0xb1,0xe3,0x46,0xf0,0x6b,0xc3,0x6a,0xb0,0x04,0xb9,0xba,0x71,0x11,0xbb,0x54,0x44,0x5e,0x46,0x53,0x76,0x01,0xd2,0xc5,0x58,0xf4,0xfe,0xa7,0xf2,0x2e,0x9e,0x9c,0x7b,0xf5,0x20,0x09,0x69,0xdd,0x78,0x91,0x70,0x76,0x03,0xc6,0x53,0x61,0x0e,0xd3,0xe8,0x09,0xa4,0xc0,0x65,0x4c,0xc8,0x9f,0x1d,0xd8,0x96,0xe3,0x22,0x8d,0xaa,0x11,0x13,0x53,0x47,0xf8,0xd5,0xb6,0x90,0xd6,0x11,0x5d,0xbf,0xa4,0x67,0x87,0xc3,0x4f,0xce,0x1d,0xd2,0xb8,0x55,0xbe,0xa9,0x6c,0xce,0x9f,0x7b,0xb2,0x79,0x4d,0x1f,0xf4,0x7e,0x7a,0x59,0xff,0x24,0x78,0xf8,0x59,0x93,0x07,0x3a,0xc8,0xe4,0xaf,0x8f,0xd5,0xfa,0x53,0xbd,0xd6,0x5f,0x4f,0x56,0xfb,0x53,0xa5,0x5a,0x26,0x6a,0x1c,0x9b,0x81,0x46,0x23,0xd5,0x7c,0x00,0x5a,0x85,0x8c,0x78,0x28,0x08,0x08,0x59,0x1a,0xd2,0x56,0x56,0x58,0x2c,0x63,0x1c,0xf5,0x0f,0x9f,0xc2,0x3f,0x33,0x96,0x99,0x88,0x21,0xd8,0xc4,0xd1,0xb1,0x77,0x7e,0x92,0x37,0xca,0xd3,0x93,0x88,0xa5,0xdd,0x79,0x50,0xb0,0xa7,0xd6,0x45,0xcd,0xd9,0xcc,0x53,0xa6,0xfa,0x09,0x64,0x70,0xc1,0xe4,0xb1,0xea,0x33,0x5d,0x64,0x23,0xb0,0x09,0x28,0x89,0xc6,0xaa,0x82,0xae,0xb5,0x00,0xf4,0x13,0x3a,0x2f,0x7e,0x15,0xef,0x3c,0x78,0x64,0xd2,0x29,0xfc,0x13,0xc3,0x45,0xbd,0x0b,0x30,0x73,0x39,0x5a,0xd4,0xae,0x37,0x4d,0x43,0xb0,0x22,0x28,0x94,0x79,0x56,0x87,0x6e,0x16,0x48,0x13,0x8e,0x10,0x01,0x54,0xde,0xca,0x75,0x43,0x8c,0xca,0xee,0x1d,0x0e,0x46,0x4e,0x74,0x6e,0x05,0x3d,0x5d,0xb0,0xdd,0x2b,0x71,0x70,0x69,0xa4,0xcf,0x65,0x7e,0x3d,0xf9,0x5e,0x60,0xfb,0x77,0x3b,0x06,0x10,0x80,0x26,0x96,0x43,0xc7,0xaa,0x26,0x8f,0x27,0x6f,0x30,0x0b,0xed,0x70,0x5a,0x55,0xdc,0x37,0x5b,0xd2,0xa0,0x66,0x67,0x41,0xe0,0xcd,0x00,0xdb,0xed,0x5d,0x7e,0x97,0xb7,0x9f,0xd8,0x18,0x9f,0xb5,0xc4,0x4a,0x22,0x76,0x4c,0x9c,0xbc,0x47,0x4c,0x32,0xfe,0x08,0x71,0x30,0x7f,0xb2,0xa7,0x16,0xf4,0x3a,0x1c,0x7d,0xba,0x7a,0xa0,0xa4,0xc1,0x10,0xa5,0x9c,0x9d,0xe1,0x9a,0xe7,0x10,0xf1,0x15,0x66,0xa5,0x8e,0x8a,0x13,0xfb,0xd1,0xd6,0xcd,0x9b,0x9d,0x0a,0x1e,0x38,0x1d,0x1f,0xda,0x97,0x80,0xc5,0x04,0x53,0x5c,0x4a,0x1a,0xcf,0x6c,0x86,0x2d,0x4e,0xb6,0x65,0x9d,0xb7,0xa9,0xd4,0xaf,0x5c,0x0c,0xac,0xdf,0xce,0xc6,0x11,0x72,0x64,0x0a,0x3d,0xdd,0x9a,0x6c,0xc5,0x01,0x4c,0xbd,0x8d,0x18,0x3d,0xd2,0x19,0x72,0x9c,0x0a,0x33,0x02,0x7d,0xd6,0xcb,0xa1,0x23,0x91,0xb6,0xd5,0x35,0x61,0x87,0xdd,0x6f,0xa9,0xa8,0x1c,0x4d,0x13,0xd6,0xfd,0x29,0xab,0x15,0x98,0xb2,0x7f,0x68,0x87,0x88,0x62,0xde,0xbd,0xac,0x48,0x62,0x61,0x04,0xc4,0x36,0x4e,0x5f,0xdb,0xb3,0xd0,0xeb,0xa9,0xe7,0x12,0xde,0xc0,0x22,0x1e,0x59,0x84,0x94,0x91,0xa0,0x9d,0xea,0x54,0x3d,0x88,0x9a,0xea,0xb4,0x70,0xbd,0x2e,0xaa,0x71,0xf0,0xd2,0x28,0xb1,0xcb,0x9c,0x8e,0x25,0x37,0x53,0x1d,0x4f,0xb5,0xad,0x92,0xf4,0xf3,0x99,0xd1,0x61,0xb7,0x98,0x11,0xf9,0x92,0xfe,0x1c,0x13,0xa8,0x80,0x49,0x1a,0x10,0xff,0x3c,0x1d,0x9a,0xb5,0x35,0xa8,0xce,0xa6,0xeb,0xb0,0x94,0x5b,0xd8,0x0a,0xec,0x55,0xc8,0xb0,0xdb,0x6b,0x50,0x85,0x02,0xbd,0x13,0x94,0x5e,0xc3,0x58,0x0c,0xfc,0xcd,0xca,0x61,0x0b,0xb3,0xa9,0x4e,0x09,0x1a,0xe5,0xd6,0x69,0x81,0xcd,0xc7,0xb7,0x5f,0x6b,0x25,0xe9,0x22,0x8f,0xb7,0xff,0x78,0xb4,0x1e,0x0d,0x93,0x02,0xf1,0x70,0xfd,0x6d,0x93,0xe9,0xb9,0x54,0xdf,0x95,0xca,0xab,0xc2,0x25,0x39,0x28,0x08,0xda,0x5d,0xf3,0xae,0x99,0x0b,0xfb,0x17,0x17,0x19,0x42,0x07,0xba,0x7c,0xf6,0x44,0xc1,0x2b,0x9d,0xb6,0xc1,0xe9,0x85,0x93,0x85,0x60,0x6f,0x24,0xbd,0x1f,0x9d,0x04,0x5f,0x1a,0x21,0x20,0xfa,0x96,0x8a,0x2b,0xc0,0xaa,0x93,0xc5,0x30,0x44,0xe2,0x4c,0xe8,0x76,0x95,0xbe,0x63,0x20,0x9c,0x95,0x5c,0xd8,0xcc,0x77,0x45,0x72,0x89,0x15,0x06,0xc5,0x31,0xb2,0xe1,0x5e,0x8c,0x87,0xa0,0xb7,0xb2,0xa7,0xd7,0xbd,0x43,0x70,0x9d,0x75,0xe9,0x66,0x94,0x7f,0x31,0xe6,0x17,0x74,0x7b,0x08,0x2e,0x75,0xce,0x3b,0xa4,0x9c,0xad,0x67,0x14,0xe6,0x74,0xc4,0x41,0x74,0x08,0x60,0x11,0x0d,0x93,0x68,0xb6,0x70,0xff,0xb7,0xf1,0x53,0x9b,0x65,0x38,0x8d,0xa3,0x91,0xf7,0x76,0xbd,0xa1,0xdb,0xef,0xe1,0xfe,0x42,0xbf,0x9f,0xad,0xf7,0xfb,0xf5,0x92,0x2e,0xbe,0xca,0x27,0x7b,0x6f,0x5c,0x49,0x10,0x5b,0xcd,0x91,0x4e,0x08,0x0a,0xc6,0x32,0xd0,0xa0, +0x31,0x71,0xb5,0xdb,0x7f,0x58,0x70,0x72,0x37,0x4e,0x3d,0x7e,0xf0,0x5a,0x9e,0x02,0xcc,0xac,0x05,0x5f,0x2d,0x58,0xb5,0x02,0xcb,0x66,0xab,0xa3,0x72,0xbc,0xad,0x3d,0xfd,0xa5,0x27,0x89,0x69,0xeb,0x1c,0xbe,0x35,0xa4,0xa5,0x95,0x7f,0x38,0x32,0xb7,0x3e,0x11,0xae,0x69,0x8a,0x10,0x02,0xba,0x79,0xf8,0x7c,0x95,0x97,0xc4,0x3e,0x71,0x5a,0xa0,0xd4,0x66,0x81,0x65,0x4c,0x1c,0xb8,0x5f,0x3b,0xa5,0x51,0x51,0xc9,0x6c,0x95,0x54,0xed,0x24,0x6e,0x74,0x82,0xc0,0x72,0x1a,0x3d,0xeb,0x13,0xdc,0x65,0xcd,0xa8,0xa7,0x67,0xc4,0x90,0xdf,0x6d,0x7d,0x37,0x4e,0xbd,0xa3,0xfa,0x4f,0xf5,0x79,0xc1,0xa6,0x0f,0x45,0x04,0xa8,0x98,0x83,0x6e,0x4d,0x39,0x60,0xad,0xcf,0x05,0xbe,0xb9,0x5b,0x26,0xf9,0x96,0x38,0x90,0xa1,0x87,0xd0,0x90,0x48,0xf7,0x70,0x13,0xd5,0x5e,0xd1,0xbc,0xd3,0x0b,0xf8,0x3b,0x9f,0x9f,0x77,0x0b,0x64,0xfe,0x8b,0xc5,0xe2,0xd7,0x36,0x21,0xd1,0x3a,0x6e,0xce,0xcf,0x6f,0xa8,0x6a,0x76,0x8c,0x7e,0x98,0x47,0xf3,0xc3,0x01,0xb7,0xca,0xe8,0xc0,0x6f,0xa2,0x6e,0x71,0x38,0x5c,0x21,0x18,0xd8,0x04,0xde,0xb3,0x5e,0xef,0x13,0x4f,0xdd,0x5c,0x46,0x13,0xc2,0x89,0x3c,0x33,0xdc,0xd9,0x9b,0xee,0xdc,0xc8,0x3b,0x11,0xbd,0xd2,0x9f,0xa0,0xdf,0x97,0x85,0x36,0x7b,0x9b,0x40,0xce,0x35,0xb5,0xd4,0x0c,0x4e,0x4c,0xaa,0xf6,0xe6,0x70,0x40,0xd5,0xb0,0xc4,0x84,0x4e,0x60,0x78,0xd3,0xf5,0xf1,0xdb,0xbd,0x0a,0x9e,0x42,0xf6,0x1f,0x76,0xf1,0x17,0x16,0x3f,0x44,0x3a,0xdd,0xc1,0x72,0x7a,0x8e,0x20,0x30,0xfb,0x78,0xbb,0x8f,0x6e,0xe8,0x8a,0x50,0x71,0xc4,0x0e,0xc6,0xec,0xb7,0x31,0x8d,0x79,0xd5,0xcd,0x92,0xcc,0xca,0x14,0x99,0x4c,0xb3,0x57,0xe1,0x2b,0x73,0xfc,0x99,0x38,0xf0,0xb5,0xcb,0x45,0x0f,0xf3,0x90,0x43,0xea,0x26,0xeb,0xec,0x43,0x25,0x3f,0x49,0x5a,0x73,0x52,0xe3,0x28,0x08,0x1a,0x3c,0x13,0x07,0x3c,0xd5,0x63,0xe9,0x33,0x2d,0x58,0xcb,0x09,0xeb,0x25,0x50,0x03,0x78,0xf0,0x3e,0x42,0xf4,0xbe,0xbc,0x92,0x7a,0xb4,0x88,0x6b,0xbe,0x34,0xa0,0x19,0xc5,0x78,0xa2,0x8f,0x7c,0x7a,0xd6,0x1f,0xf9,0x39,0xb1,0x58,0x08,0x85,0xc2,0x41,0xc0,0x79,0x92,0x3e,0x40,0xb0,0xcb,0x5e,0xe7,0x95,0x5d,0xa7,0x10,0x6d,0xd5,0xf4,0x00,0xcb,0xc0,0x70,0x2d,0xb8,0x2b,0x73,0x86,0x70,0x38,0x88,0xd3,0x3b,0xbd,0x25,0x90,0xab,0x55,0x12,0x71,0xea,0x38,0x4f,0xc4,0x81,0xb5,0x4d,0x9d,0x21,0x38,0x84,0xa9,0x77,0xc6,0x91,0x22,0x82,0x50,0x5a,0xec,0x38,0x2d,0xca,0x13,0x25,0x84,0xaf,0xd3,0xb0,0x48,0xa5,0x78,0xf7,0x41,0xf7,0xa9,0xc7,0x25,0x9a,0x9b,0x5c,0x02,0x9c,0x63,0x88,0xb5,0xfe,0xe0,0x4d,0xc5,0x9e,0xd2,0x39,0xab,0x77,0xb3,0xf5,0x7d,0xcb,0x36,0xa3,0xa9,0xe5,0x53,0x82,0xa9,0xcb,0x59,0x91,0xb5,0xa9,0xd2,0x75,0x19,0x68,0x68,0xd7,0xd3,0xe9,0xa2,0xed,0xcc,0xf2,0x90,0xbf,0x3d,0x8f,0x5d,0x9d,0xe6,0x50,0x93,0xe4,0x68,0xd8,0xd7,0x66,0xe5,0x68,0xc0,0x5c,0xd7,0x0f,0xca,0x4c,0xb7,0x32,0xdc,0xca,0xaf,0xf9,0xd0,0xdc,0xca,0xb7,0x47,0x7b,0x1a,0xcc,0x63,0xe1,0xbc,0x4d,0xb0,0xa6,0x03,0x87,0x6f,0x62,0xbf,0xa3,0x1b,0x7a,0x55,0xf5,0x5d,0xaa,0x5a,0xaa,0xd1,0x86,0x54,0x0b,0x7c,0xfe,0xe4,0x70,0x7d,0x49,0x55,0xcc,0xe3,0x77,0xf1,0x21,0x4f,0x97,0x71,0xb0,0x4b,0xb7,0xc5,0x66,0x4f,0xaf,0x97,0xb4,0x8d,0x24,0xf2,0x5b,0x38,0xba,0x52,0x9e,0xc9,0x64,0xb4,0xbc,0x5b,0xec,0x8b,0xcd,0x22,0x8f,0xfe,0x6c,0xae,0xfe,0x0c,0x0f,0xcb,0x32,0x87,0x11,0x1c,0x48,0xf2,0x38,0x93,0x8f,0xd8,0xa1,0x54,0xde,0xeb,0x4b,0x42,0x27,0xeb,0x45,0x38,0x7a,0x66,0x5f,0x3e,0xa7,0xfb,0xe9,0x76,0x7d,0xb7,0x91,0x62,0xf6,0xce,0xf9,0x62,0xbf,0xad,0x7c,0xb0,0xc7,0x7e,0xd4,0x95,0xf2,0xa5,0x5b,0x94,0x1a,0xfe,0xa8,0x5e,0xf4,0xf9,0x7e,0xab,0x8b,0x6f,0x5f,0xb4,0x7c,0xf3,0xab,0x76,0x58,0x0c,0x47,0x7d,0x04,0xed,0xf4,0xbc,0xf1,0x71,0xb0,0x24,0x44,0xb1,0xd9,0x73,0x4f,0x22,0xb9,0xa6,0x89,0xa0,0x39,0xe9,0xf1,0xd7,0x78,0xb4,0x9f,0xac,0xd7,0x7b,0x5c,0x98,0x1e,0xf3,0x75,0x2c,0x1a,0x8d,0x25,0x2b,0xb3,0xe3,0x8c,0xbf,0x98,0xf1,0x6d,0x56,0xa2,0xa4,0x95,0x1b,0xdc,0xc0,0x32,0xfc,0xa7,0xd3,0xf1,0x35,0xf3,0xbd,0x9d,0x48,0xd6,0x28,0x71,0x53,0x68,0x9b,0xb5,0xd6,0x54,0xcf,0x7c,0x34,0x6c,0x49,0x86,0x64,0x6a,0x40,0x94,0x84,0xaa,0xfc,0x4e,0xfc,0xea,0x86,0xd6,0xde,0x33,0x66,0x99,0xb0,0x13,0xb4,0x66,0x5d,0xc7,0x51,0x84,0x9a,0xb2,0x12,0x35,0xa5,0xcf,0xb3,0x41,0x4a,0x5b,0x58,0x76,0x7a,0x0c,0x25,0x98,0x37,0x5d,0xac,0x93,0x78,0x01,0xed,0x96,0xc7,0x9e,0xca,0x82,0x7e,0x92,0xfa,0xbb,0x40,0x1c,0xf2,0x36,0x00,0xea,0xc3,0xf9,0x9f,0x86,0xd7,0xf7,0xdd,0x81,0xe3,0x48,0x77,0x7b,0x2a,0x5a,0x8e,0x89,0xf6,0xa5,0x16,0x51,0xa2,0x31,0xb6,0x41,0xfc,0xaf,0xb6,0xf1,0x94,0x51,0x77,0x60,0x22,0xd1,0xf4,0xd5,0xba,0xec,0xec,0xea,0xf9,0x7a,0xb0,0x12,0xf7,0x02,0x04,0xd3,0x59,0x11,0xa2,0x95,0x00,0x95,0x93,0xa0,0x55,0x35,0x40,0x0c,0x8d,0x99,0x98,0xa5,0x9a,0x38,0x61,0x6b,0x26,0xe3,0x70,0x12,0xd8,0xfc,0xdc,0x9b,0x58,0x64,0x56,0xb0,0xb8,0x98,0x46,0x1c,0xfd,0xa1,0x92,0xb0,0xab,0xee,0xfb,0x9c,0x15,0xef,0x80,0x60,0x67,0x91,0x7f,0xa3,0x4f,0xec,0x09,0xc4,0x8b,0x02,0xa4,0x41,0x33,0x3c,0x7e,0x41,0x90,0x36,0x9a,0x11,0xa2,0x26,0x80,0x33,0x50,0xad,0xa6,0x8e,0x13,0x31,0x3c,0xf4,0xbb,0xdb,0xde,0x6c,0xbf,0x5c,0x7c,0xb7,0xcd,0x8d,0x8b,0x45,0xd0,0x2d,0x70,0xbe,0xde,0xb0,0xab,0xbf,0x13,0x67,0x66,0x0a,0xb1,0x87,0x09,0xbf,0x3c,0x28,0x07,0x38,0x75,0x0d,0x7d,0xe9,0xf8,0x59,0xb8,0x06,0xca,0x53,0x37,0xb4,0x29,0x1d,0x10,0x22,0x02,0xd0,0xf1,0x3e,0xcc,0x08,0xe1,0xf4,0xca,0xae,0xb8,0xc8,0x4d,0xb1,0xa8, +0x7d,0x81,0xc5,0xb0,0x49,0x18,0x97,0xa3,0x95,0x0e,0x7b,0x91,0xb1,0xc9,0x8e,0xb6,0x48,0x9c,0x10,0x85,0x05,0xe3,0x46,0x36,0xf1,0x31,0x51,0x17,0xcc,0x34,0xcf,0x23,0x87,0xa2,0x9c,0xd4,0x4e,0xfc,0x09,0x7a,0x4c,0x3b,0xb0,0x3a,0xf5,0x13,0x08,0xef,0x18,0xf5,0x79,0xac,0x8d,0x21,0x60,0x9e,0x42,0xa3,0xf2,0x70,0xe3,0xf4,0x65,0x3a,0xba,0x41,0x5f,0x16,0x66,0x15,0x4d,0xcc,0x45,0x2f,0xb0,0xfe,0xaa,0x13,0x4b,0xda,0x2c,0x8e,0x9d,0x86,0x41,0x52,0x76,0x1a,0x0a,0x61,0xdb,0xef,0x76,0x28,0x3b,0x01,0x0b,0x69,0xd4,0x78,0x63,0xb2,0xc3,0xa5,0xed,0xd9,0xe1,0xf8,0x54,0xe0,0xa4,0x39,0xd5,0xd7,0x26,0x43,0x9e,0xb2,0x57,0xcd,0x32,0x3a,0x63,0xdc,0x9e,0x49,0x99,0x0a,0x05,0x84,0x94,0x6d,0xfc,0xdd,0xe7,0x0b,0x3a,0xc0,0xb1,0xbf,0xf0,0xcb,0x6b,0x4a,0x27,0x69,0xed,0xce,0x02,0x91,0x89,0x2a,0xaa,0x92,0x8a,0x67,0x3b,0xd6,0x9f,0xe8,0xa4,0xf8,0xc5,0x7b,0x42,0xcb,0xe6,0xda,0xa3,0x16,0x56,0x6b,0xae,0xfe,0x73,0xf9,0x2a,0x62,0xaf,0xd7,0x13,0x35,0x57,0xbc,0xce,0x7d,0x39,0x21,0xb7,0x98,0xf2,0x7a,0x72,0xe0,0x1d,0xce,0xbd,0x9b,0xfc,0xc3,0xa5,0xda,0xeb,0x03,0x74,0xb9,0xbe,0xdb,0xe5,0x87,0xcd,0xba,0x80,0x86,0xfd,0x90,0x8a,0x47,0x31,0x95,0xbd,0x3b,0x64,0xb4,0x40,0xf4,0x67,0xbd,0x09,0x0e,0xe9,0xa2,0x48,0x6f,0x2e,0xd5,0x1d,0x7f,0x33,0xfa,0xa5,0x47,0x1c,0x13,0xb8,0xb0,0x9e,0xdf,0xeb,0x06,0xc4,0x72,0x95,0xe8,0xe8,0x5d,0xec,0x26,0x35,0xb0,0x8f,0xef,0x9d,0xc7,0x57,0x95,0x38,0x5b,0x22,0x23,0xb6,0x0c,0x41,0x25,0x5c,0x6c,0x69,0x53,0x74,0xac,0x46,0x5f,0x72,0x63,0x73,0x31,0xd5,0xab,0x66,0x55,0xad,0xa5,0x55,0x27,0x3d,0x34,0x38,0xd9,0x94,0x25,0x92,0x88,0x81,0xa4,0x8c,0xd8,0x39,0x28,0x95,0x9a,0x49,0xc0,0xf5,0xcf,0xb8,0x7e,0x84,0x5e,0xa6,0x26,0x4a,0x92,0x8a,0xda,0x10,0x25,0x69,0x26,0xf1,0x6d,0x10,0xe9,0x1e,0xf2,0x21,0xc8,0x9f,0x4c,0x5d,0xa1,0x7e,0x01,0x21,0x51,0x43,0x22,0x8e,0xd2,0x19,0x95,0x36,0x65,0xf5,0xad,0xd3,0x15,0xd0,0xd2,0x1c,0xac,0x3c,0x8f,0xee,0x63,0xbb,0xa9,0x3b,0x36,0xb9,0x52,0x6c,0x7a,0xc3,0x12,0x5c,0x68,0x4e,0x41,0x2d,0xb7,0x69,0x26,0x89,0xb3,0xea,0xad,0x27,0x10,0xe1,0xc1,0xba,0xa6,0xcd,0x10,0x90,0x18,0xc8,0xde,0x14,0x4a,0xc6,0x29,0xff,0x70,0x8c,0x34,0xbe,0xdf,0xf2,0x0f,0x1d,0x01,0xd0,0x32,0x37,0x45,0x1a,0x88,0x2d,0xba,0x67,0xb6,0x5d,0x3b,0x2d,0xe4,0x34,0x59,0x6c,0xe8,0xad,0xdf,0x45,0x0f,0x72,0x80,0x21,0xfb,0x4b,0xdd,0x79,0xa9,0x92,0x77,0xc0,0x3d,0xa6,0x4c,0xdc,0x7f,0x75,0x6b,0xc4,0x8a,0x81,0x8e,0xd0,0x9f,0xf6,0x66,0x9c,0x54,0x66,0x2b,0x06,0xf1,0x0a,0x19,0x69,0xf5,0x13,0x36,0xb3,0x31,0x91,0x09,0x58,0x34,0x77,0xc2,0x5b,0x6c,0x1b,0x2b,0x49,0xc4,0x2c,0x03,0x4d,0x6b,0x03,0x55,0x7e,0x11,0xdd,0x4a,0xe7,0x77,0xd0,0x4c,0x97,0x77,0x1c,0xe1,0x8f,0x66,0xf9,0x56,0x37,0x29,0x7a,0x6b,0x73,0x57,0x31,0xdf,0xd0,0xc4,0x70,0x0b,0xfd,0xb1,0x45,0xbf,0x64,0xda,0x08,0x24,0xa6,0xd3,0x7c,0xcb,0xa9,0xdb,0x25,0x09,0xfd,0xd0,0xbc,0x02,0x21,0xcf,0xce,0xfd,0x86,0x0d,0x77,0xcc,0x7a,0x74,0xba,0x5a,0xe0,0x4a,0xa6,0x53,0xbc,0xc0,0xd5,0xb9,0xd1,0x89,0x48,0xdc,0x71,0x4d,0xe9,0x36,0xa7,0x53,0x6c,0x16,0xdd,0xe9,0xc3,0x33,0x19,0xcd,0xc7,0x81,0x8e,0x91,0xbf,0x89,0x66,0xf0,0x31,0x5d,0x47,0xfe,0x8c,0xce,0x3e,0xa9,0x4d,0x87,0x3d,0xe9,0x99,0xb8,0x27,0x81,0x5a,0x71,0x34,0x7b,0xd3,0x39,0x0e,0x18,0x11,0x2f,0x88,0x1c,0x80,0x5b,0x0e,0x55,0xe2,0xe7,0xc3,0x45,0x0f,0x52,0xce,0x29,0xce,0x34,0x84,0xc5,0x5c,0xf4,0x12,0x9a,0x7a,0x56,0x27,0x21,0x42,0xd3,0xc9,0x6f,0x6f,0xa2,0x52,0xfe,0xcc,0xf1,0x34,0x69,0xdd,0x69,0x56,0xb8,0x8e,0x8d,0x68,0x6e,0x32,0xa5,0x17,0x38,0x4c,0x15,0x96,0x28,0x94,0x05,0x53,0x66,0xa9,0xc3,0x5c,0xb9,0x11,0x10,0x42,0x5e,0xf8,0x13,0xe1,0x11,0xb4,0x87,0x2c,0x0d,0x88,0x10,0x3a,0xcd,0x70,0x9a,0x87,0x6b,0x1d,0xd3,0xa5,0x07,0xe1,0x02,0x1d,0x7f,0xfe,0x92,0x8e,0xff,0x15,0xe6,0x47,0x5f,0x81,0x2c,0x5a,0xda,0xd1,0x7d,0xbe,0xbe,0x23,0xa0,0xee,0xab,0x05,0x4e,0x88,0xbb,0xcd,0xf9,0xb9,0xbe,0x28,0xa3,0x10,0xac,0xd5,0x14,0x71,0x08,0x3a,0x57,0xf0,0x22,0xa9,0xdb,0x65,0xc0,0x70,0xa8,0x61,0xab,0xb1,0xa2,0x2f,0x02,0xaa,0x91,0x5e,0x60,0x9e,0xf1,0x6b,0xaa,0xbb,0x09,0xd4,0x8d,0x01,0x70,0x03,0xb0,0xd5,0x07,0x91,0x4c,0x07,0xb0,0xc5,0x70,0x69,0x5c,0x1c,0x6a,0xfd,0xed,0x76,0x55,0x9f,0xaa,0x0a,0x35,0xf5,0x71,0xc3,0x52,0x7f,0x5e,0x0e,0xd9,0x9c,0x18,0x25,0xf8,0xbe,0x56,0x75,0xf2,0x1f,0xdb,0xa5,0xa5,0xc2,0xed,0xfc,0xbc,0xb2,0x67,0x69,0x44,0xce,0x7e,0x82,0xf0,0xfe,0x5f,0x01,0x5a,0x64,0xdc,0xf8,0x9f,0xc0,0x6d,0xf0,0xf0,0x18,0xd8,0x66,0xbf,0x03,0xb6,0x02,0x00,0xdc,0xe2,0x2c,0x9a,0x71,0x32,0xa7,0x5a,0xbe,0x9c,0xeb,0xeb,0x5e,0xe0,0x75,0x0d,0x0c,0xd1,0x1d,0x9d,0x85,0xbd,0xa7,0xd7,0x90,0x48,0x42,0x3b,0xe2,0xe3,0x0a,0x19,0x74,0xa0,0x6a,0x88,0x96,0xcd,0xc0,0x4b,0x37,0x44,0xd8,0x4d,0xc6,0x0a,0x91,0xab,0x36,0x04,0x34,0x37,0x3d,0x03,0xfd,0x74,0x28,0x81,0x98,0xc2,0xd2,0xf2,0x73,0x59,0xfb,0x19,0x32,0x96,0x08,0x10,0xdf,0xf4,0x2c,0x0c,0x53,0x5f,0x09,0x70,0xa4,0x9c,0xd9,0x15,0x38,0x73,0x9e,0x3e,0x65,0x33,0xa4,0xc3,0xa1,0x53,0x3e,0x67,0xb8,0xae,0x04,0x5a,0x72,0xbf,0xa9,0xc1,0xcd,0xc5,0x05,0x41,0xa5,0x80,0x04,0x00,0x5d,0xae,0x4a,0xd0,0x0c,0x06,0x53,0xea,0xcf,0xb2,0xd4,0x2a,0x82,0x82,0x8d,0xb7,0x19,0x11,0x9a,0x28,0x6e,0xae,0xcd,0x07,0x6b,0x65,0x11,0xa7,0xde,0x1f,0x5b,0xd7,0xe4,0x0a,0x21,0x9b,0xca,0x12,0x46,0x69,0xc2,0x7b,0xf1,0x68,0xcd,0x3e,0x56,0x38, +0x99,0x8b,0xc0,0x2c,0xa9,0xd5,0x51,0xac,0xba,0x00,0x0e,0x06,0x55,0x02,0xe4,0x41,0x5d,0x69,0x5f,0x30,0x54,0xda,0xd2,0x9e,0x34,0x72,0x26,0x40,0xe9,0x71,0x58,0x68,0x8d,0x70,0xdb,0x5c,0xb4,0x75,0x63,0x93,0xe2,0x3d,0x9b,0x17,0x19,0xdb,0x29,0xec,0x04,0x96,0x2b,0x6b,0xe7,0xa0,0x9a,0xf2,0x02,0x42,0x4a,0x5f,0xb6,0x82,0x84,0x55,0x30,0xad,0xb1,0x2f,0xe3,0x48,0x10,0xfe,0x58,0x4b,0x14,0xeb,0x30,0x6a,0xdf,0x6a,0xc1,0x2d,0x38,0x92,0x08,0x82,0x3b,0x44,0xdb,0xae,0xb7,0xc4,0xfc,0x64,0x01,0x7f,0x39,0xfb,0x86,0x6e,0xd8,0xc4,0xb3,0x04,0x6f,0x4e,0x06,0x20,0x8a,0x00,0x02,0x87,0xcd,0x36,0x7f,0xa9,0x47,0x7c,0x38,0x54,0x6e,0x1d,0xdf,0xe5,0x44,0xd6,0x29,0x78,0x98,0xd9,0xfe,0x69,0xcc,0xb3,0x73,0x4b,0xa9,0x39,0xc8,0x71,0xc3,0x1e,0x40,0xd0,0x39,0x4a,0xc1,0x1f,0xc0,0xbb,0x0b,0x41,0x08,0x88,0x6c,0x8c,0xa7,0x9c,0x62,0xe0,0xcd,0x7e,0x4d,0xb4,0x73,0xe6,0x03,0x0d,0x40,0xe0,0x0b,0x91,0xa0,0xee,0xd8,0xa4,0x47,0x1d,0x5d,0x3a,0x79,0xe7,0xfd,0x69,0x79,0xb4,0xef,0x24,0x39,0x9d,0xae,0xf0,0xf5,0x72,0x99,0x67,0x05,0xb2,0xce,0xb5,0xd5,0x9c,0xf4,0xb6,0x76,0x63,0xf0,0x07,0xe5,0xad,0x6c,0x9c,0x69,0x65,0xe3,0xd0,0x14,0x49,0x23,0x04,0x29,0xd1,0x94,0xe8,0x71,0x9c,0x39,0x44,0x05,0xe1,0x87,0x28,0x0a,0xdf,0xaf,0xaf,0xcc,0xd4,0x6e,0xd1,0xb1,0x38,0xa5,0xca,0xe7,0x30,0xb3,0xd5,0xbd,0x0d,0xf4,0xa1,0xad,0x87,0x54,0x38,0x1a,0x61,0x90,0x82,0xec,0x33,0x41,0x14,0x7a,0x94,0x1b,0xa7,0x40,0xc4,0x22,0xdb,0x72,0x2b,0x2f,0x85,0x78,0x07,0x1f,0xc4,0xca,0x42,0x67,0x84,0x3e,0x9c,0xb5,0x4d,0x00,0xc0,0xde,0x66,0xbd,0xdb,0x9b,0x15,0x3b,0x3f,0xaf,0xde,0x57,0x56,0x50,0x99,0xe6,0xd8,0x10,0x46,0x66,0xf3,0xb4,0x45,0x01,0x80,0x1a,0xe7,0x5d,0x11,0x25,0x55,0x4c,0x40,0xb0,0x8c,0xf4,0x0b,0x58,0x2a,0x0e,0xc0,0x0c,0x37,0x40,0xc7,0x64,0x00,0x29,0xba,0x88,0x2d,0x70,0x23,0xe4,0xc7,0x3d,0x09,0xc1,0xff,0x22,0xba,0xd2,0x96,0x78,0x73,0x9a,0x00,0x8e,0xe2,0x31,0x8f,0xe6,0x15,0xd7,0x5e,0x16,0xeb,0xe9,0x60,0x4b,0x6e,0xb5,0xa6,0xd6,0x4e,0x19,0xd7,0x7f,0x6e,0x13,0x62,0xb3,0xb5,0x68,0x20,0x72,0x0e,0xed,0xd2,0x49,0x48,0x5d,0xa2,0xd9,0x17,0xbc,0x23,0x10,0x04,0x27,0x1d,0x73,0xd6,0x1b,0x83,0xe1,0x10,0xe1,0xb0,0x14,0xe8,0x4c,0x47,0x39,0xe4,0xa7,0xf8,0x41,0xf0,0x1b,0x87,0x4e,0x18,0x22,0xe6,0x85,0x48,0x1b,0x25,0x1c,0xc1,0x9c,0xfd,0xfe,0x74,0x6c,0x17,0x79,0xa5,0x53,0x98,0xce,0x6d,0x0a,0x53,0x42,0xf2,0x5c,0xe1,0xc4,0x06,0x9e,0x9f,0x58,0xd4,0x38,0xd3,0xb1,0xc3,0x01,0x11,0xe1,0xdc,0x59,0x89,0xa3,0x5d,0xd4,0xb9,0xf6,0x56,0x79,0x9e,0xfc,0xee,0x57,0x89,0x09,0xfe,0xc7,0x11,0xcf,0x99,0xb0,0x06,0xa4,0xd4,0xd6,0xb5,0xdd,0x88,0x6f,0xdb,0x63,0x6c,0x5b,0x5a,0x12,0xaa,0x58,0x3d,0x10,0x8f,0xb7,0xcc,0x8d,0xfd,0x5e,0xdd,0x9e,0x8f,0x6d,0xe8,0xea,0x1e,0x09,0x0e,0x17,0x60,0x8c,0x4b,0xb0,0x2f,0x8a,0x15,0x64,0x55,0x4e,0x1e,0xcb,0xa4,0xed,0x5d,0x45,0xcd,0xf3,0xe8,0xe7,0xcd,0x37,0xa3,0x78,0x5c,0x33,0x2d,0x3c,0x39,0x54,0xad,0xf5,0xfd,0x9d,0xd1,0xdd,0x6f,0x8b,0xbd,0xb9,0xd6,0x16,0x8d,0x2c,0x5b,0x86,0x0b,0x62,0x7b,0x20,0x88,0x91,0xb5,0xd0,0x1c,0x0f,0xe3,0x90,0x23,0xef,0xf7,0xf4,0x11,0x06,0xaf,0x76,0x41,0x14,0xe1,0x03,0x2c,0x70,0x91,0x4b,0xe8,0xb3,0xbb,0x44,0x1b,0x46,0xea,0x0c,0x29,0x0f,0x9a,0xf0,0x6f,0x99,0x05,0x82,0x6a,0x70,0xc2,0x12,0x1d,0xa2,0xc7,0xc5,0x2b,0x53,0x31,0x91,0x0c,0x29,0x0a,0xf9,0xb2,0x2b,0x94,0x8b,0xc7,0x6f,0x8a,0x95,0x77,0x54,0xc9,0xe2,0x6e,0xfb,0x58,0x1b,0x51,0xa5,0x0d,0x94,0xae,0x34,0x81,0x07,0xa7,0x5b,0x58,0xdf,0xed,0x3d,0xa8,0xad,0x69,0x5f,0x9e,0x6a,0xc3,0x33,0x82,0x78,0xa0,0x03,0xae,0x52,0x30,0x82,0x98,0x59,0xe2,0x4b,0x48,0x51,0xe5,0x4c,0xd4,0x32,0x9b,0x4a,0x07,0xb8,0x88,0xee,0x81,0x95,0x49,0xb7,0xac,0xc3,0x67,0xbe,0x41,0x49,0xca,0x8b,0xf9,0x10,0x4f,0x72,0xc2,0x06,0xf9,0xdd,0x4a,0x66,0xde,0xc5,0x89,0xd5,0x63,0xdd,0xa0,0xe3,0x58,0xe3,0x46,0x60,0xab,0x0a,0x90,0xb1,0xc5,0x61,0xe5,0x49,0x4f,0x1a,0x65,0xb1,0x8a,0xfd,0x8e,0xda,0x3c,0x8a,0x1d,0xbf,0xa5,0x62,0x1a,0x06,0xca,0xad,0x66,0xe5,0x68,0xb0,0xcd,0xda,0x5c,0xdb,0xa0,0xf5,0x9a,0x55,0x55,0x6c,0x02,0x2a,0x41,0x84,0xa4,0xf0,0xd0,0x8f,0xd9,0x75,0xcc,0x86,0xd1,0xa9,0x76,0xdf,0x44,0xd4,0x61,0xd3,0x61,0x29,0x26,0x0f,0x8a,0x9d,0x3e,0x73,0xbe,0x93,0x13,0x28,0x87,0xa8,0x3a,0xab,0x3d,0x3a,0x1c,0x4a,0x63,0xb2,0xc6,0x4b,0x19,0x4b,0x39,0x39,0x38,0xd3,0x86,0xef,0xe2,0xf0,0xde,0x34,0x29,0x07,0xbc,0x59,0xab,0xf3,0xf3,0x8f,0xe4,0x94,0xe0,0x3b,0x37,0xc9,0xbd,0x7e,0x52,0x9e,0x0c,0xa1,0x5d,0x5f,0x81,0x8b,0x0a,0xc1,0x10,0x57,0xef,0xa5,0x08,0x87,0x2e,0xcd,0x33,0x5b,0xa4,0x72,0xaf,0xd5,0x4d,0x32,0x05,0xf0,0xee,0xb1,0xac,0xa7,0x39,0x2b,0xe5,0x7d,0xb1,0xcc,0xdf,0xec,0xe3,0xe5,0x26,0x92,0x19,0x35,0xb7,0x20,0x57,0x57,0x50,0x44,0xf1,0x01,0x22,0x06,0x29,0x25,0x22,0xe0,0xc3,0xa8,0x8a,0x0b,0xd8,0x4e,0xb0,0x81,0x6e,0xa3,0x07,0x27,0x50,0x54,0xa8,0x5f,0xab,0xe6,0x32,0x60,0xfe,0xda,0x88,0x27,0x79,0xfe,0x08,0x0d,0x24,0x05,0xde,0x14,0xcb,0x3b,0x1e,0x3b,0x1c,0x35,0xaa,0xd4,0x45,0xd3,0x3b,0xb5,0x09,0x2f,0x83,0x53,0xc0,0xf1,0x2e,0x56,0x48,0x5f,0xa8,0x5f,0xdb,0x56,0x38,0xb1,0x51,0x8d,0x86,0x21,0x54,0x58,0x25,0x61,0xfe,0x95,0x86,0x9b,0xe3,0x7a,0xac,0xe9,0x06,0xad,0x24,0x6d,0xb7,0xcd,0xd2,0xbf,0xd2,0x89,0x47,0x66,0xf9,0xf7,0x7a,0xd3,0xf6,0xa9,0xaf,0x21,0xac,0xd9,0x5b,0x76,0xb0,0xe1,0x90,0x37,0xf1,0x62,0xff,0x1f,0xf9, +0x07,0x9c,0x45,0x09,0x1f,0x1b,0x1c,0x58,0x2a,0xc5,0x5e,0x5f,0xd8,0x03,0x6c,0x06,0xc7,0xa7,0xec,0xed,0xfa,0x8e,0xf3,0x8e,0xe0,0xc9,0x7e,0xbb,0xd0,0x5f,0x65,0xf9,0x3e,0x2e,0x16,0xb8,0xe2,0xc5,0xf8,0x8e,0xf8,0x72,0xfe,0x68,0x49,0xcf,0x75,0x11,0x6a,0x37,0xff,0xd9,0x5c,0xfc,0x13,0x17,0x6c,0xb4,0xa6,0xdf,0xbe,0x2b,0xf2,0x7b,0xfc,0x12,0x16,0x8f,0xb7,0x9e,0x6e,0x6f,0xfb,0x39,0xb6,0x23,0x5d,0xdf,0x48,0x21,0xfa,0x31,0x4f,0x74,0x06,0x27,0x7b,0x25,0x1d,0x5a,0x14,0xd4,0xf8,0xcf,0xe5,0x25,0x37,0xb3,0x9e,0x4c,0xe8,0xe0,0xfe,0xb9,0xbc,0xe4,0xa7,0x5a,0xde,0xfc,0x3a,0x73,0x6e,0xf8,0xc0,0x41,0xc7,0xd2,0x6d,0x9e,0xaf,0x7e,0x2e,0x2f,0xf9,0x0b,0x41,0x0a,0xce,0xf8,0xf7,0x6b,0x2d,0x26,0x96,0x1b,0xfb,0x9c,0x38,0x89,0x56,0x8e,0xce,0xd0,0xa6,0x46,0x80,0x6a,0x82,0xb0,0x72,0x79,0x84,0x1b,0x36,0x99,0xaa,0x38,0x66,0xf1,0x50,0xfb,0x49,0xf6,0xcc,0x44,0x0c,0xcb,0x4b,0x42,0x51,0x76,0x2e,0xec,0xf7,0xae,0xfd,0xf8,0xbe,0x56,0xd7,0xd5,0x79,0x82,0xc8,0xb7,0xf4,0xf7,0xa3,0xf0,0x63,0xfa,0xfb,0x2c,0xec,0x87,0xfa,0x43,0x81,0x02,0x23,0x50,0x05,0x80,0x04,0x16,0x2c,0x58,0x34,0x8f,0x1d,0xb8,0x0d,0x3d,0xbe,0xa6,0xa3,0x63,0xeb,0x29,0xbe,0x5c,0xe4,0xf1,0xbb,0xdc,0x3c,0xa6,0xd3,0xd4,0x4c,0xa2,0x2e,0xae,0xef,0xe4,0x03,0x7d,0xa3,0x3f,0x31,0xaf,0xf8,0x4c,0xaf,0x9d,0x36,0x35,0xb6,0x27,0x1e,0x47,0x0f,0x15,0x72,0x20,0x51,0x46,0x54,0x42,0x97,0x42,0x99,0x36,0x66,0xda,0xc4,0x6f,0x54,0x79,0x1d,0x17,0x73,0x96,0x04,0xcb,0x78,0x59,0x4b,0x13,0x9d,0xc7,0x36,0x03,0xae,0xb5,0x9a,0x2b,0x0e,0x5f,0x43,0x9c,0x9a,0x4c,0x21,0x71,0x85,0x86,0x01,0x73,0xa5,0xbf,0xed,0x62,0x6d,0xa5,0xbf,0xe1,0xe8,0xce,0x0d,0xab,0xc5,0x4a,0xe2,0x31,0x63,0xae,0x64,0xa2,0x16,0xc5,0xd6,0x68,0x90,0x9f,0x1f,0xd5,0x7a,0xd5,0x94,0x8b,0x9d,0x2a,0x8e,0x7c,0xf2,0x80,0xf1,0x53,0xfe,0x2f,0x6c,0x4f,0xdb,0xc4,0x99,0x78,0x62,0x27,0xc5,0xd0,0x44,0x99,0x3b,0x53,0x08,0xdc,0x55,0xe3,0xe9,0x45,0xb4,0x9f,0x95,0x5c,0xed,0x30,0xb3,0x33,0xd4,0xf5,0x7a,0x5e,0xd7,0x79,0x15,0x96,0xaf,0x54,0xc9,0x15,0xd1,0xa5,0x61,0x5d,0x19,0x41,0xb5,0x2a,0x50,0x74,0x9e,0x0b,0x8e,0x1f,0x18,0x07,0x82,0x33,0xa9,0x5d,0x44,0xd8,0x8e,0x4f,0x05,0x07,0x4c,0x3a,0xc2,0xe0,0x96,0x9e,0xee,0x65,0x78,0x55,0x0e,0x2a,0x02,0x8f,0x2e,0x63,0x45,0x98,0x1a,0x6e,0x38,0x8d,0xee,0x75,0x34,0xb5,0x93,0x0a,0x87,0x8a,0x21,0x33,0x4d,0x78,0xe2,0x98,0x83,0xfc,0xc6,0x36,0x1f,0xc3,0x0e,0x94,0x67,0x87,0x04,0x7a,0xac,0xc5,0x21,0x5f,0x26,0x44,0xc5,0xcc,0xb6,0x87,0x62,0x39,0x3d,0x30,0xcd,0x79,0x58,0x14,0xab,0x9b,0x03,0xb0,0xe2,0x81,0x08,0x8e,0x78,0x19,0xf8,0xa7,0xcd,0x44,0x9e,0x4a,0x00,0xcb,0xe0,0xfa,0xf2,0xc5,0xe5,0xb4,0x50,0x9f,0xa2,0x01,0x51,0x92,0x1e,0x9e,0xb3,0xdd,0xcd,0xe1,0x39,0x6a,0xbb,0x2c,0xd4,0x67,0xf4,0x4a,0xab,0xf6,0x10,0x01,0x73,0x18,0x8e,0x7e,0x89,0xc6,0x87,0x88,0xae,0x8d,0xc6,0xaf,0x07,0x93,0x93,0xcf,0xa1,0x3c,0x83,0xb7,0xc7,0xf5,0xa5,0xdf,0x7b,0x1a,0x5c,0xaa,0x97,0x78,0x40,0xa5,0x9e,0x77,0xa0,0x4b,0x1b,0x7d,0xfe,0xf2,0xd3,0xb7,0x9f,0x5e,0x8f,0x0e,0x17,0x17,0xc1,0x01,0x0f,0xc6,0xd7,0x63,0x5c,0xbf,0xa0,0x12,0x4f,0x5c,0xe7,0x91,0x2f,0xe2,0x0a,0x69,0x28,0x51,0xaa,0x70,0x4c,0x40,0x13,0xfb,0x99,0xdf,0x0c,0x87,0x96,0xb8,0x1a,0x6a,0x6f,0xbf,0xf5,0x60,0x64,0xe3,0xbd,0x60,0x63,0x0e,0x4f,0xc5,0x88,0x3d,0x7f,0x38,0xc4,0xa1,0x13,0x29,0xe5,0x55,0x35,0x79,0x10,0xef,0x26,0x5f,0x10,0xe2,0xa9,0xd4,0x77,0x5d,0xef,0xd2,0xeb,0x6a,0x1a,0xd3,0xa9,0xe9,0x6f,0xa5,0x01,0x5a,0xf4,0xb9,0x96,0xe8,0x6a,0xac,0x68,0x7d,0xd4,0x86,0x66,0xbf,0x8e,0xae,0xc6,0xa1,0x21,0x91,0x1b,0x2d,0xb8,0xb5,0x7e,0x19,0xb7,0x0a,0x31,0x20,0xa3,0x1e,0x34,0x52,0x3b,0x31,0x7b,0x52,0x95,0x55,0xfb,0x13,0xc7,0x26,0x1d,0x62,0x5a,0xb1,0xcb,0x48,0xa0,0x0a,0x98,0x43,0x2a,0x65,0xe4,0xd6,0x5a,0x1e,0x69,0xa4,0x3c,0x6a,0x5a,0x2a,0x85,0x06,0x76,0x63,0xcc,0x75,0xe8,0x43,0x58,0x7c,0xcc,0x91,0x8b,0xa4,0x66,0xf4,0xe1,0x2a,0xcc,0xa0,0x2b,0x43,0x19,0x64,0x50,0x38,0xfe,0x5c,0xed,0xd4,0x2c,0xfa,0xd9,0xe9,0x54,0xe1,0x28,0x49,0x8e,0x6a,0x16,0xa8,0x9f,0x75,0x1f,0x39,0xd8,0x81,0x9d,0x8a,0xd7,0xb5,0x5c,0x21,0x27,0x12,0x28,0x96,0x21,0xcc,0x53,0xc4,0x5b,0xaf,0x1e,0x4f,0x89,0x81,0xd3,0xc8,0x66,0xbe,0x0c,0xf5,0x07,0x62,0xad,0xe6,0x19,0xbd,0x34,0xdf,0xb3,0xfc,0xcc,0xd5,0x38,0x47,0xd5,0xb0,0xe7,0x8e,0xe9,0xde,0xdf,0x1d,0x93,0xd0,0x24,0x32,0x6a,0xc8,0xd1,0x18,0x46,0xd9,0xc6,0x4e,0xb5,0xb4,0x53,0x21,0x82,0xa5,0xf4,0x47,0x5d,0x45,0xcb,0x8b,0x2b,0x64,0xf7,0x86,0x01,0xf8,0xae,0x1a,0x06,0xe5,0x96,0xb5,0x0c,0xbb,0xc3,0x61,0x89,0xa0,0x66,0x0d,0x35,0xeb,0x2d,0x52,0x6d,0x3b,0x0a,0x7a,0xda,0x16,0x7a,0xc4,0xb7,0x4e,0xa4,0xf6,0x2a,0x9a,0xb1,0xb1,0x46,0xe8,0xc5,0x2d,0xdd,0x0d,0x90,0xcb,0x99,0x93,0xe8,0xde,0x3a,0xa2,0x35,0xea,0x2f,0xdb,0x8f,0x40,0x2e,0xa7,0x68,0x70,0x13,0x73,0x50,0x70,0x87,0x96,0x7c,0x92,0x21,0xcd,0x80,0xe2,0xbc,0x3f,0x55,0xdb,0x0b,0x22,0xc9,0xd9,0xb7,0x75,0x12,0xe5,0xee,0x86,0xbc,0xe2,0x7c,0xf2,0xa5,0x4d,0x89,0xeb,0xae,0x83,0x00,0x04,0xc8,0xe4,0x24,0x58,0x78,0xa6,0x23,0xc5,0xad,0x90,0x59,0xa3,0x34,0xd8,0x78,0xc5,0xc0,0x62,0x52,0xcb,0x0d,0x16,0xcf,0x97,0x83,0x05,0x41,0xdd,0x3c,0xca,0x15,0xf6,0xeb,0x8a,0x33,0xb4,0x6c,0xc5,0x74,0xc0,0x9f,0xc3,0x4f,0x17,0x4e,0xb7,0x05,0x38,0x21,0xb1,0x69,0xa1,0xb9,0x8e,0xe9,0x85,0xad,0x11,0x86,0x16,0xc6,0x55,0x76,0x81, +0x54,0x4c,0x8b,0x40,0xf2,0xaf,0x71,0xc2,0x97,0x68,0x36,0x9a,0xd9,0x08,0x63,0xf5,0x31,0x4a,0x07,0x67,0xea,0x6f,0x31,0xb2,0x15,0xf5,0xa9,0x33,0x85,0xee,0xcc,0x0c,0x55,0x19,0xe3,0x91,0xb9,0x6b,0x3c,0xd2,0xb1,0x5b,0x71,0x5e,0x35,0x7c,0xaa,0xd8,0x46,0xdf,0xa8,0x39,0xa7,0x7c,0xea,0xed,0xb6,0xf0,0x0f,0xf8,0x35,0xa7,0x22,0x3f,0x6c,0x17,0x28,0x63,0xae,0xe5,0x65,0x10,0x6e,0x50,0x7f,0x69,0x54,0x63,0x7d,0xd9,0x5e,0xc6,0x9c,0x1a,0xe0,0xa6,0x14,0xa9,0x3a,0x28,0xe5,0x3f,0x9a,0xd9,0xcb,0x90,0xbb,0xac,0x12,0xd4,0x3b,0x08,0x63,0xd8,0x9c,0x0e,0x74,0x6a,0x9b,0x4c,0x5b,0x5c,0xc2,0x36,0x93,0x36,0xc5,0x55,0xc7,0x8d,0xa6,0xcd,0xc4,0x0c,0x6c,0x22,0x79,0x7f,0xaf,0x24,0x59,0x58,0x56,0xc9,0x0a,0x8b,0x98,0x6d,0x2e,0xc5,0x53,0xb7,0xce,0x0d,0xd8,0x12,0x07,0x9f,0x3a,0x6b,0x53,0xa9,0xa3,0x62,0x55,0xeb,0x24,0x6b,0x8a,0x5d,0xff,0x39,0xd7,0xdc,0xa9,0x3d,0x55,0xad,0x99,0xa0,0xdf,0x68,0x82,0x9e,0x3f,0xb9,0x7a,0xf1,0xfc,0xf2,0xc9,0xb3,0x17,0x9e,0xc4,0x8e,0x6d,0xd0,0x3f,0x96,0x9c,0xd1,0x82,0xe3,0xb8,0x6a,0x94,0xc2,0x78,0xeb,0x11,0x8b,0x76,0x89,0xbb,0x53,0x37,0x6e,0x91,0xd9,0x73,0x23,0xe1,0x5f,0x55,0x1f,0x88,0x8b,0xa8,0x64,0x09,0x40,0xa4,0x51,0x86,0x45,0x36,0x65,0x9a,0x61,0x47,0xc1,0xa8,0x30,0xd0,0xe1,0x6c,0x6d,0x96,0x70,0x13,0x98,0x96,0x50,0xe4,0x04,0xd9,0xe6,0xa6,0xf0,0x33,0x63,0x05,0x09,0x47,0xa1,0x0d,0x44,0x88,0x4c,0x1b,0x4c,0xbe,0x66,0x9b,0x34,0xa9,0xf0,0x44,0x45,0x5f,0x56,0x2a,0x62,0xf5,0x14,0x9f,0x44,0xb3,0x32,0x86,0x05,0xf7,0xc8,0xd9,0x9c,0x53,0x1b,0x34,0x4e,0xcc,0xaa,0x54,0x87,0x36,0x1e,0x9b,0x40,0x3a,0x4b,0x3a,0x13,0x97,0x90,0x55,0xc3,0xb1,0xcb,0xcd,0x31,0x96,0xb1,0x91,0x75,0x85,0x3e,0x67,0x60,0xb4,0x7c,0x07,0x9d,0x40,0xb1,0x85,0x47,0x1a,0xe0,0x0f,0xec,0xb3,0xcf,0x01,0xa8,0xd3,0xd1,0x4f,0xa5,0x73,0x2c,0x3f,0x32,0x47,0x5c,0xe9,0x55,0x6d,0x9e,0xc0,0x1b,0x6f,0x58,0x23,0xb9,0x38,0x25,0x58,0x55,0x6f,0xc7,0xf6,0x31,0x46,0x6f,0x37,0x70,0x1b,0xd0,0xb4,0xdd,0x31,0x1d,0xfd,0xec,0xf8,0x53,0xfb,0xee,0xad,0x21,0xff,0x9a,0xd4,0x79,0xc6,0xc9,0x53,0x5a,0x63,0xa4,0x5a,0x62,0xdb,0x71,0x85,0x7b,0xb4,0x60,0xd0,0x92,0x03,0x58,0x97,0xa9,0x3b,0x02,0x96,0x6f,0x4a,0x99,0xd7,0x50,0x12,0x55,0x88,0xfd,0xb1,0x36,0x5a,0xe6,0xec,0xc8,0x41,0x83,0x42,0xbd,0xd2,0x4a,0x8e,0x3a,0xfc,0xd6,0x9e,0xfd,0xb5,0xfe,0x88,0x4e,0x52,0x91,0x3e,0x39,0x26,0x80,0x71,0xe9,0x0d,0x18,0x37,0x7c,0xa3,0x10,0xb9,0x97,0x0d,0xd1,0x5a,0xec,0xb4,0xff,0x1e,0xd7,0x18,0xa1,0xca,0xf0,0x34,0x59,0x54,0x6b,0xfe,0xaa,0xe5,0xd9,0x5f,0xeb,0x8f,0x0c,0xfd,0xf6,0x85,0x9d,0xd8,0x41,0xd5,0x1e,0x2e,0x16,0xc9,0x39,0xb1,0x35,0xff,0xbf,0xe8,0x1a,0x21,0x9e,0x7c,0xbb,0xff,0x8c,0xc5,0xc2,0xc0,0x59,0x95,0x90,0xb0,0xe8,0xa8,0x48,0x8c,0xff,0xe5,0x7e,0x72,0xcb,0x2e,0x02,0xaf,0x3d,0xa8,0x37,0x2c,0x56,0xf3,0x58,0xb2,0xc9,0xbe,0x2a,0x2f,0xff,0x5f,0x6b,0xae,0x92,0x5c,0x04,0x4d,0x37,0x02,0x55,0xda,0x38,0xe2,0x9c,0xbf,0x40,0x9f,0x64,0x22,0x0e,0x43,0x7c,0x66,0xce,0x66,0x50,0xcf,0x4a,0xe2,0xd7,0x8e,0xb2,0x98,0x53,0x11,0x80,0xcd,0xae,0x98,0xae,0x56,0xb9,0xc1,0x96,0xc3,0xc3,0x71,0x95,0xd2,0xd2,0x15,0xa4,0xf0,0x4a,0x74,0xe0,0x11,0xe2,0x03,0xc2,0x44,0x46,0x01,0x22,0xa2,0xd5,0x27,0x88,0x69,0x98,0x8a,0xdb,0x2c,0x8e,0xb7,0x3f,0xb8,0xc3,0x05,0x54,0xb4,0x4f,0x21,0x5b,0x83,0x08,0xcd,0xee,0x38,0xc8,0x55,0x5d,0xa8,0xc5,0x49,0xde,0xe1,0x24,0x0c,0xc7,0x52,0xda,0x6f,0xb6,0x46,0xb1,0x80,0x88,0xf0,0x53,0x43,0x63,0x83,0xc2,0x59,0xc6,0x23,0x6b,0xc4,0x1c,0x3f,0x66,0xc4,0x3c,0x46,0x78,0xdb,0xba,0x89,0x32,0x01,0x35,0x6c,0x24,0x59,0x9d,0x6a,0x78,0x0a,0x3d,0x8e,0x54,0xc6,0x51,0xed,0x66,0x73,0xc1,0x12,0x59,0x30,0xd7,0xee,0x14,0x3b,0x25,0xea,0x3b,0x5e,0xf9,0xc7,0x44,0x43,0x98,0x41,0x72,0xb2,0xc3,0x59,0xbb,0x75,0x1a,0x1b,0x69,0xd2,0xa1,0x1a,0x47,0xd4,0xc8,0x58,0x47,0xd6,0x9b,0xe8,0x11,0x48,0xb7,0xbc,0x4b,0x0d,0xbc,0x07,0xa5,0xbd,0xb3,0xde,0xda,0xcf,0xfb,0xcd,0x91,0xf1,0x1e,0xd3,0xbe,0x85,0xba,0x2f,0xda,0x9c,0x5c,0xa7,0xf2,0x40,0x24,0x60,0xed,0xf1,0x2c,0xa2,0x56,0x1e,0xd5,0xdb,0x75,0xe8,0xc9,0x95,0x67,0xd0,0x16,0x1e,0xe9,0x4b,0x4f,0xb9,0x5b,0x2b,0xf4,0x04,0x5f,0x98,0xa7,0x9f,0xf2,0x6e,0xf6,0x78,0x53,0x7b,0x66,0x02,0x10,0x76,0xdb,0x73,0x26,0xa3,0x45,0xb4,0x56,0x0b,0x00,0x1d,0x57,0x1c,0x9a,0x24,0x4b,0x11,0x47,0xb2,0x99,0xd8,0x1c,0x68,0x88,0xe0,0x4b,0x9b,0x74,0xfa,0x3c,0x9a,0x0c,0xa6,0x20,0x32,0x39,0xd1,0xe2,0xc4,0x71,0x80,0x95,0x0d,0x01,0xb2,0x8b,0x78,0xd1,0xd1,0x94,0x20,0x2a,0x19,0xc3,0x3c,0xd9,0x98,0x10,0x66,0xb0,0x78,0x44,0xec,0xf7,0xca,0xde,0x74,0x42,0x3a,0x67,0x65,0x48,0x67,0x48,0x23,0x96,0xb4,0x40,0xc5,0xea,0x52,0x7d,0x5d,0xf7,0x9d,0x34,0xfe,0x92,0xc3,0xce,0xe6,0x7d,0x20,0x4e,0x93,0x5d,0xe3,0x33,0xf9,0x4d,0x1c,0x35,0x57,0xb3,0x96,0xb1,0xc2,0x32,0x88,0x45,0x7e,0xef,0x64,0xa8,0x49,0x7b,0xeb,0x0d,0x74,0x60,0x2c,0x17,0x8a,0x03,0xe9,0xed,0xe7,0xeb,0x25,0xb1,0x9e,0x79,0xf6,0x86,0xdd,0xea,0xe0,0x95,0xed,0x1a,0x93,0x5b,0x8a,0x3d,0x11,0xc5,0x63,0x81,0x34,0x90,0xe2,0x08,0x95,0xee,0x76,0xb0,0xad,0x8f,0xbc,0x64,0xfd,0xfe,0x62,0x57,0xfc,0x46,0x9b,0x32,0x4c,0xd6,0xdb,0x2c,0xdf,0x5e,0xd0,0x93,0xc1,0x46,0xa7,0x59,0x0b,0x4d,0xae,0xbb,0x81,0xf6,0x9c,0x0a,0xd9,0xfb,0x6c,0x20,0x83,0x0f,0xe3,0xbb,0xfd,0x7a,0x20,0x9f,0x85,0x57,0x1b,0xfa,0x8c,0xf8,0x76,0xd4,0x84,0xeb,0xfd,0x7a, +0x13,0x5e,0xfd,0xdb,0x80,0x73,0xa2,0x85,0x9f,0xf4,0xff,0x8d,0x60,0xc2,0xb5,0xe5,0x26,0x70,0xa8,0x1a,0xb2,0xcf,0x74,0x14,0x6a,0x91,0x9a,0x54,0x07,0x56,0x10,0xd9,0x14,0x79,0x57,0xff,0xe6,0x89,0x21,0xe8,0x7a,0x83,0xbc,0x0b,0xcf,0x36,0xe2,0x57,0xde,0x93,0xce,0xc0,0xe5,0x94,0xe0,0xc2,0xfb,0xd8,0x3c,0xe6,0x96,0x95,0x19,0xb0,0x14,0x62,0x47,0xd5,0xc8,0xe3,0xee,0x4c,0x9c,0xb2,0xce,0x5b,0xf4,0xcb,0xe5,0x15,0x90,0x8d,0x5d,0x42,0x70,0x1e,0x2b,0x09,0xe2,0x9b,0xf6,0xf5,0x6c,0x79,0x4f,0x85,0xdb,0x5f,0x0c,0x0a,0xeb,0x64,0x67,0xfa,0x04,0x3f,0x73,0xb8,0x04,0xd1,0x14,0x2c,0x8a,0x4d,0xe4,0xe9,0x78,0xe9,0x58,0x01,0xcc,0x56,0xd5,0x82,0xbd,0xfd,0x13,0x18,0xbf,0xb3,0x8f,0x34,0xb3,0x08,0x3c,0x5b,0xd5,0x7a,0x68,0x78,0xed,0xcd,0x11,0xe0,0x37,0x40,0x81,0x17,0xb2,0xaf,0xd7,0xec,0xdf,0x69,0x11,0x25,0xb1,0x1d,0x3d,0xc2,0x6a,0x12,0x8f,0x4a,0x73,0x1c,0x5e,0xfc,0x95,0xfe,0x71,0x16,0xbb,0xaf,0xa1,0xe1,0x82,0x57,0x7c,0xe3,0x00,0x4f,0x9c,0x70,0x9c,0xcf,0xdc,0x93,0x4d,0x66,0x97,0xba,0x70,0xa2,0x2a,0xac,0xd5,0xc3,0xa6,0x78,0x9f,0x2f,0x4c,0x62,0xbf,0x96,0xb3,0x3f,0xf1,0x39,0xf7,0x20,0x8d,0xe6,0x0d,0x03,0xea,0xf7,0xf9,0xa2,0x38,0x11,0x2a,0x1a,0x45,0x73,0xc4,0x98,0xa5,0x1a,0xbf,0x2e,0xd7,0xf4,0x44,0xc9,0x09,0x90,0xb2,0xd4,0xf5,0xb5,0x85,0xa1,0x13,0x65,0xa7,0x47,0x64,0x65,0xf2,0x9d,0xa4,0x0d,0xdf,0xc6,0xa7,0xb8,0x3c,0x9e,0xd7,0xd2,0x5f,0x8b,0x18,0xde,0x6f,0x98,0x6d,0x4a,0xd9,0x06,0x9d,0x37,0xae,0xb1,0xc1,0x60,0x29,0x90,0x8f,0xcc,0x96,0x29,0xd2,0x21,0x79,0x80,0xf0,0x69,0x45,0xba,0xdf,0xe4,0x0b,0xd9,0xaa,0xda,0x71,0xa5,0x45,0x90,0x84,0x75,0xaf,0x3e,0x66,0x18,0x30,0x7c,0xad,0x4f,0x56,0xe4,0x7a,0xff,0x2a,0x36,0xd9,0xb7,0xd8,0xaa,0x7f,0xa6,0xf7,0x47,0x4e,0x57,0xcb,0x62,0xf5,0x13,0xdf,0x4c,0x70,0x13,0xbf,0x97,0x9b,0xf2,0xb9,0xf3,0xd4,0x7c,0x17,0x4d,0x15,0x46,0x72,0xaf,0x4b,0xca,0xb3,0xcc,0xfd,0x26,0x57,0xce,0x57,0x08,0xe4,0x60,0xb9,0xaf,0xe9,0x70,0xda,0xf5,0xbc,0xd0,0x49,0x22,0xfe,0x5d,0x45,0x4e,0xfb,0x50,0x89,0x49,0x55,0x52,0x42,0xbe,0xce,0x68,0xa3,0x25,0x8d,0x8c,0x9e,0x51,0xd4,0xe6,0xed,0x88,0x92,0xe0,0x84,0xf5,0xbe,0xec,0xdd,0x7f,0xb0,0x4f,0x06,0x1c,0x49,0x0f,0x2c,0x05,0x26,0x14,0x7d,0x91,0x8e,0xf2,0x78,0x1c,0xf4,0xba,0xc1,0xa5,0xfa,0x1e,0xaf,0x2f,0x2e,0x2e,0xd5,0x9b,0x38,0x7a,0xb0,0x40,0xec,0x95,0x50,0xfc,0xae,0xd8,0x15,0x49,0xb1,0x28,0x88,0x4c,0xf4,0x66,0x45,0x96,0xe5,0x2b,0x4f,0x19,0xbc,0xa8,0xdd,0x72,0x8f,0xea,0x2d,0x7d,0x4c,0xfd,0xa3,0x13,0xef,0xcd,0x26,0x4e,0xb1,0x41,0x90,0x57,0x78,0x42,0xcb,0xf9,0x93,0x6c,0x26,0xef,0xe3,0x7e,0x9f,0xca,0xfd,0x00,0xf7,0xf9,0x9f,0xf2,0xe4,0xa6,0x80,0xe7,0xfc,0xd7,0xeb,0xdf,0xe8,0xef,0x72,0xe7,0x8d,0xd5,0x8f,0xf1,0x09,0x0c,0xa2,0xe1,0xca,0xce,0xda,0x4f,0xb1,0x66,0x09,0x62,0x70,0xa5,0x3f,0xc6,0xa5,0xdf,0x83,0x89,0xde,0x84,0xac,0xa2,0xeb,0x1f,0x68,0xe7,0x69,0x9a,0xa9,0x6b,0xf2,0x23,0x20,0xc0,0x7b,0xf4,0x43,0xdc,0x0c,0x56,0xc3,0xf1,0x7d,0x7f,0x80,0xd7,0x5d,0x37,0x51,0xf5,0x7a,0xcb,0x05,0xfb,0xd9,0x91,0x55,0xb3,0x3b,0x33,0xe0,0x79,0x47,0x87,0x76,0x35,0x9a,0x5a,0xe5,0x55,0xf4,0x93,0x04,0x1f,0x42,0x26,0xd7,0xb2,0xa6,0x7f,0x56,0x77,0x51,0x64,0x5c,0xce,0x93,0x32,0x31,0xd8,0x90,0x53,0x2b,0x13,0x2c,0xf9,0x44,0x82,0x8e,0x9e,0x8d,0x2f,0x7c,0xda,0x51,0xfd,0x20,0xe8,0xfa,0x19,0x3b,0xb8,0xb3,0x37,0x7b,0xe8,0xd4,0xf9,0x9f,0x15,0x47,0x3f,0x6d,0x97,0x1d,0xf5,0xb5,0x97,0x30,0xf4,0x28,0x7e,0x36,0xd4,0xe8,0xce,0x0b,0x0d,0xba,0xf4,0x82,0xe1,0xc7,0xa1,0x27,0x39,0x3c,0x39,0x78,0xc6,0x55,0x08,0x87,0xe2,0x8f,0x07,0x93,0x6e,0xf4,0x2c,0xf0,0x04,0xc9,0x19,0x2f,0xe8,0x69,0xd7,0x46,0x19,0x48,0xbb,0x29,0x3b,0x51,0x43,0xa5,0x0c,0xa1,0xd3,0xd0,0xb7,0x35,0x9a,0xc2,0x17,0x65,0x48,0x02,0x8d,0x35,0xbd,0xea,0x47,0xa6,0xf6,0x4e,0xf3,0x03,0xdd,0x4d,0x29,0x8f,0xa8,0xac,0x9c,0x63,0x54,0xbe,0x0b,0x42,0xb7,0x23,0xad,0x75,0x97,0x4f,0x3b,0x8d,0x9e,0x3f,0x5e,0xb7,0x15,0xd4,0x94,0x13,0xfb,0xa4,0x8e,0xf2,0x22,0x41,0x6c,0x93,0x48,0x23,0x43,0x44,0x50,0x36,0x07,0x89,0x39,0x7e,0x9c,0xd6,0x34,0x02,0xf7,0x20,0xe0,0x2d,0xb5,0x1a,0x5f,0x5b,0x6f,0xc7,0xe1,0x04,0x99,0x38,0x91,0x78,0x7d,0xdd,0x6b,0xa0,0x7b,0x84,0x53,0x9d,0x94,0x61,0x21,0xc0,0x82,0x29,0x0f,0x14,0x88,0xa7,0xdd,0x7b,0xe0,0x81,0xe9,0x89,0xe2,0xdc,0xeb,0x26,0x4d,0xe0,0x4f,0x2c,0xf0,0x23,0xfd,0x56,0xc4,0x81,0xc0,0x5e,0x2d,0xd6,0xf1,0x9e,0xbd,0x25,0xfb,0x6a,0xd2,0x35,0x80,0x03,0x0c,0xdb,0x06,0x20,0x0c,0x50,0x5d,0x86,0x37,0x47,0x6c,0x48,0xc3,0x93,0x38,0x34,0x0f,0x6b,0xec,0x78,0xc2,0x0e,0x55,0x0c,0xc6,0xa8,0x8d,0x65,0x69,0x9a,0xe0,0xe3,0xc9,0xf2,0x74,0x61,0xcb,0x0b,0xb2,0x7b,0x7c,0x3a,0xf4,0xae,0xbc,0x30,0x65,0xbb,0x29,0x1b,0x9a,0x21,0x7c,0x88,0x57,0xc5,0x92,0x6d,0x21,0x5e,0x13,0x5e,0xe1,0x0b,0xb6,0x3a,0x15,0x73,0xbd,0xc5,0xdd,0xb2,0xbc,0x25,0x46,0x68,0xf1,0xad,0xee,0x06,0x6e,0x17,0xf9,0xfb,0xbf,0x6d,0xd7,0xf7,0xe6,0xfa,0xcd,0x8c,0xf8,0xaf,0x1b,0xbe,0x2b,0x31,0x12,0xdd,0x11,0x17,0x9c,0x7f,0x69,0xef,0xd6,0x65,0x05,0x42,0x13,0xf0,0xc5,0x66,0x16,0x8b,0xf5,0x02,0xed,0x91,0xf5,0x3d,0x5f,0xfd,0xf6,0x9a,0xb3,0x41,0xe1,0x6a,0xbd,0x5e,0xb2,0x09,0x9f,0xd9,0xf1,0xe1,0x83,0x37,0xc1,0xe4,0x62,0xfe,0x76,0x3b,0x9e,0x67,0x0f,0x56,0x27,0x1f,0x16,0x6d,0x6a,0x62,0xd1,0xf7,0x7e,0x54,0x93,0x6c,0xfe,0x7b,0xed,0x5e,0x2f,0xbd,0x13,0xb8,0x43, +0xcd,0xea,0xd1,0xf6,0x8a,0xe8,0x7b,0x7b,0xd2,0xb1,0x55,0x6e,0xe5,0x34,0x2e,0x18,0x2f,0xfd,0x0c,0xd1,0xa5,0xc4,0x18,0x36,0x8b,0xc7,0xe1,0x2f,0x9c,0xdb,0x99,0xe3,0xd8,0x9c,0x0e,0x09,0x24,0x3d,0x5a,0x53,0x8f,0xf0,0xe1,0xd4,0xb1,0x5f,0xf0,0x73,0xf8,0x83,0x71,0x44,0x21,0x82,0xe8,0x2c,0x40,0xf4,0xea,0x39,0xa2,0xfc,0x11,0x2c,0x1a,0x3f,0x36,0x27,0xd8,0x1b,0x43,0x69,0x6e,0x51,0x5c,0x8a,0xc8,0x7d,0x92,0x44,0x8f,0x48,0xfa,0x89,0xdd,0x41,0x44,0x98,0xae,0x78,0xdd,0x09,0xe0,0x84,0xf9,0x07,0xf9,0xaf,0x71,0x89,0x79,0xa5,0x6b,0x4b,0xbb,0x1c,0xbd,0xbe,0x19,0xe8,0x63,0x56,0x06,0xfa,0x08,0x9a,0x34,0x22,0xf4,0x09,0xa2,0x9f,0x62,0x33,0x8c,0x32,0x14,0x59,0x49,0x23,0xc2,0x0e,0xdf,0xc7,0x60,0x22,0x1a,0xf5,0x2c,0xdf,0x16,0x2c,0xa3,0x85,0x0a,0xa9,0x36,0x0f,0x11,0xcb,0x53,0xa7,0x3a,0x52,0x1d,0x56,0x93,0xbd,0xc2,0x86,0x73,0x3c,0xb1,0xb6,0xa6,0x9c,0xe2,0x81,0xab,0x4b,0x2d,0x2d,0x10,0x08,0x90,0xb7,0x80,0xc3,0x1f,0x5c,0xdf,0xff,0xce,0xaa,0x56,0x97,0xd2,0x5d,0xc1,0xbe,0x2a,0xbb,0x66,0xc3,0x6d,0x68,0xc4,0x06,0x95,0x84,0xb7,0x5a,0x6f,0x97,0xf1,0x42,0x87,0xe2,0xe0,0x98,0x8e,0x6f,0x63,0x2e,0xf4,0x36,0x16,0x6c,0xc4,0x3b,0x98,0x48,0xb9,0xa1,0x5f,0x41,0x2f,0xb9,0x56,0xde,0xf7,0x0f,0x87,0x62,0xf7,0x0a,0x29,0x1d,0x73,0x46,0x76,0x34,0xfb,0xa1,0x04,0x14,0xb7,0x4c,0xf7,0xc8,0x13,0x92,0x9b,0x88,0x01,0x39,0x89,0xc6,0x0d,0xe6,0xd8,0x19,0x5b,0x54,0x47,0x34,0x66,0x2f,0x99,0xc8,0x6a,0x9d,0x7f,0xe8,0xb9,0x6a,0x86,0xc4,0xc1,0x49,0xcc,0x9c,0x16,0xdb,0x1f,0x7d,0x9f,0xa7,0xfb,0x9d,0x4d,0xa5,0x86,0x9d,0x46,0xaf,0x3e,0x03,0x1c,0x10,0xe8,0x96,0x45,0xa8,0x04,0x77,0x6b,0xf8,0xc4,0x4c,0x4b,0x98,0xe3,0xea,0x8d,0x93,0xa8,0xd5,0x12,0x6c,0xb6,0x0c,0x84,0x4f,0xbb,0x96,0x8e,0x9a,0xc8,0x37,0xd9,0xf9,0xf9,0x37,0x5a,0xaf,0x40,0x97,0x1a,0x09,0x67,0xea,0x8f,0x9d,0x23,0x50,0xfc,0x95,0x67,0x55,0x73,0x83,0xe9,0x90,0x36,0x7e,0x5e,0xd2,0x0a,0x6c,0xbd,0x6a,0x0e,0x11,0x76,0x92,0x74,0x02,0xdc,0x28,0xa6,0x48,0x52,0xc4,0xe7,0x95,0x75,0x31,0xf3,0xed,0xf0,0x99,0x11,0x11,0xac,0xeb,0x5e,0x93,0x77,0x50,0x2d,0x38,0x5f,0x7a,0xe6,0x3b,0xe0,0x20,0xe8,0xbf,0xac,0xad,0x5c,0x8b,0xd6,0x09,0x07,0xd3,0x75,0xc1,0xb3,0xfc,0x50,0x7e,0x13,0xf6,0x8f,0x2d,0x33,0xfe,0x78,0x25,0xc7,0x40,0xce,0x2e,0x47,0xc4,0xa3,0xf9,0x78,0x62,0x23,0x0d,0x37,0x47,0x97,0x9a,0x0f,0xd4,0xe4,0x40,0x53,0x3a,0x63,0x01,0x30,0xee,0x02,0x04,0x45,0x53,0x51,0x2e,0xae,0xab,0x17,0x64,0xe5,0xd0,0x03,0xe2,0xe5,0xb7,0x38,0xf8,0xa6,0xc6,0x21,0xed,0x0c,0x81,0x24,0xd2,0xf1,0x20,0x23,0x62,0x0b,0xca,0xa3,0x9c,0x6a,0x4e,0x11,0xdd,0x06,0xf5,0x43,0x8d,0x44,0x07,0xff,0x28,0xbb,0x78,0xc6,0xbf,0x7d,0x27,0xa0,0xd2,0x51,0x7d,0x65,0x65,0x87,0x06,0xff,0x95,0x5d,0x03,0xfe,0x89,0xfe,0xc9,0x41,0xba,0x2b,0x8a,0x93,0x3a,0xca,0x39,0x2d,0x08,0xad,0xb1,0x77,0x18,0x09,0xe8,0x48,0x5a,0xd7,0x7a,0x2c,0x58,0x19,0x73,0xa6,0xe9,0xa1,0xbc,0x74,0xd5,0x9b,0x3e,0xcf,0x59,0x20,0x85,0xa8,0xfa,0xd3,0xf1,0xb8,0x84,0x34,0xc4,0xfd,0xe2,0x43,0xc3,0x0c,0x67,0x62,0x54,0xad,0x4e,0x0c,0x5b,0x37,0x60,0x52,0x0a,0xbd,0x92,0x01,0xd3,0x23,0x5b,0x52,0x35,0x23,0x20,0x56,0xf3,0x0a,0xfe,0x5a,0x21,0x84,0x8d,0x31,0x5f,0x7e,0x4f,0x2f,0x4a,0x83,0x5b,0xce,0x31,0xeb,0x94,0x23,0xca,0xe6,0xed,0x7d,0x9e,0xaf,0xa2,0x5f,0x63,0xe5,0x96,0xab,0x1a,0xe6,0xd2,0x4b,0x7c,0xd7,0xe2,0x09,0x09,0x4f,0x73,0x91,0x8e,0x12,0xf7,0x62,0xec,0xaa,0xa9,0x96,0x0d,0xed,0x34,0x6d,0xb4,0xb4,0x23,0x48,0x88,0x58,0x51,0x29,0xd7,0x65,0x10,0x0a,0xb1,0x9b,0xe2,0xe0,0x28,0xbb,0x28,0x31,0xe6,0xa0,0x08,0xe0,0xa4,0x95,0x19,0xf7,0x91,0xb1,0x75,0x36,0xd6,0xa2,0x88,0xea,0x94,0xc9,0x25,0xc7,0x7c,0x9a,0xd4,0x4e,0xc2,0xb4,0x3c,0x09,0xe9,0xb8,0xb9,0xdb,0x36,0x85,0xaf,0x32,0xca,0x8d,0x40,0x8e,0xed,0xae,0x85,0xb3,0x58,0xa3,0x43,0x89,0xa6,0xa2,0x95,0x5c,0xee,0x37,0xb6,0xfb,0xe5,0xfb,0xa3,0xda,0xde,0xb5,0xa4,0x42,0x20,0x54,0xf3,0x3b,0x8d,0xb9,0x13,0xd0,0xcb,0xee,0x84,0xd8,0xd3,0x79,0x25,0xd7,0x34,0x25,0x91,0x99,0xb3,0x91,0x33,0x97,0x63,0xa3,0xd3,0xa8,0x7f,0xf8,0x34,0x56,0x7d,0x75,0xd5,0xfe,0x4e,0x2b,0xea,0xa4,0x56,0xa3,0x13,0xa1,0xf9,0xf5,0xcd,0xac,0x5e,0x94,0xb3,0x1f,0x3c,0x4d,0xba,0xe5,0x5d,0xb5,0xbe,0xdd,0x3e,0xdf,0x68,0x79,0xb8,0xfb,0xa8,0x34,0x05,0x11,0x37,0x2f,0x53,0xbf,0xc9,0xe6,0x0a,0x89,0x26,0x6d,0xd0,0x61,0x6a,0xe3,0x7b,0x9e,0x9a,0x54,0xfb,0x5e,0x62,0xed,0x1f,0x55,0x03,0x7a,0x1d,0x20,0x75,0xdf,0x29,0xb7,0xbe,0xe8,0xc1,0xfa,0x4a,0xd4,0x4e,0x4d,0xbd,0x36,0x36,0x5a,0x00,0x13,0x9d,0xe8,0xb3,0xa3,0x4e,0x33,0x26,0xae,0x78,0x3c,0x92,0x5a,0xc7,0x26,0xb0,0x81,0x2e,0x2c,0x07,0x8a,0x7e,0x37,0xac,0x16,0x0d,0x0d,0x33,0xec,0xcb,0x73,0x25,0xcf,0xd9,0xc8,0x02,0x31,0xaf,0x99,0x8d,0x01,0x25,0x36,0x4c,0xc2,0x7e,0xe3,0xb4,0x64,0x91,0xf8,0x7b,0x9e,0x55,0x5b,0x7f,0xe3,0x09,0x42,0x1a,0x9f,0xec,0x7a,0xad,0x93,0x2e,0x5f,0x2e,0x5f,0x8f,0x39,0x92,0x73,0x89,0x3e,0x5b,0x47,0xc1,0xb4,0xf8,0x7d,0x68,0x91,0x52,0x65,0x28,0xfc,0xae,0xcb,0x69,0xf0,0xd8,0xef,0xa3,0x32,0xf9,0xbd,0x1d,0x07,0xf2,0x7f,0x4b,0x48,0xa0,0xe5,0x31,0x1f,0xa9,0x0f,0xf5,0x31,0xd7,0x06,0x82,0x4d,0xc8,0x0f,0x2a,0x76,0x21,0x6d,0x1d,0x34,0xe6,0xe2,0x8c,0x65,0x1e,0xc0,0xd9,0xc4,0xed,0xd6,0x1c,0x34,0xcf,0xf7,0x38,0xf3,0x9a,0xef,0x7a,0x9f,0x5c,0xb0,0xd0,0x21,0x5d,0xd3,0x8a,0x3d,0xe5,0xcb,0xef,0x5e,0x07,0x97,0xcf, +0x1c,0x87,0x1b,0x8f,0xbf,0xf5,0x38,0x51,0xfb,0xfb,0xa8,0x01,0x92,0xca,0x2e,0x10,0x0c,0xe0,0x18,0xcf,0x24,0x2a,0x21,0x04,0x90,0x48,0x2c,0x11,0x09,0x10,0x76,0x40,0xe8,0xae,0x03,0x02,0x76,0x21,0xfe,0x75,0x46,0xef,0xca,0x38,0x90,0x4f,0x9c,0x58,0x21,0x39,0x24,0xf8,0x49,0xc2,0x81,0xed,0x44,0xf2,0x64,0xf3,0x25,0x6e,0x11,0x34,0x73,0xb7,0xff,0xd4,0x70,0x88,0xaf,0xb6,0x12,0xf8,0xa9,0xf5,0xb9,0x9f,0x27,0x41,0x58,0xc9,0xa6,0x90,0x27,0xd2,0x53,0x36,0x74,0x7e,0x17,0x2f,0x02,0xb9,0xdd,0xb3,0x8b,0x91,0x63,0x9e,0x36,0x49,0x5c,0x32,0xa3,0x3d,0xf7,0x46,0x6c,0x8c,0x55,0x91,0xb6,0x0a,0x00,0xcf,0xee,0x20,0x65,0x1d,0xd3,0xa4,0x1a,0x88,0x93,0x89,0x03,0x2d,0x62,0x8e,0xc5,0x4a,0x30,0xd1,0x72,0x17,0x21,0x05,0xa2,0x67,0x17,0x49,0x90,0x46,0x4c,0x0b,0xa8,0x7c,0x64,0xa4,0x24,0xdd,0x74,0x1c,0xd1,0x5d,0x29,0xee,0xa0,0x85,0xb7,0x82,0x27,0x50,0x80,0x3d,0xcd,0xc0,0x46,0xb9,0x96,0x4c,0xe2,0x4c,0x2e,0xfb,0x31,0x4b,0xda,0x0c,0x98,0xfc,0x9b,0xa4,0xb7,0xc7,0xc1,0x07,0x7f,0xda,0x84,0x1d,0x8f,0x03,0x88,0x61,0x53,0x22,0xd9,0xdc,0x57,0xde,0x53,0x8f,0xe5,0x07,0x08,0xa8,0x97,0xd7,0x03,0xea,0x71,0x6c,0x70,0xd8,0x3a,0x09,0xfa,0x4b,0x71,0x48,0x5b,0x43,0xba,0xcc,0x09,0xd4,0x97,0x34,0xa8,0x8b,0x4a,0x1c,0x2a,0x4d,0xfd,0x17,0x2c,0x49,0x33,0x3c,0x01,0xee,0xd4,0x52,0x2c,0xc6,0x57,0x20,0x46,0xd6,0x86,0xbd,0x55,0x9b,0x0a,0x9f,0x9c,0x31,0x15,0x62,0x83,0x83,0x20,0x7c,0x2b,0x00,0x8d,0x63,0xff,0x30,0x80,0x69,0x11,0x72,0x35,0xe0,0x2f,0x47,0x2c,0x56,0x82,0x2e,0xa6,0xb4,0x8f,0xf9,0x25,0x22,0x2e,0x94,0x37,0x11,0xe2,0x53,0x4e,0x9d,0x50,0xbc,0xca,0xbd,0x71,0x23,0xa6,0x96,0xdf,0x1c,0x0e,0x33,0x44,0xa3,0x53,0xe5,0x93,0x6e,0x57,0x2d,0x7b,0x92,0x73,0xce,0x05,0x9f,0xb6,0x67,0xe5,0x47,0x17,0x17,0xaa,0x8c,0x63,0xcc,0x5d,0xd5,0x73,0x0f,0xcf,0xe2,0x4a,0x6c,0x60,0xa4,0xb7,0x18,0x38,0x39,0x10,0x38,0xed,0x3d,0xd2,0x1e,0xd0,0xe6,0x33,0x01,0x2e,0x98,0x38,0xd7,0x82,0x64,0x7e,0x25,0x91,0x2a,0x65,0x57,0x32,0x87,0xc7,0xc1,0x65,0xfc,0xcd,0x10,0x92,0xde,0x9c,0xe8,0x07,0x99,0x40,0x71,0xe0,0xe3,0x6b,0xb8,0x2b,0x1f,0x0e,0x9d,0x5b,0xc7,0xf9,0xeb,0x16,0xd6,0x4e,0x90,0x16,0x15,0xab,0xbb,0x7c,0xb0,0x41,0x7a,0xd6,0x15,0x82,0x34,0xde,0x9e,0x9f,0xdf,0x32,0x19,0x5b,0xd2,0x73,0xc4,0x15,0x41,0x23,0x17,0xb5,0xc4,0xcf,0x57,0xc4,0xce,0x36,0x1e,0xaf,0xa8,0xe9,0x45,0x3d,0x14,0x3d,0x04,0x00,0x3d,0x38,0x2e,0x4c,0x16,0x74,0x70,0x8f,0xd6,0xf6,0x5a,0x95,0x97,0x3f,0x3b,0xd7,0xff,0x44,0x14,0x09,0x74,0xc6,0x46,0x77,0x94,0xd5,0x9e,0xb3,0xe1,0xa3,0x85,0x96,0x92,0x47,0x64,0xcf,0xfb,0x3a,0xe7,0x58,0x46,0xa4,0x44,0xa6,0xb0,0xf9,0xf0,0x26,0x9a,0x87,0x7e,0x11,0x73,0xa8,0x37,0x4e,0x52,0xd2,0x8c,0xe7,0x3a,0x6f,0xaf,0x48,0x3e,0x82,0x15,0x25,0xa2,0x53,0x01,0x53,0x73,0xad,0xb4,0x10,0x72,0x77,0x21,0x42,0x75,0x69,0x49,0x8e,0xe0,0x79,0x6b,0xa0,0x57,0x91,0x34,0x81,0xb1,0x2b,0x38,0x7e,0x03,0x67,0x6b,0x72,0xc0,0x68,0x6d,0x23,0x3f,0xce,0x8f,0x81,0x33,0xe8,0x9b,0xc8,0xbe,0xa1,0x6e,0x97,0xe3,0x02,0xc5,0x78,0xc3,0x82,0x13,0x1b,0xc1,0xb2,0xd2,0x23,0xb6,0xfb,0x34,0xb3,0xca,0xe2,0x4a,0xbb,0x0a,0x56,0x33,0xd0,0x06,0xce,0x4e,0xb9,0xf2,0x7b,0x58,0xed,0x3a,0xeb,0xe5,0xbe,0xb9,0x72,0xdf,0xfc,0xd3,0x7d,0xf3,0x6c,0x8c,0x24,0x2e,0x04,0x3f,0x57,0x25,0xa8,0xaf,0x02,0x8c,0xfe,0x76,0x68,0xba,0x40,0x8f,0x10,0x5e,0x64,0x83,0x10,0x3c,0xfc,0x24,0x08,0x6f,0xdd,0x08,0xe3,0x06,0x2d,0xa8,0x07,0xa3,0xc5,0xc0,0xec,0x40,0xac,0x64,0x3e,0x88,0x3a,0x9b,0x40,0x11,0x75,0xe7,0xac,0x6e,0x73,0x72,0x37,0x87,0x83,0x5e,0x48,0xe5,0xc6,0x90,0xb0,0x38,0xc7,0xe9,0x9f,0x03,0xff,0x0a,0x3b,0x23,0x90,0x41,0x10,0x42,0xde,0x0c,0xb1,0x43,0xc2,0x3e,0xbd,0x58,0x06,0x8a,0x8b,0xdf,0x62,0x30,0xd8,0x3e,0x85,0x26,0x3b,0x37,0xac,0x3b,0x05,0xbd,0x6f,0x9e,0xe8,0xdf,0xa8,0x5f,0x31,0xf4,0x9e,0x57,0xcf,0x19,0x41,0xae,0xdc,0x8d,0x54,0xbc,0x33,0x18,0x4b,0xbb,0x72,0x25,0x4e,0x42,0xa6,0xf1,0x00,0x87,0x1c,0xac,0xf2,0x77,0x93,0x40,0x6c,0x8c,0xb1,0x1e,0x52,0x20,0x02,0x23,0x4a,0x40,0xa0,0x93,0xf6,0x70,0x28,0xd6,0x89,0xb2,0x39,0x36,0xd2,0x71,0x4d,0x0e,0x95,0x89,0xe0,0x49,0x78,0x65,0x96,0x3d,0xd1,0xe1,0x03,0x4c,0xca,0x0f,0x10,0x56,0xce,0x7e,0x9b,0x8d,0xcb,0xae,0x4e,0x02,0xe9,0x31,0xbc,0x7b,0xa4,0x55,0xea,0x1b,0x67,0x7e,0xce,0x75,0xd0,0x8f,0x84,0x83,0xc0,0x96,0x63,0xbf,0x69,0x1e,0x2c,0x7c,0x52,0xdd,0x70,0x0c,0x05,0x31,0x93,0x71,0x62,0x5c,0xbb,0x71,0xb0,0x5b,0xe0,0xd5,0x04,0x19,0x61,0x2a,0xeb,0x18,0x54,0x93,0x41,0x03,0xaf,0x1a,0x39,0x93,0x40,0xa1,0xd6,0x3b,0xd1,0x91,0x35,0x61,0xa5,0x6d,0xe4,0x68,0x6e,0xe6,0xb2,0x54,0x20,0x1b,0xba,0x73,0xcb,0x84,0x5c,0x48,0x44,0xfa,0xcb,0xf2,0x09,0x4b,0xe2,0xa3,0xab,0x8b,0x0c,0xac,0x36,0x35,0x38,0x97,0x93,0x77,0x57,0x32,0xd4,0x05,0x33,0xd4,0xe6,0x39,0xf1,0xd1,0x3d,0xe2,0xb5,0xfc,0x52,0x08,0x34,0x73,0xb2,0x07,0x22,0x3c,0xfb,0x1c,0x39,0x8e,0x71,0x64,0x3f,0x27,0x82,0xa9,0x18,0xa6,0x21,0x90,0x44,0xb3,0xd0,0x95,0xc2,0x8a,0xce,0x2a,0x41,0xbe,0x63,0x8e,0x5f,0x00,0x93,0x20,0xe4,0x94,0x9e,0xd9,0x50,0xdb,0x12,0x71,0x20,0x46,0xa0,0xf1,0xcd,0xae,0x92,0x4c,0x9b,0xc6,0x43,0x5c,0x90,0xf3,0xac,0xd3,0x57,0x0f,0xda,0x08,0xf5,0x0b,0x26,0x49,0x11,0xe9,0x4b,0x88,0xd3,0xb0,0xc1,0xff,0x1e,0x91,0xc3,0xc6,0xb8,0x5a,0x6a,0xd9,0x69,0x91,0xef,0xc2,0xc4,0x3e,0xfc,0x56,0x58,0xac,0x30,0x55,0x76,0x3a,0x43,0x3b, +0xe1,0x66,0x0e,0xc3,0xd4,0x4e,0xa7,0x92,0x59,0x42,0xa0,0x4e,0x1d,0x52,0x11,0xf7,0x6e,0xf2,0x50,0xab,0x7a,0xd3,0x7c,0x3f,0x0d,0x7a,0x0e,0x4e,0x8e,0x53,0xac,0xeb,0xcb,0x5e,0x65,0x04,0x4c,0x1d,0xe9,0x17,0x32,0x00,0x3b,0xf7,0x76,0xb5,0x74,0x88,0x07,0x95,0x89,0xa7,0x69,0x4b,0x3e,0x5e,0xd8,0x93,0x25,0xc3,0xda,0xf2,0x86,0x2c,0x5d,0xc9,0x5d,0x7f,0x77,0xf1,0x1e,0x41,0x3e,0x2d,0x63,0xd1,0x65,0x97,0x3e,0x95,0xa5,0xbf,0x72,0x1c,0x64,0xfc,0x3f,0xbe,0xb0,0x2a,0xa1,0xa3,0x27,0x9c,0xb9,0xf9,0x31,0xcd,0x63,0xcd,0x69,0xe2,0xfc,0x9b,0x33,0x6b,0x21,0xbd,0x20,0xc4,0x72,0xd3,0x3a,0x23,0x41,0x95,0x00,0xac,0x6c,0x36,0x4b,0x0c,0xce,0x55,0xac,0xcc,0xe7,0x96,0x26,0xac,0x66,0xfa,0x62,0xd7,0xd7,0x80,0x6d,0xb7,0x5c,0xda,0x6c,0x2e,0x9c,0x96,0x6e,0x99,0x5f,0x04,0x5c,0x94,0xd6,0x8c,0x7a,0xf7,0xfe,0x83,0xfe,0x92,0xc5,0xd4,0x59,0x99,0x98,0x12,0xc6,0x81,0x37,0x6a,0xc6,0x11,0x68,0xaa,0xf9,0x52,0xf5,0x18,0x98,0xb7,0x47,0xac,0x92,0xf2,0xd6,0x44,0x23,0xa2,0x4f,0xe6,0x65,0x5e,0x5b,0x5d,0xc2,0xdc,0xeb,0xf4,0xb6,0xfa,0x29,0xae,0x4d,0xe7,0x4c,0xea,0x37,0x9d,0xf0,0x56,0x3f,0xc5,0xb5,0xc5,0x2f,0xfa,0x99,0xdc,0x59,0x7e,0x63,0x99,0x6f,0x7d,0xbb,0x69,0x0a,0x65,0x76,0x18,0x34,0x5f,0xe1,0x5c,0x49,0x2a,0x01,0x77,0x02,0x70,0x72,0xcc,0x8f,0xdb,0x9e,0x65,0x70,0x4a,0x27,0xa0,0x9b,0x44,0x3d,0x18,0x72,0x3d,0x7c,0x20,0x7a,0x3d,0x1c,0xb5,0x44,0x74,0xd1,0x82,0xa4,0x72,0x53,0xf8,0x92,0x00,0x41,0x4b,0xe3,0x62,0x22,0xad,0x84,0xbf,0x55,0xa5,0x4e,0x1a,0xdb,0x33,0x3d,0x8e,0x8f,0x4a,0x57,0x5f,0x97,0x23,0x56,0x52,0x05,0x07,0x43,0x9d,0x07,0x40,0x58,0x86,0x30,0x26,0xba,0xc8,0xc4,0xf4,0x1a,0xb8,0x06,0x6d,0x7d,0xf6,0xff,0xac,0x59,0xd0,0xc3,0x44,0x9d,0x4e,0x0d,0x97,0xf3,0x20,0xb4,0x5f,0xbd,0x95,0xe0,0x48,0x95,0x47,0x36,0xc5,0x46,0x22,0x66,0xc6,0x1a,0x08,0xc3,0x51,0x91,0x8c,0xcb,0xfb,0x7a,0xc2,0x90,0x61,0xf5,0x78,0x30,0x75,0xc4,0x41,0x58,0x7d,0xc1,0xbb,0xda,0x58,0x09,0xd2,0x0e,0x20,0x8e,0xa0,0x55,0x5a,0xca,0x99,0x22,0x9a,0x99,0x67,0x5c,0x44,0x49,0x75,0x3f,0x18,0x60,0x09,0x53,0xa2,0x78,0xd3,0xf3,0xf3,0xa4,0x96,0x30,0x93,0xd3,0xfd,0x96,0x58,0x2d,0x36,0x78,0x53,0x8a,0x9a,0xec,0x53,0x4e,0xd8,0x15,0x7a,0xee,0xe4,0x65,0x25,0xb8,0x5a,0x4f,0x26,0xc3,0xcc,0x22,0xc3,0xa8,0x1f,0x1a,0xfd,0x99,0x75,0xb4,0x2c,0xdf,0x32,0x77,0x6d,0x6e,0x70,0xec,0x0a,0x0b,0x8f,0x51,0xee,0xdc,0x4a,0x9c,0xc7,0xa3,0xf2,0xf1,0x38,0x6c,0x2f,0x62,0x71,0xbb,0x51,0xe6,0x65,0x02,0xbf,0xc4,0xa5,0xc9,0x45,0x47,0x34,0x43,0xbe,0xbe,0x8d,0x84,0x0d,0xcb,0x7a,0xeb,0x45,0x06,0x43,0x10,0x3d,0x45,0xaa,0xbc,0xac,0x26,0x65,0xa9,0xa0,0x0f,0xfa,0x26,0x40,0xc5,0xeb,0x45,0x56,0xca,0xdf,0x50,0x99,0x6e,0xb2,0x96,0x63,0x43,0x3f,0x47,0xb2,0x8e,0x63,0x55,0x46,0x3e,0x89,0x89,0xc3,0x58,0x9f,0xf6,0xe7,0x95,0x60,0x2b,0x62,0x74,0x9b,0xc5,0x01,0xd3,0xe1,0x56,0xa3,0xad,0xfa,0x26,0x26,0x39,0x88,0x35,0x10,0x16,0xbc,0x47,0xe9,0xd8,0x34,0x8a,0xe5,0xe4,0xa8,0x62,0xed,0x36,0xac,0xdf,0x9d,0x52,0x02,0x46,0x75,0x46,0x28,0x96,0x5c,0x35,0x3c,0xb9,0x92,0xe0,0xba,0x96,0xa7,0x46,0x88,0x90,0x1b,0x09,0xac,0xa3,0xaa,0x20,0x07,0x55,0x11,0x32,0x36,0xbb,0x61,0xc7,0x26,0xc5,0xaa,0xd8,0xcd,0x3c,0xe8,0x8a,0x24,0xa8,0x14,0xcc,0xed,0x2c,0x14,0x21,0xfd,0x2d,0xde,0x47,0x53,0x45,0xdf,0x4d,0xf4,0x1a,0x71,0x0c,0x8f,0xd2,0x03,0x77,0xaa,0x65,0xa9,0x32,0xb5,0xba,0x10,0x34,0x48,0xb5,0xb3,0xaf,0xb2,0x45,0xda,0xdc,0xee,0x51,0x7c,0x60,0x28,0x41,0x41,0xe9,0x09,0x32,0x82,0x3d,0x96,0x24,0x46,0xbc,0x84,0xdd,0x7c,0x23,0xb4,0x31,0x62,0xed,0x59,0xec,0x74,0xcb,0xc9,0x90,0xd2,0xee,0x3c,0x2c,0x9d,0x80,0xf5,0x87,0x63,0x73,0x5e,0xc9,0x4c,0xc2,0x33,0xcf,0xa8,0x7a,0xc7,0x9e,0xa0,0x56,0xdc,0x2d,0x87,0xb6,0x84,0x77,0xc2,0x5f,0xee,0x3a,0x01,0x22,0xc7,0x8e,0x0a,0xca,0xac,0x84,0xec,0x04,0x3a,0x6d,0x29,0x68,0x19,0xf3,0xf2,0x23,0x39,0xf6,0xad,0x67,0x51,0x7e,0x71,0x31,0x08,0x26,0xf8,0x04,0x58,0x59,0xbb,0x87,0x58,0xc1,0xec,0xf9,0x39,0xbf,0x32,0x1b,0x0a,0x04,0x33,0x3f,0x00,0x74,0xc9,0xa2,0xa6,0x88,0x07,0x0a,0x7d,0xa1,0x09,0xee,0x97,0xab,0xab,0x20,0x18,0x74,0x90,0x97,0x08,0x68,0xa7,0x25,0xf9,0x8c,0xac,0x7c,0xab,0xe8,0x50,0x4f,0xaf,0x6f,0x53,0xbb,0x3c,0x36,0xa7,0x2a,0x75,0xe7,0x0a,0xf4,0xee,0xc8,0xcc,0xab,0x37,0xe6,0x5c,0x16,0xd5,0x69,0x1e,0x57,0xe7,0x39,0x23,0xe4,0x63,0xa9,0x23,0x66,0x0d,0x0c,0x4c,0x72,0xa6,0xd0,0x4a,0x06,0x1f,0x2c,0x2e,0xc7,0xba,0x96,0x79,0x95,0x5f,0xc7,0x2b,0x13,0x1c,0x5c,0x52,0x4e,0x6a,0x22,0x93,0x9a,0xc8,0xa4,0x6a,0x97,0x11,0xcc,0x65,0x32,0xb6,0x90,0x0e,0x18,0xe3,0x07,0xe5,0x5c,0xa2,0x16,0x3b,0x8f,0x09,0xcf,0xa3,0x08,0xef,0xfa,0x83,0x84,0xe8,0x20,0x38,0x46,0x64,0xf4,0x05,0xad,0x25,0xbe,0x93,0xce,0x56,0x6e,0x1c,0xe4,0x64,0xc0,0xdd,0x0c,0xea,0x18,0x54,0xd4,0xe1,0x5a,0x22,0xa3,0x44,0xe6,0xa2,0x44,0x1a,0x53,0x57,0x8a,0x9b,0x64,0x46,0x93,0x15,0x35,0x31,0xd0,0xbf,0xee,0xc9,0x54,0xd1,0x7f,0x89,0x3c,0x9c,0x96,0xad,0x25,0x15,0x43,0xda,0x6e,0x40,0x28,0xdb,0xdb,0xa0,0xb1,0x69,0x02,0x37,0x01,0x24,0xf6,0x11,0x85,0x99,0xa3,0x50,0xdd,0x2d,0xa8,0x7f,0x2f,0xd7,0xf7,0xab,0x90,0x0a,0x69,0x99,0x91,0xe2,0x87,0x3f,0x6c,0xf8,0x11,0xf7,0x5f,0x3f,0x7a,0x2b,0x49,0x22,0xf0,0x58,0x0f,0x93,0xa6,0x95,0x10,0xef,0xeb,0x55,0x69,0x4a,0x24,0x75,0x1c,0xf9,0xf9,0xb7,0x77,0x7b,0xe7,0x05,0xd7,0x24,0x2f,0x74,0x45,0xe5,0x3b,0x5d, +0xdd,0xf1,0xf7,0x4d,0xeb,0x9b,0x58,0xdd,0x8c,0x32,0x31,0x28,0x9a,0x87,0x27,0xd0,0x08,0xdb,0x7b,0x2b,0x1f,0xae,0x23,0x5d,0xf6,0x93,0x61,0x65,0xba,0x14,0x66,0x98,0x28,0xe5,0xc0,0x04,0x1a,0xa9,0x85,0x3b,0x82,0x90,0x38,0x62,0x93,0xd7,0xd8,0xd7,0xc6,0xaf,0xb2,0x6d,0x53,0x0b,0x56,0x17,0x17,0x04,0x58,0x83,0xd4,0x4a,0xf9,0xb4,0x40,0x9d,0xc0,0x8f,0xa5,0xcb,0x5a,0xd2,0xec,0x90,0x8f,0xd5,0xc8,0xce,0xba,0x13,0x86,0x64,0x31,0xf2,0xf8,0x78,0xcb,0x91,0x77,0x2a,0x32,0xef,0xe8,0xea,0x23,0xe7,0xb5,0x3b,0xac,0x84,0xed,0x18,0x05,0x29,0x26,0x90,0x87,0x2b,0xdb,0x8b,0x6a,0x31,0xb1,0x10,0x57,0xce,0xb9,0x1f,0x11,0x2c,0xac,0xef,0xc3,0xbf,0xf4,0x89,0x9d,0x8d,0x77,0xfb,0xf0,0x19,0x5d,0x58,0xe5,0xc1,0xc7,0xfd,0xbe,0x3e,0x70,0x09,0xfa,0x63,0x27,0x19,0xb5,0x9b,0x25,0x31,0x61,0x32,0x62,0xe8,0x92,0x1b,0x60,0xbc,0x12,0x64,0x94,0x85,0x99,0x09,0x63,0x73,0x07,0xc1,0x3b,0x19,0xe8,0x9d,0xf3,0xb3,0x22,0xac,0x47,0x24,0x88,0x41,0xd6,0xe8,0x7f,0x2c,0xf6,0x41,0x56,0x27,0xa0,0xc3,0xbb,0xd5,0xf5,0xa4,0xa7,0x22,0xca,0xd3,0xba,0x37,0x5e,0x49,0xec,0x0c,0x4f,0x12,0x9c,0x3e,0x1a,0xb0,0x5e,0x94,0x86,0x74,0xfe,0x0e,0x74,0x70,0x83,0x32,0x3e,0x9a,0x89,0x1d,0xff,0xed,0x2a,0xf2,0x24,0xca,0x21,0x07,0xa0,0x83,0x4c,0x6c,0xb3,0x97,0x78,0xd5,0x39,0x02,0xfc,0xee,0xf4,0xa5,0x7a,0xa4,0x8f,0xfa,0xdb,0xc8,0xdb,0x7b,0x26,0xea,0x89,0x0e,0x79,0x4f,0xd5,0xf1,0xc5,0x8f,0xe6,0x7d,0x64,0x5b,0x32,0x01,0xe2,0x17,0x89,0x5a,0x26,0x91,0x0e,0x9b,0x8c,0x34,0x92,0x5f,0xb2,0x37,0xe7,0xa0,0x42,0x33,0xe1,0xf9,0xa3,0x86,0x05,0xf2,0xe9,0x49,0xcd,0xbd,0x2a,0xc3,0x39,0xfc,0x0b,0x49,0xd3,0xca,0x8f,0x4e,0xe5,0x2d,0x6c,0xf4,0xab,0x2a,0x1d,0x2a,0xc5,0xc9,0x38,0xc5,0x3f,0x92,0xe4,0x4b,0xff,0x2e,0x3f,0xcf,0xf0,0x13,0x34,0x82,0x75,0x47,0x95,0xb4,0x23,0x36,0xfe,0xc4,0x90,0xb9,0xd1,0x8d,0xb1,0x56,0xf0,0x75,0xdc,0xf5,0x8a,0x3b,0x32,0xed,0x27,0xd0,0x74,0x3c,0x87,0x62,0xc5,0x54,0x73,0xeb,0x62,0xed,0xbd,0x13,0x9e,0x1a,0x48,0xda,0xd8,0x7b,0x0d,0x17,0x49,0x68,0x83,0xbf,0x3b,0x66,0x12,0x82,0xd2,0xe9,0x82,0x9f,0x55,0xe6,0x84,0xd3,0x04,0xe7,0xa5,0xd9,0x5a,0xee,0x9a,0xef,0x65,0x51,0x6e,0xcd,0xd6,0x92,0x20,0x18,0x66,0xa1,0x1f,0x57,0x33,0x1b,0xd0,0x40,0xba,0x50,0xd2,0xa6,0x52,0xc9,0xd4,0x56,0xa2,0x43,0x7c,0x70,0x15,0x26,0xb1,0x23,0x57,0x00,0x39,0x0a,0x87,0x55,0x8f,0x4d,0xf3,0xea,0xff,0xe8,0x48,0xf8,0x43,0x93,0x8a,0x97,0x7d,0xad,0xcc,0x0c,0x84,0x12,0xd8,0xbb,0xa6,0xff,0xd4,0xf6,0x43,0x1d,0x17,0x36,0xa9,0x03,0x02,0xb1,0x92,0x4e,0x91,0xc3,0x99,0x98,0x00,0x81,0xfa,0x0c,0xd4,0x70,0x3b,0x70,0x35,0x75,0x8d,0x54,0x0f,0x3a,0x05,0xa5,0xd9,0x11,0xa0,0x8c,0x24,0x50,0x5f,0x1b,0xfc,0xd5,0x15,0x76,0x09,0xc8,0x64,0xcb,0xc6,0x82,0xee,0x6b,0x24,0xc8,0xd5,0xe6,0xe4,0x51,0x2e,0x61,0x69,0x9b,0x71,0x4a,0x52,0x86,0xd0,0x45,0x52,0x57,0xfa,0xaa,0x2a,0xfa,0x63,0x2a,0xbb,0xb6,0x9a,0xa9,0xd6,0x5f,0x3a,0x95,0x09,0x53,0x6e,0x43,0x84,0x35,0xa1,0x47,0xb2,0xdc,0xe9,0x4e,0x5f,0x5e,0xdf,0x77,0x2f,0xa7,0x41,0x2b,0x05,0xb1,0x4c,0xb4,0xc1,0xa0,0x5d,0xc0,0x01,0x3f,0xaa,0x32,0xb9,0x95,0xc4,0x71,0x35,0xf0,0xb5,0x46,0xeb,0x20,0x40,0x51,0xdf,0x74,0xac,0xf8,0x2f,0x2b,0x94,0xb4,0x35,0xa7,0xae,0x65,0x38,0xe5,0x04,0x08,0xfa,0xfd,0x04,0x2a,0x4a,0xed,0x60,0xb6,0xd2,0x4a,0x62,0x89,0xc4,0x23,0xd8,0xed,0x60,0x02,0x8b,0x1c,0x24,0x36,0x17,0xe7,0x6f,0x5a,0xeb,0x82,0xf1,0x01,0x6f,0xf0,0xa8,0x8a,0x92,0x36,0xcd,0xa8,0xa7,0x75,0x94,0x24,0xca,0xfb,0xc7,0x51,0x52,0x2d,0x78,0xea,0xe3,0x28,0xc9,0x71,0xcf,0x18,0x49,0xf5,0xaf,0x8a,0xf7,0x44,0x74,0x1c,0x0e,0xf1,0xb8,0x8e,0x9a,0x1a,0xfd,0xfb,0xef,0xa1,0xa6,0xb3,0x13,0x98,0x26,0x89,0xca,0x0e,0xf0,0xd1,0xc9,0x81,0x01,0x4a,0xf3,0x1b,0x48,0xfa,0x1c,0x34,0xf2,0xc7,0xd1,0x44,0xcc,0x86,0x83,0x7f,0x10,0x25,0xa0,0x30,0xe7,0xa4,0xdc,0x98,0xfd,0x1e,0x27,0x62,0x37,0xdd,0x6e,0x88,0x52,0x43,0x20,0x88,0x58,0xc4,0xf6,0xb9,0x9e,0x23,0xe6,0x64,0x73,0xc2,0xd7,0x2b,0x04,0xb9,0xb9,0x42,0x22,0x8d,0xc4,0x84,0xa9,0x31,0x41,0x6d,0x68,0xf8,0xeb,0xe6,0x43,0x0e,0xa0,0xb5,0xcd,0x27,0xc3,0x7e,0x78,0x71,0x85,0x2d,0xaf,0x67,0x07,0xc6,0xda,0x6b,0x98,0xba,0xc3,0x9d,0xf6,0x15,0x5d,0x29,0x2f,0x5d,0xc4,0xbb,0x1d,0x8c,0xb7,0xf1,0x8b,0x8f,0x3d,0x2c,0x5d,0xe5,0xe4,0x65,0x54,0xed,0x18,0x73,0x98,0x03,0xf9,0xc4,0xb0,0xe2,0x8a,0xcb,0xaa,0x55,0xd7,0x27,0x15,0x8b,0x8e,0x4a,0x26,0x3c,0x53,0x23,0xcf,0x96,0x12,0x12,0xab,0x6e,0x26,0xd2,0x52,0x77,0xc2,0x21,0xa3,0xab,0x1f,0xff,0xd1,0x56,0x6a,0xdc,0x87,0x5e,0x29,0xa4,0xc7,0xc9,0xe3,0xec,0xdb,0xd5,0xe2,0x03,0xdc,0x74,0xe2,0xf7,0x5f,0xf1,0x16,0xc1,0x34,0xe5,0x8b,0x85,0x76,0xf4,0xd1,0x77,0xdf,0x69,0xb3,0x04,0xfa,0x64,0x7d,0x4f,0xaf,0x56,0x78,0xbe,0x5e,0xe8,0xab,0xbb,0x5d,0xfe,0x75,0x8c,0x9c,0x99,0x13,0xd8,0x63,0x7c,0x26,0x2e,0x06,0xca,0xb8,0x18,0x7c,0x91,0x49,0xac,0x5d,0x97,0xc3,0xc1,0x21,0x6f,0x80,0x58,0x42,0x20,0x54,0xce,0x4b,0x66,0xd9,0x5c,0xa3,0xbf,0x4d,0xe2,0x4e,0x8b,0x9b,0x6d,0xde,0x4e,0xba,0x0e,0xc7,0x7f,0xe6,0x39,0xc6,0x19,0xb7,0x49,0x25,0xd0,0x89,0x7b,0xae,0x6b,0xfb,0x37,0x37,0x27,0x10,0x03,0x47,0x00,0x33,0xf2,0x6a,0xf2,0x3c,0x1a,0xfb,0xe7,0x78,0xd7,0x66,0xfa,0x56,0x89,0x0b,0xcf,0x72,0xfe,0x9a,0xb0,0x2f,0x38,0x8d,0x56,0x80,0xb8,0x74,0x7a,0x3b,0xd3,0x84,0x1f,0x57,0x82,0xac,0xdf,0x26,0xda,0xc3,0x59,0x47,0x18,0x6a,0x73,0x35,0x8f, +0x11,0x5a,0xa9,0x36,0x27,0xe6,0xb0,0x62,0x7c,0x55,0xe8,0x1c,0x52,0x79,0x74,0x0b,0xb9,0x0b,0x92,0xc5,0x82,0xae,0x70,0x14,0xee,0x34,0x69,0x5d,0x9a,0x61,0xb8,0x81,0xd0,0xf4,0x3d,0x4c,0x9d,0xb4,0x4f,0x84,0xc6,0xf1,0xb9,0x93,0xd8,0x9b,0xca,0x4e,0xb8,0x1c,0xfb,0x63,0x67,0xdd,0x48,0xee,0x06,0xb3,0x88,0xaa,0xc8,0x08,0xe1,0x13,0xba,0x98,0x69,0x43,0xb8,0xc6,0xec,0xaa,0x19,0xc1,0x62,0x25,0x4c,0x80,0x76,0x4b,0xfd,0x5f,0x9e,0x5f,0xa7,0x95,0xdf,0x99,0xe2,0x4e,0xc3,0xcb,0xbd,0xc2,0x47,0x02,0x7d,0x99,0xb1,0x20,0xe4,0xc1,0xff,0x97,0x8b,0x22,0xb7,0x6d,0x4b,0x83,0xec,0x60,0x10,0xb4,0x9a,0xc8,0x3e,0xf6,0x8d,0xfa,0x9f,0x2c,0x95,0x30,0xe0,0xf5,0xa5,0x72,0x74,0x0f,0x7a,0xf8,0x46,0xb0,0xd7,0x10,0x45,0x24,0x6e,0xb0,0x2e,0x3a,0x9b,0x24,0x97,0xb3,0x03,0xfc,0x5a,0x26,0x51,0x59,0xad,0x20,0xac,0x6b,0x20,0x5a,0x96,0x3a,0x2d,0x97,0xda,0xe9,0x65,0x65,0xa9,0x53,0xbb,0xd4,0x1c,0x21,0x1f,0xf9,0xe0,0x1f,0x91,0x6e,0x31,0xd8,0x55,0x57,0x97,0x68,0xca,0x07,0x21,0x17,0x75,0x53,0x7c,0x98,0xb7,0x2c,0x71,0x02,0xfb,0x6e,0x2c,0x50,0x8e,0x00,0x6f,0xd2,0x13,0xa2,0xf3,0xf3,0xca,0xb8,0x40,0xc1,0x97,0x23,0x4f,0xb4,0xe6,0xbb,0x8c,0x52,0x4d,0x53,0x65,0xe6,0xcf,0x04,0x5d,0x8b,0xca,0x01,0x20,0xb3,0x85,0x4d,0x21,0xef,0xfd,0xfa,0xab,0x3d,0xd0,0x7e,0xfd,0xd5,0xb3,0x41,0x8d,0x77,0x15,0x74,0xd7,0x78,0x64,0x17,0x1a,0x79,0x8b,0x85,0x2e,0xf5,0xbc,0xd0,0x95,0x22,0x57,0xeb,0x65,0xcc,0x18,0x48,0xd4,0x0d,0x3d,0xac,0xf6,0x1d,0x0b,0x89,0x59,0x04,0x90,0x8b,0x01,0x72,0x55,0xb8,0xcf,0x34,0xdc,0xd7,0xa1,0xdd,0xd7,0xe0,0xce,0xbb,0x41,0x40,0xde,0xc2,0x75,0xc2,0xf0,0x6c,0xb2,0x8a,0x0d,0x6c,0x1e,0x31,0x4d,0x58,0x6e,0x89,0x5e,0xbc,0xde,0x5e,0x4e,0xab,0x54,0x22,0xb1,0x01,0xa7,0xf0,0x89,0x89,0x01,0x32,0x60,0x0f,0xad,0x13,0xdb,0x3d,0x8b,0x6a,0x60,0xd7,0x26,0x0b,0xd5,0x84,0x5d,0x3e,0x68,0x04,0xab,0x61,0xdb,0x8a,0x6c,0x58,0x05,0x40,0x03,0xa2,0xd4,0x37,0x3f,0x40,0x34,0x31,0x93,0x44,0x8c,0x38,0x74,0x2f,0x2c,0xbd,0xa0,0xf4,0x76,0xa1,0xe7,0x5d,0xbc,0xa8,0xda,0x6d,0xe4,0x62,0xb7,0x21,0x8a,0xd0,0xbc,0x2d,0x86,0x91,0x16,0x06,0x62,0x35,0x69,0x05,0x3c,0x28,0x15,0x41,0x7f,0x51,0xab,0x8e,0x81,0xb6,0x4e,0x35,0x52,0x7f,0xdc,0x1e,0x3f,0x70,0xcc,0x06,0xbd,0x9a,0x8c,0x4c,0xdc,0x60,0xb7,0x25,0x14,0xe6,0xca,0x63,0xd6,0x8b,0x7d,0xac,0xb8,0x2e,0xe1,0xc4,0x72,0x83,0x59,0xcb,0xf8,0x29,0x6e,0x67,0xf2,0x96,0x9e,0xe4,0x8f,0x76,0x63,0xda,0xec,0x06,0xf4,0x05,0x0c,0xb7,0x65,0x27,0x02,0xd8,0x44,0x10,0xbb,0xa6,0xa5,0x2b,0xad,0xae,0x1a,0x06,0x3d,0x6e,0x39,0xcd,0xb4,0xce,0xdc,0x96,0x62,0xde,0x52,0xe3,0x28,0xe3,0x80,0x93,0xf5,0x87,0x64,0xbd,0xdd,0x1f,0x23,0x73,0x75,0x67,0xdc,0x98,0xc3,0x1d,0x36,0x88,0x26,0x50,0xd7,0xf1,0xa5,0x62,0x98,0xf9,0xe8,0xc4,0x56,0x27,0x6a,0xd5,0x60,0x6b,0x52,0xb1,0xee,0x18,0xf1,0x54,0x49,0xc1,0x69,0xa4,0xa5,0x53,0x17,0x36,0xfd,0x38,0x5b,0x8b,0xcf,0xa2,0x29,0x4b,0x12,0x42,0x4e,0xf0,0x31,0x1d,0x4e,0xba,0x57,0x61,0x69,0xe0,0xc9,0xee,0x1d,0x93,0xe7,0xfd,0x61,0x11,0xd2,0x2b,0x36,0x51,0x2d,0x58,0x51,0x0b,0x77,0x2b,0xb0,0xba,0x63,0xe5,0x97,0x92,0xa8,0xc3,0x21,0xe3,0x2c,0xa0,0xe7,0xe7,0x9d,0xd4,0xa6,0xe4,0x20,0x60,0xa4,0x3b,0x87,0xf2,0x34,0x2f,0x0e,0x87,0xce,0x67,0xbe,0xfb,0x06,0xde,0xa1,0x92,0x55,0x16,0x31,0x06,0x25,0x32,0xd9,0x96,0xb6,0x90,0x6c,0x07,0x35,0xb5,0xe0,0x31,0xd0,0x29,0x30,0x12,0x1b,0xa8,0x6f,0xd6,0xf0,0xb5,0x72,0x2d,0xa1,0x2a,0x13,0x83,0x7d,0x71,0x93,0x1b,0x17,0x16,0xd7,0x9c,0xb5,0xcc,0x53,0x0f,0x6b,0x56,0xe2,0x61,0xfd,0xac,0x24,0xef,0xcb,0x68,0x33,0x25,0x1c,0xea,0x5a,0x25,0x2d,0x34,0xd4,0x6c,0x40,0x43,0xac,0x9c,0xe2,0x74,0x43,0x3a,0x14,0x02,0x07,0x29,0xae,0x2c,0x46,0x44,0xc5,0xd4,0xe4,0x78,0xac,0x50,0xdd,0x5a,0x2c,0x57,0xca,0x01,0x6b,0xd4,0x70,0x65,0x1b,0x8e,0x9b,0x42,0x04,0x9e,0xb0,0x86,0x87,0x8e,0x25,0x6d,0x4d,0x04,0x4f,0x67,0x1c,0x04,0x3e,0x7a,0x6a,0x19,0x81,0x12,0x90,0x59,0xc9,0x23,0x33,0x39,0xd5,0x16,0x39,0xd6,0xc0,0x49,0x64,0xd2,0x08,0xfb,0xaa,0x01,0x7b,0xe8,0xad,0x57,0x84,0x65,0xb4,0x60,0x31,0xd0,0x18,0x79,0xa7,0x39,0x78,0x9d,0x9e,0x82,0x7f,0x0e,0x26,0x93,0x04,0xe7,0x9e,0x78,0x72,0x39,0xb0,0x7b,0x4b,0x07,0x9b,0x53,0xcd,0xcc,0x12,0x00,0x7c,0x37,0xc9,0xa3,0xb6,0x24,0xe6,0x14,0x64,0xd1,0x28,0x27,0x78,0x1c,0xab,0x4d,0xb4,0x10,0x1c,0x4b,0x7b,0x58,0x22,0xc5,0x0e,0x25,0x8f,0x52,0x48,0xb4,0x9d,0xf3,0xce,0x86,0x43,0xe6,0x02,0x65,0x7a,0xa0,0x32,0x7f,0x18,0x6d,0x91,0x01,0xa7,0x1e,0x2b,0xa2,0x1c,0x56,0xb0,0x99,0x02,0x73,0x9e,0xd7,0x5c,0x77,0xdd,0xfb,0xce,0x4e,0x73,0xa3,0x9b,0x6e,0x23,0xbd,0x1f,0x00,0x65,0x53,0x92,0x66,0x3d,0x26,0xca,0x60,0x85,0x18,0x6d,0x9c,0x36,0x15,0xec,0x18,0xc5,0x2e,0x21,0x50,0xb7,0x3a,0x83,0x19,0x2c,0x74,0x9c,0x4f,0x43,0x21,0xb5,0x31,0xd3,0xdd,0x0d,0x61,0xf2,0xc4,0x4d,0x45,0x92,0x54,0xd2,0x0f,0x6c,0x54,0x33,0x07,0x26,0x31,0x86,0x1c,0xfa,0x69,0xf7,0x56,0xba,0x16,0x4d,0x86,0xcf,0xc2,0x8f,0x94,0x33,0x07,0xd4,0x05,0x9b,0x0e,0x4f,0xb9,0xb9,0x93,0x22,0xa7,0xd0,0xb0,0x35,0x09,0xda,0xed,0xef,0x26,0x41,0x13,0x71,0x90,0xcd,0x7f,0x24,0x28,0x9b,0xee,0x25,0x9e,0x3b,0xc7,0x92,0xd5,0xc9,0x22,0xd8,0xa3,0xd4,0xe0,0x60,0x38,0x1c,0xbb,0x5b,0x39,0x55,0x2c,0xd8,0x58,0x35,0x92,0x66,0x6d,0x24,0x04,0xd6,0x84,0xd0,0xcd,0xca,0xcc,0xfe,0xe1,0x60,0x2f,0xb5,0xea,0x2b,0x57,0xa9,0xce,0x6b,0x25,0x12,0xc8, +0x09,0x2d,0xde,0xaa,0x67,0xd2,0xb4,0x68,0x9b,0x8b,0x9f,0x68,0xca,0xd7,0xf7,0x6c,0x48,0xcd,0x86,0x51,0xd1,0xaa,0x92,0x28,0xee,0x70,0xd8,0x28,0xb3,0xe0,0xf3,0xee,0x06,0xc7,0x1c,0x42,0x87,0x94,0x08,0x8e,0x08,0xeb,0x41,0xed,0xc9,0x5a,0x30,0x19,0xc7,0xdb,0x99,0x0d,0x0a,0x58,0x5f,0xe7,0xd5,0xd8,0x27,0x88,0xef,0x7a,0x7e,0xae,0xcb,0x15,0x6e,0x9c,0xa4,0xc3,0xa1,0xd0,0x55,0x49,0xc7,0x10,0x65,0xe2,0x58,0xb2,0x01,0xd4,0xf8,0x5a,0xf8,0x80,0xc7,0x72,0x6e,0xc9,0x5e,0x88,0xa6,0x2f,0xae,0x86,0xf3,0x70,0x65,0x33,0xdd,0x61,0x2c,0x4b,0x93,0xa9,0x6c,0x76,0x22,0x4d,0x99,0x49,0xea,0x37,0x33,0xb9,0xd3,0x08,0x3c,0x96,0xc8,0x17,0x27,0x73,0x3a,0x83,0x8c,0x72,0x09,0x2b,0xe6,0xd9,0xe8,0x66,0xec,0xbc,0x39,0x3f,0xff,0x81,0x46,0xec,0x66,0xbd,0xaa,0x7c,0x62,0x9f,0x8a,0xba,0xb9,0x99,0x07,0xab,0x14,0x09,0x49,0xe7,0x37,0x58,0xdd,0xa4,0x25,0x31,0x05,0xb4,0x6c,0x2b,0x6b,0x8b,0x72,0x7e,0x5e,0x5e,0xeb,0xf6,0x68,0x5a,0x59,0xb5,0x96,0x9a,0xe4,0x73,0x9d,0x1f,0x7c,0x48,0x91,0x6e,0x44,0xac,0x66,0xa9,0xbb,0x9c,0xc0,0x28,0xa8,0x43,0x01,0x27,0x31,0xcc,0x31,0xb4,0x02,0x14,0x17,0x5d,0x30,0x74,0x96,0x79,0x14,0xed,0x5e,0xa7,0x1e,0xa2,0x06,0xbf,0xed,0x95,0x06,0x78,0x5b,0x45,0x11,0x54,0xd2,0x73,0xed,0x74,0x0e,0x89,0x13,0xf6,0x14,0x16,0x49,0x3a,0xbb,0x9c,0x90,0xa2,0x48,0xd7,0x6b,0x29,0x3f,0xfa,0x47,0xe4,0xc0,0xab,0xb4,0xef,0x67,0x12,0xbb,0x2d,0x09,0x1a,0x91,0x36,0x1b,0xc8,0xb6,0x9e,0x6f,0xe6,0x64,0x1c,0x76,0x53,0x37,0x3a,0x2a,0x7e,0x81,0x88,0xb4,0x29,0x0f,0xbf,0xd4,0x59,0x3b,0x4f,0x99,0xa6,0x81,0xea,0x76,0x5c,0xb9,0xcf,0xda,0xea,0x4c,0x39,0xc2,0x67,0x79,0x76,0x7a,0x38,0x31,0xce,0xf8,0xf8,0x38,0xd3,0x27,0xca,0x99,0x39,0x4c,0xce,0x68,0x22,0x8b,0xdf,0xf2,0x33,0xf1,0xb9,0x3a,0xe3,0x2c,0x42,0x67,0x59,0xb2,0x90,0x0b,0xce,0x8e,0x80,0x6c,0x84,0x72,0x75,0xb7,0x91,0x5f,0x30,0x61,0x67,0x36,0xa1,0xc2,0x99,0xc9,0xa1,0x70,0x56,0xe6,0x5b,0x38,0x2b,0x73,0x2c,0x9c,0x49,0xe6,0x8d,0x33,0x39,0xe1,0xcf,0x76,0x77,0xc9,0xb2,0xd8,0x9f,0xdd,0xe4,0x1f,0xb8,0x5e,0xfa,0xdd,0xc0,0x76,0x10,0x17,0x54,0xbd,0x93,0x3e,0xd9,0x73,0x1c,0x81,0xdb,0x14,0xd6,0x55,0x01,0x7c,0xa9,0x21,0x68,0x48,0xac,0xfb,0xc2,0xef,0xf2,0x71,0xa8,0x03,0xf1,0xa5,0x26,0xab,0x8d,0x9e,0xb5,0x96,0xe5,0x9d,0x61,0x68,0x8f,0x2c,0x6e,0x39,0x56,0x90,0x09,0xe5,0x70,0x91,0xa7,0x53,0x6c,0xe1,0xd6,0x3d,0x3d,0xd9,0x11,0x9d,0x3c,0x26,0xd1,0x14,0xec,0xb6,0xcb,0x37,0xe2,0xec,0x0a,0x13,0x01,0xc9,0x71,0x65,0xf3,0x51,0x49,0x3a,0x2a,0x37,0x79,0x54,0x1b,0x40,0x54,0x55,0xdb,0x1a,0xad,0x6b,0x78,0x86,0xa6,0xde,0x24,0x02,0xaa,0xe6,0x62,0x84,0xe1,0x50,0x3d,0x71,0xa2,0xd0,0x4b,0x77,0x8e,0xd8,0xdd,0x6c,0x20,0x99,0xbb,0x2a,0xc2,0x15,0x76,0xa5,0x74,0x1a,0xc8,0xa0,0x40,0xc6,0x81,0xdf,0xcc,0xd2,0x1a,0x0b,0x34,0x2a,0xb7,0xac,0x82,0xb1,0x55,0x3f,0xe8,0x5e,0x71,0x80,0x59,0x49,0x77,0xf9,0xdf,0x6f,0xf8,0xe2,0x6a,0x90,0x0f,0x2b,0xd5,0xe7,0x41,0xe8,0x67,0xad,0x99,0xa2,0xca,0xde,0x68,0x8f,0x04,0x54,0xc0,0x9c,0x0a,0x93,0x5d,0x7b,0x88,0x9c,0x16,0xeb,0x54,0x6c,0xa4,0xef,0xac,0x91,0x82,0x7a,0x07,0x0e,0x79,0x08,0x92,0x8b,0x45,0xec,0x3f,0x7f,0xfd,0x55,0x33,0x62,0x1f,0xcb,0xbe,0xe0,0x97,0x55,0x37,0xc7,0x32,0xde,0xfe,0x4c,0x09,0x72,0x24,0xca,0x34,0x62,0x74,0x14,0xf7,0x5e,0x7e,0xfb,0xf5,0x77,0xa8,0x70,0x1b,0x48,0xc5,0xaf,0xb6,0xeb,0xe5,0x1b,0xfe,0x9c,0xa9,0x31,0x82,0xc5,0xcb,0xf7,0xcb,0x85,0x17,0xe8,0x30,0x93,0x59,0xf0,0x60,0xf2,0x5c,0x1b,0xc2,0x3e,0x65,0x5e,0x82,0x16,0x59,0xab,0xb6,0x77,0x9f,0x7d,0x78,0x1b,0x4f,0xc1,0xf9,0xf9,0x1e,0x57,0xb9,0xcd,0xb7,0xdb,0xf5,0xd6,0xf1,0x8b,0xa2,0xb5,0xc7,0x13,0xdf,0x7b,0xbd,0x22,0xa2,0x93,0xea,0xa2,0xd1,0x84,0x67,0x5e,0x97,0xb3,0x7d,0xf0,0x34,0xdc,0x63,0xb4,0xa3,0xeb,0xf1,0x93,0x4b,0xf5,0x9e,0x45,0x03,0xc3,0xeb,0xd5,0xe5,0x54,0x7d,0xd0,0x44,0xa9,0xec,0x60,0xad,0x70,0x3a,0x14,0xcb,0x78,0x9a,0x1f,0x68,0x0b,0x13,0x2d,0x32,0xa1,0x63,0x95,0x35,0x50,0xbf,0x3d,0xaa,0xaa,0xa2,0xad,0x3e,0xcd,0x57,0xc1,0x65,0x51,0x8a,0xa7,0x3f,0x4d,0x6a,0x16,0x7e,0xad,0xee,0xf4,0x7a,0xb3,0x24,0x4e,0xf0,0x4b,0x98,0xf0,0x10,0x07,0x71,0x6f,0xb4,0x1a,0xc1,0x30,0xf3,0x91,0xa9,0x3a,0x44,0x8d,0x5d,0x6f,0xe4,0x75,0x9b,0x49,0x39,0xac,0x56,0x06,0xc9,0x1d,0x3c,0x10,0x5b,0x63,0x8f,0xc8,0x64,0x1d,0x87,0xde,0xa4,0x0c,0x87,0x2d,0x85,0xfe,0xb2,0x03,0xa7,0x22,0x7c,0x8c,0x4e,0x64,0x62,0x11,0x5c,0xb5,0x5e,0x4b,0x02,0xdb,0x5e,0xce,0xd5,0x25,0xa3,0x7c,0x2c,0x35,0x32,0xcc,0xc4,0xcb,0xa8,0x95,0xf7,0x92,0xb0,0x95,0xed,0x76,0x4b,0xd5,0xd4,0x81,0x89,0x1f,0x84,0xc9,0x20,0x1b,0x19,0xab,0xaf,0x71,0x94,0xaf,0x52,0xa2,0x90,0x7e,0xf8,0xfe,0x35,0xc2,0x22,0x12,0xd3,0xca,0x99,0xf4,0xba,0x5e,0x44,0x5d,0x68,0xbe,0xa9,0xb2,0xe6,0xcd,0xd9,0xe5,0x18,0x5c,0xbd,0xf9,0xed,0x5d,0xbe,0xfd,0xa0,0xcf,0xf1,0xef,0x16,0x71,0xb1,0xb2,0xf6,0x95,0x66,0xf2,0x2b,0x31,0x3d,0xc4,0xc2,0x8c,0x69,0x5c,0x55,0x4a,0x2c,0xec,0x2c,0x3a,0xce,0x35,0x34,0x3d,0xa9,0x62,0x27,0x9a,0xc4,0x09,0xf2,0x94,0x69,0xea,0xf7,0xdc,0x0b,0x6a,0xa6,0xa6,0x04,0xb8,0x84,0x98,0xe8,0x64,0x6a,0x89,0xf9,0xa6,0x67,0xd4,0xd7,0x02,0x39,0x5d,0x50,0xc6,0x11,0xb0,0x9f,0xb4,0xfb,0xa8,0xa5,0x02,0x41,0xe0,0xd5,0x18,0xb9,0x62,0xcc,0xa2,0x6d,0x15,0x44,0x7a,0x97,0xeb,0x2d,0x55,0x8a,0x1f,0x60,0xac,0x5c,0x92,0xd4,0x5a,0xda,0x7a,0x0c,0x8c,0xcd,0x6b,0xa3,0x3a,0x2b, +0x28,0xaa,0x38,0xf2,0x63,0xba,0x30,0xc7,0x5a,0x98,0x55,0xec,0x88,0x45,0x31,0x4c,0x3f,0xdc,0xd5,0x7e,0xd3,0xc0,0x5c,0x91,0x27,0x81,0xba,0xfa,0x90,0x94,0x31,0x71,0xe5,0x6d,0x6a,0xc2,0xbf,0x77,0x6c,0x4e,0x0a,0x48,0x8b,0xaa,0x83,0x73,0xa1,0xca,0x15,0xa0,0x55,0x13,0x39,0x89,0xc1,0x44,0x4d,0x56,0x96,0x06,0x43,0x91,0x93,0xa5,0x2d,0x72,0xb2,0x07,0x0c,0x24,0x14,0x16,0x47,0x27,0x81,0x2c,0x63,0xe1,0xbf,0x27,0x04,0x86,0x4c,0x30,0x48,0x31,0x18,0x84,0x2d,0x45,0xd3,0xf6,0xa2,0x12,0x6e,0xd5,0x60,0xe4,0xcf,0x08,0x91,0xfc,0xdb,0xb3,0x3e,0x61,0x9f,0xcf,0xe9,0xea,0x4f,0xbd,0xa7,0x84,0x93,0x5e,0xd2,0x95,0x3f,0x1a,0x9e,0x8f,0x83,0x5f,0xa3,0xd1,0x2f,0xe7,0xe3,0xa7,0x97,0xea,0x0b,0xc6,0x37,0xbd,0xa7,0x43,0xe2,0x41,0xcf,0xae,0xf7,0xe3,0xa7,0xfe,0xe8,0x17,0xd4,0x38,0x7e,0x4a,0xe8,0x68,0xba,0x54,0xaf,0x8c,0x46,0x3c,0xa1,0xc3,0xf4,0x40,0xa4,0x2d,0xfe,0xbf,0xd8,0xed,0xd7,0x5b,0x20,0xaf,0x5e,0xf7,0x82,0xc1,0x6e,0x07,0x47,0x23,0xe0,0x30,0xa0,0xb3,0xc3,0x7d,0x91,0x71,0xfa,0x3b,0x6a,0xf1,0x6f,0xfa,0xf3,0xbf,0x7d,0xf1,0xf6,0xf0,0xe5,0x17,0x9f,0xbe,0x84,0x5b,0xf6,0x97,0x78,0x76,0x7d,0x79,0x7d,0x79,0xa9,0x5e,0x27,0x70,0xbb,0xfd,0x3b,0xff,0xfd,0x8f,0x24,0xf2,0x9e,0x5e,0x7a,0xc6,0x45,0xd8,0x7b,0x4a,0xc4,0xcb,0x57,0x2d,0x96,0x50,0x31,0x01,0xd5,0x57,0x09,0xab,0x5d,0xa3,0xbd,0xfc,0x96,0xd8,0xf0,0x6b,0x57,0xeb,0x56,0xb5,0x02,0x6b,0x9e,0x2e,0xd6,0xd8,0x17,0x6d,0x0d,0x8c,0xdf,0x32,0xdc,0xa5,0x6a,0x56,0x08,0x55,0x49,0x7b,0x5d,0x11,0x94,0x06,0x46,0x1d,0x12,0x4d,0x46,0x39,0xb8,0x22,0xaf,0x0b,0x31,0x58,0x46,0x14,0xe7,0xd0,0x87,0x2e,0xc4,0xc4,0x49,0x23,0xbc,0xf8,0xd4,0x53,0xe2,0xdd,0x16,0xb3,0x03,0x2b,0x7c,0xa2,0x8d,0x77,0x40,0x8a,0xf8,0x73,0xb5,0x77,0xcc,0x9a,0xa5,0xae,0x33,0xde,0x37,0x75,0x7c,0x2f,0xf1,0x60,0x20,0x45,0xff,0x7b,0x52,0x4e,0xc4,0x94,0xd8,0x20,0x7e,0x5f,0xd8,0xf0,0x2e,0xa3,0xd9,0x58,0x2c,0x5c,0x05,0x1b,0xd1,0x2d,0xfb,0x3b,0x38,0xc0,0xae,0x3f,0x99,0x47,0x33,0x6d,0x09,0x7e,0xca,0x4e,0x7a,0x7e,0x38,0x10,0x5f,0x94,0x8f,0xe6,0xe3,0xe1,0x64,0xd8,0x21,0x5e,0x65,0x1e,0x68,0x43,0xa2,0xd0,0x97,0x94,0xc6,0xe0,0xf4,0x4a,0xc7,0x87,0x79,0xa0,0xa6,0xf8,0x03,0xc7,0x2f,0xe2,0x47,0xcd,0x11,0x3c,0x75,0x0b,0xc3,0x01,0x90,0x76,0x63,0xce,0xde,0x1d,0xe7,0xe7,0x53,0x86,0x80,0x72,0xdc,0xdf,0xd6,0x9d,0x10,0x61,0xf5,0x34,0x8f,0xdf,0xbf,0xc9,0xf7,0x7b,0xea,0xdb,0xae,0x37,0x21,0xda,0x4d,0x7b,0x73,0xd9,0x34,0xd6,0xa9,0x9c,0x30,0xa5,0xcc,0x98,0xd0,0x28,0xad,0x3a,0xb1,0x43,0x29,0x52,0xa9,0x42,0xe3,0x9e,0xd1,0xfc,0x05,0x01,0x7c,0x41,0xf0,0xb2,0xc4,0xaf,0x4e,0xba,0x46,0x9a,0x34,0x4e,0x9f,0xe2,0x04,0x6c,0xfc,0xae,0xdd,0xdf,0x1c,0x29,0x29,0x44,0xf3,0xbc,0x23,0xae,0x3b,0x2e,0x07,0xa7,0x19,0x67,0x1a,0x12,0x02,0xc8,0x62,0xa8,0x85,0x95,0xbd,0x58,0x77,0xe7,0x8c,0x23,0x89,0xc6,0xbd,0x65,0xb1,0xd4,0x5c,0x3f,0x4b,0x96,0xbf,0xcf,0x77,0x74,0x14,0xed,0xf2,0x2f,0xf3,0x38,0x23,0x54,0xe9,0xe9,0xb0,0xe7,0x17,0x6f,0x25,0xfd,0x11,0xe0,0x31,0x0b,0xec,0x79,0x3a,0xe3,0x1c,0xf6,0x6c,0x10,0x8e,0xbf,0x82,0xda,0x90,0xc7,0xa5,0xb0,0xab,0x41,0xa7,0x48,0x42,0x7b,0xea,0x86,0x3d,0xa6,0xa9,0x2f,0xf4,0x55,0x1a,0x4c,0xb8,0x5b,0x7c,0xfa,0x94,0x89,0xb7,0x52,0x11,0x59,0x14,0x92,0xa7,0x09,0x63,0x23,0xd2,0x9e,0x9d,0xb0,0x72,0xe8,0x49,0xba,0x78,0x31,0x86,0xe7,0x65,0xae,0x6b,0x9c,0xb2,0x27,0x3c,0xf2,0xf0,0xc0,0xff,0x7b,0x8a,0x16,0xac,0x51,0xc9,0xa4,0x23,0x43,0x3f,0x3f,0x2f,0xbb,0x32,0x21,0xd2,0x69,0x34,0x19,0x97,0x13,0xfb,0x8f,0x3a,0x78,0x97,0x8e,0xfc,0x00,0xf4,0x1b,0x77,0x52,0xf5,0xbe,0xe2,0x19,0xb8,0x19,0x5d,0x8d,0x25,0x33,0x07,0x9f,0x9a,0x4e,0x57,0x83,0xf9,0x68,0x5a,0xd7,0xf1,0x57,0x86,0x32,0x1d,0x0f,0x26,0xc8,0x64,0x2f,0xcb,0x61,0x34,0x9d,0x1c,0xba,0x13,0xcc,0x32,0xcf,0xfd,0xab,0x22,0x5f,0x64,0x70,0x3d,0xe3,0x74,0x12,0x2d,0xcf,0xc7,0xc8,0xf9,0x86,0x0c,0x1b,0x9c,0x91,0x11,0x5d,0x7c,0xc5,0x27,0x1b,0x44,0x11,0x91,0xfb,0x80,0xb9,0x0b,0x33,0x04,0xf6,0xd6,0x9d,0x28,0xa7,0x79,0x34,0x2b,0x50,0x32,0xc1,0x92,0x58,0x9a,0x8a,0x9e,0x61,0xfa,0x68,0xee,0xd8,0x4e,0x07,0xcb,0x32,0x8d,0xe6,0xa3,0x82,0x97,0x61,0x02,0xff,0x41,0xda,0x37,0x7c,0xa9,0x3a,0xd3,0x12,0x1a,0xe6,0x0c,0x0d,0x51,0xee,0x72,0x84,0x33,0x9a,0xaa,0xff,0xa7,0xb4,0x2b,0x5b,0x6e,0xdb,0xc8,0xa2,0xef,0xf3,0x15,0x12,0xca,0xa5,0x02,0xc2,0x16,0x45,0x3b,0x7e,0x98,0x40,0x41,0x50,0x8e,0x2b,0x99,0x2c,0x8e,0xed,0x58,0x76,0x92,0x29,0x59,0x35,0x05,0x80,0xab,0x44,0x12,0x32,0x49,0x2d,0x8c,0xc4,0x7f,0x9f,0x7b,0xee,0xed,0x15,0x00,0x9d,0x4c,0xcd,0x0b,0x89,0x6e,0x34,0xba,0x81,0x5e,0xef,0x7e,0x74,0xb8,0x3b,0x57,0xc5,0x14,0x23,0x69,0x6b,0xe1,0x54,0x02,0xcd,0x2f,0x9c,0x69,0xf2,0x09,0x83,0x4b,0xa5,0xf8,0x61,0xef,0x1a,0x0e,0x0e,0x89,0x32,0xea,0xca,0x8e,0x24,0x6a,0x4d,0xbc,0x89,0x35,0x11,0xc0,0x6c,0x5c,0x51,0x8f,0x9c,0x47,0x9b,0xe9,0xaa,0xbe,0x5b,0x47,0x17,0x49,0x99,0x4d,0x62,0x43,0x05,0x82,0xa4,0x97,0xb4,0xa6,0xd3,0xe7,0xf6,0x84,0x5c,0x6f,0x20,0x9a,0x08,0x28,0x71,0xc5,0x7f,0xe9,0x24,0x9f,0xa7,0xd1,0xeb,0xfa,0x40,0x86,0x10,0x47,0xcf,0xc1,0x98,0xa8,0x7f,0x4c,0x47,0xfa,0x94,0x4d,0x8d,0x5e,0xd8,0x19,0xfd,0xb1,0xa9,0x67,0x7d,0xc3,0x5c,0x4e,0xa4,0xd0,0xf5,0x69,0xb9,0xf3,0xe2,0x4d,0x16,0x15,0x22,0x72,0xa7,0x03,0x35,0x2f,0xd6,0x9b,0x5f,0xea,0xe1,0x6c,0x3c,0x1b,0x0d,0xd9,0xab,0x74,0x53,0xb0,0x77, +0xa9,0xbf,0xcd,0xa4,0x0f,0x37,0xab,0x79,0xaa,0xcf,0x1d,0xc5,0xb2,0x90,0x88,0x0e,0xb7,0x48,0xcd,0xd6,0xaf,0x88,0xfd,0x99,0xa7,0xdf,0x1b,0x12,0xa4,0x94,0x08,0x26,0x55,0x3d,0xa7,0xcd,0x8f,0xb1,0x85,0x18,0x70,0x72,0x55,0xe3,0x3d,0x18,0x6f,0x05,0xfb,0xca,0x7a,0xbb,0xac,0x34,0x4a,0x34,0x16,0xb5,0x20,0x1f,0x43,0x82,0x34,0x13,0x5e,0xea,0xe4,0xfe,0xf8,0xee,0xee,0xee,0x98,0x06,0x74,0x71,0x4c,0x2d,0x0b,0x7d,0x3a,0x3c,0x85,0x68,0x60,0x85,0x78,0x5c,0x1f,0xde,0x7f,0x7f,0xfc,0xcf,0x48,0x81,0x87,0x83,0x9b,0x2c,0x7b,0xe7,0xfd,0x5c,0x0a,0xfa,0x88,0xb0,0x41,0xd7,0x20,0x46,0x23,0x41,0x2b,0x90,0x1c,0x5c,0x46,0xea,0x1e,0xe9,0xa0,0xa5,0xc5,0x5c,0x1d,0x58,0xce,0x49,0x5d,0xae,0x39,0x5a,0xaf,0x57,0x00,0x39,0xba,0xc4,0x65,0x71,0x5b,0x68,0x14,0x99,0x9d,0x79,0x77,0x6a,0x1d,0x75,0x9e,0x7c,0x2c,0xe9,0xef,0x63,0x79,0x22,0x4d,0x52,0x12,0xff,0x27,0x52,0x1f,0xa5,0xf0,0x4f,0x77,0x61,0x7e,0xe1,0xaf,0x21,0x79,0x38,0x32,0x99,0xc4,0x5a,0x45,0xfa,0x2b,0x4c,0x16,0xa2,0x6a,0x9b,0xd7,0x32,0x79,0x3f,0x9d,0xbd,0x79,0x2d,0x6f,0xa0,0x57,0x33,0x7a,0x80,0x5f,0x31,0x4a,0x85,0x1d,0x14,0x66,0xf0,0x80,0xbf,0x99,0x71,0x46,0x39,0x89,0x5a,0xa2,0x14,0x4f,0x0b,0xfb,0xa8,0xb3,0xf1,0xe1,0xa9,0x63,0x55,0xe9,0x60,0x75,0x87,0x8a,0x8c,0xbc,0x19,0xaa,0x7b,0x04,0xf6,0xdc,0xd9,0xb9,0x71,0xb3,0xc7,0x2c,0xb0,0xcc,0xe9,0xdc,0xe2,0xa3,0x2b,0x3c,0xad,0xa0,0x18,0x49,0xdf,0x40,0x11,0xe7,0xe7,0x32,0x70,0x0c,0x32,0x1c,0x6a,0x12,0x11,0x34,0x3f,0xd2,0xce,0x82,0xcc,0xf7,0xab,0x62,0x49,0x5f,0xbd,0xda,0x20,0xf3,0x27,0x9d,0xd9,0xf0,0x3c,0xee,0x92,0xc1,0xb7,0x70,0x0f,0x2b,0x3e,0x1f,0x1b,0xbb,0x2b,0xc2,0xa4,0x68,0xf5,0x86,0x7d,0xa9,0x9b,0x6b,0xb8,0x79,0x55,0x50,0x15,0xd4,0x7d,0xfd,0xd9,0x8f,0x8f,0xb5,0xfa,0xe4,0x92,0xac,0x6a,0x70,0xc1,0x91,0xae,0x35,0x23,0x04,0x9c,0xbf,0x6b,0x98,0x4f,0x88,0x7e,0x65,0x1d,0x38,0xc6,0xab,0x0d,0x25,0x5f,0x16,0xf3,0x39,0xa2,0x63,0xc2,0xcf,0x6d,0x59,0x8d,0x0e,0x16,0xa3,0x45,0xbd,0x42,0x10,0x8b,0x1b,0xaa,0x1c,0x8b,0xf6,0x66,0x0d,0x98,0x53,0x96,0xb0,0xdf,0x62,0xeb,0xbf,0xc3,0xcf,0x7d,0x16,0x09,0x38,0x2d,0xd1,0xfb,0x6a,0x9b,0x3d,0xc0,0x8a,0x6c,0x7b,0xc6,0x4b,0x9c,0x71,0xe3,0xc3,0xb3,0xb2,0x23,0x28,0x15,0x8e,0x0a,0x39,0xd4,0x88,0xd8,0x99,0x22,0x96,0x90,0x31,0x9c,0xf8,0xae,0x14,0xaf,0xd4,0x49,0x92,0x4c,0xcf,0xcb,0x36,0x3e,0x06,0xd1,0x07,0xcf,0x2e,0x76,0x25,0xed,0x7b,0x45,0xe3,0xce,0x2e,0xe0,0x06,0x4a,0xe1,0x06,0xca,0x1d,0xde,0xe7,0xc5,0x7c,0x1e,0xbe,0xd2,0xba,0x83,0xa9,0xba,0xd2,0x96,0xab,0xac,0x69,0x7c,0x27,0x71,0x86,0x5a,0x1f,0xe0,0x4d,0x2a,0x69,0xe7,0x8a,0xdd,0xa5,0xee,0x5a,0x6f,0xd3,0x91,0x45,0x67,0xb7,0xba,0x85,0x9f,0x8a,0x36,0xcf,0xd8,0x29,0x88,0xe8,0x56,0xb3,0xe1,0xe8,0x17,0x4d,0x6a,0x74,0x59,0xa3,0xba,0x66,0x6a,0x4b,0x92,0x64,0x85,0xa9,0xc1,0x0d,0x51,0x77,0x2f,0x73,0x54,0x88,0xab,0x64,0x6b,0xbc,0x94,0x8b,0xf3,0xad,0x1e,0x56,0xdf,0x81,0xad,0x14,0x1e,0xf7,0x06,0x72,0xb4,0xf3,0x1b,0xf6,0x5f,0xa1,0x9f,0x20,0x7a,0x1b,0xad,0x88,0x12,0x93,0xbe,0xc3,0x48,0xf1,0xf1,0xf1,0xde,0x83,0x72,0x1d,0xf5,0xb9,0x20,0x54,0xaf,0x2f,0xe2,0x81,0x31,0x45,0xd9,0x31,0xcb,0xbe,0xb6,0xa1,0x06,0xb6,0x10,0x2f,0xd2,0x4a,0xce,0x62,0xc8,0x1b,0xf9,0xf2,0xf1,0x51,0xef,0xe7,0x09,0x6c,0xd3,0x2d,0x73,0xf5,0x03,0x6d,0xa3,0x6e,0x13,0xef,0x45,0x27,0x27,0x11,0x9e,0x65,0xbd,0x40,0xd5,0x5f,0x8c,0x36,0xd3,0x7a,0x08,0x07,0x1b,0x01,0x94,0xab,0x6d,0x8e,0x14,0x41,0xac,0x13,0x43,0xa8,0x64,0xb1,0x4b,0x30,0x0f,0x90,0xec,0xe7,0x2f,0xa2,0xe8,0x42,0xdb,0x66,0xd0,0x1a,0x5b,0xd5,0x74,0x54,0xd4,0x0b,0xda,0xc0,0x93,0x87,0xcb,0x6e,0x56,0x08,0x47,0xe8,0xa5,0x70,0x43,0xfc,0x31,0x4a,0x27,0xe4,0x4f,0x05,0x95,0x64,0xaf,0x1a,0xdf,0xd3,0x03,0x23,0x55,0xaf,0x37,0x87,0xe2,0x96,0xef,0xdd,0xb8,0xe4,0x7c,0x7d,0x2a,0xff,0x89,0x48,0x2b,0x7e,0x3d,0xd8,0xfa,0xa8,0x57,0xe5,0xab,0x58,0x85,0xe4,0x8e,0x35,0x67,0x68,0x65,0xd9,0x05,0x53,0x4e,0x3f,0x90,0x19,0x31,0x84,0x24,0xd1,0xa7,0x50,0x51,0x63,0x70,0x8b,0x39,0x91,0x44,0xaf,0xb1,0xe7,0xa9,0x9a,0x88,0xc0,0x2d,0x30,0xf3,0xf4,0x08,0x6f,0x4f,0xe7,0x46,0x03,0x87,0x16,0xe5,0x48,0x55,0xf3,0xa3,0xa3,0x01,0x47,0xae,0x91,0x53,0xbc,0xd7,0x63,0xba,0x3d,0x10,0xec,0x47,0xbc,0x9b,0xc1,0x15,0xc9,0x8d,0x9f,0xfc,0x85,0x01,0xad,0xe9,0x1e,0x8c,0x8b,0x34,0xa8,0xd0,0xe1,0xbf,0xf4,0x41,0x2e,0x45,0x61,0x71,0xc5,0xfd,0x6b,0xa7,0xc7,0x4b,0xb6,0xdc,0x08,0x1e,0xca,0xf7,0x74,0x08,0xc7,0xf4,0xad,0xfb,0xde,0x19,0xcf,0xf6,0x4c,0x4e,0xcf,0xfa,0x97,0x07,0x3e,0xc7,0xe2,0xd1,0xbd,0x27,0x7f,0xf6,0x3d,0xbe,0xa5,0xf7,0xe8,0x45,0xe0,0x1d,0x97,0xfa,0x0d,0x85,0x2c,0x36,0x4e,0x89,0x89,0xb2,0xdd,0x3f,0xee,0x65,0xf1,0x6d,0x69,0xa3,0x83,0x47,0x47,0x51,0x1a,0xe5,0x51,0xd2,0xd3,0xe3,0xa0,0x2d,0xcd,0x25,0xc5,0x51,0x8e,0x89,0x51,0x1c,0x19,0x98,0x5c,0xa2,0xe8,0x1d,0x9e,0x21,0xb5,0xf9,0xe4,0x29,0xa2,0x5b,0x75,0x57,0x18,0xfd,0x27,0x8b,0x7a,0x37,0x65,0xaf,0x77,0xd0,0x5b,0x9a,0xf5,0x36,0x96,0xcb,0xd9,0xd8,0x90,0x58,0x1c,0x96,0xc1,0xa7,0xb9,0x98,0xbc,0xde,0xf6,0x9b,0x9b,0x60,0x1c,0xfd,0x38,0x3e,0x36,0x65,0x8e,0xcf,0x66,0xb4,0xfd,0x47,0xaa,0xf5,0x24,0xeb,0x6c,0x88,0x60,0xfb,0x5c,0x25,0xaf,0xeb,0xe5,0x08,0x81,0xe8,0xaa,0x69,0xe4,0x4a,0x23,0x5a,0x92,0x1b,0x37,0x37,0x98,0x48,0x79,0x23,0xa6,0xb5,0x78,0x95,0x9f,0x97,0x74,0xb7,0x14,0x30,0x68,0x2a,0xa8, +0x25,0x51,0x5d,0x0f,0xbc,0x60,0xea,0x2d,0xf2,0xb7,0x0c,0xe6,0x93,0xea,0xbe,0xa6,0xeb,0xce,0xc3,0x3b,0x17,0xf9,0xde,0x3b,0x3d,0xcd,0x29,0x84,0xd9,0x79,0xa4,0x88,0x2a,0xfe,0xb9,0xec,0x45,0xa7,0x07,0x9f,0xb2,0x41,0x7f,0xf0,0x34,0x82,0xf8,0x36,0x75,0xd5,0x70,0x48,0x05,0xe6,0x99,0x17,0xd8,0x92,0xa9,0x23,0xe4,0xb8,0x4a,0x3a,0xde,0x77,0xa1,0xec,0xed,0xf3,0x85,0xe0,0x1a,0xd6,0x7d,0x41,0x2a,0x3a,0x23,0x8a,0x5a,0xc2,0xcd,0xdb,0xa4,0x98,0x41,0x5c,0xab,0xad,0xaa,0x93,0x4c,0x3a,0xf1,0xca,0x5a,0x8f,0x6c,0xf5,0x9e,0xcd,0x95,0xd0,0xe1,0xce,0x29,0x22,0xfc,0x18,0xb1,0xb6,0x76,0xd1,0x2f,0xa8,0xdb,0x38,0x42,0x06,0x91,0x06,0x42,0xc8,0x23,0x87,0x23,0x62,0xd4,0x22,0x94,0x47,0xc0,0xa1,0xd7,0x20,0x8b,0xf4,0xbe,0xc1,0x67,0xfc,0xb6,0xef,0xe8,0x83,0xec,0x29,0xf6,0x8a,0x4f,0x8d,0x5d,0x81,0x21,0x97,0xce,0xe9,0xd5,0x2e,0x82,0x9d,0x86,0xfa,0x05,0x84,0x39,0x46,0x60,0x23,0x4e,0x7c,0x40,0x51,0x8c,0x67,0xd9,0xbe,0x50,0x7d,0xe6,0x3b,0x22,0x5d,0x1c,0x52,0x59,0xfb,0x6c,0x22,0xbb,0xf4,0x15,0xdc,0xa5,0x61,0x1e,0x4f,0x4c,0xc7,0xad,0x7a,0x91,0xb8,0xcd,0x95,0x4f,0x4a,0x66,0x90,0x0e,0xfe,0x3c,0x7d,0x11,0x1f,0x3f,0x55,0x7f,0x26,0x3b,0xb1,0xca,0xe4,0x14,0xb8,0x1d,0x4b,0xfd,0x45,0x9e,0x6d,0xfa,0x0b,0x91,0xd3,0x58,0xc1,0x0d,0x13,0x70,0xb7,0xea,0x8e,0xe8,0xa4,0xea,0xf4,0x8a,0x78,0xf1,0x2b,0x48,0x7c,0x66,0x60,0x49,0x03,0x8f,0xc4,0x19,0xba,0x4b,0xab,0x87,0x27,0xd9,0x14,0xbb,0x91,0x0a,0x3a,0xab,0xfc,0x66,0x90,0x3f,0x27,0x92,0xea,0x92,0xae,0xb2,0x67,0x03,0xfa,0xf8,0xf2,0xeb,0x2f,0x07,0x83,0xc7,0xc7,0x2f,0x07,0xcf,0xe1,0x2d,0xa5,0x30,0xca,0xb7,0xd9,0xdb,0x32,0xae,0x69,0x60,0x11,0x26,0xe5,0x36,0xfb,0x15,0x89,0x5b,0x4a,0x22,0xd4,0x49,0x1e,0x37,0x96,0xfa,0x5d,0xb6,0xed,0x12,0x66,0xbc,0xa2,0x55,0x6c,0x17,0x37,0x6d,0x28,0x77,0x5d,0xbb,0x42,0x76,0x47,0x37,0xba,0x9f,0xc7,0x22,0xb6,0x8f,0xe9,0x15,0x4d,0xc5,0x13,0xf5,0x4c,0x5e,0x94,0x3e,0x0d,0x72,0x47,0x30,0xd4,0xb2,0x91,0xe7,0xf7,0x88,0x4b,0x66,0x20,0x08,0x52,0xfd,0x3d,0x92,0xbb,0x59,0x98,0x17,0x49,0x69,0x32,0xde,0x32,0xbd,0x32,0x52,0x0b,0xba,0xe2,0xfd,0x71,0x49,0x17,0x3c,0xd9,0xa8,0x5b,0x0e,0x97,0xb2,0xe3,0xde,0x2b,0x78,0xbc,0xdf,0x53,0x57,0x53,0x0d,0x9a,0x4d,0x2d,0xd9,0x3c,0xbd,0x44,0x14,0x2d,0x5e,0xef,0x4c,0xf5,0x50,0x9f,0x99,0x4b,0x86,0x07,0x82,0x96,0xe4,0x1e,0x04,0x07,0xf5,0xd5,0x3a,0x88,0xa5,0x73,0xad,0xce,0x17,0xea,0x5e,0x6d,0x2f,0x92,0x74,0xed,0x07,0xd3,0xb9,0xc6,0x44,0xbd,0x57,0xcb,0x0b,0x57,0x29,0xc8,0xaf,0xf8,0x06,0x34,0xb3,0x1e,0xcc,0x60,0x8a,0x5f,0xe6,0x32,0xc9,0x35,0xeb,0x9b,0x72,0xea,0x3b,0x79,0x47,0xcc,0x79,0x6a,0x79,0x91,0xa2,0xba,0x0d,0x87,0xfa,0xf3,0x1a,0xa1,0xbc,0x39,0x87,0x2d,0x0b,0x56,0xcb,0x4b,0xbd,0x1a,0xed,0x8a,0x39,0x3e,0x36,0xe7,0x2e,0x2b,0xc7,0xba,0x4e,0xdd,0x9a,0x2d,0xee,0x0c,0x85,0xbc,0x65,0xa2,0x18,0x6c,0xd7,0x3e,0x2f,0xb5,0x95,0x71,0xf5,0xa1,0x79,0x1d,0x31,0x97,0x96,0xf0,0x33,0x67,0xcc,0x6c,0x76,0xd3,0xc3,0xe6,0x19,0x63,0xe0,0xe3,0x00,0x4e,0x03,0x33,0x38,0x18,0x6f,0xaa,0xe8,0x9a,0xc8,0x9a,0x76,0x14,0xf8,0xa6,0x4e,0x3c,0xf0,0x70,0x6f,0x48,0x7b,0xd9,0x06,0x96,0x8d,0xb4,0x86,0x1c,0xec,0xdc,0x30,0x56,0xc2,0x36,0xb9,0xa0,0x3b,0xcc,0x2f,0x16,0x4a,0x1b,0x84,0x99,0xed,0x38,0x1d,0x89,0x08,0xa2,0x52,0x7a,0x27,0x4b,0x39,0x56,0x48,0x53,0x69,0x04,0x3b,0x7e,0xfd,0xfa,0x06,0x64,0xb8,0xcb,0x44,0x4e,0xb7,0xe9,0x37,0x25,0xe2,0x08,0xdb,0x9c,0xe9,0x0b,0xc5,0x87,0xb9,0x27,0x73,0x78,0x6a,0xa5,0x12,0xb4,0xc1,0x68,0xe9,0x0c,0x5b,0x72,0x34,0xf4,0x49,0x77,0xab,0xe2,0x1a,0x18,0x73,0x6d,0x2a,0xdf,0x23,0xd2,0xf9,0xb8,0x6a,0xfa,0x47,0x48,0x3c,0x07,0x67,0x86,0x0c,0xb1,0x27,0xdb,0x03,0xeb,0xf0,0xca,0x2d,0xa0,0xea,0x04,0x00,0xd8,0x83,0xc4,0xc3,0x96,0x33,0xc5,0x42,0x67,0xa3,0x00,0x7e,0xd2,0xd4,0xac,0xca,0x6e,0xc5,0x14,0xc7,0xbc,0x12,0x26,0xaf,0x10,0x3c,0x4e,0x4d,0x3f,0x0b,0x2c,0x27,0xde,0xb0,0x95,0xeb,0xd0,0x8c,0x2d,0x18,0xa1,0x78,0x6a,0x68,0xe6,0x07,0x7d,0xf2,0xe3,0x72,0xd9,0x8d,0x6a,0xfc,0x37,0x7c,0x06,0x3c,0xf7,0x10,0x5b,0x55,0xe8,0x1c,0x92,0x7c,0xde,0x49,0xc0,0xfa,0x01,0xc0,0xfa,0xd8,0xc8,0x5f,0x62,0xe7,0xde,0x9f,0x57,0x7d,0x3d,0x6e,0xd0,0xb5,0x95,0x1e,0xa2,0x62,0x22,0x6f,0xdf,0x69,0x3f,0x1c,0xbc,0x77,0xa0,0x78,0xdb,0xeb,0xf2,0x60,0x5a,0x29,0x43,0x7b,0xf3,0x24,0x95,0xa6,0x6e,0x96,0xad,0xc6,0x02,0x34,0x40,0x1e,0x56,0x98,0x81,0xd0,0xe6,0x1b,0x47,0x65,0x3d,0xdc,0x46,0x6d,0x8c,0x5b,0xe7,0x49,0x63,0x41,0x0e,0x8d,0x0e,0xcf,0x40,0x97,0x43,0xad,0x60,0x62,0x8c,0x69,0x87,0xd5,0xeb,0xf5,0xe8,0x66,0x58,0xaf,0x4d,0xe4,0xc5,0xf6,0x2b,0x1c,0x36,0x0a,0x32,0x1c,0x15,0x66,0x09,0xaf,0x80,0xae,0x5b,0x5d,0x95,0x1c,0x02,0x51,0x8c,0x41,0x72,0x38,0xbe,0x3f,0xc4,0xe4,0x92,0x14,0xf8,0x97,0xcf,0xa0,0x42,0x70,0x33,0x81,0x0a,0xe3,0x7e,0xba,0xf2,0xc3,0x08,0x80,0x68,0xf0,0x82,0xcc,0x17,0xfd,0x3f,0x7e,0x79,0xf5,0xc3,0x66,0x73,0xad,0xa9,0x31,0x4d,0x3f,0x94,0x40,0xd0,0x64,0x69,0xce,0xbb,0x32,0x7b,0x18,0x70,0x60,0x84,0xa7,0xcf,0x9e,0x7d,0x49,0x17,0xcf,0x77,0xea,0xac,0x6c,0xea,0x49,0xa8,0x11,0x9a,0x28,0xa0,0xad,0x56,0xeb,0xec,0xf0,0xf0,0x0c,0x76,0xed,0x77,0xd4,0xa5,0x2f,0x57,0x23,0xea,0xa7,0xcd,0xac,0x98,0xaf,0x61,0x6b,0x73,0x46,0x94,0x14,0x3f,0x98,0x51,0x15,0x28,0xa6,0x5f,0xd6,0xd2,0x21,0xc1,0x6c,0xd6,0x5a,0x19,0x21,0x05,0x51,0xf3,0xe3,0x23,0x2a,0x3e, +0x2c,0x03,0x1e,0xd6,0x88,0x64,0x03,0xa8,0x5e,0xdf,0xca,0x96,0x26,0xb3,0xbc,0x1e,0xa4,0xd6,0x0c,0xd0,0x18,0x8b,0x21,0x1e,0x2d,0x6e,0xf0,0xb6,0xa5,0xd0,0x65,0x48,0xd1,0x0e,0xc0,0x2a,0x51,0xb8,0x1e,0xae,0xd7,0x77,0xf5,0x6a,0x88,0x1d,0x80,0x9e,0x16,0x71,0xa2,0xd3,0x05,0xf8,0x99,0x53,0x78,0x06,0x7b,0x19,0x90,0xfd,0x97,0x56,0xb8,0x71,0x74,0x34,0xed,0x37,0x05,0x23,0x5d,0x79,0xb1,0x7b,0x04,0x6d,0x7a,0x5f,0x08,0x9d,0x58,0xf4,0xc7,0xb1,0x1e,0x20,0xe2,0x56,0x18,0x90,0x13,0x3e,0xf8,0x9d,0xf9,0x59,0x14,0x8e,0xa8,0x0e,0xee,0xc9,0xaf,0x3d,0x4a,0xa6,0x6d,0xca,0x7b,0xa2,0x18,0x6a,0xf3,0xb4,0xea,0x3a,0x0b,0xbc,0x99,0x53,0xb1,0xd4,0x0f,0xa8,0x74,0xf5,0x72,0x5e,0x17,0x72,0xc1,0xd4,0x09,0x5f,0x31,0xad,0xca,0x57,0x4c,0xf2,0x31,0x99,0x23,0xb6,0x66,0x6c,0x66,0xa8,0x34,0x19,0xce,0x50,0xd4,0x53,0x43,0xa1,0xa7,0x9a,0xbc,0xe1,0xdc,0x56,0xd8,0xaf,0xa9,0xa6,0x48,0xf2,0x71,0x3c,0x50,0xba,0x64,0x92,0x62,0x14,0x25,0x5f,0x4d,0x3d,0xe2,0x07,0x37,0xde,0x95,0xe7,0x26,0xeb,0x02,0xe1,0x23,0x3b,0x8a,0x89,0x50,0x96,0xd1,0x46,0xa6,0x56,0xe3,0xa2,0xb9,0x67,0xbe,0x93,0x74,0xd8,0x0c,0x79,0x25,0xa9,0x48,0xfe,0x50,0xce,0x96,0xc5,0x6a,0x9b,0xba,0xec,0x5d,0xfa,0xc0,0xe2,0xdb,0xb0,0xe0,0x8e,0x5a,0xee,0x94,0xd7,0xc5,0x09,0x3b,0x49,0xd8,0x9e,0xac,0x10,0x17,0xd2,0xeb,0xcf,0x2a,0x36,0x5f,0xeb,0x5c,0x9f,0x6d,0x1f,0xe7,0xae,0xb7,0x87,0x69,0x67,0x7f,0x7b,0x83,0x06,0x42,0x74,0xea,0x11,0xe1,0x8c,0xcf,0xd4,0xc9,0x6d,0x54,0x88,0xea,0xc4,0xfb,0x6b,0x85,0xf6,0x65,0xb4,0x84,0xc9,0x98,0x0a,0x7f,0x51,0x06,0x8c,0xac,0x68,0x59,0x25,0xb8,0x93,0x61,0x3b,0x66,0x1a,0xd8,0x46,0xd8,0x8e,0xd9,0xae,0x25,0x66,0xe3,0x66,0xaa,0xd8,0x78,0xa3,0x04,0x62,0xe9,0xd8,0x9f,0x7e,0x85,0xbf,0x02,0x38,0x0e,0x82,0x39,0x90,0xfa,0x42,0x7b,0x64,0xa2,0xf6,0x0d,0xc4,0xca,0x56,0x61,0x21,0x45,0xb4,0x66,0xc2,0xa9,0x15,0xd4,0x41,0xa0,0x79,0xd8,0x93,0x3f,0xaa,0x16,0x9d,0xf9,0xf7,0xc7,0xee,0x4e,0xa0,0xa0,0xd0,0xad,0x9d,0x7c,0x2c,0xe3,0x3c,0x45,0xad,0x8f,0x28,0x98,0x48,0x36,0x6b,0x25,0x02,0x55,0x02,0x2b,0x05,0x74,0x35,0xdd,0x47,0xbd,0xd0,0x50,0xdf,0xc1,0x2a,0xa4,0x80,0x86,0xb8,0xab,0xb3,0x2c,0x09,0x16,0x9c,0xb9,0x16,0xeb,0x59,0xc4,0x2c,0xd2,0x71,0x2c,0x70,0xa1,0xee,0x52,0xed,0x4e,0x95,0x10,0x2b,0x20,0xee,0x5c,0x67,0xba,0xcd,0xb8,0xb3,0x0d,0x56,0x61,0x06,0x42,0x44,0xed,0xd3,0x73,0xba,0x7f,0x27,0x06,0x69,0x11,0x7d,0x2d,0xb5,0x7d,0x43,0x87,0x31,0x5b,0xf5,0x3c,0x68,0x75,0x13,0x62,0x45,0xf0,0x9d,0x97,0x92,0x56,0xeb,0x55,0x45,0x79,0xb4,0x35,0x13,0xa1,0x44,0x35,0x44,0x58,0x22,0x07,0x9a,0x0d,0x0a,0x37,0xa9,0xd2,0x18,0x0e,0x1a,0x53,0x7b,0x85,0x20,0x63,0xb1,0xb7,0xa9,0x08,0x7b,0xf6,0x7c,0xf0,0x9c,0x8f,0x30,0x49,0xe2,0x53,0x87,0x2c,0x6c,0x08,0x82,0xde,0x00,0x8d,0x2e,0xf9,0xdc,0x8c,0xd5,0x46,0x30,0xef,0x4b,0x98,0x89,0x7d,0x80,0xd9,0x4b,0x96,0x7c,0xcc,0xe3,0x3c,0x3b,0x7a,0x7c,0x92,0x3c,0x7e,0xcc,0xc5,0x24,0xd1,0x9b,0x8f,0x60,0x35,0xae,0xd3,0xa8,0xd2,0x5a,0x09,0xd1,0x33,0x5d,0x1b,0x25,0x45,0x1b,0xe3,0xe4,0x7d,0x29,0xf6,0xdd,0xcc,0xf8,0x88,0x47,0x44,0x2f,0xfa,0x8f,0x08,0xbd,0x02,0xc2,0xb8,0x60,0xa3,0x8b,0xa2,0x73,0x5e,0xa0,0x0d,0x56,0x45,0x5d,0x47,0xbe,0x61,0x5e,0x0b,0xb2,0xab,0xec,0x73,0x21,0x13,0x24,0xed,0x83,0x96,0xb8,0xf1,0x99,0x98,0xe4,0x11,0xfd,0x22,0xa0,0x7a,0xc3,0xcb,0xac,0xd4,0x32,0x2d,0x16,0x3e,0x96,0xff,0xaf,0xf0,0xd1,0xb5,0xc9,0xc2,0x41,0xa2,0x19,0xf0,0x2f,0x5e,0xc8,0x90,0x1f,0xc8,0x57,0x30,0xf8,0x6f,0x60,0xd1,0x61,0xc4,0xf3,0xe6,0x1b,0x4c,0x8f,0x36,0xac,0xf4,0xc2,0x9b,0x70,0x93,0x09,0x32,0x60,0xc1,0x17,0xe6,0xa8,0x69,0x5e,0xc2,0xa0,0x05,0x3f,0x56,0x2c,0xf9,0x41,0xc4,0x92,0xbd,0x91,0x2d,0x6e,0xba,0x8c,0xbb,0xca,0x93,0x7e,0x9a,0xae,0x33,0x02,0x4b,0x5d,0x5c,0x6c,0x00,0xf9,0x50,0x77,0xf6,0x01,0x7a,0x69,0x89,0xce,0xf0,0x22,0x88,0x16,0xa9,0xcd,0x58,0x9c,0x69,0x28,0xcc,0x22,0xee,0x8a,0xf5,0x01,0x91,0xb3,0x07,0x98,0x4b,0x2c,0xd3,0x98,0x50,0x47,0xec,0x54,0xd8,0x31,0x99,0x30,0xb7,0x1c,0xfe,0x7a,0x04,0xad,0xc7,0x28,0xa8,0x79,0x92,0x59,0x63,0xec,0x1d,0x2d,0x80,0x76,0x24,0x69,0xbb,0x7d,0x8c,0x73,0x36,0xa3,0x76,0x81,0x45,0x62,0xfa,0x7a,0xa9,0x8e,0x4d,0x29,0xf9,0xe3,0xc3,0xae,0xaf,0x1a,0x5d,0x89,0xb9,0x0c,0x3b,0x23,0x98,0x23,0x4c,0x1a,0xee,0x09,0x70,0xb9,0x1b,0xc7,0x13,0x66,0xb0,0x26,0xd9,0xd8,0x01,0x52,0x98,0x0d,0x87,0xcd,0xb5,0x45,0x2f,0x01,0x24,0x6a,0xc3,0xca,0xb5,0x82,0x99,0x65,0xc3,0xfe,0x0c,0xe2,0x03,0xdc,0xe4,0xb9,0xd6,0xf1,0x50,0x8c,0x79,0x09,0x26,0xc0,0xc5,0xdb,0xf1,0x20,0xae,0xbf,0xc6,0xb4,0xfc,0xe6,0xeb,0x13,0xf9,0xf3,0x13,0x91,0x7a,0x26,0x1b,0xa9,0xe5,0x07,0x34,0x89,0xbd,0x63,0xbf,0x08,0x56,0xe7,0x72,0x1d,0x4d,0xa1,0x83,0xe7,0x64,0xed,0x62,0x40,0xea,0x39,0x7b,0x7e,0x71,0xda,0xe9,0x46,0xae,0xd5,0xa9,0xb4,0x49,0x3b,0x15,0xaa,0x0f,0xd3,0xda,0xd5,0x1b,0x88,0x5c,0xfb,0x37,0xbf,0x9f,0xc1,0xae,0x1a,0x7a,0x9e,0xb2,0x58,0xc3,0x01,0x66,0x24,0x9a,0x9d,0xa1,0x35,0xb4,0x16,0x15,0x4f,0xd9,0xde,0x1f,0x69,0x24,0x53,0x6a,0x11,0x4a,0x8b,0x97,0xa2,0xd5,0x64,0x34,0x38,0xc4,0x84,0x85,0xc5,0x57,0x7e,0xde,0x6c,0x02,0xa1,0xd8,0x13,0xc6,0x59,0xfc,0x24,0xa1,0xe9,0x4b,0x35,0xe1,0xc9,0x30,0xb1,0x10,0x76,0x44,0x7d,0x26,0x6e,0x0f,0x5f,0xf5,0x17,0xa3,0xd5,0x64,0x14,0xa3,0x3a,0x9f,0x0f,0x33,0xd2,0x03,0xa6,0x95,0xf6,0x46,0xa2,0xa1,0x77,0x61,0x3e, +0x11,0x06,0x53,0x5e,0x08,0x01,0x17,0x72,0x5c,0x1c,0xd6,0x86,0x08,0x16,0x60,0x30,0x76,0x81,0x45,0x08,0x66,0x5d,0x52,0x03,0x85,0x74,0xd3,0xd4,0x37,0x54,0x75,0xa7,0x65,0x47,0xd4,0x5c,0x06,0x34,0xc9,0xa2,0xb7,0x6f,0xce,0xde,0x63,0x69,0x5a,0x57,0x07,0xcc,0xfb,0x96,0x2c,0x05,0x5b,0x65,0x43,0x9c,0x22,0xc6,0x13,0xda,0x8a,0x24,0x69,0x04,0xef,0x2f,0x60,0x0b,0x65,0x57,0x2e,0xd5,0x8e,0xd2,0xf1,0x30,0xc7,0x91,0x3a,0x9c,0xdd,0xe2,0x3c,0xd5,0x9c,0xb8,0x37,0x2b,0x61,0x78,0xca,0xae,0xbb,0xf1,0x50,0xd8,0x66,0xb3,0xdc,0x69,0xb4,0x42,0x31,0xd5,0xa4,0xc5,0x1a,0x07,0xf1,0x15,0xc7,0x8f,0x8f,0x9e,0x69,0x12,0x53,0xd0,0xa5,0x2a,0x2e,0x18,0xc1,0x43,0x0b,0x2e,0xac,0x30,0xcc,0xe9,0xc4,0x94,0x93,0xd4,0xa9,0x86,0x98,0xcf,0x17,0x18,0x06,0xa2,0x44,0xe5,0xa4,0xe7,0x6d,0x40,0xc5,0xa6,0x87,0x49,0xc8,0xf2,0xf3,0x09,0x57,0x24,0x1d,0x8c,0xba,0x0e,0x94,0x38,0xec,0x96,0x73,0x4d,0x68,0x93,0x8f,0x6d,0x14,0xcf,0x40,0x92,0xa2,0xf7,0x0a,0x3e,0x7c,0x24,0xb0,0xbe,0x59,0xfe,0xd4,0x84,0x30,0xe3,0xec,0xa4,0xf1,0x86,0x2f,0xf7,0xc7,0x47,0x72,0xa6,0x10,0x1e,0x06,0xb0,0xc6,0xad,0xa6,0xb9,0x32,0x87,0xe0,0x0a,0x6e,0x67,0x0f,0xbb,0xd3,0x08,0x34,0xfc,0xac,0x32,0xa0,0x1d,0x06,0xa0,0xc3,0x94,0xce,0xa2,0xd5,0x68,0x5e,0x40,0x30,0x0a,0x7b,0xab,0x6c,0xae,0xdf,0x22,0x96,0x58,0xbb,0xba,0x6a,0x96,0x8d,0xaa,0x99,0xcb,0x98,0x33,0x78,0xa1,0xba,0xcc,0x62,0x07,0x92,0xad,0xf1,0x3b,0xc6,0xb3,0x7b,0x8e,0x13,0x97,0x5d,0x25,0xac,0xc7,0x9b,0xf9,0xe7,0x37,0xc0,0xae,0xe0,0xe2,0x09,0xa9,0xfb,0x90,0x1a,0x33,0x6f,0x11,0x33,0x02,0x64,0x1f,0x51,0x6f,0x47,0xd8,0x35,0xa8,0x7a,0x20,0x1c,0xb7,0x91,0x7a,0x47,0x7e,0xd6,0x0c,0x59,0xad,0xa5,0xc5,0x02,0xed,0xd2,0x04,0x33,0xaf,0x82,0x38,0xc0,0x53,0xc8,0xb9,0xb5,0x7f,0x79,0x9f,0xe3,0xa4,0xc6,0x0b,0xfc,0x4b,0xea,0x78,0x8a,0xdf,0xde,0xc4,0x15,0xc1,0x8b,0x70,0x19,0x5c,0xe8,0x34,0x95,0xc2,0x1f,0x0e,0xdf,0x08,0x7e,0x3c,0x13,0xf6,0xb3,0xcf,0xc1,0xe8,0x23,0xa0,0xbf,0x6e,0x77,0x91,0xa4,0x73,0xee,0xad,0x85,0x80,0x52,0x79,0x62,0xca,0xba,0x39,0xb8,0xc9,0xe7,0x42,0x2c,0x38,0xea,0x9b,0xc5,0x73,0x5d,0x92,0x36,0x9e,0xcb,0x52,0x6b,0xdf,0x4e,0x1e,0x13,0x17,0xb6,0x34,0xfe,0xc6,0x36,0x4e,0x8c,0xef,0x63,0xe6,0x4c,0x1e,0xf7,0xc9,0x7e,0x30,0x50,0xe3,0x7d,0xe8,0x92,0x1c,0x51,0x36,0x44,0xa9,0x87,0x98,0x6f,0xa8,0x13,0x7a,0xcf,0xe6,0x83,0xc2,0xf3,0xd9,0x54,0x0f,0x88,0x8d,0xcc,0xc3,0xdd,0xa3,0x99,0x58,0x4c,0x46,0xff,0x96,0x77,0x3e,0xae,0xfa,0x15,0xd7,0xfe,0x9e,0x26,0x02,0x3a,0x39,0x1d,0xea,0xbe,0xe6,0x52,0x7f,0x34,0x4a,0xbd,0x62,0x68,0xcb,0x94,0x6b,0x1b,0x48,0x79,0x18,0x3a,0x59,0x00,0x77,0x6f,0xf7,0xa1,0x4f,0x35,0xd2,0x57,0x13,0xb1,0x54,0x59,0x67,0x3b,0xe2,0x92,0xc3,0x3a,0x8c,0x31,0xb1,0x9d,0xcb,0x32,0xf1,0x2b,0x7f,0x91,0xe5,0x65,0x56,0xed,0xeb,0x96,0x34,0xd6,0x6e,0x01,0x32,0x28,0x6f,0x45,0x76,0x88,0xde,0xf2,0x72,0x29,0xfd,0x6d,0x0c,0xa4,0x76,0x25,0x3b,0x75,0xc2,0x26,0xbe,0x85,0xbd,0x9b,0x98,0xd7,0x92,0x8e,0xd2,0x6b,0x8f,0xcb,0x0b,0x4e,0x27,0x75,0x92,0xc5,0xed,0x4e,0x82,0xee,0x6a,0x97,0x45,0x57,0xb9,0xc2,0xb4,0xa3,0x71,0xcd,0x32,0xeb,0xb9,0xfe,0x63,0xfb,0x89,0x02,0xb5,0xf5,0x1e,0x9b,0xac,0xad,0x57,0xcf,0x7c,0xa9,0xbe,0x59,0x94,0x91,0x4c,0xc5,0x1d,0x71,0xa7,0xfc,0x2f,0xfe,0x9f,0xbc,0x32,0x5a,0xfd,0x65,0x24,0xe1,0x6c,0xa5,0x61,0xb6,0xb0,0x8e,0xdd,0x8e,0xe5,0xe2,0xc1,0x83,0x66,0x9b,0x25,0x62,0xb7,0xd8,0x85,0x41,0x7a,0x2d,0xea,0x1c,0xec,0x35,0xed,0x8c,0x8a,0x94,0x45,0xa9,0x93,0x7c,0x3d,0x1f,0xf7,0xb8,0xe4,0x05,0x45,0xb0,0x97,0x9f,0xb6,0xa2,0xeb,0xba,0xc8,0xba,0x6d,0x68,0x51,0x87,0x4d,0x7f,0xea,0xc9,0xe1,0xb5,0x47,0x6d,0x91,0xe4,0x74,0x2e,0xa7,0x5f,0x35,0xd1,0x9e,0x10,0xcc,0xc2,0x5b,0x42,0x3e,0x5a,0x71,0x3e,0xce,0x11,0xa5,0x39,0x85,0x2d,0x3e,0xdb,0xb6,0xc7,0x94,0x61,0x71,0xf7,0xe2,0x8a,0x12,0xde,0xb7,0xa6,0x23,0x55,0xe5,0xa3,0x74,0xec,0xaf,0xbb,0x84,0x9f,0x85,0x31,0x34,0x07,0x36,0x6e,0x6d,0x45,0x8d,0xb8,0xcc,0x38,0x7e,0xf9,0x04,0xf8,0x3c,0x44,0x31,0x43,0xe5,0x5e,0xd3,0x12,0x9a,0xbf,0xd5,0x83,0xe5,0x17,0xaf,0x02,0xb8,0x62,0x83,0x8d,0x5e,0x26,0xca,0x80,0xc2,0xc3,0x2d,0x05,0xfc,0x82,0x3b,0x23,0xa8,0x52,0x86,0xb1,0x4d,0xab,0x5d,0xe2,0x0d,0xaa,0x46,0x2f,0xb7,0xf8,0xc9,0x3c,0xcf,0x53,0x8d,0xa3,0xd6,0x8e,0x83,0x2c,0x0f,0x59,0xd4,0x5b,0x26,0xdb,0xa3,0x5e,0x61,0x44,0x2f,0x29,0x8c,0x65,0xd2,0xa8,0xa6,0x23,0x0d,0xd9,0xbb,0x46,0x68,0x5d,0x1e,0xe9,0xa1,0x37,0xd2,0x4e,0x28,0x9c,0x35,0xfb,0x0d,0x14,0xb8,0x17,0x6c,0xda,0xd2,0xed,0x23,0x9c,0xb4,0x15,0xc7,0x43,0x15,0x04,0x80,0xb1,0x18,0x2f,0x1b,0x94,0xbb,0xd4,0x00,0xd0,0x5b,0x2a,0xb3,0x39,0x89,0x38,0xe6,0xc3,0xfe,0x49,0x44,0xf4,0x25,0x5b,0xe9,0xbb,0x93,0x57,0x3e,0x87,0x36,0xad,0x73,0xfb,0xc1,0x17,0xa9,0xdb,0xa8,0x9b,0x3b,0xf6,0x79,0x24,0x3b,0x2c,0x17,0xfb,0x8a,0xc9,0x15,0x33,0x17,0x81,0x70,0xdd,0xde,0xe1,0x2d,0xdc,0x4f,0xc9,0x5c,0x11,0x73,0xa2,0x34,0xfd,0xf0,0xbc,0x1a,0x07,0x29,0x53,0x40,0x56,0xac,0x29,0x10,0xa6,0x5c,0xe3,0x89,0xf1,0xd6,0xe0,0x89,0x2e,0xab,0x1f,0x1f,0x3f,0x4d,0x2c,0x18,0xa5,0xc4,0xbf,0x80,0x8e,0x82,0x38,0x01,0x9a,0xd9,0xc6,0x58,0x80,0x43,0x19,0x37,0x1c,0x7e,0xe1,0xd9,0xbf,0x4f,0x9f,0x6b,0x68,0xbe,0x42,0x7b,0x83,0xd3,0x2d,0x68,0x85,0x5a,0x8f,0x34,0x1e,0x18,0x8f,0xed,0x13,0x40,0x53,0xd0,0xa1,0x10,0xfe,0x02,0x3f,0x41,0x48,0x4b,0x8d,0x83, +0x80,0xe8,0xba,0x9d,0x4f,0xd9,0x67,0x38,0xca,0x6a,0x63,0x72,0xe5,0x5e,0xf3,0xd1,0x17,0x5f,0x44,0x5a,0x07,0x87,0x0c,0xaa,0x1a,0xb6,0x7b,0x5f,0x44,0xaa,0xd2,0x4b,0x77,0x5a,0xcf,0x87,0xef,0x20,0xae,0x0d,0x28,0xd6,0x82,0x43,0xac,0x52,0xee,0xef,0xc5,0x6c,0xd3,0xeb,0xa5,0x3a,0xc5,0x28,0x08,0x4c,0x55,0xb1,0x7f,0x58,0x16,0x78,0x8b,0x19,0x26,0x15,0x9a,0xf1,0xcc,0xb3,0x4a,0x5e,0x59,0x27,0xb6,0xec,0x5b,0x15,0x99,0x56,0x1c,0x33,0x23,0x01,0x84,0x8f,0x8e,0xe4,0xbf,0x5f,0x2c,0x86,0xe6,0x3a,0x8e,0xc4,0x10,0x17,0xf8,0x04,0x1d,0xc8,0xd6,0x2b,0x4d,0xbe,0xfc,0x06,0xe7,0x84,0xcb,0x5f,0x51,0x52,0xfd,0x8e,0xeb,0x27,0x6e,0xe2,0x2f,0xeb,0x97,0xf5,0x72,0x4c,0xdc,0xd6,0x26,0xeb,0x22,0xb7,0xfb,0x4f,0x70,0x72,0x30,0xf1,0xfb,0x24,0xfb,0xbd,0x14,0x98,0x04,0x5d,0x97,0xbd,0xa3,0x93,0xbf,0xd1,0xed,0xd5,0x0e,0xd1,0xbe,0x5c,0x1e,0x1e,0x5b,0x21,0x3b,0x39,0xfd,0xc7,0x7f,0x01,0x29,0xff,0x13,0x14,0x83,0x52,0x01,0x00 +}; + + + +/* Style */ +String style = + ""; + +/* Server Index Page */ +String serverIndex = + "" + "
" + "

Pedal Firmware Update

" + "" + "" + "" + "

" + "
" + "

" + "" + + style; + + +void onJavaScript(void) +{ + Serial.println("onJavaScript(void)"); + server.setContentLength(jquery_min_js_v3_2_1_gz_len); + server.sendHeader(F("Content-Encoding"), F("gzip")); + server.send_P(200, "text/javascript", jquery_min_js_v3_2_1_gz, jquery_min_js_v3_2_1_gz_len); +} + +void ota_wifi_initialize(char* APhostname) +{ + + IPAddress local_ip(192, 168, 2, 1); + IPAddress local_mask(255,255,255,0); + IPAddress gateway(192, 168, 2, 1); + //Serial.begin(115200); + WiFi.softAP(APhostname,password); + WiFi.softAPConfig(local_ip,gateway,local_mask); + Serial.println(""); + Serial.print("AP set to "); + Serial.println(APhostname); + Serial.print("IP address: "); + Serial.println(WiFi.softAPIP()); + Serial.print("wifi password: "); + Serial.println(password); + + + // Connect to WiFi network + //WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + // while (WiFi.status() != WL_CONNECTED) + // { + // delay(500); + // Serial.print("."); + // } + //Serial.println(""); + //Serial.print("Connected to "); + //Serial.println(host_name); + //Serial.print("IP address: "); + //Serial.println(WiFi.localIP()); + + /*use mdns for host name resolution*/ + /* + if (!MDNS.begin(host_name)) + { //http://esp32.local + Serial.println("Error setting up MDNS responder!"); + while (1) + { + delay(1000); + } + } + Serial.println("mDNS responder started"); + */ + + /*return javascript jquery */ + server.on("/jquery.min.js", HTTP_GET, onJavaScript); + + /*return index page which is stored in serverIndex */ + server.on("/", HTTP_GET, []() + { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", serverIndex); }); + /* + server.on("/serverIndex", HTTP_GET, []() + { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", serverIndex); }); + */ + /*handling uploading firmware file */ + server.on( + "/update", HTTP_POST, []() + { + server.sendHeader("Connection", "close"); + server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + ESP.restart(); }, + []() + { + HTTPUpload &upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) + { + Serial.printf("Update: %s\n", upload.filename.c_str()); + Serial.println("Please don't move the Pedal"); + if (!Update.begin(UPDATE_SIZE_UNKNOWN)) + { // start with max available size + Update.printError(Serial); + } + } + else if (upload.status == UPLOAD_FILE_WRITE) + { + /* flashing firmware to ESP*/ + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) + { + Update.printError(Serial); + } + } + else if (upload.status == UPLOAD_FILE_END) + { + if (Update.end(true)) + { // true to set the size to the current progress + Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + } + else + { + Update.printError(Serial); + } + } + }); + server.begin(); + +} \ No newline at end of file diff --git a/ESP32/test/README b/ESP32/test/README new file mode 100644 index 00000000..9b1e87bc --- /dev/null +++ b/ESP32/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/Helper/bins/esp32/boot_app0.bin b/Helper/bins/esp32/boot_app0.bin new file mode 100644 index 00000000..13562cab Binary files /dev/null and b/Helper/bins/esp32/boot_app0.bin differ diff --git a/Helper/bins/esp32_speedcrafter_PCB_V1p4/boot_app0.bin b/Helper/bins/esp32_speedcrafter_PCB_V1p4/boot_app0.bin new file mode 100644 index 00000000..13562cab Binary files /dev/null and b/Helper/bins/esp32_speedcrafter_PCB_V1p4/boot_app0.bin differ diff --git a/Helper/bins/esp32s3/boot_app0.bin b/Helper/bins/esp32s3/boot_app0.bin new file mode 100644 index 00000000..13562cab Binary files /dev/null and b/Helper/bins/esp32s3/boot_app0.bin differ diff --git a/Helper/zip/filelist_esp.txt b/Helper/zip/filelist_esp.txt new file mode 100644 index 00000000..b0c748fe --- /dev/null +++ b/Helper/zip/filelist_esp.txt @@ -0,0 +1,4 @@ +${{ github.workspace }}/ESP32/.pio/build/esp32/firmware.bin +${{ github.workspace }}/ESP32/.pio/build/esp32/bootloader.bin +${{ github.workspace }}/ESP32/.pio/build/esp32/partitions.bin +./Helper/bins/esp32/boot_app0.bin \ No newline at end of file diff --git a/Helper/zip/filelist_esp32_speedcrafter.txt b/Helper/zip/filelist_esp32_speedcrafter.txt new file mode 100644 index 00000000..7b967b98 --- /dev/null +++ b/Helper/zip/filelist_esp32_speedcrafter.txt @@ -0,0 +1,4 @@ +${{ github.workspace }}/ESP32/.pio/build/esp32-speedcrafter/firmware.bin +${{ github.workspace }}/ESP32/.pio/build/esp32-speedcrafter/bootloader.bin +${{ github.workspace }}/ESP32/.pio/build/esp32-speedcrafter/partitions.bin +./Helper/bins/esp32_speedcrafter_PCB_V1p4/boot_app0.bin \ No newline at end of file diff --git a/Helper/zip/filelist_esp32s3.txt b/Helper/zip/filelist_esp32s3.txt new file mode 100644 index 00000000..4e7d6270 --- /dev/null +++ b/Helper/zip/filelist_esp32s3.txt @@ -0,0 +1,4 @@ +${{ github.workspace }}/ESP32/.pio/build/esp32s3usbotg/firmware.bin +${{ github.workspace }}/ESP32/.pio/build/esp32s3usbotg/bootloader.bin +${{ github.workspace }}/ESP32/.pio/build/esp32s3usbotg/partitions.bin +./Helper/bins/esp32s3/boot_app0.bin \ No newline at end of file diff --git a/README.md b/README.md index c3d75ff7..40116f49 100644 --- a/README.md +++ b/README.md @@ -107,8 +107,7 @@ It is recommended to use a Schottky diode in the positive line from the PSU to t ## Power PCB -Depending on the load direction, the servo will act as a generator. It will produce an additional current flow from the servo to the PSU which could trigger the over-voltage protection from the PSU and the servo. To prevent the reverse current flow to the PSU and thus prevent over-voltage protection from the PSU, a Schottky diode was added to the power line. To prevent the trigger of the over-voltage protection from the servo a large capacitor was added in the power-line. - +Depending on the load direction, the servo will act as a generator. It will produce an additional current flow from the servo to the PSU which could trigger the over-voltage protection from the PSU and the servo. To prevent the reverse current flow to the PSU and thus prevent over-voltage protection from the PSU, a Schottky diode was added to the power line. To prevent the trigger of the over-voltage protection from the servo a large capacitor was added in the power-line. The other power circuit with brake-resistor can be found [Here](https://github.com/tcfshcrw/Brake_resistor_Control_Circuit). | Component | Link | :------------------------- | :------------------------- | SR5100 Schottky diode | [Amazon.de](https://www.amazon.de/Packung-20-SR5100-Schottky-Barriere-Gleichrichterdioden-DO-201AD/dp/B079KK7QL5/ref=sr_1_3?keywords=sr+5100+diode&qid=1691820234&sr=8-3) | diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/0ae0c0d4-3c74-40ea-a182-800012b5176d.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/0ae0c0d4-3c74-40ea-a182-800012b5176d.vsidx new file mode 100644 index 00000000..1d0cb225 Binary files /dev/null and b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/0ae0c0d4-3c74-40ea-a182-800012b5176d.vsidx differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/597b5af1-bc02-422e-8eb6-33885e73db26.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/597b5af1-bc02-422e-8eb6-33885e73db26.vsidx deleted file mode 100644 index b3409e90..00000000 Binary files a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/597b5af1-bc02-422e-8eb6-33885e73db26.vsidx and /dev/null differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/5fd9a84a-93f2-4915-88a0-1c4b8d0fe17f.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/5fd9a84a-93f2-4915-88a0-1c4b8d0fe17f.vsidx deleted file mode 100644 index e50dbfe6..00000000 Binary files a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/5fd9a84a-93f2-4915-88a0-1c4b8d0fe17f.vsidx and /dev/null differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/8ae5801f-a5d4-4ea0-ac91-00ea7387f82a.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/8ae5801f-a5d4-4ea0-ac91-00ea7387f82a.vsidx new file mode 100644 index 00000000..1d0cb225 Binary files /dev/null and b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/8ae5801f-a5d4-4ea0-ac91-00ea7387f82a.vsidx differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/aa0985f9-44a4-4984-aad9-947859249234.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/aa0985f9-44a4-4984-aad9-947859249234.vsidx deleted file mode 100644 index e50dbfe6..00000000 Binary files a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/aa0985f9-44a4-4984-aad9-947859249234.vsidx and /dev/null differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/db79079c-1386-442c-a771-085bea39fee8.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/db79079c-1386-442c-a771-085bea39fee8.vsidx deleted file mode 100644 index 2fcf650e..00000000 Binary files a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/db79079c-1386-442c-a771-085bea39fee8.vsidx and /dev/null differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/e55d43fa-1368-48bf-b28b-a8910ca2c1e1.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/e55d43fa-1368-48bf-b28b-a8910ca2c1e1.vsidx new file mode 100644 index 00000000..1d0cb225 Binary files /dev/null and b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/e55d43fa-1368-48bf-b28b-a8910ca2c1e1.vsidx differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/efa52a82-ce37-4e80-bf01-4b9c5e26eb0b.vsidx b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/efa52a82-ce37-4e80-bf01-4b9c5e26eb0b.vsidx new file mode 100644 index 00000000..1d0cb225 Binary files /dev/null and b/SimHubPlugin/.vs/User.PluginSdkDemo/FileContentIndex/efa52a82-ce37-4e80-bf01-4b9c5e26eb0b.vsidx differ diff --git a/SimHubPlugin/.vs/User.PluginSdkDemo/v17/.suo b/SimHubPlugin/.vs/User.PluginSdkDemo/v17/.suo index 7c6e2000..1f9a67c7 100644 Binary files a/SimHubPlugin/.vs/User.PluginSdkDemo/v17/.suo and b/SimHubPlugin/.vs/User.PluginSdkDemo/v17/.suo differ diff --git a/SimHubPlugin/DataPluginDemo.cs b/SimHubPlugin/DataPluginDemo.cs index ea4cf488..cd066170 100644 --- a/SimHubPlugin/DataPluginDemo.cs +++ b/SimHubPlugin/DataPluginDemo.cs @@ -23,7 +23,7 @@ static class Constants { // payload revisiom - public const uint pedalConfigPayload_version = 134; + public const uint pedalConfigPayload_version = 136; // pyload types @@ -119,8 +119,9 @@ public struct payloadPedalConfig public Int16 lengthPedal_d; public Int16 lengthPedal_c_horizontal; public Int16 lengthPedal_c_vertical; - - + public Int16 lengthPedal_travel; + + public byte Simulate_ABS_trigger; //simulateABS public byte Simulate_ABS_value; //simulated ABS value public byte RPM_max_freq; @@ -194,6 +195,9 @@ public struct payloadPedalConfig // OTA update flag public byte OTA_flag; + // OTA update flag + public byte enableReboot_u8; + } public struct payloadFooter @@ -271,6 +275,7 @@ public class DIY_FFB_Pedal : IPlugin, IDataPlugin, IWPFSettingsV2 public uint overlay_display = 0; public string simhub_theme_color = "#7E87CEFA"; public uint debug_value = 0; + public bool Rudder_enable_flag=false; @@ -304,6 +309,15 @@ public class DIY_FFB_Pedal : IPlugin, IDataPlugin, IWPFSettingsV2 new SerialPort("COM7", 921600, Parity.None, 8, StopBits.One), new SerialPort("COM7", 921600, Parity.None, 8, StopBits.One)}; + //for (byte pedalIdx_lcl = 0; pedalIdx_lcl< 3; pedalIdx_lcl++) + //{ + // _serialPortt[pedalIdx_lcl].RtsEnable = false; + // _serialPort[pedalIdx_lcl].DtrEnable = true; + //} + + + + @@ -1421,6 +1435,12 @@ public void Init(PluginManager pluginManager) // prepare serial port interfaces for (uint pedalIdx = 0; pedalIdx < 3; pedalIdx++) { + + _serialPort[pedalIdx].Handshake = Handshake.None; + _serialPort[pedalIdx].RtsEnable = false; + _serialPort[pedalIdx].DtrEnable = false; + + if (_serialPort[pedalIdx].IsOpen) { System.Threading.Thread.Sleep(300); @@ -1436,7 +1456,7 @@ public void Init(PluginManager pluginManager) } //try connect back to com port - if (Settings.auto_connect_flag == 1) + if (Settings.auto_connect_flag[pedalIdx] == 1) { if (Settings.connect_status[pedalIdx] == 1) @@ -1601,6 +1621,7 @@ public void Init(PluginManager pluginManager) dap_config_initial_st.payloadPedalConfig_.spindlePitch_mmPerRev_u8 = 5; dap_config_initial_st.payloadPedalConfig_.pedal_type = 0; dap_config_initial_st.payloadPedalConfig_.OTA_flag = 0; + dap_config_initial_st.payloadPedalConfig_.enableReboot_u8 = 1; diff --git a/SimHubPlugin/DataPluginDemoSettings.cs b/SimHubPlugin/DataPluginDemoSettings.cs index ae7d3aad..6b416a9d 100644 --- a/SimHubPlugin/DataPluginDemoSettings.cs +++ b/SimHubPlugin/DataPluginDemoSettings.cs @@ -17,7 +17,7 @@ public class DataPluginDemoSettings public uint[] connect_flag = new uint[3] { 0, 0, 0 }; public uint RPM_effect_type = 0; public uint table_selected = 0; - public int auto_connect_flag = 0; + public int[] auto_connect_flag = new int[3] { 0,0,0}; public int[] selectedComPortNamesInt = new int[3] { -1, -1, -1 }; public int[] ABS_enable_flag = new int[3] { 0, 0, 0 }; public int[] RPM_enable_flag = new int[3] { 0, 0, 0 }; @@ -32,10 +32,10 @@ public class DataPluginDemoSettings public string Road_impact_bind = ""; public int WS_trigger = 30; public string[] Profile_name = new string[6] { "", "", "", "", "", "" }; - public int[] Pedal_travel = new int[3] { 40, 40, 40 }; public double kinematicDiagram_zeroPos_OX = 100; public double kinematicDiagram_zeroPos_OY = 20; public double kinematicDiagram_zeroPos_scale = 1.5; + public bool[] RTSDTR_False = new bool[3] { true,true,true}; } diff --git a/SimHubPlugin/SettingsControlDemo.xaml b/SimHubPlugin/SettingsControlDemo.xaml index 4fb68468..7b3bc9bf 100644 --- a/SimHubPlugin/SettingsControlDemo.xaml +++ b/SimHubPlugin/SettingsControlDemo.xaml @@ -765,9 +765,7 @@ - - - + @@ -775,16 +773,16 @@ - + - - + +