Skip to content

Instantly share code, notes, and snippets.

@moorepants
Created February 14, 2018 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moorepants/66a411cbf6dbf4074baa86953a60b993 to your computer and use it in GitHub Desktop.
Save moorepants/66a411cbf6dbf4074baa86953a60b993 to your computer and use it in GitHub Desktop.
Example simulation of a simple boat with a CVT drive.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.integrate import solve_ivp\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here are all of the various constants for the boat system. You'll need to find good estimates for these for your system."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"boat_mass = 150 # kg\n",
"rotor_viscous_friction = 0.01 # kg/s\n",
"prop_viscous_friction = 0.01 # kg/s\n",
"rotor_inertia = 5.0 * 0.05**2 # kg m**2\n",
"prop_inertia = 2.0 * 0.06**2\n",
"motor_torque_constant = 0.1\n",
"motor_emf_constant = 0.1\n",
"winding_resistance = 2.0\n",
"motor_inductance = 0.5\n",
"\n",
"# these three numbers were taken from last year's report\n",
"hull_drag_coeff = 0.2\n",
"water_density = 998 # kg/m**3\n",
"hull_area = 0.045 # m**2\n",
"\n",
"thrust_constant = 40.0 / 100 # Ns/rad # pick it to geenrate 40 N at 1000 rpm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following equation calculates the prop's thrust given the prop rotation rate. You'll need to either come up with an equation for this or create a lookup table. The student's last year found these relationships."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def thrust(angular_rate):\n",
" \"\"\"Returns the thrust generated by the prop given the angular rate of the prop in radians per second .\"\"\"\n",
" return thrust_constant * angular_rate"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This function computes the hull drag from the water, which is dominant. You can make this more detailed if you'd like to include drag due to the other features of the boat in the water and/or air, etc."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def drag(speed):\n",
" \"\"\"Returns the drag on the boat given the boat's longitudinal speed.\"\"\"\n",
" return hull_drag_coeff * water_density * hull_area * speed**2 / 2.0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here you will need to define some function that caculates the cvt gear ratio. I'm not sure what relationship you'll want to use here. Something that tries to ensure power is constant, I suppose."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def cvt_gear_ratio(angular_rate):\n",
" \"\"\"Returns the CVT's gear ratio given the angular rate of the motor's rotor.\"\"\"\n",
" return 0.25"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here are the basic state equations of a boat modeled as a moving particle. It includes simple DC motor dynamics. Double check these."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def calc_deriv(time, state, voltage):\n",
" \"\"\"Returns the derivatives of the states.\"\"\"\n",
" \n",
" # x : boat longitudial position\n",
" # thetar : motor rotor angle\n",
" # thetap : prop angle\n",
" # v : boat longitudinal speed\n",
" # omegar : motor rotor angular rate\n",
" # omegap : prop angular rate\n",
" # i : motor current\n",
" x, thetar, thetap, v, omegar, omegap, i = state\n",
" \n",
" xdot = v\n",
" thetapdot = omegap\n",
" thetardot = omegar\n",
" \n",
" vdot = (thrust(omegap) - drag(v)) / boat_mass\n",
" \n",
" motor_torque = motor_torque_constant * i\n",
" \n",
" omegardot = (motor_torque - rotor_viscous_friction * omegar) / rotor_inertia\n",
" \n",
" omegapdot = (motor_torque / cvt_gear_ratio(omegar) - prop_viscous_friction * omegap) / prop_inertia\n",
" \n",
" idot = (-winding_resistance * i + voltage - motor_emf_constant * omegar) / motor_inductance\n",
" \n",
" return xdot, thetapdot, thetardot, vdot, omegardot, omegapdot, idot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This integrates the above equations with a constant voltage applied to the motor of 20 volts for 30 seconds starting from rest."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"sol = solve_ivp(lambda t, s: calc_deriv(t, s, 20.0), (0.0, 30.0), np.zeros(7))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plot the simulation solution."
]
},
{
"cell_type": "code",
"execution_count": 9,
"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",
" this.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 overriden (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",
" // select the cell after this one\n",
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
" IPython.notebook.select(index + 1);\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,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YQAfAvgTzMNxBIAqbf7NUM8vzYb647aacABBAG4AcAuAUKjBfb+jW2YegDwA9wMYDBUKe3u1lb7jMQCPALhamyYBaILafgC3lTcx35xjxrmOGec65psfiQcwW/f6HACFcP2Zw1ZgDMcBUEfF7+jmfQ/qDMOzvdguX/VDqG12r/b6e1AB8GfdMtdqy9zZu03zWacAvARuK29jvrmGGeceZpx7mG8+6HwALeh45LcU6miPFGM4XqHNu9Ww3G4An/dWo3zYVVDb50bt9f3a64sNy+UCGN6L7fJF50L9h9oIdYaK28p7mG+uY8a5hxnnGuabD/sp1C/jLsP8qVBHzqQYw/Fubd5PDMutBrCqtxrlo84BEAIgRjfvOagAMNoPYEpvNMoH3QTV/6UF6jLbI9p8bivvYb65jhnnOmZc15hvfsBZQH4CIK73m+OzXA3HNQBW9lajfNQ8qA73P9PNc7bTJwD4uBfa5IvOhzqL8CsAHwEohzpC5rbyHuab65hxrmPGdY355gd4icQ1vDzimtkA8gFcbpjP0/5diwSwANxW3sR8cx0zzjXMuO5hvvmoeACzdK/PAVAAdpLWc9ZB+m3dvItg3Q7SA6CCsRDmw0Q4Ov4+pZt3NdjxV28ngGBwW3kb8801zLjOMeM8w3zzUY5hEl4AcB1UlV4JjsvzXaij31uh/jiHa/++THt/BNR2ehyqv8NGWHeIhLlQ/TzuA/Bj3fRt3TLzoI7yhkDd+h+rTVY0GcA9AAKg/nY+AmAH8KD2PreV9zDfnGPGuY4Z5zrmm595HeoX0gh1xHxH3zbHJwRChaJxCtbedwySWgJ1VBwJdSRjRWbbSaDGzXJwDP55CkAtgPVQAWpFX0L1IWqEGlQ2Em3hCHBbeRvzzVwgmHGuYsa5jvlGREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREXiAAbF5aV4C2Psf0Zy+ttys27fv0qnTtmN1L7SDqKTaov+Uf9HE7+qtAqO0b2MVywWjLldQebZHrnkT73P1V3zaHiPxFTxSACwAMBXCZl9bbFRs6FoDPaG1gAUj9gQ19VwA+B+DNPvje3hQI1wvAcqhsebSH2nKz1pbbXVz+Z1p7FoAFIBG5oScKwCAvrc9VNnQsAB1YAFJ/YEPfFYAhAHL64Ht7UyBcLwBzerYpGAmgFMAANz8XBBaARB45B8CFfd2IXtSXBeB3vPS9NrAApP7NBhaAPSkQvlMARmvf464gsAAkOhuW1wJYDaAGQAWAz9GxuHMUCM8DSAPQDNWnAlAFynQA+QAaAWQAeAcdj8z068gA0ADgAIB7XWjr+QAmaMtXA6gFsAfAEMNyAdr3vAPgZQDHtTYlALjNZL1PAziitSUVwB9hHl5mBeClABZDHYU2Qm2Xl1z4WRxtDDJ5z6a9dz2AbwBUAjikvXez1rZsrb0l2vf/n8l6fgP1MzdAbYN/gwUg9X82uJ5p5wEYi7aMyAEwCcAFhuWeABAKoEhb7rj2uXN1y0Shff8yQdcF0IMAYqD64Z6BysTJuvcDtfX8RZtfApV7mwH83GR9dwAIh8rHOgC7AfzaZDlXc+tnADZq31kGYAaAh+B5AejIGkf21gPYB+Am7f1/A8iCyq4oqLw0uhhAi7YOh2eh/n84DfV7TwHwhslng8ACkOhsWCZDhcprAL7S5i0zLCtQO2spgHEAhgG4FarI2wHADmCRto7N2vIzTNaRAtU3ZCyA96BCog7AjV209QdQATwdwCsA3gVwFECT1g6HAO17DgI4pn3Hu9p35gP4lm7ZP2jtPgxgOFSBeUprY45J222615do68vTfpZXAGzSluuqH5CjjUEm79m099KgwvdVqG0NAG9DHfWOBfAvAJ9Bbbt4tC+2b9Lm50JdJnkf6j+Pw2ABSP2bDa5nWrA2fw3UPrZUe73BsNwGAKugDipfgSosBcAnumUehDpQc/R7G4q2A2QzN6DtwPS/UEXPJ1BFm0Og7mdxZNRHUAVTBoBv65a9X1tfLIC3oDLosDZP30fO1dz6tvYd9QCmQBVSiWjLkMBOfjag6wLwsNaGEdpUBZVXr0Fl31sAJmrt32myjr9AnYT4nvb6QW29kVC/y2EAZkH93oyCwAKQ6GxYbjLMn6PNv1k3TwC0Qp2Z0ntCe2+MYf5qqOLqSsM6BMBg3bzLoEJmfRdtPRfqLKDexVCFzZe6eQHad5wE8P908x/X5us7JCdDheF3dfPug/nRu7EAXARVkBrPvq2ACrNvwzlHG4NM3rNp760wec9snc9qy9+jm7cBapvqby65DuqImQUg9Wc2uJZpt2ivFxqW+0Sbr7+yYLbfzYc6M6Y/W+jOJeA30fWl6kBtmQIA/6ub/7Q2/7/a6wEAMqHO/ukPBL8NdbVgm26eq7n1hvYd+jNs/wN1UO2NArAB7c/svazNL0b7n3WyNl+/LKCK+Sjd68+09p+LrgWBBSDR2bD8nWH+tdr8kbp5AvMjsQVQhcX/GubfqX3mdcM6Yk3WsRLqEogrOy+g+h9+Hyo8Q9B2iRRoK67mGD7z/9A+NH+qvZ5ksv5kdF4ADoC6NLtAa4N+CtKWNbv0YmxjkMl7Nu29ri6LX6h9n2Ndjksd50L9x2RWQIaCBSD1bza4lmmjtNfXGZb7sTZ/mpP1/y/Ufve8ttwtuvfcKQCDtM+/BJVnZgK1ZSYb5g+AKuLCtde/0Jb7Ozrm0UKoYuscuJdbEdp3GLvxvAvvFIChhnmOgtyYQY4TDPfr5p0DdUn6Xd08G9T/Q7/vol0AC0AiAG1heblh/nlQZ/vm6eYJ2p9pcwiHOpVv9D10vEwiUJdZjCZo713SRXtfgCrOmtC+r022bpkAbd4Ik88LgPHav+/SXr9ostx6dF4A/sjw/WbTHzv5ORxtDDJ5z6a9Z9bH5/tQfZlKTb5vnLaM4z+wCSaf/xQsAKl/s8G1TJuvvf4WOqqEuizscAPUWfVqdNzv9Adq7hSA34bq/ydQl41XQg3JpC8GA+E8o6IBpGv/fsakXcbp/8G93DqqfYeR40pKYBc/XzA6LwDnGeYFwDy3A7X5T+nm3aHN01+N+hFUFyXHGdPFcF4MBoEFIJHbBaBZgRAB8wLwIrheAE5E1wWgY6y6DQD+BtUZ+bdQ/Q9zdMsFaMu9Y7IOfRHnSQHoKLK+0tpgNv2ok5/F0cYgk/dscH5pKBKqb98HUEH9INo6ZTva9hPt9Qcmn58BFoDUv9ngWqYt0F6fZ7IOfQF4MVR3kmyos+yPQu3f76FjIeTuXcDnAHgA6sDMUbzsQNuVkEA4z6g9aCsAHd1A3oHzPPoW3MutDLTvj+jgOCMX2MXPFoyubwLRC4B5bgdq8/WD5X/gZN3nA3gMwFwAJ+D8/5sgsAAkcvsSsFmB4OwSsOMozdVLwLXo/BLwRqi774yXJPaiewWgJ5eAz4W6y+ybTtrbGUcbg0zes8G8AHRcwh5nmD/IpG114CVgsiYbPLsEfAnaXwJ2PD3C2CXjX+hYCG2BZ0OfjNbW+VvtdSBcuwR8m7bcy12s353c6ulLwJ4UgIno2MXH6Byos7wC4CrDe0FgAUjUZYdpff8WZwWC44hwlGH+Srh2E8jPoW5YMN55Z7QOamgA/SWSO7TvyNHNC4BrBSCg7vbt7k0gS6DuUDO7e/mHJvP0HG0MMnnPBvMC0HFGdbxhvuN3pW8bbwIhq7LBtUxz9DlbYFhuijbfcRPIY9rr+3TLnA/V79hYCK2EOnvoiu+bzHtEW+cftNeBaLukaXYTiKPf7zlQ2ZiJ9lnmoM8jV3Orp28C6W4BeAlU5j9iWM5sKKxh2mdvMMwPAgtAog5DJgyDurtKACw3LOusQNAPA/OFto6N2vKuDgNTj/Z3HJt5EW3B/jLUcAiVUGP35eiWC4DrBeBjaBsG5k2oSwsVWhtPdPHZS7TvrYW6A+1lqLMLq6GGkumMo41BJu/Z4PwS8G7t+z6EGh5mA9qGZdC37WaobZoL1admDNTd0slgAUj9mw2uZ1qwNn+Vtpzjtf5g9P+g9uccqKFJhkMNMZWEjoWQ4+zYpwD+CpUvznymrWcigH9Cnf0rgDogdQxtEqj7WRwZ5RgG5hhUQQbdso593gZ1htIGlRlbdMu5mluOYq8ewMfw/jAw3S0AX4S6wmG8M3sD1M86HurGmglQ/z8koeNNNkFgAUh0Niyvg+rzUgMVArPgfCBoM9+FCr1CqBs0MtH1QNCZUHenHUTXYQJtXaOgQsXxuT+gY9AEwPUCEFDjSaVDHRWnQIX2WrT1r+nssz/Sfp48qJ+7GKqf3r+6+FkcbQwyec8G5wXgpVD9EyuhhjxYjbY+f8a23QsV2I6BazkQNFmBDa5n2nlQXSqyofbfPKjLrcaBoO+GGqi4DirjpkBdYjYWQt+BKjIrYX4VQe9+qAPlQqh9tBDq0uwg3TKB2nqe1dpVqrUhBObPD78V6krJSbQNbL0K7e+gBVzPrcugDrhroQ7aP4N3B4LWC4BrBeAadLyDGFA3iUSgbXDrXKhLwD82WTYILACJev2xSf5SZCQB2N6D6w9AW//IH6Dj+Ia9yTGcjr/8boisIhAd+7/5i2CoAvMHUDfSeMN5UAe+w7pa0Inztfa8DhaARJYvAM9DxxtPAqHaaRzY2psC0NYfsq8DvkrXDl/63RBZXSD6Ph+6KxhtuZLqpXX+COr/rJ908/OOG3ocEwtAsjQbrF0ABkD1c7FB9YX5FOoSSzHMOxV7y4VwfciYnnafrh3X9GE7iKi9QPhvAXg92nLlzj5ui8MP0T53jSNXEFmKDdYuAL8H1UemAKrfyCmoPiZXdvYhIqJeEAj/LQCJiIiIiMiqRgFIAHAa6hl+G9HxctOFUGM1VUA9F3YdOj4Z4jKoO4DqtPV8go4juQdC3SHaCDU2UpB3fgQioj5nQ/t+TAL12C4iIp8UDlWI3QA1AGco1G3a39EtMw/qrqH7oQYK3gf1lAeHc6GG6dgOdbv7w1C3putHSb8c6pb16VDDALwONfjtQ17+eYiI+oINqlP9j3VTb3XhICLy2A/R/vE634Maj0jf18Hx6B5HB9KHoZ7dqD8r+ArUA7odw2hMQcc7jlai7ZE5RET+zAY1TBERkV+6Cqq4czyO5n7ttXG8oFyokdcBNbK3Mfgu1z73C+11NNRglXovQhWJzlwA9Zgt/RRgMo8TJ079Z7oUHQcq9wc2qKscRVADGC+H+aDAzgyA+tn7evtz4sSpZyefzLhzoEYyj9HNew6qz57RfqizeoB6zFiE4f3/gSoAH9ZeZ6LjM2kdz1c0PjrGwYaOfWo4ceLU/6dL4X8ehno+681QXVtioQ6UnQ1pYTzAvQZ9v905ceLUO5PPZdw8qEfE/Ew3z1kBmAD1HEKg8wLw99prswLwD9oyxscAORgD8lIAkp+fL9XV1Zw4cfKxqfTkKYlOzZG5EYdl2OI9cs+HIfLzN1fLsMV7XPp8fn6+Ixwv6jSp/MPFUFc4XnLyvg0m/zEw36w9VVVVSenJU3KiqFwy8krkcHaRJGTmy560XNmRdEJCDxyX9XGZsjLmqHy1+4gs3pkqCyJTZFb4Yfk09JBM2XRQPlyfKOPW7pfRq+Jl5Io4efvrWHlj6V4ZtmSP/PvLaPnnwt3y4oIo+du8XfLcnB3yl9k75OmZkfLHGdvliU+3yaPTIuThTyLk91PD5cEpYfLAR1tlyORQuW9SqNzzYYj8ZmKI3D1hi9z5wWa5ffxmuW3cJhk8dpP88v2NcuuYjXLrmA1y8+gNcuOo9XLDyPVy3ch1ct2IdXLNe2vl6vfWyqB318pV766VK95ZK1e8vVaueHuNXP7WGgl4a40MHL5aBg5fLZcNXy0/f9N/pj1puS79fn0142ZDPez6csP8vrwEbHQRAKmurhYi6lstrXY5WlwjqxLyZPT6ZHl05h65anSoDBwR0mF6fmGcS+usrq72yXD0QAKAj5y8Z3qAy3zzX62tdqmsbZTs8jNyIPeURB4pkTWJ+bI4Jls+j8yUSaFHZOS6wzJs+QH5+5fx8qe5e+WhGbvl3qk75Vcfbpfrx4bJ5SM77j+cfH86lFfp0t+Ir2XcAKjirxDtH3bt4LgJ5CndvKuhfgDjTSD6pya8DFXcOR7ePQXqTmG9b+DeTSAsAIn6gN1ul7yKWtlyuFA+DEmTp+fHynVjw0yD8JcTtknQ4nj5dFuG7EwvlZOnG1z+Hl8LRw99F2oA8/+6uDzzzQfZ7Xapqm2SjJIaic4sk7WJ+TJn1zEZvylVXv06UZ6au1eGTNslv5iwzevF21WjQ+WGceEyeOI2ufujHTJk2i55+LNoeXJOjDwzP1aGLoqTfyzZL698lSj/+eagvLUqSUauS5ZxG1Nk4pY0+WhrukyLOCoztmfI7J3HZH5Ulizaky1LYrLlq305siI+V1Yl5Mm6A/my8VCBbDlcKGEpRRKeWizb00pkZ3qp7DpaKtGZZbL3WLnEZp2U+OwKSThRIYk5p+Rg7ilJyquU5PwqSSmoktTCKjlSVC1Hi2sks6RGjpXWSFbZaTledlqyy89IzskzknuyVvIqaiX/VK0UVNZJYWWdFFXVSXFVvZRU10tpdb2U1qiprKZByk83yMnTDVJxplFOaVNlrZqqapukqk5N1fVqqtGm0w3NZ6cz2lTbqKa6xpazU32T86mhueupsblVmlpapbXV7tLfk69l3FyoZ47eh/bDF+j75c2DOuM3BGoYmFhtcnAMAxMBNZTMQ1BjARqHgakDMBXqLuJhcH8YGAYkUS8oP90gO9JLZPq2DHlhcbz8YsI20/+grh8bJs/Mj5XJoUck5HCR5FXUit3uWhCa8bVwdNM0qBwNAHA31LBY5VAjK7iC+dZHahubJb24WrallciiPdkyflOqvLhkv/x2epRc8/5Wtwu3G8aFy68/3iGPztwjQxfFyWvLD8jIdYdlUugR+TwyUxbHZMvqhDwJSymS3RllknCiQtIKq+VE+Rkpra6XmvomaW5p7evNQj3A1zLOWQfFIN0yjoGgT0Hd5bYeqkjUGwhgK1SRVw4VhsaBoIcAOATVp/A43B8ImgFJ5GWnG5olNuukzIvKkle/TpS7P9rh9GzEY7P2yJgNybI6IU8ySmqkxcWjXlf5Wji6aSXUHcCNUI8wXAn3Hl3IfOthZxqaJSmvUtYk5svk0CMStDhefv2x+d+7cbrlgwh58NMoGbooTt5alSRTwtJlSUy2hCYXyb7jJ+VocY2UVtdLYzMLN3LOzzOuTzEgiTzQ0NwiSXmVsiz2hLy1Kkl+Oz1KAkwuWwWMDJEHpkfJ8FWHZGnsCTmUVykNzS093j6LhyPzzYsam1vlcH6lLI09IW+uPCSBn+zqssB7bNYeGfb1Afloa7p8HZcj0ZllcqL8jNQ39fzfPlmDxTPOIwxIIhe1ttrlWGmNrE3Ml7EbU+Tx2TEyaLT55ay7JkfKK18lytxdWbI3q1xq6pv6pM0WD0fmmwfyT9XKpqRC+WBzmvxxTowMGmP+tz544jZ5dsE+GbcxRb7alyNxx0/KqTONfd18sgiLZ5xHGJBEJux2uxRU1snW5CKZvPWIPLtgn9w4LtzpmY6/fRkv0yOOyva0Eimtqe/r5p9l8XBkvrmhrrFFdh4tlfGbUmWIk7N7N9si5O9fajckHXXvhiSinmDxjPMIA5JIRE6daZRdR0vl88hM+ceS/TJ44nbT/wCvfT9M/jxvr0zYkiabkgol5+QZj27S6GkWD0fmWyfsdnVGe2H0cRm6KK7DGb4rRqk+qmM3psj6g/mSXe7bf+tkTRbPOI8wIMlyahubJT67QhZGH5fXlh+Qe6bsNC32rhgVKo98Hi0j1yXLyv25cqSo2u/uJLR4ODLfTGSW1Mgn4UdN/+7vmhwpI9cdlrCUIqnuo24LRO6weMZ5hAFJ/VpTS6ukFFTJ13E58u6aJHloxm6nY4sN+WSXvLHioHy5J1sScyr6RUd1i4cj801TWFkn86Oy5PefRbf7mx80ZqsMXRQnC6OPy7HSGp7hI79j8YzzCAOS+o3WVrtklZ2W9QfzZfymVHlyToxc7aTj+u2Ttsu/libI7J3HZE9muVTV9c+zHRYPR0vnW31Ti6xKyJOn58e2+9u/clSovBS8XzYlFUptY3NfN5PIIxbPOI9YOiDJf9ntdimqqpOwlGKZEpYuzy3cJzeON79J46bx4TJ0UZxMDU+XiNRiKan2nZs0eprFw9GS+VZYWSdTwtLl1g8i2u0Hz8yPleVxubxDl/oVi2ecRywZkOR/KmsbZXdGmcyMzJSXghPktg/Nb9K4esxW+dPcvWLbnCobDhZYvuO6xcPRMvlmt9slPrtCXv06Ua4Y1fb86Ls/2iFzdh2Twsq6vm4iUY+weMZ5xDIBSf6jrrFFEk5UyKI92fKfbw7KfVOd36Tx0IzdMmLtYfkmPldSC6ukyc9u0uhpFg/Hfp9vdrtdtiYXycOGvn3PLtgnYSnFfnfTEpG7LJ5xHun3AUm+rbmlVdIKq+Wb+FwZue6w/P6z6HZnMPTTvVN3yn++OSgLo49LwokKqWv0/5s0eprFw7Hf5pvdbpddR0vl0Zl7zu4f17y/VUauS5b04v738xI5Y/GM80i/DUjyPXa7XU6Un5GNhwrkg81p8tTcvU4fDD944nZ5KXi/fB6ZKVEZZey31E0WD8d+mW/x2RXy53l7z+4r148Nk+kRR6WylvsIWY/FM84j/TIgyTeUVtfLtrQSmRZxVIYuipObbRGmxd6N48Llr1/sk4/D0iUspUgKK+ss3W/Pmywejv0q3w7nV8rfvoxvN4TLhyFpfBoHWZrFM84j/Sogqe9U1zdJzLFymbPrmPx7WaLcOTnStNgbNGarPDE7RsZtTJF1B/LlWOlpaW1lsddTLB6O/SLfssvPyL+XJbYbxmX0+mQprrLO3exEzlg84zzSLwKSeld9U4scyD0lS2Ky5c2Vh2TINPPnhgaMDJHffbpb3lmdJMv25UhyfpU0NrNTem+yeDj6db41NrfKzMjMs49oCxgZIsNXHpLck7V93TQin2HxjPOIXwck9byWVrukF1fLqv15Mmp9svxhZrRc6eQmjV9/vEOGLT8gC3ZnSdzxk3KmgYPM9jWLh6Pf5ltiToX8dnrU2X1r6KI4ySip6etmEfkci2ecR/w2IMn77Ha75FXUypbDhfJhSJo8PS9WrhsbZlrs/XLCNnlxyX6ZsT1Ddh4tZT8kH2XxcPS7fKuub5IxG5Lb7WcbDxWwTyyRExbPOI/4XUCS95TVNEjkkRKZvi1DXlgcL7+YsM202Lt+bJg8Mz9WJocekdDkIsk/Vcv/kPyExcPRb/LNMZ6ffoDzd9ck8e53oi5YPOM84jcBSZ6pqW+SvVnlMi8qS175KlHu/miHabF31ehQeWzWHnl/Q4qsTsiTzJIaaeFNGn7L4uHoF/lWWl0vLwUnnN0HAz/ZJXuzyvu6WUR+weIZ5xG/CEhyT0NziyTlVcrS2BPy1qokeWB6lASMNL9J44HpUTJ81SFZGntCDuVVSkMzB1fuTywejj6fb9GZZTJ44razB1/TI45KfRP3QSJXWTzjPOLzAUmda2m1S2ZJjaxOyJP3N6TI47P2yKDR5oMr3zU5Uv69LFHm7sqSvVnlUlPf1NfNpx5m8XD02XxrabXL9IijZw/MHpqxW44W8yYPIndZPOM84rMBSR3Z7XbJP1UroclFMjn0iPxlQaxc7+QmjVs/iJC/fxkv07dlSOSREimr4U0aVmTxcPTJfCuprpdn5see3VdHrkvmWT+ibrJ4xnnEJwOSlIozjbLzaKnM2J4hLy7ZL790cpPGte+HydPzYuXDkDTZnFQouSd5kwYpI7a6rgAAIABJREFUFg9Hn8u33RllZ/fj68eGycZDBX3dJCK/ZvGM84jPBaRVnWlolrjjJ2XB7iwZtvyA/GaK+U0aV44KlT/MjJZR65Nl1f48SS+uluYWDq5M5iwejj6Tb80trTLNcMn3eNnpvm4Wkd+zeMZ5xGcC0koam1slOb9KvtqXI++sTpLffbpbLje5SWPgiBAZMm2XvLnykCyJyZYDuad4qYjcYvFw9Il8K61pf8l31Hpe8iXyFotnnEd8IiD7s9ZWu2SVnZZ1B/Jl/KZUeWJ2zNlHOxmnOydHysvLEmT2zmMSc6xcqup4kwZ5xuLh2Of5llZYLXdpz8XmJV8i77N4xnmkzwOyP7Hb7VJUVSdhKcXycVi6PLdwn9w4Pty02LvZFiFDF8XJJ+FHZVtaiZRW88Hu5H0WD8c+zbcd6SVnb9IaMm0XL/kS9QCLZ5xHWAB6oLK2UaIyymRmZKa8FJwgv9KN4q+frh6zVf40d698sDlNNh4qkBPlZ3iTBvUKi4djn+Xbkpjss906/vrFPqmq5dl8op5g8YzzCAtAF9U1tsj+ExWyMPq4/Oebg3Lv1J2mxd4Vo0Ll959Fy4i1h2V5XK6kFlZJE2/SoD5i8XDs9XxrbmmVcRtTzubBe2sOc/8n6kEWzziPsAA00dTSKqmFVbI8LldGrD0sD83YLVeMCjUt+O6bulP+u+KgLNqTLQknKqSukZ27yXdYPBx7Nd9ONzTLC4vjzz5lZ35UFs/0E/Uwi2ecRyxfANrtdjlRfkY2HioQ2+ZU+dPcvXK1k5s0bvtwu/xzaYLM2pEp0ZllUlnLB7WTb7N4OPZavpXW1Msjn0fLwBEhcs37WyUspbjHv5OILJ9xHrFcAVhaXS8RqcXySfhRGbooTm62RZgWezeOD5fnFu6TKWHpEpZSLMVVvEmD/I/Fw7FX8i27/MzZcTsHT9wmh/Mre/T7iKiNxTPOI/26AKyqa5I9meUye+cx+dfSBLljUqRpsTdozFZ5ck6MjN+UKusP5svxstPS2spLN+T/LB6OPZ5vSXmV8gvtyR73Tt0pOSfP9Nh3EVFHFs84j/SbArC+qUUSc07J4phseWPFQRnyyS7TYu/ykWoU/nfXJMlX+3IkOb9KGpvZSZv6J4uHY4/mW1RGmVynDfPy6Mw9fN42UR+weMZ5xC8LwOaWVjlSVC0r4nNl5LpkeeTzaLnSyU0a90zZKa8tPyBf7D4u8dkVcqahua+bT9RrLB6OPZZvq/bnnb0xbOiiODnNXCHqExbPOI/4fAGov0njg81p8tTcvXLN++Y3aQyeuE3+sWS/fLY9U3YdLZWKM7xJg6zN4uHo9Xyz2+0yPeLo2cx5c+UhXkEg6kMWzziP+FQBqH+SxpSwdHl+YZzc5ORJGjeMC5dnF+yTyVuPyNbkIimorOOQC0QGFg9Hr+ZbQ3OLvLUq6WwGTYs4yswh6mMWzzi8BiAHQAOAeAC3u/HZPi0Ay2oaZGd6qXy2PVNeCt7v9Ekag0Zvlcdnx8i4jSmyNjFfjpXW8CYNIhf0k3DsbsZ5Ld/yKmrlsVl7zg72viI+1wu/HSLyVD/JuG75C4BGAC8CuB7AFwAqAfzIxc/3SgHoOLMXeaREK/ac35F7xahQeWjGbnlvzWH5Oo43aRB5oh+EoycZ55V8C08tPnsl4pYPIiQqo8xLvx0i8lQ/yLhuiwcwW/f6HACFAEa6+HmvF4BVtU2SmFMhK+JzZcKWNPnrF/vklg/Mx9oLGBkiD0yPkjdXHuKTNIh6QD8IR08yzqN8O1Z6Wl5csv9sXj0xO0YKKuu8/BsiIk/0g4zrlvMBtAB40jB/KYBNLq7D5YAMTS6SkMNFsjmpUNYdyJdl+3Jk9s5jYtucKsO+PiCPzdojtzop9Bxn9n736e6zxd7+E7wjl6in+Xk4eppxLudbZkmNhBwukg0HC2TWjkx5Zn7s2ey6clSoTA49wisRRD7IzzOu234K9UPfZZg/Feqo2cwFUBvJMV0KFwPy8pHmhZ3ZdOfkSBm6KE7Gb0qVVfvzJKWgSuqbeGaPqLf5eTi6m3HdzrfPIzNNs+yl4ATJKjvdC78pIuoOP8+4bnMWjp8AiHPyGZv2mXaTKwH5lwWx8vT8WHlmfqwMXRQnLy9LkLdXJ8lHW9Nl0Z5sCU8tlrTCao6HReRD/Dwc3c04G7qZb2sS8+Xp+bHy3MJ98ubKQ7Iw+jgv9xL5AT/PuG7rzuUR0yPk/Px8qa6u5sSJUz+b8vPz/Tkc3c045hsnThab/DzjPBIPYJbu9TkACuD6TSCXwuSImRMnTv1uuhT+yZOMY75x4mSdyV8zrtscQyS8AOA6AAughki4xMXPD4DaaBe5MDnC1NXlrT5xe3Fb+cr2uhRqX/dHnmScO/nGv8Oe/Ru0+sTt1bPbyp8zziOvA8iFCsl4AHf00PdcBPVLuaiH1t/fcHu5jtvKPVbbXsw438Nt5R5uL9dxW/kg/lLcw+3lOm4r93B79QxuV9dxW7mH28t13FY+iL8U93B7uY7byj3cXj2D29V13Fbu4fZyHbeVD7oAapiFC/q4Hf6C28t13Fbu4fbqGdyuruO2cg+3l+u4rYiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi64zUAOQAaoAZjvb1PW+Mb7gWwBUAR1G3rxmeWDgAwAUAxgHoAkQAG9WYDfcgoAAkATgMoA7ARwDWGZS4EMAdABYAzANbB9afa9DevAkgGUKNN+wA8rHuf28q7mG/mmHGuY8a5jvnmRxyPY3oRwPUAvoB6HNOP+rJRPuBhAB8C+BPMw3EEgCpt/s1QD7DPhvrjtppwAEEAbgBwC4BQqKc7fEe3zDwAeQDuBzAYKhT29morfcdjAB4BcLU2TQLQBLX9AG4rb2K+OceMcx0zznXMNz8SD2C27vU5AArh2gPZrcIYjgOgjorf0c37HtQZhmd7sV2+6odQ2+xe7fX3oALgz7plrtWWubN3m+azTgF4CdxW3sZ8cw0zzj3MOPcw33zQ+QBa0PHIbynU0R4pxnC8Qpt3q2G53QA+761G+bCroLbPjdrr+7XXFxuWywUwvBfb5YvOhfoPtRHqDBW3lfcw31zHjHMPM841zDcf9lOoX8ZdhvlToY6cSTGG493avJ8YllsNYFVvNcpHnQMgBECMbt5zUAFgtB/AlN5olA+6Car/SwvUZbZHtPncVt7DfHMdM851zLiuMd/8gLOA/ARAXO83x2e5Go5rAKzsrUb5qHlQHe5/ppvnbKdPAPBxL7TJF50PdRbhVwA+AlAOdYTMbeU9zDfXMeNcx4zrGvPND/ASiWt4ecQ1swHkA7jcMJ+n/bsWCWABuK28ifnmOmaca5hx3cN881HxAGbpXp8DoADsJK3nrIP027p5F8G6HaQHQAVjIcyHiXB0/H1KN+9qsOOv3k4AweC28jbmm2uYcZ1jxnmG+eajHMMkvADgOqgqvRIcl+e7UEe/t0L9cQ7X/n2Z9v4IqO30OFR/h42w7hAJc6H6edwH4Me66du6ZeZBHeUNgbr1P1abrGgygHsABED97XwEwA7gQe19bivvYb45x4xzHTPOdcw3P/M61C+kEeqI+Y6+bY5PCIQKReMUrL3vGCS1BOqoOBLqSMaKzLaTQI2b5eAY/PMUgFoA66EC1Iq+hOpD1Ag1qGwk2sIR4LbyNuabuUAw41zFjHMd842IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIh6QQ4A0abZvfSdgdr3BXphXU+irf0C4FdeWCcR+bcgtM+FH/TS90Zpkzckoa39IV5aJ5Gl3Q3ABuDiPm6Hu54D8GYPrDcHQDSAoQBu64H1mwmE8wLwZu29211c18+g2r4ALACJ/M1PofL4Vi+vNwgqD96EyocLvLx+Z6LgvABcD2CrG+t6BKrt5WABSOQV70AFQ0Aft8NdIVDFmrflAAjugfV2JhDOC8CRAEoBDHBznUFgAUjkb34Ftd8GeXm9QeibnI+CeQH4LQA1AF7rxjpzwAKQyNR33Fy+JwrAAQC+7eZn/sfN5X2hALwQwDle+M5AOC8Ao91oj14QWACStZ0DtY/2JXczwlcKQHfz2JkomBeA90O15/JurDMHLACJYIPaia4H8A2ASgCHdO/fD2APgFoAVQA2AbjO5PPGKUB7/zwAYwEcB9AIteNNQsfLBzlQO+RDABK1ZTu7PBsFIBXAYKgCpw7AZ9p7TwAIBVCkree41oZzDZ83tjlH9/4FAD4AkKWtIx/AVJN2m8mBecEVqH3PswA+BFAAwA516fz7AKYBSAFwBurINgzALSbr+RmAjVC/kzIAM6C2m1kBeDGAFgBP6+Y9C+AAgNPa96QAeMPke4LAApD8nw3q7/haAKuh/uYrAHyOjsWdo9/u8wDSADRD9YkF1IHxdKgsaAT+f3t3HiZHWa5//JsoIIrIOcqiHOkgi7KIePAn7kSOHg4uiCyKijKIoiIqUbTZGUB2lC0kIewokBCWAE0SICGTfYMsJCFkn2QSCEnIvmdmnt8fb1WmpqZ6prq7eqZn6v5c13vBVFVXVVdl7nmq36q3mYu7+A1/sh5cx1xgG+737Rsx9rUnpWWE//pwqwosczwwDFiPy81RwFdj7FsV+QvAGkrLY9/53vytwGTg6+QvAP+BO0e+A4CHcMdsO/AO7u9V1P7WogJQZFc4zsYVFb8DLvDmfQsXgHOBvwJX4e6dWEPTL9UxuMIxeG/I2TR9iviwN2+Qt95HvJ+fDe1HLTDfW/eNwG9o/YGGGtwv+ErgLlxw/MCb9ywwEBfOv8WFvgG3Bl7/bVyhuyqwz37QdwdewhVYt3vrvts7FoNb2afge3k4YnpPmo71NKAXrnv2g7giawHuvZ+PC8hluKL7E4F17Ik7H1uBm3GF22vADKILwB97+/2RwPs2YDjufFzgvbeBEftbhQpA6fyqcf+O3wCex3UZ/sub9mhoWQPexN0ycRXu9+NYXJE3AleM3e+t43lv+dsj1jETly1XAn/DZcIW4Og29rUnpWXE/t50w93D62fbp7z5J+KKo/HAn3GZPcOb1tY9wlW0XgCWkscA53nTxwF/wB3XtbiCsCZim3NC6xiHOxbXeeu6FHiV6MK7FhWAIrvC8YmIedNwQfifgWnHAA24Qs6Xrwv4c970+0LTb/WmfzMwrdabdlLM/a7xlv9NxLyoruN+uIIu+Alevi7gs3Hv8Wuh6b/xtvmVNvatltYLwIUR+7gHLbt5euA+PbgyMO1P3jqCn+h9EFc8RxWAj9I8PO/AhWTU1XdYFSoApfOrxv07fi40/R5v+jGBaYb73T8ytOwPvHmXh6Y/iSsKDwmtw3CfhvkOwl20PdPGvvak9IzI1wXcDZiH+/Qv+KnlnsAi4OU29q2K1gvAUvJ4N9zfmmnA7oHlfu2ttyb0+oNpnnf7eD9f3Mr+B9WiAlBkVziGr5I+7k2/OeI1w3BXt758BeCl3vQjQtMP8KbfFphWiwuhuGpwwbd7G8t9GDdcwc+8bQa7S/IVgM/hujM+FmqHEf1HIKyW1gvAq9p4/fuAj3rbnEHzT0tfwnWlhLud/krLArA77or8r4Fp1bgu4f9rYx9ABaB0DdW4f8f/G5r+GW/6JYFphvvUKOxe3O/Nh0PTv+S95sLQOsZHrGMAruu2tYuvnpSeEfkKwM97039By2y7D5enrd1rWEXrBWApefxlogvI3XCfAtaEpl+Iu5B9v/fzHrhPMXPAf7SxD6ACUARoCsdPhqb7wfbLiNfc7s3zu3nzFYD9cFfTu0WsYy2uW9hXi+tiiasGd5Uc5ShcIK6n5b0wwUI3XwH4ZsTrgu3ONvatltYLwJ9HzOuO6+6Zj/tDE9xe8A/SW7h7bMJOoWUBeDxN93f69qPp/S0DHiR/MViFCkDp/KqJfljg/bh86huYZsADEesYBiyNmP4RWnZnGs17SHzXevP2b2Vfe1J6RuQrAH9E67lmtF48VdF6AVhKHp/l/XxixOun0rIAHIr79DXoItz53IHLyL/hPmyIUosKQJFd4Rge1NO/IiulALwX9wv5flqKKgAL+YWswX1KF7YPsBr3aeKfgO/h7mX8Gy0LpHwF4Fu4+4W+lad9uo19q6X1AvCMiHlXePMexIXh/3rbmkXz8JuLu2k7zO+i6hmYdg3R72934PtAH2Ax+f9gVaECUDq/agorAKMGb3+J6AJwb+IXgNcRvwAsJSPyFYB+kXUx+bMt6mLdV0XrBWApefwTWt4W5JtG8/e3J647vSpi2UOAv+C6s7fj/s58PmK5WlQAiuQtAFvrAh5K8y7gv1BYF/D+RHcBJ1EA+t9iEe7S9u8l6RmY9gLRBdKLuE/HCh03z1dL4QXgdKK7npbRPPwK6QJ+DXefU2u64z6pNeDQ0LwqVABK51dNYV3AUQVgvi5g/1P2uF3Am4nXBVxKRhxHdAH4/7zp57ey/dZUUXgBGDePC+kC/h7uvsvWCmlwt+xsBv4dMa8WFYAieQtAcFdeK2j+DR9H0/IhkN966wiPPO8/BHJvaPrNtLzaqyWZAvD73rpPCEzbHfdewgXSAFy4hJ1D/qDck7bHSayl8ALwdWBkaNqZtLwBOu5DIPvjQvI7oXV+NGLbF3ivPSo0vQoVgNL5VeP+Hed7CCR4X3C+AtD/hP3S0PQBxHsI5JO4T63Cox+E9aT0jPAL2/AwWt1xTxHPA/aKWP++bexbFYUXgHHzeDfc/cpxHgLpgxsiJuiDtBzSpzvu79cgWqpFBaBIqwWgPwzMHFy3wZW4X9I1NO9O8a8sX8Tdu3IWLYeBGYgrNPyfo4aBSaIA/Ki3f7W4YQ564e4h8b8DsmdgWf9Ts3/iuiC+703v7r2XRtzT0RfiCq++uPHD2iqIaim8ALzGm/cQLvTu8rYVHgLBL/a2AjeRfxiYc3HDToSfwHsW14V8NW6ohGtxRfB0Wt4AXoUKQOn8qnH/jv1hYC7APR1vwGOhZfMVgMFhYPp76xjsLR93GJitNH/iOEpPSs8I/1Ozt3C/42fRlNc9vf1Ygjsuv/b+OwrXI9KaKgovAAvJ4/O9aWNxw8D8k+hhYGpx+RV0LO5Y9PVe+ztcN7ABp0fsVy0qAEVaLQAB/gf3C7kFdxPv87Ts0gV3f8oy3KeDwZB4P+6JtkW4m3OXAjeQfyDouGqIDhxww7RM8PZ5Oe4Tx/+lZeB8CPcHYK03rzYwbzdccM/CPd22BldoXYW776c1tRReAO6B6xJ/29vvsbgHcWpoeQP0QbhPMzbj/sjcQcuBoAfhitiw03HdyO/i7pFZgusCjrpZugoVgNL5VdN0K8og3ADKa3DjX+YbCDrKXriiZDkuy+bR9kDQ83D5MZXWxzX19SSZjDiFpoGsw93BxwJP4+7N8wfnH0j0AxhBVRReAEL8PAZXuC3CHbMptBwI+ijvdeHvWP8o7pjPwT1pvQ6YSPOekqBaVACKSBnU4j45/BiFf61eEt6PC8AL2lowj91x+34hKgCl86um9YvcpLVWRHZmVbj39nncsSz2HulS/A3XrVvstvfB7ftSVACKSBnU0nQfUEf8IdgP90fv40W+3r9x228qAKUzq0YFYBKqaJ4L7XU8g36Eu12nWH7Xs6ECUETK4KvEHzKmEu1L86Ehwk8+inQm1agATMLHiT9kTKU6nqb9j/qedREREekiqlEBKCIiIiIiIiIiIiIiIiIiIpJW3YADceO+qampdc12IB0zpEVHU76pqaWjpTXjSnIgzR97V1NT65rtQNJH+aamlp6Wxowryd6A1dXV2fr16yumrV27zhYtX2kT5iy156fMt/tHzLLbX5xmtzw/1a5/9nWrfmqKXT5wkmUfn2C9/jXeLnx4rP3uwTH2mwdG2/n3j7Zf3TfKzutfY7/sX2NV9460X/QbaT/v27z9ol9TO6fI5r++ab2v2s/7vmpn92nZ/HmuFb8Pvyh6uy23Wcx242yzre2W/xhHn+e47z36GI+05ucw+pwWu822zm1wO+f0G2k3Pzc11u9SXV2dH457d3DWdISy59uatets6oJl9sTYt+zqp6bYef1r7JIBE63/iJk2YvpiW7x8ZYfnqZpaV24pz7iS7A3Y+vXrraMsWrXJbn9lrv3m0dfsB73H2pdvGG6HXPqiZbI5NTW1PK3qwUmxfr/Wr1+f5nAsW741NDTa5c++YYdfPqTNc3XsNS/ZGX3H2Z8HTrc7h8+zwdOW2dQla2z1xm3W2NiY+L6JpEnKM64kHVIAvrdpuz08brH9oPfYvKHZ45KcHXfdK/adO0fbuQ9Ntl4Dpln2qRl2xbMz7ZrnZ9sNQ960f7z0lt01fJ71GbnA+o9aaPeNXmgPjFlkD41dZI+MX2yPTqi1f02otccmLrHHJzW1xyY2tX9PrM3b/jWh5bTga4PrfcJrAya7NnDy0l1twOSm+VH7EG5R24naZmvb9X8ObreUbebbbnDbUdtMcruFbLMcx7g932vUufW399jEJfbqnHdj/a51UDheivue043ASmAwLQcMr6FlF06/0DIH4b7veYu3nltxXwMYV9ny7eahc3Zl1RFXDrVT7xlrlz7zht03eqFd98JsO+fBSfa1m0dYj0taLw6PumqYnXzHaDv/0Sn2t0Ez7JrnZ9s/Xp5r945aYI9NXGKDpy2zEXNW2ISFq23K4vds6pI1NqNurc1avs7mvLPe5r+7wRau3GhLVm+2ujWbbfnaLbZi/VZbuWGbvbdpu63dvN3Wbdlh67fusA1bd9jGbTtt07adtmV7vW3d0Xbbsr3eNm93r9m4badt2OrWtW7LDlu32Wuh9W/enn/923Y2b3G22WK7W1puc1Mb2y3lvZZzu/428x7jMr/X8HaD79nfZni7/rYL3b7/mnzn1t/elu31tqO+IfbFkQrA4rVrATh2/io77+HJzT7h+9SlL9o5D06yB8cusqEz37FpS9fa2+u22I76hnbZJ5GurIPCcRjua7WOwn0bwYvAEpp/b3QN0B84INCC+/g+YCbwCnAscDKwCrihgP0oS749M7VuV34NnLLUGhry/6Hasr3eZi1fZ89NX253j5hnfx003X7Ub7x96YbhbRaHamppbq8vWRPr91EFYPHarQB8dPziZoH33btG2/1jFtnKDdvKvm2RtKqQcNzX24dvBKbVAHe08pqTgQZg/8C03wLrgd1jbjfxfHt9yRo7zOv2vXnonJLWtXVHvc1/d6ONmLPCHhm/2Hq/Ot9uGjrHrnh2pl00YJr96pEpdta9E+z7d4+xE28baSfc8qp97eYR9uUbhtsXr3/FjrvuZTv2mpfs6KuH2RFXDrXDLx9ih102xD6lW2jUukCbUbc21u9RhWRcp1T2ArCxsdHueGXerpP654HTbd6KDWXbnog0qZBwPNTbh6MD02pwn+itBmYBNwIfDMy/FvfF9kEHe+v5fMztJppv727Yasdd94plsjn71SNTWv3krxI0NDTajvqGZl2twS44v2uvtRbV3eh3327f2WA76hts+86mbQTX77eNEV2MG0It7najthm13Y0dvN1itplvu21tM997jtpOsEV15QbPbXibrZ3buNuNep/B97p1R71t2rbT1m3ZYTtj9gJWSMZ1SmUtABsaGu3q52btKv7++fJc3fQs0o4qIBy7AzlgbGj6+cBJwGeBnwHLgGcC8/sDL4Ve80Hcezk5z7b2oOXYYInl2+2vzLVMNmff/meNbdy2M5F1ikhpKiDjOq2yFYA76xvsogHTdhV/D45dlPg2RKR1FRCOfYFa4L/aWO5E3H4e4v3cWgH4f3nWUU3E+GBJ5du3/lFjmWzOnnqtLpH1iUjpKiDjyqKalmH2VmD+B4B7gPeATcDTNL9fJo6yFYC9X51vmax7yOPZqcsSX7+ItK2Dw7E3UIfrum3Lh3D7eZL3czFdwGX7BPCtdzZYJpuzwy4bYuu37kjgzIhIErpyATiL5k/JfSwwvy+wFHflfBwwARhX4DbKUgCu3rjNjrpqmGWyORs4eWmi6xaR+DooHLvhir/lwGExX/NV3H4e4/3sPwSyX2CZ83EPgewRc52J5dttL71lmWzOznt4SgJnRUSS0pULwPAVsO8jwA7gjMC0z+AOwpcK2EZZCkD/vr/v3jW64m+UFunKOigc+wDrgBNofgG7pzf/EOBK3IVrD+AUYCEwKrAOfxiYl3BDyZyEGwuw3YeBaWxstJ63jrRMNmeDp6k3Q6SSdOUCcDPwNrAIeAw3MCo03S+zT+g1S4BeBWwj8QKwdvUmO/QyNwzBmHmrEluviBSug8Ix33d1VnnzP4kr9t4DtgHzgVsi9jEDDMENBL0KuI0OGAh61vJ1lsnm7PDLh+jhD5EK01ULwJOBM3FdIicB43EF3oeBnwLbI14zGbi5lXWW9Sk5M7PfP/a6ZbI5+/kD8b6qSkTKp6uGY0yJFIA3ed/68ZtHX0vorIhIUtKScfvg7n85j/wF4BTgplbWUU0Zn5KbvnStZbLua9xmL++47xcWESct4ZhHyQVgY2Ojfe3mEZbJ5uyFGcsTPDMikoQ0ZdwU3ICpxXYBl/UTwLPvn2iZbM56DZiWyPpEpDRpCscIJReAC1dudE//Xj7ENm9X969IpUlLxu0FrAH+SNNDIKcH5h9OBz4E8s66rbvG/FuyenMCp1VESpWWcMyj5HwbPG2ZZbI5O/WesQmeFRFJSlfNuNtwT9H1AL6C+1L0Vbjv1QQ3DMwS4Ju4p+nGe60QiRWAD45dZJlszk7rMy6BUyoiSeiq4RhTyfl27QuzLZPN2VWDZyZ4VkQkKV014wbgngDejvuapAE0jZIPTQNBr8E9LfwMbqiFQiRWAJ7eZ5xlsjm7f4y+8UOkUnTVcIyp5Hw7s+94y2RzNkjf/iE3nOLZAAAgAElEQVRSkVKecSVJpAAMdv++vW5LQqdVREqV8nAsKd/qGxrtiCuHWiabs7krNiR8ZkQkCSnPuJIkUgA+MEbdvyKVKOXhWFK+zX/Xff3bZ64YavUa0F6kIqU840qSSAHod/8+oO5fkYqS8nAsKd+efr3OMtmcndFXF7YilSrlGVeSkgtAdf+KVK6Uh2NJ+eZ/peU1z89O+KyISFJSnnElKbkA9Lt/T1f3r0jFSXk4lpRvp3k9G89O1ff/ilSqlGdcSUouAH/xwCTLZHN23+iFCZ5SEUlCysOx6HzbWd9gn75iiGWyOZv/7sYynBkRSULKM64kJRWAO+sb7EjvKbmZy9YlfFpFpFQpD8ei8+2td9wDIEdeOdQa9ACISMVKecaVpKQC0P/u389ePUxPyYlUoJSHY9H59sKM5foGEJFOIOUZV5KSCsB7Ry2wTDZn5z08OeFTKiJJSHk4Fp1vt78y1zLZnF385PQynBURSUrKM64kJRWAv3xosmWyOes/Svf/iVSilIdj0fn2+8det0w2Z/1qFpThrIhIUlKecSUpOiDrGxrt6KuHWSabsxl1a8twWkWkVCkPx6Lz7aTbR1kmm7Phb64ow1kRkaSkPONKUnRAzly2zjLZnB111TDbWd9QhtMqIqVKeTgWlW/1DY122OXuCeDa1ZvKdGZEJAkpz7iSFF0A3u+N/3fOg5PKcEpFJAkpD8ei8q129SbLZHN22OVD9HCbSIVLecaVpOgC8PxHp1gmm7M+I3WPjEilSnk4FpVvw99cYZlszk66fVSZzoqIJCXlGVeSogKysbHRvvD3VyyTzdmUxe+V6bSKSKlSHo5F5Vu/Gje6we8fe71MZ0VEkpLyjCtJUQFZt2azZbI5O+TSF23rjvoynVYRKVXKw7GofLv4yemWyebs9lfmlumsiEhSUp5xJSkqIJ+b7gZJPeXuMWU6pSKShJSHY1H5duo9Yy2TzdkLM5aX6ayISFJSnnElKSogq5+fZZlszq5+blaZTqmIJCHl4VhwvjU2Ng1vNeed4r8jXUTaR8ozriRFFYCn9HZXyIOnLSvTKRWRJKQ8HAvOt3fXb7VMNmcHX5KzbTt1e4tIpUt5xpWk4IDcuqPeDr3sRctkc7Zk9eYynlYRKVXKw7HgfBs3f5Vlsjk74ZZXy3hWRCQpKc+4khQckK/VvmeZbM6Ou+5la2zUGFkilSzl4Vhwvj0yfrG+31ykE0l5xpWk4IDs6w2RcP6jU8p4SkUkCSkPx4Lz7crBMy2TzdkNQ94s41kRkaSkPONKUnBA/vKhyZbJ5uy+0QvLeEpFJAkpD8eC8+0n/SdYJpuzJ6csLeNZEZGkpDzjSlJQQDY0NNox1S9ZJpuz6UvXlvm0ikipUh6OBeVbY2Oj/fe1LyvfRDqRlGdcSQoKyLfe2WCZbM6OuHKo7axvKPNpFZFSpTwcC8q3ZWu3aIB7kU4m5RnH74FaYBswCfhiAa8tKCDvH7PIMtmc/ey+iWU+pSKShC4SjsVmXEH5NnTmO5bJ5uzkO0aX+ayISFK6SMYV5cfAduBc4EigP7AW2C/m6wsKyB/1G2+ZbM4eGLOozKdURJLQBcKxlIwrKN9uGTbHMtmcZZ+aUeazIiJJ6QIZV7RJQO/Az92B5cAlMV8fOyBXrN9qB1+Ss0w2Z0vf0/h/Ip1BFwjHUjKuoALwlLvHWCabsycmLSnzWRGRpHSBjCvK7kA9cGpo+iPAc3leswfuIPntQGIGZCbrir8z+o5rh1MqIkno5OFYaMYVnW93vDJv1zeArNq4rR3OjIgkoZNnXNE+gXvTXw5NvwV31Ryl2ntNsxYnIA++JGdHXzVMT8eJdCKdPBwLzbhqisw3vwCsfl7fby7SmXTyjCtavnC8FZiY5zWRV8h1dXW2fv36VtucJSvsnVXvtbmcmppa5bS6urrOHI6FZlzR+Vb37mqbX/duh58vNTW1wlonz7iiFdMFHHYgEVfMampqXa4dSOdTasYp39TU0tM6Y8aVZBJwd+Dn7sAy4j8E0g130PaO0fwwjbt82puOl45VpRyvA3G/651RKRlXSL7p32F5/w2mvel4lfdYdeaMK5o/RMI5wBHAvbghEvYvw7b2xp2Uvcuw7q5Ixys+HavCpOl4KeMqk45VYXS84tOxKsCFwBJcSE4Cji/TdnRSCqPjFZ+OVWHSdryUcZVHx6owOl7x6VhVIJ2Uwuh4xadjVRgdr/LQcY1Px6owOl7x6VhVoD1wwyzs0cH70VnoeMWnY1UYHa/y0HGNT8eqMDpe8elYiYiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiBTj90AtsA03FtcXO3RvKsM3gBeAt3GPrYe/sqobcC3wDrAVGA4c1p47WEEuBaYAG4GVwGDg06FlPgDcA7wHbAKepjwD/nYGvwPeADZ4bQJwcmC+jlWylG/RlHHxKePiU751Iv5o/OcCRwL9caPx79eRO1UBTgb+DpxGdDhmgXXe9GNw31+6CPePO22GAVXAUcDngBdxg/t+KLBMX2ApcCJwHC4UxrXrXlaO7wPfAQ732vXADtzxAx2rJCnf8lPGxaeMi0/51olMAnoHfu4OLCf+dw6nQTgcu+Guii8OTPsI7hOGs9pxvyrVvrhj9g3v54/gAuCMwDKf8Zb5UvvuWsVaA5yHjlXSlG/xKOMKo4wrjPKtAu0O1NPyyu8R3NWeOOFw/JQ37djQcqOAO9trpyrYobjjc7T384nez/uEllsC9GrH/apE78P9Qd2O+4RKxyo5yrf4lHGFUcbFo3yrYJ/AnYwvh6bfgrtyFiccjl/xpn08tNyTwMD22qkK1R3IAWMD036KC4CwycDN7bFTFeizuPtf6nHdbN/xputYJUf5Fp8yLj5lXNuUb51AvoC8FZjY/rtTseKG4yBgQHvtVIXqi7vh/r8C0/L90k8BbmqHfapEu+M+RfgCcCOwCneFrGOVHOVbfMq4+JRxbVO+dQLqIolH3SPx9AbqgIND0/Wxf9uGA/eiY5Uk5Vt8yrh4lHHFUb5VqEnA3YGfuwPL0E3SQflukP5LYNrepPcG6W64YFxO9DAR/o2/pwemHY5u/A16FXgYHaukKd/iUca1ThlXGuVbhfKHSTgHOAJXpa9F4/Lshbv6PRb3j7OX9/8HefOzuON0Cu5+h8Gkd4iEPrj7PE4ADgi0PQPL9MVd5X0T9+j/eK+l0Q3A14EeuH87NwKNwLe9+TpWyVG+5aeMi08ZF5/yrZO5EHdCtuOumI/v2N2pCD1xoRhuD3vz/UFSV+CuiofjrmTSKOo4GW7cLJ8/+OcaYDPwDC5A0+gB3D1E23GDyg6nKRxBxyppyrdoPVHGxaWMi0/5JiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIh2kBjCv5UpYz+DAemaVvlsikhJVNGWHAR/r0L1J1hk0f2/HduzuiEhXdiRQDfSIuXwNMAc4G+iZZ5l9gO24ADsizzIneOuYgwpAkSRV0byI2AbMA3oD+3fcbiWmCve+LsJlyB7e9L5AI/CfoeX/05u+HfhAaN6nvHVdX+A+7IXLzW9EzPsecFWB6/MdhHtP96MCUETKzL/i7Blz+RqvtebXwFbgHeDvMdanAlAkOVW43+krccXEr4CHgQZgEfDBjtqxhFTh3l+P0PSfedO/H5r+PaAeVwR+LTTv595rTi5wHw7wXndFxLx+3vZK8StUAIqkRjdgzzzzutPyyjUp5SgARwFPA//E/cFpa30qAEWSU4X7nf5CaPo/vOk/ae8dKsKHWplXRXQBeJA3/ebQ9BuBqbjehktC8+7FFcYfKXD/VACKpMyBwAPA27juhMW4bofdvfnVuF/asCpaBlYt7h66k4DXvPVd5M0zXHfNz4DZwE7gVG9ed2+52biunXdxIfYfoW366/8aMNlbdhHwi4j9Creeed4/tF0AfhJ3pX0m8EVvfV9pY30qAEWSU0V0Afhdb/plgWmfAgYBa4AtwERvOV83YDXuYs7XHViHK5z2CUzP4gqfvQLTPgM85a1/Gy7rTsmzvycAfYCVwNoY769HxLylwNjQtNHA3bjsfiE0bxYwI/DzB4DrcAXjemAz7oI22NV7KNG5eQXw74jpwWLwZ966NwEbgDeACyPehwpAkQryCWA5LhBuB34DXAu8SVMIVlNYATgfF4w3euvr6c0zb73v4u4luYCmILgPVxD2915zEy5MJgO7hdb/FrACd3/L74HXccXZUd4ynwLupOkemLO91tp9QjW0XgBmgY00fZq5ALinjfWpABRJThXRBeAfvem/8X7eH5cPG3C3avQCpuMy4oeB1z2HK9x8x3rraaB5sZgDpgR+PgpXKM4G/obLoFER6/f3dzYuDy7E5Uhb769HxLwncIWmf1/g7rjbUX4CnAe8hytqweV2I83z6QBczt8G/Nbb73m4C/TPesvs5b0XwxXPfm4ejbvYHYE7Nv70n3qvO9l7zcvA73C53tvb5zAVgCIV5BHcL3U4VKEpUKoprAA03CeAYX64Hhma/jVv3k9D00+KmO6v/+uBafviwvG2wLSku4DfwF0F+64HVgHvb2V9KgBFklOF+53+H9wTsv8F/Bj3Sd4WXE8GuAtZo/l9cXvhegoW4z7pA7gY9ynWh72f/4DLl0m4C1BwGbiW5p8UDsflwR6Bad2AcbiiKry/Y4D3FfD+ekTM8wsz/z19yfv5INwDaUZTrvqfiAa7xN9PU4+O7z9wGXZvYFoxXcC9cQVo94h5YSoARSpEd1x3wOA2lqumsAIw3/1xBrwaMf1O3BX1vrhgD7aNuE8Hg+ufHbGOGcAzgZ+TLACP8dYV/FTg6Ihp4fWpABRJThXRXZS1NL/gnIsr4sIu8ZY/2vv5eJpfrD4J/At3ITnOm+b/7vuf7PlP3l5By6y6ylvWL0T9/Q3enhLn/fWImPc5b55/r99fgGXe//vd2b/2fr7BW/aTebbT3XsfHwOG0vzTzWIKwL/jem/+N8/2glQAilSI/XG/jG090VpNYQXgiDzrMdz9KmFDiA52vz0XWv/QiHXUACMDPydZAN6C6yI/AnefjN8WA4+3sj4VgCLJqcL9Tl8AfAv3u30ELT952gY8GvH6H9D8om033O+1n39vA+fj7uXzh1bxP3nb11vGv/+3tfb50P4GeyvivL8eEfP8+xP9e/2eAQYG5udwT0SD645eErGOc4GZwI7Q/gY/tSymAPw47rYcA+pwGR/VAwQqAEUqhv/L3lYBeDXRBeB55H8IJIr/EEjYMNx9gd/K0z4XY/01NC/gkioAu+FCLV/Yb6b5zeHB9akAFElOFdH3AIbFLQDB9UiMpukBiCNo+pTvBGAA7r5ln9/1eiv588rvUo67vz5/+R555g+l6V6/d4E/BeZdhrsveTdcd/hjedb9NO7+vZO8fa3xXucr9ingPXCFc1+abtOJuthXAShSIeJ2Afs3We8Tmn4tyRSA9+CCJd9wMUH51l9D8wLudJIpAHt667kSV1QG26+9eWfnWZ8KQJHkVBGvoMrXBZyleRcwwDW4gvF3uPvh/PueZ+KKoOU0v0duP28dNyS4v+Hle+SZf7k33y9kg+vt6U071fvv70KvzeGOS7fQ9Ek0LwD9XqGoArAv8YaB6U7TgM89QvNUAIpUkDgPgfg3FQeHOfgQrpshiQLwBPKH6vtpXnjmW38NzQu4/6MpEOMIv953P+5p5HzjFc4lf5e0CkCR5FQRr6DyHwL5cmDah4CFNH8IBNynYIbrwnw2ML0P7nc76gJvJO6TuI9HbHvfwP/H3d/w8j3yzPdzcjyu5yH4ANoHcffhjfeW+Wzotc/RsgD8Ku6TzmABuJf3+uADdb7bvHnhHo+PRizrf2jw6dB0FYAiFeRA3Ddb+MPAnI/r8p1FU+G1G67YW4UbPuAvuAcxXiOZAhBc94Lh7ge8CHfvzR24K/AzYqy/huYF3AG4q9UJwDnAWbir93zCrwfXrbGW5n8Ywm7zthNedw0qAEWSVEW8gsofBmYdrpfiImAaLYdpAVcY7vTW++fA9LNous0jE3rNkbhhrlbjhrr6Ne4TsxdpPvZe3P0NL98jz/wP0PRVlCMj5vt5vJaW90X6vRXP4jL+Jm+52TQvAMEVistxnyKeRdPTxT/x1vEQbty/H3nTX/D25yrcbUF/xx3712n5iaMKQJEKcxDuk8CVuO6QhbhCLThswH/jBlPdjisGe9H6QNBRWisAwYXUa7h7WPzBRG+m+ZV2vvXX0LKA+xXuvdTTdndw1OtP8173y1Ze51+V/zFifSoARZJTRfyCyh8Iei1uvLxJ5H9if7K33i8Gph3oTVvayvofwV0878A9kfsC7taTYvY3uHyPVpbxP+GL+o5ff+zTFyPmdcMVqUtwx+N13Ph9/6ZlAfg1b75fbPrdwe/D3a6zCldM+93BP8KNAfiu95paXHdx1LirKgBFpOLU4IZ++Biwdwnr+bC3jnGoABSR+Kpoeor4Y7T89Kwz2x33ni5CBaCIVJgamrp88n2CGcfgwHpUAIpIXFU0H2HgYx26N8nyR2XwmwpAEakYxxE97Eyhjgms50sJ7JeIpMPHaT6czG6tL96p7Efz9xY1dJaIiIiIiIiIiIiIiIiIiIiIiIiIiKRAN9x4TXurqal12XYgXWtIjLiUb2pq6WhpzbiS+IN1qqmpde12IOmjfFNTS09LY8aVZG/A6urqbP369QW3devW2YxFb1vutQW2es3aotahpqZWvlZXV+eH494dnDUdoaR8S6qtXbvOFi1faXOWrLA3Fr1tU+cvs4lvLbUxs5fYq28stpenLrLcawvshSkL7Pkp8+25yfNt8KT59uyk+fbMxHn2dKA9M3GePTvJzR88yS37/JT5u17rv+7pifPsqQnz7Knxc23Q+Ln25DjXBo57ywaOe8sGjI1u/ny/PTnOvf6p8XPtqQkt9+G5yc3386kJ8+ypCe41g/JsO6r5y/itaZvR243a5lMR2+yo7Ra1zTzbbc/3Gtxui22Gthvedpzmvy7fe3164jwbNH6uPT5mji15Z5Uyrsz2Bmz9+vVWqBdmLLcv/P0Vy2Rzlsnm7Gf3TbQNW3cUvB4RKZ/169enORyLzrdivLthq/17Yq3dOGSO/fGJqXZm3/H21ZtG2KGXvbgrJ9XU1OK1GXVrY/3epTzjSlJ0QJ5420jLZHN22GVD7PDLh1gmm7P/u2O0rVi/teB1iUh5pDwcy14ANjY22mu179kfHp/aZqF32OVD7Kirhtmx17xk/+/vr9hXbhxhPW8dad/+Z42dfMdo++5do+17d42x7989xk65e4yd0nus/aD3WDv1nrH2w3vcf0+9x007pfdYt8zdbvnv3dX0Gn/50/uMszP6jrMz+463M/u59iOv/fjelu1H/Zq3M/uNtzP7jrcz+o6z0/uMs9P6jNu1H8F9CO7jaRHbTGq7zd534Nic1mdcq+81yfcbfq9R2w1vu8U2gs3bXhLbTGK7wW1HbbOQ99piu31bHt/g+/W3d2a/8faT/hNs/rsblHFlVlRALly50TLZnB1y6Yu2ZtN2e6NunR13nfs08Cs3jrC5K+KdOBEpr5SHY1kLwOFvrrDv3Dm6WZF3Su+xdvVzs6xfzQJ7fvpye632PVu+dovtrG8oyz6IpF3KM64kRQVkv5oFlsnm7Oz7J+6atvS9zfbNW92ngkdfPczGL1id9HkWkQKlPBzLVgBOXLjaPnWp+8Tv8MuH2F8HTbeZy9Ylvh0RaV3KM64kRQXk6X3GWSabs0fGL242fc2m7XaaN++wy4bY4GnLEjzNIlKolIdjWQrAFeu37urx+O2/XrM1m7Ynun4RiS/lGVeSggNy1cZt1uMS192xfO2WFvO37qi33/37tV1dIlc8O9Pe3aD7AkU6QsrDMfECcPvOBvvhPWMtk83ZSbePss3bdya2bhEpXMozriQFB+SouSstk83ZibeNzLtMQ0OjXffC7F1F4KevGGI3DpljazfrSlmkPaU8HBMvAK8cPHPXbS6LV21KbL0iUpyUZ1xJCg7IxyctsUw2Z+c+NLnNZcfNX2WnelfLmWzOjr5qmF3/4pv28uwVtnLDtlLOuYjEkPJwTLQAHD1v5a4sG/7mikTWKSKlSXnGlaTggLxl2JxdXbtxNDY22vA3V9hJt49qMSzCV28aYRc+PtXuH7PIauautGlL19qiVZts9cZttkNPzYmULOXhmGgBeNGAaZbJ5uzSZ95IZH0iUrqUZ1xJCg7IPz0x1TLZnPWtWVDQSWpoaLQhb7xtfxs0w779z5pd9xG21j5zxVA7/vrh9u1/1tgpvd14RP4YQT9/YJKd+9Bk+9UjU+y3/3rNLnjsdbvw8an2xyem2kUDplmvgdPsL09Ot78Omm7Zp2bYJU/PsL8NmmF/HTTdLn5yuv154HTrNXCaXTTAtT89MdX+9IR7/R8ej25/9Ob/KdB6edv688Dp9pcn3br/NmjGrm1mn3LbvfhJN//PA6dbrwHR241qwW35r4na5l8Htdyu/17/Eni/cbcd3ma+7Ua91/Ax7jWg+XaD245qF4WWbev9BrcZfq9R77c9tpnUdsPb7jVwmv3xiamxf/9SHo6JFYDbdtbb0VcNs0w2Z1MWv1fy+kQkGSnPuJIUHJBn9HVP+b4wY3lJJ23D1h02dv4qu3vEPDvv4cl20u2j7Cs3jtgVsmpqavnbOQ9OivV7lvJwTKwAfGX2Cstkc3b89cOtoaGx5PWJSDJSnnElKTggv3TDcMtkczZ1yZqyndCd9Q22dvN2W7J6s81cts7GzV9lr8xeYUNnvmO5GW/b4GnL7OnX62zg5KX22MQl9sj4xfbQ2EX2wJhFdt/ohdZ/1ELrV7PA+oxcYL1fnW93j5hndw6fZ3ePmGe9X51v94ycb31rFli/mgV276gFdt/ohS3a/WMWtWjhZfqPWmj3jnLr6RvYXnCbdw2ft2taeLv9R7W+3aj9itrmPSPnN9tueJt9Ri7Ytd3+oxbualHrz9da266/Tf/45nuvhW4z7vvN916L3W5S24yz3eCy4W1Gbbdvjfv3+tKsd2L9LqU8HBMrAHt53b9XPzer5HWJSHJSnnElKSggd9Q32MFe162GdhGpfCkPx0QKwGD372R1/4pUlJRnXEkKCsil7222TNZ9p6W6QUQqX8rDMZECcMQc1/37xetfUe6JVJiUZ1xJCgrIcQtWWSabs2/eOrK8Z1REEpHycEykAPzzwOnq/hWpUGnNuGrcmw62twpcR0EB+czUOstkc/aT/hPKfEpFJAlpDUdPyQXgjvoG++zVrvt30iJ1/4pUmrRmXDUwCzgg0D5W4DoKCsgHxy6yTDZnFzz2eplPqYgkoYLD8VJgCrARWAkMBj4dWqaGlhe5/QrYRskF4Mxl6yyTzdkx1S+p+1ekAlVwxpVVNTC9xHUUFJC3vzLXMtmcXaaBUEU6hQoOx2FAFXAU8DngRWAJ8KHAMjVAf5pf5BbyPkouAAdMdt989NP71OshUokqOOPKqhrYDLwNLAIeAw5q4zV74A6S3w6kgIC8+rlZlsnm7Oahc8p8SkUkCZ0oHPfF7ec3AtNqgDtKWGfJBaD/3b9/z81O8KyISFI6UcYl6mTgTOAY4CRgPO4K+sOtvKaall0qsQPSHwvr3lGFfQuIiHSMThSOh+L28+jAtBpgFbAad7vLjcAHC1hnyQXg6X3cwPfPTl2W4FkRkaR0oowrq32A9cB5rSxT0ieA5z402TLZnA2YvKTMp1REktBJwrE7kAPGhqafj7u4/SzwM2AZ8Ewr6ykp38IaGhrtyCuHWiabs3krNiR8ZkQkCZ0k49rFFNxVclwFXSH/8J6xlsnmbOjMeN9CICIdq5OEY1+gFvivNpY7EfdeDskzv5oSejjCFq7caJlszg6/fIjtrG9I+MyISBI6ScaV3V7AGuCPBbymoALwxNtGWiabs/ELVpf5lIpIEjpBOPYG6oCDYyz7Idx7OSnP/EQ/AXx++nLLZHN2Su+xCZ8VEUlKJ8i4srgNOAHoAXwFeAV3v8y+BayjoALwuOtetkw2Z2++Xfp3a4pI+VVwOHbDFX/LgcNivuaruPdyTMzlS7oH8KahcyyTzdmlGvVApGJVcMaV1QDcE8DbcffGDCB/10g+sQOysbHRDr3sRctkc/b2ui3tcFpFpFQVHI59gHW4i9jgMC97evMPAa4EjsNd5J4CLARGFbCNkgrAnz8wyTLZnP1rQm3CZ0VEklLBGVfxYgfkpm07LZPNWSabs83bd7bDaRWRUlVwOLa4V89rVd78T+KKvfeAbcB84BbaaRzAxsbGXT0eU5esKcOZEZEkVHDGVbzYAbl87RbLZHN22GVDrLFRI+KLdAYpD8eiC8AV67daJpuzgy/J2Zbt9WU4MyKShJRnXEliB+Rb72ywTDZnn7/25XY4pSKShJSHY9EF4Oh5Ky2Tzdk3bxuZ/EkRkcSkPONKEjsgX1+yxjLZnH3t5hHtcEpFJAkpD8eiC8CHvO89/9UjU8pwVkQkKSnPuJLEDsgx81ZZJpuzk24f1Q6nVESSkPJwLLoA9L8C7sYh+tpLkUqW8owrSeyAHDrzHctkc3Zan3HtcEpFJAkpD8eiC8Cf3jfBMtmcPTllaRnOiogkJeUZV5LYAfn063WWyebs7PsntsMpFZEkpDwciy4Aj79+uGWyOXutVk8Ai1SylGdcSWIH5KMTai2TzdlvHn2tHU6piCQh5eFYVAG4MTDk1drN28t0ZkQkCSnPuJLEDsi+NQssk81Zr4HT2uGUikgSUh6ORRWAb9Sts0w2Z8ddpxEPRCpdyjOuJLED8h8vvWWZbM6uHDyzHU6piCQh5eFYVAH47NRllsnm7My+48t0Vh8slBcAAAmlSURBVEQkKSnPuJLEDshrX5itp+JEOpmUh2NRBaB/sXvJ0zPKdFZEJCkpz7iSxA7I7FMzLJPN2V3D57XDKRWRJKQ8HIsqAC/49+uWyebsvtELy3RWRCQpKc+4ksQOyD88PlWhKNLJpDwciyoA/+cfNZbJ5uzVt94t01kRkaSkPONKEjsgf/XIFMtkc/bYxCXtcEpFJAkpD8eCC8CtO+rt4EvcE8Ar1m8t45kRkSSkPONKEjsgz75/omWyOXtmal07nFIRSULKw7HgAnDmMvcE8LHXvGSNjY1lPDMikoSUZ1xJYgfk6X3GWSabs6Ez326HUyoiSUh5OBZcAD45Zallsjk7694JZTwrIpKUlGdcSWIH5HfuHG2ZbM5G6r4YkU4j5eFYcAF4nTfaQfXzs8p4VkQkKSnPOH4P1ALbgEnAFwt4beyA/OZtIy2TzdnEhavb4ZSKSBK6SDgWm3EFF4A/u8/d6jJwsr4DWKQz6CIZV5QfA9uBc4Ejgf7AWmC/mK+PHZBfvsF9N+aMurXtcEpFJAldIBxLybiCCsCtO+rtiCuHWiabs5nL1pX5zIhIErpAxhVtEtA78HN3YDlwSczXxw7IY695yTLZnM1dsaEdTqmIJKELhGMpGVdQAfjqnHctk83Z8dcPt4YGPQAi0hl0gYwryu5APXBqaPojwHN5XrMH7iD57UBiBuSnrxhimWzOlr63uR1OqYgkoZOHY6EZV3S+DX9zhX3jllctk83Z5c++0Q5nRkSS0MkzrmifwL3pL4em34K7ao5S7b2mWWsrIBsbG62HNzbWuxs0NpZIZ9HJw7HQjKumiHwzM7tz+DzLZHN2+OVD7M23Cxs4WkQ6TifPuKLlC8dbgYl5XlPUFXJDQ6P1q1lgd7wyz7buqG+n0yoiperk4VhoxhX9CeDkxe/ZPSPn2+zlKv5EOpNOnnFFK6YLOGxvwOrq6mz9+vVqampdrNXV1XXmcCw145RvampdvHXyjCvJJODuwM/dgWXEfwjkQCK6TNTU1LpcO5DOqZSMU76pqaWnddaMK5o/RMI5wBHAvbghEvaP+fpuuIO2d4zmh2nc5dPedLx0rCrleB2I+13vjErJuELyTf8Oy/tvMO1Nx6u8x6ozZ1xJLgSW4EJyEnB8mbazN+6k7F2m9Xc1Ol7x6VgVJm3HSxlXeXSsCqPjFZ+OVQXSSSmMjld8OlaF0fEqDx3X+HSsCqPjFZ+OVQXSSSmMjld8OlaF0fEqDx3X+HSsCqPjFZ+OVQXaAzfO1h4dvB+dhY5XfDpWhdHxKg8d1/h0rAqj4xWfjpWIiIiIiIiIiIiIiIiIiIiIiIiISEJ+D9QC23BjcX2xQ/emMnwDeAF4G/fUUvgrq7oB1wLvAFuB4cBh7bmDFeRSYAqwEVgJDAY+HVrmA8A9wHvAJuBp4g9q3tX8DngD2OC1CcDJgfk6VslSvkVTxsWnjItP+daJ+KPxnwscCfTHjca/X0fuVAU4Gfg7cBrR4ZgF1nnTj8F9f+ki3D/utBkGVAFHAZ8DXsQN7vuhwDJ9gaXAicBxuFAY1657WTm+D3wHONxr1wM7cMcPdKySpHzLTxkXnzIuPuVbJzIJ6B34uTuwnPjfOZwG4XDshrsqvjgw7SO4TxjOasf9qlT74o7ZN7yfP4ILgDMCy3zGW+ZL7btrFWsNcB46VklTvsWjjCuMMq4wyrcKtDtQT8srv0dwV3vihMPxU960Y0PLjQLubK+dqmCH4o7P0d7PJ3o/7xNabgnQqx33qxK9D/cHdTvuEyodq+Qo3+JTxhVGGReP8q2CfQJ3Mr4cmn4L7spZnHA4fsWb9vHQck8CA9trpypUdyAHjA1M+ykuAMImAze3x05VoM/i7n+px3WzfcebrmOVHOVbfMq4+JRxbVO+dQL5AvJWYGL7707FihuOg4AB7bVTFaov7ob7/wpMy/dLPwW4qR32qRLtjvsU4QvAjcAq3BWyjlVylG/xKePiU8a1TfnWCaiLJB51j8TTG6gDDg5N18f+bRsO3IuOVZKUb/Ep4+JRxhVH+VahJgF3B37uDixDN0kH5btB+i+BaXuT3huku+GCcTnRw0T4N/6eHph2OLrxN+hV4GF0rJKmfItHGdc6ZVxplG8Vyh8m4RzgCFyVvhaNy7MX7ur3WNw/zl7e/x/kzc/ijtMpuPsdBpPeIRL64O7zOAE4IND2DCzTF3eV903co//jvZZGNwBfB3rg/u3cCDQC3/bm61glR/mWnzIuPmVcfMq3TuZC3AnZjrtiPr5jd6ci9MSFYrg97M33B0ldgbsqHo67kkmjqONkuHGzfP7gn2uAzcAzuABNowdw9xBtxw0qO5ymcAQdq6Qp36L1RBkXlzIuPuWbiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIdKyeRH85dnvwR7NfF3P5noHXDC7TPolI19ET5ZuIpFC+rw/yWzWwO+5rcLp10P5VAfvFXN7f14EoIEXSTvkmIpJH8EvD/wSsD03bq+N2DXABeWoRr3sYBaRI2infRERiqCK6K6InzbtI/OW+B8wFtgBPAR8EzsF9yfZa4C7gfYH17AHcBizHfcH2JG/drYkKyM8BI4GNwAbgdeALoWUeRgEpIk2qUL6JiESqIn5A7gBeBj4PfANYDbyE65o4Ehee24EfB9ZzHzAO+DpwCHAxsA04rJV9igrIWcC/gM94rz0TF5pBD6OAFJEmVSjfREQiVRE/IA0Xcr5+uKveYJfKMG86wEFAPfCJ0LqHAze0sk9RAbkBdyXemodRQIpIkyqUbyIikaqIH5CbQ8tcA8wOTXsEeMb7/+9669gUajtxV9X5RAVktfe64cAlNA9q38MoIEWkSRXKNxGRSFUUdo9MUDUwPTTtYZpC6se4K+RPA4eG2gGt7FO+m6QPB3rhumm2Az9sZdsiIlUo30REIlVRvoA83FvH1wvcpzhPyT0BPN/KtkVEqlC+iYhEqqJ8AQnwb2AxcBpwMPBF4FJc90k+4YDcE+jt7VMG+CqwALi5jW2LSLpVoXwTEYlURXkDcjfcvTSLcU/ZvYO7h+azrexTOCB3x10RL8V1jSwH7gY+0Ma2RSTdqlC+iYh0GhooVUS6KuWbiEgeBmwFlsVc/us0PX2ngBSRSqZ8ExHJw3+S7uCYy+9JvKfvREQ6mvJNRERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERETS5v8DwBsWNoD0QS4AAAAASUVORK5CYII=\" width=\"640\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"labels = ['boat position [m]',\n",
" 'rotor angle [rad]',\n",
" 'prop angle [rad]',\n",
" 'boat speed [m/s]',\n",
" 'rotor rate [rad/s]',\n",
" 'prop rate [rad/s]',\n",
" 'current [A]']\n",
"\n",
"fig, axes = plt.subplots(4, 2)\n",
"\n",
"for lab, ax, y in zip(labels, axes.flatten(), sol.y):\n",
" ax.plot(sol.t, y)\n",
" ax.set_title(lab)\n",
"\n",
"ax.set_xlabel('Time [s]')\n",
"\n",
"# rotor rate * motor torque\n",
"motor_power = sol.y[4] * motor_torque_constant * sol.y[6]\n",
"\n",
"axes[3, -1].plot(sol.t, motor_power)\n",
"axes[3, -1].set_title('Power [Watts]')\n",
"axes[3, -1].set_xlabel('Time [s]')\n",
"\n",
"plt.tight_layout()"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python [default]",
"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.5.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment