diff options
author | jaseg <git@jaseg.net> | 2020-10-11 23:31:12 +0200 |
---|---|---|
committer | jaseg <git@jaseg.net> | 2020-10-11 23:31:12 +0200 |
commit | 226fef1618dd9a37c050bb79a24c77396cd14c46 (patch) | |
tree | ed1d41cd5268e607ecd2abe8e628a1e796fda838 /fw | |
parent | 0e25682ed73ead8da26ea8659492bd5bce3cfe64 (diff) | |
download | usb-remote-226fef1618dd9a37c050bb79a24c77396cd14c46.tar.gz usb-remote-226fef1618dd9a37c050bb79a24c77396cd14c46.tar.bz2 usb-remote-226fef1618dd9a37c050bb79a24c77396cd14c46.zip |
Initial fw commit
Diffstat (limited to 'fw')
-rw-r--r-- | fw/.gitignore | 11 | ||||
-rw-r--r-- | fw/Makefile | 104 | ||||
-rw-r--r-- | fw/Scope.ipynb | 2615 | ||||
-rw-r--r-- | fw/base.c | 25 | ||||
-rw-r--r-- | fw/cmsis_exports.c | 48 | ||||
-rw-r--r-- | fw/global.h | 65 | ||||
-rw-r--r-- | fw/i2c.c | 236 | ||||
-rw-r--r-- | fw/i2c.h | 107 | ||||
-rw-r--r-- | fw/main.c | 97 | ||||
-rw-r--r-- | fw/mpu6050.c | 244 | ||||
-rw-r--r-- | fw/mpu6050.h | 491 | ||||
-rw-r--r-- | fw/openocd.cfg | 15 | ||||
-rw-r--r-- | fw/scope.gdb | 12 | ||||
-rw-r--r-- | fw/startup_stm32f030x6.s | 273 | ||||
-rw-r--r-- | fw/stm32_flash.ld | 136 | ||||
-rw-r--r-- | fw/system_stm32f0xx.c | 336 | ||||
-rw-r--r-- | fw/tools/gen_cmsis_exports.py | 30 |
17 files changed, 4845 insertions, 0 deletions
diff --git a/fw/.gitignore b/fw/.gitignore new file mode 100644 index 0000000..9ba4454 --- /dev/null +++ b/fw/.gitignore @@ -0,0 +1,11 @@ +*.elf +*.o +*.expand +*.hex +*.lst +*.map +*.bin +sources.c +sources.tar.xz +sources.tar.xz.zip +.ipynb_checkpoints diff --git a/fw/Makefile b/fw/Makefile new file mode 100644 index 0000000..07b7c13 --- /dev/null +++ b/fw/Makefile @@ -0,0 +1,104 @@ +# Megumin LED display firmware +# Copyright (C) 2018 Sebastian Götte <code@jaseg.net> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +CUBE_PATH ?= $(wildcard ~)/resource/STM32CubeF0 +CMSIS_PATH ?= $(CUBE_PATH)/Drivers/CMSIS +CMSIS_DEV_PATH ?= $(CMSIS_PATH)/Device/ST/STM32F0xx +HAL_PATH ?= $(CUBE_PATH)/Drivers/STM32F0xx_HAL_Driver + +MAC_ADDR ?= 0xdeadbeef + +CC := arm-none-eabi-gcc +LD := arm-none-eabi-ld +OBJCOPY := arm-none-eabi-objcopy +OBJDUMP := arm-none-eabi-objdump +SIZE := arm-none-eabi-size + +CFLAGS = -g -Wall -Wextra -std=gnu11 -O0 +CFLAGS += -mlittle-endian -mcpu=cortex-m0 -march=armv6-m -mthumb +#CFLAGS += -ffunction-sections -fdata-sections +LDFLAGS = -nostartfiles +#LDFLAGS += -specs=rdimon.specs -DSEMIHOSTING +LDFLAGS += -Wl,-Map=main.map -nostdlib +#LDFLAGS += -Wl,--gc-sections +LIBS = -lgcc +#LIBS += -lrdimon + +# Technically we're using an STM32F030F4, but apart from the TSSOP20 package that one is largely identical to the +# STM32F030*6 and there is no separate device header provided for it, so we're faking a *6 device here. This is +# even documented in stm32f0xx.h. Thanks ST! +CFLAGS += -DSTM32F030x6 -DHSE_VALUE=8000000 + +LDFLAGS += -Tstm32_flash.ld +CFLAGS += -I$(CMSIS_DEV_PATH)/Include -I$(CMSIS_PATH)/Include -I$(HAL_PATH)/Inc -Iconfig -Wno-unused -I../common +LDFLAGS += -L$(CMSIS_PATH)/Lib/GCC -larm_cortexM0l_math + +################################################### + +.PHONY: program clean + +all: main.elf + +cmsis_exports.c: $(CMSIS_DEV_PATH)/Include/stm32f030x6.h $(CMSIS_PATH)/Include/core_cm0.h + python3 tools/gen_cmsis_exports.py $^ > $@ + +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $^ +# $(CC) -E $(CFLAGS) -o $(@:.o=.pp) $^ + +%.o: %.s + $(CC) -c $(CFLAGS) -o $@ $^ +# $(CC) -E $(CFLAGS) -o $(@:.o=.pp) $^ + +sources.tar.xz: main.c Makefile + tar -caf $@ $^ + +# don't ask... +sources.tar.xz.zip: sources.tar.xz + zip $@ $^ + +sources.c: sources.tar.xz.zip + xxd -i $< | head -n -1 | sed 's/=/__attribute__((section(".source_tarball"))) =/' > $@ + +main.elf: main.c startup_stm32f030x6.s system_stm32f0xx.c $(HAL_PATH)/Src/stm32f0xx_ll_utils.c base.c cmsis_exports.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + $(OBJCOPY) -O ihex $@ $(@:.elf=.hex) + $(OBJCOPY) -O binary $@ $(@:.elf=.bin) + $(OBJDUMP) -St $@ >$(@:.elf=.lst) + $(SIZE) $@ + +program: main.elf openocd.cfg + openocd -f openocd.cfg -c "program $< verify reset exit" + +8b10b_test_encode: 8b10b_test_encode.c 8b10b.c + gcc -o $@ $^ + +8b10b_test_decode: 8b10b_test_decode.c 8b10b.c + gcc -o $@ $^ + +protocol_test: protocol.c protocol_test.c + gcc -o $@ -O0 -Wall -Wextra -g -I../common $^ + +clean: + rm -f **.o + rm -f main.elf main.hex main.bin main.map main.lst + rm -f **.expand + rm -f cmsis_exports.c + rm -f sources.tar.xz + rm -f sources.tar.xz.zip + rm -f sources.c + rm -f protocol_test + diff --git a/fw/Scope.ipynb b/fw/Scope.ipynb new file mode 100644 index 0000000..9f22f23 --- /dev/null +++ b/fw/Scope.ipynb @@ -0,0 +1,2615 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt\n", + "%matplotlib notebook\n", + "import struct\n", + "import math\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_data(offx=0, end=-1, dtype='h', channels=1):\n", + " with open('/tmp/scope_dump.bin', 'rb') as f:\n", + " raw_data = f.read()\n", + " data = struct.unpack(f'<{len(raw_data)//struct.calcsize(dtype)}{dtype}', raw_data)\n", + " \n", + " fig, axs = plt.subplots(channels, 1, squeeze=False, sharex=True, figsize=(10, 5))\n", + " for i, ax in enumerate(axs.flatten()):\n", + " ax.plot([math.nan if x==-255 else x for x in data[offx:][:end][i::channels]])\n", + " ax.grid()\n", + " \n", + " return data[offx:]" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " fig.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'] / mpl.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", + " var x1 = msg['x1'] / mpl.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * http://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys (original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object')\n", + " obj[key] = original[key]\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * mpl.ratio;\n", + " var y = canvas_pos.y * mpl.ratio;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event)});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value,\n", + " guiEvent: simpleKeys(event)});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " var width = fig.canvas.width/mpl.ratio\n", + " fig.root.unbind('remove')\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", + " fig.close_ws(fig, msg);\n", + "}\n", + "\n", + "mpl.figure.prototype.close_ws = function(fig, msg){\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width/mpl.ratio\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAH0CAYAAACuKActAAAgAElEQVR4nOzdeXTU9b3/8TnXCrb+Wu+5Pee29d5WXFCw1FuxWlfqUkWvlta2Kl5FrYpVq6JWa1jDEsIWFtkhhk0ISyBMFrLvZE/INtnInkxWMklmkkwyM8nM6/dHIDgkQIDknW/m+3qc8/lDCDI8GSCvmcl3NCAiIiIiIiKiUacZ7RtARERERERERBzoRERERERERIrAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAgU5ERERERESkABzoRERERERERArAga4ydrsder0eRqMRJpOJh4eHh4eHh4eHh0elx2g0Qq/Xw263j/ZMobM40FVGr9dDo9Hw8PDw8PDw8PDw8PBAo9FAr9eP9kyhszjQVcZoNPb/IbzwETSDwQBfX18YDIZRfzRPDYe92dzVD3uzuasf9mZzVz/s7fq9zz15ZzQa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\" width=\"1000\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "data = plot_data(offx=0, dtype='i', channels=3)\n", + "#print(''.join(str(x) for x in data[4:][3::4]))" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65535.99494457225, 4294966633.375)" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import math\n", + "math.sqrt(statistics.mean(data[::3])), statistics.mean(data[::3])" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x7d1cc8de6438>]" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plt.plot([x-42465024 for x in data])" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "33552384.03125" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(0x7fff/2)**2 / 8" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "32767" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0x7fff" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "33554432.0" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "16384**2 / 8" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " fig.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'] / mpl.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", + " var x1 = msg['x1'] / mpl.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * http://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys (original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object')\n", + " obj[key] = original[key]\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * mpl.ratio;\n", + " var y = canvas_pos.y * mpl.ratio;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event)});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value,\n", + " guiEvent: simpleKeys(event)});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " var width = fig.canvas.width/mpl.ratio\n", + " fig.root.unbind('remove')\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", + " fig.close_ws(fig, msg);\n", + "}\n", + "\n", + "mpl.figure.prototype.close_ws = function(fig, msg){\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width/mpl.ratio\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4nOzde1zUVf4/8CkETDN369eWdhnLvLCZbdq67W7lppW2We21tjVz225Wltbut0FNyRTUSmsttTI1TUXLS9mAKCiCAuIFEBQUlauAeIEZEJmBmXn9/pg5h/kwgwIfLnN5PR+P83jo5zMzfGbmzJz3nMv7aEBEREREfkXT2RdARERERB2LASARERGRn2EASERERORnGAASERER+RkGgERERER+hgEgERERkZ9hAEhERETkZxgAEhEREfkZBoBEREREfoYBIBEREZGfYQBIRERE5GcYABIRERH5GQaARERERH6GASARERGRn2EASERERORnGAASERER+RkGgERERER+hgEgERERkZ9hAEhERETkZxgAEhEREfkZBoBEREREfoYBIBEREZGfYQBIRERE5GcYABIRERH5GQaARERERH6GASARERGRn2EASERERORnGAASERER+RkGgERERER+hgEgERERkZ9hAEhERETkZxgAEhEREfkZBoBEREREfoYBIBEREZGfYQBIRERE5GcYABIRERH5GQaARERERH6GASARERGRn2EASERERORnGAASERER+RkGgERERER+hgEgERERkZ9hAEhERETkZxgAEhEREfkZBoBEREREfoYBIBEREZGfYQBIRERE5GcYABIRERH5GQaARERERH6GASARERGRn2EASERERORnGAASERER+RmfCQATEhIwZswY9OrVCxqNBps3b77sfXbt2oUhQ4YgODgYt912G5YsWdIBV0pERETUuXwmAIyOjsa0adOwcePGZgWAeXl56NatGyZNmoTs7GwsXboUgYGB2LBhQwddMREREVHn8JkA0FlzAsB3330XAwcOVBx79dVXcd9997XnpRERERF1Or8NAB944AG89dZbimObNm1Cly5dUFdX156XR0RERNSp/DYA7NevH8LDwxXHkpKSoNFoUFpa6vY+JpMJRqNRlsrKSpw8eRIGg0FxnIWFhYWFhcVzi8FgQHFxMaxWa5vFHt7GrwPAiIgIxbE9e/ZAo9GgrKzM7X3CwsKg0WhYWFhYWFhYfKAUFxe3Wezhbfw2AGzNEHDjHsCioiJZgTr71wwLCwsLCwtL80pxcTE0Gg0MBkObxR7exm8DwHfffRchISGKYxMmTGjRIhCj0QiNRgOj0diq6yQiIqKOx/bbhwLA6upqpKenIz09HRqNBgsWLEB6ejoKCwsBAKGhoRg3bpy8vUgD8/bbbyM7OxvLli1rcRoYViAiIiLvw/bbhwLA+Ph4t+P748ePBwCMHz8ew4cPV9xn165duOeeexAUFIQ+ffq0OBE0KxAREZH3YfvtQwFgZ2AFIiIi8j5svxkAqsIKRERE5H3YfjMAVIUViIiIyPuw/WYAqAorEBERkfdh+80AUBVWICIiIu/D9psBoCqsQERERN6H7TcDQFVYgYiIiLwP228GgKqwAhEREXkftt8MAFVhBSIiIvI+bL8ZAKrCCkREROR92H4zAFSFFYiIiMj7sP1mAKgKKxAREZH3YfvNAFAVViAiIiLvw/abAaAqrEBERETeh+03A0BVWIGIiIi8D9tvBoCqsAIRERF5H7bfDABVYQUiIiLyPmy/GQCqwgpERETkfdh+MwBUhRWIiIjI+7D9ZgCoCisQERGR92H7zQBQFVYgIiIi78P2mwGgKqxARERE3oftNwNAVViBiIiIvA/bbwaAqrACEREReR+23wwAVWEFIiIi8j5svxkAqsIKRERE5H3YfjMAVIUViIiIyPuw/WYAqAorEBERkfdh+80AUBVWICIiIu/D9psBoCqsQERERN6H7TcDQFVYgYiIiLwP228GgKqwAhEREXkftt8MAFVhBSIiIvI+bL8ZAKrCCkREROR92H4zAFSFFYiIiMj7sP1mAKgKKxAREZH3YfvNAFAVViAiIiLvw/abAaAqrEBERETeh+03A0BVWIGIiIi8D9tvBoCqsAIRERF5H7bfDABVYQUiIiLyPmy/GQCqwgpERETkfdh+MwBUhRWIiIjI+7D9ZgCoCisQERGR92H77WMB4KJFi9CnTx8EBwdjyJAhSExMbPK2K1asgEajcSm1tbXN/nusQERERN6H7bcPBYDr1q1DYGAgli5diuzsbEyaNAndu3dHYWGh29uvWLEC11xzDcrKyhSlJViBiIiIvA/bbx8KAIcNG4YJEyYojg0cOBChoaFub79ixQr07NlT1d9kBSIiIvI+bL99JAA0m80ICAjApk2bFMffeustPPjgg27vs2LFCgQEBODWW2/FTTfdhMcffxxpaWkt+rusQERERN6H7bePBIAlJSXQaDRISkpSHA8PD0f//v3d3iclJQXffvstMjIykJiYiL/+9a+46qqrkJub2+TfMZlMMBqNshQXF/t9BSIiIvI2DAB9LABMTk5WHJ89ezYGDBjQrMewWq24++678eabbzZ5m7CwMLcLR/y5AhEREXkbBoA+EgC2ZgjYnZdeegmjR49u8jx7AImIiLwfA0AfCQAB+yKQ1157TXEsJCSkyUUgjdlsNtx777144YUXmv03WYGIiIi8D9tvHwoARRqYZcuWITs7G5MnT0b37t1RUFAAABg3bpwiGHz//fcRExODkydPIj09HS+88AK6dOmC1NTUZv9NViAiIiLvw/bbhwJAwJ4IWqvVIigoCEOGDEFCQoI8N3z4cIwfP17+f/Lkybj11lsRFBSE66+/Ho8++qjLHMLLYQUiIiLyPmy/fSwA7GisQERERN6H7TcDQFVYgYiIiLwP228GgKqwAhEREXkftt8MAFVhBSIiIvI+bL8ZAKrCCkREROR92H4zAFSFFYiIiMj7sP1mAKgKKxAREZH3YfvNAFAVViAiIiLvw/abAaAqrEBERETeh+03A0BVWIGIiIi8D9tvBoCqsAIRERF5H7bfDABVYQUiIiLyPmy/GQCqwgpERETkfdh+MwBUhRWIiIjI+7D9ZgCoCisQERGR92H7zQBQFVYgIiIi78P2mwGgKqxARERE3oftNwNAVViBiIiIvA/bbwaAqrACEREReR+23wwAVWEFIiIi8j5svxkAqsIKRERE5H3YfjMAVIUViIiIyPuw/WYAqAorEBERkfdh+80AUBVWICIiIu/D9psBoCqsQERERN6H7TcDQFVYgYiIiLwP228GgKqwAhEREXkftt8MAFVhBSIiIvI+bL8ZAKrCCkREROR92H4zAFSFFYiIiMj7sP1mAKgKKxAREZH3YfvNAFAVViAiIiLvw/abAaAqrEBERETeh+03A0BVWIGIiIi8D9tvBoCqsAIRERF5H7bfDABVYQUiIiLyPmy/GQCqwgpERETkfdh+MwBUhRWIiIjI+7D9ZgCoCisQERGR92H7zQBQFVYgIiIi78P2mwGgKqxARERE3oftNwNAVViBiIiIvA/bbwaAqrACEREReR+23wwAVWEFIiIi8j5svxkAqsIKRERE5H3YfvtYALho0SL06dMHwcHBGDJkCBITEy95+w0bNiAkJARBQUEICQnBpk2bWvT3WIGIiIi8D9tvHwoA161b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\" width=\"640\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x7d1cc934e0f0>]" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import statistics\n", + "fig, (ax1, ax2, ax3) = plt.subplots(3, 1)\n", + "ndata = np.array(data)\n", + "w = 16\n", + "#var = [ statistics.variance(list(ndata[i:i+w])) for i in range(0, len(ndata)-w) ]\n", + "mean = [ statistics.mean(list(ndata[i:i+w])) for i in range(0, len(ndata)-w) ]\n", + "#ax1.plot(var)\n", + "ax2.plot(mean)\n", + "ax3.plot([\n", + " (ndata[i] - statistics.mean(ndata[(i-w) - (i-w)%w : i - i%w]))**2 for i in range(w, len(ndata))\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " fig.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'] / mpl.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", + " var x1 = msg['x1'] / mpl.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * http://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys (original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object')\n", + " obj[key] = original[key]\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (name === 'button_press')\n", + " {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * mpl.ratio;\n", + " var y = canvas_pos.y * mpl.ratio;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event)});\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "}\n", + "\n", + "mpl.figure.prototype.key_event = function(event, name) {\n", + "\n", + " // Prevent repeat events\n", + " if (name == 'key_press')\n", + " {\n", + " if (event.which === this._key)\n", + " return;\n", + " else\n", + " this._key = event.which;\n", + " }\n", + " if (name == 'key_release')\n", + " this._key = null;\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.which != 17)\n", + " value += \"ctrl+\";\n", + " if (event.altKey && event.which != 18)\n", + " value += \"alt+\";\n", + " if (event.shiftKey && event.which != 16)\n", + " value += \"shift+\";\n", + "\n", + " value += 'k';\n", + " value += event.which.toString();\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, {key: value,\n", + " guiEvent: simpleKeys(event)});\n", + " return false;\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message(\"toolbar_button\", {name: name});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " function ondownload(figure, format) {\n", + " window.open(figure.imageObj.src);\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " ondownload,\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + " if (!fig.cell_info) {\n", + " console.error(\"Failed to find cell for figure\", id, fig);\n", + " return;\n", + " }\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " var width = fig.canvas.width/mpl.ratio\n", + " fig.root.unbind('remove')\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", + " fig.close_ws(fig, msg);\n", + "}\n", + "\n", + "mpl.figure.prototype.close_ws = function(fig, msg){\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "}\n", + "\n", + "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width/mpl.ratio\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i<ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code'){\n", + " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "<IPython.core.display.Javascript object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4nOydeXgUZb62S5iAMgrnwHCUwGV0BNcIM+q4jSMii5xDUM8sn3pghplREJ3gMo4T9ghIQGVTEQUEBCWLEmhICEtCQliysCRA2LeQhCQQOqGzd0LSz/dHU9XV6eol6Wq6OvXc1/W7CFXV1fV013L3W1VvCSCEEEIIIbpC8PcCEEIIIYSQGwsFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AACSGEEEJ0BgWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFkBBCCCFEZ1AAvaC5uRlFRUUwmUyorKxksVgsFosVAGUymVBUVITm5mZ/q4TfoAB6QVFREQRBYLFYLBaLFYBVVFTkb5XwGxRALzCZTNIKpPavE6PRiOjoaBiNRr//UrqRpdfczM7szO7/5WF2N3WxBJWb0+zrYknrX9PaefigxAYck8nkb5XwGxRAL6isrIQgCKisrFR93o2NjTAYDGhsbFR93lpGr7kBZmd2ZtcTAZm9qgbYud++qmpa/5rWzsMH+PL4HShQAL2AAqg+es0NMDuzM7ueCMjsFMB2BQXQCyiA6qPX3ACzMzuz64mAzE4BbFdQAL2AAqg+es0NMDuzM7ueCMjsFMB2BQXQCyiA6qPX3ACzMzuz6wmtZ/9293n8M+4QmpsttoEUwHYFBdALKIDqo9fcALMzO7PrCa1nD4lIREhEInaeKrMNpAC2KyiAXkABVB+95gaYndmZXU9oPbsogElHSmwDKYDtCgqgF1AA1UevuQFmZ3Zm1xNaz04BbP9QAL2AAqg+es0NMDuzM7ue0Hp2CmD7hwLoBRRA9dFrboDZmZ3Z9YTWs4sCuCWPAtheoQB6AQVQffSaG2B2Zmd2PaH17BTA9g8F0AsogOqj19wAszM7s+sJrWenALZ/KIBeQAFUH73mBpid2ZldT2g9e1sEcE1GPp6eswPnr9iGxaSfxlORSTiVkEkB1BgUQC+gAKqPXnMDzM7szK4ntJzdYrHIBLDUNsKNAIqv+fOKbIdhL0ZtpQBqDAqgF1AA1UevuQFmZ3Zm1xNazt7U7J0Avro002HYCx+7F8CGa82ob2y6IRkpgBRAr6AAqo9ecwPMzuzMrie0nL2xqVl1ARw2y70A/ubjZPxy0uYbIoEUQAqgV1AA1UevuQFmZ3Zm1xNazl7f2GQngBbL9ecBeyiAryzNcBg2ZNYWtwIoTnus2PdSRgGkAHoFBVB99JobYHZmZ3Y9oeXsdQ02AVyUfBqPztqO7zMvAFU1OL4pA6bk7FYL4KCZrgVQft3h0WKTzzNSACmAXkEBVB+95gaYndmZXU9oOXu1+ZokY/Laf6wYIRGJeOYjm8xZKquxLP0cMs8Zpen+3zeOAvjcDNcCKL/ukAJ4Y9CkAKanpyMsLAy9evWCIAjYsGGDwzTHjx/HyJEj0bVrV9x666144oknUFBQII03m80IDw9Hjx490KVLF4wcORJFRUV28ygoKEBYWBi6dOmCHj16YMKECWhoaPB4OSmA6qPX3ACzMzuz6wktZ6+sb1QUwImxB6W/RXnbsi/fYbo/KQjgs24EUH7dYd5FE8prPD8WtykjBVCbApiUlIQpU6YgPj5eUQDPnj2L7t2748MPP0ROTg7OnTuHxMREXL58WZpm/Pjx6N27N5KTk5GTk4NBgwZhwIABaGqyXlza1NSE0NBQDBo0CDk5OUhOTkZwcDDCw8M9Xk4KoProNTfA7MzO7HpCy9mv1jYoCuAbKzIdBHDJ1uMeCeBvP0pyKYDy6w5nJhxDSEQiPt16wmcZKYAaFUA5SgL4yiuvYPTo0U5fYzKZEBQUhNjYWGlYcXExOnTogK1btwKwSmaHDh1QXFwsTRMTE4POnTt7vEJQANVHr7kBZmd2ZtcTWs5eXqMsgC99nu4ggF9tPeaRAD4d6VoA5dcdystXUAADUACbm5tx6623YubMmRg2bBh69uyJxx9/3G6aHTt2QBAEVFRU2M2rf//+mD59OgBg2rRp6N+/v934iooKCIKA1NRUj5aNAqg+es0NMDuzM7ue0HL2siqzooz9NirFQQAXb/FMAJ+Y7loAa5xcd+grKIABKIClpaUQBAFdunTBggULkJubizlz5uCmm27Czp07AQBr165Fp06dHOY1dOhQjBs3DgAwduxYDB061GGaTp06ITo6WnFZzGYzKisrpSoqKoIgCDAajWhsbFS1amtrYTAYUFtbq/q8tVx6zc3szM7s/l8eZrfWxfJqRRm7d0qSgwB+vjnPYbo/LNkjzUsc9hsXAthYYUJ5VZ3ie/oqo9FopAD6ewHc0VIAi4uLIQgCXnvtNbvpRo4ciVdffRWAcwEcMmQI3nzzTQBWARw2bJjDNEFBQYiJiVFclsjISAiC4FDR0dEwGAwsFovFYgV8fRdnUJQxu5a56/L2/hLHcc9/nCDNSxz26DTnApiWsBnR65Tf01cZo6OjKYD+XgB3tBTAhoYG/OxnP8OsWbPspvv3v/+Np59+GoDvTgGzBdD3pdfczM7szO7/5WF2a10oq/RYABcmHvGoBfDXUze7bAEsM9WyBfAGE3ACCABPPfWUw00gL7/8stQqKN4EEhcXJ40vKSlRvAmkpKREmiY2NpY3gfgZveYGmJ3ZmV1PaDl7YbmyjCkJ4OebjzqM++PXe6V5icMGuBBAVNU4vfHEV/AaQI0KYHV1NXJzc5GbmwtBEKRr/cR+/tavX4+goCAsW7YMZ86cwZdffomOHTti9+7d0jzGjx+PPn36ICUlBTk5OXj++ecVu4EZPHgwcnJykJKSgj59+rAbGD+j19wAszM7s+sJLWe/YKzxWAAXeSiAD09xLYDObjzxFRRAjQpgWlqa4rV2Y8aMkaZZsWIF+vbti5tvvhkDBgyAwWCwm0d9fT3Cw8PRvXt33HLLLQgLC0NhYaHdNAUFBRgxYgRuueUWdO/eHeHh4TCbzR4vJwVQffSaG2B2Zmd2PaHF7BU1DZiVcAxb8kpacQrYUQD/sMRRAB+abC+Acd/vwprV6ZIAXq6spwDeYDQpgIECBVB99JobYHZmZ3Y9ocXsb6454Fb8WgrggkSlu4D34nJlvV3nzg/IBLAqOVsabkrOBqpqUGqiAN5oKIBeQAFUH73mBpid2ZldT2gx+yMzt7daAOcnOArgk9f7C3x0lm1+902yveZ0gu2JIle3WwWw+KpyNzC+ggJIAfQKCqD66DU3wOzMzux6QovZH5y2pdUCOG+TowAqVb+JttfsWrdHGl5+XQCLKpRvPPEVFEAKoFdQANVHr7kBZmd2ZtcTWsz+y0mbWy2An21y7AZGqe6RCeCPP+yShl/ckoUdBwtwsrSKAniDoQB6AQVQffSaG2B2Zmd2PaHF7J7Kn1wAP93omQDeLXvNF9+mScPfmJ+MkIhEvLAwnQJ4g6EAegEFUH30mhtgdmZndj2hxeytEcClK3fi2RlbMGHNPo9fc3FLFkzJ2Zi4eIfnoukjKIAUQK+gAKqPXnMDzM7szK4ntJi9NQLY1rp3UiLCFyVTADUABdALKIDqo9fcALMzO7PrCS1mb4vQPTpzW6tf88e5nr/GV1AAKYBeQQFUH73mBpi9tdnTTl5GUUVt696nqRlbj5aioqbB6TQ7T5WhsLx18/UGfu/MrhXaIoCPz/K86xix7m7FtL6CAkgB9AoKoProNTfA7PLsZVVmTIw/jCNFJsXpd5++0qYDxKLk0wiJsF5wrsTes22brzfwe2d2rXCjWgBbU76CAkgB9AoKoProNTfQvrLXNlzDqUtVTscnHi7Bbz5Oxv78cgCO2eVPJJCzOPUMnp6zAxPjjyiO35JnnW/2+XLF9x00L83lgWVh8ikK4A2E2bWVvS2CNuCjrRTAAIUC6AUUQPXRa26gfWUXu3TYc+aK3fAf9xci9cRlaef+0PStAGzZdxwrQey+Agyev1PxAODuANFyvi0Z9JlrAZy/nQJ4I2F2bWVvi6CFTve882gKoLagAHoBBVB99JobCPzsprpGvLo0EzHZBdLO+73YXGn8ubJqpzt4Mbs4rO/kzYoHAE8FsO/kzXg/LhcfbTpqN/45mQBea2pGtfma3fh52066PPBYLBb868dDmG7Ic/o5LE49g7+t2ofGpmb3HxoC/3v3BmbXTvbmZkubBK01Tw+hAGoLCqAXUADVR6+5gcDPPnfLCYed97g1+6XxWeeMHgugO8GTl8VicTleLmIDP02Vho/8cjdCIhJhrDZL4z/desLlgaew3Pa4qoZryoInjt98pMSjzy3Qv3dvYHbtZK8xX2uToPWb7PnTQyiA2oIC6AUUQPXRa24g8LNP2eD4RIDR32ZJ4/fnl7sUwA0b2iaAchFTGm+qs32ez8oEUKyfDhRJ4+USu+fMFfwz7pDd609fsj2u6nDRVbwXm4tzZdWKy7jpULFHn1ugf+/ewOzayX61tqFNgnb3RN/JHwXQt1AAvYACqD56zQ0EfvbpBseHwv/x672ob2zCwYIKtwIY30YBNNU2uhx/qbJeGq8kgGuzCqTxc5IcWzGnbDgijT9WXCkNv29qEkIiEvH0nB3S+GtNzdL4rUdLcajwKmob7E8ztyTQv3dvYHbtZC+vaZsA+rp8BQWQAugVFED10WtuILCybztaiu8zLwCwtuwtTj2DaQoC+D+f78LfV1kfFSW/c1dJAH+M91wA75K1OpSa6h3Gy+v8lRpp/O8+cRTA7/bmS+Ojko47jP+/5ZkoqqjFvG0nsePEJcX3qDFfw2dbT+LAhQpp2LsxOQiJSMTLX+1x+VkG0veuNsyunezGarPfZY8CeGOhAHoBBVB99Job0G72+sYmVJuv4XhJJUZ+uRs7T5VJO2d5i9gznzg+31N+162zOl5SiZcX78ZHKzZKw/pNTpL+rqhpwP9+tQer9pxXfP2Zy9UYt2Y/IjceVRx/tNgE87UmmOoaFZdx+a5zUtbZmx0F8E/fZOCxj62Prnp8tvIjrMRua0KnK3eJ4Qqtfu83AmbXRvb6xiacdXKTlr/LV1AAKYBeQQFUH73mBrSbXZSaJ6NSHHbOazLypb9FSZLXA7I7BIctSHe5o39gSoL09/1Tba/714+HXL5u+a5zLsfvzy/H03N24JeTNmPADMdOa79KO4MCYy025FzER5scJfLlr/b49CCm1e/9RsDs2siutG1rpXwFBZAC6BUUQPXRa25Am9nddQ3xt+und0Mi3Lf2vbjYtUj1n2YTQHnXEu4OTu/H5bocv/2Y8mlbscSngzh7r/9etIsC6COY3f/ZK+sb/S55FED/QAH0Agqg+ug1N6DN7OZrTS53zvIWtYEKN1jIa+iCnS7H/3amTQAfcnIqValGfOFa0JaknXU5Xum0r7zkrZFKLYgUwLbD7P7Pfrjoqt8ljwLoHyiAXkABVB+95ga0mb26FX2DKV1fZyd4c12P/02kTQDvnZLk8fv2czPt+7GuWwj/9E2G9Leza/ik95rs+XJRAN3D7P7Pbsi96HfJc1XNzRb3IdoABZAC6BUUQPXRa25Am9lb0zWEu1O1j8zc7nJ8/6kJHr9Xa8pdC6G8tbGtgkcBbBvM7v/sC2SPP9RiefpEndZCAaQAegUFUH30mhvQZvZLlfUe76jdnR6Vn0pVHD/ZNwLoroXwRpQrtPi93yiY3f/ZJ69X7p5JK1Xf2OST3BRACqBXUADVR6+5AW1mlz/6zNeidXeEbwRQC+UKLX7vNwpm91/2a03NaG62IGLdYb9vH66qxuy6I/W2QgGkAHoFBVB99Job0GZ2rfYNFmglf15xS7T4vd8omP3GZq9tuIb0U2Woa2jCoHlp+NPXGfhnnOtulvxdlfW++XwogBoUwPT0dISFhaFXr14QBAEbNmywGz9mzBgIgmBXTzzxhN00AwcOdJjmlVdesZumoqICo0ePRteuXdG1a1eMHj0aV69ebdWyUgDVR6+5AW1mP1Fa6fcDQHuo+sYmfLb1JFJPXnb4jF1979HZBViTkX8Dvmn/oMV1/kbRluyG3Iv4ZufZNr/n699Zu20S/w2JSMTbaw/6fftwVRU1DW3O6woKoAYFMCkpCVOmTEF8fLxTARw+fDhKS0ulKi8vt5tm4MCBGDt2rN00JpPJbprhw4cjNDQUGRkZyMjIQGhoKMLCwlq1rHoVwLqGJrvHb6mJlnP7mtp6M1bEqpO9vrEJJaY6r+dzpMjk9wNAe6jVsg6zW+Jsna9vtHXBY6w2e/U9Nlxrxu+X7MV0Q55X8wGsjwwbtiDdKxERaWxsxLexBtSbvT/IWywW5F+pcdna6ik/7i/EoHlpOFdW7fV7fZFyGsMX7YKpzv77Fb/3enMDLhhrnLzaHnF9OFla5dH0zl4vr9HfZvl9+3BVV7xc951BAdSgAMpxJoAvvfSSy9cNHDgQ7777rtPxx48fhyAIyMrKkoZlZmZCEAScPHnS4+XTqwA+df1uz8LyWtXnreXcvuavK6074u1Hi72el9gnn7MDmBxD7kXszy9XHHfgQrnfDwDtoeSn2VoiStCy9DN2F7ybam0d9BYYlbe1I0UmxO4rcCsiqScvO31/kT1nrmBLXonL+QD2z0t2RuLhEuw9e8XtvH7aX4CQiES8F5ujON5isSAmuwB5F02K4+XM33YSIRGJWJx6RnF8fWMTvtub7/SzlCPme21ZpuL4r9LOICQiEfO2uT9eiPP6IuW03XBxX/d+rPWZ0etzirDpUDEyzxkV5yPvlN3dZ2uxWLA2qwDHiu2PTUrr5otf7vb79uGqLlX6prGBAhigAtitWzf07NkT/fr1wxtvvIHLl+1PqwwcOBC/+MUv0KNHDzz44IP44IMPUFVl+8W0YsUKdOvWzeH9unXrhpUrVzpdHrPZjMrKSqmKioogCAKMRiMaGxtVrdraWsxfY8CoZRk4WXzV7fRmcwOq68xOx2/KLcKYFVm4bKpxOk11bb31vevNqKt3Pi9xw1y155zLZaqrN+PtHw5g6c4zrcptMBhQW1trN/xKZS3+ujILGw4WOl/+OjPM5ga7LEp1ptSE0cszsfvUJY+WydW8ss+VYfTyTBy7WOH29Ut3nsHbPxxw+tlKB52le90u08xNeYg0HHG6fOK85J99Q0ODw7SHCozStDtPlGL08kycu2yS5rv7lOunaLA8qz9/myn9nXT4Iv6yIgsFV6rw1vf7sTTtFB693gfidNl3Wmiskl4jX79KKqrx528zkXTY1n9byrESafzazPP4+6psVFTXIeVYCf78bSZW77U9Li/1eClGLc/E2UsmHCuqwKjlmcg+a3u+c6GxSprX/G0n8H5sDurqzYjJysffV2Xjwx9t/Squ21+Av67MQnlVLXadvITRyzOx43iJNF6+rjU0NEjrvri/GviprW9I+T5s/QHrfNdm5ivOq97cgHpzAxoaGjBl/WFEJR6z+7xbvm91nRmfXBfXB6Ztsd/n1JvR0NAg7UMaGmxdH434fJfLbTUkIhF19WbUerC/jEo8privE8ffNdE2z92nLuH/lmXiVMlVaX0w1dRJ49fsPY/RyzOx6+QllFdZ940/7S+Q5r0pt0jxs1BaN5/9xHUH7v6ugitVTj9bb8poNFIA/b0ArlASwNjYWCQmJiIvLw+bNm3CgAED8NBDD8FstjUTL1u2DMnJycjLy0NMTAzuuusuDBkyRBo/e/Zs9OvXz+H9+vXrh6ioKKfLExkZ6XBtoSAIiI6OhsFgUL3EDeDJGQn4/HsDvo62Hx+/3oCPV21E9DoDhs/ZhIemJGDtTwbMXrURa3+yTvPJdxuxKs42r1ELNim+16RlG3H3xAR8tGIjHv8oAY9/lID1G1wv14dLNyqO/+H6MsxYsVGaNnqddVnjW8xzyVoDvvzBgJ/WG/Dxyo2IjbcfvzLOgE9Wb8TfPt8kzeu7OAPmfmf/3nHxBjw0NQFDozbhn19vxN0RCZjz3UYs+t6Ab1p8bs99bLvb1NlnP/e7jfguzoAxizbhnokJ+Cbauhwr46zjxe9DnM9jkQmK85m/xoC7JyYg/CvbZxH5rfLnJo5/IUr5O5JnFaf94Btr1oilG7F+g/Vz/+En2/h/fr0RUas2Ys2PBvy/eZvQb1ICvo01SOtJpOw7kg4Is6zrwV0RCXj5001Od8wsz0v+lBOxHlDo9qbfpARMXW79Tt9abPtu5q02YNbKjYiLN+ClTxy/k3eXbMSc69+zOGz0Att0D05xfK9nZiYo9r34xQ/WbTFmnW1e07+1LcsvJzq+5i8Lbe91l+xu7ph11nnFxRvw+EcJeGZmAuLXGzBk9ib0n5qAX0+3TfvfczbhwSkJiJa9r7y+l+3bHpySgJGfbMI30crTLl5rwOK11u1FnO9jso7GV8Qa8NnqjVgaY0DfiQn466JN+OEnAx6YnICXZOv80zMSMH+NActjrPP66vr+quXn+Mj0BMTFGxC1aiPWrTdgw/Vt8XvZtvi3RZsw57uNWP2jbd+wLEZ5+cXP8JHpCfh4lXV9eFu2Psg/41fn25ZX3EeM+9JxmHwfY7ceKqwbWqoVseofWw0GA6KjoymA/l4AVygJYEtKSkoQFBSE+Ph4p9McOHAAgiDg4MGDAKwCeO+99zpM17dvX8yZM8fpfG50C6DSxmA2NyAu+wLmbz2B+VtPICQiEX/6eq80/g9LrM9bHTI/Tfol/uB0W/9rb3y3z+0vWrGyz5YhYt0hnCk14UplLSbFH7JrKViSehpT1h9GxpnLSD5ajE+3HEdNnVl6Juwr39iW64/Xl/Hz5JM4d9mEiHWHcKLY9giiiHXWU2Rjvs3Eh0s3YsHWY3a/xJ+TPWbs0VnWDoW3HL6InHwjJq47hDV7z7vcidSbG7A2Mx8Lt5/E47OTpeE/7ruAeVuPo6GhASnHSjBtwxHp17P8c3tC9pqL5cp3xso/z/UHCvBlyinFX9cLt5/E5PjDyDpbhnpzA2ZuyrNrzRm1LAMfJxzFptwiZJ8rw79/OoQrlbYW0dKrNYrvv3K39ZFnQ+fbHrkmfgePzbJ1wvzW9/vxwvXHsr22LMNhPq15CgfLs5Kvc22pkdc7s/77qmz8TuGJK68ttX6Pj8q+51/PbNtj615fle2wboSvPSD9fc+kzU7fv2WJLZ9vfb9fGhadle/y/cX1uGU9Py/NYVm+THHdifGVStfdGI1bbbsZYuoGx/7wHpA9k/pqdZ3LeU28vg97efFuKaP8+djiPuyh6VtxpFC9SyuenmPrgH3Hces+bMG2E9KwGtmZIaXXy1setVhnSk1sAfQRAS+AgFXc5s6d63S8xWJBUFAQYmNjAbT9FHBLfH0NoNLG0Jp+2SbGO/bv9NqyTAyevxOzNx+HsdqM3y/Z6/QuMPHJDY99nIz3FB6nJT/YiCW/PsjZPJ+9viN09+SIvWevuBz/9g8H7ebralp3z7uUv5d8p69UabLrqeT16tJMvLF6P0pMrg8U8g6RY/cVOIyX75DFz3jMymyknbyMp6JSEJPt+JqQCPfP2hXL3TN7WeqXkjTdN7Vtoq20fvad7Dh/ebnrhNtdPTHbtq0qyeyQ+Z6teyERifjfr/a4HD8x3iZi7uRk2IJ0l+PX5xS5HC/fB7nbH6U62e6Vvs/nZOKnVItTz3j1fbgr+edy/koNTl+yXgLly/f0VZ314DrmtsBrANuBABqNRnTu3BmrV692Ok1eXh4EQUB6ejoA200g2dnZ0jRZWVmauwlEaWP4IeuCxxuOu7u75ALlrtrSgvFLhYNea+rdmBzp79BIx2e0vrTY9YFEXjMTjrkc35rOUL/ZqdxCIVbkxqMez0tJ0ttav1aQ4LZKBsv39aCbHxqtqd987Hr7dPeUltbUo7Mc38vdM5TtplXYluU1uBUy6e7RfeO/P+ByvFzM3cnm+3Gunyn99BzXz7qW15trXC+Xt9X/I9v3Lf7g25JX6tP39FWdutS2O57dQQHUoABWV1cjNzcXubm5EAQBCxYsQG5uLgoKClBdXY0PPvgAGRkZyM/PR1paGp566in07t1busnj7NmzmDFjBvbv34/8/Hxs3rwZ999/P37961+jqcl2h93w4cPRv39/ZGZmIjMzEw8//LDmuoFR2hj+tmqfxxuOu1N5g9z8SpVXW2TO2ydDyHfu7g4aD7sZ/5SbX/cPteIA9vp3+12OH77I9bNn5a01v53r+qBxt5enZ+QHAqVSapli3ZhS81S7Umu8vLxdj+Sl13XG2x+0vvrulUqpRThQfwweL/GNoFEANSiAaWlpijdajBkzBnV1dRg2bBh69uyJoKAg3HnnnRgzZgwKCwul1xcWFuLZZ59F9+7d0alTJ9xzzz145513HPoKLC8vx6hRo3Dbbbfhtttuw6hRozTXEbS/Nzx35W6HqOZOTgvPcxXL3SliNQ+2Xn9ublpI3J06ZAVGaWmdY7HULE+6AGoLFEANCmAgoXcBZLFYLNgjAdMAACAASURBVBbLl3WosHUNM55CAaQAegUFkMVisVgs39WBCxWqH18BCiBAAfQKCiCLxWKxWL6rfU6eUuQtFEAKoFf4cgUymxv8vuGxWCwWi+XPcvZoPG+hAFIAvcKXK5C7TkdZLBaLxWrv5ckzpdsCBZAC6BW+XIFKKpSfNsFisVgsll5q92kKoK+gAHqBL1egs5dMft/wWCwWi8XyZ6WfKlP9+ApQAAEKoFf4cgXKU/FZkSwWi8ViBWKlnrys+vEVoAACFECv8NUKdKXajO/2nPP7hsdisVgslj9rx4lLqh5fRSiAFECv8NUK9OnWE37f6FgslvfVmkcMslgsx9p+jALoKyiAXuCrFWhx6hm/b3QsFsv7+jjxmN+XgcUK5Np6tFTV46sIBZAC6BW+WoFW7jnv942OxWJ5X99nXvD7MrBYgVxJR0pUPb6KUAApgF7hqxUobl+h3zc6FovlfSUcLvb7MrBYgXwpQuJhCqCvoAB6ga9WoE2HeNBgsdpDbT92ye/LwNJvLd91Dp9uPYGY7AK/L0tba+OhYlWPryIUQAqgV/hqBUo5zoMGixXItSz9HC4Ya7DrdJnfl4Wl3xLZklfi92VpaxlyL6p6fBWhAFIAvcJXK1DGWaPfNzoWi9X2uni1DgCQfZ79ebL8VyK7T19RHN//o21+X0Z3FX+wSNXjqwgFkALoFb5agQ4VXvX7RsdisdpelyrrPdqW75m02e/Lymq/JZLrZD0c+Gmq35fRXf10gALoKyiAXuCrFej0pSrVNp67Jvp24/xlOziAPRwZOBdI+/r7ZKlTxmozAOBkqW1bDpVdiP/AtC0IiUjEo7OS2zT/uyc6/v2rGeq15ojL19plUaoHncyr3+SkVi+Xr1us2sP+TKyn5+yQjilny5SfLf/i4j1+X053FbevUNXjqwgFkALoFb5agS5erfN44+g72fUOy93O2V3JWyiUWisenbXd43m19QCldKBwNy9nLStKAvX6d/t9ugNTUzDvn+r5gbktRcFUp0y1jQCA/Cs10rDffGyTvaeiUhASkYgh83dKw+SipLTOPz1nh+J7/XqmdRsc9FmaNCzUzV2fLyxMVxwubjfy95Ivt1K9+OVul+Pl85Lvj0T57TcpQRr2yEzX+5PW7EMen916uXYmq0p139TWC+yNqBW7z+PfPx3GubJq6ZhyubJecdq/rMj2+/K6q5jsAlWPryIUQAqgV/hqBaqoaVDcEKZuyHMYFtpGufjfr6y//MastO0AlA46I53s3EWpkR/Anpid4vI9x39/wOPl++1c20HjsesHoH5TbMv34U+HXL7emZiKO3j557Yh56JqO6t//WhdrtHfZknDvG21+LXsoKgk/G2dv9LrAuGUUCBUtfkaAKC+sUkaNli2rfzP57sQEpGIP32TIQ37v2WZ0t9K3Xa8/JWttUYuUoPmWcXvf2XjJ60/4nL5JsYfVhwubjfi8oVEJOLfPylPK9asBNedXT8rW6dGLc9yGD5gmk0A/7H2oMefsbsft2NXq/fDTv55iDXAjYyq2fVKa+a181SZwzGltuGaNF4uue/E5Ki2jL6qH7IuqHp8FaEAUgC9wlcrkPygIa/ahmuYEJ2D1Rn50jC5FLWmrjU1o8RUZ/fLUOkX7ehvs6Sdj3xHLv79p69tB7ApG1wfdL7eedbj5Zu39bj0tyiZ8pYId3e1/e4T27LKZVJsdZAPK6syY8qGI5hucBTslvWYm9YQi8WCi1fr0NxskYYptdrdM2mzJIlia1DLEg/Gi5JPu3xPdy0w8gqPtu3wxe9QLsPvxea2aX1i2Vd9Y5O0Pe/PL8eo5Vn46UCRNF4UIXnr82dbbOu80gH/laUZ2HW6DK8uzcQ3sm1JFL8319h+YO05o3zRv1jO1qnnrrciDltgayE05Lr+geRsWxRPp8pPJ8v3EWIr5NMzbAIYu891dyXiKcunolLc/uBU84lKK3ZbO+eXt5CLrZXOzpI4O53s7qyNWK8utf0gUGrNfNbJj7W9Z644HFMsFtv+aEnaWYz+Ngubj5R4tM/zd63JpAD6CgqgF/hqBZJvrPKS42qD8WQHI9JwrdnldG+vPYjC8losSz+HH/fbOqgWd8Qf/HgIv5qxDfdNTcI5J9eZyDfksC+ssqJ0Cko86DwyPQGbcm0Hyz9+vRchEYkYusDWgpJ1zojMc0bE7S9UvI5K3gI4QUF6xFaTkAj7g3VrdsrefEehkVthqm3ENzvP4mBBhfKO/OwVrDtQ5HR9EOuVpRkux8tL3i+d2Lorl2E+hlCdutbU7LBdy39svX29pUu+biYeKnI5zzErs6V57Txl617mb6v2ISQiEdMNeUg8XILUE5dRVmV2Oa+vd57F75dYt6uXZNeB/fcia0vXzOutek/MTnF7J3Nu4VXp1Ky8VVk8DS0+Du+VpRmYv/2UNF58/8GzN0nDLhhrEBKRiHud/LDNPl+OpelnUWKqc/vDR7yU5plPlE+di/XgtC1uT+c2N1uwcs95HC02ScPEH4PyH1CebItin3x/kZ0lUCr5tqh0GcmwBenS9/Sc7PR/9vlyxeOKOF7eoib/PrRa3+3Nb9Xx01MogBRAr/DlCqS0IbgbL9ajs5Kx9+wVPD1nB9bnKB9UPJ3X1zvPStNtO1oqDRdPHUclHYex2oyKmganLZdi/XSgCLUN15B/pQYR6xxPK7255gDOXjIhdp0Bu07aREVsJZGfLjtabJKWS+kaI/kBJFrWCaoonvIWDk8/i5AI6wHqd5+kYo2sFbY1n6t4mv1vq/ZJ05nqGt3O64uU0xg0L00SB3nJT+MrXcN090SruI5bsx9V9bb3Eg/AI76wnd76drd/HkPo7m5YrV5vJV/P5NfdWSwWh2262mw7DReTXYBnPtmBxMMl+NePuRg4KwHVdTZpU2rZH7dmvzSvzHO2rqLEVtu5W05I4939aFix+zzqG5tw/koNojbbWh7/cH2diM4uQGF5LarqG+2uSRa3NfmPrrNl1aisb0RRRS2GL7KtS6Ig7TlzBefKqlHf2ISkI7bWQrEFfPgcmwBWm6/hSrUZl6uUr1krLK+VMs6WLbdSAdbrqZ1tX4bci3h6zg4cLKhATkEFnp6zw2lrp9J2LbbKPT47GSt2n8fvPklFYXmt23XGYrHg9KUqlFTYfjDLJXVWwjEMX7TLbltVqhe/3I2mZgtOXarCGtkjBw8WVDise/Lllj9bNxCeVLNi93nFPN5CAaQAeoUWBFB+akD89fnljtPSdM6kzN177T59BTM2HUPDNVtLxo4TNikTWy4WJZ9WnNfk69cgyaVN/kgf+amgJ6+fAjXkXkRjYyMMBgNKZTvH968f4OTX9MgPBPKL38VTZx9cvxZv4KepaGq2ICrpOLYdLZVOl8mv6XH2WXyfeQHf7j4v/coeu3q/02mV5iWKVeTGo9L44yWVmGbIw5Xrd4kCwLUmWyvsC1HWg+Go5VmK64X4HchPq43//gBOlFZi6oY8xMlaaf9vubW1cmbCMcXlFq9XlLdarM7IR8ZZI2ZsOiZ9hjeiHpzu+uJ7Ne9wbU25u1NVfh2Y/IeIEk2yywIOFV6VhovrfGNjI3adLsPMhGOYGO94OcWE6BzpNXJBmrftJEIiHE+VGXIvYmHyKenSC/kdn/JWoE+2nLB7j5AIx+vIvt19HmsyL+CCsQZTN+Qh76KtJUzs8xCA1MIfEmFrWZTfjGCxWPDJlhNIOlIi/Qj84zybAMrFWZ79kZnbce+UJOnaSgCoa2hC5MajyDqn3G+q0jov/kD85aTNipIOQPHHiNK8xP3Wc5+l2Y0Xb6wYt8a2vxJld9L6I9J0NTLh35BzEdMNeXbrBQDpNLd4ev++qUnSj9jVGfnSdOtklxccKTJBiW1HSzF3ywk0N9tyNzdbvw+xlVaLtXzXOcU83kIBpAB6hS9XoPFr9uP+yQnYfeoS+k1JwpykE3bj5Tvs/fnlKDDWwlTXiNQTl+1OP1ksFoz4Yhd+90kqth0txd0TE7E2y/6uqn/GHULo9K3IOmfEfVOTELnxqOIy7cu3nQr6PvMC+k1Owq7T9geKc2XVyC28isamZqSeuIzK+kaMWZmNR2dtt9t5f7TpqDSvK9VmpJ8qg8VisTsYHi66itOXqqSbX/4lE5LymgZpXuIpsJAImxDnFl5F2snLqJBNB0DaEX+7+zx+NWMb3mghdRsPFeOXkzbbPX7IfK0JO05cQo1s+QHg3ZgcPDhtCzLOGtFvchJmtRAtU20jUk9av48DFyqQf6VG8XMFgJe/2oPfzklBXLwB248W231WciwWC3afvmJ3OvHjRNv7yq/9MtVa1wfztSa7eZy/UoMDFyqwJM0qBvLPL1p2x91MhYv7Pb1+qbXl7m7yJ51cJymWXNTU7MrD3R3c8mtC5a1fznhp8R48FZVi953I13mRwvJa3DslCZPWH8GshGPoNyUJZ2UiBVj7GDxzuRo15muK37PItaZmpJ68bNcStiXP9mNMfhqwvKYBO0+V2UmCEs3NFrywMB3Pz0tDk2xa+Y0qF6/WIfOc0ek8TLWNSDlWgtmrNip+bvLP+fSlKqdiAwD//ukw7p+6xe7mMKV5PTE7BU/P2YFXl2Y6ndf/Lc/EU1Ep2HmqDH0nb8aydHsBmW7Iw31Tk5B9vhyh07fi3z8dthtfbb6GHScu2XW9Ul7TgB0nLtldbtLYqPx9yDFWm7HzVJn0dKgBM7bham0D0k5etvvcEw/bWlaPFbf+eHTggvJlKFqopeln3QdoAxRAjQpgeno6wsLC0KtXLwiCgA0bNtiNHzNmDARBsKsnnnjCbhqz2Yzw8HD06NEDXbp0wciRI1FUZN+hZEFBAcLCwtClSxf06NEDEyZMQEODvTC4wpcrUENDA9attx4UlHbsqScv42+r9kkdzrqiqdmCxutSqDQvi8UiDXd2EBGn+/CnQ/gq7YzbaeU0N1vsWhIBq2gp7aiVDoa5hVcxZmU2DlywCaj8vUtN9fjbqn3YeaoM3+3Nx4ToHMVrsADrY/b+vmofyqrMMF9rUmwF8DSXp5+bJzQ3W1Bbb3bI7opNh4rx+nf7UVlvm17eGuKshUNEvKZTLtbyTlfnylqGxGrNXeetabWTX4eoVPJWXqWSn35V8+5L+TVtSqfX5WIqPz3v6ntubLFuKq3zgP065e36JbJi93m8G5NjJw/JsutCW8O1pmaH7WxhslUm75+6xaN5NDY2Yv0GA96JPujQ0iNe+vH7JXvdzkfcFqvqG/HG6v0Oz48Vb7SaZshDw7Vml4Ir3185+9zl272z7ayyvhH3TNqMB6dtUXw/uQDmXXQut4Dt6VBPzE5RHC8/O3P6UpXLeSnhrKNoLZT8MiQ1oQBqVACTkpIwZcoUxMfHOxXA4cOHo7S0VKrycvsLX8ePH4/evXsjOTkZOTk5GDRoEAYMGICmJuuG29TUhNDQUAwaNAg5OTlITk5GcHAwwsPDPV5OX65Azg4K7QWLxYLYfQU4UWr/2bnLnXL8EtIVujloD6jxnVdev26o7+TNbqeta2jCmox8XLxaJ7WclppsPyi+SrNdhC6eOpNfcuDsLkSxxGsMPanB81wLnvyaTaWSy6avOvZWuplAPkxsff31zO2t+s78va1bLBbE7S+0u662rZivNWFN5gUUGGvdTwzX2U11jVidkW93uURbKTHVYXVGPmoblFvWfcX2Y5ewR+GuXMCafdH3Bqzb776fu1JTPe6ZtNnppSF7z9pa/s+7ONPgjCNFJp9sM2rU4tQzrc7jCRRAjQqgHGcC+NJLLzl9jclkQlBQEGJjY6VhxcXF6NChA7Zu3QrAKpkdOnRAcbHtl2JMTAw6d+7s8QpBAVQfveYG1MturDY7PYXsjLqGJlxu0ZpcVd+I//l8Fz5POS3d7CC/2/ANN/2s/cvNNYTyTn9HKPSzJr/BojV9HcqvjxSX0V2nwH+XnQZ3VkqtkPLlqm9sQlmV2eFSAXdwnWd2T7hUWW93CllOjqwnAfn10Z4iv7tZayW/pl1NKIABLIDdunVDz5490a9fP7zxxhu4fPmyNH7Hjh0QBAEVFfZ3Q/Xv3x/Tp08HAEybNg39+/e3G19RUQFBEJCamurRslEA1UevuQFtZxdPscq74vls60m3v9xdjZf3IfmHJY6PpPJE+pSmkV+naL7WhEXJp+1Oc8or7eRlzEw45rTzdXnJsytVW9Hy9+5rmF2d7CdKK6X1UN6K7ynyRxZqrVreaKgWFMAAFcDY2FgkJiYiLy8PmzZtwoABA/DQQw/BbLaeKli7di06derkMK+hQ4di3LhxAICxY8di6NChDtN06tQJ0dHRistiNptRWVkpVVFREQRBgNFoRGNjo6pVW1sLg8GA2tpa1eet5dJrbq1nF0+xylvSorOUu8IRa/0B1536fvijrdPpsM+tp3jld2A+Omu79H9n3cDkXrDeuLRwu7KMyjMojS+tqHY5PirxGO6bmoTvM84r9l35jx8O4OHIrYg0HGmX37ue1/lAyn6m1KS4Tntaxy9q9yaQeVuP++TzNxqNFEB/L4A7lASwJSUlJQgKCkJ8fDwA5wI4ZMgQvPnmmwCsAjhs2DCHaYKCghATE6P4PpGRkQ43nwiCgOjoaBgMBhar3Vb/qdanNQyRddr70YqNLnfcS9YaXI5/4wvbvH4TaZ2//Lmwv5qWgK/WGjBi7ibMWqn8XgaDAfEbrMvobLxYSuNj1rke/9nqjdL8n5mZ4DD+759vksazWP6qlXG29Td6XetfvyzG9bbqzxr7xSaffGbR0dEUQH8vgDs8EUAA6Nu3L+bOnQvAd6eA2QIYWL+KA620nP23c6x3Uc7fdgLrDxTgcEE5dhxXfgTYzhOlSDtRivKqOpc79mXptlPE4vWA8hs4Bn6aKr1/oVH5FJV8Gdsyvrq23uX4rLNl0viXFzs+eWJ24tF2/b3reZ0PpOzyTqWNla2f35lL2r0GcO7mYz75/NkC2E4E0Gg0onPnzli9ejUA200gcXFx0jQlJSWKN4GUlNj6X4qNjeVNIH5Gr7kBbWcXb4AQuwACnPcdJiJ/HrK8Pk85jT8s2Yu6Blsn5a98s9dOBEMirNfciRirlR9tJqct4+XdcyiNl3fMK+/UXKyopONef7Za/t59DbOrk91iseCdmBy8E5PjfmIFPHmCib+qZR+4asFrADUqgNXV1cjNzUVubi4EQcCCBQuQm5uLgoICVFdX44MPPkBGRgby8/ORlpaGp556Cr1790ZVla3/o/Hjx6NPnz5ISUlBTk4Onn/+ecVuYAYPHoycnBykpKSgT58+7AbGz+g1N6Dt7OINEPK+2pxdOC5HHCZ2wD3yy9124/MumjBv20nsPGF9zKD8GsOwL2zTVjp5LJbSe6k5Xt6p7mvLHJ8DHbWZAugNzK6N7MVXXbfW+7PU2MaUoABqVADT0tIUr7UbM2YM6urqMGzYMPTs2RNBQUG48847MWbMGBQWFtrNo76+HuHh4ejevTtuueUWhIWFOUxTUFCAESNG4JZbbkH37t0RHh4u3UjiCRRA9dFrbkDb2cVH58kfzN7UbEHYF7vxytIMbMkrRej0rUg5fsnude/H5eLpOTtQbb6GAxcqnHaRUl5VhwHTEvCvH3OxYvd5PBy51a5fuvrGJtw10bXATdlwBI/PTpZkUw0BlHeq++cV2Q7jZ1MAvYLZtZH9UqXys5e1UC2fsKQWFECNCmCgQAFUH73mBrSdXXyuq/wZsoD11JP4JARnT1dw91gxwPZECDG70msWp57Bgu2n8E6M9Xm1ry1zfJxXc7MFb/1wwKXg9ZP1L6g0Xl7yx/f9TaGvQAqgdzC7NrJfcXKJhRZqxiYKoK+gAHoBBVB99Job0HZ2sc++uP2F7iduA63JXm2+hviDRTDVKk978HqnuG//cNBu+OT1RxASkYjUE5ex48QlhydfKB18iipsneoqdXy9MPlUG9Lao+Xv3dcwuzaye9IPpr/K2bPpvYUCSAH0Cgqg+ug1N6Dt7OKTMpw9tN5b1M5eUdPg8IxWi8WC8hrnz/oWr/Eb9FkaHpi2BXdNTESFbHp5y+J3e/Pxp68zUFXv/fJq+Xv3NcyujeymOuVrbLVQ0wx5PslMAaQAegUFUH30mhvQdvbjJZX4IuW000dReYsWsl+pNmNh8ilcvFqHLXmliNtn39opPk1EfneyGmghu79gdm1krzZf87voOavJ64/4JDMFkALoFRRA9dFrboDZtZ7dYrHgaLGp1c/6dUcgZPcVzK6N7PIumbRWE+MpgL6CAugFFED10WtugNmZndn1hJaym69pVwD//dNhn2SmAFIAvYICqD56zQ0wO7Mzu57QUvYmJ522a6H+9eMhn2SmAFIAvYICqD56zQ0wO7Mzu57QUnaLRbsC+H5crk8yUwApgF5BAVQfveYGmJ3ZmV1PaC27v0XPWb0XSwH0FRRAL6AAqo9ecwPMzuzMrie0lt3fouesJkS37fnG7qAAUgC9ggKoPnrNDTA7szO7ntBadn+LnrP6x9qD7he+DVAAKYBeQQFUH73mBpid2ZldT2gtu79Fz1m99cMBn+SlAFIAvYICqD56zQ0wO7Mzu57QWnZ/i56zenMNBdBXUAC9gAKoPnrNDTA7szO7ntBadlcS1v+jbX4TwDdW7/dJXgogBdArKIDqo9fcALMzO7PrCa1ldyVhT8xO8ZsA/n3VPp/kpQBSAL2CAqg+es0NMDuzM7ue0Fp2VxL2ZJT/BPDuiYkoMNaqnpcCSAH0Cgqg+ug1N8DszM7sekJr2V1J2FN+FMCQiETM33ZS9bwUQAqgV1AA1UevuQFmZ3Zm1xNay+5KwJ6es8OvArgk7azqeSmAFECvoACqj15zA8zO7MyuJ7SWXWsCePdE29/f7c1XPS8FkALoFRRA9dFrboDZmZ3Z9YTWsmtNAPtNSZL+jttXqHpeCiAF0CsogOqj19wAszM7s+sJrWXXmgA+OG2L9PemQ8Wq56UAUgC9ggKoPnrNDTA7szO7ntBadlcy9swnO/Cbj5NvqADK+x5MOX5J9bwUQAqgV1AA1UevuQFmZ3Zm1xNay+5Kxn73SSrOXK7GmyuzMH7BjRHBR2dtl/7ee/aK6nkpgBRAr6AAqo9ecwPMzuzMrie0lt2dAAIAqmqweMXOGyKA8s6ncwoqVM9LAaQAegUFUH30mhtgdmZndj2hteyuZOzZT20C+M1K9QXwLoVhv51ru+7wZGmV6nkpgBRAr6AAqo9ecwPMzuzMrie0lt1TAVy+Sn0B/OVEx2HPfZYm/V1YzieB+AJNCmB6ejrCwsLQq1cvCIKADRs2OJ123LhxEAQBCxcutBseEhICQRDsKiIiwm6agoIChIWFoUuXLujRowcmTJiAhoYGj5eTAqg+es0NMDuzM7ue0Fr2lgIm74dvoEwAV3yXrroA9p202SaD1/9+fp5NAK9Um1XPSwHUqAAmJSVhypQpiI+PdymAGzZswIABAxAcHKwogDNnzkRpaalU1dXV0vimpiaEhoZi0KBByMnJQXJyMoKDgxEeHu7xclIA1UevuQFmZ3Zm1xNay95Syu6RSdlzn6VZJ6qqwerV6gtgv8m297pvapIkneKw2oZrquelAGpUAOU4E8CLFy+id+/eOHr0KEJCQhQFsOUwOUlJSejQoQOKi239C8XExKBz584erxAUQPXRa26A2Zmd2fWE1rI7SJmsI2a5AH6/Rn0BvFf2XqHTtyIkwr7vweZmi+p5KYABKoDNzc0YNGgQFi1aBEBZ9kJCQnDHHXege/fuGDBgAD7++GO707vTpk1D//797V5TUVEBQRCQmpqquCxmsxmVlZVSFRUVQRAEGI1GNDY2qlq1tbUwGAyora1Vfd5aLr3mZnZmZ3b/L4+es7eUMrElLiQiEc99mmqdrsKEmO9bL4D3KFzjZ/deMgH81Qxr/3/yfgd9kddoNFIA/b0A7lASwKioKAwdOhQWi/VXgZIALliwADt37sThw4exfPly/OIXv8Drr78ujR87diyGDh3q8H6dOnVCdHS04rJERkY6XFcoCAKio6NhMBhYLBaLxQrIcmiVm5Qg/f2byAQYDAakJWxG3A+7Wi2ASjd52F8DaPv74anW931wiu39fZE3OjqaAujvBXBHSwE8cOAAbr/9drtTt+5O9wLAunXrpNY6wCqAw4YNc5guKCgIMTExivNgC6DvS6+5mZ3Zmd3/y6Pn7KJs3XVd1h66fio2JCIRb3y3zzpdhQnxa1svgO6qr+wawMdnW1v+Bn1muwbQF3nZAhiAArhw4ULcdNNN6Nixo1SCIKBDhw4ICQlxOp+LFy9CEARkZWUBaNsp4JbwGkD10WtugNmZndn1hNayPxVl7Xh5/PcHrC1xkVuRU1CBD386hLKq63fhVtXAEO0DAZTdcCIux2dbTyJy41EkHFb/OcAArwEEAlAAjUYj8vLy7Co4OBgRERE4efKk0/kkJCRAEAQUFBQAsN0EUlJSIk0TGxvLm0D8jF5zA8zO7MyuJ7SW/Uq1GVvySpBTUCFdi+dAVQ0SYnZ7LHbi9XytOUUsdgC9MPmUT/NSADUqgNXV1cjNzUVubi4EQcCCBQuQm5sryVtLWp4CzsjIkF5z/vx5xMXFITg4GC+++KI0jdgNzODBg5GTk4OUlBT06dOH3cD4Gb3mBpid2ZldT2g1+9FiE0IiEvHIzO2OI6tqkBTruQAOXeBZp9HyJ4H87hPrqd8vUk77NCcFUKMCmJaWpnizxZgxYxSnbymABw8exBNPPIFu3brh5ptvxn333YfIyEjU1tr3Jl5QUIARI0bglltuQffu3REeHg6z2fMOJymA6qPX3ACzMzuz6wmtZjdWm9F38ma8uHiP48iqGmyL81wA/295pvT3czO2WK8tlF3vp1TiE0AWp57xaU4KoEYFMFCgAKqPXnMDzM7szK4ntJy9sLwWlfUKy1VVg5Qf93gssUZFAgAAIABJREFUgJ9tPSn9XbdjH85tzsSYedtdvmbQ9SeALEk769OMFEAKoFdQANVHr7kBZmd2ZtcTAZm9qgZpP7kWwD99nYGQiET8fsle1Dc2YdqPOdi9bg+wcz+wcz/+5kYAh8y3njZemk4B9DUUQC+gAKqPXnMDzM7szK4nAjJ7VQ12rXMtgHvPXkHysUu2FsSqGkn+sHM/Xp/vWgCHLbB2NL181zmfRqEAUgC9ggKoPnrNDTA7szO7ngjI7FU12Bu/16XA7c8vd3iNXADHLkh2+foXFloFcMXu8z6NQgGkAHoFBVB99JobYHZmZ3Y9EZDZq2qQtd47AXzTjQD+z+fWfgZX7aEA+hoKoBdQANVHr7kBZmd2ZtcTAZm9qgYHNrgWwAMXXAvg2wtdC2DYF9a7jNdk5Ps0CgWQAugVFED10WtugNmZndn1REBmr6pBjiHDKwEMX+RaAF9cbL3G8PvMCz6NQgGkAHoFBVB99JobYHZmZ3Y9EZDZq2pwZKN3AvjOohSXr3/5K6sARmcrP/hBLSiAFECvoACqj15zA8zO7MyuJwIye1UNjm7yTgDf/9xRAJ+OsrUK/mGJ9RRz7D4KoK+hAHoBBVB99JobYHZmZ3Y9EZDZq2pwMiHTpQDmFl51eI1cAD/4wlEAp/6Yg3UHinCitFLqR/DH/YU+jUIBpAB6BQVQffSaG2B2Zmd2PRGQ2atqcCZRWQAnrz+Cv6zIRnOzxeE1cgH88EtHAZwSlyNN/spSqwCuO1Dk0ygUQAqgV1AA1UevuQFmZ3Zm1xMBmb2qBuc3Kwugq9fIBXDi4h0Or50Ue1Ca/LVl1vlvyLno0ygUQAqgV1AA1UevuQFmZ3Zm1xMBmb2qBgVJ3gngJAUBnBhjE8Avd5zGw5FbceZytU+jUAApgF5BAVQfveYGmJ3ZmV1PBGT2qhpc3JLllQBO/cpRACNkAggATS1PI/sACiAF0CsogOqj19wAszM7s+uJgMxeVYPSrd4JYOQSRwH8MPrAjctwHQogBdArKIDqo9fcALMzO7PriYDMXlWDsm3eCeAPa9IdXvvBWgqgP6AAegEFUH30mhtgdmZndj0RkNmralC+PVsSt39e79IlPDrH5WvkAngtdR+++DbN7pFy7/+w/8ZluA4FkALoFRRA9dFrboDZmZ3Z9URAZq+qgSnZJoAXNmfiwPFimK81uXyNXADlJc7nk41HblyG61AAKYBeQQFUH73mBpid2ZldTwRk9qoaVKfYBLAgKdMqeG5e40wAU3/agw++SEGN0XRjll8GBZAC6BUUQPXRa26A2Zmd2fVEQGavqkH9jn2SAOZv9k4ApXI3Dx9AAaQAegUFUH30mhtgdmZndj0RkNmratCQahPAcxTAgIYC6AUUQPXRa26A2Zmd2fVEQGavqkFzmk0AzyRSAAMZCqAXUADVR6+5AWZndmbXEwGZ/brMvT4/GX+Yuw3NafsogAEMBdALKIDqo9fcALMzO7PriYDMLpM5S9o+z+SNAqhZKIBeQAFUH73mBpid2ZldTwRkdiWZowAGLJoUwPT0dISFhaFXr14QBAEbNmxwOu24ceMgCAIWLlxoN7yiogKjR49G165d0bVrV4wePRpXr161m+bIkSN49tlncfPNNyM4OBgzZsyAxeL5MwgpgOqj19wAszM7s+uJgMxOAWxXaFIAk5KSMGXKFMTHx7sUwA0bNmDAgAEIDg52EMDhw4cjNDQUGRkZyMjIQGhoKMLCwqTxlZWVuP322/Hqq68iLy8P8fHxuO222zBv3jyPl5MCqD56zQ0wO7Mzu54IyOwUwHaFJgVQjjMBvHjxInr37o2jR48iJCTETgCPHz8OQRCQlZUlDcvMzIQgCDh58iQAYMmSJejWrRvMZrM0zZw5cxAcHOxxKyAFUH30mhtgdmZndj0RkNkpgO2KgBTA5uZmDBo0CIsWLQIABwFcsWIFunXr5jCvbt26YeXKlQCAP//5z3jxxRftxufk5EAQBJw/f96jZaMAqo9ecwPMzuzMricCMjsFsF0RkAIYFRWFoUOHSi11LQVw9uzZ6Nevn8O8+vXrh6ioKADA0KFDMXbsWLvxxcXFEAQBGRkZistiNptRWVkpVWFhIQRBQH5+PoxGo6pVWlqK6OholJaWqj5vLZdeczM7szO7/5eH2d3UhUJUbk6zK+OFwla/ptXz8EHl5+dDEASYTDf+MXRaIeAE8MCBA7j99ttRXFwsDVMSwHvvvddhXn379sWcOXMAWAVw3LhxduMvXrwIQRCQmZmpuCyRkZEQBIHFYrFYLFY7qKKiIq8cJZAJOAFcuHAhbrrpJnTs2FEqQRDQoUMHhISEAPDdKeCWLYBXr17FuXPnYDKZ7IarUUVFRdLKqfa8tVx6zc3szM7s/l8eZtdPdpPJhKKiIjQ3N6tkK4FHwAmg0WhEXl6eXQUHByMiIkK6wUO8CSQ7O1t6XVZWFgTB/iaQ//iP/0BDQ4M0zdy5c1t1E4gvqazU5/UJes0NMDuzM7ueYHZ9ZtcSmhTA6upq5ObmIjc3F4IgYMGCBcjNzUVBQYHi9C1PAQPWbmD69++PzMxMZGZm4uGHH7brBsZkMuH222/Ha6+9hry8PKxfvx5du3ZtVTcwvkSvG4hecwPMzuzMrieYXZ/ZtYQmBTAtLU3xXP2YMWMUp1cSwPLycowaNQq33XYbbrvtNowaNUqxI+jf/e536Ny5M+644w589NFHmmj9A/S7geg1N8DszM7seoLZ9ZldS2hSAIn1esPIyEi7fgr1gF5zA8zO7MyuJ5hdn9m1BAWQEEIIIURnUAAJIYQQQnQGBZAQQgghRGdQAAkhhBBCdAYFUIN89dVXuOuuu9C5c2c88sgj2LVrl78XyWvS09MRFhaGXr16OfTtCAAWiwWRkZHo1asXbr75ZgwcOBBHjx61m6aiogKjR49G165d0bVrV4wePdrhzm6tERUVhcceewy33norevbsiZdeeknqi1LEbDYjPDwcPXr0QJcuXTBy5EiH3ukLCgoQFhaGLl26oEePHpgwYYJdH5ZaZMmSJXj44YelO/GffPJJJCUlSePba24loqKiIAgC3n33XWlYe82v9MSk22+/XRrfXrd1kYsXL2LUqFHo3r07brnlFgwYMAAHDhyQxrfX/CEhIYq9d7z99tsA2u/6HshQADVGbGwsgoKCsHz5chw/fhzvvvsufv7znzvtAzFQSEpKwpQpUxAfH68ogHPnzsVtt92G+Ph45OXl4ZVXXkGvXr1QVVUlTTN8+HCEhoYiIyMDGRkZCA0NtevbUYu88MILWLVqFY4ePYpDhw5hxIgRuPPOO1FTY3v4+fjx49G7d28kJycjJycHgwYNwoABA9DU1AQAaGpqQmhoKAYNGoScnBwkJycjODgY4eHh/orlEZs2bcLmzZtx6tQpnDp1CpMnT0ZQUJB0sGuvuVuyb98+3HXXXejfv7+dALbX/JGRkXjooYdQWloqVVlZmTS+vW7rgFXcQkJC8Ne//hXZ2dnIz89HSkoKzp49K03TXvOXlZXZfefJyckQBAFpaWkA2u/6HshQADXG448/jvHjx9sNu//++zFx4kQ/LZH6tBRAi8WCO+64A3PnzpWGmc1mdOvWDd988w0A29NdsrKypGkyMzPtnu4SCJSVlUEQBKSnpwOwdkgeFBSE2NhYaZri4mJ06NABW7duBWCV5w4dOtg9/zomJgadO3cOuH60/vM//xPffvutbnJXV1ejX79+SE5OxsCBAyUBbM/5IyMjMWDAAMVx7X1bj4iIwDPPPON0fHvPL+fdd9/FPffcA4vF0q7X90CGAqghGhoa0LFjR6xfv95u+DvvvINnn33WT0ulPi0F8Ny5cxAEATk5OXbTvfjii/jLX/4CwLPnOwcCZ86cgSAIyMvLAwDs2LEDgiCgoqLCbrr+/ftj+vTpAIBp06ahf//+duMrKiogCAJSU1NvzIJ7SVNTE2JiYtCpUyccO3ZMN7n/8pe/4L333gMAOwFsz/kjIyPRpUsX9OrVC3fddRdeeeUVnDt3DkD739YfeOABvPfee/jjH/+Inj174le/+hWWLVsmjW/v+UUaGhrQo0cPzJ49G0D7Xt8DGQqghiguLoYgCNi7d6/d8NmzZ+Pee+/101KpT0sB3Lt3LwRBsPvlBwBjx47FsGHDAFg/g379+jnMq1+/foiKivLtAquExWLByJEj7VoI1q5di06dOjlMO3ToUIwbNw6A9XMYOnSowzSdOnVCdHS07xZYBY4cOYKf//zn6NixI7p164bNmzcDaP+5AWvrRWhoKOrr6wHYC2B7zp+UlIR169bhyJEjUsvn7bffDqPR2O639c6dO6Nz586YNGkScnJy8M033+Dmm2/G6tWrAehnXxcXF4eOHTtKOdvz+h7IUAA1hCiAGRkZdsM//vhj3HfffX5aKvVxJoAlJSV2073xxht44YUXADiX4L59+2LOnDm+XWCVePvttxESEmJ34bOzHeOQIUPw5ptvArA/OMgJCgpCTEyM7xZYBRoaGnDmzBns378fEydOxC9+8QscO3as3ecuLCzEf/3Xf+HQoUPSME8EsL3kl1NTU4Pbb78d8+fPb/fbelBQEJ566im7YRMmTMCTTz4JQD/7umHDhtlds6in9T2QoABqCJ4Cbr+nRcLDw9GnTx+cP3/ebrjeTo0MHjwY48aNa/e5N2zYAEEQ0LFjR6kEQcBNN92Ejh07IiUlpV3nb8mQIUMwfvz4dr+t33nnnXj99dfthi1ZsgTBwcEA9LGvu3DhAjp06ACDwSANa+/be6BCAdQYjz/+ON566y27YQ888IAubgL55JNPpGENDQ2KF0ZnZ2dL02RlZWn+wmiLxYJ//OMfCA4OxunTpx3GixdHx8XFScNKSkoUL46WtxrExsYG5MXRzz//PMaMGdPuc1dVVSEvL8+uHnvsMYwePRp5eXntPr8cs9mM3r17Y8aMGe16WweA1157zeEmkPfee09qFWzv+QHrNaB33HEHrl27Jg3T0/oeSFAANYbYDcyKFStw/PhxvPfee/j5z3+OCxcu+HvRvKK6uhq5ubnIzc2FIAhYsGABcnNzpe5t5s6di27dumH9+vXIy8vDa6+9ptg1Qv/+/ZGZmYnMzEw8/PDDmu8a4a233kK3bt2wc+dOuy4S6urqpGnGjx+PPn36ICUlBTk5OXj++ecVu0cYPHgwcnJykJKSgj59+mi+e4RJkyZh165dyM/Px5EjRzB58mR06NAB27dvB9B+cztDfgoYaL/5P/jgA+zcuRPnz59HVlYWwsLCcNttt0n7sPa6rQPWLn9+9rOfYfbs2Thz5gzWrl2LLl264IcffpCmac/5m5ubceeddyIiIsJhXHtd3wMZCqAG+eqrrxASEoJOnTrhkUcekboMCWTS0tIUOwkdM2YMAFvnqHfccQc6d+6MZ599VrpTVqS8vByjRo2SOhYeNWqU5jtHVcosCAJWrVolTVNfX4/w8HCp49iwsDAUFhbazaegoAAjRozALbfcgu7duyM8PBxms/kGp2kdf//736X1uGfPnhg8eLAkf0D7ze2MlgLYXvOL/doFBQUhODgYv//973Hs2DFpfHvd1kUSEhIQGhqKzp074/7777e7Cxho3/m3bdsGQRBw6tQph3HtdX0PZCiAhBBCCCE6gwJICCGEEKIzKICEEEIIITqDAkgIIYQQojMogIQQQgghOoMCSAghhBCiMyiAhBBCCCE6gwJICCGEEKIzKICEEEIIITqDAkgIIYQQojMogIQQQgghOoMCSAghhBCiMyiAXtDc3IyioiKYTCZUVlayWCwWi8UKgDKZTCgqKkJzc7O/VcJvBKQARkVF4bHHHsOtt96Knj174qWXXsLJkyftpjGbzQgPD0ePHj3QpUsXjBw5EkVFRXbTFBQUICwsDF26dEGPHj0wYcIENDQ0eLwcRUVFEASBxWKxWCxWAFZLL9ATASmAL7zwAlatWoWjR4/i0KFDGDFiBO68807U1NRI04wfPx69e/dGcnIycnJyMGjQIAwYMABNTU0AgKamJoSGhmLQoEHIyclBcnIygoODER4e7vFymEwmaQVS+9eJ0WhEdHQ0jEaj338p3ehidmb397IwO7Mze/vOLjbgmEwm1R0lUAhIAWxJWVkZBEFAeno6AKuYBQUFITY2VpqmuLgYHTp0wNatWwEASUlJ6NChA4qLi6VpYmJi0LlzZ1RWVnr0vpWVlRAEwePpW0NjYyMMBgMaGxtVn7fWYXZm1xvMzux6w9/ZfXn8DhTahQCeOXMGgiAgLy8PALBjxw4IgoCKigq76fr374/p06cDAKZNm4b+/fvbja+oqIAgCEhNTfXofSmAvoHZmV1vMDuz6w1/Z6cAtgMBtFgsGDlyJJ555hlp2Nq1a9GpUyeHaYcOHYpx48YBAMaOHYuhQ4c6TNOpUydER0crvpfZbFZsQjYajWhsbFS1amtrYTAYUFtbq/q8tV7Mzuz+XhZmZ3Zmb9/ZjUYjBdDfC+Atb7/9NkJCQuwu5HQmgEOGDMGbb74JwCqAw4YNc5gmKCgIMTExiu8VGRmpeBFpdHQ0DAYDi8VisVisAKjo6GgKoL8XwBvCw8PRp08fnD9/3m64r04BswVQH78MmZ3ZmZ3Zmd1F1ZShsa4ioLOzBTBABdBiseAf//gHgoODcfr0aYfx4k0gcXH/v71zj6+ivPP/VBpQKGaLtYC4QrfiZUVobdfarav1hrai7bptXSsrrfWCFpVd97dqbU1tvWC32qutW2+1bcI1IVzlEq7KHRMgEO6XEJJACCEhJDk5yTmf3x/DnDMz53memZM5kzPJfN6v1/MS83zPd57P3M7nzDyX6Ym/1dTUCAeB1NTUJGKmTZvGQSABgNqpPWxQO7X3GDpagXxNL/F4l9NkWzv7APZQA/jII48gNzcXK1euRG1tbaK0trYmYiZOnIgLL7wQJSUlKC0txY033iicBuamm25CaWkpSkpKcOGFF6Y1DQwNoD9QO7WHDWqn9h5D486kAYx1dDlNtrXTAPZQAyib0PHdd99NxLS1tWHSpEkYNGgQzjnnHIwbNw6HDx+25KmsrMTtt9+Oc845B4MGDcKkSZMQiURct4MG0B+ondrDBrVTe4+hscJkALve7mxrpwHsoQYwKNAA+gO1U3vYoHZq7zGYDWBnW5fTZFs7DSANoCdoAP2B2qk9bFA7tfcYzAawo6XLabKtnQaQBtATNID+QO3UHjaondp7DI07kgYw2tzlNNnWTgNIA+gJGkB/oHZqDxvUTu09hpPbkwaw/cw6um11QKxTL211rtJkWzsNIA2gJ2gA/YHaqT1sUDu19xgsBrABOFGq/3vp9cCysfq/j693TCPV3noUODIPiMf8af8ZaABpAD1BA+gP1E7tYYPaqb3HYDaAkXpg46PJ/zfK2vsc00i1z8jVc+x7yycBOjSANICeoAH0B2qn9rBB7dQeOOIx/ZWunZPlSaPXVgdsfCSzBtDIseqbGRIihgaQBtATNID+QO3UHjaondoDRTwOvP8FYO4lqSawYUvSpLXW0gD2YGgAPUAD6A/UTu1hg9qpPVB0RpJG7NQ+a92Jj5J1LdViA7jmPxw3IdReW0ID2I3QAHqABtAfqJ3awwa1U3ugUBnA+k0mA1gFbJiYGQMYj1tz0AD6Dg2gB2gA/YHaqT1sUDu1BwqzATy+wVp3fH2y7vQhsQHsyivgeCw1T9UcH8Tp0ADSAHqCBtAfqJ3awwa1U3ug6GyzGrG6Ncm6ujXJvzcfyJwBjHWm5sn3z6LQANIAeoIG0B+ondrDBrVTe6DoaLGasA++k6w7ttr6enjDw5l5BRzroAHsZmgAPUAD6A/UTu1hg9qpPVB0nJYbwKMrkn9v2iM2gPkaULdWuYloNIqPZj6O2LJb9RVFOttpALsZGkAP0AD6A7VTe9igdmoPFNFmmwH8drKudpmpj14xsOEhsXFzMG/RaDQZV/aMtd8hDWC3QAPoARpAf6B2ag8b1E7tgSLaJDeANUusdQvGdMkAdhxekIzb+Ehqv0MaQN+hAfQADaA/UDu1hw1qp/ZA0d4ofwVc/b7c8Lk1b9FT1riNjwAdrTSA3QwNoAdoAP2B2qk9bFA7tQeK9pNyA3hkvncD2FpjjfvwntSBJzSAvkMD6AEaQH+gdmoPG9RO7YGivUH+CrhqTuYNYL4GbHueBrCboQH0AA2gP1A7tYcNaqf2QBGplxvAw7O9G8CWau85PEIDSAPoCRpAf6B2ag8b1E7tgaLtuNwAVs5K37zFY9b8LUdoAAMADaAHaAD9gdqpPWxQO7UHirY6eR/AQ9PTM2/bXwZmfhJorEjmOH2YBjAA0AB6gAbQH6id2sMGtVN7oGg9Kn8CeLAgPfNm/Hv5rckczQdpAAMADaAHaAD9gdqpPWxQO7UHitZauQE88FfvBvDUPhrAAEAD6AEaQH+gdmoPG9RO7YHCPkp32dhk3f73vBvApt00gAGABtADNID+QO3UHjaondoDhWiQxvaX9Lp973gzgPE4sOcPNIABgAbQAzSA/kDt1B42qJ3aA0VLldyM7X2zawbQeIp4uND952kAfYUG0AM0gP5A7dQeNqid2gOFbJQuAOx5w5sBXPc9GsCAQAPoARpAf6B2ag8b1E7tgeJ0pdyM7X69iwbwFv3/0zWAGyb6IpEGsIcawFWrVmHcuHEYOnQoNE3D7NmzLfUTJkyApmmW8qUvfckSE4lEMGnSJJx33nno378/7rjjDlRVVaXVDhpAf6B2ag8b1E7tgUI2TQsA7Ppd9xpAn54C0gD2UAO4cOFCPPvssygsLJQawNtuuw21tbWJcuLECUvMxIkTMWzYMCxduhSlpaW44YYbMGbMGHR2drpuBw2gP1A7tYcNaqf2QNF8QG7Edv6aBrCX0CMNoBmZAfzGN74h/UxjYyNycnIwbdq0xN+qq6tx1llnYdGiRa63TQPoD9RO7WGD2qk9UDTvlxuxile7ZgAXXaP//7rv0wAGhF5rAHNzc3H++edj5MiReOCBB3Ds2LFE/bJly6BpGhoaGiyfGz16NJ577jnX26YB9Adqp/awQe3UHihO7ZUbsR2/cG/c7CuKlL9AAxggeqUBnDZtGubPn4/y8nLMnTsXY8aMwRVXXIFIJAIAyM/PR9++fVNy3XLLLXjooYek24pEImhqakqUqqoqaJqG+vp6RKPRjJaWlhYUFxejpaUl47mDXqid2rPdFmqn9tBqb29D57oHhUYsGo2ic9sL6Rs4U4mtmZD2Z/zQWV9fTwOY7QZ4RWQA7dTU1CAnJweFhYUA5Abw5ptvxsMPPyzNk5eXlzK4RNM0FBQUoLi4mIWFhYWFpUeX0pmTpEasuLgYO2bc68kAHpp+U9qf8UNnQUEBDWC2G+AVNwYQAC6++GJMmTIFQNdfAfMJYPcUaqf2bLeF2qk9rNo7Nz6hfBLXuSWPTwB7CaEwgPX19ejXrx/ee+89AMlBINOnT0/E1NTUcBBIQKB2ag8b1E7tgSDaBMz6lNyMxTqBuZd4MoDsAxgceqQBbG5uRllZGcrKyqBpGl577TWUlZWhsrISzc3NePLJJ7F27VocPHgQK1aswJe//GUMGzYMp06dSuSYOHEiLrzwQpSUlKC0tBQ33ngjp4EJCNRO7WGD2qk9EKy8U23E9vzRm/mjAQwUPdIArlixQtgXb8KECWhtbcXYsWNx/vnnIycnBxdddBEmTJiAw4cPW3K0tbVh0qRJGDRoEM455xyMGzcuJcYJGkB/oHZqDxvUTu2BwMmIrfpXGsBeRI80gEGBBtAfqJ3awwa1U3sgoAEMFTSAHqAB9Adqp/awQe3UHghoAEMFDaAHaAD9gdqpPWxQO7UHgm4xgPfTAAYEGkAP0AD6A7VTe9igdmoPBDSAoYIG0AM0gP5A7dQeNqid2gNBdxjAqf3S/0w8nnGpNIA0gJ6gAfQHaqf2sEHt1B4IusMAdqXEYxmXSgNIA+gJGkB/oHZqDxvUTu2BIKgGMJb5/UMDSAPoCRpAf6B2ag8b1E7tgcDJiC35SnYMYGck41JpAGkAPUED6A/UTu1hg9qpPRBkw9y5KR0tGZdKA0gD6AkaQH+gdmoPG9RO7YEg20ZPVqLNGZdKA0gD6AkaQH+gdmoPG9RO7YEg20ZPagAz/x1LA0gD6AkaQH+gdmoPG9RO7YEg20ZPVtobMi6VBpAG0BM0gP5A7dQeNqid2gNBto2erETqMy6VBpAG0BM0gP5A7dQeNqid2gNBto2erLTVZVwqDSANoCdoAP2B2qk9bFA7tQeCbBs9WWmtzbhUGkAaQE/QAPoDtVN72KB2ag8E2TZ6stJSnXGpNIA0gJ6gAfQHaqf2sEHt1B4Ism30pAawKuNSaQBpAD1BA+gP1E7tYYPaqT3rxDqyb/Rk5fShjMulAaQB9AQNoD9QO7WHDWqn9qyTzjJvxcO71wA2H8i4XBpAGkBP0AD6A7VTe9igdmrPOukYsuLPdK8BPLU343JpAGkAPUED6A/UTu1hg9qpPesE2QA27c64XBpAGkBP0AD6A7VTe9igdmrPOmkZwBHdawAbd2ZcLg0gDaAnaAD9gdqpPWxQO7VnnbQM4PDuNYAnt2dcLg0gDaAnaAD9gdqpPWxQO7VnnXQM2eyLutkAbsu4XBpAGkBP0AD6A7VTe9igdmrPOkE2gA1bMi6XBpAG0BM0gP5A7dQeNqid2rNOWgbw77vXAJ74KONyaQBpAD1BA+gP1E7tYYPaqT3rpGUAL0zfxFW8Ciy5tmsGsH5TxuXSANIAeoIG0B+ondrDBrVTe9Zxa8YK+nTNAKa7HXM5viHjcmkAaQA9QQPoD9RO7WGD2qk967g1Y7t+BxQNS8/ALf9a+tsxl7q1GZdLA9hDDeCqVaswbtw4DB06FJqmYfbs2Zb6eDyOvLw8DB06FGeffTauv/56bN8y4G/jAAAgAElEQVRuHUbe0NCA8ePH49xzz8W5556L8ePH4+TJk2m1gwbQH6id2sMGtVN71nFjxJZeB8Tj6b/KjXUkt7NmPDB/FOIFfdIwgB9mXC4NYA81gAsXLsSzzz6LwsJCoQGcMmUKBg4ciMLCQpSXl+Puu+/G0KFDcerUqUTMbbfdhlGjRmHt2rVYu3YtRo0ahXHjxqXVDhpAf6B2ag8b1E7tWceNEVv3fT329CH35m1qTuq24nHE517qPsexVRmXSwPYQw2gGbsBjMfjGDJkCKZMmZL4WyQSQW5uLt544w0AQEVFBTRNw/r16xMx69atg6Zp2LVrl+tt0wD6A7VTe9igdmrPOm6M2PoHkvEH/uLSAPYVbi4tA3h0Rcbl0gD2QgO4f/9+aJqG0tJSS9ydd96J++67DwDw9ttvIzc3NyVXbm4u3nnnHdfbpgH0B2qn9rBB7dSeddwYsQ0PJ+M9GsDYomvcG8DaZRmXSwPYCw3gmjVroGkaqqurLXEPPvggxo4dCwB48cUXMXLkyJRcI0eOxEsvvSTdViQSQVNTU6JUVVVB0zTU19cjGo1mtLS0tKC4uBgtLS0Zzx30Qu3Unu22UDu1h027GyPWuf7hRHzHnrddfSY+tZ9we63Htro2gB1VCzOut76+ngYw2w3wiswA1tTUWOIeeOAB3HrrrQB0A3jJJZek5Lr44ovx8ssvS7eVl5cHTdNSSkFBAYqLi1lYWFhYWHpkcWPE9k//eiL+o5lPuDON+TnSba6f9YyrHGtm5WVcb0FBAQ1gthvgle58BcwngN1TqJ3as90Waqf2sGl3ZeY2PpaI79jzlssngGdLtbs1gB2VczOul08Ae6EBNAaBvPLKK4m/tbe3CweBbNiQnFxy/fr1HAQSEKid2sMGtVN71nHzOvaj/0rG7/+zu89MO1u4uWg06toA4si8jMtlH8AeagCbm5tRVlaGsrIyaJqG1157DWVlZaisrASgTwOTm5uLoqIilJeX45577hFOAzN69GisW7cO69atw5VXXslpYAICtVN72KB2as86boxY6X8n47vTAFbNybhcGsAeagBXrFgh7Is3YcIEAMmJoIcMGYJ+/frhuuuuQ3l5uSXHiRMncO+992LgwIEYOHAg7r33Xk4EHRCondrDBrVTe1aJx10awP9JfqaxovsM4OGijEumAeyhBjAo0AD6A7VTe9igdmrPKrEOd0as7Cnr5ypnuDCA5wg3mZYBrJyVcck0gDSAnqAB9Adqp/asUbsU2PgIsPUnQKS+2zYbCO1ZgtoDoL2z3aUBfNr6ueaD3WQAZ2RcMg0gDaAnaAD9gdqp3TfiMf11lwzzl86qb/rXDhs87tSeVTpaXRrAZ6yfO13pwgD2F24yLQN4cGrGJdMA0gB6ggbQH6id2h2JnABOH05vA7FOYOHngUXXAKf2imPMXzozB6WX3wM87tSe3YY0d80AtlR5NIBPuzSA+RmXTANIA+gJGkB/oHZqd8T4YmirS607uhKoWZz698Yd1i+V+o3yvDID2FIF7Hsb6Iw4tzENeNypPbsNaXJnxLb8yPq5lmrnz0wfIN5kOgbwwF8zLpkGkAbQEzSA/kDt1C4l1glsnpz8Yqhdaqs3dWZvb7DW2Q2gvT8T4GwAZ33qzBfhs+kLVMDjTu1Zpb3BpQG0nfettd1jAPf/OeOSaQBpAD1BA+gP1B5y7ZFWYOlXgc1PWANOfJT6xVCzxBrTejRZ11BmrTu53WYAbSMaAWv9rPPU9YemexNsgsed2rNKpN6lAfyx9XNtx7rHAO6Tr9DVVWgAaQA9QQPoD9Qebu0dh+cnb/xmioalfjFUL0rWN+5Mra/flKxP1wDma3pfQ1V9hrWH+bhTexZxY+TyNX10vOVzxz0ZwA2uDeBbGZdMA0gD6AkaQH+g9pBqr9+GPTO+ifi8y8UGa/onBAbw/WT9ZsHi9B/cnaw/WZ5a31pr3Ya9vniEut5sQL1oD/Nxp/bsa2+t6ZoBjJxwYQA/IdxkWgZw7/9lXDINIA2gJ2gA/YHae6j2TY+ljhI0s/t14IPv6F82K25Pnd1fdOM3IzSAC5P1QgP4nWS9yAAuusZ9G7a/7NzGLtKjj7tHqD0A2luOuDSAz1k/136yewzgnj9mXDINIA2gJ2gA/YHaA6q9eb8+X5gI83xgMUnbncyTqN48Z5/IAB5ZkKwXGsBvJ+tPbutaG1R1NICeofYAaHczn1++BmzNs37Ozejh6QOFm0yrD+Du32dcMg0gDaAnaAD9gdoDpr29Edj4Q/1GPOdicUzTnuTNuvWo3mm7vdEa0xXzZTaTQgM4P1m/6fHU+tXfStY7GUDZeqiq9tEAeobaA6C9+YA7I7btp9bPuZk/UGEAXT8B3PXbjEumAaQB9AQNoD9Qe8C0r7zT2fCYDWDJDfp/V95hjemKAexoSdaLDGDV3GS90AD+W7K+YYu8DfG4PsWFqp4G0BeoPQDaT+1zaQCft36uo8X5MzPOFW4yLQO489cZl0wDSAPoCRpAf6D2gGl3MjzRU+IpWvI1fcWNBWP0yZO7YgCjZ64tlQE7tU+P2fSYwADepdepJqwFgOPr1fWqtVIzQCCPezdB7QHQ3rS7iwbQxRJyCgO4pOiP7rZb8WrmJdMA0gB6gQbQH6g9YNpVhqfil+ob99KvqusNTh8S1xujfNf/QJ5j2c36yhzz/zG1btW/6p9fM17dhiPz1PXRUzSAPkHtAdDeWOHSAP7M+rnOiCcDWFxcjI7aD1wYwP/NuGQaQBpAT9AA+gO1B0y7yvA43bjnX6Guryp2ziMavWsuCz8HrLtfXLfqm3r+lXeoDVzVXHW9aqLcDBDI495NUHsAtNvnyJSV8p9bPxeLejaA0aiLHDteybhkGkAaQE/QAPoDtQdMuxcDOHekc8yH/66u3+Pwmmj+KHndyjv1dq643cEAFqvrVfOkxWOed3Egj3s3Qe0B0N6w1aUBfMH6uVhn9xjA7S9lXDINIA2gJ2gA/YHaA6bdfjPubNenhBHV2UvxCHdfLKqy67fq+nmXqesPTQdWfF1ev+XHwOHZ8voV44DmgwqNw4GO0/r+iMf1ATFpmsJAHvdugtoDoP1EqbtrcfuL1s+p+uYmDGCucJNpGUC78cwANIA0gJ6gAfQHag+YdvvNuOAs/b+1S72bOzel4lV1/ZzPOudYfpu6/nChul60zJy57H9X31c7X9P/f+MP09rFgTzu3QS1B0D7ic1dM4BA9xhA++CTDEADSAPoCRpAf6D2gGmX3ZRXfbN7DGDxZ9T1sy9yzrFsrLq+cqa6vn6Tun7vm/q+mpqT/FsaBPK4dxPUHgDtxze4NICCV7FOn1kxTrjJtAwgJ4L2BRpAD9AA+gO1B0y76sbeHQYwE2XZzer6Q9PV9bXL1PXGUlUFH6cBTBNqD4D2urUuDeDLqZ9VxU8fCLQdF27StQG0Tz6dIWgAaQA9QQPoD9QeMO2yG/OCMdk3dm5LyU3q+oNT1fWqPoL5mj7dTKwTKOhDA5gm1B4A7XUf+mMAFRM40wBmHxpAD9AA+gO1B0x7ts1bd5QDf3Wo/4tzjtX/Zv3/NAjkce8mqD0A2o+t8scA7vqNdJM0gNmHBtADNID+QO0B0u70arS3lH3vZD5nGgTuuHcj1B4A7UeXuzund0xJ/azvBjDzA0AAGkCABtATNID+QO0B0p5tY9ZdZc8bNIBZgtoDoL22xKUBFEzIrDSAv5VukgYw+9AAeoAG0B+oPUDas23Muqvs/BUNYJag9gBor1ns0gD+IvWzNIA9FhpAD9AA+gO1B0h7to1Zd5WiYTSAWYLaA6C9+n1357RoTV4awB4LDaAHaAD9gdoDpN1v47Xo6uybP79KGgTuuHcj1B4A7U6j3I1Styb1s0oD+DvpJt0bwJ/5IpkGkAbQEzSA/kDtAdLuuwG8JvtGjQYwq1B7lrW7Wc83X0vOdWlH9fScBjDQ9FoDmJeXB03TLGXw4MGJ+ng8jry8PAwdOhRnn302rr/+emzfvj2tbdAA+gO1B0i7VxNU/oK6fslXsm/U/DSATbv1pew6WpW7OXDHvRuh9ixrP13p7nw+tVf8+c4IDWAPpVcbwCuuuAK1tbWJUldXl6ifMmUKBg4ciMLCQpSXl+Puu+/G0KFDcerUKdfboAH0B2oPkHanL4Xdv1fX7/0/BwP4L87bWPh5dX3ZU9k3ezIDaG6jgsAd926E2jOgPdYJrLtfv97S5dRed+dz8355DtX9QYJF+/53aQCzQK82gGPGjBHWxeNxDBkyBFOmJOc0ikQiyM3NxRtvvOF6GzSA/kDtAdLu9KVQNUdd7zTB8tKvujCAV6nrtz6XfbPnZAAXf1m5mwN33LsRas+A9sOF1vMuHU6UWs/bujUSA3hAnkNqAF+XfiRFOw1gt9OrDWD//v0xdOhQjBgxAnfffTf279d/wezfvx+apqG0tNTymTvvvBP33Xef623QAPoDtQdIu5PJqd/kYBDnqutLbnTexvtfUNdvfyn7Zs/JAC66WrmbA3fcuxFqz4D2vX9Knmu1S9P7rHkVkGnn6H8Tnc+nD8lz+GkAy3+enh6X0AD2YgO4cOFCzJo1C9u2bcPSpUtx/fXXY/Dgwaivr8eaNWugaRqqq6stn3nwwQcxduxYac5IJIKmpqZEqaqqgqZpqK+vRzQazWhpaWlBcXExWlpaMp476IXag6NdZXA6Nz+JaP0WaX3s/X9CtL1NnWPTfzoaqdhCtQHs3P6L7Js9QTHvv/iCz/eo4x7mcz7I2jvLX0bHvr+k/L1j1x+s596J7a7b0FFZLD1vLX9v3CfNIb02d/7OtXZpji0/9WXf19fX0wBmuwHdxenTpzF48GC8+uqrCQNYU1NjiXnggQdw6623SnOIBpZomoaCggIUFxezsASmzJ09MyN5VAanuLgYS4v+KK1fOPs9ZY6TUz+DebOnOhqphqkjlfVbZj6kNoj5fbNiAM3aG6eOyPo5wZKdMmd2IebMLvScZ3nhryznlrmUzXzEcu6tm/Ws67ybZj0pPW/NZVHRW9IcsmtgZeEvXLdDlqNixnd9OS4FBQU0gNluQHdy8803Y+LEiV1+BcwngMH8VdybSia0d36kD4roOLrW3WdaG/TY9vaUOpXBiUajiDbuk9e3HFfm6Kha5LgN5GuIva+eK7Bjl3oZt86K32bFAJq1xeePQjTSgo7a1YhGUo+t8Lifie84uiaxLz2V9nY9V+vJrJ/nmT7nA1va2xAv/gfE51yMaHtEqH3B7L+gtWadY66OqkXWa89UOne+br0mKue6bmPHrj9Kz1vL35sOSnMIr8sDU9M67tLrd+vzvhwbPgEMkQGMRCIYNmwYnn/++cQgkFdeSa5r2N7ezkEgbjgyT5801EcCq70byIh24+a59KvOseYloBZdDbRUJeuq1E8AAQCttfL6jhZre+zl2Cp1faJdDnMF7ntHXX/gL1kxgIjHk/+e/4/AxjNPaTY95u64r/9B8vOzL+riyWDC2E+Lrkmti54Cdr6mTwnihvZGPd58vhi0Hdenvmk75ipVtLka5TO/j2hzdWplyxF9O+0n3bXr9GE9Piq4J5/aB+z8teOUPAD0fnTlP9fjO9vcbVtEa03yGEZOpFRHo1F05ufo9Sc+cmiTab3evW/q55fBHtuT+JrF4hyNO4FdvwE625N/q3g19boWnc8tguNjIIoXHQObdss5L7uOyl9Q75cuwj6AvdgAPvnkk1i5ciUOHDiA9evXY9y4cRg4cCAOHToEQJ8GJjc3F0VFRSgvL8c999zDaWCc6DT151pxu/6l4QNC7fE4sPkJ/YbcGzm+Dvjg24g27sucASy5wX2sUYo/o/+9/aSzwQH0LzVZfczhxl63Vl1vlMVfVtfv/7O63snIui0fflddf2R+qn7j3/MuTd13JoTnvGh/dxX7yE4ztUuTfy+6wF2+NfdazxcD82hUh5HPBrHF+lRAsSXXWSvMU4N8cLdzon1vJ+PXCt7kTD3TFaD0f5xzmffVlmdT66vmAO//E7D8a0DDFnke8w+k5V8DOk5bqi1PvnZMkSQB0HoUmHW+tV01S5L1dgMoGwhi1G9/Ofm3bc+nnhui87u1Vt4+UbzD9wMNYPbxeFcJLsa8fjk5Objgggtw1113YceOHYl6YyLoIUOGoF+/frjuuutQXl6e1jZ8NYCR01g4+8+INh30nqyjVf9VrvoF5wa7KSh7xvkz0Sb5L8H2hpQbIiC4MTTutG5X9GShrU5/KuGk8/g6YPbf66PTOiPO7VcRj+tPKFprgViHt1wmfVtmPuRsAA/8VddxZD4QjyX/bjcpBwvkOcxPJ+xfAM0HnA0RoN/kZfXGEwpZff1Gdb1RnCaL3v+euv7ocudtuCmG6ZEVs5HK14ANE5P/nnOxtc52vnTZALZUA5F64XVkIUXLeHmd03Wx67fyeHuumMN5vO2n8v2SkqtTnidSb42d9Slr/dr7rPUH/qJul+X8u1Zdb4ycFWGfYsW2rm309LFk3abH5Xk++E7q/jD/GN5tfQWM2mVqXSvv0O+ZnRGg9L9TzzPR+d16VN4+UXy0WR4PGsAgILmrEDf4eQLF55qeGIgm92w7rv/adrrBAsCMv0vmEr2+bakGDk4FKmcAh6brr0pE2G+ya/5DISBufRpgvrHHOpJf2tP6p3zUcmOwb9Mo5jmpWo6k1lfOELercEgyZu5IefvdsP1F9ZeEmeaDwJEF8npb+5UG0G6I19yr/938ytHJONhz2OObdqvNjhGnWgVAoi1RXBvAa9X1Tq94T2x23oab4mQAVUaz+DM2Tf9iORwpX4bNB9XHsb0B2GAa/DK1n/x8adqV3vEpHi7PJYqfe4m8btGX0stl3i/2upV3inO0HRfrMz+xcntdAPp1amnTV5zbLZoj7/j61LgNDyXro02p9cvGin/AiqZCWnJt8vX0ztesdeang6p2z7sU2PBw6n4R7a+2OnFOWbzRBUQCDWD2UVwFxAlfTyD7RXB8fbKucpbp4vg5sOMXwNoJ+oSZ5n4h8Xjq4/33v5C6rRnnOt8cY1Fg3fdtX4gKA3jQNrpz/QPJfie7fqPcluXGsGCM+Kaw8zU9uO04MPOT4hj7q5njG1zo7NBf+Ri/oFtrgI/+C2jaY40Tfdnb6Titv25a8x/JmAVXivtN2XIpDaDsy8z8ytHpi27Lj+U3XEDfd7J6c1w8pq6XtTdfc2cA6z5Urxay/13gwN/UOZr2qOvNrw5lpWax2gDu/j1wdIVzHtH+icfQWfojrJmVp/4y3Pun5GdEr8XN1z6g/79qjkTjR5mbcybWqZ8zayekf6xl7PmD91ztJ/UfcqLYoqHqc/RwUWq+Y6vFsY3Jt0fY95a7ttmfOuZrwPoHk/V1HyrOyXeScSfL5XGbJukx9uMs+7EpymF5a/AxeVzbcXFOWXy6yx/KNG5/UZmnq9AA0gB6olsNoNONUXSzEq3SsOBK63ZkN8cPvmP9Qtn5a3HcsdXWfLt+p78SEcVW/FKPsa/+MDVHX+2hoQxA8sbQuSVPrtHIteqb6n2x5cfq/bb635KvUO0G0d63zNimLJf9FdVHT4rj7JMCC76ko+2K13Cyc0P2tNS4gRoG/MRH6n0WqdePiSqmfpO6PW7OVycDuOHhM+fLdZKYM19WB/PVbW09qq4/8DfrZLj2YjzdXTNerVWVQ1QKPm59Op+vIdrert5Oe6N8n5XcaP3SdWpP9JT8+l/1Tf18Ns4Zp36WgB4vqlt7n/Ve0laX2icyJZfkx8zmydZrQWZIzbk6WtT15gER234mjpuao9dHm+W5zG85ZEYyX9P7YzrF5Gv6k0fRU0JzMV4/lz1j/XtVMYSIcqwYp/93xrnAyW3yOMEgFmVeh8EzNIDZR8t2A3oyWTGAstd85jL/Cv1mLPsiMYxMPK6bEVme3a8nb9z21wT2dqnabZT1P9Bjll4vrj/TAd24MShz7TgzgnvWp5z3B6A/dZPVH12px0zr7y6XrH/cvMuSJtBpDVxzv0hBfWyRpAO97NVt2zH19tY/qL8mrPtQ/sTUKJseV9fP+Dtrm2SG32DZzeJ6RwM4UX2+GAawcoa6vSoDkK/pBlLVDuO1pPlJrkirbAmtNEr0eJk6xng9KKvf+Wvd4Lc3Jkcdy0rbMfVSfNt+Ckw7W38F57RiS6wTWDBaXt+0S3+LET0lf6qfOF6tev9W1bYA5+NqHBeVEd74qH5dHF2ZOohHlEt1nRUO0c8B1cAoc66jK53jtv5EXT+1r/62wv73ypni+4coh9HHtnKWOq69QZxTFm821gJoALOPlu0G9GSyYgBVfa6MMuNca/80WS5VB36jHPirHrtBMdmugbnPn6is+74ep3ql11ih3xhmz1bnMkaxzRzkrMHpi6J6oXyf24vTPjOWS3LKY+5/KIuxv9IT9QszSvkL7trvpqy+S10/85PWdnWcTo0xzBugPxkR5Tm5Xa+XGcjEE8CvStpyxgB2tgEFfdT7UaXHGCgjq196ZnSqkwGMdah/ULkosbX3q2Oa9+sDmWT1qmvLXk7J53BMu2Qyl70PXso+OAjs/JXz6HDjHMtUuwD9+s5Ers6I89uLfM3ZLBeclfIUGfmafPCXKtfR5eo41VQ8oniH/unuDeBLyjxdhQaQBtATWTGAbn5dTh+g/6p1upmpnooZZfE/67HrH5DHvP8F5yky8jVg3ffEHaNtpXPr8zg0XfLUKHFTOPOrcEau83Ybd6jrj8yX73N7UfXHydeSncHd5FId63zNOrqypVqda+tz7raZiTJzkPP56lS/7v5knay/oWEApdo+lswhG+hgtKXil/L6g1PVx2Hp9Xr9rt85H0u3x76r5eS2zOXKwBPLRBH1r+1qcfohmU6p+N/M5epo0ad+yUQue9/sTJf976Veg07n5pkuONI41bx+oniHmRFoALOPlu0G9GSyYgAbHF4R5Wv6axunGMDZzORr+ii+6kXOcW6KqFN0l2+gP5N3Ik+3VM2VP6Wyl7q16npjsIibXJ0R8eg+c5lxrv7LfOHn1XGbn8jcvnUqmTCA9ikiRAMsDAPY2QYs/JygLSYDqHoCpbqm8jXg0DR1vTGhdqxDf5Wu2obbY9/Vonr6l27Z+6fM5XKaiiedojLa6RanNyHpFFn/wK4Up8nNvZaP/ksfeb7/z+7PTePthSxONa+fKN48PZUAGsDso2W7AT0Z304gWcfs6oWZu0EAwLEPnOOcDEo6xU0fO7dlwZWZy1VVrM+L5Sb2cJG6vmGruzn08jV9Sp5MaejOMuu81HPWHuNUbx8hqDKAgN4VQdQWA9nrcfOIa5meQ9PV9eYJtRsr1O1Q5clE6annTLZKJp+MO00DlE4R/qDxqRg0bFXHOfRLVs41KYq3d2GxQQOYfTTnECLDtxPITT8/r+XgVOD9LwbrRpWt4jSIIJ2i6lRvLzJTE/Rin2QXSI1xqrd3EBcNWDLPmWafVsi+ndOH1fWiNpiPv6q+5MZkDtmUMmbskxtnet9n+/iHtaj6mQa5AO4GD5oNm6heNa+fbLsK3BvAl9WJuggNIA2gJ3w7gVRTDbCwZLvMOj/1nLXHONXbXw85GUCZSTdQrWoia4NRjBGTsnqzAWze77wd2XQoLCzZKIC7AX9O14pqWhenfAJoALOP81EiUnw7gdobsn/TYGGRlUwYQDtOBlA2MMBANj2Hqg1GMaa/kNWX3JTMIRsF6qSXJTxFtGybn2X6QHU9IH9Cbo9Tnb+qJQKd8gmgAcw+zkeJSPHtBHKa042Fpatl56+cY5ympyj8dOo5a49Jt140xcrWnyTrqyTzQhrIRser2mCU2qXq+mU3J3PIRs476e3ukqkBUvkasGNK5nJlclRuJgeL2NfS9VLcXGOZLE7bq/vQ3ehxp/NXNa+fUz4BNIDZx/koESm+nUCitW1DVOILHEa8snS9VM50jrEv+Wcv3WEA5/yDdaTwkXnithi0N6rrRW3I1/Qnj0bfJ5neZbc4X5tOeru7qNYlTjvXiszlyuTUM/WbMperdmnmcu36beZyqSbgN4qbpQydVh05XOh8/qqmdRHFO0ADmH2cjxKR4tsJpJrwN6jFzTyALktsoYvBKfkaUDTMOWbtffpyb9neP15KOpP7OhU3o0g3PKQvfyarLxyces7aY9KttxtA85xkgHgE/EdPJutFk1HbtyOqNz/VkE2lkwkDuOLrjvt960zFZOvpltpl+lq4mciVycmUneazzFauTBrmTD5NrC1xjqmcBUz/hHOMqt7p/M3XUpe6dIp3wLUBNFYMyjA0gDSAnvDtBJKNNJx1XuZuLPmavmRcJvIs/mf91+H0ARnJ11Hr8GvVKIemOcesneBuyagtz2Zuv867zHl9Wrdl9V1A9fuZybX+QaBmiXPcxh+qO40XDkk9Z+0x6dbbDaCxJqlBzWJr/fLbrCsNdLaJ26pqQ75m/VKLRcWjuJeNTcZ01QDuecNxv2+d+RA6N/+X8/FxMzVTR4t8beh0y+nKzOTJ1+RTXJnLjHPd5Yp16stHqmLcmmA394iSG5xjPnoSaDvuHOd2TlTA+RV8zRJ9fWVVzI5XnLfjdK2opnURLVnngCsDaF6dJMPQANIAesK3E0j2i3v+P7q7aXS2OU/xYnypzR/lLqeqlP9cz+XUGTlf0+dcU9Xv/j2i0SiaCxxu7Pma86TM+Zq++gjg/BRt12+87wejdLTIR6WmW+Ixd5P/rrzTRa64btTnXKyO2zxZ32eyHxxFQ1PPWXtMuvX2L8TGHdZ6+5MQ+9xgsom8VW0w9okZ0bxxy29N1rvpayjalotJl7fOfFD/Mpya43x8VPVm8+x0ThjrwKpK5IS7qW2clmXcNElvk9Ok5YWfdt7W1uf0XE5TZi3+Z+dcxvFr2q2O2fiouzyA/tRKFbfjF+7b5bR60rFVetzsC+UxqpWczO1WnTdOzLs0rXhXBlA1+UYuTNsAAB3LSURBVLRHaABpAD3h2wkkW+1jwWhg+dfc3TScfv1/8G09zmlG+j1/BFZ/y/lmBrj75S5b9ssoh6YjGo1iSZHzExPHXPlaci3jpdc55HKxwkq+5vwqJV/TTYWbaRfcFEA++bC5uFnVwcBpRGDp/9PjZp0vri+6IPWclW3Lbb3dADbtstbb+6HZDaBsnjNVG0TtEBrA26wxoh8LTnr3veV4fFYXvqx/GYrWdjWXj55U17uZ/NqsTVW/Y4r7XMXD3Z1/Trlm/737c9kp1zKHJSXzNX2lI8B58J3TJPDTzkm26dRedaybASxGNwinCZzr1rjbptv9CYjvrU4svCqteHcGsFmdxAM0gDSAnvDtBJL9enz/C/qC3Kp1fudfoeeQ9Ykyyof/rsc5LagOOK8ZXPGqHuf0xZWvOffbOTIvcWPo3ODwi9tJ44aHkvPNLb3eWafTGqSGTqcnFIC7ueBkkxvbczmtLFL2jLtXawZOX3RbfnRG52BxfXcYwFN7rfX2Tuyi1QHs21gxTl0vaocbA9gVvfveUe7zzq0/S34ZzvykPPbD7wKl/6M+fpETas1G2fyfwIrb1TFOkwMbZccr+tJjbs4/p1z7/+z+XHbK5WYZS4Nokzxma57zZMrT+idzOd0z972t9y2V1S+6JplL1iXIKO2NydjynzvrddqfBvZ+wE7Yl6t0wJUBVK0+4hEaQBpAT/h2AslGyhk3BdXjfGOyTidDsGa8HvfhPc43Bqd5CXe+psct+pI67sPvqm+y+RpQs9hkAH8oj9v0uL5NlZmp+zC5T51W6ACcb/DtJ/U4NwYQUL+WrfilHuPmxhyPqY160273uQDnY7Drt3qcrP9U0bDUc1a2Lbf1dgPYfMBaX/ehtd6NAbSPWlTtEwORAVz/A+96HUxNtKHCZAAlr1ILPq5rqnhVffzME/bKYhaM0c8rpyUQnfZfvqbfQ+JxYM5nvecyupPM+QfvuQ5O1evdDoDobJfHnNqnx6ietE8fkMylegMz4+/0J1uxqDzGbABVT+xbj1r3RVen7BERCAOoWH3EIzSANICe8O0EOrpSfDEs+Re9XrYgvdsbY76W7BvXehSY2lccc7hIj1HdGPM1YOev9bhT+5xvMk5PxmqWuDOAbnSe2JyMcerA7ZSr5UgyRvZkLJ22Vc1xPkbmXCpz2rxfj3H7BEZ1PJdcm5zwVTbK2vjxoNKZbv3aCdb605XW+nQNYPFnUutFI4nt2A1g0VC9U79XvQf+ojw20cb9zgZw3mV67o5W9fns5qnd6m/p9au+4f1cNvrjzR3pPZdh2py6PbjJdaI0GfPhv3u7xszX/4LR4pjpn0jGqN5OmPq1xWX3XrMBVA0qsbP9RfV+c5sH8GYAjW5BClwZQNXqIx6hAaQB9IRvJ5BsTqp9b+n1R+Z7uzHma9anGrXLUusLPu4+l/HECND7DDq1TTapb74G1JaYDOAkbzpPlidjSm6Ux5mfaMliIvXJmMIh7m+mspiquc771W0uwwA6jdY0UH3RmXWK+mLNu9z6yknWtnTr7QbQ/IULpD4V74oBBFJHjdqxG8D6TeI86ep1GGAQba5OfhnKBt/M/8dkfpXBULXDKEYfYKduCG5ybc3T6yt+6T3XoWnJGNXrTDe5zFMJqd50uMllvi5k66ObDaBsUFK+ZnkyHZd151n85WQuVX9iO11dh1qEFwPoAncGULH6iEdoAGkAPeHbCXRkQeqFsO1nyf5s8bh8Wgkzqgt+w8PJuGOrUuvTMYC7f5+MkxlAo1+ZU75jqzNnAJv2JGNKbnJ3c5eNijR3RnaaWsJN24yO59PO9pZr5iDrdCiqJzpu2mUedSfq1H/gLxDi9IXiVG83gK011nr7iO/uMoAnPsqc3hObpfs92lLvwgCOSuZXPUk3U3CWOMZ4OhaPq1cNUWkyyulDyTZt8njNHp6djPFsALckY7waQHNfNNmPyekD3eUy0bG/QBxjNoDxmPxpvB3RdFYzcvU+tPZRuqo8QBcM4FXuY+HWACpWH/EIDSANoCd8O4FET8jq1lpjZCO+zMgu9nxNX5LJQDRL/NQca67S/6e4aRcl40QGcM7FqRpFeZaNBWKdSQO4Q9HXyYxseSnza8RNj7vLJftiNZssp7nHHHXeknwK4LQyglMu+y9k2QCBeZc757LnE71S7i4D2HbMWt8bDKDo72dKNHLaZAA/JY778B7rNmR9gc0c+yC13v5UU/W61UxBH8E1dsgaoxpIpdo/+Rpii//Fep15NYDmfqReDaC5P6ns3jvjXHe5TESjUVTMuDc1xt7vVNZtw07ZU6kxH3xHr1NNYyPCbAA3PSaOMeOHATSfDxmGBpAG0BO+nUCi5brsBlD2us+M7GIvucn6RS8ygGsnpLZLlGvN+OSTSUD8ZHLh51JzieY6O0PixhA5LZ7Ha8lX3LXN/BQp2iTvo+Qml7lflcoAHl/vnMuOzFwvujr9XNFm8dNO4zWxKpddp6gjvhsDOHekut7o/2hm3fesMfZ+d2kbwBHidqZtAEtTY+zbMk8ULao3b0ey36Pt7ckvw42Cvq+z/95dX8TKWakx9h8/xoAGg8Yd8vPBjGg92ajt3iczgMZUJQaCuS0TRsCg/AVxLvOTPUDcZca+fNjeN8W5Vtyu3p/5mv6E1I5ocvYZudaY/e857tNoNIptM++3nbvD9YF3dtxc/x8JJhI3fji0N+o/BN3kAawG0A1+vAJWLT/nERpAGkBP+HYCHRS8FrAbwNajqTHmpbEA+S97+xeA3QDOHyWef0mUyzzlBCA2gAvGpOY6fUh6E7LcGETz85lf2ara1lprjZGtaeomlxnZSMA5/5B+LkD8qz1fS/3CX3Cluxt3405rTOl/p8bI5vgzI3pl5MYA2kcm2utF2A2g/RjbDYPIAJrPFTcG0NxnK5HDNv+ZkwGc1j85Olym16z5RKlwv1vO+Y4W4P1/ssYcmaduh2rf2idetv8YkN4n9qbmsk9d0tFqrRdNgC5aOhBIea2ZYgBFE+Iv/WrX9kOsM3W1iukDUo+daJsiRAbQ3Bda1i7bE2WhAaxZ4k5jY0VqzKbHUuPMg7ZET4RlGgNhABXLz3mEBpAG0BO+nUCiX47H11lj7CPDlnzF+iTOQHRRnT5sjbH3TTK/HnbKZTeKok7l9l/ZBvb1K89guTHYb8jG/IVObVvyldRVHkSvW0U3W3uM/UsiHtf7NNrj5nw2NZf99bRoSpGyp1NyxZZ9LTVOtNyZCPsXusgARpsFOm1PHeo3pcY4GcAl16rrZW3OhAE0b8eNAZRNMms+55wM4MZH1fX5WnJqE1Fdvgasviv1y9C+nuyR+c7bke1buwG0/wCMdYpXDhJhN4CiL2j7HHhdNYBA6g/Akhu6vh+a91tjNv6w67ns8wtumiReKs0hVzQaxUczbaZNtvyZm3ZtfCQ1zpjxARCvKlI8XJwrbQPowytg1fJzHqEBpAH0hG8nkGjVAPtN2z4KcN394lyii8revyoet04tk44BtHfSjXVYVxco6GNdmcCMGwNoHz0pM4D2qRlEN47jG6wx234qzmV/UihCZNJFfR0B67QeIkQGcPnXxbFuvgTsT2GMlT26ksu+dJ+TAfzov5y3JWLd960xdnPmhwGU0dGajDGPJBVtZ+Mj6vr8j6XW2UfdQ/BlaB+YkckngPbrHxCPDBdhN4AyzDGitaMBdwbQfp8rudF5e7J22d86eDGA9tfhu37XpVzRaBRzZs9ErGRsMuboiq63a/fvU+PMTyZFA5Hsyy4amFeccYO564ILXBlAH6EBpAH0hG8nkH0gxb63xXHmp22iJ0uA+KISvbIyxxqTFNsRTUYsMlrmhdWr35frnD5AeLFbbgzNB60xMgPYcsT5xmFfYWXb8/K2mUcDi0jHAJpHdYoQvJp2ZQDt/Q1l7fNiACtnWGNkBrBxh95nSzZzv9N2TAawY99fU+u70wCa15f1agAL+ojrza944cEAFo9w3rdmAyg7fuY2q3ItMxkV84hdVS6ZAbStXSs0gPbBD64MoMB0A6kG0FibWJlLcZ6Yu7uYZ0JII1fiuLc2JmO8GMBYVB/0kbhfftfaj85sALf8WP2EreO0fk3LDGKKmGb9aXfjTnfhNIBZx9893Mvx7QSyrzWqwohJ5wmgbIHthAGUPAEUrSAhwjwHVvVCedun9U/GGZNcw3ZjsBs78+TOdjY8lF0D+P4XxbFOBhDQB0akawCdSBjA/3HOtf4BV+1KTNSbLjNyXRtA8ZOgFv2L3chhH31q1+TFAJrPX5kBXPWvev3J7eL6Hb84Y7j+Jq53NIC2H4GiuRcBvQ+uowGcnN69xK0BdJuraKg4xmQAFxe9KT7u9ieTJTc5b8/e99fAvqKGVwNojt39unMuwQ+x5IA301NnLwYQsA7qMVYJMjB36/BxlQ030ABmn9AbwNdffx0jRoxAv379cNVVV2H16tWuP+vbCWSfUFVFugbww+8655IZQCC1r5AI803brQE09V+03BjMS73ZR//ZcTSAtj5tsierQNcMoOyXshsDaJvstXPTZHFcVwxg2VPq+qk58icBsWhyGb0l13Z9Xq7j6/V1qmsWi+u3/UxtAAH9C6szIv8BAzgbwOW3Ou8/8zKKMgMYj6eOgLUjM22APsLbrQF0Wg/V3JVCxOb/7H4DaO7W4MIAWrSr2uXGAMqIx4HVd5kMoGRqE3OuPW/I85ljnQygqH8wTMe93fSk040BNOYRFWFekcn+Q8n8I9jHEbZuoAHMPqE2gNOmTUNOTg7efPNNVFRU4IknnsCAAQNQWVnp/GH4eAJtfzlzBtA+sbGbXLUl8hizAVQZKDcG0Oj4v/Aqy58tNwbzOsTNB9XtN/djFGE2gJseU98AnQygvS+NaPoNAzcG0DTAo37q5Yi2nBDH+WEAZw5yzuU3Ha3oXP8w1szKkxsBNzgZwNYa/WmnbHCHPY/MAHrF0QC+4f44Z8oAHpmXjFt5pzjGjYE2MEaRy/ramVaaURpA877YMUUc05XrwskATjvHfS4nA7jw88Jqy3FPGMCV6lyqH/GA9a2JfUJ1swH0cYCFG2gAs0+oDeDVV1+NiRMnWv522WWX4emnn3b1ed9OINPTkHih5NezgZMBtC9KruJkuXUpJhG2V1eO7VIZwGizPuClrc76Z/ONwTxa1TyxqwgnA9je6P7G4mQAAeucZ0oDeL5zLtNrx2VFv3H3NMSJhAGUnM9BMoAQfCF0BaP/054/emuMsW+cnvJ1lUwaQOONgewe4NYAAvoPrn1vyZ9epmMAW2v1H4myV41uDSCgP8na/658YuB0rgtjLjz71Fr2XNPOds7l2gAK5kKFxACKprkCkj++ZdPEGJinCLPdW9O6B/oMDWD2yf5ZkCXa29vRp08fFBUVWf7++OOP47rrrnOVw7cT6Mx8VUenXYVoq2TAhoFxocj6eQH6PFCZuqAayoAZfwfs/LU6bvW/6RMCd2Exb8uNwTz1iWw0sYF5ahYZLUfkN1gzH3w7PaOrMoDHPtD7wO17Rx4TjwMlNyG+YAyKZxfKvwynD9S3J5rDzs7m/9Sn4BDNy2duu2iexiyQEQMY69BfxXt9uhGpB1qqveVQseqbagO49//cX7PxuN4XUTZnmnlCZa+YV2zximmSd8/HPR3T0NFqXSZSlmvepe63e0AwcMlcv1wwrRNsxz1yQn2P62xL7dMnwtwv1D5PK+D+HugzKef8vnf07xbzFEg+QgMYYgNYXV0NTdOwZo11hvoXX3wRl1xyifAzkUgETU1NiVJVVQVN01BfX49oNJq5EmlBS9MxzJ09HS0tLcrYjn1/QWz51xFtOS6N6SyfkrigMtK+9oiLmHZ3cYLS0tKC4uLihPbOzU+ic8Ojzp9tqUds+dfRsffP3jU2VSK27DZ0HCpSxhn7taP2w4zss5bTzRbtKeXYBsRKbkH02AbPx6qjukTPdWJ7Zs4Lj8V+3Ht1aTqI2LJb0VE5R6i9o2ph5q7Z1pOILb8dHXve8p7r1BH9ujg4w3uuhl2IlYxF26H5no97bLH+xD4+9zLP7eqoWalfF/VbHWM7d/wKsVV3IRoRt72jci5iy25FtHG/sN6vc75z02R0bnw8ozkzXYTa2yOItrcjtvb76Cz7sa/br6+vpwHMdgOyhWEA1661vgZ44YUXcOml4l9+eXl50DQtpRQUFKC4uDiwZc7smaiY8V2sLPxl1tvS28raWc9h68wHst4Oll5WZs9G+czvY03h89lvSw8o7xe9i50z7saiojez3haWnlEKCgpoALPdgGzRlVfA3fYE0Mdfhj2hUDu1Z7st1E7t1N67tfMJYIgNIKAPAnnkEetErpdffnn2B4EgQ/2heijUTu1hg9qpPWxkWzv7AIbcABrTwLz99tuoqKjA5MmTMWDAABw6dMjV52kA/YHaqT1sUDu1h41sa6cBDLkBBPSJoIcPH46+ffviqquuwqpVq1x/lgbQH6id2sMGtVN72Mi2dhpAGkBP0AD6A7VTe9igdmoPG9nWTgNIA+iJxsZGaJqGqqoqy+CQTJT6+noUFBSgvr4+47mDXqid2rPdFmqndmrv3dqNQZyNjYolG3s5NIAeME4gFhYWFhYWlp5XqqocFhjoxdAAeiAWi6GqqgqNjY2+/Trx4+li0Au1U3u220Lt1E7tvVt7Y2MjqqqqEIvFsm0lsgYNYEBpagpv/wRqp/awQe3UHjbCrD0o0AAGlDBfHNRO7WGD2qk9bIRZe1CgAQwoYb44qJ3awwa1U3vYCLP2oEADGFAikQjy8vIQiUSy3ZRuh9qpPWxQO7WHjTBrDwo0gIQQQgghIYMGkBBCCCEkZNAAEkIIIYSEDBpAQgghhJCQQQNICCGEEBIyaAADyOuvv44RI0agX79+uOqqq7B69epsN8kzq1atwrhx4zB06FBomobZs2db6uPxOPLy8jB06FCcffbZuP7667F9+3ZLTENDA8aPH49zzz0X5557LsaPH4+TJ092p4wu8dJLL+GLX/wiPvGJT+D888/HN77xDezatcsSE4lEMGnSJJx33nno378/7rjjjpQliiorKzFu3Dj0798f5513Hh577DG0t7d3p5S0+cMf/oArr7wSAwcOxMCBA3HNNddg4cKFifreqtvOSy+9BE3T8MQTTyT+1pu15+XlpSy5NXjw4ER9b77eAeDIkSO49957MWjQIJxzzjkYM2YMNm/enKjvrfqHDx8uXG7t0UcfBdC7z/meCA1gwJg2bRpycnLw5ptvoqKiAk888QQGDBiAysrKbDfNEwsXLsSzzz6LwsJCoQGcMmUKBg4ciMLCQpSXl+Puu+/G0KFDcerUqUTMbbfdhlGjRmHt2rVYu3YtRo0ahXHjxnW3lLS59dZb8e6772L79u3YsmULbr/9dlx00UU4ffp0ImbixIkYNmwYli5ditLSUtxwww0YM2YMOjs7AQCdnZ0YNWoUbrjhBpSWlmLp0qW44IILMGnSpGzJcsXcuXOxYMEC7N69G7t378aPfvQj5OTkJL7seqtuMxs3bsSIESMwevRoiwHszdrz8vJwxRVXoLa2NlHq6uoS9b35em9oaMDw4cPxve99Dxs2bMDBgwdRUlKCffv2JWJ6q/66ujrLMV+6dCk0TcOKFSsA9O5zvidCAxgwrr76akycONHyt8suuwxPP/10llqUeewGMB6PY8iQIZgyZUrib5FIBLm5uXjjjTcAABUVFdA0DevXr0/ErFu3DpqmpTxNCzp1dXXQNA2rVq0CADQ2NiInJwfTpk1LxFRXV+Oss87CokWLAOgG+qyzzkJ1dXUiZurUqejXr1+Pm0j1k5/8JN56661Q6G5ubsbIkSOxdOlSXH/99QkD2Nu15+XlYcyYMcK63n69P/XUU7j22mul9b1dv5knnngCn/3sZxGPx3v9Od8ToQEMEO3t7ejTpw+Kioosf3/88cdx3XXXZalVmcduAPfv3w9N01BaWmqJu/POO3HfffcBAN5++23k5uam5MrNzcU777zjb4MzzN69e6FpGsrLywEAy5Ytg6ZpaGhosMSNHj0azz33HADgJz/5CUaPHm2pb2hogKZpWL58efc03COdnZ2YOnUq+vbtix07doRC93333YfJkycDgMUA9nbteXl56N+/P4YOHYoRI0bg7rvvxv79+wH0/uv98ssvx+TJk/Gtb30L559/Pj73uc/hT3/6U6K+t+s3aG9vx3nnnYcXX3wRQO8/53siNIABorq6GpqmYc2aNZa/v/jii7jkkkuy1KrMYzeAa9asgaZpll99APDggw9i7NixAPR9MHLkyJRcI0eOxEsvveRvgzNIPB7HHXfcYXlCkJ+fj759+6bE3nLLLXjooYcA6PvilltuSYnp27cvCgoK/GtwBti2bRsGDBiAPn36IDc3FwsWLADQ+3VPnToVo0aNQltbGwCrAezt2hcuXIhZs2Zh27ZtiaefgwcPRn19fa+/3vv164d+/frhmWeeQWlpKd544w2cffbZeO+99wCE5343ffp09OnTJ6Gzt5/zPREawABhGMC1a9da/v7CCy/g0ksvzVKrMo/MANbU1FjiHnjgAdx6660A5Cb44osvxssvv+xvgzPIo48+iuHDh1s6PstujDfffDMefvhhANYvBzM5OTmYOnWqfw3OAO3t7di7dy82bdqEp59+Gp/61KewY8eOXq378OHD+PSnP40tW7Yk/ubGAPYG7SJOnz6NwYMH49VXX+3113tOTg6+/OUvW/722GOP4ZprrgEQnvvd2LFjLX0Ww3bO9wRoAAMEXwH37lcikyZNwoUXXogDBw5Y/h62VyM33XQTHnrooV6te/bs2dA0DX369EkUTdPwsY99DH369EFJSUmv1S7j5ptvxsSJE3v99X7RRRfhBz/4geVvf/jDH3DBBRcACMf97tChQzjrrLNQXFyc+Ftvvt57KjSAAePqq6/GI488Yvnb5ZdfHopBIK+88krib+3t7cJO0Rs2bEjErF+/vkd0io7H4/jhD3+ICy64AHv27EmpNzpHT58+PfG3mpoaYedo81ODadOm9cjO0TfeeCMmTJjQq3WfOnUK5eXllvLFL34R48ePR3l5ea/WLiISiWDYsGF4/vnne/31fs8996QMApk8eXLiqWBv1w/ofUCHDBmCjo6OxN/Cds73BGgAA4YxDczbb7+NiooKTJ48GQMGDMChQ4ey3TRPNDc3o6ysDGVlZdA0Da+99hrKysoS09tMmTIFubm5KCoqQnl5Oe655x7htAijR4/GunXrsG7dOlx55ZWBnxYBAB555BHk5uZi5cqVlikSWltbEzETJ07EhRdeiJKSEpSWluLGG28UTo9w0003obS0FCUlJbjwwgsDPz3CM888g9WrV+PgwYPYtm0bfvSjH+Gss87CkiVLAPRe3SLMr4CB3q39ySefxMqVK3HgwAGsX78e48aNw8CBAxP3sd58vW/cuBEf//jH8eKLL2Lv3r3Iz89H//798be//S0R05v1x2IxXHTRRXjqqadS6nrzOd8ToQEMIK+//jqGDx+Ovn374qqrrkpMF9KTWbFihXCC0AkTJgBITow6ZMgQ9OvXD9ddd11ilKzBiRMncO+99yYmFb733nsDPzEqAKFuTdPw7rvvJmLa2towadKkxMSx48aNw+HDhy15Kisrcfvtt+Occ87BoEGDMGnSJEQikW5Wkx73339/4lw+//zzcdNNNyXMH9B7dYuwG8DerN2Y1y4nJwcXXHAB7rrrLuzYsSNR35uvdwCYN28eRo0ahX79+uGyyy6zjAIGerf+xYsXQ9M07N69O6WuN5/zPREaQEIIIYSQkEEDSAghhBASMmgACSGEEEJCBg0gIYQQQkjIoAEkhBBCCAkZNICEEEIIISGDBpAQQgghJGTQABJCCCGEhAwaQEIIIYSQkEEDSAghhBASMmgACSGEEEJCBg0gIYQQQkjIoAEkhBBCCAkZNICEEEIIISGDBpAQQgghJGTQABJCCCGEhAwaQEIIIYSQkEEDSAghhBASMmgACSGEEEJCBg0gIYQQQkjIoAEkhBBCCAkZNICEEEIIISGDBpAQQgghJGTQABJCCCGEhAwaQEIIIYSQkEEDSAghhBASMmgACSGEEEJCBg0gIYQQQkjIoAEkhBBCCAkZNICEEEIIISGDBpAQQgghJGTQABJCCCGEhIz/DyOLkytYcndKAAAAAElFTkSuQmCC\" width=\"640\">" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "[<matplotlib.lines.Line2D at 0x73633a5ac4e0>]" + ] + }, + "execution_count": 224, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ndata = np.array(data)\n", + "fig, (ax1, ax2) = plt.subplots(2, 1)\n", + "ax1.grid()\n", + "ax2.grid()\n", + "\n", + "offx = 2\n", + "width = 4\n", + "thr = 100\n", + "\n", + "delta = np.abs(ndata[offx:] - ndata[:-offx])\n", + "for i in range(0, len(delta)-width):\n", + " if (delta[i:i+width] > thr).all():\n", + " ax1.axvline(x = i+width, color='pink', linewidth=1)\n", + "ax1.plot(ndata)\n", + "ax2.plot(delta, color='orange')\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/fw/base.c b/fw/base.c new file mode 100644 index 0000000..8e7c03b --- /dev/null +++ b/fw/base.c @@ -0,0 +1,25 @@ + +#include <unistd.h> +#include <stdbool.h> + +int __errno = 0; +void *_impure_ptr = NULL; + +void __sinit(void) { +} + +void *memset(void *s, int c, size_t n) { + char *end = (char *)s + n; + for (char *p = (char *)s; p < end; p++) + *p = (char)c; + return s; +} + +size_t strlen(const char *s) { + const char *start = s; + while (*s++); + return s - start - 1; +} + +void __assert_func(bool value) { +} diff --git a/fw/cmsis_exports.c b/fw/cmsis_exports.c new file mode 100644 index 0000000..39874b5 --- /dev/null +++ b/fw/cmsis_exports.c @@ -0,0 +1,48 @@ +#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__ +#define __GENERATED_CMSIS_HEADER_EXPORTS__ + +#include <stm32f030x6.h> + +/* stm32f030x6.h */ +TIM_TypeDef *tim3 = TIM3; +TIM_TypeDef *tim14 = TIM14; +RTC_TypeDef *rtc = RTC; +WWDG_TypeDef *wwdg = WWDG; +IWDG_TypeDef *iwdg = IWDG; +I2C_TypeDef *i2c1 = I2C1; +PWR_TypeDef *pwr = PWR; +SYSCFG_TypeDef *syscfg = SYSCFG; +EXTI_TypeDef *exti = EXTI; +ADC_TypeDef *adc1 = ADC1; +ADC_Common_TypeDef *adc1_common = ADC1_COMMON; +ADC_Common_TypeDef *adc = ADC; +TIM_TypeDef *tim1 = TIM1; +SPI_TypeDef *spi1 = SPI1; +USART_TypeDef *usart1 = USART1; +TIM_TypeDef *tim16 = TIM16; +TIM_TypeDef *tim17 = TIM17; +DBGMCU_TypeDef *dbgmcu = DBGMCU; +DMA_TypeDef *dma1 = DMA1; +DMA_Channel_TypeDef *dma1_channel1 = DMA1_Channel1; +DMA_Channel_TypeDef *dma1_channel2 = DMA1_Channel2; +DMA_Channel_TypeDef *dma1_channel3 = DMA1_Channel3; +DMA_Channel_TypeDef *dma1_channel4 = DMA1_Channel4; +DMA_Channel_TypeDef *dma1_channel5 = DMA1_Channel5; +FLASH_TypeDef *flash = FLASH; +OB_TypeDef *ob = OB; +RCC_TypeDef *rcc = RCC; +CRC_TypeDef *crc = CRC; +GPIO_TypeDef *gpioa = GPIOA; +GPIO_TypeDef *gpiob = GPIOB; +GPIO_TypeDef *gpioc = GPIOC; +GPIO_TypeDef *gpiod = GPIOD; +GPIO_TypeDef *gpiof = GPIOF; + +#include <core_cm0.h> + +/* core_cm0.h */ +SCB_Type *scb = SCB; +SysTick_Type *systick = SysTick; +NVIC_Type *nvic = NVIC; + +#endif//__GENERATED_CMSIS_HEADER_EXPORTS__ diff --git a/fw/global.h b/fw/global.h new file mode 100644 index 0000000..1109b98 --- /dev/null +++ b/fw/global.h @@ -0,0 +1,65 @@ +/* Megumin LED display firmware + * Copyright (C) 2018 Sebastian Götte <code@jaseg.net> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GLOBAL_H__ +#define __GLOBAL_H__ + +/* Workaround for sub-par ST libraries */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#include <stm32f0xx.h> +#include <stm32f0xx_ll_utils.h> +#include <stm32f0xx_ll_spi.h> +#pragma GCC diagnostic pop + +#include <system_stm32f0xx.h> + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> + +/* Microcontroller part number: STM32F030F4C6 */ + +/* Things used for module status reporting. */ +#define FIRMWARE_VERSION 1 +#define HARDWARE_VERSION 0 + +#define TS_CAL1 (*(uint16_t *)0x1FFFF7B8) +#define VREFINT_CAL (*(uint16_t *)0x1FFFF7BA) + +#define VMEAS_R_HIGH 10000 /* kiloohms */ +#define VMEAS_R_LOW 3300 /* kiloohms */ + +static inline uint16_t be16toh(uint16_t be16in) { return __builtin_bswap16(be16in); } +static inline uint16_t htobe16(uint16_t hostin) { return __builtin_bswap16(hostin); } +static inline uint16_t le16toh(uint16_t le16in) { return le16in; } +static inline uint16_t htole16(uint16_t hostin) { return hostin; } + +static inline uint32_t be32toh(uint32_t be32in) { return __builtin_bswap32(be32in); } +static inline uint32_t htobe32(uint32_t hostin) { return __builtin_bswap32(hostin); } +static inline uint32_t le32toh(uint32_t le32in) { return le32in; } +static inline uint32_t htole32(uint32_t hostin) { return hostin; } + + +#define ARRAY_SIZE(arr) \ + (sizeof(arr) / sizeof((arr)[0]) \ + + sizeof(typeof(int[1 - 2 * \ + !!__builtin_types_compatible_p(typeof(arr), \ + typeof(&arr[0]))])) * 0) + +#endif/*__GLOBAL_H__*/ diff --git a/fw/i2c.c b/fw/i2c.c new file mode 100644 index 0000000..6c24c04 --- /dev/null +++ b/fw/i2c.c @@ -0,0 +1,236 @@ +// Inter-integrated circuit (I2C) management
+
+
+#include "i2c.h"
+
+
+// I2C timeout, about 2ms
+#define I2C_TIMEOUT 200U
+
+// Maximum NBYTES value
+#define I2C_NBYTES_MAX 255U
+
+
+// Count rough delay for timeouts
+static uint32_t i2c_calc_delay(uint32_t delay) {
+ uint32_t cnt;
+
+ if (SystemCoreClock > 1000000U) {
+ cnt = (delay * ((SystemCoreClock / 1000000U) + 1U));
+ } else {
+ cnt = (((delay / 100U) + 1U) * ((SystemCoreClock / 10000U) + 1U));
+ }
+
+ return cnt;
+}
+
+// Check if target device is ready for communication
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+// devAddr - target device address
+// trials - number of trials (must not be zero)
+// return:
+// I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
+i2cstatus i2c_is_device_ready(I2C_TypeDef* I2Cx, uint8_t devAddr, uint32_t trials) {
+ volatile uint32_t wait;
+ uint32_t delay_val = i2c_calc_delay(I2C_TIMEOUT);
+ uint32_t reg;
+
+ while (trials--) {
+ // Clear all flags
+ I2Cx->ICR = I2C_ICR_ALL;
+
+ // Generate START
+ i2c_genstart(I2Cx, devAddr);
+
+ // Wait for STOP, NACK or BERR
+ wait = delay_val;
+ while (!((reg = I2Cx->ISR) & (I2C_ISR_STOPF | I2C_ISR_NACKF | I2C_ISR_BERR)) && --wait);
+ if (wait == 0) { return I2C_ERROR; }
+
+ // Wait while STOP flag is reset
+ wait = delay_val;
+ while (!(I2Cx->ISR & I2C_ISR_STOPF) && --wait);
+ if (wait == 0) { return I2C_ERROR; }
+
+ // Clear the NACK, STOP and BERR flags
+ I2Cx->ICR = I2C_ICR_STOPCF | I2C_ICR_NACKCF | I2C_ICR_BERRCF;
+
+ // Check for BERR flag
+ if (reg & I2C_ISR_BERR) {
+ // Misplaced START/STOP? Perform a software reset of I2C
+ i2c_disable(I2Cx);
+ i2c_enable(I2Cx);
+ } else {
+ // Device responded if NACK flag is not set
+ if (!(reg & I2C_ISR_NACKF)) { return I2C_SUCCESS; }
+ }
+ }
+
+ return I2C_ERROR;
+}
+
+// Transmit an amount of data in master mode
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+// pBbuf - pointer to the data buffer
+// nbytes - number of bytes to transmit
+// devAddr - address of target device
+// flags - options for transmission, combination of I2C_TX_xx values:
+// I2C_TX_NOSTART - don't generate START condition
+// I2C_TX_NOSTOP - don't generate STOP condition
+// I2C_TX_CONT - this flag indicates that transmission will be continued
+// e.g. by calling this function again with NOSTART flag
+// zero value - generate both START and STOP conditions
+// return:
+// I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
+i2cstatus i2c_transmit(I2C_TypeDef* I2Cx, uint8_t devAddr, const uint8_t *pBuf, uint32_t nbytes, uint32_t flags) {
+ uint32_t reg;
+ uint32_t tx_count;
+ uint32_t delay_val = i2c_calc_delay(I2C_TIMEOUT);
+ volatile uint32_t wait;
+
+ // Clear all flags
+ I2Cx->ICR = I2C_ICR_ALL;
+
+ // Everything regarding to the transmission is in the CR2 register
+ reg = I2Cx->CR2;
+ reg &= ~I2C_CR2_ALL;
+
+ // Slave device address
+ reg |= (devAddr & I2C_CR2_SADD);
+
+ // Whether it need to generate START condition
+ if (!(flags & I2C_TX_NOSTART)) { reg |= I2C_CR2_START; }
+
+ // Whether it need to generate STOP condition
+ if ((flags & I2C_TX_CONT) || (nbytes > I2C_NBYTES_MAX)) {
+ reg |= I2C_CR2_RELOAD;
+ } else {
+ if (!(flags & I2C_TX_NOSTOP)) { reg |= I2C_CR2_AUTOEND; }
+ }
+
+ // Transfer length
+ tx_count = (nbytes > I2C_NBYTES_MAX) ? I2C_NBYTES_MAX : nbytes;
+ nbytes -= tx_count;
+ reg |= tx_count << I2C_CR2_NBYTES_Pos;
+
+ // Write a composed value to the I2C register
+ I2Cx->CR2 = reg;
+
+ // Transmit data
+ while (tx_count) {
+ // Wait until either TXIS or NACK flag is set
+ wait = delay_val;
+ while (!((reg = I2Cx->ISR) & (I2C_ISR_TXIS | I2C_ISR_NACKF)) && --wait);
+ if ((reg & I2C_ISR_NACKF) || (wait == 0)) { return I2C_ERROR; }
+
+ // Transmit byte
+ I2Cx->TXDR = *pBuf++;
+ tx_count--;
+
+ if ((tx_count == 0) && (nbytes != 0)) {
+ // Wait until TCR flag is set (Transfer Complete Reload)
+ wait = delay_val;
+ while (!(I2Cx->ISR & I2C_ISR_TCR) && --wait);
+ if (wait == 0) { return I2C_ERROR; }
+
+ // Configure next (or last) portion transfer
+ reg = I2Cx->CR2;
+ reg &= ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND);
+ if ((flags & I2C_TX_CONT) || (nbytes > I2C_NBYTES_MAX)) {
+ reg |= I2C_CR2_RELOAD;
+ } else {
+ if (!(flags & I2C_TX_NOSTOP)) { reg |= I2C_CR2_AUTOEND; }
+ }
+ tx_count = (nbytes > I2C_NBYTES_MAX) ? I2C_NBYTES_MAX : nbytes;
+ nbytes -= tx_count;
+ reg |= tx_count << I2C_CR2_NBYTES_Pos;
+ I2Cx->CR2 = reg;
+ }
+ }
+
+ // End of transmission
+ wait = delay_val;
+ while (!(I2Cx->ISR & (I2C_ISR_TC | I2C_ISR_TCR | I2C_ISR_STOPF)) && --wait);
+
+ return (wait) ? I2C_SUCCESS : I2C_ERROR;
+}
+
+// Receive an amount of data in master mode
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+// buf - pointer to the data buffer
+// nbytes - number of bytes to receive
+// devAddr - address of target device
+// return:
+// I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
+i2cstatus i2c_receive(I2C_TypeDef* I2Cx, uint8_t devAddr, uint8_t *pBuf, uint32_t nbytes) {
+ uint32_t reg;
+ uint32_t rx_count;
+ uint32_t delay_val = i2c_calc_delay(I2C_TIMEOUT);
+ volatile uint32_t wait;
+
+ // Clear all flags
+ I2Cx->ICR = I2C_ICR_ALL;
+
+ // Everything regarding to the transmission is in the CR2 register
+ reg = I2Cx->CR2;
+ reg &= ~I2C_CR2_ALL;
+
+ // Configure slave device address, enable START condition and set direction to READ
+ reg |= (devAddr & I2C_CR2_SADD) | I2C_CR2_START | I2C_CR2_RD_WRN;
+
+ // Transfer length
+ if (nbytes > I2C_NBYTES_MAX) {
+ rx_count = I2C_NBYTES_MAX;
+ reg |= I2C_CR2_RELOAD;
+ } else {
+ rx_count = nbytes;
+ reg |= I2C_CR2_AUTOEND;
+ }
+ reg |= rx_count << I2C_CR2_NBYTES_Pos;
+ nbytes -= rx_count;
+
+ // Write a composed value to the I2C register
+ I2Cx->CR2 = reg;
+
+ // Receive data
+ while (rx_count) {
+ // Wait until either RXNE or NACK flag is set
+ wait = delay_val;
+ while (!((reg = I2Cx->ISR) & (I2C_ISR_RXNE | I2C_ISR_NACKF)) && --wait);
+ if ((reg & I2C_ISR_NACKF) || (wait == 0)) { return I2C_ERROR; }
+
+ // Read received data
+ *pBuf++ = I2Cx->RXDR;
+ rx_count--;
+
+ if ((rx_count == 0) && (nbytes != 0)) {
+ // Wait until TCR flag is set (Transfer Complete Reload)
+ wait = delay_val;
+ while (!(I2Cx->ISR & I2C_ISR_TCR) && --wait);
+ if (wait == 0) { return I2C_ERROR; }
+
+ // Configure next (or last) portion transfer
+ reg = I2Cx->CR2;
+ reg &= ~(I2C_CR2_NBYTES | I2C_CR2_AUTOEND | I2C_CR2_RELOAD);
+ if (nbytes > I2C_NBYTES_MAX) {
+ rx_count = I2C_NBYTES_MAX;
+ reg |= I2C_CR2_RELOAD;
+ } else {
+ rx_count = nbytes;
+ reg |= I2C_CR2_AUTOEND;
+ }
+ reg |= rx_count << I2C_CR2_NBYTES_Pos;
+ nbytes -= rx_count;
+ I2Cx->CR2 = reg;
+ }
+ }
+
+ // Wait for the STOP flag
+ wait = delay_val;
+ while (!(I2Cx->ISR & I2C_ISR_STOPF) && --wait);
+
+ return (wait) ? I2C_SUCCESS : I2C_ERROR;
+}
diff --git a/fw/i2c.h b/fw/i2c.h new file mode 100644 index 0000000..d0c7ec6 --- /dev/null +++ b/fw/i2c.h @@ -0,0 +1,107 @@ +#ifndef __I2C_H
+#define __I2C_H
+
+#include "global.h"
+
+// Definitions of I2C analog filter state
+#define I2C_AF_ENABLE ((uint32_t)0x00000000U) // Analog filter is enabled
+#define I2C_AF_DISABLE I2C_CR1_ANFOFF // Analog filter is disabled
+
+// Flags definitions for transmit function
+#define I2C_TX_STOP ((uint32_t)0x00000000U) // Generate STOP condition
+#define I2C_TX_NOSTOP ((uint32_t)0x10000000U) // Don't generate STOP condition
+#define I2C_TX_NOSTART ((uint32_t)0x20000000U) // Don't generate START condition
+#define I2C_TX_CONT ((uint32_t)0x40000000U) // The transmission will be continued
+// Definitions for compatibility with old code using this library
+#define I2C_GENSTOP_YES I2C_TX_STOP
+#define I2C_GENSTOP_NO I2C_TX_NOSTOP
+
+// Definition of bits to reset in CR2 register
+#define I2C_CR2_ALL (I2C_CR2_SADD | \
+ I2C_CR2_NBYTES | \
+ I2C_CR2_RELOAD | \
+ I2C_CR2_AUTOEND | \
+ I2C_CR2_RD_WRN | \
+ I2C_CR2_START | \
+ I2C_CR2_STOP)
+
+// Definition of all bits in ICR register (clear all I2C flags at once)
+#define I2C_ICR_ALL (I2C_ICR_ADDRCF | \
+ I2C_ICR_ALERTCF | \
+ I2C_ICR_ARLOCF | \
+ I2C_ICR_BERRCF | \
+ I2C_ICR_NACKCF | \
+ I2C_ICR_OVRCF | \
+ I2C_ICR_PECCF | \
+ I2C_ICR_STOPCF | \
+ I2C_ICR_TIMOUTCF)
+
+
+// Result of I2C functions
+typedef enum {
+ I2C_ERROR = 0,
+ I2C_SUCCESS = !I2C_ERROR
+} i2cstatus;
+
+
+// Public functions and macros
+
+// Enable I2C peripheral
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+static inline void i2c_enable(I2C_TypeDef* I2Cx) {
+ I2Cx->CR1 |= I2C_CR1_PE;
+}
+
+// Disable I2C peripheral
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+static inline void i2c_disable(I2C_TypeDef* I2Cx) {
+ I2Cx->CR1 &= ~I2C_CR1_PE;
+}
+
+// Configure I2C noise filters
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+// af - analog filter state, I2C_AF_DISABLE or I2C_AF_ENABLE
+// df - digital filter configuration, can be a value in range from 0 to 15
+// zero value means the digital filter is disabled
+// this values means filtering capability up to (df * ti2cclk)
+// note: must be called only when I2C is disabled (PE bit in I2C_CR1 register is reset)
+static inline void i2c_config_filters(I2C_TypeDef* I2Cx, uint32_t af, uint32_t df) {
+ I2Cx->CR1 &= ~(I2C_CR1_ANFOFF | I2C_CR1_DNF);
+ I2Cx->CR1 |= (af & I2C_CR1_ANFOFF) | ((df << I2C_CR1_DNF_Pos) & I2C_CR1_DNF);
+}
+
+// Configure the I2C timings (SDA setup/hold time and SCL high/low period)
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+// timing - the value for I2C_TIMINGR register
+// note: must be called only when I2C is disabled (PE bit in I2C_CR1 register is reset)
+static inline void i2c_config_timing(I2C_TypeDef* I2Cx, uint32_t timing) {
+ I2Cx->TIMINGR = timing;
+}
+
+// Generate START condition
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+// addr - I2C device address
+// note: 7-bit addressing mode
+static inline void i2c_genstart(I2C_TypeDef* I2Cx, uint32_t addr) {
+ I2Cx->CR2 = (addr & I2C_CR2_SADD) | I2C_CR2_START | I2C_CR2_AUTOEND;
+}
+
+// Generate STOP condition
+// input:
+// I2Cx - pointer to the I2C peripheral (I2C1, etc.)
+static inline void i2c_genstop(I2C_TypeDef* I2Cx) {
+ I2Cx->CR2 |= I2C_CR2_STOP;
+}
+
+
+// Function prototypes
+i2cstatus i2c_is_device_ready(I2C_TypeDef* I2Cx, uint8_t devAddr, uint32_t Trials);
+i2cstatus i2c_transmit(I2C_TypeDef* I2Cx, uint8_t devAddr, const uint8_t *pBuf, uint32_t nbytes, uint32_t flags);
+i2cstatus i2c_receive(I2C_TypeDef* I2Cx, uint8_t devAddr, uint8_t *pBuf, uint32_t nbytes);
+
+#endif // __I2C_H
diff --git a/fw/main.c b/fw/main.c new file mode 100644 index 0000000..360728e --- /dev/null +++ b/fw/main.c @@ -0,0 +1,97 @@ +/* Megumin LED display firmware + * Copyright (C) 2018 Sebastian Götte <code@jaseg.net> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "global.h" +#include "i2c.h" +#include "mpu6050.h" + +uint32_t pcg32_random_r() { + // *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org + // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) + static uint64_t state = 0xbc422715d3aef60f; + static uint64_t inc = 0x6605e3bc6d1a869b; + uint64_t oldstate = state; + // Advance internal state + state = oldstate * 6364136223846793005ULL + (inc|1); + // Calculate output function (XSH RR), uses old state for max ILP + uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + uint32_t rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + +unsigned char dumb_random() { + static unsigned char x=0x66, a=0x05, b=0xe3, c=0xbc; + x++; //x is incremented every round and is not affected by any other variable + a = (a ^ c ^ x); //note the mix of addition and XOR + b = (b + a); //And the use of very few instructions + c = ((c + (b >> 1) ^ a)); // the AES S-Box Operation ensures an even distributon of entropy + return (c); +} + +int main(void) { + /* We're starting out from HSI@8MHz */ + SystemCoreClockUpdate(); + + /* Turn on lots of neat things */ + RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; + + GPIOA->MODER |= + (1<<GPIO_MODER_MODER1_Pos)| /* PA1 - Port 1 */ + (1<<GPIO_MODER_MODER2_Pos)| /* PA2 - Port 2 */ + (1<<GPIO_MODER_MODER3_Pos)| /* PA3 - Port 3 */ + (1<<GPIO_MODER_MODER4_Pos)| /* PA4 - Port 4 */ + (1<<GPIO_MODER_MODER9_Pos); /* PA9 - LED */ + + int idx = 1; + while (1) { + GPIOA->ODR ^= (1<<9); + GPIOA->ODR = 2<<idx; + idx += 1; + if (idx > 4) + idx = 1; + for (size_t j=0; j<1000000; j++) { + asm volatile ("nop"); + } + } +} + +void gdb_dump(void) { + /* debugger hook */ +} + +void NMI_Handler(void) { + asm volatile ("bkpt"); +} + +void HardFault_Handler(void) __attribute__((naked)); +void HardFault_Handler() { + asm volatile ("bkpt"); +} + +void SVC_Handler(void) { + asm volatile ("bkpt"); +} + + +void PendSV_Handler(void) { + asm volatile ("bkpt"); +} + +void SysTick_Handler(void) { + asm volatile ("bkpt"); +} + diff --git a/fw/mpu6050.c b/fw/mpu6050.c new file mode 100644 index 0000000..dc374d8 --- /dev/null +++ b/fw/mpu6050.c @@ -0,0 +1,244 @@ +/* MPU6050 device I2C library code for ARM STM32F103xx is placed under the MIT license + Copyright (c) 2012 Harinadha Reddy Chintalapalli + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "mpu6050.h" +#include "i2c.h" + +/** @defgroup MPU_Library + * @{ + */ + +/** Power on and prepare for general usage. + * This will activate the device and take it out of sleep mode (which must be done + * after start-up). This function also sets both the accelerometer and the gyroscope + * to their most sensitive settings, namely +/- 2g and +/- 250 degrees/sec, and sets + * the clock source to use the X Gyro for reference, which is slightly better than + * the default internal clock source. + */ +void mpu_init() +{ + mpu_reg_write(MPU_RA_PWR_MGMT1, MPU_CLOCK_PLL_XGYRO); + mpu_set_gyro_fs(MPU_GYRO_FS_250); + mpu_set_accel_fs(MPU_ACCEL_FS_2); +} + +void mpu_init_low_power(uint8_t wake_freq, bool enable_interrupt) +{ + mpu_set_gyro_fs(MPU_GYRO_FS_250); + mpu_set_accel_fs(MPU_ACCEL_FS_2); + /* For these two regs, see pwr mgmt reg 2 desc in reg map ref manual, pg. 42 */ + mpu_reg_write(MPU_RA_PWR_MGMT1, MPU_PWR_MGMT1_CYCLE | MPU_PWR_MGMT1_TEMP_DIS | MPU_CLOCK_INTERNAL); + mpu_reg_write(MPU_RA_PWR_MGMT2, MPU_PWR_MGMT2_STBY_XG | MPU_PWR_MGMT2_STBY_YG | MPU_PWR_MGMT2_STBY_ZG | wake_freq); + + if (enable_interrupt) { + mpu_reg_write(MPU_RA_INT_PIN_CFG, MPU_INT_PIN_CFG_RD_CLEAR); + mpu_reg_write(MPU_RA_INT_ENABLE, MPU_INT_ENABLE_DATA_RDY); + } +} + +/** Verify the I2C connection. + * Make sure the device is connected and responds as expected. + * @return True if connection is valid, FALSE otherwise + */ +bool mpu_test_connection() +{ + return mpu_device_id() == MPU_DEVICE_ID; +} + +/** Get Device ID. + * This register is used to verify the identity of the device (0b110100). + * @return Device ID (should be 0x68, 104 dec, 150 oct) + * @see MPU_RA_WHO_AM_I + * @see MPU_WHO_AM_I_BIT + * @see MPU_WHO_AM_I_LENGTH + */ +uint8_t mpu_device_id() +{ + return mpu_reg_read(MPU_RA_WHO_AM_I) >> MPU_WHO_AM_I_Pos; +} + +/** Set full-scale gyroscope range. + * @param range New full-scale gyroscope range value + * @see MPU_GetFullScaleGyroRange() + * @see MPU_GYRO_FS_250 + * @see MPU_RA_GYRO_CONFIG + * @see MPU_GCONFIG_FS_SEL_BIT + * @see MPU_GCONFIG_FS_SEL_LENGTH + */ +void mpu_set_gyro_fs(uint8_t range) +{ + mpu_reg_write(MPU_RA_GYRO_CONFIG, range); +} + + +/** Get full-scale gyroscope range. + * The FS_SEL parameter allows setting the full-scale range of the gyro sensors, + * as described in the table below. + * + * <pre> + * 0 = +/- 250 degrees/sec + * 1 = +/- 500 degrees/sec + * 2 = +/- 1000 degrees/sec + * 3 = +/- 2000 degrees/sec + * </pre> + * + * @return Current full-scale gyroscope range setting + * @see MPU_GYRO_FS_250 + * @see MPU_RA_GYRO_CONFIG + * @see MPU_GCONFIG_FS_SEL_BIT + * @see MPU_GCONFIG_FS_SEL_LENGTH + */ +uint8_t mpu_get_gyro_fs() +{ + return mpu_reg_read(MPU_RA_GYRO_CONFIG) & MPU_GYRO_FS_SEL_Msk; +} + +/** Get full-scale accelerometer range. + * The FS_SEL parameter allows setting the full-scale range of the accelerometer + * sensors, as described in the table below. + * + * <pre> + * 0 = +/- 2g + * 1 = +/- 4g + * 2 = +/- 8g + * 3 = +/- 16g + * </pre> + * + * @return Current full-scale accelerometer range setting + * @see MPU_ACCEL_FS_2 + * @see MPU_RA_ACCEL_CONFIG + */ +uint8_t mpu_get_accel_fs() +{ + return mpu_reg_read(MPU_RA_ACCEL_CONFIG) & MPU_ACCEL_CONFIG_FS_SEL_Msk; +} + +/** Set full-scale accelerometer range. + * @param range New full-scale accelerometer range setting + * @see MPU_GetFullScaleAccelRange() + */ +void mpu_set_accel_fs(uint8_t range) +{ + mpu_reg_write(MPU_RA_ACCEL_CONFIG, range); +} + +/** Get sleep mode status. + * Setting the SLEEP bit in the register puts the device into very low power + * sleep mode. In this mode, only the serial interface and internal registers + * remain active, allowing for a very low standby current. Clearing this bit + * puts the device back into normal mode. To save power, the individual standby + * selections for each of the gyros should be used if any gyro axis is not used + * by the application. + * @return Current sleep mode bit + * @see MPU_RA_PWR_MGMT_1 + * @see MPU_PWR1_SLEEP_BIT + */ +bool mpu_get_sleep_mode() +{ + return mpu_reg_read(MPU_RA_PWR_MGMT1) & MPU_PWR_MGMT1_SLEEP; +} + +/** Set sleep mode status. + * @param enabled New sleep mode enabled status + * @see MPU_GetSleepModeStatus() + * @see MPU_RA_PWR_MGMT_1 + * @see MPU_PWR1_SLEEP_BIT + */ +void mpu_set_sleep_mode(bool val) +{ + int tmp = mpu_reg_read(MPU_RA_PWR_MGMT1); + tmp = (tmp & ~MPU_PWR_MGMT1_SLEEP) | (val ? MPU_PWR_MGMT1_SLEEP : 0); + mpu_reg_write(MPU_RA_PWR_MGMT1, tmp); +} + +/** Get raw 6-axis motion sensor readings (accel/gyro). + * Retrieves all currently available motion sensor values. + * @see MPU_RA_ACCEL_XOUT_H + */ +void mpu_read_accel_gyro(struct mpu_raw_data *out) +{ + struct mpu_raw_data buf; + mpu_reg_read_multiple(MPU_RA_ACCEL_XOUT_H, (uint8_t *)&buf, sizeof(buf)); + + for (uint8_t i=0; i<ARRAY_SIZE(buf.channels); i++) + out->channels[i] = (int16_t)be16toh((uint16_t)buf.channels[i]); +} + +int16_t mpu_read_temp() { + uint16_t buf; + mpu_reg_read_multiple(MPU_RA_TEMP_OUT_H, (uint8_t *)&buf, sizeof(buf)); + return (int16_t)be16toh(buf); +} + +void mpu_read_accel(struct mpu_accel_data *out) +{ + struct mpu_accel_data buf; + mpu_reg_read_multiple(MPU_RA_ACCEL_XOUT_H, (uint8_t *)&buf, sizeof(buf)); + + for (uint8_t i=0; i<ARRAY_SIZE(buf.channels); i++) + out->channels[i] = (int16_t)be16toh((uint16_t)buf.channels[i]); +} + +void mpu_read_gyro(struct mpu_gyro_data *out) +{ + struct mpu_gyro_data buf; + mpu_reg_read_multiple(MPU_RA_GYRO_XOUT_H, (uint8_t *)&buf, sizeof(buf)); + + for (uint8_t i=0; i<ARRAY_SIZE(buf.channels); i++) + out->channels[i] = (int16_t)be16toh((uint16_t)buf.channels[i]); +} + +/** + * @brief Writes one byte to the MPU6050. + * @param reg : address of the register in which the data will be written + * @param val : the data to be written to the MPU6050. + * @return None + */ +void mpu_reg_write(uint8_t reg, uint8_t val) +{ + uint8_t tx[2] = {reg, val}; + i2c_transmit(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, tx, sizeof(tx), I2C_GENSTOP_YES); +} + +/** + * @brief Reads a block of data from the MPU6050. + * @param buf : pointer to the buffer that receives the data read from the MPU6050. + * @param addr : MPU6050's internal address to read from. + * @param len : number of bytes to read from the MPU6050 + * @return None + */ +void mpu_reg_read_multiple(uint8_t addr, uint8_t* buf, size_t len) +{ + i2c_transmit(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, &addr, 1, I2C_GENSTOP_NO); + i2c_receive(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, buf, len); +} + +uint8_t mpu_reg_read(uint8_t addr) +{ + uint8_t buf; + i2c_transmit(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, &addr, 1, I2C_GENSTOP_NO); + i2c_receive(MPU_I2C_PERIPH, MPU_DEFAULT_ADDRESS, &buf, 1); + return buf; +} +/** + * @} + *//* end of group MPU_Library */ diff --git a/fw/mpu6050.h b/fw/mpu6050.h new file mode 100644 index 0000000..39132a4 --- /dev/null +++ b/fw/mpu6050.h @@ -0,0 +1,491 @@ +/* MPU6050 device I2C library code for ARM STM32F103xx is placed under the MIT license + Copyright (c) 2012 Harinadha Reddy Chintalapalli + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#ifndef __MPU6050_H +#define __MPU6050_H + +#include "global.h" + +#ifndef MPU_I2C_PERIPH +#define MPU_I2C_PERIPH I2C1 +#endif + + +enum mpu_i2c_addr { + MPU_ADDRESS_AD0_LOW = 0xd0, // address pin low (GND), default for InvenSense evaluation board + MPU_ADDRESS_AD0_HIGH = 0xd1, // address pin high (VCC) + MPU_DEFAULT_ADDRESS = MPU_ADDRESS_AD0_LOW +}; + +enum mpu_reg_addr { + MPU_RA_XG_OFFS_TC = 0x00, //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD + MPU_RA_YG_OFFS_TC = 0x01, //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD + MPU_RA_ZG_OFFS_TC = 0x02, //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD + MPU_RA_X_FINE_GAIN = 0x03, //[7:0] X_FINE_GAIN + MPU_RA_Y_FINE_GAIN = 0x04, //[7:0] Y_FINE_GAIN + MPU_RA_Z_FINE_GAIN = 0x05, //[7:0] Z_FINE_GAIN + MPU_RA_XA_OFFS_H = 0x06, //[15:0] XA_OFFS + MPU_RA_XA_OFFS_L_TC = 0x07, + MPU_RA_YA_OFFS_H = 0x08, //[15:0] YA_OFFS + MPU_RA_YA_OFFS_L_TC = 0x09, + MPU_RA_ZA_OFFS_H = 0x0A, //[15:0] ZA_OFFS + MPU_RA_ZA_OFFS_L_TC = 0x0B, + MPU_RA_XG_OFFS_USRH = 0x13, //[15:0] XG_OFFS_USR + MPU_RA_XG_OFFS_USRL = 0x14, + MPU_RA_YG_OFFS_USRH = 0x15, //[15:0] YG_OFFS_USR + MPU_RA_YG_OFFS_USRL = 0x16, + MPU_RA_ZG_OFFS_USRH = 0x17, //[15:0] ZG_OFFS_USR + MPU_RA_ZG_OFFS_USRL = 0x18, + MPU_RA_SMPLRT_DIV = 0x19, + MPU_RA_CONFIG = 0x1A, + MPU_RA_GYRO_CONFIG = 0x1B, + MPU_RA_ACCEL_CONFIG = 0x1C, + MPU_RA_FF_THR = 0x1D, + MPU_RA_FF_DUR = 0x1E, + MPU_RA_MOT_THR = 0x1F, + MPU_RA_MOT_DUR = 0x20, + MPU_RA_ZRMOT_THR = 0x21, + MPU_RA_ZRMOT_DUR = 0x22, + MPU_RA_FIFO_EN = 0x23, + MPU_RA_I2C_MST_CTRL = 0x24, + MPU_RA_I2C_SLV0_ADDR = 0x25, + MPU_RA_I2C_SLV0_REG = 0x26, + MPU_RA_I2C_SLV0_CTRL = 0x27, + MPU_RA_I2C_SLV1_ADDR = 0x28, + MPU_RA_I2C_SLV1_REG = 0x29, + MPU_RA_I2C_SLV1_CTRL = 0x2A, + MPU_RA_I2C_SLV2_ADDR = 0x2B, + MPU_RA_I2C_SLV2_REG = 0x2C, + MPU_RA_I2C_SLV2_CTRL = 0x2D, + MPU_RA_I2C_SLV3_ADDR = 0x2E, + MPU_RA_I2C_SLV3_REG = 0x2F, + MPU_RA_I2C_SLV3_CTRL = 0x30, + MPU_RA_I2C_SLV4_ADDR = 0x31, + MPU_RA_I2C_SLV4_REG = 0x32, + MPU_RA_I2C_SLV4_DO = 0x33, + MPU_RA_I2C_SLV4_CTRL = 0x34, + MPU_RA_I2C_SLV4_DI = 0x35, + MPU_RA_I2C_MST_STATUS = 0x36, + MPU_RA_INT_PIN_CFG = 0x37, + MPU_RA_INT_ENABLE = 0x38, + MPU_RA_DMP_INT_STATUS = 0x39, + MPU_RA_INT_STATUS = 0x3A, + MPU_RA_ACCEL_XOUT_H = 0x3B, + MPU_RA_ACCEL_XOUT_L = 0x3C, + MPU_RA_ACCEL_YOUT_H = 0x3D, + MPU_RA_ACCEL_YOUT_L = 0x3E, + MPU_RA_ACCEL_ZOUT_H = 0x3F, + MPU_RA_ACCEL_ZOUT_L = 0x40, + MPU_RA_TEMP_OUT_H = 0x41, + MPU_RA_TEMP_OUT_L = 0x42, + MPU_RA_GYRO_XOUT_H = 0x43, + MPU_RA_GYRO_XOUT_L = 0x44, + MPU_RA_GYRO_YOUT_H = 0x45, + MPU_RA_GYRO_YOUT_L = 0x46, + MPU_RA_GYRO_ZOUT_H = 0x47, + MPU_RA_GYRO_ZOUT_L = 0x48, + MPU_RA_EXT_SENS_DATA_00 = 0x49, + MPU_RA_EXT_SENS_DATA_01 = 0x4A, + MPU_RA_EXT_SENS_DATA_02 = 0x4B, + MPU_RA_EXT_SENS_DATA_03 = 0x4C, + MPU_RA_EXT_SENS_DATA_04 = 0x4D, + MPU_RA_EXT_SENS_DATA_05 = 0x4E, + MPU_RA_EXT_SENS_DATA_06 = 0x4F, + MPU_RA_EXT_SENS_DATA_07 = 0x50, + MPU_RA_EXT_SENS_DATA_08 = 0x51, + MPU_RA_EXT_SENS_DATA_09 = 0x52, + MPU_RA_EXT_SENS_DATA_10 = 0x53, + MPU_RA_EXT_SENS_DATA_11 = 0x54, + MPU_RA_EXT_SENS_DATA_12 = 0x55, + MPU_RA_EXT_SENS_DATA_13 = 0x56, + MPU_RA_EXT_SENS_DATA_14 = 0x57, + MPU_RA_EXT_SENS_DATA_15 = 0x58, + MPU_RA_EXT_SENS_DATA_16 = 0x59, + MPU_RA_EXT_SENS_DATA_17 = 0x5A, + MPU_RA_EXT_SENS_DATA_18 = 0x5B, + MPU_RA_EXT_SENS_DATA_19 = 0x5C, + MPU_RA_EXT_SENS_DATA_20 = 0x5D, + MPU_RA_EXT_SENS_DATA_21 = 0x5E, + MPU_RA_EXT_SENS_DATA_22 = 0x5F, + MPU_RA_EXT_SENS_DATA_23 = 0x60, + MPU_RA_MOT_DETECT_STATUS = 0x61, + MPU_RA_I2C_SLV0_DO = 0x63, + MPU_RA_I2C_SLV1_DO = 0x64, + MPU_RA_I2C_SLV2_DO = 0x65, + MPU_RA_I2C_SLV3_DO = 0x66, + MPU_RA_I2C_MST_DELAY_CTRL = 0x67, + MPU_RA_SIGNAL_PATH_RESET = 0x68, + MPU_RA_MOT_DETECT_CTRL = 0x69, + MPU_RA_USER_CTRL = 0x6A, + MPU_RA_PWR_MGMT1 = 0x6B, + MPU_RA_PWR_MGMT2 = 0x6C, + MPU_RA_BANK_SEL = 0x6D, + MPU_RA_MEM_START_ADDR = 0x6E, + MPU_RA_MEM_R_W = 0x6F, + MPU_RA_DMP_CFG1 = 0x70, + MPU_RA_DMP_CFG2 = 0x71, + MPU_RA_FIFO_COUNTH = 0x72, + MPU_RA_FIFO_COUNTL = 0x73, + MPU_RA_FIFO_R_W = 0x74, + MPU_RA_WHO_AM_I = 0x75, +}; + +enum mpu_tc { + MPU_TC_PWR_MODE_BIT = 7, + MPU_TC_OFFSET_BIT = 6, + MPU_TC_OFFSET_LENGTH = 6, + MPU_TC_OTP_BNK_VLD_BIT = 0, +}; + +enum mpu_vddio { + MPU_VDDIO_LEVEL_VLOGIC = 0, + MPU_VDDIO_LEVEL_VDD = 1, +}; + +enum mpu_cfg { + MPU_CFG_EXT_SYNC_SET_BIT = 5, + MPU_CFG_EXT_SYNC_SET_LENGTH = 3, + MPU_CFG_DLPF_CFG_BIT = 2, + MPU_CFG_DLPF_CFG_LENGTH = 3, +}; + +enum mpu_ext { + MPU_EXT_SYNC_DISABLED = 0x0, + MPU_EXT_SYNC_TEMP_OUT_L = 0x1, + MPU_EXT_SYNC_GYRO_XOUT_L = 0x2, + MPU_EXT_SYNC_GYRO_YOUT_L = 0x3, + MPU_EXT_SYNC_GYRO_ZOUT_L = 0x4, + MPU_EXT_SYNC_ACCEL_XOUT_L = 0x5, + MPU_EXT_SYNC_ACCEL_YOUT_L = 0x6, + MPU_EXT_SYNC_ACCEL_ZOUT_L = 0x7, +}; + +enum mpu_dlpf { + MPU_DLPF_BW_256 = 0x00, + MPU_DLPF_BW_188 = 0x01, + MPU_DLPF_BW_98 = 0x02, + MPU_DLPF_BW_42 = 0x03, + MPU_DLPF_BW_20 = 0x04, + MPU_DLPF_BW_10 = 0x05, + MPU_DLPF_BW_5 = 0x06, +}; + +enum mpu_gyro_config { + /* self test */ + MPU_GYRO_XG_ST = 1<<7, + MPU_GYRO_YG_ST = 1<<6, + MPU_GYRO_ZG_ST = 1<<5, + /* full scale */ + MPU_GYRO_FS_SEL_Pos = 3, + MPU_GYRO_FS_SEL_Msk = 3<<3, + MPU_GYRO_FS_250 = 0<<3, + MPU_GYRO_FS_500 = 1<<3, + MPU_GYRO_FS_1000 = 2<<3, + MPU_GYRO_FS_2000 = 3<<3, + /* dlpf config, default 0b00 */ + MPU_GYRO_FCHOICE_B_Pos = 0, + MPU_GYRO_FCHOICE_B_Msk = 3<<0, +}; + +enum mpu_accel_config { + /* self test */ + MPU_ACCEL_CONFIG_XA_ST = 1<<7, + MPU_ACCEL_CONFIG_YA_ST = 1<<6, + MPU_ACCEL_CONFIG_ZA_ST = 1<<5, + /* full scale */ + MPU_ACCEL_CONFIG_FS_SEL_Pos = 3, + MPU_ACCEL_CONFIG_FS_SEL_Msk = 3<<3, + MPU_ACCEL_FS_2 = 0x00, + MPU_ACCEL_FS_4 = 0x01, + MPU_ACCEL_FS_8 = 0x02, + MPU_ACCEL_FS_16 = 0x03, +}; + +enum mpu_en { + MPU_TEMP_FIFO_EN_BIT = 7, + MPU_XG_FIFO_EN_BIT = 6, + MPU_YG_FIFO_EN_BIT = 5, + MPU_ZG_FIFO_EN_BIT = 4, + MPU_ACCEL_FIFO_EN_BIT = 3, + MPU_SLV2_FIFO_EN_BIT = 2, + MPU_SLV1_FIFO_EN_BIT = 1, + MPU_SLV0_FIFO_EN_BIT = 0, + + MPU_MULT_MST_EN_BIT = 7, + MPU_WAIT_FOR_ES_BIT = 6, + MPU_SLV_3_FIFO_EN_BIT = 5, + MPU_I2C_MST_P_NSR_BIT = 4, + MPU_I2C_MST_CLK_BIT = 3, + MPU_I2C_MST_CLK_LENGTH = 4, +}; + +enum mpu_clkdiv { + MPU_CLOCK_DIV_348 = 0x0, + MPU_CLOCK_DIV_333 = 0x1, + MPU_CLOCK_DIV_320 = 0x2, + MPU_CLOCK_DIV_308 = 0x3, + MPU_CLOCK_DIV_296 = 0x4, + MPU_CLOCK_DIV_286 = 0x5, + MPU_CLOCK_DIV_276 = 0x6, + MPU_CLOCK_DIV_267 = 0x7, + MPU_CLOCK_DIV_258 = 0x8, + MPU_CLOCK_DIV_500 = 0x9, + MPU_CLOCK_DIV_471 = 0xA, + MPU_CLOCK_DIV_444 = 0xB, + MPU_CLOCK_DIV_421 = 0xC, + MPU_CLOCK_DIV_400 = 0xD, + MPU_CLOCK_DIV_381 = 0xE, + MPU_CLOCK_DIV_364 = 0xF, +}; + +enum mpu_i2c_slv { + MPU_I2C_SLV_RW_BIT = 7, + MPU_I2C_SLV_ADDR_BIT = 6, + MPU_I2C_SLV_ADDR_LENGTH = 7, + MPU_I2C_SLV_EN_BIT = 7, + MPU_I2C_SLV_BYTE_SW_BIT = 6, + MPU_I2C_SLV_REG_DIS_BIT = 5, + MPU_I2C_SLV_GRP_BIT = 4, + MPU_I2C_SLV_LEN_BIT = 3, + MPU_I2C_SLV_LEN_LENGTH = 4, +}; + +enum mpu_i2c_slv4 { + MPU_I2C_SLV4_RW_BIT = 7, + MPU_I2C_SLV4_ADDR_BIT = 6, + MPU_I2C_SLV4_ADDR_LENGTH = 7, + MPU_I2C_SLV4_EN_BIT = 7, + MPU_I2C_SLV4_INT_EN_BIT = 6, + MPU_I2C_SLV4_REG_DIS_BIT = 5, + MPU_I2C_SLV4_MST_DLY_BIT = 4, + MPU_I2C_SLV4_MST_DLY_LENGTH = 5, +}; + +enum mpu_mst { + MPU_MST_PASS_THROUGH_BIT = 7, + MPU_MST_I2C_SLV4_DONE_BIT = 6, + MPU_MST_I2C_LOST_ARB_BIT = 5, + MPU_MST_I2C_SLV4_NACK_BIT = 4, + MPU_MST_I2C_SLV3_NACK_BIT = 3, + MPU_MST_I2C_SLV2_NACK_BIT = 2, + MPU_MST_I2C_SLV1_NACK_BIT = 1, + MPU_MST_I2C_SLV0_NACK_BIT = 0, +}; + +enum mpu_intcfg { + MPU_INT_PIN_CFG_INT_LEVEL = 1<<7, + MPU_INTMODE_ACTIVEHIGH = 0, + MPU_INTMODE_ACTIVELOW = 1<<7, + + MPU_INT_PIN_CFG_INT_OPEN = 1<<6, + MPU_INTDRV_PUSHPULL = 0, + MPU_INTDRV_OPENDRAIN = 1<<6, + + MPU_INT_PIN_CFG_LATCH_INT_EN = 1<<5, + MPU_INTLATCH_50USPULSE = 0, + MPU_INTLATCH_WAITCLEAR = 1<<5, + + MPU_INT_PIN_CFG_RD_CLEAR = 1<<4, + MPU_INTCLEAR_STATUSREAD = 0, + MPU_INTCLEAR_ANYREAD = 1<<4, + + MPU_INT_PIN_CFG_FSYNC_INT_LEVEL = 1<<3, + MPU_INT_PIN_CFG_FSYNC_INT_EN = 1<<2, + MPU_INT_PIN_CFG_I2C_BYPASS_EN = 1<<1, + MPU_INT_PIN_CFG_CLKOUT_EN = 1<<0, +}; + +enum mpu_int_enable { + MPU_INT_ENABLE_FIFO_OFLOW = 1<<4, + MPU_INT_ENABLE_I2C_MST_INT = 1<<3, + MPU_INT_ENABLE_DATA_RDY = 1<<0, +}; + +// TODO: Need to work on DMP related things +enum mpu_dmpint { + MPU_DMPINT_5_BIT = 5, + MPU_DMPINT_4_BIT = 4, + MPU_DMPINT_3_BIT = 3, + MPU_DMPINT_2_BIT = 2, + MPU_DMPINT_1_BIT = 1, + MPU_DMPINT_0_BIT = 0, +}; + +enum mpu_motion { + MPU_MOTION_MOT_XNEG_BIT = 7, + MPU_MOTION_MOT_XPOS_BIT = 6, + MPU_MOTION_MOT_YNEG_BIT = 5, + MPU_MOTION_MOT_YPOS_BIT = 4, + MPU_MOTION_MOT_ZNEG_BIT = 3, + MPU_MOTION_MOT_ZPOS_BIT = 2, + MPU_MOTION_MOT_ZRMOT_BIT = 0, +}; + +enum mpu_delayctrl { + MPU_DELAYCTRL_DELAY_ES_SHADOW_BIT = 7, + MPU_DELAYCTRL_I2C_SLV4_DLY_EN_BIT = 4, + MPU_DELAYCTRL_I2C_SLV3_DLY_EN_BIT = 3, + MPU_DELAYCTRL_I2C_SLV2_DLY_EN_BIT = 2, + MPU_DELAYCTRL_I2C_SLV1_DLY_EN_BIT = 1, + MPU_DELAYCTRL_I2C_SLV0_DLY_EN_BIT = 0, +}; + +enum mpu_pathreset { + MPU_PATHRESET_GYRO_RESET_BIT = 2, + MPU_PATHRESET_ACCEL_RESET_BIT = 1, + MPU_PATHRESET_TEMP_RESET_BIT = 0, +}; + +enum mpu_detect { + MPU_DETECT_ACCEL_ON_DELAY_BIT = 5, + MPU_DETECT_ACCEL_ON_DELAY_LENGTH = 2, + MPU_DETECT_FF_COUNT_BIT = 3, + MPU_DETECT_FF_COUNT_LENGTH = 2, + MPU_DETECT_MOT_COUNT_BIT = 1, + MPU_DETECT_MOT_COUNT_LENGTH = 2, + + MPU_DETECT_DECREMENT_RESET = 0x0, + MPU_DETECT_DECREMENT_1 = 0x1, + MPU_DETECT_DECREMENT_2 = 0x2, + MPU_DETECT_DECREMENT_4 = 0x3, +}; + +enum mpu_userctrl { + MPU_USERCTRL_DMP_EN_BIT = 7, + MPU_USERCTRL_FIFO_EN_BIT = 6, + MPU_USERCTRL_I2C_MST_EN_BIT = 5, + MPU_USERCTRL_I2C_IF_DIS_BIT = 4, + MPU_USERCTRL_DMP_RESET_BIT = 3, + MPU_USERCTRL_FIFO_RESET_BIT = 2, + MPU_USERCTRL_I2C_MST_RESET_BIT = 1, + MPU_USERCTRL_SIG_COND_RESET_BIT = 0, +}; + +enum mpu_pwr_mgmt1 { + MPU_PWR_MGMT1_DEVICE_RESET = 1<<7, + /* Low power sleep mode */ + MPU_PWR_MGMT1_SLEEP = 1<<6, + /* Low power auto wakeup/cycle mode */ + MPU_PWR_MGMT1_CYCLE = 1<<5, + /* Temperature sensor disable */ + MPU_PWR_MGMT1_TEMP_DIS = 1<<3, + /* Clock source selection */ + MPU_PWR_MGMT1_CLKSEL_Msk = 0x7, + MPU_PWR_MGMT1_CLKSEL_Pos = 0, + MPU_CLOCK_INTERNAL = 0, + MPU_CLOCK_PLL_XGYRO = 1, + MPU_CLOCK_PLL_YGYRO = 2, + MPU_CLOCK_PLL_ZGYRO = 3, + MPU_CLOCK_PLL_EXT32K = 4, + MPU_CLOCK_PLL_EXT19M = 5, + MPU_CLOCK_KEEP_RESET = 7, +}; + +enum mpu_pwr_mgmt2 { + /* low-power cyclic auto wakeup */ + MPU_PWR_MGMT2_LP_WAKE_CTRL_Pos = 6, + MPU_PWR_MGMT2_LP_WAKE_CTRL_Msk = 3<<6, + MPU_WAKE_FREQ_1P25 = 0<<6, + MPU_WAKE_FREQ_5 = 1<<6, + MPU_WAKE_FREQ_20 = 2<<6, + MPU_WAKE_FREQ_40 = 3<<6, + /* axis standby bits */ + MPU_PWR_MGMT2_STBY_XA = 1<<5, + MPU_PWR_MGMT2_STBY_YA = 1<<4, + MPU_PWR_MGMT2_STBY_ZA = 1<<3, + MPU_PWR_MGMT2_STBY_XG = 1<<2, + MPU_PWR_MGMT2_STBY_YG = 1<<1, + MPU_PWR_MGMT2_STBY_ZG = 1<<0, +}; + +enum mpu_banksel { + MPU_BANKSEL_PRFTCH_EN_BIT = 6, + MPU_BANKSEL_CFG_USER_BANK_BIT = 5, + MPU_BANKSEL_MEM_SEL_BIT = 4, + MPU_BANKSEL_MEM_SEL_LENGTH = 5, +}; + +enum mpu_whoami { + MPU_WHO_AM_I_Pos = 1, + MPU_WHO_AM_I_Msk = 0x3f<<1, + MPU_DEVICE_ID = 0x34, +}; + +enum mpu_dmp_mem { + MPU_DMP_MEMORY_BANKS = 8, + MPU_DMP_MEMORY_BANK_SIZE = 256, + MPU_DMP_MEMORY_CHUNK_SIZE = 16, +}; + + +struct __attribute__((packed)) mpu_accel_data { + union { + struct { int16_t x, y, z; }; + int16_t channels[3]; + }; +}; + +struct __attribute__((packed)) mpu_gyro_data { + union { + struct { int16_t x, y, z; }; + int16_t channels[3]; + }; +}; + +struct __attribute__((packed)) mpu_raw_data { + union { + struct { + struct mpu_accel_data accel; + int16_t temp; + struct mpu_gyro_data gyro; + }; + int16_t channels[7]; + }; +}; + + +void mpu_init(); +void mpu_init_low_power(uint8_t wake_freq, bool enable_interrupt); +bool mpu_test_connection(); + +uint8_t mpu_get_gyro_fs(); +void mpu_set_gyro_fs(uint8_t range); + +uint8_t mpu_get_accel_fs(); +void mpu_set_accel_fs(uint8_t range); + +// PWR_MGMT_1 register +bool mpu_get_sleep_mode(); +void mpu_set_sleep_mode(bool val); +// WHO_AM_I register +uint8_t mpu_device_id(); + +void mpu_read_accel_gyro(struct mpu_raw_data *out); +int16_t mpu_read_temp(); +void mpu_read_accel(struct mpu_accel_data *out); +void mpu_read_gyro(struct mpu_gyro_data *out); + +void mpu_reg_write(uint8_t reg, uint8_t val); +void mpu_reg_read_multiple(uint8_t addr, uint8_t* buf, size_t len); +uint8_t mpu_reg_read(uint8_t addr); + +#endif /* __MPU6050_H */ diff --git a/fw/openocd.cfg b/fw/openocd.cfg new file mode 100644 index 0000000..065bf31 --- /dev/null +++ b/fw/openocd.cfg @@ -0,0 +1,15 @@ +telnet_port 4444 +gdb_port 3333 + +source [find interface/stlink-v2.cfg] +#hla_serial "000000000001" +transport select hla_swd + +source [find target/stm32f0x.cfg] +#adapter_khz 10000 + +init +arm semihosting enable + +#flash bank sysflash.alias stm32f0x 0x00000000 0 0 0 $_TARGETNAME +#program main.elf diff --git a/fw/scope.gdb b/fw/scope.gdb new file mode 100644 index 0000000..da325bd --- /dev/null +++ b/fw/scope.gdb @@ -0,0 +1,12 @@ +target remote localhost:3333 +set pagination off +file main.elf +load + +break gdb_dump +command 1 + dump binary value /tmp/scope_dump.bin debug_buf + continue +end + +continue diff --git a/fw/startup_stm32f030x6.s b/fw/startup_stm32f030x6.s new file mode 100644 index 0000000..2f0eb42 --- /dev/null +++ b/fw/startup_stm32f030x6.s @@ -0,0 +1,273 @@ +/**
+ ******************************************************************************
+ * @file startup_stm32f030x6.s
+ * copied from: STM32Cube/Drivers/CMSIS/Device/ST/STM32F0xx/Source/Templates/gcc
+ * @author MCD Application Team
+ * @version V2.3.1
+ * @date 04-November-2016
+ * @brief STM32F030x4/STM32F030x6 devices vector table for Atollic TrueSTUDIO toolchain.
+ * This module performs:
+ * - Set the initial SP
+ * - Set the initial PC == Reset_Handler,
+ * - Set the vector table entries with the exceptions ISR address
+ * - Branches to main in the C library (which eventually
+ * calls main()).
+ * After Reset the Cortex-M0 processor is in Thread mode,
+ * priority is Privileged, and the Stack is set to Main.
+ ******************************************************************************
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+ .syntax unified
+ .cpu cortex-m0
+ .fpu softvfp
+ .thumb
+
+.global g_pfnVectors
+.global Default_Handler
+
+/* start address for the initialization values of the .data section.
+defined in linker script */
+.word _sidata
+/* start address for the .data section. defined in linker script */
+.word _sdata
+/* end address for the .data section. defined in linker script */
+.word _edata
+/* start address for the .bss section. defined in linker script */
+.word _sbss
+/* end address for the .bss section. defined in linker script */
+.word _ebss
+
+ .section .text.Reset_Handler
+ .weak Reset_Handler
+ .type Reset_Handler, %function
+Reset_Handler:
+ ldr r0, =_estack
+ mov sp, r0 /* set stack pointer */
+
+/* Copy the data segment initializers from flash to SRAM */
+ movs r1, #0
+ b LoopCopyDataInit
+
+CopyDataInit:
+ ldr r3, =_sidata
+ ldr r3, [r3, r1]
+ str r3, [r0, r1]
+ adds r1, r1, #4
+
+LoopCopyDataInit:
+ ldr r0, =_sdata
+ ldr r3, =_edata
+ adds r2, r0, r1
+ cmp r2, r3
+ bcc CopyDataInit
+ ldr r2, =_sbss
+ b LoopFillZerobss
+/* Zero fill the bss segment. */
+FillZerobss:
+ movs r3, #0
+ str r3, [r2]
+ adds r2, r2, #4
+
+
+LoopFillZerobss:
+ ldr r3, = _ebss
+ cmp r2, r3
+ bcc FillZerobss
+
+/* Call the clock system intitialization function.*/
+ bl SystemInit
+/* Call static constructors */
+// bl __libc_init_array
+/* Call the application's entry point.*/
+ bl main
+
+LoopForever:
+ b LoopForever
+
+
+.size Reset_Handler, .-Reset_Handler
+
+/**
+ * @brief This is the code that gets called when the processor receives an
+ * unexpected interrupt. This simply enters an infinite loop, preserving
+ * the system state for examination by a debugger.
+ *
+ * @param None
+ * @retval : None
+*/
+ .section .text.Default_Handler,"ax",%progbits
+Default_Handler:
+Infinite_Loop:
+ b Infinite_Loop
+ .size Default_Handler, .-Default_Handler
+/******************************************************************************
+*
+* The minimal vector table for a Cortex M0. Note that the proper constructs
+* must be placed on this to ensure that it ends up at physical address
+* 0x0000.0000.
+*
+******************************************************************************/
+ .section .isr_vector,"a",%progbits
+ .type g_pfnVectors, %object
+ .size g_pfnVectors, .-g_pfnVectors
+
+
+g_pfnVectors:
+ .word _estack
+ .word Reset_Handler
+ .word NMI_Handler
+ .word HardFault_Handler
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word SVC_Handler
+ .word 0
+ .word 0
+ .word PendSV_Handler
+ .word SysTick_Handler
+ .word WWDG_IRQHandler /* Window WatchDog */
+ .word 0 /* Reserved */
+ .word RTC_IRQHandler /* RTC through the EXTI line */
+ .word FLASH_IRQHandler /* FLASH */
+ .word RCC_IRQHandler /* RCC */
+ .word EXTI0_1_IRQHandler /* EXTI Line 0 and 1 */
+ .word EXTI2_3_IRQHandler /* EXTI Line 2 and 3 */
+ .word EXTI4_15_IRQHandler /* EXTI Line 4 to 15 */
+ .word 0 /* Reserved */
+ .word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */
+ .word DMA1_Channel2_3_IRQHandler /* DMA1 Channel 2 and Channel 3 */
+ .word DMA1_Channel4_5_IRQHandler /* DMA1 Channel 4 and Channel 5 */
+ .word ADC1_IRQHandler /* ADC1 */
+ .word TIM1_BRK_UP_TRG_COM_IRQHandler /* TIM1 Break, Update, Trigger and Commutation */
+ .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
+ .word 0 /* Reserved */
+ .word TIM3_IRQHandler /* TIM3 */
+ .word 0 /* Reserved */
+ .word 0 /* Reserved */
+ .word TIM14_IRQHandler /* TIM14 */
+ .word 0 /* Reserved */
+ .word TIM16_IRQHandler /* TIM16 */
+ .word TIM17_IRQHandler /* TIM17 */
+ .word I2C1_IRQHandler /* I2C1 */
+ .word 0 /* Reserved */
+ .word SPI1_IRQHandler /* SPI1 */
+ .word 0 /* Reserved */
+ .word USART1_IRQHandler /* USART1 */
+ .word 0 /* Reserved */
+ .word 0 /* Reserved */
+ .word 0 /* Reserved */
+ .word 0 /* Reserved */
+
+/*******************************************************************************
+*
+* Provide weak aliases for each Exception handler to the Default_Handler.
+* As they are weak aliases, any function with the same name will override
+* this definition.
+*
+*******************************************************************************/
+
+ .weak NMI_Handler
+ .thumb_set NMI_Handler,Default_Handler
+
+ .weak HardFault_Handler
+ .thumb_set HardFault_Handler,Default_Handler
+
+ .weak SVC_Handler
+ .thumb_set SVC_Handler,Default_Handler
+
+ .weak PendSV_Handler
+ .thumb_set PendSV_Handler,Default_Handler
+
+ .weak SysTick_Handler
+ .thumb_set SysTick_Handler,Default_Handler
+
+ .weak WWDG_IRQHandler
+ .thumb_set WWDG_IRQHandler,Default_Handler
+
+ .weak RTC_IRQHandler
+ .thumb_set RTC_IRQHandler,Default_Handler
+
+ .weak FLASH_IRQHandler
+ .thumb_set FLASH_IRQHandler,Default_Handler
+
+ .weak RCC_IRQHandler
+ .thumb_set RCC_IRQHandler,Default_Handler
+
+ .weak EXTI0_1_IRQHandler
+ .thumb_set EXTI0_1_IRQHandler,Default_Handler
+
+ .weak EXTI2_3_IRQHandler
+ .thumb_set EXTI2_3_IRQHandler,Default_Handler
+
+ .weak EXTI4_15_IRQHandler
+ .thumb_set EXTI4_15_IRQHandler,Default_Handler
+
+ .weak DMA1_Channel1_IRQHandler
+ .thumb_set DMA1_Channel1_IRQHandler,Default_Handler
+
+ .weak DMA1_Channel2_3_IRQHandler
+ .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler
+
+ .weak DMA1_Channel4_5_IRQHandler
+ .thumb_set DMA1_Channel4_5_IRQHandler,Default_Handler
+
+ .weak ADC1_IRQHandler
+ .thumb_set ADC1_IRQHandler,Default_Handler
+
+ .weak TIM1_BRK_UP_TRG_COM_IRQHandler
+ .thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler
+
+ .weak TIM1_CC_IRQHandler
+ .thumb_set TIM1_CC_IRQHandler,Default_Handler
+
+ .weak TIM3_IRQHandler
+ .thumb_set TIM3_IRQHandler,Default_Handler
+
+ .weak TIM14_IRQHandler
+ .thumb_set TIM14_IRQHandler,Default_Handler
+
+ .weak TIM16_IRQHandler
+ .thumb_set TIM16_IRQHandler,Default_Handler
+
+ .weak TIM17_IRQHandler
+ .thumb_set TIM17_IRQHandler,Default_Handler
+
+ .weak I2C1_IRQHandler
+ .thumb_set I2C1_IRQHandler,Default_Handler
+
+ .weak SPI1_IRQHandler
+ .thumb_set SPI1_IRQHandler,Default_Handler
+
+ .weak USART1_IRQHandler
+ .thumb_set USART1_IRQHandler,Default_Handler
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/fw/stm32_flash.ld b/fw/stm32_flash.ld new file mode 100644 index 0000000..cba7577 --- /dev/null +++ b/fw/stm32_flash.ld @@ -0,0 +1,136 @@ +
+ENTRY(Reset_Handler)
+
+MEMORY {
+ FLASH (rx): ORIGIN = 0x08000000, LENGTH = 0x3C00
+ CONFIGFLASH (rw): ORIGIN = 0x08003C00, LENGTH = 0x400
+ RAM (xrw): ORIGIN = 0x20000000, LENGTH = 4K
+}
+
+/* highest address of the user mode stack */
+_estack = 0x20001000;
+
+SECTIONS {
+ /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */
+ .isr_vector : {
+ . = ALIGN(4);
+ KEEP(*(.isr_vector)) /* Startup code */
+ . = ALIGN(4);
+ } >FLASH
+
+ /* the program code is stored in the .text section, which goes to Flash */
+ .text : {
+ . = ALIGN(4);
+
+ *(.text) /* normal code */
+ *(.text.*) /* -ffunction-sections code */
+ *(.rodata) /* read-only data (constants) */
+ *(.rodata*) /* -fdata-sections read only data */
+ *(.glue_7) /* TBD - needed ? */
+ *(.glue_7t) /* TBD - needed ? */
+
+ *(.source_tarball)
+
+ /* Necessary KEEP sections (see http://sourceware.org/ml/newlib/2005/msg00255.html) */
+ KEEP (*(.init))
+ KEEP (*(.fini))
+ KEEP (*(.source_tarball))
+
+ . = ALIGN(4);
+ _etext = .;
+ /* This is used by the startup in order to initialize the .data section */
+ _sidata = _etext;
+ } >FLASH
+
+ /*
+ .configflash : {
+ . = ALIGN(0x400);
+ *(.configdata)
+ _econfig = .;
+ } >FLASH
+ */
+
+ /* This is the initialized data section
+ The program executes knowing that the data is in the RAM
+ but the loader puts the initial values in the FLASH (inidata).
+ It is one task of the startup to copy the initial values from FLASH to RAM. */
+ .data : AT ( _sidata ) {
+ . = ALIGN(4);
+ /* This is used by the startup in order to initialize the .data secion */
+ _sdata = . ;
+ _data = . ;
+
+ *(.data)
+ *(.data.*)
+ *(.RAMtext)
+
+ . = ALIGN(4);
+ /* This is used by the startup in order to initialize the .data secion */
+ _edata = . ;
+ } >RAM
+
+ /* This is the uninitialized data section */
+ .bss : {
+ . = ALIGN(4);
+ /* This is used by the startup in order to initialize the .bss secion */
+ _sbss = .;
+ _bss = .;
+
+ *(.bss)
+ *(.bss.*) /* patched by elias - allows the use of -fdata-sections */
+ *(COMMON)
+
+ . = ALIGN(4);
+ /* This is used by the startup in order to initialize the .bss secion */
+ _ebss = . ;
+ } >RAM
+
+ PROVIDE ( end = _ebss);
+ PROVIDE (_end = _ebss);
+
+ __exidx_start = .;
+ __exidx_end = .;
+
+ /* after that it's only debugging information. */
+
+ /* remove the debugging information from the standard libraries */
+/* /DISCARD/ : {
+ libc.a ( * )
+ libm.a ( * )
+ libgcc.a ( * )
+ }*/
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}
diff --git a/fw/system_stm32f0xx.c b/fw/system_stm32f0xx.c new file mode 100644 index 0000000..a43c3d6 --- /dev/null +++ b/fw/system_stm32f0xx.c @@ -0,0 +1,336 @@ +/**
+ ******************************************************************************
+ * @file system_stm32f0xx.c
+ * copied from: STM32Cube/Drivers/CMSIS/Device/ST/STM32F0xx/Source/Templates
+ * @author MCD Application Team
+ * @version V2.3.1
+ * @date 04-November-2016
+ * @brief CMSIS Cortex-M0 Device Peripheral Access Layer System Source File.
+ *
+ * 1. This file provides two functions and one global variable to be called from
+ * user application:
+ * - SystemInit(): This function is called at startup just after reset and
+ * before branch to main program. This call is made inside
+ * the "startup_stm32f0xx.s" file.
+ *
+ * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+ * by the user application to setup the SysTick
+ * timer or configure other parameters.
+ *
+ * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+ * be called whenever the core clock is changed
+ * during program execution.
+ *
+ * 2. After each device reset the HSI (8 MHz) is used as system clock source.
+ * Then SystemInit() function is called, in "startup_stm32f0xx.s" file, to
+ * configure the system clock before to branch to main program.
+ *
+ * 3. This file configures the system clock as follows:
+ *=============================================================================
+ * Supported STM32F0xx device
+ *-----------------------------------------------------------------------------
+ * System Clock source | HSI
+ *-----------------------------------------------------------------------------
+ * SYSCLK(Hz) | 8000000
+ *-----------------------------------------------------------------------------
+ * HCLK(Hz) | 8000000
+ *-----------------------------------------------------------------------------
+ * AHB Prescaler | 1
+ *-----------------------------------------------------------------------------
+ * APB1 Prescaler | 1
+ *-----------------------------------------------------------------------------
+ *=============================================================================
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>© COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+/** @addtogroup CMSIS
+ * @{
+ */
+
+/** @addtogroup stm32f0xx_system
+ * @{
+ */
+
+/** @addtogroup STM32F0xx_System_Private_Includes
+ * @{
+ */
+
+#include "stm32f0xx.h"
+
+/**
+ * @}
+ */
+
+/** @addtogroup STM32F0xx_System_Private_TypesDefinitions
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+/** @addtogroup STM32F0xx_System_Private_Defines
+ * @{
+ */
+#if !defined (HSE_VALUE)
+ #define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz.
+ This value can be provided and adapted by the user application. */
+#endif /* HSE_VALUE */
+
+#if !defined (HSI_VALUE)
+ #define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz.
+ This value can be provided and adapted by the user application. */
+#endif /* HSI_VALUE */
+
+#if !defined (HSI48_VALUE)
+#define HSI48_VALUE ((uint32_t)48000000) /*!< Default value of the HSI48 Internal oscillator in Hz.
+ This value can be provided and adapted by the user application. */
+#endif /* HSI48_VALUE */
+/**
+ * @}
+ */
+
+/** @addtogroup STM32F0xx_System_Private_Macros
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+/** @addtogroup STM32F0xx_System_Private_Variables
+ * @{
+ */
+ /* This variable is updated in three ways:
+ 1) by calling CMSIS function SystemCoreClockUpdate()
+ 2) by calling HAL API function HAL_RCC_GetHCLKFreq()
+ 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
+ Note: If you use this function to configure the system clock there is no need to
+ call the 2 first functions listed above, since SystemCoreClock variable is
+ updated automatically.
+ */
+uint32_t SystemCoreClock = 8000000;
+
+const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
+
+/**
+ * @}
+ */
+
+/** @addtogroup STM32F0xx_System_Private_FunctionPrototypes
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+/** @addtogroup STM32F0xx_System_Private_Functions
+ * @{
+ */
+
+/**
+ * @brief Setup the microcontroller system.
+ * Initialize the default HSI clock source, vector table location and the PLL configuration is reset.
+ * @param None
+ * @retval None
+ */
+void SystemInit(void)
+{
+ /* Reset the RCC clock configuration to the default reset state ------------*/
+ /* Set HSION bit */
+ RCC->CR |= (uint32_t)0x00000001U;
+
+#if defined (STM32F051x8) || defined (STM32F058x8)
+ /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */
+ RCC->CFGR &= (uint32_t)0xF8FFB80CU;
+#else
+ /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */
+ RCC->CFGR &= (uint32_t)0x08FFB80CU;
+#endif /* STM32F051x8 or STM32F058x8 */
+
+ /* Reset HSEON, CSSON and PLLON bits */
+ RCC->CR &= (uint32_t)0xFEF6FFFFU;
+
+ /* Reset HSEBYP bit */
+ RCC->CR &= (uint32_t)0xFFFBFFFFU;
+
+ /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
+ RCC->CFGR &= (uint32_t)0xFFC0FFFFU;
+
+ /* Reset PREDIV[3:0] bits */
+ RCC->CFGR2 &= (uint32_t)0xFFFFFFF0U;
+
+#if defined (STM32F072xB) || defined (STM32F078xx)
+ /* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFFCFE2CU;
+#elif defined (STM32F071xB)
+ /* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFFFCEACU;
+#elif defined (STM32F091xC) || defined (STM32F098xx)
+ /* Reset USART3SW[1:0], USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFF0FEACU;
+#elif defined (STM32F030x6) || defined (STM32F030x8) || defined (STM32F031x6) || defined (STM32F038xx) || defined (STM32F030xC)
+ /* Reset USART1SW[1:0], I2C1SW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFFFFEECU;
+#elif defined (STM32F051x8) || defined (STM32F058xx)
+ /* Reset USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFFFFEACU;
+#elif defined (STM32F042x6) || defined (STM32F048xx)
+ /* Reset USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFFFFE2CU;
+#elif defined (STM32F070x6) || defined (STM32F070xB)
+ /* Reset USART1SW[1:0], I2C1SW, USBSW and ADCSW bits */
+ RCC->CFGR3 &= (uint32_t)0xFFFFFE6CU;
+ /* Set default USB clock to PLLCLK, since there is no HSI48 */
+ RCC->CFGR3 |= (uint32_t)0x00000080U;
+#else
+ #warning "No target selected"
+#endif
+
+ /* Reset HSI14 bit */
+ RCC->CR2 &= (uint32_t)0xFFFFFFFEU;
+
+ /* Disable all interrupts */
+ RCC->CIR = 0x00000000U;
+
+}
+
+/**
+ * @brief Update SystemCoreClock variable according to Clock Register Values.
+ * The SystemCoreClock variable contains the core clock (HCLK), it can
+ * be used by the user application to setup the SysTick timer or configure
+ * other parameters.
+ *
+ * @note Each time the core clock (HCLK) changes, this function must be called
+ * to update SystemCoreClock variable value. Otherwise, any configuration
+ * based on this variable will be incorrect.
+ *
+ * @note - The system frequency computed by this function is not the real
+ * frequency in the chip. It is calculated based on the predefined
+ * constant and the selected clock source:
+ *
+ * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+ *
+ * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+ *
+ * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
+ * or HSI_VALUE(*) multiplied/divided by the PLL factors.
+ *
+ * (*) HSI_VALUE is a constant defined in stm32f0xx_hal.h file (default value
+ * 8 MHz) but the real value may vary depending on the variations
+ * in voltage and temperature.
+ *
+ * (**) HSE_VALUE is a constant defined in stm32f0xx_hal.h file (default value
+ * 8 MHz), user has to ensure that HSE_VALUE is same as the real
+ * frequency of the crystal used. Otherwise, this function may
+ * have wrong result.
+ *
+ * - The result of this function could be not correct when using fractional
+ * value for HSE crystal.
+ *
+ * @param None
+ * @retval None
+ */
+void SystemCoreClockUpdate (void)
+{
+ uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0;
+
+ /* Get SYSCLK source -------------------------------------------------------*/
+ tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+ switch (tmp)
+ {
+ case RCC_CFGR_SWS_HSI: /* HSI used as system clock */
+ SystemCoreClock = HSI_VALUE;
+ break;
+ case RCC_CFGR_SWS_HSE: /* HSE used as system clock */
+ SystemCoreClock = HSE_VALUE;
+ break;
+ case RCC_CFGR_SWS_PLL: /* PLL used as system clock */
+ /* Get PLL clock source and multiplication factor ----------------------*/
+ pllmull = RCC->CFGR & RCC_CFGR_PLLMUL;
+ pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
+ pllmull = ( pllmull >> 18) + 2;
+ predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1;
+
+ if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV)
+ {
+ /* HSE used as PLL clock source : SystemCoreClock = HSE/PREDIV * PLLMUL */
+ SystemCoreClock = (HSE_VALUE/predivfactor) * pllmull;
+ }
+#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F072xB) || defined(STM32F078xx) || defined(STM32F091xC) || defined(STM32F098xx)
+ else if (pllsource == RCC_CFGR_PLLSRC_HSI48_PREDIV)
+ {
+ /* HSI48 used as PLL clock source : SystemCoreClock = HSI48/PREDIV * PLLMUL */
+ SystemCoreClock = (HSI48_VALUE/predivfactor) * pllmull;
+ }
+#endif /* STM32F042x6 || STM32F048xx || STM32F072xB || STM32F078xx || STM32F091xC || STM32F098xx */
+ else
+ {
+#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F070x6) \
+ || defined(STM32F078xx) || defined(STM32F071xB) || defined(STM32F072xB) \
+ || defined(STM32F070xB) || defined(STM32F091xC) || defined(STM32F098xx) || defined(STM32F030xC)
+ /* HSI used as PLL clock source : SystemCoreClock = HSI/PREDIV * PLLMUL */
+ SystemCoreClock = (HSI_VALUE/predivfactor) * pllmull;
+#else
+ /* HSI used as PLL clock source : SystemCoreClock = HSI/2 * PLLMUL */
+ SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
+#endif /* STM32F042x6 || STM32F048xx || STM32F070x6 ||
+ STM32F071xB || STM32F072xB || STM32F078xx || STM32F070xB ||
+ STM32F091xC || STM32F098xx || STM32F030xC */
+ }
+ break;
+ default: /* HSI used as system clock */
+ SystemCoreClock = HSI_VALUE;
+ break;
+ }
+ /* Compute HCLK clock frequency ----------------*/
+ /* Get HCLK prescaler */
+ tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+ /* HCLK clock frequency */
+ SystemCoreClock >>= tmp;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
diff --git a/fw/tools/gen_cmsis_exports.py b/fw/tools/gen_cmsis_exports.py new file mode 100644 index 0000000..ba3422b --- /dev/null +++ b/fw/tools/gen_cmsis_exports.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import re +import os + +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('cmsis_device_header', nargs='+', type=argparse.FileType('rb')) + args = parser.parse_args() + + print('#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__') + print('#define __GENERATED_CMSIS_HEADER_EXPORTS__') + print() + for header in args.cmsis_device_header: + lines = header.readlines() + name = os.path.basename(header.name) + print('#include <{}>'.format(name)) + print() + + print('/* {} */'.format(name)) + for l in lines: + match = re.match(b'^#define (\w+)\s+\W*(\w+_TypeDef|\w+_Type).*$', l) + if match: + inst, typedef = match.groups() + inst, typedef = inst.decode(), typedef.decode() + print('{} *{} = {};'.format(typedef, inst.lower(), inst)) + print() + print('#endif//__GENERATED_CMSIS_HEADER_EXPORTS__') + |