PyMeasure-0.5/0000755000175000017500000000000013171765525013536 5ustar colincolin00000000000000PyMeasure-0.5/PKG-INFO0000644000175000017500000002337513171765525014645 0ustar colincolin00000000000000Metadata-Version: 1.1 Name: PyMeasure Version: 0.5 Summary: Scientific measurement library for instruments, experiments, and live-plotting Home-page: https://github.com/ralph-group/pymeasure Author: PyMeasure Developers Author-email: UNKNOWN License: MIT License Download-URL: https://github.com/ralph-group/pymeasure/tarball/v0.5 Description-Content-Type: UNKNOWN Description: .. image:: https://raw.githubusercontent.com/ralph-group/pymeasure/master/docs/images/PyMeasure.png :alt: PyMeasure Scientific package PyMeasure scientific package ############################ PyMeasure makes scientific measurements easy to set up and run. The package contains a repository of instrument classes and a system for running experiment procedures, which provides graphical interfaces for graphing live data and managing queues of experiments. Both parts of the package are independent, and when combined provide all the necessary requirements for advanced measurements with only limited coding. PyMeasure is currently under active development, so please report any issues you experience to our `Issues page`_. .. _Issues page: https://github.com/ralph-group/pymeasure/issues PyMeasure runs on Python 3.4, 3.5, and 3.6, and is tested with continous-integration on Linux, macOS, and Windows. .. image:: https://ci.appveyor.com/api/projects/status/hcj2n2a7l97wfbb8/branch/master?svg=true :target: https://ci.appveyor.com/project/cjermain/pymeasure .. image:: https://travis-ci.org/ralph-group/pymeasure.svg?branch=master :target: https://travis-ci.org/ralph-group/pymeasure .. image:: http://readthedocs.org/projects/pymeasure/badge/?version=latest :target: http://pymeasure.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://zenodo.org/badge/23569/ralph-group/pymeasure.svg :target: https://zenodo.org/badge/latestdoi/23569/ralph-group/pymeasure .. image:: https://anaconda.org/conda-forge/pymeasure/badges/version.svg :target: https://anaconda.org/conda-forge/pymeasure .. image:: https://anaconda.org/conda-forge/pymeasure/badges/downloads.svg :target: https://anaconda.org/conda-forge/pymeasure Quick start =========== Check out `the documentation`_ for the `quick start guide`_, that covers the installation of Python and PyMeasure. There are a number of examples in the `examples`_ directory that can help you get up and running. .. _the documentation: http://pymeasure.readthedocs.org/en/latest/ .. _quick start guide: http://pymeasure.readthedocs.io/en/latest/quick_start.html .. _examples: https://github.com/ralph-group/pymeasure/tree/master/examples Version 0.5 -- released 10/18/17 ================================ - Threads are used by default, eliminating multiprocessing issues with spawn - Enhanced unit tests for threading - Sphinx Doctests are added to the documentation (@bilderbuchi) - Improvements to documentation (@JuMaD) Version 0.4.6 -- released 8/12/17 ================================= - Reverted multiprocessing start method keyword arguments to fix Unix spawn issues (@ndr37) - Fixes to regressions in Results writing (@feinsteinben) - Fixes to TCP support using cloudpickle (@feinsteinben) - Restructing of unit test framework Version 0.4.5 -- released 7/4/17 ================================ - Recorder and Scribe now leverage QueueListener (@feinsteinben) - PrologixAdapter and SerialAdapter now handle Serial objects as adapters (@feinsteinben) - Optional TCP support now uses cloudpickle for serialization (@feinsteinben) - Significant PEP8 review and bug fixes (@feinsteinben) - Includes docs in the code distribution (@ghisvail) - Continuous integration support for Python 3.6 (@feinsteinben) Version 0.4.4 -- released 6/4/17 ================================ - Fix pip install for non-wheel builds - Update to Agilent E4980 (@dvspirito) - Minor fixes for docs, tests, and formatting (@ghisvail, @feinsteinben) Version 0.4.3 -- released 3/30/17 ================================= - Added Agilent E4980, AMI 430, Agilent 34410A, Thorlabs PM100, and Anritsu MS9710C instruments (@TvBMcMaster, @dvspirito, and @mhdg) - Updates to PyVISA support (@minhhaiphys) - Initial work on resource manager (@dvspirito) - Fixes for Prologix adapter that allow read-write delays (@TvBMcMaster) - Fixes for conda environment on continuous integration Version 0.4.2 -- released 8/23/16 ================================= - New instructions for installing with Anaconda and conda-forge package (thanks @melund!) - Bug-fixes to the Keithley 2000, SR830, and Agilent E4408B - Re-introduced the Newport ESP300 motion controller - Major update to the Keithely 2400, 2000 and Yokogawa 7651 to achieve a common interface - New command-string processing hooks for Instrument property functions - Updated LakeShore 331 temperature controller with new features - Updates to the Agilent 8257D signal generator for better feature exposure Version 0.4.1 -- released 7/31/16 ================================= - Critical fix in setup.py for importing instruments (also added to documentation) Version 0.4 -- released 7/29/16 =============================== - Replaced Instrument add_measurement and add_control with measurement and control functions - Added validators to allow Instrument.control to match restricted ranges - Added mapping to Instrument.control to allow more flexible inputs - Conda is now used to set up the Python environment - macOS testing in continuous integration - Major updates to the documentation Version 0.3 -- released 4/8/16 ============================== - Added IPython (Jupyter) notebook support with significant features - Updated set of example scripts and notebooks - New PyMeasure logo released - Removed support for Python <3.4 - Changed multiprocessing to use spawn for compatibility - Significant work on the documentation - Added initial tests for non-instrument code - Continuous integration setup for Linux and Windows Version 0.2 -- released 12/16/15 ================================ - Python 3 compatibility, removed support for Python 2 - Considerable renaming for better PEP8 compliance - Added MIT License - Major restructuring of the package to break it into smaller modules - Major rewrite of display functionality, introducing new Qt objects for easy extensions - Major rewrite of procedure execution, now using a Worker process which takes advantage of multi-core CPUs - Addition of a number of examples - New methods for listening to Procedures, introducing ZMQ for TCP connectivity - Updates to Keithley2400 and VISAAdapter Version 0.1.6 -- released 4/19/15 ================================= - Renamed the package to PyMeasure from Automate to be more descriptive about its purpose - Addition of VectorParameter to allow vectors to be input for Procedures - Minor fixes for the Results and Danfysik8500 Version 0.1.5 -- release 10/22/14 ================================= - New Manager class for handling Procedures in a queue fashion - New Browser that works in tandem with the Manager to display the queue - Bug fixes for Results loading Version 0.1.4 -- released 8/2/14 ================================ - Integrated Results class into display and file writing - Bug fixes for Listener classes - Bug fixes for SR830 Version 0.1.3 -- released 7/20/14 ================================= - Replaced logging system with Python logging package - Added data management (Results) and bug fixes for Procedures and Parameters - Added pandas v0.14 to requirements for data management - Added data listeners, Qt4 and PyQtGraph helpers Version 0.1.2 -- released 7/18/14 ================================= - Bug fixes to LakeShore 425 - Added new Procedure and Parameter classes for generic experiments - Added version number in package Version 0.1.1 -- released 7/16/14 ================================= - Bug fixes to PrologixAdapter, VISAAdapter, Agilent 8722ES, Agilent 8257D, Stanford SR830, Danfysik8500 - Added Tektronix TDS 2000 with basic functionality - Fixed Danfysik communication to handle errors properly Version 0.1.0 -- released 7/15/14 ================================= - Initial release Keywords: measure instrument experiment control automate graph plot Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Scientific/Engineering PyMeasure-0.5/tests/0000755000175000017500000000000013171765525014700 5ustar colincolin00000000000000PyMeasure-0.5/tests/experiment/0000755000175000017500000000000013171765525017060 5ustar colincolin00000000000000PyMeasure-0.5/tests/experiment/test_parameters.py0000644000175000017500000000622113143613470022623 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import pytest from pymeasure.experiment.parameters import Parameter from pymeasure.experiment.parameters import IntegerParameter from pymeasure.experiment.parameters import BooleanParameter from pymeasure.experiment.parameters import FloatParameter def test_parameter_default(): p = Parameter('Test', default=5) assert p.value == 5 def test_integer_units(): p = IntegerParameter('Test', units='V') assert p.units == 'V' def test_integer_value(): p = IntegerParameter('Test') with pytest.raises(ValueError): v = p.value # not set with pytest.raises(ValueError): p.value = 'a' # not an integer p.value = 0.5 # a float assert p.value == 0 p.value = False # a boolean assert p.value == 0 p.value = 10 assert p.value == 10 def test_integer_bounds(): p = IntegerParameter('Test', minimum=0, maximum=10) p.value = 10 assert p.value == 10 with pytest.raises(ValueError): p.value = 100 # above maximum with pytest.raises(ValueError): p.value = -100 # below minimum def test_boolean_value(): p = BooleanParameter('Test') with pytest.raises(ValueError): v = p.value # not set p.value = 'a' # a string assert p.value == True p.value = 10 # a number assert p.value == True p.value = 0 # zero assert p.value == False p.value = True assert p.value == True def test_float_value(): p = FloatParameter('Test') with pytest.raises(ValueError): v = p.value # not set with pytest.raises(ValueError): p.value = 'a' # not a float p.value = False # boolean assert p.value == 0.0 p.value = 100 assert p.value == 100.0 def test_float_bounds(): p = FloatParameter('Test', minimum=0.1, maximum=0.5) p.value = 0.3 assert p.value == 0.3 with pytest.raises(ValueError): p.value = 10 # above maximum with pytest.raises(ValueError): p.value = -10 # below minimum # TODO: Add tests for VectorParameter, ListParamter, and Measurable PyMeasure-0.5/tests/experiment/test_results.py0000644000175000017500000000523313143613470022163 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import os import tempfile import pickle from importlib.machinery import SourceFileLoader from pymeasure.experiment.results import Results, CSVFormatter # Load the procedure, without it being in a module #data_path = os.path.join(os.path.dirname(__file__), 'data/procedure_for_testing.py') #RandomProcedure = SourceFileLoader('procedure', data_path).load_module().RandomProcedure from data.procedure_for_testing import RandomProcedure def test_procedure(): """ Ensure that the loaded test procedure is properly functioning """ p = RandomProcedure() assert p.iterations == 100 assert hasattr(p, 'execute') def test_csv_formatter_format_header(): """Tests CSVFormatter.format_header() method.""" columns = ['t', 'x', 'y', 'z', 'V'] formatter = CSVFormatter(columns=columns) assert formatter.format_header() == 't,x,y,z,V' def test_csv_formatter_format(): """Tests CSVFormatter.format() method.""" columns = ['t', 'x', 'y', 'z', 'V'] formatter = CSVFormatter(columns=columns) data = {'t': 1, 'y': 2, 'z': 3.0, 'x': -1, 'V': 'abc'} assert formatter.format(data) == '1,-1,2,3.0,abc' def test_procedure_wrapper(): assert RandomProcedure.iterations.value == 100 procedure = RandomProcedure() procedure.iterations = 101 file = tempfile.mktemp() results = Results(procedure, file) new_results = pickle.loads(pickle.dumps(results)) assert hasattr(new_results, 'procedure') assert new_results.procedure.iterations == 101 assert RandomProcedure.iterations.value == 100PyMeasure-0.5/tests/experiment/test_workers.py0000644000175000017500000000512213171754361022161 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import pytest import os import tempfile from time import sleep from importlib.machinery import SourceFileLoader from pymeasure.experiment.workers import Worker from pymeasure.experiment.results import Results # Load the procedure, without it being in a module data_path = os.path.join(os.path.dirname(__file__), 'data/procedure_for_testing.py') RandomProcedure = SourceFileLoader('procedure', data_path).load_module().RandomProcedure #from data.procedure_for_testing import RandomProcedure slow = pytest.mark.skipif( not pytest.config.getoption("--runslow"), reason="need --runslow option to run" ) def test_procedure(): """ Ensure that the loaded test procedure is properly functioning """ procedure = RandomProcedure() assert procedure.iterations == 100 assert procedure.delay == 0.001 assert hasattr(procedure, 'execute') def test_worker_stop(): procedure = RandomProcedure() file = tempfile.mktemp() results = Results(procedure, file) worker = Worker(results) worker.start() worker.stop() assert worker.should_stop() worker.join() def test_worker_finish(): procedure = RandomProcedure() procedure.iterations = 100 procedure.delay = 0.001 file = tempfile.mktemp() results = Results(procedure, file) worker = Worker(results) worker.start() worker.join(timeout=5) new_results = Results.load(file, procedure_class=RandomProcedure) assert new_results.data.shape == (100, 2)PyMeasure-0.5/tests/experiment/data/0000755000175000017500000000000013171765525017771 5ustar colincolin00000000000000PyMeasure-0.5/tests/experiment/data/procedure_for_testing.py0000644000175000017500000000370513143613470024731 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.experiment import ( Procedure, IntegerParameter, Parameter, FloatParameter ) import random from time import sleep class RandomProcedure(Procedure): iterations = IntegerParameter('Loop Iterations', default=100) delay = FloatParameter('Delay Time', units='s', default=0.001) seed = Parameter('Random Seed', default='12345') DATA_COLUMNS = ['Iteration', 'Random Number'] def startup(self): random.seed(self.seed) def execute(self): for i in range(self.iterations): data = { 'Iteration': i, 'Random Number': random.random() } self.emit('results', data) self.emit('progress', 100.*i/self.iterations) sleep(self.delay) if self.should_stop(): break PyMeasure-0.5/tests/experiment/data/__init__.py0000644000175000017500000000000013143613470022056 0ustar colincolin00000000000000PyMeasure-0.5/tests/experiment/data/results_for_testing.csv0000644000175000017500000000000013143613470024566 0ustar colincolin00000000000000PyMeasure-0.5/tests/experiment/test_procedure.py0000644000175000017500000000405713143613470022455 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import pytest import pickle from pymeasure.experiment.procedure import Procedure, ProcedureWrapper from pymeasure.experiment.parameters import Parameter from data.procedure_for_testing import RandomProcedure def test_parameters(): class TestProcedure(Procedure): x = Parameter('X', default=5) p = TestProcedure() assert p.x == 5 p.x = 10 assert p.x == 10 assert p.parameters_are_set() objs = p.parameter_objects() assert 'x' in objs assert objs['x'].value == p.x # TODO: Add tests for measureables def test_procedure_wrapper(): assert RandomProcedure.iterations.value == 100 procedure = RandomProcedure() procedure.iterations = 101 wrapper = ProcedureWrapper(procedure) new_wrapper = pickle.loads(pickle.dumps(wrapper)) assert hasattr(new_wrapper, 'procedure') assert new_wrapper.procedure.iterations == 101 assert RandomProcedure.iterations.value == 100PyMeasure-0.5/tests/experiment/test_listeners.py0000644000175000017500000000274613171754361022506 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import time from queue import Queue from pymeasure.experiment.listeners import Listener, Recorder from pymeasure.experiment.results import Results # TODO: Make results_for_testing.csv # TODO: Make procedure_for_testing.py """ def test_recorder_stop(): q = Queue() d = Results.load('results_for_testing.csv') r = Recorder(d, q) r. """ PyMeasure-0.5/tests/instruments/0000755000175000017500000000000013171765525017273 5ustar colincolin00000000000000PyMeasure-0.5/tests/instruments/test_validators.py0000644000175000017500000000531413143613470023045 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import pytest from pymeasure.instruments.validators import ( strict_range, strict_discrete_set, truncated_range, truncated_discrete_set, joined_validators ) def test_strict_range(): assert strict_range(5, range(10)) == 5 assert strict_range(5.1, range(10)) == 5.1 with pytest.raises(ValueError) as e_info: strict_range(20, range(10)) def test_strict_discrete_set(): assert strict_discrete_set(5, range(10)) == 5 with pytest.raises(ValueError) as e_info: strict_discrete_set(5.1, range(10)) with pytest.raises(ValueError) as e_info: strict_discrete_set(20, range(10)) def test_truncated_range(): assert truncated_range(5, range(10)) == 5 assert truncated_range(5.1, range(10)) == 5.1 assert truncated_range(-10, range(10)) == 0 assert truncated_range(20, range(10)) == 9 def test_truncated_discrete_set(): assert truncated_discrete_set(5, range(10)) == 5 assert truncated_discrete_set(5.1, range(10)) == 6 assert truncated_discrete_set(11, range(10)) == 9 assert truncated_discrete_set(-10, range(10)) == 0 def test_joined_validators(): tst_validator = joined_validators(strict_discrete_set, strict_range) assert tst_validator(5, [["ON", "OFF"], range(10)]) == 5 assert tst_validator(5.1, [["ON", "OFF"], range(10)]) == 5.1 assert tst_validator("ON", [["ON", "OFF"], range(10)]) == "ON" with pytest.raises(ValueError) as e_info: tst_validator("OUT", [["ON", "OFF"], range(10)]) with pytest.raises(ValueError) as e_info: tst_validator(20, [["ON", "OFF"], range(10)]) PyMeasure-0.5/tests/instruments/test_instrument.py0000644000175000017500000001047313143613470023107 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import pytest from pymeasure.instruments.instrument import Instrument, FakeInstrument from pymeasure.instruments.validators import strict_discrete_set, strict_range def test_fake_instrument(): fake = FakeInstrument() fake.write("Testing") assert fake.read() == "Testing" assert fake.read() == "" assert fake.values("5") == [5] def test_control_doc(): doc = """ X property """ class Fake(Instrument): x = Instrument.control( "", "%d", doc ) assert Fake.x.__doc__ == doc def test_control_validator(): class Fake(FakeInstrument): x = Instrument.control( "", "%d", "", validator=strict_discrete_set, values=range(10), ) fake = Fake() fake.x = 5 assert fake.read() == '5' fake.x = 5 assert fake.x == 5 with pytest.raises(ValueError) as e_info: fake.x = 20 def test_control_validator_map(): class Fake(FakeInstrument): x = Instrument.control( "", "%d", "", validator=strict_discrete_set, values=[4, 5, 6, 7], map_values=True, ) fake = Fake() fake.x = 5 assert fake.read() == '1' fake.x = 5 assert fake.x == 5 with pytest.raises(ValueError) as e_info: fake.x = 20 def test_control_dict_map(): class Fake(FakeInstrument): x = Instrument.control( "", "%d", "", validator=strict_discrete_set, values={5: 1, 10: 2, 20: 3}, map_values=True, ) fake = Fake() fake.x = 5 assert fake.read() == '1' fake.x = 5 assert fake.x == 5 fake.x = 20 assert fake.read() == '3' def test_control_dict_str_map(): class Fake(FakeInstrument): x = Instrument.control( "", "%d", "", validator=strict_discrete_set, values={'X': 1, 'Y': 2, 'Z': 3}, map_values=True, ) fake = Fake() fake.x = 'X' assert fake.read() == '1' fake.x = 'Y' assert fake.x == 'Y' fake.x = 'Z' assert fake.read() == '3' def test_control_process(): class Fake(FakeInstrument): x = Instrument.control( "", "%d", "", validator=strict_range, values=[5e-3, 120e-3], get_process=lambda v: v * 1e-3, set_process=lambda v: v * 1e3, ) fake = Fake() fake.x = 10e-3 assert fake.read() == '10' fake.x = 30e-3 assert fake.x == 30e-3 def test_control_get_process(): class Fake(FakeInstrument): x = Instrument.control( "", "JUNK%d", "", validator=strict_range, values=[0, 10], get_process=lambda v: int(v.replace('JUNK', '')), ) fake = Fake() fake.x = 5 assert fake.read() == 'JUNK5' fake.x = 5 assert fake.x == 5 def test_measurement_dict_str_map(): class Fake(FakeInstrument): x = Instrument.measurement( "", "", values={'X': 1, 'Y': 2, 'Z': 3}, map_values=True, ) fake = Fake() fake.write('1') assert fake.x == 'X' fake.write('2') assert fake.x == 'Y' fake.write('3') assert fake.x == 'Z' PyMeasure-0.5/tests/conftest.py0000644000175000017500000000242613143613470017071 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import pytest def pytest_addoption(parser): parser.addoption("--runslow", action="store_true", help="run slow tests") PyMeasure-0.5/tests/test_log.py0000644000175000017500000000317413143613470017065 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import time from pymeasure.process import context from pymeasure.log import Scribe # TODO: Add tests for logging convenience functions and TopicQueueHandler def test_scribe_stop(): q = context.Queue() s = Scribe(q) s.start() assert s.is_alive() is True s.stop() assert s.is_alive() is False def test_scribe_finish(): q = context.Queue() s = Scribe(q) s.start() assert s.is_alive() is True q.put(None) time.sleep(0.1) assert s.is_alive() is False PyMeasure-0.5/tests/test_thread.py0000644000175000017500000000265113143613470017552 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.thread import StoppableThread def test_thread_stopping(): t = StoppableThread() t.start() t.stop() assert t.should_stop() is True t.join() def test_thread_joining(): t = StoppableThread() t.start() t.join() assert t.should_stop() is True PyMeasure-0.5/tests/adapters/0000755000175000017500000000000013171765525016503 5ustar colincolin00000000000000PyMeasure-0.5/tests/adapters/test_adapter.py0000644000175000017500000000311113143613470021516 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from pymeasure.adapters import FakeAdapter log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) def test_adapter_values(): a = FakeAdapter() assert a.values("5,6,7") == [5, 6, 7] assert a.values("5,6,7", cast=str) == ['5', '6', '7'] assert a.values("X,Y,Z") == ['X', 'Y', 'Z'] assert a.values("X,Y,Z", cast=str) == ['X', 'Y', 'Z'] assert a.values("X.Y.Z", separator='.') == ['X', 'Y', 'Z'] PyMeasure-0.5/tests/test_process.py0000644000175000017500000000274613143613470017766 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.process import StoppableProcess def test_process_stopping(): process = StoppableProcess() process.start() process.stop() assert process.should_stop() is True process.join() def test__process_joining(): process = StoppableProcess() process.start() process.join() assert process.should_stop() is True PyMeasure-0.5/PyMeasure.egg-info/0000755000175000017500000000000013171765525017142 5ustar colincolin00000000000000PyMeasure-0.5/PyMeasure.egg-info/PKG-INFO0000644000175000017500000002337513171765525020251 0ustar colincolin00000000000000Metadata-Version: 1.1 Name: PyMeasure Version: 0.5 Summary: Scientific measurement library for instruments, experiments, and live-plotting Home-page: https://github.com/ralph-group/pymeasure Author: PyMeasure Developers Author-email: UNKNOWN License: MIT License Download-URL: https://github.com/ralph-group/pymeasure/tarball/v0.5 Description-Content-Type: UNKNOWN Description: .. image:: https://raw.githubusercontent.com/ralph-group/pymeasure/master/docs/images/PyMeasure.png :alt: PyMeasure Scientific package PyMeasure scientific package ############################ PyMeasure makes scientific measurements easy to set up and run. The package contains a repository of instrument classes and a system for running experiment procedures, which provides graphical interfaces for graphing live data and managing queues of experiments. Both parts of the package are independent, and when combined provide all the necessary requirements for advanced measurements with only limited coding. PyMeasure is currently under active development, so please report any issues you experience to our `Issues page`_. .. _Issues page: https://github.com/ralph-group/pymeasure/issues PyMeasure runs on Python 3.4, 3.5, and 3.6, and is tested with continous-integration on Linux, macOS, and Windows. .. image:: https://ci.appveyor.com/api/projects/status/hcj2n2a7l97wfbb8/branch/master?svg=true :target: https://ci.appveyor.com/project/cjermain/pymeasure .. image:: https://travis-ci.org/ralph-group/pymeasure.svg?branch=master :target: https://travis-ci.org/ralph-group/pymeasure .. image:: http://readthedocs.org/projects/pymeasure/badge/?version=latest :target: http://pymeasure.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://zenodo.org/badge/23569/ralph-group/pymeasure.svg :target: https://zenodo.org/badge/latestdoi/23569/ralph-group/pymeasure .. image:: https://anaconda.org/conda-forge/pymeasure/badges/version.svg :target: https://anaconda.org/conda-forge/pymeasure .. image:: https://anaconda.org/conda-forge/pymeasure/badges/downloads.svg :target: https://anaconda.org/conda-forge/pymeasure Quick start =========== Check out `the documentation`_ for the `quick start guide`_, that covers the installation of Python and PyMeasure. There are a number of examples in the `examples`_ directory that can help you get up and running. .. _the documentation: http://pymeasure.readthedocs.org/en/latest/ .. _quick start guide: http://pymeasure.readthedocs.io/en/latest/quick_start.html .. _examples: https://github.com/ralph-group/pymeasure/tree/master/examples Version 0.5 -- released 10/18/17 ================================ - Threads are used by default, eliminating multiprocessing issues with spawn - Enhanced unit tests for threading - Sphinx Doctests are added to the documentation (@bilderbuchi) - Improvements to documentation (@JuMaD) Version 0.4.6 -- released 8/12/17 ================================= - Reverted multiprocessing start method keyword arguments to fix Unix spawn issues (@ndr37) - Fixes to regressions in Results writing (@feinsteinben) - Fixes to TCP support using cloudpickle (@feinsteinben) - Restructing of unit test framework Version 0.4.5 -- released 7/4/17 ================================ - Recorder and Scribe now leverage QueueListener (@feinsteinben) - PrologixAdapter and SerialAdapter now handle Serial objects as adapters (@feinsteinben) - Optional TCP support now uses cloudpickle for serialization (@feinsteinben) - Significant PEP8 review and bug fixes (@feinsteinben) - Includes docs in the code distribution (@ghisvail) - Continuous integration support for Python 3.6 (@feinsteinben) Version 0.4.4 -- released 6/4/17 ================================ - Fix pip install for non-wheel builds - Update to Agilent E4980 (@dvspirito) - Minor fixes for docs, tests, and formatting (@ghisvail, @feinsteinben) Version 0.4.3 -- released 3/30/17 ================================= - Added Agilent E4980, AMI 430, Agilent 34410A, Thorlabs PM100, and Anritsu MS9710C instruments (@TvBMcMaster, @dvspirito, and @mhdg) - Updates to PyVISA support (@minhhaiphys) - Initial work on resource manager (@dvspirito) - Fixes for Prologix adapter that allow read-write delays (@TvBMcMaster) - Fixes for conda environment on continuous integration Version 0.4.2 -- released 8/23/16 ================================= - New instructions for installing with Anaconda and conda-forge package (thanks @melund!) - Bug-fixes to the Keithley 2000, SR830, and Agilent E4408B - Re-introduced the Newport ESP300 motion controller - Major update to the Keithely 2400, 2000 and Yokogawa 7651 to achieve a common interface - New command-string processing hooks for Instrument property functions - Updated LakeShore 331 temperature controller with new features - Updates to the Agilent 8257D signal generator for better feature exposure Version 0.4.1 -- released 7/31/16 ================================= - Critical fix in setup.py for importing instruments (also added to documentation) Version 0.4 -- released 7/29/16 =============================== - Replaced Instrument add_measurement and add_control with measurement and control functions - Added validators to allow Instrument.control to match restricted ranges - Added mapping to Instrument.control to allow more flexible inputs - Conda is now used to set up the Python environment - macOS testing in continuous integration - Major updates to the documentation Version 0.3 -- released 4/8/16 ============================== - Added IPython (Jupyter) notebook support with significant features - Updated set of example scripts and notebooks - New PyMeasure logo released - Removed support for Python <3.4 - Changed multiprocessing to use spawn for compatibility - Significant work on the documentation - Added initial tests for non-instrument code - Continuous integration setup for Linux and Windows Version 0.2 -- released 12/16/15 ================================ - Python 3 compatibility, removed support for Python 2 - Considerable renaming for better PEP8 compliance - Added MIT License - Major restructuring of the package to break it into smaller modules - Major rewrite of display functionality, introducing new Qt objects for easy extensions - Major rewrite of procedure execution, now using a Worker process which takes advantage of multi-core CPUs - Addition of a number of examples - New methods for listening to Procedures, introducing ZMQ for TCP connectivity - Updates to Keithley2400 and VISAAdapter Version 0.1.6 -- released 4/19/15 ================================= - Renamed the package to PyMeasure from Automate to be more descriptive about its purpose - Addition of VectorParameter to allow vectors to be input for Procedures - Minor fixes for the Results and Danfysik8500 Version 0.1.5 -- release 10/22/14 ================================= - New Manager class for handling Procedures in a queue fashion - New Browser that works in tandem with the Manager to display the queue - Bug fixes for Results loading Version 0.1.4 -- released 8/2/14 ================================ - Integrated Results class into display and file writing - Bug fixes for Listener classes - Bug fixes for SR830 Version 0.1.3 -- released 7/20/14 ================================= - Replaced logging system with Python logging package - Added data management (Results) and bug fixes for Procedures and Parameters - Added pandas v0.14 to requirements for data management - Added data listeners, Qt4 and PyQtGraph helpers Version 0.1.2 -- released 7/18/14 ================================= - Bug fixes to LakeShore 425 - Added new Procedure and Parameter classes for generic experiments - Added version number in package Version 0.1.1 -- released 7/16/14 ================================= - Bug fixes to PrologixAdapter, VISAAdapter, Agilent 8722ES, Agilent 8257D, Stanford SR830, Danfysik8500 - Added Tektronix TDS 2000 with basic functionality - Fixed Danfysik communication to handle errors properly Version 0.1.0 -- released 7/15/14 ================================= - Initial release Keywords: measure instrument experiment control automate graph plot Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Scientific/Engineering PyMeasure-0.5/PyMeasure.egg-info/dependency_links.txt0000644000175000017500000000000113171765525023210 0ustar colincolin00000000000000 PyMeasure-0.5/PyMeasure.egg-info/SOURCES.txt0000644000175000017500000001517713171765525021041 0ustar colincolin00000000000000AUTHORS.txt CHANGES.txt LICENSE.txt MANIFEST.in README.rst setup.cfg setup.py PyMeasure.egg-info/PKG-INFO PyMeasure.egg-info/SOURCES.txt PyMeasure.egg-info/dependency_links.txt PyMeasure.egg-info/requires.txt PyMeasure.egg-info/top_level.txt docs/Makefile docs/conf.py docs/index.rst docs/introduction.rst docs/make.bat docs/quick_start.rst docs/about/authors.rst docs/about/license.rst docs/api/adapters.rst docs/api/display/Qt.rst docs/api/display/browser.rst docs/api/display/curves.rst docs/api/display/index.rst docs/api/display/inputs.rst docs/api/display/listeners.rst docs/api/display/log.rst docs/api/display/manager.rst docs/api/display/plotter.rst docs/api/display/thread.rst docs/api/display/widgets.rst docs/api/display/windows.rst docs/api/experiment/experiment.rst docs/api/experiment/index.rst docs/api/experiment/listeners.rst docs/api/experiment/parameters.rst docs/api/experiment/procedure.rst docs/api/experiment/results.rst docs/api/experiment/workers.rst docs/api/instruments/comedi.rst docs/api/instruments/index.rst docs/api/instruments/instruments.rst docs/api/instruments/resources.rst docs/api/instruments/validators.rst docs/api/instruments/agilent/agilent34410A.rst docs/api/instruments/agilent/agilent8257D.rst docs/api/instruments/agilent/agilent8722ES.rst docs/api/instruments/agilent/agilentE4408B.rst docs/api/instruments/agilent/agilentE4980.rst docs/api/instruments/agilent/index.rst docs/api/instruments/ami/ami430.rst docs/api/instruments/ami/index.rst docs/api/instruments/anritsu/anritsuMG3692C.rst docs/api/instruments/anritsu/anritsuMS9710C.rst docs/api/instruments/anritsu/index.rst docs/api/instruments/danfysik/adapters.rst docs/api/instruments/danfysik/danfysik8500.rst docs/api/instruments/danfysik/index.rst docs/api/instruments/fwbell/fwbell5080.rst docs/api/instruments/fwbell/index.rst docs/api/instruments/hp/hp33120A.rst docs/api/instruments/hp/index.rst docs/api/instruments/keithley/index.rst docs/api/instruments/keithley/keithley2000.rst docs/api/instruments/keithley/keithley2400.rst docs/api/instruments/lakeshore/adapters.rst docs/api/instruments/lakeshore/index.rst docs/api/instruments/lakeshore/lakeshore331.rst docs/api/instruments/lakeshore/lakeshore425.rst docs/api/instruments/newport/esp300.rst docs/api/instruments/newport/index.rst docs/api/instruments/parker/index.rst docs/api/instruments/parker/parkerGV6.rst docs/api/instruments/signalrecovery/dsp7265.rst docs/api/instruments/signalrecovery/index.rst docs/api/instruments/srs/index.rst docs/api/instruments/srs/sr830.rst docs/api/instruments/tektronix/index.rst docs/api/instruments/tektronix/tds2000.rst docs/api/instruments/thorlabs/index.rst docs/api/instruments/thorlabs/thorlabspm100usb.rst docs/api/instruments/yokogawa/index.rst docs/api/instruments/yokogawa/yokogawa7651.rst docs/dev/adding_instruments.rst docs/dev/coding_standards.rst docs/dev/contribute.rst docs/dev/reporting_errors.rst docs/images/PyMeasure logo.png docs/images/PyMeasure logo.svg docs/images/PyMeasure.png docs/images/PyMeasure.svg docs/tutorial/connecting.rst docs/tutorial/graphical.rst docs/tutorial/index.rst docs/tutorial/procedure.rst docs/tutorial/pymeasure-managedwindow-queued.png docs/tutorial/pymeasure-managedwindow-resume.png docs/tutorial/pymeasure-managedwindow-running.png docs/tutorial/pymeasure-managedwindow.png docs/tutorial/pymeasure-plotter.png pymeasure/__init__.py pymeasure/console.py pymeasure/errors.py pymeasure/log.py pymeasure/process.py pymeasure/thread.py pymeasure/adapters/__init__.py pymeasure/adapters/adapter.py pymeasure/adapters/prologix.py pymeasure/adapters/serial.py pymeasure/adapters/visa.py pymeasure/display/Qt.py pymeasure/display/__init__.py pymeasure/display/browser.py pymeasure/display/curves.py pymeasure/display/inputs.py pymeasure/display/listeners.py pymeasure/display/log.py pymeasure/display/manager.py pymeasure/display/plotter.py pymeasure/display/thread.py pymeasure/display/widgets.py pymeasure/display/windows.py pymeasure/experiment/__init__.py pymeasure/experiment/config.py pymeasure/experiment/experiment.py pymeasure/experiment/listeners.py pymeasure/experiment/parameters.py pymeasure/experiment/procedure.py pymeasure/experiment/results.py pymeasure/experiment/workers.py pymeasure/instruments/__init__.py pymeasure/instruments/comedi.py pymeasure/instruments/instrument.py pymeasure/instruments/mock.py pymeasure/instruments/resources.py pymeasure/instruments/validators.py pymeasure/instruments/agilent/__init__.py pymeasure/instruments/agilent/agilent34410A.py pymeasure/instruments/agilent/agilent8257D.py pymeasure/instruments/agilent/agilent8722ES.py pymeasure/instruments/agilent/agilentE4408B.py pymeasure/instruments/agilent/agilentE4980.py pymeasure/instruments/ami/__init__.py pymeasure/instruments/ami/ami430.py pymeasure/instruments/anritsu/__init__.py pymeasure/instruments/anritsu/anritsuMG3692C.py pymeasure/instruments/anritsu/anritsuMS9710C.py pymeasure/instruments/danfysik/__init__.py pymeasure/instruments/danfysik/adapters.py pymeasure/instruments/danfysik/danfysik8500.py pymeasure/instruments/fwbell/__init__.py pymeasure/instruments/fwbell/fwbell5080.py pymeasure/instruments/hp/__init__.py pymeasure/instruments/hp/hp33120A.py pymeasure/instruments/keithley/__init__.py pymeasure/instruments/keithley/buffer.py pymeasure/instruments/keithley/keithley2000.py pymeasure/instruments/keithley/keithley2400.py pymeasure/instruments/lakeshore/__init__.py pymeasure/instruments/lakeshore/adapters.py pymeasure/instruments/lakeshore/lakeshore331.py pymeasure/instruments/lakeshore/lakeshore425.py pymeasure/instruments/newport/__init__.py pymeasure/instruments/newport/esp300.py pymeasure/instruments/ni/__init__.py pymeasure/instruments/ni/daqmx.py pymeasure/instruments/ni/nidaq.py pymeasure/instruments/parker/__init__.py pymeasure/instruments/parker/parkerGV6.py pymeasure/instruments/signalrecovery/__init__.py pymeasure/instruments/signalrecovery/dsp7265.py pymeasure/instruments/srs/__init__.py pymeasure/instruments/srs/sr830.py pymeasure/instruments/tektronix/__init__.py pymeasure/instruments/tektronix/tds2000.py pymeasure/instruments/thorlabs/__init__.py pymeasure/instruments/thorlabs/thorlabspm100usb.py pymeasure/instruments/yokogawa/__init__.py pymeasure/instruments/yokogawa/yokogawa7651.py tests/conftest.py tests/test_log.py tests/test_process.py tests/test_thread.py tests/adapters/test_adapter.py tests/experiment/test_listeners.py tests/experiment/test_parameters.py tests/experiment/test_procedure.py tests/experiment/test_results.py tests/experiment/test_workers.py tests/experiment/data/__init__.py tests/experiment/data/procedure_for_testing.py tests/experiment/data/results_for_testing.csv tests/instruments/test_instrument.py tests/instruments/test_validators.pyPyMeasure-0.5/PyMeasure.egg-info/top_level.txt0000644000175000017500000000001213171765525021665 0ustar colincolin00000000000000pymeasure PyMeasure-0.5/PyMeasure.egg-info/requires.txt0000644000175000017500000000021413171765525021537 0ustar colincolin00000000000000numpy>=1.6.1 pandas>=0.14 pyvisa>=1.8 pyserial>=2.7 pyqtgraph>=0.9.10 [matplotlib] matplotlib>=2.0.2 [tcp] zmq>=16.0.2 cloudpickle>=0.3.1 PyMeasure-0.5/pymeasure/0000755000175000017500000000000013171765525015550 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/experiment/0000755000175000017500000000000013171765525017730 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/experiment/workers.py0000644000175000017500000001430613171754361021776 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import sys import logging import time import traceback from logging.handlers import QueueHandler from importlib.machinery import SourceFileLoader from queue import Queue from .listeners import Recorder from .procedure import Procedure, ProcedureWrapper from .results import Results from ..log import TopicQueueHandler from ..thread import StoppableThread log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) try: import zmq import cloudpickle except ImportError: zmq = None cloudpickle = None log.warning("ZMQ and cloudpickle are required for TCP communication") class Worker(StoppableThread): """ Worker runs the procedure and emits information about the procedure and its status over a ZMQ TCP port. In a child thread, a Recorder is run to write the results to """ def __init__(self, results, log_queue=None, log_level=logging.INFO, port=None): """ Constructs a Worker to perform the Procedure defined in the file at the filepath """ super().__init__() self.port = port if not isinstance(results, Results): raise ValueError("Invalid Results object during Worker construction") self.results = results self.results.procedure.check_parameters() self.results.procedure.status = Procedure.QUEUED self.recorder = None self.recorder_queue = Queue() self.monitor_queue = Queue() if log_queue is None: log_queue = Queue() self.log_queue = log_queue self.log_level = log_level self.context = None self.publisher = None def join(self, timeout=0): try: super().join(timeout) except (KeyboardInterrupt, SystemExit): log.warning("User stopped Worker join prematurely") self.stop() super().join(0) def emit(self, topic, record): """ Emits data of some topic over TCP """ log.debug("Emitting message: %s %s", topic, record) try: self.publisher.send_serialized((topic, record), serialize=cloudpickle.dumps) except (NameError, AttributeError): pass # No dumps defined if topic == 'results': self.recorder.handle(record) elif topic == 'status' or topic == 'progress': self.monitor_queue.put((topic, record)) def handle_abort(self): log.exception("User stopped Worker execution prematurely") self.update_status(Procedure.ABORTED) def handle_error(self): log.exception("Worker caught an error on %r", self.procedure) traceback_str = traceback.format_exc() self.emit('error', traceback_str) self.update_status(Procedure.FAILED) def update_status(self, status): self.procedure.status = status self.emit('status', status) def shutdown(self): self.procedure.shutdown() if self.should_stop() and self.procedure.status == Procedure.RUNNING: self.update_status(Procedure.ABORTED) elif self.procedure.status == Procedure.RUNNING: self.update_status(Procedure.FINISHED) self.emit('progress', 100.) self.recorder.enqueue_sentinel() self.monitor_queue.put(None) def run(self): global log log = logging.getLogger() log.setLevel(self.log_level) # log.handlers = [] # Remove all other handlers # log.addHandler(TopicQueueHandler(self.monitor_queue)) # log.addHandler(QueueHandler(self.log_queue)) log.info("Worker thread started") self.procedure = self.results.procedure self.recorder = Recorder(self.results, self.recorder_queue) self.recorder.start() #locals()[self.procedures_file] = __import__(self.procedures_file) # route Procedure methods & log self.procedure.should_stop = self.should_stop self.procedure.emit = self.emit if self.port is not None and zmq is not None: try: self.context = zmq.Context() log.debug("Worker ZMQ Context: %r" % self.context) self.publisher = self.context.socket(zmq.PUB) self.publisher.bind('tcp://*:%d' % self.port) log.info("Worker connected to tcp://*:%d" % self.port) time.sleep(0.01) except Exception: log.exception("couldn't connect to ZMQ context") log.info("Worker started running an instance of %r", self.procedure.__class__.__name__) self.update_status(Procedure.RUNNING) self.emit('progress', 0.) try: self.procedure.startup() self.procedure.execute() except (KeyboardInterrupt, SystemExit): self.handle_abort() except Exception: self.handle_error() finally: self.shutdown() self.stop() def __repr__(self): return "<%s(port=%s,procedure=%s,should_stop=%s)>" % ( self.__class__.__name__, self.port, self.procedure.__class__.__name__, self.should_stop() ) PyMeasure-0.5/pymeasure/experiment/listeners.py0000644000175000017500000000762713137471263022321 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from logging import StreamHandler, FileHandler from ..log import QueueListener from ..thread import StoppableThread log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) try: import zmq import cloudpickle except ImportError: zmq = None cloudpickle = None log.warning("ZMQ and cloudpickle are required for TCP communication") class Monitor(QueueListener): def __init__(self, results, queue): console = StreamHandler() console.setFormatter(results.formatter) super().__init__(queue, console) class Listener(StoppableThread): """Base class for Threads that need to listen for messages on a ZMQ TCP port and can be stopped by a thread-safe method call """ def __init__(self, port, topic='', timeout=0.01): """ Constructs the Listener object with a subscriber port over which to listen for messages :param port: TCP port to listen on :param topic: Topic to listen on :param timeout: Timeout in seconds to recheck stop flag """ super().__init__() self.port = port self.topic = topic self.context = zmq.Context() log.debug("%s has ZMQ Context: %r" % (self.__class__.__name__, self.context)) self.subscriber = self.context.socket(zmq.SUB) self.subscriber.connect('tcp://localhost:%d' % port) self.subscriber.setsockopt(zmq.SUBSCRIBE, topic.encode()) log.info("%s connected to '%s' topic on tcp://localhost:%d" % ( self.__class__.__name__, topic, port)) self.poller = zmq.Poller() self.poller.register(self.subscriber, zmq.POLLIN) self.timeout = timeout def receive(self, flags=0): topic, record = self.subscriber.recv_serialized(deserialize=cloudpickle.loads, flags=flags) return topic, record def message_waiting(self): return self.poller.poll(self.timeout) def __repr__(self): return "<%s(port=%s,topic=%s,should_stop=%s)>" % ( self.__class__.__name__, self.port, self.topic, self.should_stop()) class Recorder(QueueListener): """ Recorder loads the initial Results for a filepath and appends data by listening for it over a queue. The queue ensures that no data is lost between the Recorder and Worker. """ def __init__(self, results, queue, **kwargs): """ Constructs a Recorder to record the Procedure data into the file path, by waiting for data on the subscription port """ handlers = [] for filename in results.data_filenames: fh = FileHandler(filename=filename, **kwargs) fh.setFormatter(results.formatter) fh.setLevel(logging.NOTSET) handlers.append(fh) super().__init__(queue, *handlers) PyMeasure-0.5/pymeasure/experiment/procedure.py0000644000175000017500000002244013143613470022262 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import sys from copy import deepcopy from importlib.machinery import SourceFileLoader from .parameters import Parameter, Measurable log = logging.getLogger() log.addHandler(logging.NullHandler()) class Procedure(object): """Provides the base class of a procedure to organize the experiment execution. Procedures should be run by Workers to ensure that asynchronous execution is properly managed. .. code-block:: python procedure = Procedure() results = Results(procedure, data_filename) worker = Worker(results, port) worker.start() Inheriting classes should define the startup, execute, and shutdown methods as needed. The shutdown method is called even with a software exception or abort event during the execute method. If keyword arguments are provided, they are added to the object as attributes. """ DATA_COLUMNS = [] MEASURE = {} FINISHED, FAILED, ABORTED, QUEUED, RUNNING = 0, 1, 2, 3, 4 STATUS_STRINGS = { FINISHED: 'Finished', FAILED: 'Failed', ABORTED: 'Aborted', QUEUED: 'Queued', RUNNING: 'Running' } _parameters = {} def __init__(self, **kwargs): self.status = Procedure.QUEUED self._update_parameters() for key in kwargs: if key in self._parameters.keys(): setattr(self, key, kwargs[key]) log.info('Setting parameter %s to %s' % (key, kwargs[key])) self.gen_measurement() def gen_measurement(self): """Create MEASURE and DATA_COLUMNS variables for get_datapoint method.""" # TODO: Refactor measurable-s implementation to be consistent with parameters self.MEASURE = {} for item in dir(self): parameter = getattr(self, item) if isinstance(parameter, Measurable): if parameter.measure: self.MEASURE.update({parameter.name: item}) if not self.DATA_COLUMNS: self.DATA_COLUMNS = Measurable.DATA_COLUMNS def get_datapoint(self): data = {key: getattr(self, self.MEASURE[key]).value for key in self.MEASURE} return data def measure(self): data = self.get_datapoint() log.debug("Produced numbers: %s" % data) self.emit('results', data) def _update_parameters(self): """ Collects all the Parameter objects for the procedure and stores them in a meta dictionary so that the actual values can be set in their stead """ if not self._parameters: self._parameters = {} for item in dir(self): parameter = getattr(self, item) if isinstance(parameter, Parameter): self._parameters[item] = deepcopy(parameter) if parameter.is_set(): setattr(self, item, parameter.value) else: setattr(self, item, None) def parameters_are_set(self): """ Returns True if all parameters are set """ for name, parameter in self._parameters.items(): if getattr(self, name) is None: return False return True def check_parameters(self): """ Raises an exception if any parameter is missing before calling the associated function. Ensures that each value can be set and got, which should cast it into the right format. Used as a decorator @check_parameters on the startup method """ for name, parameter in self._parameters.items(): value = getattr(self, name) if value is None: raise NameError("Missing %s '%s' in %s" % ( parameter.__class__, name, self.__class__)) def parameter_values(self): """ Returns a dictionary of all the Parameter values and grabs any current values that are not in the default definitions """ result = {} for name, parameter in self._parameters.items(): value = getattr(self, name) if value is not None: parameter.value = value setattr(self, name, parameter.value) result[name] = parameter.value else: result[name] = None return result def parameter_objects(self): """ Returns a dictionary of all the Parameter objects and grabs any current values that are not in the default definitions """ result = {} for name, parameter in self._parameters.items(): value = getattr(self, name) if value is not None: parameter.value = value setattr(self, name, parameter.value) result[name] = parameter return result def refresh_parameters(self): """ Enforces that all the parameters are re-cast and updated in the meta dictionary """ for name, parameter in self._parameters.items(): value = getattr(self, name) parameter.value = value setattr(self, name, parameter.value) def set_parameters(self, parameters, except_missing=True): """ Sets a dictionary of parameters and raises an exception if additional parameters are present if except_missing is True """ for name, value in parameters.items(): if name in self._parameters: self._parameters[name].value = value setattr(self, name, self._parameters[name].value) else: if except_missing: raise NameError("Parameter '%s' does not belong to '%s'" % ( name, repr(self))) def startup(self): """ Executes the commands needed at the start-up of the measurement """ pass def execute(self): """ Preforms the commands needed for the measurement itself. During execution the shutdown method will always be run following this method. This includes when Exceptions are raised. """ pass def shutdown(self): """ Executes the commands necessary to shut down the instruments and leave them in a safe state. This method is always run at the end. """ pass def emit(self, topic, record): raise NotImplementedError('should be monkey patched by a worker') def should_stop(self): raise NotImplementedError('should be monkey patched by a worker') def __str__(self): result = repr(self) + "\n" for parameter in self._parameters.items(): result += str(parameter) return result def __repr__(self): return "<{}(status={},parameters_are_set={})>".format( self.__class__.__name__, self.STATUS_STRINGS[self.status], self.parameters_are_set() ) class UnknownProcedure(Procedure): """ Handles the case when a :class:`.Procedure` object can not be imported during loading in the :class:`.Results` class """ def __init__(self, parameters): super().__init__() self._parameters = parameters def startup(self): raise NotImplementedError("UnknownProcedure can not be run") class ProcedureWrapper(object): def __init__(self, procedure): self.procedure = procedure def __getstate__(self): # Get all information needed to reconstruct procedure self._parameters = self.procedure.parameter_values() self._class = self.procedure.__class__.__name__ module = sys.modules[self.procedure.__module__] self._package = module.__package__ self._module = module.__name__ self._file = module.__file__ state = self.__dict__.copy() del state['procedure'] return state def __setstate__(self, state): self.__dict__.update(state) # Restore the procedure module = SourceFileLoader(self._module, self._file).load_module() cls = getattr(module, self._class) self.procedure = cls() self.procedure.set_parameters(self._parameters) self.procedure.refresh_parameters() del self._parameters del self._class del self._package del self._module del self._filePyMeasure-0.5/pymeasure/experiment/__init__.py0000644000175000017500000000306513137471263022040 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .parameters import (Parameter, IntegerParameter, FloatParameter, VectorParameter, ListParameter, BooleanParameter, Measurable) from .procedure import Procedure, UnknownProcedure from .results import Results, unique_filename from .workers import Worker from .listeners import Listener, Recorder from .config import get_config from .experiment import Experiment, get_array, get_array_steps, get_array_zeroPyMeasure-0.5/pymeasure/experiment/config.py0000644000175000017500000000567313137471263021555 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # # # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import configparser import logging import os log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) def set_file(filename): os.environ['CONFIG'] = filename def get_config(filename='default_config.ini'): if 'CONFIG' in os.environ.keys(): filename = os.environ['CONFIG'] config = configparser.ConfigParser() config.read(filename) return config # noinspection PyProtectedMember def set_mpl_rcparams(config): if 'matplotlib.rcParams' in config._sections.keys(): import matplotlib for key in config._sections['matplotlib.rcParams']: matplotlib.rcParams[key] = eval(config._sections['matplotlib.rcParams'][key]) PyMeasure-0.5/pymeasure/experiment/parameters.py0000644000175000017500000003562513160054362022444 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # class Parameter(object): """ Encapsulates the information for an experiment parameter with information about the name, and units if supplied. :var value: The value of the parameter :param name: The parameter name :param default: The default value :param ui_class: A Qt class to use for the UI of this parameter """ def __init__(self, name, default=None, ui_class=None): self.name = name self._value = default self.default = default self.ui_class = ui_class @property def value(self): if self.is_set(): return self._value else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): self._value = value def is_set(self): """ Returns True if the Parameter value is set """ return self._value is not None def __str__(self): return str(self._value) if self.is_set() else '' def __repr__(self): return "<%s(name=%s,value=%s,default=%s)>" % ( self.__class__.__name__, self.name, self._value, self.default) class IntegerParameter(Parameter): """ :class:`.Parameter` sub-class that uses the integer type to store the value. :var value: The integer value of the parameter :param name: The parameter name :param units: The units of measure for the parameter :param minimum: The minimum allowed value (default: -1e9) :param maximum: The maximum allowed value (default: 1e9) :param default: The default integer value :param ui_class: A Qt class to use for the UI of this parameter """ def __init__(self, name, units=None, minimum=-1e9, maximum=1e9, **kwargs): super().__init__(name, **kwargs) self.units = units self.minimum = int(minimum) self.maximum = int(maximum) @property def value(self): if self.is_set(): return int(self._value) else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): try: value = int(value) except ValueError: raise ValueError("IntegerParameter given non-integer value of " "type '%s'" % type(value)) if value < self.minimum: raise ValueError("IntegerParameter value is below the minimum") elif value > self.maximum: raise ValueError("IntegerParameter value is above the maximum") else: self._value = value def __str__(self): if not self.is_set(): return '' result = "%d" % self._value if self.units: result += " %s" % self.units return result def __repr__(self): return "<%s(name=%s,value=%s,units=%s,default=%s)>" % ( self.__class__.__name__, self.name, self._value, self.units, self.default) class BooleanParameter(Parameter): """ :class:`.Parameter` sub-class that uses the boolean type to store the value. :var value: The boolean value of the parameter :param name: The parameter name :param default: The default boolean value :param ui_class: A Qt class to use for the UI of this parameter """ @property def value(self): if self.is_set(): return self._value else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): try: self._value = bool(value) except ValueError: raise ValueError("BooleanParameter given non-boolean value of " "type '%s'" % type(value)) class FloatParameter(Parameter): """ :class:`.Parameter` sub-class that uses the floating point type to store the value. :var value: The floating point value of the parameter :param name: The parameter name :param units: The units of measure for the parameter :param minimum: The minimum allowed value (default: -1e9) :param maximum: The maximum allowed value (default: 1e9) :param default: The default floating point value :param ui_class: A Qt class to use for the UI of this parameter """ def __init__(self, name, units=None, minimum=-1e9, maximum=1e9, **kwargs): super().__init__(name, **kwargs) self.units = units self.minimum = minimum self.maximum = maximum @property def value(self): if self.is_set(): return float(self._value) else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): try: value = float(value) except ValueError: raise ValueError("FloatParameter given non-float value of " "type '%s'" % type(value)) if value < self.minimum: raise ValueError("FloatParameter value is below the minimum") elif value > self.maximum: raise ValueError("FloatParameter value is above the maximum") else: self._value = value def __str__(self): if not self.is_set(): return '' result = "%g" % self._value if self.units: result += " %s" % self.units return result def __repr__(self): return "<%s(name=%s,value=%s,units=%s,default=%s)>" % ( self.__class__.__name__, self.name, self._value, self.units, self.default) class VectorParameter(Parameter): """ :class:`.Parameter` sub-class that stores the value in a vector format. :var value: The value of the parameter as a list of floating point numbers :param name: The parameter name :param length: The integer dimensions of the vector :param units: The units of measure for the parameter :param default: The default value :param ui_class: A Qt class to use for the UI of this parameter """ def __init__(self, name, length=3, units=None, **kwargs): super().__init__(name, **kwargs) self._length = length self.units = units @property def value(self): if self.is_set(): return [float(ve) for ve in self._value] else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): # Strip initial and final brackets if isinstance(value, str): if (value[0] != '[') or (value[-1] != ']'): raise ValueError("VectorParameter must be passed a vector" " denoted by square brackets if initializing" " by string.") raw_list = value[1:-1].split(",") elif isinstance(value, (list, tuple)): raw_list = value else: raise ValueError("VectorParameter given undesired value of " "type '%s'" % type(value)) if len(raw_list) != self._length: raise ValueError("VectorParameter given value of length " "%d instead of %d" % (len(raw_list), self._length)) try: self._value = [float(ve) for ve in raw_list] except ValueError: raise ValueError("VectorParameter given input '%s' that could " "not be converted to floats." % str(value)) def __str__(self): """If we eliminate spaces within the list __repr__ then the csv parser will interpret it as a single value.""" if not self.is_set(): return '' result = "".join(repr(self.value).split()) if self.units: result += " %s" % self.units return result def __repr__(self): return "<%s(name=%s,value=%s,units=%s,length=%s)>" % ( self.__class__.__name__, self.name, self._value, self.units, self._length) class ListParameter(Parameter): """ :class:`.Parameter` sub-class that stores the value as a list. :param name: The parameter name :param choices: An explicit list of choices, which is disregarded if None :param units: The units of measure for the parameter :param default: The default value :param ui_class: A Qt class to use for the UI of this parameter """ def __init__(self, name, choices=None, units=None, **kwargs): super().__init__(name, **kwargs) self._choices = choices self.units = units @property def value(self): if self.is_set(): return self._value else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): if self._choices is not None and value in self._choices: self._value = value else: raise ValueError("Invalid choice for parameter. " "Must be one of %s" % str(self._choices)) class PhysicalParameter(VectorParameter): """ :class:`.VectorParameter` sub-class of 2 dimentions to store a value and its uncertainty. :var value: The value of the parameter as a list of 2 floating point numbers :param name: The parameter name :param uncertainty_type: Type of uncertainty, 'absolute', 'relative' or 'percentage' :param units: The units of measure for the parameter :param default: The default value :param ui_class: A Qt class to use for the UI of this parameter """ def __init__(self, name, uncertaintyType='absolute', **kwargs): super().__init__(name, length=2, **kwargs) self._utype = ListParameter("uncertainty type", choices=['absolute', 'relative', 'percentage'], default=None) self._utype.value = uncertaintyType @property def value(self): if self.is_set(): return [float(ve) for ve in self._value] else: raise ValueError("Parameter value is not set") @value.setter def value(self, value): # Strip initial and final brackets if isinstance(value, str): if (value[0] != '[') or (value[-1] != ']'): raise ValueError("VectorParameter must be passed a vector" " denoted by square brackets if initializing" " by string.") raw_list = value[1:-1].split(",") elif isinstance(value, (list, tuple)): raw_list = value else: raise ValueError("VectorParameter given undesired value of " "type '%s'" % type(value)) if len(raw_list) != self._length: raise ValueError("VectorParameter given value of length " "%d instead of %d" % (len(raw_list), self._length)) try: self._value = [float(ve) for ve in raw_list] except ValueError: raise ValueError("VectorParameter given input '%s' that could " "not be converted to floats." % str(value)) # Uncertainty must be non-negative self._value[1] = abs(self._value[1]) @property def uncertainty_type(self): return self._utype.value @uncertainty_type.setter def uncertainty_type(self, uncertaintyType): oldType = self._utype.value self._utype.value = uncertaintyType newType = self._utype.value if self.is_set(): # Convert uncertainty value to the new type if (oldType, newType) == ('absolute', 'relative'): self._value[1] = abs(self._value[1] / self._value[0]) if (oldType, newType) == ('relative', 'absolute'): self._value[1] = abs(self._value[1] * self._value[0]) if (oldType, newType) == ('relative', 'percentage'): self._value[1] = abs(self._value[1] * 100.0) if (oldType, newType) == ('percentage', 'relative'): self._value[1] = abs(self._value[1] * 0.01) if (oldType, newType) == ('percentage', 'absolute'): self._value[1] = abs(self._value[1] * self._value[0] * 0.01) if (oldType, newType) == ('absolute', 'percentage'): self._value[1] = abs(self._value[1] * 100.0 / self._value[0]) def __str__(self): if not self.is_set(): return '' result = "%g +/- %g" % (self._value[0], self._value[1]) if self.units: result += " %s" % self.units if self._utype.value is not None: result += " (%s)" % self._utype.value return result def __repr__(self): return "<%s(name=%s,value=%s,units=%s,uncertaintyType=%s)>" % ( self.__class__.__name__, self.name, self._value, self.units, self._utype.value) class Measurable(object): """ Encapsulates the information for a measurable experiment parameter with information about the name, fget function and units if supplied. The value property is called when the procedure retrieves a datapoint and calls the fget function. If no fget function is specified, the value property will return the latest set value of the parameter (or default if never set). :var value: The value of the parameter :param name: The parameter name :param fget: The parameter fget function (e.g. an instrument parameter) :param default: The default value """ DATA_COLUMNS = [] def __init__(self, name, fget=None, units=None, measure=True, default=None, **kwargs): self.name = name self.units = units self.measure = measure if fget is not None: self.fget = fget self._value = fget() else: self._value = default Measurable.DATA_COLUMNS.append(name) def fget(self): return self._value @property def value(self): if hasattr(self, 'fget'): self._value = self.fget() return self._value @value.setter def value(self, value): self._value = value PyMeasure-0.5/pymeasure/experiment/results.py0000644000175000017500000003141713143613470021777 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import os import re import sys from copy import deepcopy from importlib.machinery import SourceFileLoader from datetime import datetime import pandas as pd from .procedure import Procedure, UnknownProcedure from .parameters import Parameter log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) def unique_filename(directory, prefix='DATA', suffix='', ext='csv', dated_folder=False, index=True, datetimeformat="%Y-%m-%d"): """ Returns a unique filename based on the directory and prefix """ now = datetime.now() directory = os.path.abspath(directory) if dated_folder: directory = os.path.join(directory, now.strftime('%Y-%m-%d')) if not os.path.exists(directory): os.makedirs(directory) if index: i = 1 basename = "%s%s" % (prefix, now.strftime(datetimeformat)) basepath = os.path.join(directory, basename) filename = "%s_%d%s.%s" % (basepath, i, suffix, ext) while os.path.exists(filename): i += 1 filename = "%s_%d%s.%s" % (basepath, i, suffix, ext) else: basename = "%s%s%s.%s" % (prefix, now.strftime(datetimeformat), suffix, ext) filename = os.path.join(directory, basename) return filename class CSVFormatter(logging.Formatter): """ Formatter of data results """ def __init__(self, columns, delimiter=','): """Creates a csv formatter for a given list of columns (=header). :param columns: list of column names. :type columns: list :param delimiter: delimiter between columns. :type delimiter: str """ super().__init__() self.columns = columns self.delimiter = delimiter def format(self, record): """Formats a record as csv. :param record: record to format. :type record: dict :return: a string """ return self.delimiter.join('{}'.format(record[x]) for x in self.columns) def format_header(self): return self.delimiter.join(self.columns) class Results(object): """ The Results class provides a convenient interface to reading and writing data in connection with a :class:`.Procedure` object. :cvar COMMENT: The character used to identify a comment (default: #) :cvar DELIMITER: The character used to delimit the data (default: ,) :cvar LINE_BREAK: The character used for line breaks (default \\n) :cvar CHUNK_SIZE: The length of the data chuck that is read :param procedure: Procedure object :param data_filename: The data filename where the data is or should be stored """ COMMENT = '#' DELIMITER = ',' LINE_BREAK = "\n" CHUNK_SIZE = 1000 def __init__(self, procedure, data_filename): if not isinstance(procedure, Procedure): raise ValueError("Results require a Procedure object") self.procedure = procedure self.procedure_class = procedure.__class__ self.parameters = procedure.parameter_objects() self._header_count = -1 self.formatter = CSVFormatter(columns=self.procedure.DATA_COLUMNS) if isinstance(data_filename, (list, tuple)): data_filenames, data_filename = data_filename, data_filename[0] else: data_filenames = [data_filename] self.data_filename = data_filename self.data_filenames = data_filenames if os.path.exists(data_filename): # Assume header is already written self.reload() self.procedure.status = Procedure.FINISHED # TODO: Correctly store and retrieve status else: for filename in self.data_filenames: with open(filename, 'w') as f: f.write(self.header()) f.write(self.labels()) self._data = None def __getstate__(self): # Get all information needed to reconstruct procedure self._parameters = self.procedure.parameter_values() self._class = self.procedure.__class__.__name__ module = sys.modules[self.procedure.__module__] self._package = module.__package__ self._module = module.__name__ self._file = module.__file__ state = self.__dict__.copy() del state['procedure'] del state['procedure_class'] return state def __setstate__(self, state): self.__dict__.update(state) # Restore the procedure module = SourceFileLoader(self._module, self._file).load_module() cls = getattr(module, self._class) self.procedure = cls() self.procedure.set_parameters(self._parameters) self.procedure.refresh_parameters() self.procedure_class = cls del self._parameters del self._class del self._package del self._module del self._file def header(self): """ Returns a text header to accompany a datafile so that the procedure can be reconstructed """ h = [] procedure = re.search("'(?P[^']+)'", repr(self.procedure_class)).group("name") h.append("Procedure: <%s>" % procedure) h.append("Parameters:") for name, parameter in self.parameters.items(): h.append("\t%s: %s" % (parameter.name, str(parameter))) h.append("Data:") self._header_count = len(h) h = [Results.COMMENT + l for l in h] # Comment each line return Results.LINE_BREAK.join(h) + Results.LINE_BREAK def labels(self): """ Returns the columns labels as a string to be written to the file """ return self.formatter.format_header() + Results.LINE_BREAK def format(self, data): """ Returns a formatted string containing the data to be written to a file """ return self.formatter.format(data) def parse(self, line): """ Returns a dictionary containing the data from the line """ data = {} items = line.split(Results.DELIMITER) for i, key in enumerate(self.procedure.DATA_COLUMNS): data[key] = items[i] return data @staticmethod def parse_header(header, procedure_class=None): """ Returns a Procedure object with the parameters as defined in the header text. """ if procedure_class is not None: procedure = procedure_class() else: procedure = None header = header.split(Results.LINE_BREAK) procedure_module = None parameters = {} for line in header: if line.startswith(Results.COMMENT): line = line[1:] # Uncomment else: raise ValueError("Parsing a header which contains " "uncommented sections") if line.startswith("Procedure"): regex = "<(?:(?P[^>]+)\.)?(?P[^.>]+)>" search = re.search(regex, line) procedure_module = search.group("module") procedure_class = search.group("class") elif line.startswith("\t"): regex = ("\t(?P[^:]+):\s(?P[^\s]+)" "(?:\s(?P.+))?") search = re.search(regex, line) if search is None: raise Exception("Error parsing header line %s." % line) else: parameters[search.group("name")] = ( search.group("value"), search.group("units") ) if procedure is None: if procedure_class is None: raise ValueError("Header does not contain the Procedure class") try: from importlib import import_module procedure_module = import_module(procedure_module) procedure_class = getattr(procedure_module, procedure_class) procedure = procedure_class() except ImportError: procedure = UnknownProcedure(parameters) log.warning("Unknown Procedure being used") except Exception as e: raise e def units_found(parameter, units): return (hasattr(parameter, 'units') and parameter.units is None and isinstance(parameter, Parameter) and units is not None) # Fill the procedure with the parameters found for name, parameter in procedure.parameter_objects().items(): if parameter.name in parameters: value, units = parameters[parameter.name] if units_found(parameter, units): # Force full string to be matched value = value + " " + str(units) setattr(procedure, name, value) else: raise Exception("Missing '%s' parameter when loading '%s' class" % ( parameter.name, procedure_class)) procedure.refresh_parameters() # Enforce update of meta data return procedure @staticmethod def load(data_filename, procedure_class=None): """ Returns a Results object with the associated Procedure object and data """ header = "" header_read = False header_count = 0 with open(data_filename, 'r') as f: while not header_read: line = f.readline() if line.startswith(Results.COMMENT): header += line.strip() + Results.LINE_BREAK header_count += 1 else: header_read = True procedure = Results.parse_header(header[:-1], procedure_class) results = Results(procedure, data_filename) results._header_count = header_count return results @property def data(self): # Need to update header count for correct referencing if self._header_count == -1: self._header_count = len( self.header()[-1].split(Results.LINE_BREAK)) if self._data is None or len(self._data) == 0: # Data has not been read try: self.reload() except Exception: # Empty dataframe self._data = pd.DataFrame(columns=self.procedure.DATA_COLUMNS) else: # Concatenate additional data skiprows = len(self._data) + self._header_count chunks = pd.read_csv( self.data_filename, comment=Results.COMMENT, header=0, names=self._data.columns, chunksize=Results.CHUNK_SIZE, skiprows=skiprows, iterator=True ) try: tmp_frame = pd.concat(chunks, ignore_index=True) self._data = pd.concat([self._data, tmp_frame], ignore_index=True) except Exception: pass # All data is up to date return self._data def reload(self): """ Preforms a full reloading of the file data, neglecting any changes in the comments """ chunks = pd.read_csv( self.data_filename, comment=Results.COMMENT, chunksize=Results.CHUNK_SIZE, iterator=True ) try: self._data = pd.concat(chunks, ignore_index=True) except Exception: self._data = chunks.read() def __repr__(self): return "<{}(filename='{}',procedure={},shape={})>".format( self.__class__.__name__, self.data_filename, self.procedure.__class__.__name__, self.data.shape ) PyMeasure-0.5/pymeasure/experiment/experiment.py0000644000175000017500000002267113137471263022465 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger() log.addHandler(logging.NullHandler()) try: from IPython import display except ImportError: log.warning("IPython could not be imported") from .results import unique_filename from .config import get_config, set_mpl_rcparams from pymeasure.log import setup_logging, console_log from pymeasure.experiment import Results, Worker from .parameters import Measurable import time, signal import numpy as np import tempfile import gc def get_array(start, stop, step): """Returns a numpy array from start to stop""" step = np.sign(stop - start) * abs(step) return np.arange(start, stop + step, step) def get_array_steps(start, stop, numsteps): """Returns a numpy array from start to stop in numsteps""" return get_array(start, stop, (abs(stop - start) / numsteps)) def get_array_zero(maxval, step): """Returns a numpy array from 0 to maxval to -maxval to 0""" return np.concatenate((np.arange(0, maxval, step), np.arange(maxval, -maxval, -step), np.arange(-maxval, 0, step))) def create_filename(title): """ Create a new filename according to the style defined in the config file. If no config is specified, create a temporary file. """ config = get_config() if 'Filename' in config._sections.keys(): filename = unique_filename(suffix='_%s' % title, **config._sections['Filename']) else: filename = tempfile.mktemp() return filename class Experiment(object): """ Class which starts logging and creates/runs the results and worker processes. .. code-block:: python procedure = Procedure() experiment = Experiment(title, procedure) experiment.start() experiment.plot_live('x', 'y', style='.-') for a multi-subplot graph: import pylab as pl ax1 = pl.subplot(121) experiment.plot('x','y',ax=ax1) ax2 = pl.subplot(122) experiment.plot('x','z',ax=ax2) experiment.plot_live() :var value: The value of the parameter :param title: The experiment title :param procedure: The procedure object :param analyse: Post-analysis function, which takes a pandas dataframe as input and returns it with added (analysed) columns. The analysed results are accessible via experiment.data, as opposed to experiment.results.data for the 'raw' data. :param _data_timeout: Time limit for how long live plotting should wait for datapoints. """ def __init__(self, title, procedure, analyse=(lambda x: x)): self.title = title self.procedure = procedure self.measlist = [] self.port = 5888 self.plots = [] self.figs = [] self._data = [] self.analyse = analyse self._data_timeout = 10 config = get_config() set_mpl_rcparams(config) if 'Logging' in config._sections.keys(): self.scribe = setup_logging(log, **config._sections['Logging']) else: self.scribe = console_log(log) self.scribe.start() self.filename = create_filename(self.title) log.info("Using data file: %s" % self.filename) self.results = Results(self.procedure, self.filename) log.info("Set up Results") self.worker = Worker(self.results, self.scribe.queue, logging.DEBUG) log.info("Create worker") def start(self): """Start the worker""" log.info("Starting worker...") self.worker.start() @property def data(self): """Data property which returns analysed data, if an analyse function is defined, otherwise returns the raw data.""" self._data = self.analyse(self.results.data.copy()) return self._data def wait_for_data(self): """Wait for the data attribute to fill with datapoints.""" t = time.time() while self.data.empty: time.sleep(.1) if (time.time() - t) > self._data_timeout: log.warning('Timeout, no data received for liveplot') return False return True def plot_live(self, *args, **kwargs): """Live plotting loop for jupyter notebook, which automatically updates (an) in-line matplotlib graph(s). Will create a new plot as specified by input arguments, or will update (an) existing plot(s).""" if self.wait_for_data(): if not (self.plots): self.plot(*args, **kwargs) while not self.worker.should_stop(): self.update_plot() display.clear_output(wait=True) if self.worker.is_alive(): self.worker.terminate() self.scribe.stop() def plot(self, *args, **kwargs): """Plot the results from the experiment.data pandas dataframe. Store the plots in a plots list attribute.""" if self.wait_for_data(): kwargs['title'] = self.title ax = self.data.plot(*args, **kwargs) self.plots.append({'type': 'plot', 'args': args, 'kwargs': kwargs, 'ax': ax}) if ax.get_figure() not in self.figs: self.figs.append(ax.get_figure()) self._user_interrupt = False def clear_plot(self): """Clear the figures and plot lists.""" for fig in self.figs: fig.clf() pl.close() self.figs = [] self.plots = [] gc.collect() def update_plot(self): """Update the plots in the plots list with new data from the experiment.data pandas dataframe.""" try: tasks = [] self.data for plot in self.plots: ax = plot['ax'] if plot['type'] == 'plot': x, y = plot['args'][0], plot['args'][1] if type(y) == str: y = [y] for yname, line in zip(y, ax.lines): self.update_line(ax, line, x, yname) if plot['type'] == 'pcolor': x, y, z = plot['x'], plot['y'], plot['z'] update_pcolor(ax, x, y, z) display.clear_output(wait=True) display.display(*self.figs) time.sleep(0.1) except KeyboardInterrupt: display.clear_output(wait=True) display.display(*self.figs) self._user_interrupt = True def pcolor(self, xname, yname, zname, *args, **kwargs): """Plot the results from the experiment.data pandas dataframe in a pcolor graph. Store the plots in a plots list attribute.""" title = self.title x, y, z = self._data[xname], self._data[yname], self._data[zname] shape = (len(y.unique()), len(x.unique())) diff = shape[0] * shape[1] - len(z) Z = np.concatenate((z.values, np.zeros(diff))).reshape(shape) df = pd.DataFrame(Z, index=y.unique(), columns=x.unique()) ax = sns.heatmap(df) pl.title(title) pl.xlabel(xname) pl.ylabel(yname) ax.invert_yaxis() pl.plt.show() self.plots.append( {'type': 'pcolor', 'x': xname, 'y': yname, 'z': zname, 'args': args, 'kwargs': kwargs, 'ax': ax}) if ax.get_figure() not in self.figs: self.figs.append(ax.get_figure()) def update_pcolor(self, ax, xname, yname, zname): """Update a pcolor graph with new data.""" x, y, z = self._data[xname], self._data[yname], self._data[zname] shape = (len(y.unique()), len(x.unique())) diff = shape[0] * shape[1] - len(z) Z = np.concatenate((z.values, np.zeros(diff))).reshape(shape) df = pd.DataFrame(Z, index=y.unique(), columns=x.unique()) cbar_ax = ax.get_figure().axes[1] sns.heatmap(df, ax=ax, cbar_ax=cbar_ax) ax.set_xlabel(xname) ax.set_ylabel(yname) ax.invert_yaxis() def update_line(self, ax, hl, xname, yname): """Update a line in a matplotlib graph with new data.""" del hl._xorig, hl._yorig hl.set_xdata(self._data[xname]) hl.set_ydata(self._data[yname]) ax.relim() ax.autoscale() gc.collect() def __del__(self): self.scribe.stop() if self.worker.is_alive(): self.worker.recorder_queue.put(None) self.worker.monitor_queue.put(None) self.worker.stop() PyMeasure-0.5/pymeasure/instruments/0000755000175000017500000000000013171765525020143 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/keithley/0000755000175000017500000000000013171765525021761 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/keithley/keithley2400.py0000644000175000017500000006007513160054362024453 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument, RangeException from pymeasure.adapters import PrologixAdapter from pymeasure.instruments.validators import truncated_range, strict_discrete_set from .buffer import KeithleyBuffer import numpy as np import time from io import BytesIO import re class Keithley2400(Instrument, KeithleyBuffer): """ Represents the Keithely 2400 SourceMeter and provides a high-level interface for interacting with the instrument. .. code-block:: python keithley = Keithley2400("GPIB::1") keithley.apply_current() # Sets up to source current keithley.source_current_range = 10e-3 # Sets the source current range to 10 mA keithley.compliance_voltage = 10 # Sets the compliance voltage to 10 V keithley.source_current = 0 # Sets the source current to 0 mA keithley.enable_source() # Enables the source output keithley.measure_voltage() # Sets up to measure voltage keithley.ramp_to_current(5e-3) # Ramps the current to 5 mA print(keithley.voltage) # Prints the voltage in Volts keithley.shutdown() # Ramps the current to 0 mA and disables output """ # TODO: Add measurement mode property source_mode = Instrument.control( ":SOUR:FUNC?", ":SOUR:FUNC %s", """ A string property that controls the source mode, which can take the values 'current' or 'voltage'. The convenience methods :meth:`~.Keithley2400.apply_current` and :meth:`~.Keithley2400.apply_voltage` can also be used. """, validator=strict_discrete_set, values={'current':'CURR', 'voltage':'VOLT'}, map_values=True ) source_enabled = Instrument.measurement("OUTPUT?", """ Reads a boolean value that is True if the source is enabled. """, cast=bool ) ############### # Current (A) # ############### current = Instrument.measurement(":READ?", """ Reads the current in Amps, if configured for this reading. """ ) current_range = Instrument.control( ":SENS:CURR:RANG?", ":SENS:CURR:RANG:AUTO 0;:SENS:CURR:RANG %g", """ A floating point property that controls the measurement current range in Amps, which can take values between -1.05 and +1.05 A. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[-1.05, 1.05] ) current_nplc = Instrument.control( ":SENS:CURR:NPLC?", ":SENS:CURR:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the DC current measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) compliance_current = Instrument.control( ":SENS:CURR:PROT?", ":SENS:CURR:PROT %g", """ A floating point property that controls the compliance current in Amps. """, validator=truncated_range, values=[-1.05, 1.05] ) source_current = Instrument.control( ":SOUR:CURR?", ":SOUR:CURR:LEV %g", """ A floating point property that controls the source current in Amps. """ ) source_current_range = Instrument.control( ":SOUR:CURR:RANG?", ":SOUR:CURR:RANG:AUTO 0;:SOUR:CURR:RANG %g", """ A floating point property that controls the source current range in Amps, which can take values between -1.05 and +1.05 A. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[-1.05, 1.05] ) ############### # Voltage (V) # ############### voltage = Instrument.measurement(":READ?", """ Reads the voltage in Volts, if configured for this reading. """ ) voltage_range = Instrument.control( ":SENS:VOLT:RANG?", ":SENS:VOLT:RANG:AUTO 0;:SENS:VOLT:RANG %g", """ A floating point property that controls the measurement voltage range in Volts, which can take values from -210 to 210 V. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[-210, 210] ) voltage_nplc = Instrument.control( ":SENS:CURRVOLT:NPLC?", ":SENS:VOLT:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the DC voltage measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) compliance_voltage = Instrument.control( ":SENS:VOLT:PROT?", ":SENS:VOLT:PROT %g", """ A floating point property that controls the compliance voltage in Volts. """, validator=truncated_range, values=[-210, 210] ) source_voltage = Instrument.control( ":SOUR:VOLT?", ":SOUR:VOLT:LEV %g", """ A floating point property that controls the source voltage in Volts. """ ) source_voltage_range = Instrument.control( ":SOUR:VOLT:RANG?", ":SOUR:VOLT:RANG:AUTO 0;:SOUR:VOLT:RANG %g", """ A floating point property that controls the source voltage range in Volts, which can take values from -210 to 210 V. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[-210, 210] ) #################### # Resistance (Ohm) # #################### resistance = Instrument.measurement(":READ?", """ Reads the resistance in Ohms, if configured for this reading. """ ) resistance_range = Instrument.control( ":SENS:RES:RANG?", ":SENS:RES:RANG:AUTO 0;:SENS:RES:RANG %g", """ A floating point property that controls the resistance range in Ohms, which can take values from 0 to 210 MOhms. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 210e6] ) resistance_nplc = Instrument.control( ":SENS:RES:NPLC?", ":SENS:RES:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the 2-wire resistance measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) wires = Instrument.control( ":SYSTEM:RSENSE?", ":SYSTEM:RSENSE %d", """ An integer property that controls the number of wires in use for resistance measurements, which can take the value of 2 or 4. """, validator=strict_discrete_set, values={4:1, 2:2}, map_values=True ) buffer_points = Instrument.control( ":TRAC:POIN?", ":TRAC:POIN %d", """ An integer property that controls the number of buffer points. This does not represent actual points in the buffer, but the configuration value instead. """, validator=truncated_range, values=[1, 2500], cast=int ) means = Instrument.measurement( ":CALC3:FORM MEAN;:CALC3:DATA?;", """ Reads the calculated means (averages) for voltage, current, and resistance from the buffer data as a list. """ ) maximums = Instrument.measurement( ":CALC3:FORM MAX;:CALC3:DATA?;", """ Returns the calculated maximums for voltage, current, and resistance from the buffer data as a list. """ ) minimums = Instrument.measurement( ":CALC3:FORM MIN;:CALC3:DATA?;", """ Returns the calculated minimums for voltage, current, and resistance from the buffer data as a list. """ ) standard_devs = Instrument.measurement( ":CALC3:FORM SDEV;:CALC3:DATA?;", """ Returns the calculated standard deviations for voltage, current, and resistance from the buffer data as a list. """ ) ########### # Trigger # ########### trigger_count = Instrument.control( ":TRIG:COUN?", ":TRIG:COUN %d", """ An integer property that controls the trigger count, which can take values from 1 to 9,999. """, validator=truncated_range, values=[1, 2500], cast=int ) trigger_delay = Instrument.control( ":TRIG:SEQ:DEL?", ":TRIG:SEQ:DEL %g", """ A floating point property that controls the trigger delay in seconds, which can take values from 0 to 999.9999 s. """, validator=truncated_range, values=[0, 999.9999] ) def __init__(self, adapter, **kwargs): super(Keithley2400, self).__init__( adapter, "Keithley 2400 SourceMeter", **kwargs ) def enable_source(self): """ Enables the source of current or voltage depending on the configuration of the instrument. """ self.write("OUTPUT ON") def disable_source(self): """ Disables the source of current or voltage depending on the configuration of the instrument. """ self.write("OUTPUT OFF") def measure_resistance(self, nplc=1, resistance=2.1e5, auto_range=True): """ Configures the measurement of resistance. :param nplc: Number of power line cycles (NPLC) from 0.01 to 10 :param resistance: Upper limit of resistance in Ohms, from -210 MOhms to 210 MOhms :param auto_range: Enables auto_range if True, else uses the set resistance """ log.info("%s is measuring resistance." % self.name) self.write(":SENS:FUNC RES;" ":SENS:RES:MODE MAN;" ":SENS:RES:NPLC %f;:FORM:ELEM RES;" % nplc) if auto_range: self.write(":SENS:RES:RANG:AUTO 1;") else: self.resistance_range = resistance self.check_errors() def measure_voltage(self, nplc=1, voltage=21.0, auto_range=True): """ Configures the measurement of voltage. :param nplc: Number of power line cycles (NPLC) from 0.01 to 10 :param voltage: Upper limit of voltage in Volts, from -210 V to 210 V :param auto_range: Enables auto_range if True, else uses the set voltage """ log.info("%s is measuring voltage." % self.name) self.write(":SENS:FUNC 'VOLT';" ":SENS:VOLT:NPLC %f;:FORM:ELEM VOLT;" % nplc) if auto_range: self.write(":SENS:VOLT:RANG:AUTO 1;") else: self.voltage_range = voltage self.check_errors() def measure_current(self, nplc=1, current=1.05e-4, auto_range=True): """ Configures the measurement of current. :param nplc: Number of power line cycles (NPLC) from 0.01 to 10 :param current: Upper limit of current in Amps, from -1.05 A to 1.05 A :param auto_range: Enables auto_range if True, else uses the set current """ log.info("%s is measuring current." % self.name) self.write(":SENS:FUNC 'CURR';" ":SENS:CURR:NPLC %f;:FORM:ELEM CURR;" % nplc) if auto_range: self.write(":SENS:CURR:RANG:AUTO 1;") else: self.current_range = current self.check_errors() def auto_range_source(self): """ Configures the source to use an automatic range. """ if self.source_mode == 'current': self.write(":SOUR:CURR:RANG:AUTO 1") else: self.write(":SOUR:VOLT:RANG:AUTO 1") def apply_current(self, current_range=None, compliance_voltage=0.1): """ Configures the instrument to apply a source current, and uses an auto range unless a current range is specified. The compliance voltage is also set. :param compliance_voltage: A float in the correct range for a :attr:`~.Keithley2400.compliance_voltage` :param current_range: A :attr:`~.Keithley2400.current_range` value or None """ log.info("%s is sourcing current." % self.name) self.source_mode = 'current' if current_range is None: self.auto_range_source() else: self.source_current_range = current_range self.compliance_voltage = compliance_voltage self.check_errors() def apply_voltage(self, voltage_range=None, compliance_current=0.1): """ Configures the instrument to apply a source voltage, and uses an auto range unless a voltage range is specified. The compliance current is also set. :param compliance_current: A float in the correct range for a :attr:`~.Keithley2400.compliance_current` :param voltage_range: A :attr:`~.Keithley2400.voltage_range` value or None """ log.info("%s is sourcing voltage." % self.name) self.source_mode = 'voltage' if voltage_range is None: self.auto_range_source() else: self.source_voltage_range = voltage_range self.compliance_current = compliance_current self.check_errors() def beep(self, frequency, duration): """ Sounds a system beep. :param frequency: A frequency in Hz between 65 Hz and 2 MHz :param duration: A time in seconds between 0 and 7.9 seconds """ self.write(":SYST:BEEP %g, %g" % (frequency, duration)) def triad(self, base_frequency, duration): """ Sounds a musical triad using the system beep. :param base_frequency: A frequency in Hz between 65 Hz and 1.3 MHz :param duration: A time in seconds between 0 and 7.9 seconds """ self.beep(base_frequency, duration) time.sleep(duration) self.beep(base_frequency*5.0/4.0, duration) time.sleep(duration) self.beep(base_frequency*6.0/4.0, duration) @property def error(self): """ Returns a tuple of an error code and message from a single error. """ err = self.values(":system:error?") if len(err) < 2: err = self.read() # Try reading again code = err[0] message = err[1].replace('"', '') return (code, message) def check_errors(self): """ Logs any system errors reported by the instrument. """ code, message = self.error while code != 0: t = time.time() log.info("Keithley 2400 reported error: %d, %s" % (code, message)) code, message = self.error if (time.time()-t)>10: log.warning("Timed out for Keithley 2400 error retrieval.") def reset(self): """ Resets the instrument and clears the queue. """ self.write("status:queue:clear;*RST;:stat:pres;:*CLS;") def ramp_to_current(self, target_current, steps=30, pause=20e-3): """ Ramps to a target current from the set current value over a certain number of linear steps, each separated by a pause duration. :param target_current: A current in Amps :param steps: An integer number of steps :param pause: A pause duration in seconds to wait between steps """ currents = np.linspace( self.source_current, target_current, steps ) for current in currents: self.source_current = current time.sleep(pause) def ramp_to_voltage(self, target_voltage, steps=30, pause=20e-3): """ Ramps to a target voltage from the set voltage value over a certain number of linear steps, each separated by a pause duration. :param target_voltage: A voltage in Amps :param steps: An integer number of steps :param pause: A pause duration in seconds to wait between steps """ voltages = np.linspace( self.source_voltage, target_voltage, steps ) for voltage in voltages: self.source_voltage = voltage time.sleep(pause) def trigger(self): """ Executes a bus trigger, which can be used when :meth:`~.trigger_on_bus` is configured. """ return self.write("*TRG") def trigger_immediately(self): """ Configures measurements to be taken with the internal trigger at the maximum sampling rate. """ self.write(":ARM:SOUR IMM;:TRIG:SOUR IMM;") def trigger_on_bus(self): """ Configures the trigger to detect events based on the bus trigger, which can be activated by :code:`GET` or :code:`*TRG`. """ self.write(":ARM:COUN 1;:ARM:SOUR BUS;:TRIG:SOUR BUS;") def set_trigger_counts(self, arm, trigger): """ Sets the number of counts for both the sweeps (arm) and the points in those sweeps (trigger), where the total number of points can not exceed 2500 """ if arm * trigger > 2500 or arm * trigger < 0: raise RangeException("Keithley 2400 has a combined maximum " "of 2500 counts") if arm < trigger: self.write(":ARM:COUN %d;:TRIG:COUN %d" % (arm, trigger)) else: self.write(":TRIG:COUN %d;:ARM:COUN %d" % (trigger, arm)) def sample_continuously(self): """ Causes the instrument to continuously read samples and turns off any buffer or output triggering """ self.disable_buffer() self.disable_output_trigger() self.trigger_immediately() def set_timed_arm(self, interval): """ Sets up the measurement to be taken with the internal trigger at a variable sampling rate defined by the interval in seconds between sampling points """ if interval > 99999.99 or interval < 0.001: raise RangeException("Keithley 2400 can only be time" " triggered between 1 mS and 1 Ms") self.write(":ARM:SOUR TIM;:ARM:TIM %.3f" % interval) def trigger_on_external(self, line=1): """ Configures the measurement trigger to be taken from a specific line of an external trigger :param line: A trigger line from 1 to 4 """ cmd = ":ARM:SOUR TLIN;:TRIG:SOUR TLIN;" cmd += ":ARM:ILIN %d;:TRIG:ILIN %d;" % (line, line) self.write(cmd) def output_trigger_on_external(self, line=1, after='DEL'): """ Configures the output trigger on the specified trigger link line number, with the option of supplying the part of the measurement after which the trigger should be generated (default to delay, which is right before the measurement) :param line: A trigger line from 1 to 4 :param after: An event string that determines when to trigger """ self.write(":TRIG:OUTP %s;:TRIG:OLIN %d;" % (after, line)) def disable_output_trigger(self): """ Disables the output trigger for the Trigger layer """ self.write(":TRIG:OUTP NONE") @property def mean_voltage(self): """ Returns the mean voltage from the buffer """ return self.means[0] @property def max_voltage(self): """ Returns the maximum voltage from the buffer """ return self.maximums[0] @property def min_voltage(self): """ Returns the minimum voltage from the buffer """ return self.minimums[0] @property def std_voltage(self): """ Returns the voltage standard deviation from the buffer """ return self.standard_devs[0] @property def mean_current(self): """ Returns the mean current from the buffer """ return self.means[1] @property def max_current(self): """ Returns the maximum current from the buffer """ return self.maximums[1] @property def min_current(self): """ Returns the minimum current from the buffer """ return self.mininums[1] @property def std_current(self): """ Returns the current standard deviation from the buffer """ return self.standard_devs[1] @property def mean_resistance(self): """ Returns the mean resistance from the buffer """ return self.means[2] @property def max_resistance(self): """ Returns the maximum resistance from the buffer """ return self.maximums()[2] @property def min_resistance(self): """ Returns the minimum resistance from the buffer """ return self.minimums[2] @property def std_resistance(self): """ Returns the resistance standard deviation from the buffer """ return self.standard_devs[2] def status(self): return self.ask("status:queue?;") def RvsI(self, startI, stopI, stepI, compliance, delay=10.0e-3, backward=False): num = int(float(stopI-startI)/float(stepI)) + 1 currRange = 1.2*max(abs(stopI),abs(startI)) # self.write(":SOUR:CURR 0.0") self.write(":SENS:VOLT:PROT %g" % compliance) self.write(":SOUR:DEL %g" % delay) self.write(":SOUR:CURR:RANG %g" % currRange ) self.write(":SOUR:SWE:RANG FIX") self.write(":SOUR:CURR:MODE SWE") self.write(":SOUR:SWE:SPAC LIN") self.write(":SOUR:CURR:STAR %g" % startI) self.write(":SOUR:CURR:STOP %g" % stopI) self.write(":SOUR:CURR:STEP %g" % stepI) self.write(":TRIG:COUN %d" % num) if backward: currents = np.linspace(stopI, startI, num) self.write(":SOUR:SWE:DIR DOWN") else: currents = np.linspace(startI, stopI, num) self.write(":SOUR:SWE:DIR UP") self.connection.timeout = 30.0 self.enable_source() data = self.values(":READ?") self.check_errors() return zip(currents,data) def RvsIaboutZero(self, minI, maxI, stepI, compliance, delay=10.0e-3): data = [] data.extend(self.RvsI(minI, maxI, stepI, compliance=compliance, delay=delay)) data.extend(self.RvsI(minI, maxI, stepI, compliance=compliance, delay=delay, backward=True)) self.disable_source() data.extend(self.RvsI(-minI, -maxI, -stepI, compliance=compliance, delay=delay)) data.extend(self.RvsI(-minI, -maxI, -stepI, compliance=compliance, delay=delay, backward=True)) self.disable_source() return data def use_rear_terminals(self): """ Enables the rear terminals for measurement, and disables the front terminals. """ self.write(":ROUT:TERM REAR") def use_front_terminals(self): """ Enables the front terminals for measurement, and disables the rear terminals. """ self.write(":ROUT:TERM FRON") def shutdown(self): """ Ensures that the current or voltage is turned to zero and disables the output. """ log.info("Shutting down %s." % self.name) if self.source_mode == 'current': self.ramp_to_current(0.0) else: self.ramp_to_voltage(0.0) self.stop_buffer() self.disable_source() PyMeasure-0.5/pymeasure/instruments/keithley/keithley2000.py0000644000175000017500000005762513160054362024456 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument from pymeasure.instruments.validators import ( truncated_range, truncated_discrete_set, strict_discrete_set ) from pymeasure.adapters import VISAAdapter from .buffer import KeithleyBuffer class Keithley2000(Instrument, KeithleyBuffer): """ Represents the Keithley 2000 Multimeter and provides a high-level interface for interacting with the instrument. .. code-block:: python meter = Keithley2000("GPIB::1") meter.measure_voltage() print(meter.voltage) """ MODES = { 'current':'CURR:DC', 'current ac':'CURR:AC', 'voltage':'VOLT:DC', 'voltage ac':'VOLT:AC', 'resistance':'RES', 'resistance 4W':'FRES', 'period':'PER', 'frequency':'FREQ', 'temperature':'TEMP', 'diode':'DIOD', 'continuity':'CONT' } mode = Instrument.control( ":CONF?", ":CONF:%s", """ A string property that controls the configuration mode for measurements, which can take the values: :code:'current' (DC), :code:'current ac', :code:'voltage' (DC), :code:'voltage ac', :code:'resistance' (2-wire), :code:'resistance 4W' (4-wire), :code:'period', :code:'frequency', :code:'temperature', :code:'diode', and :code:'frequency'. """, validator=strict_discrete_set, values=MODES, map_values=True, get_process=lambda v: v.replace('"', '') ) ############### # Current (A) # ############### current = Instrument.measurement(":READ?", """ Reads a DC or AC current measurement in Amps, based on the active :attr:`~.Keithley2000.mode`. """ ) current_range = Instrument.control( ":SENS:CURR:RANG?", ":SENS:CURR:RANG:AUTO 0;:SENS:CURR:RANG %g", """ A floating point property that controls the DC current range in Amps, which can take values from 0 to 3.1 A. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 3.1] ) current_reference = Instrument.control( ":SENS:CURR:REF?", ":SENS:CURR:REF %g", """ A floating point property that controls the DC current reference value in Amps, which can take values from -3.1 to 3.1 A. """, validator=truncated_range, values=[-3.1, 3.1] ) current_nplc = Instrument.control( ":SENS:CURR:NPLC?", ":SENS:CURR:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the DC current measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) current_digits = Instrument.control( ":SENS:CURR:DIG?", ":SENS:CURR:DIG %d", """ An integer property that controls the number of digits in the DC current readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int, ) current_ac_range = Instrument.control( ":SENS:CURR:AC:RANG?", ":SENS:CURR:AC:RANG:AUTO 0;:SENS:CURR:AC:RANG %g", """ A floating point property that controls the AC current range in Amps, which can take values from 0 to 3.1 A. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 3.1] ) current_ac_reference = Instrument.control( ":SENS:CURR:AC:REF?", ":SENS:CURR:AC:REF %g", """ A floating point property that controls the AC current reference value in Amps, which can take values from -3.1 to 3.1 A. """, validator=truncated_range, values=[-3.1, 3.1] ) current_ac_nplc = Instrument.control( ":SENS:CURR:AC:NPLC?", ":SENS:CURR:AC:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the AC current measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) current_ac_digits = Instrument.control( ":SENS:CURR:AC:DIG?", ":SENS:CURR:AC:DIG %d", """ An integer property that controls the number of digits in the AC current readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) current_ac_bandwidth = Instrument.control( ":SENS:CURR:AC:DET:BAND?", ":SENS:CURR:AC:DET:BAND %g", """ A floating point property that sets the AC current detector bandwidth in Hz, which can take the values 3, 30, and 300 Hz. """, validator=truncated_discrete_set, values=[3, 30, 300] ) ############### # Voltage (V) # ############### voltage = Instrument.measurement(":READ?", """ Reads a DC or AC voltage measurement in Volts, based on the active :attr:`~.Keithley2000.mode`. """ ) voltage_range = Instrument.control( ":SENS:VOLT:RANG?", ":SENS:VOLT:RANG:AUTO 0;:SENS:VOLT:RANG %g", """ A floating point property that controls the DC voltage range in Volts, which can take values from 0 to 1010 V. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 1010] ) voltage_reference = Instrument.control( ":SENS:VOLT:REF?", ":SENS:VOLT:REF %g", """ A floating point property that controls the DC voltage reference value in Volts, which can take values from -1010 to 1010 V. """, validator=truncated_range, values=[-1010, 1010] ) voltage_nplc = Instrument.control( ":SENS:CURRVOLT:NPLC?", ":SENS:VOLT:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the DC voltage measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) voltage_digits = Instrument.control( ":SENS:VOLT:DIG?", ":SENS:VOLT:DIG %d", """ An integer property that controls the number of digits in the DC voltage readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) voltage_ac_range = Instrument.control( ":SENS:VOLT:AC:RANG?", ":SENS:VOLT:RANG:AUTO 0;:SENS:VOLT:AC:RANG %g", """ A floating point property that controls the AC voltage range in Volts, which can take values from 0 to 757.5 V. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 757.5] ) voltage_ac_reference = Instrument.control( ":SENS:VOLT:AC:REF?", ":SENS:VOLT:AC:REF %g", """ A floating point property that controls the AC voltage reference value in Volts, which can take values from -757.5 to 757.5 Volts. """, validator=truncated_range, values=[-757.5, 757.5] ) voltage_ac_nplc = Instrument.control( ":SENS:VOLT:AC:NPLC?", ":SENS:VOLT:AC:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the AC voltage measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) voltage_ac_digits = Instrument.control( ":SENS:VOLT:AC:DIG?", ":SENS:VOLT:AC:DIG %d", """ An integer property that controls the number of digits in the AC voltage readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) voltage_ac_bandwidth = Instrument.control( ":SENS:VOLT:AC:DET:BAND?", ":SENS:VOLT:AC:DET:BAND %g", """ A floating point property that sets the AC voltage detector bandwidth in Hz, which can take the values 3, 30, and 300 Hz. """, validator=truncated_discrete_set, values=[3, 30, 300] ) #################### # Resistance (Ohm) # #################### resistance = Instrument.measurement(":READ?", """ Reads a resistance measurement in Ohms for both 2-wire and 4-wire configurations, based on the active :attr:`~.Keithley2000.mode`. """ ) resistance_range = Instrument.control( ":SENS:RES:RANG?", ":SENS:RES:RANG:AUTO 0;:SENS:RES:RANG %g", """ A floating point property that controls the 2-wire resistance range in Ohms, which can take values from 0 to 120 MOhms. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 120e6] ) resistance_reference = Instrument.control( ":SENS:RES:REF?", ":SENS:RES:REF %g", """ A floating point property that controls the 2-wire resistance reference value in Ohms, which can take values from 0 to 120 MOhms. """, validator=truncated_range, values=[0, 120e6] ) resistance_nplc = Instrument.control( ":SENS:RES:NPLC?", ":SENS:RES:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the 2-wire resistance measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) resistance_digits = Instrument.control( ":SENS:RES:DIG?", ":SENS:RES:DIG %d", """ An integer property that controls the number of digits in the 2-wire resistance readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) resistance_4W_range = Instrument.control( ":SENS:FRES:RANG?", ":SENS:FRES:RANG:AUTO 0;:SENS:FRES:RANG %g", """ A floating point property that controls the 4-wire resistance range in Ohms, which can take values from 0 to 120 MOhms. Auto-range is disabled when this property is set. """, validator=truncated_range, values=[0, 120e6] ) resistance_4W_reference = Instrument.control( ":SENS:FRES:REF?", ":SENS:FRES:REF %g", """ A floating point property that controls the 4-wire resistance reference value in Ohms, which can take values from 0 to 120 MOhms. """, validator=truncated_range, values=[0, 120e6] ) resistance_4W_nplc = Instrument.control( ":SENS:FRES:NPLC?", ":SENS:FRES:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the 4-wire resistance measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) resistance_4W_digits = Instrument.control( ":SENS:FRES:DIG?", ":SENS:FRES:DIG %d", """ An integer property that controls the number of digits in the 4-wire resistance readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) ################## # Frequency (Hz) # ################## frequency = Instrument.measurement(":READ?", """ Reads a frequency measurement in Hz, based on the active :attr:`~.Keithley2000.mode`. """ ) frequency_reference = Instrument.control( ":SENS:FREQ:REF?", ":SENS:FREQ:REF %g", """ A floating point property that controls the frequency reference value in Hz, which can take values from 0 to 15 MHz. """, validator=truncated_range, values=[0, 15e6] ) frequency_digits = Instrument.control( ":SENS:FREQ:DIG?", ":SENS:FREQ:DIG %d", """ An integer property that controls the number of digits in the frequency readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) frequency_threshold = Instrument.control( ":SENS:FREQ:THR:VOLT:RANG?", ":SENS:FREQ:THR:VOLT:RANG %g", """ A floating point property that controls the voltage signal threshold level in Volts for the frequency measurement, which can take values from 0 to 1010 V. """, validator=truncated_range, values=[0, 1010] ) frequency_aperature = Instrument.control( ":SENS:FREQ:APER?", ":SENS:FREQ:APER %g", """ A floating point property that controls the frequency aperature in seconds, which sets the integration period and measurement speed. Takes values from 0.01 to 1.0 s. """, validator=truncated_range, values=[0.01, 1.0] ) ############## # Period (s) # ############## period = Instrument.measurement(":READ?", """ Reads a period measurement in seconds, based on the active :attr:`~.Keithley2000.mode`. """ ) period_reference = Instrument.control( ":SENS:PER:REF?", ":SENS:PER:REF %g", """ A floating point property that controls the period reference value in seconds, which can take values from 0 to 1 s. """, validator=truncated_range, values=[0, 1] ) period_digits = Instrument.control( ":SENS:PER:DIG?", ":SENS:PER:DIG %d", """ An integer property that controls the number of digits in the period readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) period_threshold = Instrument.control( ":SENS:PER:THR:VOLT:RANG?", ":SENS:PRE:THR:VOLT:RANG %g", """ A floating point property that controls the voltage signal threshold level in Volts for the period measurement, which can take values from 0 to 1010 V. """, validator=truncated_range, values=[0, 1010] ) period_aperature = Instrument.control( ":SENS:PER:APER?", ":SENS:PER:APER %g", """ A floating point property that controls the period aperature in seconds, which sets the integration period and measurement speed. Takes values from 0.01 to 1.0 s. """, validator=truncated_range, values=[0.01, 1.0] ) ################### # Temperature (C) # ################### temperature = Instrument.measurement(":READ?", """ Reads a temperature measurement in Celsius, based on the active :attr:`~.Keithley2000.mode`. """ ) temperature_reference = Instrument.control( ":SENS:TEMP:REF?", ":SENS:TEMP:REF %g", """ A floating point property that controls the temperature reference value in Celsius, which can take values from -200 to 1372 C. """, validator=truncated_range, values=[-200, 1372] ) temperature_nplc = Instrument.control( ":SENS:TEMP:NPLC?", ":SENS:TEMP:NPLC %g", """ A floating point property that controls the number of power line cycles (NPLC) for the temperature measurements, which sets the integration period and measurement speed. Takes values from 0.01 to 10, where 0.1, 1, and 10 are Fast, Medium, and Slow respectively. """ ) temperature_digits = Instrument.control( ":SENS:TEMP:DIG?", ":SENS:TEMP:DIG %d", """ An integer property that controls the number of digits in the temperature readings, which can take values from 4 to 7. """, validator=truncated_discrete_set, values=[4, 5, 6, 7], cast=int ) ########### # Trigger # ########### trigger_count = Instrument.control( ":TRIG:COUN?", ":TRIG:COUN %d", """ An integer property that controls the trigger count, which can take values from 1 to 9,999. """, validator=truncated_range, values=[1, 9999], cast=int ) trigger_delay = Instrument.control( ":TRIG:SEQ:DEL?", ":TRIG:SEQ:DEL %g", """ A floating point property that controls the trigger delay in seconds, which can take values from 1 to 9,999,999.999 s. """, validator=truncated_range, values=[0, 999999.999] ) def __init__(self, adapter, **kwargs): super(Keithley2000, self).__init__( adapter, "Keithley 2000 Multimeter", **kwargs ) # Set up data transfer format if isinstance(self.adapter, VISAAdapter): self.adapter.config( is_binary=False, datatype='float32', converter='f', separator=',' ) # TODO: Clean up error checking def check_errors(self): """ Read all errors from the instrument.""" while True: err = self.values(":SYST:ERR?") if int(err[0]) != 0: errmsg = "Keithley 2000: %s: %s" % (err[0],err[1]) log.error(errmsg + '\n') else: break def measure_voltage(self, max_voltage=1, ac=False): """ Configures the instrument to measure voltage, based on a maximum voltage to set the range, and a boolean flag to determine if DC or AC is required. :param max_voltage: A voltage in Volts to set the voltage range :param ac: False for DC voltage, and True for AC voltage """ if ac: self.mode = 'voltage ac' self.voltage_ac_range = max_voltage else: self.mode = 'voltage' self.voltage_range = max_voltage def measure_current(self, max_current=10e-3, ac=False): """ Configures the instrument to measure current, based on a maximum current to set the range, and a boolean flag to determine if DC or AC is required. :param max_current: A current in Volts to set the current range :param ac: False for DC current, and True for AC current """ if ac: self.mode = 'current ac' self.current_ac_range = max_current else: self.mode = 'current' self.current_range = max_current def measure_resistance(self, max_resistance=10e6, wires=2): """ Configures the instrument to measure voltage, based on a maximum voltage to set the range, and a boolean flag to determine if DC or AC is required. :param max_voltage: A voltage in Volts to set the voltage range :param ac: False for DC voltage, and True for AC voltage """ if wires == 2: self.mode = 'resistance' self.resistance_range = max_resistance elif wires == 4: self.mode = 'resistance 4W' self.resistance_4W_range = max_resistance else: raise ValueError("Keithley 2000 only supports 2 or 4 wire" "resistance meaurements.") def measure_period(self): """ Configures the instrument to measure the period. """ self.mode = 'period' def measure_frequency(self): """ Configures the instrument to measure the frequency. """ self.mode = 'frequency' def measure_temperature(self): """ Configures the instrument to measure the temperature. """ self.mode = 'temperature' def measure_diode(self): """ Configures the instrument to perform diode testing. """ self.mode = 'diode' def measure_continuity(self): """ Configures the instrument to perform continuity testing. """ self.mode = 'continuity' def _mode_command(mode=None): if mode is None: mode = self.mode return self.MODES[mode] def auto_range(self, mode=None): """ Sets the active mode to use auto-range, or can set another mode by its name. :param mode: A valid :attr:`~.Keithley2000.mode` name, or None for the active mode """ self.write(":SENS:%s:RANG:AUTO 1" % self._mode_command(mode)) def enable_reference(self, mode=None): """ Enables the reference for the active mode, or can set another mode by its name. :param mode: A valid :attr:`~.Keithley2000.mode` name, or None for the active mode """ self.write(":SENS:%s:REF:STAT 1" % self._mode_command(mode)) def disable_reference(self, mode=None): """ Disables the reference for the active mode, or can set another mode by its name. :param mode: A valid :attr:`~.Keithley2000.mode` name, or None for the active mode """ self.write(":SENS:%s:REF:STAT 0" % self._mode_command(mode)) def acquire_reference(self, mode=None): """ Sets the active value as the reference for the active mode, or can set another mode by its name. :param mode: A valid :attr:`~.Keithley2000.mode` name, or None for the active mode """ self.write(":SENS:%s:REF:ACQ" % self._mode_command(mode)) def enable_filter(self, mode=None, type='repeat', count=1): """ Enables the averaging filter for the active mode, or can set another mode by its name. :param mode: A valid :attr:`~.Keithley2000.mode` name, or None for the active mode :param type: The type of averaging filter, either 'repeat' or 'moving'. :param count: A number of averages, which can take take values from 1 to 100 """ self.write(":SENS:%s:AVER:STAT 1") self.write(":SENS:%s:AVER:TCON %s") self.write(":SENS:%s:AVER:COUN %d") def disable_filter(self, mode=None): """ Disables the averaging filter for the active mode, or can set another mode by its name. :param mode: A valid :attr:`~.Keithley2000.mode` name, or None for the active mode """ self.write(":SENS:%s:AVER:STAT 0" % self._mode_command(mode)) def local(self): """ Returns control to the instrument panel, and enables the panel if disabled. """ self.write(":SYST:LOC") def remote(self): """ Places the instrument in the remote state, which is does not need to be explicity called in general. """ self.write(":SYST:REM") def remote_lock(self): """ Disables and locks the front panel controls to prevent changes during remote operations. This is disabled by calling :meth:`~.Keithley2000.local`. """ self.write(":SYST:RWL") def reset(self): """ Resets the instrument state. """ self.write(":STAT:QUEUE:CLEAR;*RST;:STAT:PRES;:*CLS;") def beep(self, frequency, duration): """ Sounds a system beep. :param frequency: A frequency in Hz between 65 Hz and 2 MHz :param duration: A time in seconds between 0 and 7.9 seconds """ self.write(":SYST:BEEP %g, %g" % (frequency, duration)) PyMeasure-0.5/pymeasure/instruments/keithley/buffer.py0000644000175000017500000001114313137471263023577 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument from pymeasure.instruments.validators import truncated_range from pymeasure.adapters import PrologixAdapter import numpy as np from time import sleep, time class KeithleyBuffer(object): """ Implements the basic buffering capability found in many Keithley instruments. """ buffer_points = Instrument.control( ":TRAC:POIN?", ":TRAC:POIN %d", """ An integer property that controls the number of buffer points. This does not represent actual points in the buffer, but the configuration value instead. """, validator=truncated_range, values=[2, 1024], cast=int ) def config_buffer(self, points=64, delay=0): """ Configures the measurement buffer for a number of points, to be taken with a specified delay. :param points: The number of points in the buffer. :param delay: The delay time in seconds. """ # Enable measurement status bit # Enable buffer full measurement bit self.write(":STAT:PRES;*CLS;*SRE 1;:STAT:MEAS:ENAB 512;") self.write(":TRAC:CLEAR;") self.buffer_points = points self.trigger_count = points self.trigger_delay = delay self.write(":TRAC:FEED SENSE;:TRAC:FEED:CONT NEXT;") self.check_errors() def is_buffer_full(self): """ Returns True if the buffer is full of measurements. """ status_bit = int(self.ask("*STB?")) return status_bit == 65 def wait_for_buffer(self, should_stop=lambda: False, timeout=60, interval=0.1): """ Blocks the program, waiting for a full buffer. This function returns early if the :code:`should_stop` function returns True or the timeout is reached before the buffer is full. :param should_stop: A function that returns True when this function should return early :param timeout: A time in seconds after which this function should return early :param interval: A time in seconds for how often to check if the buffer is full """ # TODO: Use SRQ initially instead of constant polling #self.adapter.wait_for_srq() t = time() while not self.is_buffer_full(): sleep(interval) if should_stop(): return if (time()-t)>10: raise Exception("Timed out waiting for Keithley buffer to fill.") @property def buffer_data(self): """ Returns a numpy array of values from the buffer. """ self.write(":FORM:DATA ASCII") return np.array(self.values(":TRAC:DATA?"), dtype=np.float64) def start_buffer(self): """ Starts the buffer. """ self.write(":INIT") def reset_buffer(self): """ Resets the buffer. """ self.write(":STAT:PRES;*CLS;:TRAC:CLEAR;:TRAC:FEED:CONT NEXT;") def stop_buffer(self): """ Aborts the buffering measurement, by stopping the measurement arming and triggering sequence. If possible, a Selected Device Clear (SDC) is used. """ if type(self.adapter) is PrologixAdapter: self.write("++clr") else: self.write(":ABOR") def disable_buffer(self): """ Disables the connection between measurements and the buffer, but does not abort the measurement process. """ self.write(":TRAC:FEED:CONT NEV") PyMeasure-0.5/pymeasure/instruments/keithley/__init__.py0000644000175000017500000000232313137471263024065 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .keithley2000 import Keithley2000 from .keithley2400 import Keithley2400 PyMeasure-0.5/pymeasure/instruments/validators.py0000644000175000017500000000770413137471263022670 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # def strict_range(value, values): """ Provides a validator function that returns the value if its value is less than the maximum and greater than the minimum of the range. Otherwise it raises a ValueError. :param value: A value to test :param values: A range of values (range, list, etc.) :raises: ValueError if the value is out of the range """ if min(values) <= value <= max(values): return value else: raise ValueError('Value of {:g} is not in range [{:g},{:g}]'.format( value, min(values), max(values) )) def strict_discrete_set(value, values): """ Provides a validator function that returns the value if it is in the discrete set. Otherwise it raises a ValueError. :param value: A value to test :param values: A set of values that are valid :raises: ValueError if the value is not in the set """ if value in values: return value else: raise ValueError('Value of {} is not in the discrete set {}'.format( value, values )) def truncated_range(value, values): """ Provides a validator function that returns the value if it is in the range. Otherwise it returns the closest range bound. :param value: A value to test :param values: A set of values that are valid """ if min(values) <= value <= max(values): return value elif value > max(values): return max(values) else: return min(values) def truncated_discrete_set(value, values): """ Provides a validator function that returns the value if it is in the discrete set. Otherwise, it returns the smallest value that is larger than the value. :param value: A value to test :param values: A set of values that are valid """ # Force the values to be sorted values = list(values) values.sort() for v in values: if value <= v: return v return values[-1] def joined_validators(*validators): """ Join a list of validators together as a single. Expects a list of validator functions and values. :param validators: an iterable of other validators """ def validate(value, values): for validator, vals in zip(validators, values): try: return validator(value, vals) except (ValueError, TypeError): pass raise ValueError("Value of {} not in chained validator set".format(value)) return validate def discreteTruncate(number, discreteSet): """ Truncates the number to the closest element in the positive discrete set. Returns False if the number is larger than the maximum value or negative. """ if number < 0: return False discreteSet.sort() for item in discreteSet: if number <= item: return item return False PyMeasure-0.5/pymeasure/instruments/fwbell/0000755000175000017500000000000013171765525021416 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/fwbell/__init__.py0000644000175000017500000000225013137471263023521 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .fwbell5080 import FWBell5080 PyMeasure-0.5/pymeasure/instruments/fwbell/fwbell5080.py0000644000175000017500000001323113137471263023553 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument, RangeException from pymeasure.instruments.validators import truncated_discrete_set, strict_discrete_set from pymeasure.adapters import SerialAdapter from numpy import array, float64 class FWBell5080(Instrument): """ Represents the F.W. Bell 5080 Handheld Gaussmeter and provides a high-level interface for interacting with the instrument :param port: The serial port of the instrument .. code-block:: python meter = FWBell5080('/dev/ttyUSB0') # Connects over serial port /dev/ttyUSB0 (Linux) meter.units = 'gauss' # Sets the measurement units to Gauss meter.range = 3e3 # Sets the range to 3 kG print(meter.field) # Reads and prints a field measurement in G fields = meter.fields(100) # Samples 100 field measurements print(fields.mean(), fields.std()) # Prints the mean and standard deviation of the samples """ id = Instrument.measurement( "*IDN?", """ Reads the idenfitication information. """ ) field = Instrument.measurement( ":MEAS:FLUX?", """ Reads a floating point value of the field in the appropriate units. """, get_process=lambda v: v.split(' ')[0] # Remove units ) UNITS = { 'gauss':'DC:GAUSS', 'gauss ac':'AC:GAUSS', 'tesla':'DC:TESLA', 'tesla ac':'AC:TESLA', 'amp-meter':'DC:AM', 'amp-meter ac':'AC:AM' } units = Instrument.control( ":UNIT:FLUX?", ":UNIT:FLUX%s", """ A string property that controls the field units, which can take the values: 'gauss', 'gauss ac', 'tesla', 'tesla ac', 'amp-meter', and 'amp-meter ac'. The AC versions configure the instrument to measure AC. """, validator=strict_discrete_set, values=UNITS, map_values=True, get_process=lambda v: v.replace(' ', ':') # Make output consistent with input ) def __init__(self, port): super(FWBell5080, self).__init__( SerialAdapter(port, 2400, timeout=0.5), "F.W. Bell 5080 Handheld Gaussmeter" ) @property def range(self): """ A floating point property that controls the maximum field range in the active units. This can take the values of 300 G, 3 kG, and 30 kG for Gauss, 30 mT, 300 mT, and 3 T for Tesla, and 23.88 kAm, 238.8 kAm, and 2388 kAm for Amp-meter. """ i = self.values(":SENS:FLUX:RANG?", cast=int) units = self.units if 'gauss' in self.units: return [300, 3e3, 30e3][i] elif 'tesla' in self.units: return [30e-3, 300e-3, 3][i] elif 'amp-meter' in self.units: return [23.88e3, 238.8e3, 2388e3][i] @range.setter def range(self, value): units = self.units if 'gauss' in self.units: i = truncated_discrete_set(value, [300, 3e3, 30e3]) elif 'tesla' in self.units: i = truncated_discrete_set(value, [30e-3, 300e-3, 3]) elif 'amp-meter' in self.units: i = truncated_discrete_set(value, [23.88e3, 238.8e3, 2388e3]) self.write(":SENS:FLUX:RANG %d" % i) def read(self): """ Overwrites the :meth:`Instrument.read ` method to remove the last 2 characters from the output. """ return super(FWBell5080, self).read()[:-2] def ask(self, command): """ Overwrites the :meth:`Instrument.ask ` method to remove the last 2 characters from the output. """ return super(FWBell5080, self).ask()[:-2] def values(self, command): """ Overwrites the :meth:`Instrument.values ` method to remove the lastv2 characters from the output. """ return super(FWBell5080, self).values()[:-2] def reset(self): """ Resets the instrument. """ self.write("*OPC") def fields(self, samples=1): """ Returns a numpy array of field samples for a given sample number. :param samples: The number of samples to preform """ if samples < 1: raise Exception("F.W. Bell 5080 does not support samples less than 1.") else: data = [self.field for i in range(int(samples))] return array(data, dtype=float64) def auto_range(self): """ Enables the auto range functionality. """ self.write(":SENS:FLUX:RANG:AUTO") PyMeasure-0.5/pymeasure/instruments/comedi.py0000644000175000017500000001543413160054362021750 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.experiment import Procedure, Parameter, FloatParameter, IntegerParameter import numpy as np from time import time, sleep from threading import Event from importlib.util import find_spec if find_spec('pycomedi'): # Guard against pycomedi not being installed from pycomedi.subdevice import StreamingSubdevice from pycomedi.constant import * from pycomedi.constant import _NamedInt from pycomedi.channel import AnalogChannel from pycomedi.utility import inttrig_insn, Reader, CallbackReader def getAI(device, channel, range=None): """ Returns the analog input channel as specified for a given device """ ai = device.find_subdevice_by_type( SUBDEVICE_TYPE.ai, factory=StreamingSubdevice ).channel(channel, factory=AnalogChannel, aref=AREF.diff) if range is not None: ai.range = ai.find_range(unit=UNIT.volt, min=range[0], max=range[1]) return ai def getAO(device, channel, range=None): """ Returns the analog output channel as specified for a given device """ ao = device.find_subdevice_by_type( SUBDEVICE_TYPE.ao, factory=StreamingSubdevice ).channel(channel, factory=AnalogChannel, aref=AREF.diff) if range is not None: ao.range = ao.find_range(unit=UNIT.volt, min=range[0], max=range[1]) return ao def readAI(device, channel, range=None, count=1): """ Reads a single measurement (count==1) from the analog input channel of the device specified. Multiple readings can be preformed with count not equal to one, which are seperated by an arbitrary time """ ai = getAI(device, channel, range) converter = ai.get_converter() if count is 1: return converter.to_physical(ai.data_read()) else: return converter.to_physical(ai.data_read_n(count)) def writeAO(device, channel, voltage, range=None): """ Writes a single voltage to the analog output channel of the device specified """ ao = getAO(device, channel, range) converter = ao.get_converter() ao.data_write(converter.from_physical(voltage)) class SynchronousAI(object): def __init__(self, channels, period, samples): self.channels = channels self.samples = samples self.period = period self.scanPeriod = int(1e9*float(period)/float(samples)) # nano-seconds self.subdevice = self.channels[0].subdevice self.subdevice.cmd = self._command() def _command(self): """ Returns the command used to initiate and end the sampling """ command = self.subdevice.get_cmd_generic_timed(len(self.channels), self.scanPeriod) command.start_src = TRIG_SRC.int command.start_arg = 0 command.stop_src = TRIG_SRC.count command.stop_arg = self.samples command.chanlist = self.channels # Adding to remove chunk transfers (TRIG_WAKE_EOS) wake_eos = _NamedInt('wake_eos', 32) if wake_eos not in CMDF: CMDF.append(wake_eos) command.flags = CMDF.wake_eos return command def _verifyCommand(self): """ Checks the command over three times and allows comedi to correct the command given any device specific conflicts """ for i in range(3): rc = self.subdevice.command_test() # Verify command is correct if rc == None: break def measure(self, hasAborted=lambda:False): """ Initiates the scan after first checking the command and does not block, returns the starting timestamp """ self._verifyCommand() sleep(0.01) self.subdevice.command() length = len(self.channels) dtype = self.subdevice.get_dtype() converters = [c.get_converter() for c in self.channels] self.data = np.zeros((self.samples, length), dtype=np.float32) # Trigger AI self.subdevice.device.do_insn(inttrig_insn(self.subdevice)) # Measurement loop count = 0 size = int(self.data.itemsize/2)*length previous_bin_slice = b'' while not hasAborted() and self.samples > count: bin_slice = previous_bin_slice while len(bin_slice) < size: bin_slice += self.subdevice.device.file.read(size) previous_bin_slice = bin_slice[size:] bin_slice = bin_slice[:size] slice = np.fromstring( bin_slice, dtype=dtype, count=length ) if len(slice) != length: # Reading finished break # Convert to physical values for i, c in enumerate(converters): self.data[count,i] = c.to_physical(slice[i]) self.emit_progress(100.*count/self.samples) self.emit_data(self.data[count]) count += 1 # Cancel measurement if it is still running (abort event) if self.subdevice.get_flags().running: self.subdevice.cancel() """ Command for limited samples command = self.subdevice.get_cmd_generic_timed(len(self.channels), self.scanPeriod) command.start_src = TRIG_SRC.int command.start_arg = 0 command.stop_src = TRIG_SRC.count command.stop_arg = self.samples command.chanlist = self.channels Command for continuous AI command = self.subdevice.get_cmd_generic_timed(len(self.channels), self.scanPeriod) command.start_src = TRIG_SRC.int command.start_arg = 0 command.stop_src = TRIG_SRC.none command.stop_arg = 0 command.chanlist = self.channels """ PyMeasure-0.5/pymeasure/instruments/yokogawa/0000755000175000017500000000000013171765525021764 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/yokogawa/yokogawa7651.py0000644000175000017500000002164613137471263024506 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument from pymeasure.instruments.validators import ( truncated_discrete_set, strict_discrete_set, truncated_range ) from time import sleep import numpy as np import re class Yokogawa7651(Instrument): """ Represents the Yokogawa 7651 Programmable DC Source and provides a high-level for interacting with the instrument. .. code-block:: python yoko = Yokogawa7651("GPIB::1") yoko.apply_current() # Sets up to source current yoko.source_current_range = 10e-3 # Sets the current range to 10 mA yoko.compliance_voltage = 10 # Sets the compliance voltage to 10 V yoko.source_current = 0 # Sets the source current to 0 mA yoko.enable_source() # Enables the current output yoko.ramp_to_current(5e-3) # Ramps the current to 5 mA yoko.shutdown() # Ramps the current to 0 mA and disables output """ @staticmethod def _find(v, key): """ Returns a value by parsing a current panel setting output string array, which is returned with a call to "OS;E". This is used for Instrument.control methods, and should not be called directly by the user. """ status = ''.join(v.split("\r\n\n")[1:-1]) keys = re.findall('[^\dE+.-]+', status) values = re.findall('[\dE+.-]+', status) if key not in keys: raise ValueError("Invalid key used to search for status of Yokogawa 7561") else: return values[keys.index(key)] source_voltage = Instrument.control( "OD;E", "S%g;E", """ A floating point property that controls the source voltage in Volts, if that mode is active. """ ) source_current = Instrument.control( "OD;E", "S%g;E", """ A floating point property that controls the source current in Amps, if that mode is active. """ ) source_voltage_range = Instrument.control( "OS;E", "R%d;E", """ A floating point property that sets the source voltage range in Volts, which can take values: 10 mV, 100 mV, 1 V, 10 V, and 30 V. Voltages are truncted to an appropriate value if needed. """, validator=truncated_discrete_set, values={10e-3:2, 100e-3:3, 1:4, 10:5, 30:6}, map_values=True, get_process=lambda v: int(Yokogawa7651._find(v, 'R')) ) source_current_range = Instrument.control( "OS;E", "R%d;E", """ A floating point property that sets the current voltage range in Amps, which can take values: 1 mA, 10 mA, and 100 mA. Currents are truncted to an appropriate value if needed. """, validator=truncated_discrete_set, values={1e-3:4, 10e-3:5, 100e-3:6}, map_values=True, get_process=lambda v: int(Yokogawa7651._find(v, 'R')) ) source_mode = Instrument.control( "OS;E", "F%d;E", """ A string property that controls the source mode, which can take the values 'current' or 'voltage'. The convenience methods :meth:`~.Yokogawa7651.apply_current` and :meth:`~.Yokogawa7651.apply_voltage` can also be used. """, validator=strict_discrete_set, values={'current':5, 'voltage':1}, map_values=True, get_process=lambda v: int(Yokogawa7651._find(v, 'F')) ) compliance_voltage = Instrument.control( "OS;E", "LV%g;E", """ A floating point property that sets the compliance voltage in Volts, which can take values between 1 and 30 V. """, validator=truncated_range, values=[1, 30], get_process=lambda v: int(Yokogawa7651._find(v, 'LV')) ) compliance_current = Instrument.control( "OS;E", "LA%g;E", """ A floating point property that sets the compliance current in Amps, which can take values from 5 to 120 mA. """, validator=truncated_range, values=[5e-3, 120e-3], get_process=lambda v: float(Yokogawa7651._find(v, 'LA'))*1e-3, # converts A to mA set_process=lambda v: v*1e3, # converts mA to A ) def __init__(self, adapter, **kwargs): super(Yokogawa7651, self).__init__( adapter, "Yokogawa 7651 Programmable DC Source", **kwargs ) self.write("H0;E") # Set no header in output data @property def id(self): """ Returns the identification of the instrument """ return self.ask("OS;E").split('\r\n\n')[0] @property def source_enabled(self): """ Reads a boolean value that is True if the source is enabled, determined by checking if the 5th bit of the OC flag is a binary 1. """ oc = int(self.ask("OC;E")[5:]) return oc & 0b10000 def enable_source(self): """ Enables the source of current or voltage depending on the configuration of the instrument. """ self.write("O1;E") def disable_source(self): """ Disables the source of current or voltage depending on the configuration of the instrument. """ self.write("O0;E") def apply_current(self, max_current=1e-3, complinance_voltage=1): """ Configures the instrument to apply a source current, which can take optional parameters that defer to the :attr:`~.Yokogawa7651.source_current_range` and :attr:`~.Yokogawa7651.compliance_voltage` properties. """ self.source_mode = 'current' self.source_current_range = max_current self.complinance_voltage = complinance_voltage def apply_voltage(self, max_voltage=1, complinance_current=10e-3): """ Configures the instrument to apply a source voltage, which can take optional parameters that defer to the :attr:`~.Yokogawa7651.source_voltage_range` and :attr:`~.Yokogawa7651.compliance_current` properties. """ self.source_mode = 'voltage' self.source_voltage_range = max_voltage self.complinance_current = compliance_current def ramp_to_current(self, current, steps=25, duration=0.5): """ Ramps the current to a value in Amps by traversing a linear spacing of current steps over a duration, defined in seconds. :param steps: A number of linear steps to traverse :param duration: A time in seconds over which to ramp """ start_current = self.source_current stop_current = current pause = duration/steps if (start_current != stop_current): currents = np.linspace(start_current, stop_current, steps) for current in currents: self.source_current = current sleep(pause) def ramp_to_voltage(self, voltage, steps=25, duration=0.5): """ Ramps the voltage to a value in Volts by traversing a linear spacing of voltage steps over a duration, defined in seconds. :param steps: A number of linear steps to traverse :param duration: A time in seconds over which to ramp """ start_voltage = self.source_voltage stop_voltage = voltage pause = duration/steps if (start_voltage != stop_voltage): voltages = np.linspace(start_voltage, stop_voltage, steps) for voltage in voltages: self.source_voltage = voltage sleep(pause) def shutdown(self): """ Shuts down the instrument, and ramps the current or voltage to zero before disabling the source. """ # Since voltage and current are set the same way, this # ramps either the current or voltage to zero self.ramp_to_current(0.0, steps=25) self.source_current = 0.0 self.disable_source() super(Yokogawa7651, self).shutdown()PyMeasure-0.5/pymeasure/instruments/yokogawa/__init__.py0000644000175000017500000000225413137471263024073 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .yokogawa7651 import Yokogawa7651 PyMeasure-0.5/pymeasure/instruments/ami/0000755000175000017500000000000013171765525020711 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/ami/__init__.py0000644000175000017500000000224013137471263023013 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .ami430 import AMI430 PyMeasure-0.5/pymeasure/instruments/ami/ami430.py0000644000175000017500000001651613137471263022264 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument from pymeasure.adapters import VISAAdapter from pymeasure.instruments.validators import ( truncated_discrete_set, strict_discrete_set, truncated_range ) from time import sleep, time import numpy as np import re class AMI430(Instrument): """ Represents the AMI 430 Power supply and provides a high-level for interacting with the instrument. .. code-block:: python magnet = AMI430("TCPIP::web.address.com::7180::SOCKET") magnet.coilconst = 1.182 # kGauss/A magnet.voltage_limit = 2.2 # Sets the voltage limit in V magnet.target_current = 10 # Sets the target current to 10 A magnet.target_field = 1 # Sets target field to 1 kGauss magnet.ramp_rate_current = 0.0357 # Sets the ramp rate in A/s magnet.ramp_rate_field = 0.0422 # Sets the ramp rate in kGauss/s magnet.ramp # Initiates the ramping magnet.pause # Pauses the ramping magnet.status # Returns the status of the magnet magnet.ramp_to_current(5) # Ramps the current to 5 A magnet.shutdown() # Ramps the current to zero and disables output """ def __init__(self, resourceName, **kwargs): adapter = VISAAdapter(resourceName, read_termination='\n') super(ami430, self).__init__( adapter, "AMI superconducting magnet power supply.", includeSCPI=True, **kwargs ) # Read twice in order to remove welcome/connect message self.read() self.read() maximumfield = 1.00 maximumcurrent = 50.63 coilconst = Instrument.control( "COIL?", "CONF:COIL %g", """ A floating point property that sets the coil contant in kGauss/A. """ ) voltage_limit = Instrument.control( "VOLT:LIM?", "CONF:VOLT:LIM %g", """ A floating point property that sets the voltage limit for charging/discharging the magnet. """ ) target_current = Instrument.control( "CURR:TARG?", "CONF:CURR:TARG %g", """ A floating point property that sets the target current in A for the magnet. """ ) target_field = Instrument.control( "FIELD:TARG?", "CONF:FIELD:TARG %g", """ A floating point property that sets the target field in kGauss for the magnet. """ ) ramp_rate_current = Instrument.control( "RAMP:RATE:CURR:1?", "CONF:RAMP:RATE:CURR 1,%g", """ A floating point property that sets the current ramping rate in A/s. """ ) ramp_rate_field = Instrument.control( "RAMP:RATE:FIELD:1?", "CONF:RAMP:RATE:FIELD 1,%g,1.00", """ A floating point property that sets the field ramping rate in kGauss/s. """ ) magnet_current = Instrument.measurement("CURR:MAG?", """ Reads the current in Amps of the magnet. """ ) supply_current = Instrument.measurement("CURR:SUPP?", """ Reads the current in Amps of the power supply. """ ) field = Instrument.measurement("FIELD:MAG?", """ Reads the field in kGauss of the magnet. """ ) state = Instrument.measurement("STATE?", """ Reads the field in kGauss of the magnet. """ ) def zero(self): """ Initiates the ramping of the magnetic field to zero current/field with ramping rate previously set. """ self.write("ZERO") def pause(self): """ Pauses the ramping of the magnetic field. """ self.write("PAUSE") def ramp(self): """ Initiates the ramping of the magnetic field to set current/field with ramping rate previously set. """ self.write("RAMP") def has_persistent_switch_enabled(self): """ Returns a boolean if the persistent switch is enabled. """ return bool(self.ask("PSwitch?")) def enable_persistent_switch(self): """ Enables the persistent switch. """ self.write("PSwitch 1") def disable_persistent_switch(self): """ Disables the persistent switch. """ self.write("PSwitch 0") @property def magnet_status(self): STATES = { 1: "RAMPING", 2: "HOLDING", 3: "PAUSED", 4: "Ramping in MANUAL UP", 5: "Ramping in MANUAL DOWN", 6: "ZEROING CURRENT in progress", 7: "QUENCH!!!", 8: "AT ZERO CURRENT", 9: "Heating Persistent Switch", 10: "Cooling Persistent Switch" } return STATES[self.state] def ramp_to_current(self, current, rate): """ Heats up the persistent switch and ramps the current with set ramp rate. """ self.enable_persistent_switch() self.target_current = current self.ramp_rate_current = rate self.wait_for_holding() self.ramp() def ramp_to_field(self, field, rate): """ Heats up the persistent switch and ramps the current with set ramp rate. """ self.enable_persistent_switch() self.target_field = field self.ramp_rate_field = rate self.wait_for_holding() self.ramp() def wait_for_holding(self, should_stop=lambda: False, timeout=800, interval=0.1): """ """ t = time() while self.state != 2 and self.state != 3 and self.state != 8: sleep(interval) if should_stop(): return if (time()-t) > timeout: raise Exception("Timed out waiting for AMI430 switch to warm up.") def shutdown(self, ramp_rate=0.0357): """ Turns on the persistent switch, ramps down the current to zero, and turns off the persistent switch. """ self.enable_persistent_switch() self.wait_for_holding() self.ramp_rate_current = ramp_rate self.zero() self.wait_for_holding() self.disable_persistent_switch()PyMeasure-0.5/pymeasure/instruments/hp/0000755000175000017500000000000013171765525020552 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/hp/__init__.py0000644000175000017500000000224313137471263022657 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .hp33120A import HP33120APyMeasure-0.5/pymeasure/instruments/hp/hp33120A.py0000644000175000017500000001134113160054362022211 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument from pymeasure.instruments.validators import strict_discrete_set class HP33120A(Instrument): """ Represents the Hewlett Packard 33120A Arbitrary Waveform Generator and provides a high-level interface for interacting with the instrument. """ SHAPES = { 'sinusoid':'SIN', 'square':'SQU', 'triangle':'TRI', 'ramp':'RAMP', 'noise':'NOIS', 'dc':'DC', 'user':'USER' } shape = Instrument.control( "SOUR:FUNC:SHAP?", "SOUR:FUNC:SHAP %s", """ A string property that controls the shape of the wave, which can take the values: sinusoid, square, triangle, ramp, noise, dc, and user. """, validator=strict_discrete_set, values=SHAPES, map_values=True ) frequency = Instrument.control( "SOUR:FREQ?", "SOUR:FREQ %g", """ A floating point property that controls the frequency of the output in Hz. The allowed range depends on the waveform shape and can be queried with :attr:`~.max_frequency` and :attr:`~.min_frequency`. """ ) max_frequency = Instrument.measurement( "SOUR:FREQ? MAX", """ Reads the maximum :attr:`~.HP33120A.frequency` in Hz for the given shape """ ) min_frequency = Instrument.measurement( "SOUR:FREQ? MIN", """ Reads the minimum :attr:`~.HP33120A.frequency` in Hz for the given shape """ ) amplitude = Instrument.control( "SOUR:VOLT?", "SOUR:VOLT %g", """ A floating point property that controls the voltage amplitude of the output signal. The default units are in peak-to-peak Volts, but can be controlled by :attr:`~.amplitude_units`. The allowed range depends on the waveform shape and can be queried with :attr:`~.max_amplitude` and :attr:`~.min_amplitude`. """ ) max_amplitude = Instrument.measurement( "SOUR:VOLT? MAX", """ Reads the maximum :attr:`~.amplitude` in Volts for the given shape """ ) min_amplitude = Instrument.measurement( "SOUR:VOLT? MIN", """ Reads the minimum :attr:`~.amplitude` in Volts for the given shape """ ) offset = Instrument.control( "SOUR:VOLT:OFFS?", "SOUR:VOLT:OFFS %g", """ A floating point property that controls the amplitude voltage offset in Volts. The allowed range depends on the waveform shape and can be queried with :attr:`~.max_offset` and :attr:`~.min_offset`. """ ) max_offset = Instrument.measurement( "SOUR:VOLT:OFFS? MAX", """ Reads the maximum :attr:`~.offset` in Volts for the given shape """ ) min_offset = Instrument.measurement( "SOUR:VOLT:OFFS? MIN", """ Reads the minimum :attr:`~.offset` in Volts for the given shape """ ) AMPLITUDE_UNITS = {'Vpp':'VPP', 'Vrms':'VRMS', 'dBm':'DBM', 'default':'DEF'} amplitude_units = Instrument.control( "SOUR:VOLT:UNIT?", "SOUR:VOLT:UNIT %s", """ A string property that controls the units of the amplitude, which can take the values Vpp, Vrms, dBm, and default. """, validator=strict_discrete_set, values=AMPLITUDE_UNITS, map_values=True ) def __init__(self, resourceName, **kwargs): super(HP33120A, self).__init__( resourceName, "Hewlett Packard 33120A Function Generator", **kwargs ) self.amplitude_units = 'Vpp' def beep(self): """ Causes a system beep. """ self.write("SYST:BEEP") PyMeasure-0.5/pymeasure/instruments/parker/0000755000175000017500000000000013171765525021427 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/parker/parkerGV6.py0000644000175000017500000001675113137471263023615 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument from pymeasure.adapters import SerialAdapter from time import sleep import re class ParkerGV6(Instrument): """ Represents the Parker Gemini GV6 Servo Motor Controller and provides a high-level interface for interacting with the instrument """ degrees_per_count = 0.00045 # 90 deg per 200,000 count def __init__(self, port): super(ParkerGV6, self).__init__( SerialAdapter(port, 9600, timeout=0.5), "Parker GV6 Motor Controller" ) self.setDefaults() def write(self, command): """ Overwrites the Insturment.write command to provide the correct line break syntax """ self.connection.write(command + "\r") def read(self): """ Overwrites the Instrument.read command to provide the correct functionality """ return re.sub(r'\r\n\n(>|\?)? ', '', "\n".join(self.readlines())) def set_defaults(self): """ Sets up the default values for the motor, which is run upon construction """ self.echo = False self.set_hardware_limits(False, False) self.use_absolute_position() self.average_acceleration = 1 self.acceleration = 1 self.velocity = 3 def reset(self): """ Resets the motor controller while blocking and (CAUTION) resets the absolute position value of the motor """ self.write("RESET") sleep(5) self.setDefault() self.enable() def enable(self): """ Enables the motor to move """ self.write("DRIVE1") def disable(self): """ Disables the motor from moving """ self.write("DRIVE0") @property def status(self): """ Returns a list of the motor status in readable format """ return self.ask("TASF").split("\r\n\n") def is_moving(self): """ Returns True if the motor is currently moving """ return self.position is None @property def angle(self): """ Returns the angle in degrees based on the position and whether relative or absolute positioning is enabled, returning None on error """ position = self.position if position is not None: return position*self.degrees_per_count else: return None @angle.setter def angle(self, angle): """ Gives the motor a setpoint in degrees based on an angle from a relative or absolution position """ self.position = int(angle*self.degrees_per_count**-1) @property def angle_error(self): """ Returns the angle error in degrees based on the position error, or returns None on error """ position_error = self.position_error if position_error is not None: return position_error*self.degrees_per_count else: return None @property def position(self): """ Returns an integer number of counts that correspond to the angular position where 1 revolution equals 4000 counts """ match = re.search(r'(?<=TPE)-?\d+', self.ask("TPE")) if match is None: return None else: return int(match.group(0)) @position.setter def position(self, counts): # in counts: 4000 count = 1 rev """ Gives the motor a setpoint in counts where 4000 counts equals 1 revolution """ self.write("D" + str(int(counts))) @property def position_error(self): """ Returns the error in the number of counts that corresponds to the error in the angular position where 1 revolution equals 4000 counts """ match = re.search(r'(?<=TPER)-?\d+', self.ask("TPER")) if match is None: return None else: return int(match.group(0)) def move(self): """ Initiates the motor to move to the setpoint """ self.write("GO") def stop(self): """ Stops the motor during movement """ self.write("S") def kill(self): """ Stops the motor """ self.write("K") def use_absolute_position(self): """ Sets the motor to accept setpoints from an absolute zero position """ self.write("MA1") self.write("MC0") def use_relative_position(self): """ Sets the motor to accept setpoints that are relative to the last position """ self.write("MA0") self.write("MC0") def set_hardware_limits(self, positive=True, negative=True): """ Enables (True) or disables (False) the hardware limits for the motor """ if positive and negative: self.write("LH3") elif positive and not negative: self.write("LH2") elif not positive and negative: self.write("LH1") else: self.write("LH0") def set_software_limits(self, positive, negative): """ Sets the software limits for motion based on the count unit where 4000 counts is 1 revolution """ self.write("LSPOS%d" % int(positive)) self.write("LSNEG%d" % int(negative)) def echo(self, enable=False): """ Enables (True) or disables (False) the echoing of all commands that are sent to the instrument """ if enable: self.write("ECHO1") else: self.write("ECHO0") @property def acceleration(self): pass # TODO: Implement acceleration return value @acceleration.setter def acceleration(self, acceleration): """ Sets the acceleration setpoint in revolutions per second squared """ self.write("A" + str(float(acceleration))) @property def average_acceleration(self): pass # TODO: Implement average_acceleration return value @average_acceleration.setter def average_acceleration(self, acceleration): """ Sets the average acceleration setpoint in revolutions per second squared """ self.write("AA" + str(float(acceleration))) @property def velocity(self): pass # TODO: Implement velocity return value @velocity.setter def velocity(self, velocity): # in revs/s """ Sets the velocity setpoint in revolutions per second """ self.write("V" + str(float(velocity))) PyMeasure-0.5/pymeasure/instruments/parker/__init__.py0000644000175000017500000000224613137471263023537 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .parkerGV6 import ParkerGV6 PyMeasure-0.5/pymeasure/instruments/signalrecovery/0000755000175000017500000000000013171765525023177 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/signalrecovery/dsp7265.py0000644000175000017500000002136013137471263024660 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument from pymeasure.instruments.validators import truncated_discrete_set from time import sleep import numpy as np class DSP7265(Instrument): """This is the class for the DSP 7265 lockin amplifier""" voltage = Instrument.control( "OA.", "OA. %g", """ A floating point property that represents the voltage in Volts. This property can be set. """ ) frequency = Instrument.control( "OF.", "OF. %g", """ A floating point property that represents the lock-in frequency in Hz. This property can be set. """ ) dac1 = Instrument.control( "DAC. 1", "DAC. 1 %g", """ A floating point property that represents the output value on DAC1 in Volts. This property can be set. """ ) dac2 = Instrument.control( "DAC. 2", "DAC. 2 %g", """ A floating point property that represents the output value on DAC2 in Volts. This property can be set. """ ) dac3 = Instrument.control( "DAC. 3", "DAC. 3 %g", """ A floating point property that represents the output value on DAC3 in Volts. This property can be set. """ ) dac4 = Instrument.control( "DAC. 4", "DAC. 4 %g", """ A floating point property that represents the output value on DAC4 in Volts. This property can be set. """ ) harmonic = Instrument.control( "REFN", "REFN %d", """ An integer property that represents the reference harmonic mode control, taking values from 1 to 65535. This property can be set. """ ) phase = Instrument.control( "REFP.", "REFP. %g", """ A floating point property that represents the reference harmonic phase in degrees. This property can be set. """ ) x = Instrument.measurement("X.", """ Reads the X value in Volts """ ) y = Instrument.measurement("Y.", """ Reads the Y value in Volts """ ) xy = Instrument.measurement("X.Y.", """ Reads both the X and Y values in Volts """ ) mag = Instrument.measurement("Mag.", """ Reads the magnitude in Volts """ ) adc1 = Instrument.measurement("ADC. 1", """ Reads the input value of ADC1 in Volts """ ) adc2 = Instrument.measurement("ADC. 2", """ Reads the input value of ADC2 in Volts """ ) id = Instrument.measurement("ID", """ Reads the instrument identification """ ) sensitivity = Instrument.control( "SEN.", "SEN %d", """ A floating point property that controls the sensitivity range in Volts, which can take discrete values from 2 nV to 1 V. This property can be set. """, validator=truncated_discrete_set, values=[ 0.0, 2.0e-9, 5.0e-9, 10.0e-9, 20.0e-9, 50.0e-9, 100.0e-9, 200.0e-9, 500.0e-9, 1.0e-6, 2.0e-6, 5.0e-6, 10.0e-6, 20.0e-6, 50.0e-6, 100.0e-6, 200.0e-6, 500.0e-6, 1.0e-3, 2.0e-3, 5.0e-3, 10.0e-3, 20.0e-3, 50.0e-3, 100.0e-3, 200.0e-3, 500.0e-3, 1.0 ] ) slope = Instrument.control( "SLOPE", "SLOPE %d", """ A integer property that controls the filter slope in dB/octave, which can take the values 6, 12, 18, or 24 dB/octave. This property can be set. """, validator=truncated_discrete_set, values=[6, 12, 18, 24], map_values=True ) time_constant = Instrument.control( "TC.", "TC %d", """ A floating point property that controls the time constant in seconds, which takes values from 10 microseconds to 50,000 seconds. This property can be set. """, validator=truncated_discrete_set, values=[ 10.0e-6, 20.0e-6, 40.0e-6, 80.0e-6, 160.0e-6, 320.0e-6, 640.0e-6, 5.0e-3, 10.0e-3, 20.0e-3, 50.0e-3, 100.0e-3, 200.0e-3, 500.0e-3, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1.0e3, 2.0e3, 5.0e3, 10.0e3, 20.0e3, 50.0e3 ] ) def __init__(self, resourceName, **kwargs): super(DSP7265, self).__init__( resourceName, "Signal Recovery DSP 7265", **kwargs ) self.curve_bits = { 'x': 1, 'y': 2, 'mag': 4, 'phase': 8, 'ADC1': 32, 'ADC2': 64, 'ADC3': 128 } # Pre-condition self.adapter.config(datatype = 'str', converter = 's') def values(self, command): """ Rewrite the method because of extra character in return string.""" result = self.ask(command).strip() result = result.replace('\x00','') # Remove extra unicode character try: return [float(x) for x in result.split(",")] except: return result def setDifferentialMode(self, lineFiltering=True): self.write("VMODE 3") self.write("LF %d 0" % 3 if lineFiltering else 0) def setChannelAMode(self): self.write("VMODE 1") @property def adc3(self): # 50,000 for 1V signal over 1 s integral = self.values("ADC 3") return float(integral)/(50000.0*self.adc3_time) @property def adc3_time(self): # Returns time in seconds return self.values("ADC3TIME")/1000.0 @adc3_time.setter def adc3_time(self, value): # Takes time in seconds self.write("ADC3TIME %g" % int(1000*value)) sleep(value*1.2) @property def auto_gain(self): return (int(self.values("AUTOMATIC")) == 1) @auto_gain.setter def auto_gain(self, value): if value: self.write("AUTOMATIC 1") else: self.write("AUTOMATIC 0") def auto_sensitivity(self): self.write("AS") def auto_phase(self): self.write("AQN") @property def gain(self): return self.values("ACGAIN") @gain.setter def gain(self, value): self.write("ACGAIN %d" % int(value/10.0)) @property def reference(self): return "external" if int(self.ask("IE")) == 2 else "internal" @reference.setter def reference(self, value): if value == "internal": val = 0 elif value == "external": val = 2 else: raise Exception("Incorrect value for reference type." " Must be either internal or extenal.") self.write("IE %d" % val) def set_buffer(self, points, quantities=['x'], interval=10.0e-3): num = 0 for q in quantities: num += self.curve_bits[q] self.points = points self.write("CBD %d" % int(num)) self.write("LEN %d" % int(points)) # interval in increments of 5ms interval = int(float(interval)/5.0e-3) self.write("STR %d" % interval) self.write("NC") def start_buffer(self): self.write("TD") def get_buffer(self, quantity='x', timeout=1.00, average=False): count = 0 maxCount = int(timeout/0.05) failed = False while int(self.values("M")) != 0: # Sleeping sleep(0.05) if count > maxCount: # Count reached max value, wait longer before asking! failed = True break if not failed: data = [] # Getting data for i in range(self.length): val = self.values("DC. %d" % self.curve_bits[quantity]) data.append(val) if average: return np.mean(data) else: return data else: return [0.0] PyMeasure-0.5/pymeasure/instruments/signalrecovery/__init__.py0000644000175000017500000000224213137471263025303 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .dsp7265 import DSP7265 PyMeasure-0.5/pymeasure/instruments/thorlabs/0000755000175000017500000000000013171765525021761 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/thorlabs/thorlabspm100usb.py0000644000175000017500000000777113137471263025450 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from pymeasure.instruments import Instrument, RangeException class ThorlabsPM100USB(Instrument): """Represents Thorlabs PM100USB powermeter""" # TODO: refactor to check if the sensor wavelength is adjustable wavelength = Instrument.control("SENSE:CORR:WAV?", "SENSE:CORR:WAV %g", "Wavelength in nm; not set outside of range") # TODO: refactor to check if the sensor is a power sensor power = Instrument.measurement("MEAS:POW?", "Power, in Watts") wavelength_min = Instrument.measurement("SENS:CORR:WAV? MIN", "Get minimum wavelength, in nm") wavelength_max = Instrument.measurement("SENS:CORR:WAV? MAX", "Get maximum wavelength, in nm") def __init__(self, adapter, **kwargs): super(ThorlabsPM100USB, self).__init__( adapter, "ThorlabsPM100USB powermeter", **kwargs) self.timout = 3000 self.sensor() def measure_power(self, wavelength): """Set wavelength in nm and get power in W If wavelength is out of range it will be set to range limit""" if wavelength < self.wavelength_min: raise RangeException("Wavelength %.2f nm out of range: using minimum wavelength: %.2f nm" % ( wavelength, self.wavelength_min)) # explicit setting wavelenghth, althought it would be automatically set wavelength = self.wavelength_min if wavelength > self.wavelength_max: raise RangeException("Wavelength %.2f nm out of range: using maximum wavelength: %.2f nm" % ( wavelength, self.wavelength_max)) wavelength = self.wavelength_max self.wavelength = wavelength return self.power def sensor(self): "Get sensor info" response = self.ask("SYST:SENSOR:IDN?").split(',') self.sensor_name = response[0] self.sensor_sn = response[1] self.sensor_cal_msg = response[2] self.sensor_type = response[3] self.sensor_subtype = response[4] self._flags_str = response[-1][:-1] # interpretation of the flags # rough trick using bin repr, maybe something more elegant exixts # (bitshift, bitarray?) self._flags = tuple( map(lambda x: x == '1', bin(int(self._flags_str))[2:])) # setting the flags; _dn are empty self.is_power, self.is_energy, _d4, _d8, \ self.resp_settable, self.wavelength_settable, self.tau_settable, _d128, self.temperature_sens = self._flags @property def energy(self): if self.is_energy: return self.values("MEAS:ENER?") else: raise Exception("%s is not an energy sensor" % self.sensor_name) return 0 @energy.setter def energy(self, val): raise Exception("Energy not settable!") PyMeasure-0.5/pymeasure/instruments/thorlabs/__init__.py0000644000175000017500000000226313137471263024070 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .thorlabspm100usb import ThorlabsPM100USBPyMeasure-0.5/pymeasure/instruments/tektronix/0000755000175000017500000000000013171765525022172 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/tektronix/tds2000.py0000644000175000017500000000655613137471263023647 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument class TDS2000(Instrument): """ Represents the Tektronix TDS 2000 Oscilloscope and provides a high-level for interacting with the instrument """ class Measurement(object): SOURCE_VALUES = ['CH1', 'CH2', 'MATH'] TYPE_VALUES = [ 'FREQ', 'MEAN', 'PERI', 'PHA', 'PK2', 'CRM', 'MINI', 'MAXI', 'RIS', 'FALL', 'PWI', 'NWI' ] UNIT_VALUES = ['V', 's', 'Hz'] def __init__(self, parent, preamble="MEASU:IMM:"): self.parent = parent self.preamble = preamble @property def value(self): return self.parent.values("%sVAL?" % self.preamble) @property def source(self): return self.parent.ask("%sSOU?" % self.preamble).strip() @source.setter def source(self, value): if value in TDS2000.Measurement.SOURCE_VALUES: self.parent.write("%sSOU %s" % (self.preamble, value)) else: raise ValueError("Invalid source ('%s') provided to %s" % ( self.parent, value)) @property def type(self): return self.parent.ask("%sTYP?" % self.preamble).strip() @type.setter def type(self, value): if value in TDS2000.Measurement.TYPE_VALUES: self.parent.write("%sTYP %s" % (self.preamble, value)) else: raise ValueError("Invalid type ('%s') provided to %s" % ( self.parent, value)) @property def unit(self): return self.parent.ask("%sUNI?" % self.preamble).strip() @unit.setter def unit(self, value): if value in TDS2000.Measurement.UNIT_VALUES: self.parent.write("%sUNI %s" % (self.preamble, value)) else: raise ValueError("Invalid unit ('%s') provided to %s" % ( self.parent, value)) def __init__(self, resourceName, **kwargs): super(TDS2000, self).__init__( resourceName, "Tektronix TDS 2000 Oscilliscope", **kwargs ) self.measurement = TDS2000.Measurement(self) PyMeasure-0.5/pymeasure/instruments/tektronix/__init__.py0000644000175000017500000000224213137471263024276 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .tds2000 import TDS2000 PyMeasure-0.5/pymeasure/instruments/instrument.py0000644000175000017500000003431313160054362022715 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import re import numpy as np from pymeasure.adapters import FakeAdapter from pymeasure.adapters.visa import VISAAdapter log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class Instrument(object): """ This provides the base class for all Instruments, which is independent of the particular Adapter used to connect for communication to the instrument. It provides basic SCPI commands by default, but can be toggled with :code:`includeSCPI`. :param adapter: An :class:`Adapter` object :param name: A string name :param includeSCPI: A boolean, which toggles the inclusion of standard SCPI commands """ # noinspection PyPep8Naming def __init__(self, adapter, name, includeSCPI=True, **kwargs): try: if isinstance(adapter, (int, str)): adapter = VISAAdapter(adapter, **kwargs) except ImportError: raise Exception("Invalid Adapter provided for Instrument since " "PyVISA is not present") self.name = name self.SCPI = includeSCPI self.adapter = adapter class Object(object): pass self.get = Object() # TODO: Determine case basis for the addition of these methods if includeSCPI: # Basic SCPI commands self.status = self.measurement("*STB?", """ Returns the status of the instrument """) self.complete = self.measurement("*OPC?", """ TODO: Add this doc """) self.isShutdown = False log.info("Initializing %s." % self.name) @property def id(self): """ Requests and returns the identification of the instrument. """ if self.SCPI: return self.adapter.ask("*IDN?").strip() else: return "Warning: Property not implemented." # Wrapper functions for the Adapter object def ask(self, command): """ Writes the command to the instrument through the adapter and returns the read response. :param command: command string to be sent to the instrument """ return self.adapter.ask(command) def write(self, command): """ Writes the command to the instrument through the adapter. :param command: command string to be sent to the instrument """ self.adapter.write(command) def read(self): """ Reads from the instrument through the adapter and returns the response. """ return self.adapter.read() def values(self, command, **kwargs): """ Reads a set of values from the instrument through the adapter, passing on any key-word arguments. """ return self.adapter.values(command, **kwargs) def binary_values(self, command, header_bytes=0, dtype=np.float32): return self.adapter.binary_values(command, header_bytes, dtype) @staticmethod def control(get_command, set_command, docs, validator=lambda v, vs: v, values=(), map_values=False, get_process=lambda v: v, set_process=lambda v: v, check_set_errors=False, check_get_errors=False, **kwargs): """Returns a property for the class based on the supplied commands. This property may be set and read from the instrument. :param get_command: A string command that asks for the value :param set_command: A string command that writes the value :param docs: A docstring that will be included in the documentation :param validator: A function that takes both a value and a group of valid values and returns a valid value, while it otherwise raises an exception :param values: A list, tuple, range, or dictionary of valid values, that can be used as to map values if :code:`map_values` is True. :param map_values: A boolean flag that determines if the values should be interpreted as a map :param get_process: A function that take a value and allows processing before value mapping, returning the processed value :param set_process: A function that takes a value and allows processing before value mapping, returning the processed value :param check_set_errors: Toggles checking errors after setting :param check_get_errors: Toggles checking errors after getting """ if map_values and isinstance(values, dict): # Prepare the inverse values for performance inverse = {v: k for k, v in values.items()} def fget(self): vals = self.values(get_command, **kwargs) if check_get_errors: self.check_errors() if len(vals) == 1: value = get_process(vals[0]) if not map_values: return value elif isinstance(values, (list, tuple, range)): return values[int(value)] elif isinstance(values, dict): return inverse[value] else: raise ValueError( 'Values of type `{}` are not allowed ' 'for Instrument.control'.format(type(values)) ) else: vals = get_process(vals) return vals def fset(self, value): value = set_process(validator(value, values)) if not map_values: pass elif isinstance(values, (list, tuple, range)): value = values.index(value) elif isinstance(values, dict): value = values[value] else: raise ValueError( 'Values of type `{}` are not allowed ' 'for Instrument.control'.format(type(values)) ) self.write(set_command % value) if check_set_errors: self.check_errors() # Add the specified document string to the getter fget.__doc__ = docs return property(fget, fset) @staticmethod def measurement(get_command, docs, values=(), map_values=None, get_process=lambda v: v, command_process=lambda c: c, check_get_errors=False, **kwargs): """ Returns a property for the class based on the supplied commands. This is a measurement quantity that may only be read from the instrument, not set. :param get_command: A string command that asks for the value :param docs: A docstring that will be included in the documentation :param values: A list, tuple, range, or dictionary of valid values, that can be used as to map values if :code:`map_values` is True. :param map_values: A boolean flag that determines if the values should be interpreted as a map :param get_process: A function that take a value and allows processing before value mapping, returning the processed value :param command_process: A function that take a command and allows processing before executing the command, for both getting and setting :param check_get_errors: Toggles checking errors after getting """ if map_values and isinstance(values, dict): # Prepare the inverse values for performance inverse = {v: k for k, v in values.items()} def fget(self): vals = self.values(command_process(get_command), **kwargs) if check_get_errors: self.check_errors() if len(vals) == 1: value = get_process(vals[0]) if not map_values: return value elif isinstance(values, (list, tuple, range)): return values[int(value)] elif isinstance(values, dict): return inverse[value] else: raise ValueError( 'Values of type `{}` are not allowed ' 'for Instrument.measurement'.format(type(values)) ) else: return get_process(vals) # Add the specified document string to the getter fget.__doc__ = docs return property(fget) @staticmethod def setting(set_command, docs, validator=lambda x, y: x, values=(), map_values=False, check_set_errors=False, **kwargs): """Returns a property for the class based on the supplied commands. This property may be set, but raises an exception when being read from the instrument. :param set_command: A string command that writes the value :param docs: A docstring that will be included in the documentation :param validator: A function that takes both a value and a group of valid values and returns a valid value, while it otherwise raises an exception :param values: A list, tuple, range, or dictionary of valid values, that can be used as to map values if :code:`map_values` is True. :param map_values: A boolean flag that determines if the values should be interpreted as a map :param check_set_errors: Toggles checking errors after setting """ if map_values and isinstance(values, dict): # Prepare the inverse values for performance inverse = {v: k for k, v in values.items()} def fget(self): raise LookupError("Instrument.setting properties can not be read.") def fset(self, value): value = validator(value, values) if not map_values: pass elif isinstance(values, (list, tuple, range)): value = values.index(value) elif isinstance(values, dict): value = values[value] else: raise ValueError( 'Values of type `{}` are not allowed ' 'for Instrument.control'.format(type(values)) ) self.write(set_command % value) if check_set_errors: self.check_errors() # Add the specified document string to the getter fget.__doc__ = docs return property(fget, fset) # TODO: Determine case basis for the addition of this method def clear(self): """ Clears the instrument status byte """ self.write("*CLS") # TODO: Determine case basis for the addition of this method def reset(self): """ Resets the instrument. """ self.write("*RST") def shutdown(self): """Brings the instrument to a safe and stable state""" self.isShutdown = True log.info("Shutting down %s" % self.name) def check_errors(self): """Return any accumulated errors. Must be reimplemented by subclasses. """ pass class FakeInstrument(Instrument): """ Provides a fake implementation of the Instrument class for testing purposes. """ def __init__(self, adapter=None, name=None, includeSCPI=False, **kwargs): super().__init__( FakeAdapter(), name or "Fake Instrument", includeSCPI=includeSCPI, **kwargs ) @staticmethod def control(get_command, set_command, docs, validator=lambda v, vs: v, values=(), map_values=False, get_process=lambda v: v, set_process=lambda v: v, check_set_errors=False, check_get_errors=False, **kwargs): """Fake Instrument.control. Strip commands and only store and return values indicated by format strings to mimic many simple commands. This is analogous how the tests in test_instrument are handled. """ # Regex search to find first format specifier in the command fmt_spec_pattern = r'(%[\w.#-+ *]*[diouxXeEfFgGcrsa%])' match = re.search(fmt_spec_pattern, set_command) if match: format_specifier = match.group(0) else: format_specifier = '' # To preserve as much functionality as possible, call the real # control method with modified get_command and set_command. return Instrument.control(get_command="", set_command=format_specifier, docs=docs, validator=validator, values=values, map_values=map_values, get_process=get_process, set_process=set_process, check_set_errors=check_set_errors, check_get_errors=check_get_errors, **kwargs) PyMeasure-0.5/pymeasure/instruments/resources.py0000644000175000017500000000434113137471263022524 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import visa def list_resources(): """ Prints the available resources, and returns a list of VISA resource names .. code-block:: python resources = list_resources() #prints (e.g.) #0 : GPIB0::22::INSTR : Agilent Technologies,34410A,****** #1 : GPIB0::26::INSTR : Keithley Instruments Inc., Model 2612, ***** dmm = Agilent34410(resources[0]) """ rm = visa.ResourceManager() instrs = rm.list_resources() for n, instr in enumerate(instrs): # trying to catch errors in comunication try: res = rm.open_resource(instr) # try to avoid errors from *idn? try: # noinspection PyUnresolvedReferences idn = res.ask('*idn?')[:-1] except visa.Error: idn = "Not known" finally: res.close() print(n, ":", instr, ":", idn) except visa.VisaIOError as e: print(n, ":", instr, ":", "Visa IO Error: check connections") print(e) rm.close() return instrs PyMeasure-0.5/pymeasure/instruments/anritsu/0000755000175000017500000000000013171765525021630 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/anritsu/anritsuMS9710C.py0000644000175000017500000002463113137471263024554 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from time import sleep import numpy as np from pymeasure.instruments import Instrument from pymeasure.instruments.validators import ( strict_discrete_set, truncated_discrete_set, truncated_range, joined_validators ) import re log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) # Analysis Results with Units, ie -24.5DBM -> (-24.5, 'DBM') r_value_units = re.compile("([-\d]*\.\d*)(.*)") # Join validators to allow for special sets of characters truncated_range_or_off = joined_validators(strict_discrete_set, truncated_range) def _int_or_neg_one(v): try: return int(v) except ValueError: return -1 def _parse_trace_peak(vals): """Parse the returned value from a trace peak query.""" l, p = vals res = [l] m = r_value_units.match(p) if m is not None: data = list(m.groups()) data[0] = float(data[0]) res.extend(data) else: res.append(float(p)) return res class AnritsuMS9710C(Instrument): """Anritsu MS9710C Optical Spectrum Analyzer.""" ############# # Mappings # ############# ONOFF = ["ON", "OFF"] ONOFF_MAPPING = {True: 'ON', False: 'OFF', 1: 'ON', 0: 'OFF'} ###################### # Status Registers # ###################### ese2 = Instrument.control( "ESE2?", "ESE2 %d", "Extended Event Status Enable Register 2", get_process=int ) esr2 = Instrument.control( "ESR2?", "ESR2 %d", "Extended Event Status Register 2", get_process=_int_or_neg_one ) ########### # Modes # ########### measure_mode = Instrument.measurement( "MOD?", "Returns the current Measure Mode the OSA is in.", values={None: 0, "SINGLE": 1.0, "AUTO": 2.0, "POWER": 3.0}, map_values=True ) #################################### # Spectrum Parameters - Wavelength # #################################### wavelength_center = Instrument.control('CNT?', 'CNT %g', "Center Wavelength of Spectrum Scan in nm.") wavelength_span = Instrument.control('SPN?', 'SPN %g', "Wavelength Span of Spectrum Scan in nm.") wavelength_start = Instrument.control('STA?', 'STA %g', "Wavelength Start of Spectrum Scan in nm.") wavelength_stop = Instrument.control('STO?', 'STO %g', "Wavelength Stop of Spectrum Scan in nm.") wavelength_marker_value = Instrument.control( 'MKV?', 'MKV %s', "Wavelength Marker Value (wavelength or freq.?)", validator=strict_discrete_set, values=["WL", "FREQ"] ) wavelength_value_in = Instrument.control( 'WDP?', 'WDP %s', "Wavelength value in Vacuum or Air", validator=strict_discrete_set, values=["VACUUM", "AIR"] ) level_scale = Instrument.measurement( 'LVS?', "Current Level Scale", values=["LOG", "LIN"] ) level_log = Instrument.control( "LOG?", "LOG %f", "Level Log Scale (/div)", validator=truncated_range, values=[0.1, 10.0] ) level_lin = Instrument.control( "LIN?", "LIN %f", "Level Linear Scale (/div)", validator=truncated_range, values=[1e-12, 1] ) level_opt_attn = Instrument.control( "ATT?", "ATT %s", "Optical Attenuation Status (ON/OFF)", validator=strict_discrete_set, values=ONOFF ) resolution = Instrument.control( "RES?", "RES %f", "Resolution (nm)", validator=truncated_discrete_set, values=[0.05, 0.07, 0.1, 0.2, 0.5, 1.0] ) resolution_actual = Instrument.control( "ARES?", "ARES %s", "Resolution Actual (ON/OFF)", validator=strict_discrete_set, values=ONOFF, map_values=True ) resolution_vbw = Instrument.control( "VBW?", "VBW %s", "Video Bandwidth Resolution", validator=strict_discrete_set, values=["1MHz", "100kHz", "10kHz", "1kHz", "100Hz", "10Hz"] ) average_point = Instrument.control( "AVT?", "AVT %d", "Number of averages to take on each point (2-1000), or OFF", validator=truncated_range_or_off, values=[["OFF"], [2, 1000]] ) average_sweep = Instrument.control( "AVS?", "AVS %d", "Number of averages to make on a sweep (2-1000) or OFF", validator=truncated_range_or_off, values=[["OFF"], [2, 1000]] ) sampling_points = Instrument.control( "MPT?", "MPT %d", "Number of sampling points", validator=truncated_discrete_set, values=[51, 101, 251, 501, 1001, 2001, 5001], get_process=lambda v: int(v) ) ##################################### # Analysis Peak Search Parameters # ##################################### peak_search = Instrument.control( "PKS?", "PKS %s", "Peak Search Mode", validator=strict_discrete_set, values=["PEAK", "NEXT", "LAST", "LEFT", "RIGHT"] ) dip_search = Instrument.control( "DPS?", "DPS %s", "Dip Search Mode", validator=strict_discrete_set, values=["DIP", "NEXT", "LAST", "LEFT", "RIGHT"] ) analysis = Instrument.control( "ANA?", "ANA %s", "Analysis Control" ) analysis_result = Instrument.measurement( "ANAR?", "Read back anaysis result from current scan." ) ########################## # Data Memory Commands # ########################## data_memory_a_size = Instrument.measurement( 'DBA?', "Returns the number of points sampled in data memory register A." ) data_memory_b_size = Instrument.measurement( 'DBB?', "Returns the number of points sampled in data memory register B." ) data_memory_a_condition = Instrument.measurement( "DCA?", """Returns the data condition of data memory register A. Starting wavelength, and a sampling point (l1, l2, n).""" ) data_memory_b_condition = Instrument.measurement( "DCB?", """Returns the data condition of data memory register B. Starting wavelength, and a sampling point (l1, l2, n).""" ) data_memory_a_values = Instrument.measurement( "DMA?", "Reads the binary data from memory register A." ) data_memory_b_values = Instrument.measurement( "DMA?", "Reads the binary data from memory register B." ) data_memory_select = Instrument.control( "MSL?", "MSL %s", "Memory Data Select.", validator=strict_discrete_set, values=["A", "B"] ) ########################### # Trace Marker Commands # ########################### trace_marker_center = Instrument.setting( "TMC %s", "Trace Marker at Center. Set to 1 or True to initiate command", map_values=True, values={True: ''} ) trace_marker = Instrument.control( "TMK?", "TMK %f", "Sets the trace marker with a wavelength. Returns the trace wavelength and power.", get_process=_parse_trace_peak ) def __init__(self, adapter, **kwargs): """Constructor.""" self.analysis_mode = None super(AnritsuMS9710C, self).__init__(adapter, "Anritsu MS9710C Optical Spectrum Analyzer", **kwargs) @property def wavelengths(self): """Return a numpy array of the current wavelengths of scans.""" return np.linspace( self.wavelength_start, self.wavelength_stop, self.sampling_points ) def read_memory(self, slot="A"): """Read the scan saved in a memory slot.""" cond_attr = "data_memory_{}_condition".format(slot.lower()) data_attr = "data_memory_{}_values".format(slot.lower()) scan = getattr(self, cond_attr) wavelengths = np.linspace(scan[0], scan[1], int(scan[2])) power = np.fromstring(getattr(self, data_attr), sep="\r\n") return wavelengths, power def wait(self, n=3, delay=1): """Query OPC Command and waits for appropriate response.""" log.info("Wait for OPC") res = self.adapter.ask("*OPC?") n_attempts = n while(res == ''): log.debug("Empty OPC Repsonse. {} remaining".format(n_attempts)) if n_attempts == 0: break n_attempts -= 1 sleep(delay) res = self.adapter.read().strip() log.debug(res) def wait_for_sweep(self, n=20, delay=0.5): """Wait for a sweep to stop. This is performed by checking bit 1 of the ESR2. """ log.debug("Waiting for spectrum sweep") while(self.esr2 != 3 and n > 0): log.debug("Wait for sweep [{}]".format(n)) # log.debug("ESR2: {}".format(esr2)) sleep(delay) n -= 1 if n <= 0: log.warning("Sweep Timeout Occurred ({} s)".format(int(delay * n))) def single_sweep(self, **kwargs): """Perform a single sweep and wait for completion.""" log.debug("Performing a Spectrum Sweep") self.clear() self.write('SSI') self.wait_for_sweep(**kwargs) def center_at_peak(self, **kwargs): """Center the spectrum at the measured peak.""" self.write("PKC") self.wait(**kwargs) def measure_peak(self): """Measure the peak and return the trace marker.""" self.peak_search = "PEAK" return self.trace_marker PyMeasure-0.5/pymeasure/instruments/anritsu/anritsuMG3692C.py0000644000175000017500000000523513137471263024542 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument, discreteTruncate, RangeException class AnritsuMG3692C(Instrument): """ Represents the Anritsu MG3692C Signal Generator """ power = Instrument.control( ":POWER?;", ":POWER %g dBm;", """ A floating point property that represents the output power in dBm. This property can be set. """ ) frequency = Instrument.control( ":FREQUENCY?;", ":FREQUENCY %e Hz;", """ A floating point property that represents the output frequency in Hz. This property can be set. """ ) def __init__(self, resourceName, **kwargs): super(AnritsuMG3692C, self).__init__( resourceName, "Anritsu MG3692C Signal Generator", **kwargs ) @property def output(self): """ A boolean property that represents the signal output state. This property can be set to control the output. """ return int(self.ask(":OUTPUT?")) == 1 @output.setter def output(self, value): if value: self.write(":OUTPUT ON;") else: self.write(":OUTPUT OFF;") def enable(self): """ Enables the signal output. """ self.output = True def disable(self): """ Disables the signal output. """ self.output = False def shutdown(self): """ Shuts down the instrument, putting it in a safe state. """ # TODO: Implement modulation self.modulation = False self.disable()PyMeasure-0.5/pymeasure/instruments/anritsu/__init__.py0000644000175000017500000000233313137471263023735 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .anritsuMG3692C import AnritsuMG3692C from .anritsuMS9710C import AnritsuMS9710C PyMeasure-0.5/pymeasure/instruments/srs/0000755000175000017500000000000013171765525020752 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/srs/sr830.py0000644000175000017500000002661313137471263022206 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument, discreteTruncate from pymeasure.instruments.validators import strict_discrete_set, truncated_discrete_set import numpy as np import time import re class SR830(Instrument): SAMPLE_FREQUENCIES = [ 62.5e-3, 125e-3, 250e-3, 500e-3, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 ] SENSITIVITIES = [ 2e-9, 5e-9, 10e-9, 20e-9, 50e-9, 100e-9, 200e-9, 500e-9, 1e-6, 2e-6, 5e-6, 10e-6, 20e-6, 50e-6, 100e-6, 200e-6, 500e-6, 1e-3, 2e-3, 5e-3, 10e-3, 20e-3, 50e-3, 100e-3, 200e-3, 500e-3, 1 ] TIME_CONSTANTS = [ 10e-6, 30e-6, 100e-6, 300e-6, 1e-3, 3e-3, 10e-3, 30e-3, 100e-3, 300e-3, 1, 3, 10, 100, 300, 1e3, 3e3, 10e3, 30e3 ] FILTER_SLOPES = [6, 12, 18, 24] EXPANSION_VALUES = [1, 10, 100] RESERVE_VALUES = ['High Reserve', 'Normal', 'Low Noise'] CHANNELS = ['X', 'Y', 'R'] sine_voltage = Instrument.control( "SLVL?", "SLVL%0.3f", """ A floating point property that represents the reference sine-wave voltage in Volts. This property can be set. """ ) frequency = Instrument.control( "FREQ?", "FREQ%0.5e", """ A floating point property that represents the lock-in frequency in Hz. This property can be set. """ ) phase = Instrument.control( "PHAS?", "PHAS%0.2f", """ A floating point property that represents the lock-in phase in degrees. This property can be set. """ ) x = Instrument.measurement("OUTP?1", """ Reads the X value in Volts. """ ) y = Instrument.measurement("OUTP?2", """ Reads the Y value in Volts. """ ) magnitude = Instrument.measurement("OUTP?3", """ Reads the magnitude in Volts. """ ) theta = Instrument.measurement("OUTP?4", """ Reads the theta value in degrees. """ ) channel1 = Instrument.control( "DDEF?1;", "DDEF1,%d,0", """ A string property that represents the type of Channel 1, taking the values X, R, X Noise, Aux In 1, or Aux In 2. This property can be set.""", validator=strict_discrete_set, values=['X', 'R', 'X Noise', 'Aux In 1', 'Aux In 2'], map_values=True ) channel2 = Instrument.control( "DDEF?2;", "DDEF2,%d,0", """ A string property that represents the type of Channel 2, taking the values Y, Theta, Y Noise, Aux In 3, or Aux In 4. This property can be set.""", validator=strict_discrete_set, values=['Y', 'Theta', 'Y Noise', 'Aux In 3', 'Aux In 4'], map_values=True ) sensitivity = Instrument.control( "SENS?", "SENS%d", """ A floating point property that controls the sensitivity in Volts, which can take discrete values from 2 nV to 1 V. Values are truncated to the next highest level if they are not exact. """, validator=truncated_discrete_set, values=SENSITIVITIES, map_values=True ) time_constant = Instrument.control( "OFLT?", "OFLT%d", """ A floating point property that controls the time constant in seconds, which can take discrete values from 10 microseconds to 30,000 seconds. Values are truncated to the next highest level if they are not exact. """, validator=truncated_discrete_set, values=TIME_CONSTANTS, map_values=True ) filter_slope = Instrument.control( "OFSL?", "OFSL%d", """ An integer property that controls the filter slope, which can take on the values 6, 12, 18, and 24 dB/octave. Values are truncated to the next highest level if they are not exact. """, validator=truncated_discrete_set, values=FILTER_SLOPES, map_values=True ) def __init__(self, resourceName, **kwargs): super(SR830, self).__init__( resourceName, "Stanford Research Systems SR830 Lock-in amplifier", **kwargs ) def auto_gain(self): self.write("AGAN") def auto_reserve(self): self.write("ARSV") def auto_phase(self): self.write("APHS") def auto_offset(self, channel): """ Offsets the channel (X, Y, or R) to zero """ if channel not in self.CHANNELS: raise ValueError('SR830 channel is invalid') channel = self.CHANNELS.index(channel) + 1 self.write("AOFF %d" % channel) def get_scaling(self, channel): """ Returns the offset precent and the exapnsion term that are used to scale the channel in question """ if channel not in self.CHANNELS: raise ValueError('SR830 channel is invalid') channel = self.CHANNELS.index(channel) + 1 offset, expand = self.ask("OEXP? %d" % channel).split(',') return float(offset), self.EXPANSION_VALUES[int(expand)] def set_scaling(self, channel, precent, expand=0): """ Sets the offset of a channel (X=1, Y=2, R=3) to a certain precent (-105% to 105%) of the signal, with an optional expansion term (0, 10=1, 100=2) """ if channel not in self.CHANNELS: raise ValueError('SR830 channel is invalid') channel = self.CHANNELS.index(channel) + 1 expand = discreteTruncate(expand, self.EXPANSION_VALUES) self.write("OEXP %i,%.2f,%i" % (channel, precent, expand)) def output_conversion(self, channel): """ Returns a function that can be used to determine the signal from the channel output (X, Y, or R) """ offset, expand = self.get_scaling(channel) sensitivity = self.sensitivity return lambda x: (x/(10.*expand) + offset) * sensitivity @property def sample_frequency(self): """ Gets the sample frequency in Hz """ index = int(self.ask("SRAT?")) if index is 14: return None # Trigger else: return SR830.SAMPLE_FREQUENCIES[index] @sample_frequency.setter def sample_frequency(self, frequency): """Sets the sample frequency in Hz (None is Trigger)""" assert type(frequency) in [float, int, type(None)] if frequency is None: index = 14 # Trigger else: frequency = discreteTruncate(frequency, SR830.SAMPLE_FREQUENCIES) index = SR830.SAMPLE_FREQUENCIES.index(frequency) self.write("SRAT%f" % index) def aquireOnTrigger(self, enable=True): self.write("TSTR%d" % enable) @property def reserve(self): return SR830.RESERVE_VALUES[int(self.ask("RMOD?"))] @reserve.setter def reserve(self, reserve): if reserve not in SR830.RESERVE_VALUES: index = 1 else: index = SR830.RESERVE_VALUES.index(reserve) self.write("RMOD%d" % index) def is_out_of_range(self): """ Returns True if the magnitude is out of range """ return int(self.ask("LIAS?2")) is 1 def quick_range(self): """ While the magnitude is out of range, increase the sensitivity by one setting """ while self.is_out_of_range(): self.write("SENS%d" % (int(self.ask("SENS?"))+1)) time.sleep(5.0*self.time_constant) self.write("*CLS") # Set the range as low as possible self.sensitivity(1.15*abs(self.R)) @property def buffer_count(self): query = self.ask("SPTS?") if query.count("\n") > 1: return int(re.match(r"\d+\n$", query, re.MULTILINE).group(0)) else: return int(query) def fill_buffer(self, count, has_aborted=lambda: False, delay=0.001): ch1 = np.empty(count, np.float32) ch2 = np.empty(count, np.float32) currentCount = self.buffer_count index = 0 while currentCount < count: if currentCount > index: ch1[index:currentCount] = self.buffer_data(1, index, currentCount) ch2[index:currentCount] = self.buffer_data(2, index, currentCount) index = currentCount time.sleep(delay) currentCount = self.buffer_count if has_aborted(): self.pause_buffer() return ch1, ch2 self.pauseBuffer() ch1[index:count+1] = self.buffer_data(1, index, count) ch2[index:count+1] = self.buffer_data(2, index, count) return ch1, ch2 def buffer_measure(self, count, stopRequest=None, delay=1e-3): self.write("FAST0;STRD") ch1 = np.empty(count, np.float64) ch2 = np.empty(count, np.float64) currentCount = self.buffer_count index = 0 while currentCount < count: if currentCount > index: ch1[index:currentCount] = self.buffer_data(1, index, currentCount) ch2[index:currentCount] = self.buffer_data(2, index, currentCount) index = currentCount time.sleep(delay) currentCount = self.buffer_count if stopRequest is not None and stopRequest.isSet(): self.pauseBuffer() return (0, 0, 0, 0) self.pauseBuffer() ch1[index:count] = self.buffer_data(1, index, count) ch2[index:count] = self.buffer_data(2, index, count) return (ch1.mean(), ch1.std(), ch2.mean(), ch2.std()) def pause_buffer(self): self.write("PAUS") def start_buffer(self, fast=False): if fast: self.write("FAST2;STRD") else: self.write("FAST0;STRD") def wait_for_buffer(self, count, has_aborted=lambda: False, timeout=60, timestep=0.01): """ Wait for the buffer to fill a certain count """ i = 0 while not self.buffer_count >= count and i < (timeout / timestep): time.sleep(timestep) i += 1 if has_aborted(): return False self.pauseBuffer() def get_buffer(self, channel=1, start=0, end=None): """ Aquires the 32 bit floating point data through binary transfer """ if end is None: end = self.buffer_count return self.binary_values("TRCB?%d,%d,%d" % ( channel, start, end-start)) def reset_buffer(self): self.write("REST") def trigger(self): self.write("TRIG") PyMeasure-0.5/pymeasure/instruments/srs/__init__.py0000644000175000017500000000223613137471263023061 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .sr830 import SR830 PyMeasure-0.5/pymeasure/instruments/__init__.py0000644000175000017500000000314113137471263022246 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from ..errors import RangeError, RangeException from .instrument import Instrument from .mock import Mock from .resources import list_resources from .validators import discreteTruncate from . import agilent from . import anritsu from . import danfysik from . import fwbell from . import hp from . import keithley from . import lakeshore from . import parker from . import signalrecovery from . import srs from . import tektronix from . import thorlabs from . import yokogawa PyMeasure-0.5/pymeasure/instruments/newport/0000755000175000017500000000000013171765525021641 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/newport/esp300.py0000644000175000017500000002512013137471263023220 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from time import sleep from pymeasure.instruments import Instrument from pymeasure.instruments.validators import strict_discrete_set class AxisError(Exception): """ Raised when a particular axis causes an error for the Newport ESP300. """ MESSAGES = { '00': 'MOTOR TYPE NOT DEFINED', '01': 'PARAMETER OUT OF RANGE', '02': 'AMPLIFIER FAULT DETECTED', '03': 'FOLLOWING ERROR THRESHOLD EXCEEDED', '04': 'POSITIVE HARDWARE LIMIT DETECTED', '05': 'NEGATIVE HARDWARE LIMIT DETECTED', '06': 'POSITIVE SOFTWARE LIMIT DETECTED', '07': 'NEGATIVE SOFTWARE LIMIT DETECTED', '08': 'MOTOR / STAGE NOT CONNECTED', '09': 'FEEDBACK SIGNAL FAULT DETECTED', '10': 'MAXIMUM VELOCITY EXCEEDED', '11': 'MAXIMUM ACCELERATION EXCEEDED', '12': 'Reserved for future use', '13': 'MOTOR NOT ENABLED', '14': 'Reserved for future use', '15': 'MAXIMUM JERK EXCEEDED', '16': 'MAXIMUM DAC OFFSET EXCEEDED', '17': 'ESP CRITICAL SETTINGS ARE PROTECTED', '18': 'ESP STAGE DEVICE ERROR', '19': 'ESP STAGE DATA INVALID', '20': 'HOMING ABORTED', '21': 'MOTOR CURRENT NOT DEFINED', '22': 'UNIDRIVE COMMUNICATIONS ERROR', '23': 'UNIDRIVE NOT DETECTED', '24': 'SPEED OUT OF RANGE', '25': 'INVALID TRAJECTORY MASTER AXIS', '26': 'PARAMETER CHARGE NOT ALLOWED', '27': 'INVALID TRAJECTORY MODE FOR HOMING', '28': 'INVALID ENCODER STEP RATIO', '29': 'DIGITAL I/O INTERLOCK DETECTED', '30': 'COMMAND NOT ALLOWED DURING HOMING', '31': 'COMMAND NOT ALLOWED DUE TO GROUP', '32': 'INVALID TRAJECTORY MODE FOR MOVING' } def __init__(self, code): self.axis = str(code)[0] self.error = str(code)[1:] self.message = self.MESSAGES[self.error] def __str__(self): return "Newport ESP300 axis %s reported the error: %s" % ( self.axis, self.message) class GeneralError(Exception): """ Raised when the Newport ESP300 has a general error. """ MESSAGES = { '1': 'PCI COMMUNICATION TIME-OUT', '4': 'EMERGENCY SOP ACTIVATED', '6': 'COMMAND DOES NOT EXIST', '7': 'PARAMETER OUT OF RANGE', '8': 'CABLE INTERLOCK ERROR', '9': 'AXIS NUMBER OUT OF RANGE', '13': 'GROUP NUMBER MISSING', '14': 'GROUP NUMBER OUT OF RANGE', '15': 'GROUP NUMBER NOT ASSIGNED', '17': 'GROUP AXIS OUT OF RANGE', '18': 'GROUP AXIS ALREADY ASSIGNED', '19': 'GROUP AXIS DUPLICATED', '16': 'GROUP NUMBER ALREADY ASSIGNED', '20': 'DATA ACQUISITION IS BUSY', '21': 'DATA ACQUISITION SETUP ERROR', '23': 'SERVO CYCLE TICK FAILURE', '25': 'DOWNLOAD IN PROGRESS', '26': 'STORED PROGRAM NOT STARTED', '27': 'COMMAND NOT ALLOWED', '29': 'GROUP PARAMETER MISSING', '30': 'GROUP PARAMETER OUT OF RANGE', '31': 'GROUP MAXIMUM VELOCITY EXCEEDED', '32': 'GROUP MAXIMUM ACCELERATION EXCEEDED', '22': 'DATA ACQUISITION NOT ENABLED', '28': 'STORED PROGRAM FLASH AREA FULL', '33': 'GROUP MAXIMUM DECELERATION EXCEEDED', '35': 'PROGRAM NOT FOUND', '37': 'AXIS NUMBER MISSING', '38': 'COMMAND PARAMETER MISSING', '34': 'GROUP MOVE NOT ALLOWED DURING MOTION', '39': 'PROGRAM LABEL NOT FOUND', '40': 'LAST COMMAND CANNOT BE REPEATED', '41': 'MAX NUMBER OF LABELS PER PROGRAM EXCEEDED' } def __init__(self, code): self.error = str(code) self.message = self.MESSAGES[self.error] def __str__(self): return "Newport ESP300 reported the error: %s" % ( self.message) class Axis(object): """ Represents an axis of the Newport ESP300 Motor Controller, which can have independent parameters from the other axes. """ position = Instrument.control( "TP", "PA%g", """ A floating point property that controls the position of the axis. The units are defined based on the actuator. Use the :meth:`~.wait_for_stop` method to ensure the position is stable. """ ) enabled = Instrument.measurement( "MO?", """ Returns a boolean value that is True if the motion for this axis is enabled. """, cast=bool ) left_limit = Instrument.control( "SL?", "SL%g", """ A floating point property that controls the left software limit of the axis. """ ) right_limit = Instrument.control( "SR?", "SR%g", """ A floating point property that controls the right software limit of the axis. """ ) units = Instrument.control( "SN?", "SN%d", """ A string property that controls the displacement units of the axis, which can take values of: enconder count, motor step, millimeter, micrometer, inches, milli-inches, micro-inches, degree, gradient, radian, milliradian, and microradian. """, validator=strict_discrete_set, values={ 'encoder count':0, 'motor step':1, 'millimeter':2, 'micrometer':3, 'inches':4, 'milli-inches':5, 'micro-inches':6, 'degree':7, 'gradient':8, 'radian':9, 'milliradian':10, 'microradian':11 }, map_values=True ) motion_done = Instrument.measurement( "MD?", """ Returns a boolean that is True if the motion is finished. """, cast=bool ) def __init__(self, axis, controller): self.axis = str(axis) self.controller = controller def ask(self, command): command = self.axis + command return self.controller.ask(command) def write(self, command): command = self.axis + command self.controller.write(command) def values(self, command, **kwargs): command = self.axis + command return self.controller.values(command, **kwargs) def enable(self): """ Enables motion for the axis. """ self.write("MO") def disable(self): """ Disables motion for the axis. """ self.write("MF") def home(self, type=1): """ Drives the axis to the home position, which may be the negative hardware limit for some actuators (e.g. LTA-HS). """ self.write("OR%d") def define_position(self, position): """ Overwrites the value of the current position with the given value. """ self.write("DH%g" % position) def zero(self): """ Resets the axis position to be zero at the current poisiton. """ self.write("DH") def wait_for_stop(self, delay=0, interval=0.05): """ Blocks the program until the motion is completed. A further delay can be specified in seconds. """ self.write("WS%g" % delay*1e3) while not self.motion_done: sleep(interval) class ESP300(Instrument): """ Represents the Newport ESP 300 Motion Controller and provides a high-level for interacting with the instrument. By default this instrument is constructed with x, y, and phi attributes that represent axes 1, 2, and 3. Custom implementations can overwrite this depending on the avalible axes. Axes are controlled through an :class:`Axis ` class. """ error = Instrument.measurement( "TE?", """ Reads an error code from the motion controller. """, cast=int ) def __init__(self, resourceName, **kwargs): super(ESP300, self).__init__( resourceName, "Newport ESP 300 Motion Controller", **kwargs ) # Defines default axes, which can be overwritten self.x = Axis(1, self) self.y = Axis(2, self) self.phi = Axis(3, self) def clear_errors(self): """ Clears the error messages by checking until a 0 code is recived. """ while self.error != 0: continue @property def errors(self): """ Returns a list of error Exceptions that can be later raised, or used to diagnose the situation. """ errors = [] code = self.error while code != 0: if code > 100: errors.append(AxisError(code)) else: errors.append(GeneralError(code)) code = self.error return errors @property def axes(self): """ A list of the :class:`Axis ` objects that are present. """ axes = [] directory = dir(self) for name in directory: if name == 'axes': continue # Skip this property try: item = getattr(self, name) if isinstance(item, Axis): axes.append(item) except TypeError: continue except Exception as e: raise e return axes def enable(self): """ Enables all of the axes associated with this controller. """ for axis in self.axes: axis.enable() def disable(self): """ Disables all of the axes associated with this controller. """ for axis in self.axes: axis.disable() def shutdown(self): """ Shuts down the controller by disabling all of the axes. """ self.disable() PyMeasure-0.5/pymeasure/instruments/newport/__init__.py0000644000175000017500000000224013137471263023743 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .esp300 import ESP300 PyMeasure-0.5/pymeasure/instruments/danfysik/0000755000175000017500000000000013171765525021753 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/danfysik/danfysik8500.py0000644000175000017500000002757313160054362024454 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument, RangeException from .adapters import DanfysikAdapter from time import sleep import numpy as np import re class Danfysik8500(Instrument): """ Represents the Danfysik 8500 Electromanget Current Supply and provides a high-level interface for interacting with the instrument To allow user access to the Prolific Technology PL2303 Serial port adapter in Linux, create the file: :code:`/etc/udev/rules.d/50-danfysik.rules`, with contents: .. code-block:: none SUBSYSTEMS=="usb",ATTRS{idVendor}=="067b",ATTRS{idProduct}=="2303",MODE="0666",SYMLINK+="danfysik" Then reload the udev rules with: .. code-block:: bash sudo udevadm control --reload-rules sudo udevadm trigger The device will be accessible through the port :code:`/dev/danfysik`. """ id = Instrument.measurement( "PRINT", """ Reads the idenfitication information. """ ) def __init__(self, port): super(Danfysik8500, self).__init__( DanfysikAdapter(port), "Danfysik 8500 Current Supply", includeSCPI=False ) self.write("ERRT") # Use text error messages self.write("UNLOCK") # Unlock from remote or local mode def local(self): """ Sets the instrument in local mode, where the front panel can be used. """ self.write("LOC") def remote(self): """ Sets the instrument in remote mode, where the the front panel is disabled. """ self.write("REM") @property def polarity(self): """ The polarity of the current supply, being either -1 or 1. This property can be set by suppling one of these values. """ return 1 if self.ask("PO").strip() == '+' else -1 @polarity.setter def polarity(self, value): polarity = "+" if value > 0 else "-" self.write("PO %s" % polarity) def reset_interlocks(self): """ Resets the instrument interlocks. """ self.write("RS") def enable(self): """ Enables the flow of current. """ self.write("N") def disable(self): """ Disables the flow of current. """ self.write("F") def is_enabled(self): """ Returns True if the current supply is enabled. """ return self.status_hex & 0x800000 == 0 @property def status_hex(self): """ The status in hexadecimal. This value is parsed in :attr:`~.Danfysik8500.status` into a human-readable list. """ status = self.ask("S1H") match = re.search(r'(?P[A-Z0-9]{6})', status) if match is not None: return int(match.groupdict()['hex'], 16) else: raise Exception("Danfysik status not properly returned. Instead " "got '%s'" % status) @property def current(self): """ The actual current in Amps. This property can be set through :attr:`~.current_ppm`. """ return int(self.ask("AD 8"))*1e-2*self.polarity @current.setter def current(self, amps): if amps > 160 or amps < -160: raise RangeException("Danfysik 8500 is only capable of sourcing " "+/- 160 Amps") self.current_ppm = int((1e6/160)*amps) @property def current_ppm(self): """ The current in parts per million. This property can be set. """ return int(self.ask("DA 0")[2:]) @current_ppm.setter def current_ppm(self, ppm): if abs(ppm) < 0 or abs(ppm) > 1e6: raise RangeException("Danfysik 8500 requires parts per million " "to be an appropriate integer") self.write("DA 0,%d" % ppm) @property def current_setpoint(self): """ The setpoint for the current, which can deviate from the actual current (:attr:`~.Danfysik8500.current`) while the supply is in the process of setting the value. """ return self.current_ppm*(160/1e6) @property def slew_rate(self): """ The slew rate of the current sweep. """ return float(self.ask("R3")) def wait_for_current(self, has_aborted=lambda: False, delay=0.01): """ Blocks the process until the current has stabilized. A provided function :code:`has_aborted` can be supplied, which is checked after each delay time (in seconds) in addition to the stability check. This allows an abort feature to be integrated. :param has_aborted: A function that returns True if the process should stop waiting :param delay: The delay time in seconds between each check for stability """ self.wait_for_ready(has_aborted, delay) while not has_aborted() and not self.is_current_stable(): sleep(delay) def is_current_stable(self): """ Returns True if the current is within 0.02 A of the setpoint value. """ return abs(self.current - self.current_setpoint) <= 0.02 def is_ready(self): """ Returns True if the instrument is in the ready state. """ return self.status_hex & 0b10 == 0 def wait_for_ready(self, has_aborted=lambda: False, delay=0.01): """ Blocks the process until the instrument is ready. A provided function :code:`has_aborted` can be supplied, which is checked after each delay time (in seconds) in addition to the readiness check. This allows an abort feature to be integrated. :param has_aborted: A function that returns True if the process should stop waiting :param delay: The delay time in seconds between each check for readiness """ while not has_aborted() and not self.is_ready(): sleep(delay) @property def status(self): """ A list of human-readable strings that contain the instrument status information, based on :attr:`~.status_hex`. """ status = [] indicator = self.ask("S1") if indicator[0] == "!": status.append("Main Power OFF") else: status.append("Main Power ON") # Skipping 5, 6 and 7 (from Appendix Manual on command S1) messages = { 1: "Polarity Normal", 2: "Polarity Reversed", 3: "Regulation Transformer is not equal to zero", 7: "Spare Interlock", 8: "One Transistor Fault", 9: "Sum - Interlock", 10: "DC Overcurrent (OCP)", 11: "DC Overload", 12: "Regulation Module Failure", 13: "Preregulator Failure", 14: "Phase Failure", 15: "MPS Waterflow Failure", 16: "Earth Leakage Failure", 17: "Thermal Breaker/Fuses", 18: "MPS Overtemperature", 19: "Panic Button/Door Switch", 20: "Magnet Waterflow Failure", 21: "Magnet Overtemperature", 22: "MPS Not Ready" } for index, message in messages.items(): if indicator[index] == "!": status.append(message) return status def clear_ramp_set(self): """ Clears the ramp set. """ self.write("RAMPSET C") def set_ramp_delay(self, time): """ Sets the ramp delay time in seconds. :param time: The time delay time in seconds """ self.write("RAMPSET %f" % time) def start_ramp(self): """ Starts the current ramp. """ self.write("RAMP R") def add_ramp_step(self, current): """ Adds a current step to the ramp set. :param current: A current in Amps """ self.write("R %.6f" % (current/160.)) def stop_ramp(self): """ Stops the current ramp. """ self.ask("RAMP S") def set_ramp_to_current(self, current, points, delay_time=1): """ Sets up a linear ramp from the initial current to a different current, with a number of points, and delay time. :param current: The final current in Amps :param points: The number of linear points to traverse :param delay_time: A delay time in seconds """ initial_current = self.current self.clear_ramp_set() self.set_ramp_delay(delay_time) steps = np.linspace(initial_current, current, num=points) cmds = ["R %.6f" % (step/160.) for step in steps] self.write("\r".join(cmds)) def ramp_to_current(self, current, points, delay_time=1): """ Executes :meth:`~.set_ramp_to_current` and starts the ramp. """ self.set_ramp_to_current(current, points, delay_time) self.start_ramp() # self.setSequence(0, [0, 10], [0.01]) def set_sequence(self, stack, currents, times, multiplier=999999): """ Sets up an arbitrary ramp profile with a list of currents (Amps) and a list of interval times (seconds) on the specified stack number (0-15) """ self.clear_sequence(stack) if min(times) >= 1 and max(times) <= 65535: self.write("SLOW %i" % stack) elif min(times) >= 0.1 and max(times) <= 6553.5: self.write("FAST %i" % stack) times = [0.1*x for x in times] else: raise RangeException("Timing for Danfysik 8500 ramp sequence is" " out of range") for i in range(len(times)): self.write("WSA %i,%i,%i,%i" % ( stack, int(6250*abs(currents[i])), int(6250*abs(currents[i+1])), times[i]) ) self.write("MULT %i,%i" % (stack, multiplier)) def clear_sequence(self, stack): """ Clears the sequence by the stack number. :param stack: A stack number between 0-15 """ self.write("CSS %i" % stack) def sync_sequence(self, stack, delay=0): """ Arms the ramp sequence to be triggered by a hardware input to pin P33 1&2 (10 to 24 V) or a TS command. If a delay is provided, the sequence will start after the delay. :param stack: A stack number between 0-15 :param delay: A delay time in seconds """ self.write("SYNC %i, %i" % (stack, delay)) def start_sequence(self, stack): """ Starts a sequence by the stack number. :param stack: A stack number between 0-15 """ self.write("TS %i" % stack) def stop_sequence(self): """ Stops the currently running sequence. """ self.write("STOP") def is_sequence_running(self, stack): """ Returns True if a sequence is running with a given stack number :param stack: A stack number between 0-15 """ return re.search("R%i," % stack, self.ask("S2")) is not None PyMeasure-0.5/pymeasure/instruments/danfysik/adapters.py0000644000175000017500000000553613137471263024134 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.adapters.serial import SerialAdapter import re class DanfysikAdapter(SerialAdapter): """ Provides a :class:`SerialAdapter` with the specific baudrate and timeout for Danfysik serial communication. Initiates the adapter to open serial communcation over the supplied port. :param port: A string representing the serial port """ def __init__(self, port): super(DanfysikAdapter, self).__init__(port, baudrate=9600, timeout=0.5) def write(self, command): """ Overwrites the :func:`SerialAdapter.write ` method to automatically append a Unix-style linebreak at the end of the command. :param command: SCPI command string to be sent to the instrument """ command += "\r" self.connection.write(command.encode()) def read(self): """ Overwrites the :func:`SerialAdapter.read ` method to automatically raise exceptions if errors are reported by the instrument. :returns: String ASCII response of the instrument :raises: An :code:`Exception` if the Danfysik raises an error """ # Overwrite to raise exceptions on error messages result = b"".join(self.connection.readlines()) result = result.decode() result = result.replace("\r", "") search = re.search("^\?\\x07\s(?P.*)$", result, re.MULTILINE) if search: raise Exception("Danfysik raised the error: %s" % ( search.groups()[0])) else: return result def __repr__(self): return "" % self.connection.port PyMeasure-0.5/pymeasure/instruments/danfysik/__init__.py0000644000175000017500000000232213137471263024056 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .adapters import DanfysikAdapter from .danfysik8500 import Danfysik8500 PyMeasure-0.5/pymeasure/instruments/agilent/0000755000175000017500000000000013171765525021566 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/agilent/agilent34410A.py0000644000175000017500000000430413160054362024245 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument class Agilent34410A(Instrument): """ Represent the HP/Agilent/Keysight 34410A and related multimeters. Implemented measurements: voltage_dc, voltage_ac, current_dc, current_ac, resistance, resistance_4w """ #only the most simple functions are implemented voltage_dc = Instrument.measurement("MEAS:VOLT:DC? DEF,DEF", "DC voltage, in Volts") voltage_ac = Instrument.measurement("MEAS:VOLT:AC? DEF,DEF", "AC voltage, in Volts") current_dc = Instrument.measurement("MEAS:CURR:DC? DEF,DEF", "DC current, in Amps") current_ac = Instrument.measurement("MEAS:CURR:AC? DEF,DEF", "AC current, in Amps") resistance = Instrument.measurement("MEAS:RES? DEF,DEF", "Resistance, in Ohms") resistance_4w = Instrument.measurement("MEAS:FRES? DEF,DEF", "Four-wires (remote sensing) resistance, in Ohms") def __init__(self, adapter, delay=0.02, **kwargs): super(Agilent34410A, self).__init__( adapter, "HP/Agilent/Keysight 34410A Multimiter", **kwargs ) PyMeasure-0.5/pymeasure/instruments/agilent/agilent8257D.py0000644000175000017500000002520213137471263024211 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument from pymeasure.instruments.validators import truncated_range, strict_discrete_set class Agilent8257D(Instrument): """Represents the Agilent 8257D Signal Generator and provides a high-level interface for interacting with the instrument. .. code-block:: python generator = Agilent8257D("GPIB::1") generator.power = 0 # Sets the output power to 0 dBm generator.frequency = 5 # Sets the output frequency to 5 GHz generator.enable() # Enables the output """ power = Instrument.control( ":POW?;", ":POW %g dBm;", """ A floating point property that represents the output power in dBm. This property can be set. """ ) frequency = Instrument.control( ":FREQ?;", ":FREQ %e Hz;", """ A floating point property that represents the output frequency in Hz. This property can be set. """ ) start_frequency = Instrument.control( ":SOUR:FREQ:STAR?", ":SOUR:FREQ:STAR %e Hz", """ A floating point property that represents the start frequency in Hz. This property can be set. """ ) center_frequency = Instrument.control( ":SOUR:FREQ:CENT?", ":SOUR:FREQ:CENT %e Hz;", """ A floating point property that represents the center frequency in Hz. This property can be set. """ ) stop_frequency = Instrument.control( ":SOUR:FREQ:STOP?", ":SOUR:FREQ:STOP %e Hz", """ A floating point property that represents the stop frequency in Hz. This property can be set. """ ) start_power = Instrument.control( ":SOUR:POW:STAR?", ":SOUR:POW:STAR %e dBm", """ A floating point property that represents the start power in dBm. This property can be set. """ ) start_power = Instrument.control( ":SOUR:POW:STOP?", ":SOUR:POW:STOP %e dBm", """ A floating point property that represents the stop power in dBm. This property can be set. """ ) dwell_time = Instrument.control( ":SOUR:SWE:DWEL1?", ":SOUR:SWE:DWEL1 %.3f", """ A floating point property that represents the settling time in seconds at the current frequency or power setting. This property can be set. """ ) step_points = Instrument.control( ":SOUR:SWE:POIN?", ":SOUR:SWE:POIN %d", """ An integer number of points in a step sweep. This property can be set. """ ) is_enabled = Instrument.measurement(":OUTPUT?", """ Reads a boolean value that is True if the output is on. """, cast=bool ) has_modulation = Instrument.measurement(":OUTPUT:MOD?", """ Reads a boolean value that is True if the modulation is enabled. """, cast=bool ) ######################## # Amplitude modulation # ######################## has_amplitude_modulation = Instrument.measurement(":SOUR:AM:STAT?", """ Reads a boolean value that is True if the amplitude modulation is enabled. """, cast=bool ) amplitude_depth = Instrument.control( ":SOUR:AM:DEPT?", ":SOUR:AM:DEPT %g", """ A floating point property that controls the amplitude modulation in precent, which can take values from 0 to 100 %. """, validator=truncated_range, values=[0, 100] ) AMPLITUDE_SOURCES = { 'internal':'INT', 'internal 2':'INT2', 'external':'EXT', 'external 2':'EXT2' } amplitude_source = Instrument.control( ":SOUR:AM:SOUR?", ":SOUR:AM:SOUR %s", """ A string property that controls the source of the amplitude modulation signal, which can take the values: 'internal', 'internal 2', 'external', and 'external 2'. """, validator=strict_discrete_set, values=AMPLITUDE_SOURCES, map_values=True ) #################### # Pulse modulation # #################### has_pulse_modulation = Instrument.measurement(":SOUR:PULM:STAT?", """ Reads a boolean value that is True if the pulse modulation is enabled. """, cast=bool ) PULSE_SOURCES = { 'internal':'INT', 'external':'EXT', 'scalar':'SCAL' } pulse_source = Instrument.control( ":SOUR:PULM:SOUR?", ":SOUR:PULM:SOUR %s", """ A string property that controls the source of the pulse modulation signal, which can take the values: 'internal', 'external', and 'scalar'. """, validator=strict_discrete_set, values=PULSE_SOURCES, map_values=True ) PULSE_INPUTS = { 'square':'SQU', 'free-run':'FRUN', 'triggered':'TRIG', 'doublet':'DOUB', 'gated':'GATE' } pulse_input = Instrument.control( ":SOUR:PULM:SOUR:INT?", ":SOUR:PULM:SOUR:INT %s", """ A string property that controls the internally generated modulation input for the pulse modulation, which can take the values: 'square', 'free-run', 'triggered', 'doublet', and 'gated'. """, validator=strict_discrete_set, values=PULSE_INPUTS, map_values=True ) pulse_frequency = Instrument.control( ":SOUR:PULM:INT:FREQ?", ":SOUR:PULM:INT:FREQ %g", """ A floating point property that controls the pulse rate frequency in Hertz, which can take values from 0.1 Hz to 10 MHz. """, validator=truncated_range, values=[0.1, 10e6] ) ####################### # Internal Oscillator # ####################### internal_frequency = Instrument.control( ":SOUR:AM:INT:FREQ?", ":SOUR:AM:INT:FREQ %g", """ A floating point property that controls the frequency of the internal oscillator in Hertz, which can take values from 0.5 Hz to 1 MHz. """, validator=truncated_range, values=[0.5, 1e6] ) INTERNAL_SHAPES = { 'sine':'SINE', 'triangle':'TRI', 'square':'SQU', 'ramp':'RAMP', 'noise':'NOIS', 'dual-sine':'DUAL', 'swept-sine':'SWEP' } internal_shape = Instrument.control( ":SOUR:AM:INT:FUNC:SHAP?", ":SOUR:AM:INT:FUNC:SHAP %s", """ A string property that controls the shape of the internal oscillations, which can take the values: 'sine', 'triangle', 'square', 'ramp', 'noise', 'dual-sine', and 'swept-sine'. """, validator=strict_discrete_set, values=INTERNAL_SHAPES, map_values=True ) def __init__(self, adapter, delay=0.02, **kwargs): super(Agilent8257D, self).__init__( adapter, "Agilent 8257D RF Signal Generator", **kwargs ) def enable(self): """ Enables the output of the signal. """ self.write(":OUTPUT ON;") def disable(self): """ Disables the output of the signal. """ self.write(":OUTPUT OFF;") def enable_modulation(self): self.write(":OUTPUT:MOD ON;") self.write(":lfo:sour int; :lfo:ampl 2.0vp; :lfo:stat on;") def disable_modulation(self): """ Disables the signal modulation. """ self.write(":OUTPUT:MOD OFF;") self.write(":lfo:stat off;") def config_amplitude_modulation(self, frequency=1e3, depth=100.0, shape='sine'): """ Configures the amplitude modulation of the output signal. :param frequency: A modulation frequency for the internal oscillator :param depth: A linear depth precentage :param shape: A string that describes the shape for the internal oscillator """ self.enable_amplitude_modulation() self.amplitude_source = 'internal' self.internal_frequency = frequency self.internal_shape = 'sine' self.amplitude_depth = depth def enable_amplitude_modulation(self): """ Enables amplitude modulation of the output signal. """ self.write(":SOUR:AM:STAT ON") def disable_amplitude_modulation(self): """ Disables amplitude modulation of the output signal. """ self.write(":SOUR:AM:STAT OFF") def config_pulse_modulation(self, frequency=1e3, input='square'): """ Configures the pulse modulation of the output signal. :param frequency: A pulse rate frequency in Hertz :param input: A string that describes the internal pulse input """ self.enable_pulse_modulation() self.pulse_source = 'internal' self.pulse_input = 'square' self.pulse_frequency = frequency def enable_pulse_modulation(self): """ Enables pulse modulation of the output signal. """ self.write(":SOUR:PULM:STAT ON") def disable_pulse_modulation(self): """ Disables pulse modulation of the output signal. """ self.write(":SOUR:PULM:STAT OFF") def config_step_sweep(self): """ Configures a step sweep through frequency """ self.write(":SOUR:FREQ:MODE SWE;" ":SOUR:SWE:GEN STEP;" ":SOUR:SWE:MODE AUTO;") def enable_retrace(self): self.write(":SOUR:LIST:RETR 1") def disable_retrace(self): self.write(":SOUR:LIST:RETR 0") def single_sweep(self): self.write(":SOUR:TSW") def start_step_sweep(self): """ Starts a step sweep. """ self.write(":SOUR:SWE:CONT:STAT ON") def stop_step_sweep(self): """ Stops a step sweep. """ self.write(":SOUR:SWE:CONT:STAT OFF") def shutdown(self): """ Shuts down the instrument by disabling any modulation and the output signal. """ self.disable_modulation() self.disable() PyMeasure-0.5/pymeasure/instruments/agilent/agilentE4980.py0000644000175000017500000001570613160054362024212 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument from pymeasure.instruments.validators import truncated_range, strict_discrete_set, strict_range from pyvisa.errors import VisaIOError class AgilentE4980(Instrument): """Represents LCR meter E4980A/AL""" ac_voltage = Instrument.control(":VOLT:LEV?", ":VOLT:LEV %g", "AC voltage level, in Volts", validator=strict_range, values=[0, 20]) ac_current = Instrument.control(":CURR:LEV?", ":CURR:LEV %g", "AC current level, in Amps", validator=strict_range, values=[0, 0.1]) frequency = Instrument.control(":FREQ:CW?", ":FREQ:CW %g", "AC frequency (range depending on model), in Hertz", validator=strict_range, values=[20, 2e6]) # FETCH? returns [A,B,state]: impedance returns only A,B impedance = Instrument.measurement(":FETCH?", "Measured data A and B, according to :attr:`~.AgilentE4980.mode`", get_process=lambda x: x[:2]) mode = Instrument.control("FUNCtion:IMPedance:TYPE?", "FUNCtion:IMPedance:TYPE %s", """ Select quantities to be measured: * CPD: Parallel capacitance [F] and dissipation factor [number] * CPQ: Parallel capacitance [F] and quality factor [number] * CPG: Parallel capacitance [F] and parallel conductance [S] * CPRP: Parallel capacitance [F] and parallel resistance [Ohm] * CSD: Series capacitance [F] and dissipation factor [number] * CSQ: Series capacitance [F] and quality factor [number] * CSRS: Series capacitance [F] and series resistance [Ohm] * LPD: Parallel inductance [H] and dissipation factor [number] * LPQ: Parallel inductance [H] and quality factor [number] * LPG: Parallel inductance [H] and parallel conductance [S] * LPRP: Parallel inductance [H] and parallel resistance [Ohm] * LSD: Series inductance [H] and dissipation factor [number] * LSQ: Seriesinductance [H] and quality factor [number] * LSRS: Series inductance [H] and series resistance [Ohm] * RX: Resitance [Ohm] and reactance [Ohm] * ZTD: Impedance, magnitude [Ohm] and phase [deg] * ZTR: Impedance, magnitude [Ohm] and phase [rad] * GB: Conductance [S] and susceptance [S] * YTD: Admittance, magnitude [Ohm] and phase [deg] * YTR: Admittance magnitude [Ohm] and phase [rad] """, validator=strict_discrete_set, values=["CPD", "CPQ", "CPG", "CPRP", "CSD", "CSQ", "CSRS", "LPD", "LPQ", "LPG", "LPRP", "LSD", "LSQ", "LSRS", "RX", "ZTD", "ZTR", "GB", "YTD", "YTR",]) trigger_source = Instrument.control("TRIG:SOUR?", "TRIG:SOUR %s", """ Select trigger source; accept the values: * HOLD: manual * INT: internal * BUS: external bus (GPIB/LAN/USB) * EXT: external connector""", validator=strict_discrete_set, values = ["HOLD", "INT", "BUS", "EXT"]) def __init__(self, adapter, **kwargs): super(AgilentE4980, self).__init__( adapter, "Agilent E4980A/AL LCR meter", **kwargs ) self.timeout = 30000 # format: output ascii self.write("FORM ASC") def freq_sweep(self, freq_list, return_freq=False): """ Run frequency list sweep using sequential trigger. :param freq_list: list of frequencies :param return_freq: if True, returns the frequencies read from the instrument Returns values as configured with :attr:`~.AgilentE4980.mode` """ # manual, page 299 # self.write("*RST;*CLS") self.write("TRIG:SOUR BUS") self.write("DISP:PAGE LIST") self.write("FORM ASC") #trigger in sequential mode self.write("LIST:MODE SEQ") lista_str = ",".join(['%e' % f for f in freq_list]) self.write("LIST:FREQ %s" % lista_str) # trigger self.write("INIT:CONT ON") self.write(":TRIG:IMM") #wait for completed measurement #using the Error signal (there should be a better way) while 1: try: measured = self.values(":FETCh:IMPedance:FORMatted?") break except VisaIOError: pass #at the end return to manual trigger self.write(":TRIG:SOUR HOLD") # gets 4-ples of numbers, first two are data A and B a_data = [measured[_] for _ in range(0, 4*len(freq_list), 4)] b_data = [measured[_] for _ in range(1, 4*len(freq_list), 4)] if return_freq: read_freqs = self.values("LIST:FREQ?") return a_data, b_data, read_freqs else: return a_data, b_data # TODO: maybe refactor as property? def aperture(self, time=None, averages=1): """ Set and get aperture. :param time: integration time as string: SHORT, MED, LONG (case insensitive); if None, get values :param averages: number of averages, numeric """ if time is None: read_values = self.ask(":APER?").split(',') return read_values[0], int(read_values[1]) else: if time.upper() in ["SHORT", "MED", "LONG"]: self.write(":APER {0}, {1}".format(time, averages)) else: raise Exception("Time must be a string: SHORT, MED, LONG") PyMeasure-0.5/pymeasure/instruments/agilent/agilent8722ES.py0000644000175000017500000001631013137471263024332 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument, discreteTruncate, RangeException import numpy as np import re from io import BytesIO class Agilent8722ES(Instrument): """ Represents the Agilent8722ES Vector Network Analyzer and provides a high-level interface for taking scans of the scattering parameters. """ SCAN_POINT_VALUES = [3, 11, 21, 26, 51, 101, 201, 401, 801, 1601] SCATTERING_PARAMETERS = ("S11", "S12", "S21", "S22") S11, S12, S21, S22 = SCATTERING_PARAMETERS start_frequency = Instrument.control( "STAR?", "STAR %e Hz", """ A floating point property that represents the start frequency in Hz. This property can be set. """ ) stop_frequency = Instrument.control( "STOP?", "STOP %e Hz", """ A floating point property that represents the stop frequency in Hz. This property can be set. """ ) sweep_time = Instrument.control( "SWET?", "SWET%.2e", """ A floating point property that represents the sweep time in seconds. This property can be set. """ ) def __init__(self, resourceName, **kwargs): super(Agilent8722ES, self).__init__( resourceName, "Agilent 8722ES Vector Network Analyzer", **kwargs ) def set_fixed_frequency(self, frequency): """ Sets the scan to be of only one frequency in Hz """ self.start_frequency = frequency self.stop_frequency = frequency self.scan_points = 3 @property def parameter(self): for parameter in Agilent8722ES.SCATTERING_PARAMETERS: if int(self.values("%s?" % parameter)) == 1: return parameter return None @parameter.setter def parameter(self, value): if value in Agilent8722ES.SCATTERING_PARAMETERS: self.write("%s" % value) else: raise Exception("Invalid scattering parameter requested" " for Agilent 8722ES") @property def scan_points(self): """ Gets the number of scan points """ search = re.search(r"\d\.\d+E[+-]\d{2}$", self.ask("POIN?"), re.MULTILINE) if search: return int(float(search.group())) else: raise Exception("Improper message returned for the" " number of points") @scan_points.setter def scan_points(self, points): """ Sets the number of scan points, truncating to an allowed value if not properly provided """ points = discreteTruncate(points, Agilent8722ES.SCAN_POINT_VALUES) if points: self.write("POIN%d" % points) else: raise RangeException("Maximum scan points (1601) for" " Agilent 8722ES exceeded") def set_IF_bandwidth(self, bandwidth): """ Sets the resolution bandwidth (IF bandwidth) """ allowedBandwidth = [10, 30, 100, 300, 1000, 3000, 3700, 6000] bandwidth = discreteTruncate(bandwidth, allowedBandwidth) if bandwidth: self.write("IFBW%d" % bandwidth) else: raise RangeException("Maximum IF bandwidth (6000) for Agilent " "8722ES exceeded") def set_averaging(self, averages): """ Turns on averaging of a specific number between 0 and 999 """ if int(averages) > 999 or int(averages) < 0: raise RangeException("Averaging must be in the range 0 to 999") else: self.write("AVERO1") self.write("AVERFACT%d" % int(averages)) def disable_averaging(self): """ Disables averaging """ self.write("AVERO0") def is_averaging(self): """ Returns True if averaging is enabled """ return self.ask("AVERO?") == '1\n' def restart_averaging(self, averages): if int(averages) > 999 or int(averages) < 0: raise RangeException("Averaging must be in the range 0 to 999") else: self.write("NUMG%d" % averages) def scan(self, averages=1, blocking=True, timeout=25, delay=0.1): """ Initiates a scan with the number of averages specified and blocks until the operation is complete if blocking is True """ if averages == 1: self.disableAveraging() self.setSingleSweep() else: self.setAveraging(averages) self.write("*CLS;SRE 4;ESNB 1;") self.restartAveraging(averages) if blocking: self.adapter.wait_for_srq(timeout, delay) def is_scan_complete(): pass # TODO: Implement method for determining if the scan is completed def scan_single(self): """ Initiates a single scan """ self.write("SING") def scan_continuous(self): """ Initiates a continuous scan """ self.write("CONT") @property def frequencies(self): """ Returns a list of frequencies from the last scan """ return np.linspace( self.start_frequency, self.stop_frequency, num=self.scan_points ) @property def data(self): """ Returns the real and imaginary data from the last scan """ # TODO: Implement binary transfer instead of ASCII data = np.loadtxt( BytesIO(self.ask("FORM4;OUTPDATA")), delimiter=',', dtype=np.float32 ) return data[:, 0], data[:, 1] def log_magnitude(self, real, imaginary): """ Returns the magnitude in dB from a real and imaginary number or numpy arrays """ return 20*np.log10(self.magnitude(real, imaginary)) def magnitude(self, real, imaginary): """ Returns the magnitude from a real and imaginary number or numpy arrays """ return np.sqrt(real**2 + imaginary**2) def phase(self, real, imaginary): """ Returns the phase in degrees from a real and imaginary number or numpy arrays """ return np.arctan2(imaginary, real)*180/np.pi PyMeasure-0.5/pymeasure/instruments/agilent/__init__.py0000644000175000017500000000251613137471263023676 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .agilent8257D import Agilent8257D from .agilent8722ES import Agilent8722ES from .agilentE4408B import AgilentE4408B from .agilentE4980 import AgilentE4980 from .agilent34410A import Agilent34410A PyMeasure-0.5/pymeasure/instruments/agilent/agilentE4408B.py0000644000175000017500000001033113137471263024303 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument from pymeasure.instruments.validators import truncated_range from io import StringIO import numpy as np import pandas as pd class AgilentE4408B(Instrument): """ Represents the AgilentE4408B Spectrum Analyzer and provides a high-level interface for taking scans of high-frequency spectrums """ start_frequency = Instrument.control( ":SENS:FREQ:STAR?;", ":SENS:FREQ:STAR %e Hz;", """ A floating point property that represents the start frequency in Hz. This property can be set. """ ) stop_frequency = Instrument.control( ":SENS:FREQ:STOP?;", ":SENS:FREQ:STOP %e Hz;", """ A floating point property that represents the stop frequency in Hz. This property can be set. """ ) frequency_points = Instrument.control( ":SENSe:SWEEp:POINts?;", ":SENSe:SWEEp:POINts %d;", """ An integer property that represents the number of frequency points in the sweep. This property can take values from 101 to 8192. """, validator=truncated_range, values=[101, 8192], cast=int ) frequency_step = Instrument.control( ":SENS:FREQ:CENT:STEP:INCR?;", ":SENS:FREQ:CENT:STEP:INCR %g Hz;", """ A floating point property that represents the frequency step in Hz. This property can be set. """ ) center_frequency = Instrument.control( ":SENS:FREQ:CENT?;", ":SENS:FREQ:CENT %e Hz;", """ A floating point property that represents the center frequency in Hz. This property can be set. """ ) sweep_time = Instrument.control( ":SENS:SWE:TIME?;", ":SENS:SWE:TIME %.2e;", """ A floating point property that represents the sweep time in seconds. This property can be set. """ ) def __init__(self, resourceName, **kwargs): super(AgilentE4408B, self).__init__( resourceName, "Agilent E4408B Spectrum Analyzer", **kwargs ) @property def frequencies(self): """ Returns a numpy array of frequencies in Hz that correspond to the current settings of the instrument. """ return np.linspace( self.start_frequency, self.stop_frequency, self.frequency_points, dtype=np.float64 ) def trace(self, number=1): """ Returns a numpy array of the data for a particular trace based on the trace number (1, 2, or 3). """ self.write(":FORMat:TRACe:DATA ASCII;") data = np.loadtxt( StringIO(self.ask(":TRACE:DATA? TRACE%d;" % number)), delimiter=',', dtype=np.float64 ) return data def trace_df(self, number=1): """ Returns a pandas DataFrame containing the frequency and peak data for a particular trace, based on the trace number (1, 2, or 3). """ return pd.DataFrame({ 'Frequency (GHz)': self.frequencies*1e-9, 'Peak (dB)': self.trace(number) }) PyMeasure-0.5/pymeasure/instruments/lakeshore/0000755000175000017500000000000013171765525022120 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/lakeshore/lakeshore331.py0000644000175000017500000001172513137471263024677 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from time import sleep, time from pymeasure.instruments import Instrument from pymeasure.instruments.validators import strict_discrete_set class LakeShore331(Instrument): """ Represents the Lake Shore 331 Temperature Controller and provides a high-level interface for interacting with the instrument. .. code-block:: python controller = LakeShore331("GPIB::1") print(controller.setpoint_1) # Print the current setpoint for loop 1 controller.setpoint_1 = 50 # Change the setpoint to 50 K controller.heater_range = 'low' # Change the heater range to Low controller.wait_for_temperature() # Wait for the temperature to stabilize print(controller.temperature_A) # Print the temperature at sensor A """ temperature_A = Instrument.measurement( "KRDG? A", """ Reads the temperature of the sensor A in Kelvin. """ ) temperature_B = Instrument.measurement( "KRDG? B", """ Reads the temperature of the sensor B in Kelvin. """ ) setpoint_1 = Instrument.control( "SETP? 1", "SETP 1, %g", """ A floating point property that controls the setpoint temperature in Kelvin for Loop 1. """ ) setpoint_2 = Instrument.control( "SETP? 2", "SETP 2, %g", """ A floating point property that controls the setpoint temperature in Kelvin for Loop 2. """ ) heater_range = Instrument.control( "RANGE?", "RANGE %d", """ A string property that controls the heater range, which can take the values: off, low, medium, and high. These values correlate to 0, 0.5, 5 and 50 W respectively. """, validator=strict_discrete_set, values={'off':0, 'low':1, 'medium':2, 'high':3}, map_values=True ) def __init__(self, adapter, **kwargs): super(LakeShore331, self).__init__( adapter, "Lake Shore 331 Temperature Controller", **kwargs ) def disable_heater(self): """ Turns the :attr:`~.heater_range` to :code:`off` to disable the heater. """ self.heater_range = 'off' def wait_for_temperature(self, accuracy=0.1, interval=0.1, sensor='A', setpoint=1, timeout=360, should_stop=lambda: False): """ Blocks the program, waiting for the temperature to reach the setpoint within the accuracy (%), checking this each interval time in seconds. :param accuracy: An acceptable percentage deviation between the setpoint and temperature :param interval: A time in seconds that controls the refresh rate :param sensor: The desired sensor to read, either A or B :param setpoint: The desired setpoint loop to read, either 1 or 2 :param timeout: A timeout in seconds after which an exception is raised :param should_stop: A function that returns True if waiting should stop, by default this always returns False """ temperature_name = 'temperature_%s' % sensor setpoint_name = 'setpoint_%d' % setpoint # Only get the setpoint once, assuming it does not change setpoint_value = getattr(self, setpoint_name) def percent_difference(temperature): return abs(100*(temperature - setpoint_value)/setpoint_value) t = time() while percent_difference(getattr(self, temperature_name)) > accuracy: sleep(interval) if (time()-t) > timeout: raise Exception(( "Timeout occurred after waiting %g seconds for " "the LakeShore 331 temperature to reach %g K." ) % (timeout, setpoint)) if should_stop(): return PyMeasure-0.5/pymeasure/instruments/lakeshore/adapters.py0000644000175000017500000000410213137471263024265 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.adapters import SerialAdapter class LakeShoreUSBAdapter(SerialAdapter): """ Provides a :class:`SerialAdapter` with the specific baudrate, timeout, parity, and byte size for LakeShore USB communication. Initiates the adapter to open serial communcation over the supplied port. :param port: A string representing the serial port """ def __init__(self, port): super(LakeShoreUSBAdapter, self).__init__( port, baudrate=57600, timeout=0.5, parity='O', bytesize=7 ) def write(self, command): """ Overwrites the :func:`SerialAdapter.write ` method to automatically append a Unix-style linebreak at the end of the command. :param command: SCPI command string to be sent to the instrument """ super(LakeShoreUSBAdapter, self).write(command + "\n") PyMeasure-0.5/pymeasure/instruments/lakeshore/__init__.py0000644000175000017500000000237513137471263024233 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .adapters import LakeShoreUSBAdapter from .lakeshore425 import LakeShore425 from .lakeshore331 import LakeShore331 PyMeasure-0.5/pymeasure/instruments/lakeshore/lakeshore425.py0000644000175000017500000001045613137471263024703 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from pymeasure.instruments import Instrument from pymeasure.instruments.validators import strict_discrete_set, truncated_discrete_set from .adapters import LakeShoreUSBAdapter from time import sleep import numpy as np class LakeShore425(Instrument): """ Represents the LakeShore 425 Gaussmeter and provides a high-level interface for interacting with the instrument To allow user access to the LakeShore 425 Gaussmeter in Linux, create the file: :code:`/etc/udev/rules.d/52-lakeshore425.rules`, with contents: .. code-block:: none SUBSYSTEMS=="usb",ATTRS{idVendor}=="1fb9",ATTRS{idProduct}=="0401",MODE="0666",SYMLINK+="lakeshore425" Then reload the udev rules with: .. code-block:: bash sudo udevadm control --reload-rules sudo udevadm trigger The device will be accessible through :code:`/dev/lakeshore425`. """ field = Instrument.measurement( "RDGFIELD?", """ Returns the field in the current units """ ) unit = Instrument.control( "UNIT?", "UNIT %d", """ A string property that controls the units of the instrument, which can take the values of G, T, Oe, or A/m. """, validator=strict_discrete_set, values={'G':1, 'T':2, 'Oe':3, 'A/m':4}, map_values=True ) range = Instrument.control( "RANGE?", "RANGE %d", """ A floating point property that controls the field range in units of Gauss, which can take the values 35, 350, 3500, and 35,000 G. """, validator=truncated_discrete_set, values={35:1, 350:2, 3500:3, 35000:4}, map_values=True ) def __init__(self, port): super(LakeShore425, self).__init__( LakeShoreUSBAdapter(port), "LakeShore 425 Gaussmeter", ) def auto_range(self): """ Sets the field range to automatically adjust """ self.write("AUTO") def dc_mode(self, wideband=True): """ Sets up a steady-state (DC) measurement of the field """ if wideband: self.mode = (1, 0, 1) else: self.mode(1, 0, 2) def ac_mode(self, wideband=True): """ Sets up a measurement of an oscillating (AC) field """ if wideband: self.mode = (2, 1, 1) else: self.mode = (2, 1, 2) @property def mode(self): return tuple(self.values("RDGMODE?")) @mode.setter def mode(self, value): """ Provides access to directly setting the mode, filter, and bandwidth settings """ mode, filter, band = value self.write("RDGMODE %d,%d,%d" % (mode, filter, band)) def zero_probe(self): """ Initiates the zero field sequence to calibrate the probe """ self.write("ZPROBE") def measure(self, points, has_aborted=lambda: False, delay=1e-3): """Returns the mean and standard deviation of a given number of points while blocking """ data = np.zeros(points, dtype=np.float32) for i in range(points): if has_aborted(): break data[i] = self.field sleep(delay) return data.mean(), data.std() PyMeasure-0.5/pymeasure/instruments/ni/0000755000175000017500000000000013171765525020551 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/instruments/ni/daqmx.py0000644000175000017500000001435313143613470022231 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # # Most of this code originally from: # http://www.scipy.org/Cookbook/Data_Acquisition_with_NIDAQmx import ctypes import numpy as np from sys import platform if platform == "win32": nidaq = ctypes.windll.nicaiu # Data Types int32 = ctypes.c_long uInt32 = ctypes.c_ulong uInt64 = ctypes.c_ulonglong float64 = ctypes.c_double TaskHandle = uInt32 # Constants DAQmx_Val_Cfg_Default = int32(-1) DAQmx_Val_Volts = 10348 DAQmx_Val_Rising = 10280 DAQmx_Val_FiniteSamps = 10178 DAQmx_Val_GroupByChannel = 1 class DAQmx(object): """Instrument object for interfacing with NI-DAQmx devices.""" def __init__(self, name, *args, **kwargs): super(DAQmx, self).__init__() self.resourceName = name self.numChannels = 0 self.numSamples = 0 self.dataBuffer = 0 self.taskHandleAI = TaskHandle(0) self.taskHandleAO = TaskHandle(0) self.terminated = False def setup_analog_voltage_in(self, channelList, numSamples, sampleRate=10000, scale=3.0): resourceString = "" for num, channel in enumerate(channelList): if num > 0: resourceString += ", " # Add a comma before entries 2 and so on resourceString += self.resourceName + "/ai" + str(num) self.numChannels = len(channelList) self.numSamples = numSamples self.taskHandleAI = TaskHandle(0) self.dataBuffer = np.zeros((self.numSamples,self.numChannels), dtype=np.float64) self.CHK(nidaq.DAQmxCreateTask("",ctypes.byref(self.taskHandleAI))) self.CHK(nidaq.DAQmxCreateAIVoltageChan(self.taskHandleAI,resourceString,"", DAQmx_Val_Cfg_Default, float64(-scale),float64(scale), DAQmx_Val_Volts,None)) self.CHK(nidaq.DAQmxCfgSampClkTiming(self.taskHandleAI,"",float64(sampleRate), DAQmx_Val_Rising,DAQmx_Val_FiniteSamps, uInt64(self.numSamples))); def setupAnalogVoltageOut(self, channel=0): resourceString = self.resourceName + "/ao" + str(channel) self.taskHandleAO = TaskHandle(0) self.CHK(nidaq.DAQmxCreateTask("", ctypes.byref( self.taskHandleAO ))) self.CHK(nidaq.DAQmxCreateAOVoltageChan( self.taskHandleAO, resourceString, "", float64(-10.0), float64(10.0), DAQmx_Val_Volts, None)) def setupAnalogVoltageOutMultipleChannels(self, channelList): resourceString = "" for num, channel in enumerate(channelList): if num > 0: resourceString += ", " # Add a comma before entries 2 and so on resourceString += self.resourceName + "/ao" + str(num) self.taskHandleAO = TaskHandle(0) self.CHK(nidaq.DAQmxCreateTask("", ctypes.byref( self.taskHandleAO ))) self.CHK(nidaq.DAQmxCreateAOVoltageChan( self.taskHandleAO, resourceString, "", float64(-10.0), float64(10.0), DAQmx_Val_Volts, None)) def writeAnalogVoltage(self, value): timeout = -1.0 self.CHK(nidaq.DAQmxWriteAnalogScalarF64( self.taskHandleAO, 1, # Autostart float64(timeout), float64(value), None)) def writeAnalogVoltageMultipleChannels(self, values): timeout = -1.0 self.CHK(nidaq.DAQmxWriteAnalogF64( self.taskHandleAO, 1, # Samples per channel 1, # Autostart float64(timeout), DAQmx_Val_GroupByChannel, (np.array(values)).ctypes.data, None, None)) def acquire(self): read = int32() self.CHK(nidaq.DAQmxReadAnalogF64(self.taskHandleAI ,self.numSamples,float64(10.0), DAQmx_Val_GroupByChannel, self.dataBuffer.ctypes.data, self.numChannels*self.numSamples,ctypes.byref(read),None)) return self.dataBuffer.transpose() def acquireAverage(self): if not self.terminated: avg = np.mean(self.acquire(), axis=1) return avg else: return np.zeros(3) def stop(self): if self.taskHandleAI.value != 0: nidaq.DAQmxStopTask(self.taskHandleAI) nidaq.DAQmxClearTask(self.taskHandleAI) if self.taskHandleAO.value != 0: nidaq.DAQmxStopTask(self.taskHandleAO) nidaq.DAQmxClearTask(self.taskHandleAO) def CHK(self, err): """a simple error checking routine""" if err < 0: buf_size = 100 buf = ctypes.create_string_buffer('\000' * buf_size) nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size) raise RuntimeError('nidaq call failed with error %d: %s'%(err,repr(buf.value))) if err > 0: buf_size = 100 buf = ctypes.create_string_buffer('\000' * buf_size) nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size) raise RuntimeError('nidaq generated warning %d: %s'%(err,repr(buf.value))) def shutdown(self): self.stop() self.terminated = True PyMeasure-0.5/pymeasure/instruments/ni/__init__.py0000644000175000017500000000223613137471263022660 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from .daqmx import DAQmx PyMeasure-0.5/pymeasure/instruments/ni/nidaq.py0000644000175000017500000000467413137471263022225 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # # Requires 'instrumental' package: https://github.com/mabuchilab/Instrumental from instrumental.drivers.daq import ni from pymeasure.instruments import Instrument def get_dict_attr(obj,attr): for obj in [obj]+obj.__class__.mro(): if attr in obj.__dict__: return obj.__dict__[attr] raise AttributeError class NIDAQ(Instrument): ''' Instrument driver for NIDAQ card. ''' def __init__(self, name='Dev1', *args, **kwargs): self._daq = ni.NIDAQ(name) super(NIDAQ, self).__init__( None, "NIDAQ", includeSCPI = False, **kwargs) for chan in self._daq.get_AI_channels(): self.add_property(chan) for chan in self._daq.get_AO_channels(): self.add_property(chan, set=True) def add_property(self, chan, set=False): if set: fset = lambda self, value: self.set_chan(chan, value) fget = lambda self: getattr(self, '_%s' %chan) setattr(self, '_%s' %chan, None) setattr(self.__class__,chan,property(fset=fset, fget=fget)) else: fget = lambda self: self.get_chan(chan) setattr(self.__class__,chan,property(fget=fget)) setattr(self.get, chan, lambda: getattr(self, chan)) def get_chan(self, chan): return getattr(self._daq,chan).read().magnitude def set_chan(self, chan, value): setattr(self, '_%s' %chan, value) getattr(self._daq,chan).write('%sV' %value)PyMeasure-0.5/pymeasure/instruments/mock.py0000644000175000017500000000606413137471263021447 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import numpy import time from pymeasure.adapters import FakeAdapter from pymeasure.instruments import Instrument class Mock(Instrument): """Mock instrument for testing.""" def __init__(self, wait=.1, **kwargs): super().__init__( FakeAdapter, "Mock instrument", includeSCPI=False, **kwargs ) self._wait = wait self._tstart = 0 self._voltage = 10 self._output_voltage = 0 self._time = 0 self._wave = self.wave self._units = {'voltage': 'V', 'output_voltage': 'V', 'time': 's', 'wave': 'a.u.'} def get_time(self): """Get elapsed time""" if self._tstart == 0: self._tstart = time.time() self._time = time.time() - self._tstart return self._time def set_time(self, value): """ Wait for the timer to reach the specified time. If value = 0, reset. """ if value == 0: self._tstart = 0 else: while self.time < value: time.sleep(0.001) def reset_time(self): """Reset the timer to 0 s.""" self.time = 0 self.get_time() time = property(fget=get_time, fset=set_time) def get_wave(self): """Get wave.""" return float(numpy.sin(self.time)) wave = property(fget=get_wave) def get_voltage(self): """Get the voltage.""" time.sleep(self._wait) return self._voltage def __getitem__(self, keys): return keys voltage = property(fget=get_voltage) def get_output_voltage(self): return self._output_voltage def set_output_voltage(self, value): """Set the voltage.""" time.sleep(self._wait) self._output_voltage = value output_voltage = property(fget=get_output_voltage, fset=set_output_voltage) PyMeasure-0.5/pymeasure/display/0000755000175000017500000000000013171765525017215 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/display/listeners.py0000644000175000017500000001054513137471263021577 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from .Qt import QtCore from .thread import StoppableQThread from ..experiment.procedure import Procedure log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) try: import zmq import cloudpickle except ImportError: zmq = None cloudpickle = None log.warning("ZMQ and cloudpickle are required for TCP communication") class QListener(StoppableQThread): """Base class for QThreads that need to listen for messages on a ZMQ TCP port and can be stopped by a thread- and process-safe method call """ def __init__(self, port, topic='', timeout=0.01): """ Constructs the Listener object with a subscriber port over which to listen for messages :param port: TCP port to listen on :param topic: Topic to listen on :param timeout: Timeout in seconds to recheck stop flag """ super().__init__() self.port = port self.topic = topic self.context = zmq.Context() log.debug("%s has ZMQ Context: %r" % (self.__class__.__name__, self.context)) self.subscriber = self.context.socket(zmq.SUB) self.subscriber.connect('tcp://localhost:%d' % port) self.subscriber.setsockopt(zmq.SUBSCRIBE, topic.encode()) log.info("%s connected to '%s' topic on tcp://localhost:%d" % ( self.__class__.__name__, topic, port)) self.poller = zmq.Poller() self.poller.register(self.subscriber, zmq.POLLIN) self.timeout = timeout def receive(self, flags=0): topic, record = self.subscriber.recv_serialized(deserialize=cloudpickle.loads, flags=flags) return topic, record def message_waiting(self): return self.poller.poll(self.timeout) def __repr__(self): return "<%s(port=%s,topic=%s,should_stop=%s)>" % ( self.__class__.__name__, self.port, self.topic, self.should_stop()) class Monitor(QtCore.QThread): """ Monitor listens for status and progress messages from a Worker through a queue to ensure no messages are losts """ status = QtCore.QSignal(int) progress = QtCore.QSignal(float) log = QtCore.QSignal(object) worker_running = QtCore.QSignal() worker_failed = QtCore.QSignal() worker_finished = QtCore.QSignal() # Distinguished from QThread.finished worker_abort_returned = QtCore.QSignal() def __init__(self, queue): super().__init__() self.queue = queue def run(self): while True: data = self.queue.get() if data is None: break topic, data = data if topic == 'status': self.status.emit(data) if data == Procedure.RUNNING: self.worker_running.emit() elif data == Procedure.FAILED: self.worker_failed.emit() elif data == Procedure.FINISHED: self.worker_finished.emit() elif data == Procedure.ABORTED: self.worker_abort_returned.emit() elif topic == 'progress': self.progress.emit(data) elif topic == 'log': self.log.emit(data) log.info("Monitor caught stop command") PyMeasure-0.5/pymeasure/display/log.py0000644000175000017500000000304113137471263020341 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from logging import Handler from .Qt import QtCore log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class LogHandler(QtCore.QObject, Handler): record = QtCore.QSignal(object) def __init__(self, parent=None): QtCore.QObject.__init__(self, parent) Handler.__init__(self) def emit(self, record): self.record.emit(self.format(record)) PyMeasure-0.5/pymeasure/display/widgets.py0000644000175000017500000003533013137471263021234 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import os import re import pyqtgraph as pg from .browser import Browser from .curves import ResultsCurve, Crosshairs from .inputs import BooleanInput, IntegerInput, ListInput, ScientificInput, StringInput from .log import LogHandler from .Qt import QtCore, QtGui from ..experiment import parameters, Procedure from ..experiment.results import Results log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class PlotFrame(QtGui.QFrame): """ Combines a PyQtGraph Plot with Crosshairs. Refreshes the plot based on the refresh_time, and allows the axes to be changed on the fly, which updates the plotted data """ LABEL_STYLE = {'font-size': '10pt', 'font-family': 'Arial', 'color': '#000000'} updated = QtCore.QSignal() x_axis_changed = QtCore.QSignal(str) y_axis_changed = QtCore.QSignal(str) def __init__(self, x_axis=None, y_axis=None, refresh_time=0.2, check_status=True, parent=None): super().__init__(parent) self.refresh_time = refresh_time self.check_status = check_status self._setup_ui() self.change_x_axis(x_axis) self.change_y_axis(y_axis) def _setup_ui(self): self.setAutoFillBackground(False) self.setStyleSheet("background: #fff") self.setFrameShape(QtGui.QFrame.StyledPanel) self.setFrameShadow(QtGui.QFrame.Sunken) self.setMidLineWidth(1) vbox = QtGui.QVBoxLayout(self) self.plot_widget = pg.PlotWidget(self, background='#ffffff') self.coordinates = QtGui.QLabel(self) self.coordinates.setMinimumSize(QtCore.QSize(0, 20)) self.coordinates.setStyleSheet("background: #fff") self.coordinates.setText("") self.coordinates.setAlignment( QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) vbox.addWidget(self.plot_widget) vbox.addWidget(self.coordinates) self.setLayout(vbox) self.plot = self.plot_widget.getPlotItem() self.crosshairs = Crosshairs(self.plot, pen=pg.mkPen(color='#AAAAAA', style=QtCore.Qt.DashLine)) self.crosshairs.coordinates.connect(self.update_coordinates) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.update_curves) self.timer.timeout.connect(self.crosshairs.update) self.timer.timeout.connect(self.updated) self.timer.start(int(self.refresh_time * 1e3)) def update_coordinates(self, x, y): self.coordinates.setText("(%g, %g)" % (x, y)) def update_curves(self): for item in self.plot.items: if isinstance(item, ResultsCurve): if self.check_status: if item.results.procedure.status == Procedure.RUNNING: item.update() else: item.update() def parse_axis(self, axis): """ Returns the units of an axis by searching the string """ units_pattern = "\((?P\w+)\)" match = re.search(units_pattern, axis) if match: if 'units' in match.groupdict(): label = re.sub(units_pattern, '', axis) return label, match.groupdict()['units'] else: return axis, None def change_x_axis(self, axis): for item in self.plot.items: if isinstance(item, ResultsCurve): item.x = axis item.update() label, units = self.parse_axis(axis) self.plot.setLabel('bottom', label, units=units, **self.LABEL_STYLE) self.x_axis = axis self.x_axis_changed.emit(axis) def change_y_axis(self, axis): for item in self.plot.items: if isinstance(item, ResultsCurve): item.y = axis item.update() label, units = self.parse_axis(axis) self.plot.setLabel('left', label, units=units, **self.LABEL_STYLE) self.y_axis = axis self.y_axis_changed.emit(axis) class PlotWidget(QtGui.QWidget): """ Extends the PlotFrame to allow different columns of the data to be dynamically choosen """ def __init__(self, columns, x_axis=None, y_axis=None, refresh_time=0.2, check_status=True, parent=None): super().__init__(parent) self.columns = columns self.refresh_time = refresh_time self.check_status = check_status self._setup_ui() self._layout() if x_axis is not None: self.columns_x.setCurrentIndex(self.columns_x.findText(x_axis)) self.plot_frame.change_x_axis(x_axis) if y_axis is not None: self.columns_y.setCurrentIndex(self.columns_y.findText(y_axis)) self.plot_frame.change_y_axis(y_axis) def _setup_ui(self): self.columns_x_label = QtGui.QLabel(self) self.columns_x_label.setMaximumSize(QtCore.QSize(45, 16777215)) self.columns_x_label.setText('X Axis:') self.columns_y_label = QtGui.QLabel(self) self.columns_y_label.setMaximumSize(QtCore.QSize(45, 16777215)) self.columns_y_label.setText('Y Axis:') self.columns_x = QtGui.QComboBox(self) self.columns_y = QtGui.QComboBox(self) for column in self.columns: self.columns_x.addItem(column) self.columns_y.addItem(column) self.columns_x.activated.connect(self.update_x_column) self.columns_y.activated.connect(self.update_y_column) self.plot_frame = PlotFrame( self.columns[0], self.columns[1], self.refresh_time, self.check_status ) self.updated = self.plot_frame.updated self.plot = self.plot_frame.plot self.columns_x.setCurrentIndex(0) self.columns_y.setCurrentIndex(1) def _layout(self): vbox = QtGui.QVBoxLayout(self) vbox.setSpacing(0) hbox = QtGui.QHBoxLayout() hbox.setSpacing(10) hbox.setContentsMargins(-1, 6, -1, 6) hbox.addWidget(self.columns_x_label) hbox.addWidget(self.columns_x) hbox.addWidget(self.columns_y_label) hbox.addWidget(self.columns_y) vbox.addLayout(hbox) vbox.addWidget(self.plot_frame) self.setLayout(vbox) def sizeHint(self): return QtCore.QSize(300, 600) def new_curve(self, results, color=pg.intColor(0), **kwargs): if 'pen' not in kwargs: kwargs['pen'] = pg.mkPen(color=color, width=2) if 'antialias' not in kwargs: kwargs['antialias'] = False curve = ResultsCurve(results, x=self.plot_frame.x_axis, y=self.plot_frame.y_axis, **kwargs ) curve.setSymbol(None) curve.setSymbolBrush(None) return curve def update_x_column(self, index): axis = self.columns_x.itemText(index) self.plot_frame.change_x_axis(axis) def update_y_column(self, index): axis = self.columns_y.itemText(index) self.plot_frame.change_y_axis(axis) class BrowserWidget(QtGui.QWidget): def __init__(self, *args, parent=None): super().__init__(parent) self.browser_args = args self._setup_ui() self._layout() def _setup_ui(self): self.browser = Browser(*self.browser_args, parent=self) self.clear_button = QtGui.QPushButton('Clear all', self) self.clear_button.setEnabled(False) self.hide_button = QtGui.QPushButton('Hide all', self) self.hide_button.setEnabled(False) self.show_button = QtGui.QPushButton('Show all', self) self.show_button.setEnabled(False) self.open_button = QtGui.QPushButton('Open', self) self.open_button.setEnabled(True) def _layout(self): vbox = QtGui.QVBoxLayout(self) vbox.setSpacing(0) hbox = QtGui.QHBoxLayout() hbox.setSpacing(10) hbox.setContentsMargins(-1, 6, -1, 6) hbox.addWidget(self.show_button) hbox.addWidget(self.hide_button) hbox.addWidget(self.clear_button) hbox.addStretch() hbox.addWidget(self.open_button) vbox.addLayout(hbox) vbox.addWidget(self.browser) self.setLayout(vbox) class InputsWidget(QtGui.QWidget): def __init__(self, procedure_class, inputs=(), parent=None): super().__init__(parent) self._procedure_class = procedure_class self._procedure = procedure_class() self._inputs = inputs self._setup_ui() self._layout() def _setup_ui(self): parameter_objects = self._procedure.parameter_objects() for name in self._inputs: parameter = parameter_objects[name] if parameter.ui_class is not None: element = parameter.ui_class(parameter) elif isinstance(parameter, parameters.FloatParameter): element = ScientificInput(parameter) elif isinstance(parameter, parameters.IntegerParameter): element = IntegerInput(parameter) elif isinstance(parameter, parameters.BooleanParameter): # noinspection PyArgumentList element = BooleanInput(parameter) # TODO not implemented yet elif isinstance(parameter, parameters.ListParameter): # noinspection PyArgumentList element = ListInput(parameter) # TODO not implemented yet elif isinstance(parameter, parameters.Parameter): element = StringInput(parameter) setattr(self, name, element) def _layout(self): vbox = QtGui.QVBoxLayout(self) vbox.setSpacing(6) parameters = self._procedure.parameter_objects() for name in self._inputs: label = QtGui.QLabel(self) label.setText("%s:" % parameters[name].name) vbox.addWidget(label) vbox.addWidget(getattr(self, name)) self.setLayout(vbox) def set_parameters(self, parameter_objects): for name in self._inputs: element = getattr(self, name) element.set_parameter(parameter_objects[name]) def get_procedure(self): """ Returns the current procedure """ self._procedure = self._procedure_class() parameter_values = {} for name in self._inputs: element = getattr(self, name) parameter_values[name] = element.parameter.value self._procedure.set_parameters(parameter_values) return self._procedure class LogWidget(QtGui.QWidget): def __init__(self, parent=None): super().__init__(parent) self._setup_ui() self._layout() def _setup_ui(self): self.view = QtGui.QPlainTextEdit() self.view.setReadOnly(True) self.handler = LogHandler() self.handler.setFormatter(logging.Formatter( fmt='%(asctime)s : %(message)s (%(levelname)s)', datefmt='%m/%d/%Y %I:%M:%S %p' )) self.handler.record.connect(self.view.appendPlainText) def _layout(self): vbox = QtGui.QVBoxLayout(self) vbox.setSpacing(0) vbox.addWidget(self.view) self.setLayout(vbox) class ResultsDialog(QtGui.QFileDialog): def __init__(self, columns, x_axis=None, y_axis=None, parent=None): super().__init__(parent) self.columns = columns self.x_axis, self.y_axis = x_axis, y_axis self._setup_ui() def _setup_ui(self): preview_tab = QtGui.QTabWidget() vbox = QtGui.QVBoxLayout() param_vbox = QtGui.QVBoxLayout() vbox_widget = QtGui.QWidget() param_vbox_widget = QtGui.QWidget() self.plot_widget = PlotWidget(self.columns, self.x_axis, self.y_axis, parent=self) self.plot = self.plot_widget.plot self.preview_param = QtGui.QTreeWidget() param_header = QtGui.QTreeWidgetItem(["Name", "Value"]) self.preview_param.setHeaderItem(param_header) self.preview_param.setColumnWidth(0, 150) self.preview_param.setAlternatingRowColors(True) vbox.addWidget(self.plot_widget) param_vbox.addWidget(self.preview_param) vbox_widget.setLayout(vbox) param_vbox_widget.setLayout(param_vbox) preview_tab.addTab(vbox_widget, "Plot Preview") preview_tab.addTab(param_vbox_widget, "Run Parameters") self.layout().addWidget(preview_tab, 0, 5, 4, 1) self.layout().setColumnStretch(5, 1) self.setMinimumSize(900, 500) self.resize(900, 500) self.setFileMode(QtGui.QFileDialog.ExistingFiles) self.currentChanged.connect(self.update_plot) def update_plot(self, filename): self.plot.clear() if not os.path.isdir(filename) and filename != '': try: results = Results.load(str(filename)) except ValueError: return except Exception as e: raise e curve = ResultsCurve(results, x=self.plot_widget.plot_frame.x_axis, y=self.plot_widget.plot_frame.y_axis, pen=pg.mkPen(color=(255, 0, 0), width=1.75), antialias=True ) curve.update() self.plot.addItem(curve) self.preview_param.clear() for key, param in results.procedure.parameter_objects().items(): new_item = QtGui.QTreeWidgetItem([param.name, str(param)]) self.preview_param.addTopLevelItem(new_item) self.preview_param.sortItems(0, QtCore.Qt.AscendingOrder) PyMeasure-0.5/pymeasure/display/Qt.py0000644000175000017500000000354613137471263020156 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from pyqtgraph.Qt import QtGui, QtCore, loadUiType log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) QtCore.QSignal = QtCore.pyqtSignal def fromUi(*args, **kwargs): """ Returns a Qt object constructed using loadUiType based on its arguments. All QWidget objects in the form class are set in the returned object for easy accessability. """ form_class, base_class = loadUiType(*args, **kwargs) widget = base_class() form = form_class() form.setupUi(widget) form.retranslateUi(widget) for name in dir(form): element = getattr(form, name) if isinstance(element, QtGui.QWidget): setattr(widget, name, element) return widget PyMeasure-0.5/pymeasure/display/windows.py0000644000175000017500000003576613137471263021275 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import os import pyqtgraph as pg from .browser import BrowserItem from .curves import ResultsCurve from .manager import Manager, Experiment from .Qt import QtCore, QtGui from .widgets import PlotWidget, BrowserWidget, InputsWidget, LogWidget, ResultsDialog from ..experiment.results import Results log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class PlotterWindow(QtGui.QMainWindow): def __init__(self, plotter, refresh_time=0.1, parent=None): super().__init__(parent) self.plotter = plotter self.refresh_time = refresh_time columns = plotter.results.procedure.DATA_COLUMNS self.setWindowTitle('Results Plotter') self.main = QtGui.QWidget(self) vbox = QtGui.QVBoxLayout(self.main) vbox.setSpacing(0) hbox = QtGui.QHBoxLayout() hbox.setSpacing(6) hbox.setContentsMargins(-1, 6, -1, -1) file_label = QtGui.QLabel(self.main) file_label.setText('Data Filename:') self.file = QtGui.QLineEdit(self.main) self.file.setText(plotter.results.data_filename) hbox.addWidget(file_label) hbox.addWidget(self.file) vbox.addLayout(hbox) self.plot_widget = PlotWidget(columns, refresh_time=self.refresh_time, check_status=False) self.plot = self.plot_widget.plot vbox.addWidget(self.plot_widget) self.main.setLayout(vbox) self.setCentralWidget(self.main) self.main.show() self.resize(800, 600) self.curve = ResultsCurve(plotter.results, columns[0], columns[1], pen=pg.mkPen(color=pg.intColor(0), width=2), antialias=False) self.plot.addItem(self.curve) self.plot_widget.updated.connect(self.check_stop) def quit(self, evt=None): log.info("Quitting the Plotter") self.close() self.plotter.stop() def check_stop(self): """ Checks if the Plotter should stop and exits the Qt main loop if so """ if self.plotter.should_stop(): QtCore.QCoreApplication.instance().quit() class ManagedWindow(QtGui.QMainWindow): """ The ManagedWindow uses a Manager to control Workers in a Queue, and provides a simple interface. The queue method must be overwritten by a child class which is required to pass an Experiment containing the Results and Procedure to self.manager.queue. """ EDITOR = 'gedit' def __init__(self, procedure_class, inputs=(), displays=(), x_axis=None, y_axis=None, log_channel='', log_level=logging.INFO, parent=None): super().__init__(parent) app = QtCore.QCoreApplication.instance() app.aboutToQuit.connect(self.quit) self.procedure_class = procedure_class self.inputs = inputs self.displays = displays self.log = logging.getLogger(log_channel) self.log_level = log_level log.setLevel(log_level) self.log.setLevel(log_level) self.x_axis, self.y_axis = x_axis, y_axis self._setup_ui() self._layout() def _setup_ui(self): self.log_widget = LogWidget() self.log.addHandler(self.log_widget.handler) # needs to be in Qt context? log.info("ManagedWindow connected to logging") self.queue_button = QtGui.QPushButton('Queue', self) self.queue_button.clicked.connect(self.queue) self.abort_button = QtGui.QPushButton('Abort', self) self.abort_button.setEnabled(False) self.abort_button.clicked.connect(self.abort) self.plot_widget = PlotWidget(self.procedure_class.DATA_COLUMNS, self.x_axis, self.y_axis) self.plot = self.plot_widget.plot self.browser_widget = BrowserWidget( self.procedure_class, self.displays, [self.x_axis, self.y_axis], parent=self ) self.browser_widget.show_button.clicked.connect(self.show_experiments) self.browser_widget.hide_button.clicked.connect(self.hide_experiments) self.browser_widget.clear_button.clicked.connect(self.clear_experiments) self.browser_widget.open_button.clicked.connect(self.open_experiment) self.browser = self.browser_widget.browser self.browser.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.browser.customContextMenuRequested.connect(self.browser_item_menu) self.browser.itemChanged.connect(self.browser_item_changed) self.inputs = InputsWidget( self.procedure_class, self.inputs, parent=self ) self.manager = Manager(self.plot, self.browser, log_level=self.log_level, parent=self) self.manager.abort_returned.connect(self.abort_returned) self.manager.queued.connect(self.queued) self.manager.running.connect(self.running) self.manager.finished.connect(self.finished) self.manager.log.connect(self.log.handle) def _layout(self): self.main = QtGui.QWidget(self) inputs_dock = QtGui.QWidget(self) inputs_vbox = QtGui.QVBoxLayout(self.main) hbox = QtGui.QHBoxLayout() hbox.setSpacing(10) hbox.setContentsMargins(-1, 6, -1, 6) hbox.addWidget(self.queue_button) hbox.addWidget(self.abort_button) hbox.addStretch() inputs_vbox.addWidget(self.inputs) inputs_vbox.addLayout(hbox) inputs_vbox.addStretch() inputs_dock.setLayout(inputs_vbox) dock = QtGui.QDockWidget('Input Parameters') dock.setWidget(inputs_dock) dock.setFeatures(QtGui.QDockWidget.NoDockWidgetFeatures) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock) tabs = QtGui.QTabWidget(self.main) tabs.addTab(self.plot_widget, "Results Graph") tabs.addTab(self.log_widget, "Experiment Log") splitter = QtGui.QSplitter(QtCore.Qt.Vertical) splitter.addWidget(tabs) splitter.addWidget(self.browser_widget) self.plot_widget.setMinimumSize(100, 200) vbox = QtGui.QVBoxLayout(self.main) vbox.setSpacing(0) vbox.addWidget(splitter) self.main.setLayout(vbox) self.setCentralWidget(self.main) self.main.show() self.resize(1000, 800) def quit(self, evt=None): self.close() def browser_item_changed(self, item, column): if column == 0: state = item.checkState(0) experiment = self.manager.experiments.with_browser_item(item) if state == 0: self.plot.removeItem(experiment.curve) else: experiment.curve.x = self.plot_widget.plot_frame.x_axis experiment.curve.y = self.plot_widget.plot_frame.y_axis experiment.curve.update() self.plot.addItem(experiment.curve) def browser_item_menu(self, position): item = self.browser.itemAt(position) if item is not None: experiment = self.manager.experiments.with_browser_item(item) menu = QtGui.QMenu(self) # Open action_open = QtGui.QAction(menu) action_open.setText("Open Data Externally") action_open.triggered.connect( lambda: self.open_file_externally(experiment.results.data_filename)) menu.addAction(action_open) # Change Color action_change_color = QtGui.QAction(menu) action_change_color.setText("Change Color") action_change_color.triggered.connect( lambda: self.change_color(experiment)) menu.addAction(action_change_color) # Remove action_remove = QtGui.QAction(menu) action_remove.setText("Remove Graph") if self.manager.is_running(): if self.manager.running_experiment() == experiment: # Experiment running action_remove.setEnabled(False) action_remove.triggered.connect(lambda: self.remove_experiment(experiment)) menu.addAction(action_remove) # Use parameters action_use = QtGui.QAction(menu) action_use.setText("Use These Parameters") action_use.triggered.connect( lambda: self.set_parameters(experiment.procedure.parameter_objects())) menu.addAction(action_use) menu.exec_(self.browser.viewport().mapToGlobal(position)) def remove_experiment(self, experiment): reply = QtGui.QMessageBox.question(self, 'Remove Graph', "Are you sure you want to remove the graph?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.manager.remove(experiment) def show_experiments(self): root = self.browser.invisibleRootItem() for i in range(root.childCount()): item = root.child(i) item.setCheckState(0, QtCore.Qt.Checked) def hide_experiments(self): root = self.browser.invisibleRootItem() for i in range(root.childCount()): item = root.child(i) item.setCheckState(0, QtCore.Qt.Unchecked) def clear_experiments(self): self.manager.clear() def open_experiment(self): dialog = ResultsDialog(self.procedure_class.DATA_COLUMNS, self.x_axis, self.y_axis) if dialog.exec_(): filenames = dialog.selectedFiles() for filename in map(str, filenames): if filename in self.manager.experiments: QtGui.QMessageBox.warning(self, "Load Error", "The file %s cannot be opened twice." % os.path.basename( filename)) elif filename == '': return else: results = Results.load(filename) experiment = self.new_experiment(results) experiment.curve.update() experiment.browser_item.progressbar.setValue(100.) self.manager.load(experiment) log.info('Opened data file %s' % filename) def change_color(self, experiment): color = QtGui.QColorDialog.getColor( initial=experiment.curve.opts['pen'].color(), parent=self) if color.isValid(): pixelmap = QtGui.QPixmap(24, 24) pixelmap.fill(color) experiment.browser_item.setIcon(0, QtGui.QIcon(pixelmap)) experiment.curve.setPen(pg.mkPen(color=color, width=2)) def open_file_externally(self, filename): # TODO: Make this function OS-agnostic import subprocess proc = subprocess.Popen([self.EDITOR, filename]) def make_procedure(self): if not isinstance(self.inputs, InputsWidget): raise Exception("ManagedWindow can not make a Procedure" " without a InputsWidget type") return self.inputs.get_procedure() def new_curve(self, results, color=None, **kwargs): if color is None: color = pg.intColor(self.browser.topLevelItemCount() % 8) return self.plot_widget.new_curve(results, color=color, **kwargs) def new_experiment(self, results, curve=None): if curve is None: curve = self.new_curve(results) browser_item = BrowserItem(results, curve) return Experiment(results, curve, browser_item) def set_parameters(self, parameters): """ This method should be overwritten by the child class. The parameters argument is a dictionary of Parameter objects. The Parameters should overwrite the GUI values so that a user can click "Queue" to capture the same parameters. """ if not isinstance(self.inputs, InputsWidget): raise Exception("ManagedWindow can not set parameters" " without a InputsWidget") self.inputs.set_parameters(parameters) def queue(self): """ This method should be overwritten by the child class. The self.manager.queue method should be passed an Experiment object which contains the Results and Procedure to be run. """ raise Exception("ManagedWindow child class does not implement queue method") def abort(self): self.abort_button.setEnabled(False) self.abort_button.setText("Resume") self.abort_button.clicked.disconnect() self.abort_button.clicked.connect(self.resume) try: self.manager.abort() except: log.error('Failed to abort experiment', exc_info=True) self.abort_button.setText("Abort") self.abort_button.clicked.disconnect() self.abort_button.clicked.connect(self.abort) def resume(self): self.abort_button.setText("Abort") self.abort_button.clicked.disconnect() self.abort_button.clicked.connect(self.abort) if self.manager.experiments.has_next(): self.manager.resume() else: self.abort_button.setEnabled(False) def queued(self, experiment): self.abort_button.setEnabled(True) self.browser_widget.show_button.setEnabled(True) self.browser_widget.hide_button.setEnabled(True) self.browser_widget.clear_button.setEnabled(True) def running(self, experiment): self.browser_widget.clear_button.setEnabled(False) def abort_returned(self, experiment): if self.manager.experiments.has_next(): self.abort_button.setText("Resume") self.abort_button.setEnabled(True) else: self.browser_widget.clear_button.setEnabled(True) def finished(self, experiment): if not self.manager.experiments.has_next(): self.abort_button.setEnabled(False) self.browser_widget.clear_button.setEnabled(True) PyMeasure-0.5/pymeasure/display/plotter.py0000644000175000017500000000374313137471263021262 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import sys import time from .Qt import QtGui from .windows import PlotterWindow from ..process import StoppableProcess log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class Plotter(StoppableProcess): """ Plotter dynamically plots data from a file through the Results object and supports error bars. """ def __init__(self, results, refresh_time=0.1): super(Plotter, self).__init__() self.results = results self.refresh_time = refresh_time def run(self): app = QtGui.QApplication(sys.argv) window = PlotterWindow(self, refresh_time=self.refresh_time) app.aboutToQuit.connect(window.quit) window.show() app.exec_() def wait_for_close(self, check_time=0.1): while not self.should_stop(): time.sleep(check_time) PyMeasure-0.5/pymeasure/display/curves.py0000644000175000017500000001441713137471263021100 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import pyqtgraph as pg import numpy as np from .Qt import QtCore log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class ResultsCurve(pg.PlotDataItem): """ Creates a curve loaded dynamically from a file through the Results object and supports error bars. The data can be forced to fully reload on each update, useful for cases when the data is changing across the full file instead of just appending. """ def __init__(self, results, x, y, xerr=None, yerr=None, force_reload=False, **kwargs): super().__init__(**kwargs) self.results = results self.pen = kwargs.get('pen', None) self.x, self.y = x, y self.force_reload = force_reload if xerr or yerr: self._errorBars = pg.ErrorBarItem(pen=kwargs.get('pen', None)) self.xerr, self.yerr = xerr, yerr def update(self): """Updates the data by polling the results""" if self.force_reload: self.results.reload() data = self.results.data # get the current snapshot # Set x-y data self.setData(data[self.x], data[self.y]) # Set error bars if enabled at construction if hasattr(self, '_errorBars'): self._errorBars.setOpts( x=data[self.x], y=data[self.y], top=data[self.yerr], bottom=data[self.yerr], left=data[self.xerr], right=data[self.yerr], beam=max(data[self.xerr], data[self.yerr]) ) # TODO: Add method for changing x and y class BufferCurve(pg.PlotDataItem): """ Creates a curve based on a predefined buffer size and allows data to be added dynamically, in additon to supporting error bars """ data_updated = QtCore.QSignal() def __init__(self, errors=False, **kwargs): super().__init__(**kwargs) if errors: self._errorBars = pg.ErrorBarItem(pen=kwargs.get('pen', None)) self._buffer = None def prepare(self, size, dtype=np.float32): """ Prepares the buffer based on its size, data type """ if hasattr(self, '_errorBars'): self._buffer = np.empty((size, 4), dtype=dtype) else: self._buffer = np.empty((size, 2), dtype=dtype) self._ptr = 0 def append(self, x, y, xError=None, yError=None): """ Appends data to the curve with optional errors """ if self._buffer is None: raise Exception("BufferCurve buffer must be prepared") if len(self._buffer) <= self._ptr: raise Exception("BufferCurve overflow") # Set x-y data self._buffer[self._ptr, :2] = [x, y] self.setData(self._buffer[:self._ptr, :2]) # Set error bars if enabled at construction if hasattr(self, '_errorBars'): self._buffer[self._ptr, 2:] = [xError, yError] self._errorBars.setOpts( x=self._buffer[:self._ptr, 0], y=self._buffer[:self._ptr, 1], top=self._buffer[:self._ptr, 3], bottom=self._buffer[:self._ptr, 3], left=self._buffer[:self._ptr, 2], right=self._buffer[:self._ptr, 2], beam=np.max(self._buffer[:self._ptr, 2:]) ) self._ptr += 1 self.data_updated.emit() class Crosshairs(QtCore.QObject): """ Attaches crosshairs to the a plot and provides a signal with the x and y graph coordinates """ coordinates = QtCore.QSignal(float, float) def __init__(self, plot, pen=None): """ Initiates the crosshars onto a plot given the pen style. Example pen: pen=pg.mkPen(color='#AAAAAA', style=QtCore.Qt.DashLine) """ super().__init__() self.vertical = pg.InfiniteLine(angle=90, movable=False, pen=pen) self.horizontal = pg.InfiniteLine(angle=0, movable=False, pen=pen) plot.addItem(self.vertical, ignoreBounds=True) plot.addItem(self.horizontal, ignoreBounds=True) self.position = None self.proxy = pg.SignalProxy(plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) self.plot = plot def hide(self): self.vertical.hide() self.horizontal.hide() def show(self): self.vertical.show() self.horizontal.show() def update(self): """ Updates the mouse position based on the data in the plot. For dynamic plots, this is called each time the data changes to ensure the x and y values correspond to those on the display. """ if self.position is not None: mouse_point = self.plot.vb.mapSceneToView(self.position) self.coordinates.emit(mouse_point.x(), mouse_point.y()) self.vertical.setPos(mouse_point.x()) self.horizontal.setPos(mouse_point.y()) def mouseMoved(self, event=None): """ Updates the mouse position upon mouse movement """ if event is not None: self.position = event[0] self.update() else: raise Exception("Mouse location not known") PyMeasure-0.5/pymeasure/display/thread.py0000644000175000017500000000420413137471263021031 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from threading import Event from .Qt import QtCore log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class StoppableQThread(QtCore.QThread): """ Base class for QThreads which require the ability to be stopped by a thread-safe method call """ def __init__(self, parent=None): super().__init__(parent) self._should_stop = Event() self._should_stop.clear() def join(self, timeout=0): """ Joins the current thread and forces it to stop after the timeout if necessary :param timeout: Timeout duration in seconds """ self._should_stop.wait(timeout) if not self.should_stop(): self.stop() super(StoppableQThread, self).wait() def stop(self): self._should_stop.set() def should_stop(self): return self._should_stop.is_set() def __repr__(self): return "<%s(should_stop=%s)>" % ( self.__class__.__name__, self.should_stop()) PyMeasure-0.5/pymeasure/display/__init__.py0000644000175000017500000000342513137471263021325 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) try: from .manager import Manager from .plotter import Plotter except ImportError: log.warning("Python bindings for Qt (PySide, PyQt) can not be imported") def run_in_ipython(app): """ Attempts to run the QApplication in the IPython main loop, which requires the command "%gui qt" to be run prior to the script execution. On failure the Qt main loop is initialized instead """ try: from IPython.lib.guisupport import start_event_loop_qt4 except ImportError: app.exec_() else: start_event_loop_qt4(app) PyMeasure-0.5/pymeasure/display/browser.py0000644000175000017500000001231113137471263021243 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from os.path import basename from .Qt import QtCore, QtGui from ..experiment import Procedure log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class BrowserItem(QtGui.QTreeWidgetItem): def __init__(self, results, curve, parent=None): super().__init__(parent) pixelmap = QtGui.QPixmap(24, 24) pixelmap.fill(curve.opts['pen'].color()) self.setIcon(0, QtGui.QIcon(pixelmap)) self.setFlags(self.flags() | QtCore.Qt.ItemIsUserCheckable) self.setCheckState(0, QtCore.Qt.Checked) self.setText(1, basename(results.data_filename)) self.setStatus(results.procedure.status) self.progressbar = QtGui.QProgressBar() self.progressbar.setRange(0, 100) self.progressbar.setValue(0) def setStatus(self, status): status_label = { Procedure.QUEUED: 'Queued', Procedure.RUNNING: 'Running', Procedure.FAILED: 'Failed', Procedure.ABORTED: 'Aborted', Procedure.FINISHED: 'Finished'} self.setText(3, status_label[status]) if status == Procedure.FAILED or status == Procedure.ABORTED: # Set progress bar color to red return # Commented this out self.progressbar.setStyleSheet(""" QProgressBar { border: 1px solid #AAAAAA; border-radius: 5px; text-align: center; } QProgressBar::chunk { background-color: red; } """) def setProgress(self, progress): self.progressbar.setValue(progress) class Browser(QtGui.QTreeWidget): """Graphical list view of :class:`Experiment` objects allowing the user to view the status of queued Experiments as well as loading and displaying data from previous runs. In order that different Experiments be displayed within the same Browser, they must have entries in `DATA_COLUMNS` corresponding to the `measured_quantities` of the Browser. """ def __init__(self, procedure_class, display_parameters, measured_quantities, sort_by_filename=False, parent=None): super().__init__(parent) self.display_parameters = display_parameters self.procedure_class = procedure_class self.measured_quantities = measured_quantities header_labels = ["Graph", "Filename", "Progress", "Status"] for parameter in self.display_parameters: header_labels.append(getattr(self.procedure_class, parameter).name) self.setColumnCount(len(header_labels)) self.setHeaderLabels(header_labels) self.setSortingEnabled(True) if sort_by_filename: self.sortItems(1, QtCore.Qt.AscendingOrder) for i, width in enumerate([80, 140]): self.header().resizeSection(i, width) def add(self, experiment): """Add a :class:`Experiment` object to the Browser. This function checks to make sure that the Experiment measures the appropriate quantities to warrant its inclusion, and then adds a BrowserItem to the Browser, filling all relevant columns with Parameter data. """ experiment_parameters = experiment.procedure.parameter_objects() experiment_parameter_names = list(experiment_parameters.keys()) for measured_quantity in self.measured_quantities: if measured_quantity not in experiment.procedure.DATA_COLUMNS: raise Exception("Procedure does not measure the" " %s quantity." % measured_quantity) # Set the relevant fields within the BrowserItem if # that Parameter is implemented item = experiment.browser_item for i, column in enumerate(self.display_parameters): if column in experiment_parameter_names: item.setText(i + 4, str(experiment_parameters[column])) self.addTopLevelItem(item) self.setItemWidget(item, 2, item.progressbar) return item PyMeasure-0.5/pymeasure/display/manager.py0000644000175000017500000002271713137471263021205 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from os.path import basename from .Qt import QtCore from .listeners import Monitor from ..experiment import Procedure from ..experiment.workers import Worker log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class Experiment(QtCore.QObject): """ The Experiment class helps group the :class:`.Procedure`, :class:`.Results`, and their display functionality. Its function is only a convenient container. :param results: :class:`.Results` object :param curve: :class:`.ResultsCurve` object :param browser_item: :class:`.BrowserItem` object """ def __init__(self, results, curve, browser_item, parent=None): super().__init__(parent) self.results = results self.data_filename = self.results.data_filename self.procedure = self.results.procedure self.curve = curve self.browser_item = browser_item class ExperimentQueue(QtCore.QObject): """ Represents a Queue of Experiments and allows queries to be easily preformed """ def __init__(self): super().__init__() self.queue = [] def append(self, experiment): self.queue.append(experiment) def remove(self, experiment): if experiment not in self.queue: raise Exception("Attempting to remove an Experiment that is " "not in the ExperimentQueue") else: if experiment.procedure.status == Procedure.RUNNING: raise Exception("Attempting to remove a running experiment") else: self.queue.pop(self.queue.index(experiment)) def __contains__(self, value): if isinstance(value, Experiment): return value in self.queue if isinstance(value, str): for experiment in self.queue: if basename(experiment.data_filename) == basename(value): return True return False return False def __getitem__(self, key): return self.queue[key] def next(self): """ Returns the next experiment on the queue """ for experiment in self.queue: if experiment.procedure.status == Procedure.QUEUED: return experiment raise StopIteration("There are no queued experiments") def has_next(self): """ Returns True if another item is on the queue """ try: self.next() except StopIteration: return False return True def with_browser_item(self, item): for experiment in self.queue: if experiment.browser_item is item: return experiment return None class Manager(QtCore.QObject): """Controls the execution of :class:`.Experiment` classes by implementing a queue system in which Experiments are added, removed, executed, or aborted. When instantiated, the Manager is linked to a :class:`.Browser` and a PyQtGraph `PlotItem` within the user interface, which are updated in accordance with the execution status of the Experiments. """ _is_continuous = True _start_on_add = True queued = QtCore.QSignal(object) running = QtCore.QSignal(object) finished = QtCore.QSignal(object) failed = QtCore.QSignal(object) aborted = QtCore.QSignal(object) abort_returned = QtCore.QSignal(object) log = QtCore.QSignal(object) def __init__(self, plot, browser, port=5888, log_level=logging.INFO, parent=None): super().__init__(parent) self.experiments = ExperimentQueue() self._worker = None self._running_experiment = None self._monitor = None self.log_level = log_level self.plot = plot self.browser = browser self.port = port def is_running(self): """ Returns True if a procedure is currently running """ return self._running_experiment is not None def running_experiment(self): if self.is_running(): return self._running_experiment else: raise Exception("There is no Experiment running") def _update_progress(self, progress): if self.is_running(): self._running_experiment.browser_item.setProgress(progress) def _update_status(self, status): if self.is_running(): self._running_experiment.procedure.status = status self._running_experiment.browser_item.setStatus(status) def _update_log(self, record): self.log.emit(record) def load(self, experiment): """ Load a previously executed Experiment """ self.plot.addItem(experiment.curve) self.browser.add(experiment) self.experiments.append(experiment) def queue(self, experiment): """ Adds an experiment to the queue. """ self.load(experiment) self.queued.emit(experiment) if self._start_on_add and not self.is_running(): self.next() def remove(self, experiment): """ Removes an Experiment """ self.experiments.remove(experiment) self.browser.takeTopLevelItem( self.browser.indexOfTopLevelItem(experiment.browser_item)) self.plot.removeItem(experiment.curve) def clear(self): """ Remove all Experiments """ for experiment in self.experiments[:]: self.remove(experiment) def next(self): """ Initiates the start of the next experiment in the queue as long as no other experiments are currently running and there is a procedure in the queue. """ if self.is_running(): raise Exception("Another procedure is already running") else: if self.experiments.has_next(): log.debug("Manager is initiating the next experiment") experiment = self.experiments.next() self._running_experiment = experiment self._worker = Worker(experiment.results, port=self.port, log_level=self.log_level) self._monitor = Monitor(self._worker.monitor_queue) self._monitor.worker_running.connect(self._running) self._monitor.worker_failed.connect(self._failed) self._monitor.worker_abort_returned.connect(self._abort_returned) self._monitor.worker_finished.connect(self._finish) self._monitor.progress.connect(self._update_progress) self._monitor.status.connect(self._update_status) self._monitor.log.connect(self._update_log) self._monitor.start() self._worker.start() def _running(self): if self.is_running(): self.running.emit(self._running_experiment) def _clean_up(self): self._worker.join() del self._worker del self._monitor self._worker = None self._running_experiment = None log.debug("Manager has cleaned up after the Worker") def _failed(self): log.debug("Manager's running experiment has failed") experiment = self._running_experiment self._clean_up() self.failed.emit(experiment) def _abort_returned(self): log.debug("Manager's running experiment has returned after an abort") experiment = self._running_experiment self._clean_up() self.abort_returned.emit(experiment) def _finish(self): log.debug("Manager's running experiment has finished") experiment = self._running_experiment self._clean_up() experiment.browser_item.setProgress(100.) experiment.curve.update() self.finished.emit(experiment) if self._is_continuous: # Continue running procedures self.next() def resume(self): """ Resume processing of the queue. """ self._start_on_add = True self._is_continuous = True self.next() def abort(self): """ Aborts the currently running Experiment, but raises an exception if there is no running experiment """ if not self.is_running(): raise Exception("Attempting to abort when no experiment " "is running") else: self._start_on_add = False self._is_continuous = False self._worker.stop() self.aborted.emit(self._running_experiment) PyMeasure-0.5/pymeasure/display/inputs.py0000644000175000017500000001154413137471263021111 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import re from .Qt import QtCore, QtGui log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class Input(QtCore.QObject): """ Takes a Parameter object in the constructor and has a parameter method """ def __init__(self, parameter): super().__init__() self._parameter = None self.set_parameter(parameter) def set_parameter(self, parameter): """Connects a parameter to the input box, and initializes the box value. :param parameter: parameter to connect. :return: """ self._parameter = parameter if parameter.default: self.setValue(parameter.default) if hasattr(parameter, 'units') and parameter.units: self.setSuffix(" %s" % parameter.units) def update_parameter(self): """ Mutates the self._parameter variable to update its value """ self._parameter.value = self.value() @property def parameter(self): self.update_parameter() return self._parameter class StringInput(QtGui.QLineEdit, Input): def __init__(self, parameter, parent=None): QtGui.QLineEdit.__init__(self, parent) Input.__init__(self, parameter) def setValue(self, value): # QtGui.QLineEdit has a setText() method instead of setValue() return super().setText(value) def setSuffix(self, value): pass def value(self): # QtGui.QLineEdit has a text() method instead of value() return super().text() class FloatInput(QtGui.QDoubleSpinBox, Input): def __init__(self, parameter, parent=None): QtGui.QDoubleSpinBox.__init__(self, parent) Input.__init__(self, parameter) self.setMinimum(self._parameter.minimum) self.setMaximum(self._parameter.maximum) self.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons) class IntegerInput(QtGui.QSpinBox, Input): def __init__(self, parameter, parent=None): QtGui.QSpinBox.__init__(self, parent) Input.__init__(self, parameter) self.setMinimum(self._parameter.minimum) self.setMaximum(self._parameter.maximum) self.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons) class BooleanInput(object): # TODO: Implement this class pass class ListInput(object): # TODO: Implement this class pass class ScientificInput(QtGui.QDoubleSpinBox, Input): def __init__(self, parameter, parent=None): QtGui.QDoubleSpinBox.__init__(self, parent) self.setMinimum(parameter.minimum) self.setMaximum(parameter.maximum) self.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons) self.validator = QtGui.QDoubleValidator( parameter.minimum, parameter.maximum, 10, self) self.validator.setNotation(QtGui.QDoubleValidator.ScientificNotation) Input.__init__(self, parameter) def validate(self, text, pos): if self._parameter.units: text = text[:-(len(self._parameter.units) + 1)] result = self.validator.validate(text, pos) return result[0], result[1] + " %s" % self._parameter.units, result[2] else: return self.validator.validate(text, pos) def fixCase(self, text): self.lineEdit().setText(text.toLower()) def valueFromText(self, text): if self._parameter.units: return float(str(text)[:-(len(self._parameter.units) + 1)]) else: return float(str(text)) def textFromValue(self, value): string = "{:g}".format(value).replace("e+", "e") string = re.sub("e(-?)0*(\d+)", r"e\1\2", string) return string def stepEnabled(self): return QtGui.QAbstractSpinBox.StepNone PyMeasure-0.5/pymeasure/log.py0000644000175000017500000000746613171754361016714 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import logging.handlers from logging.handlers import QueueHandler from queue import Queue log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class QueueListener(logging.handlers.QueueListener): def is_alive(self): try: return self._thread.is_alive() except AttributeError: return False def console_log(logger, level=logging.INFO, queue=None): """Create a console log handler. Return a scribe thread object.""" if queue is None: queue = Queue() logger.setLevel(level) ch = logging.StreamHandler() ch.setLevel(level) formatter = logging.Formatter( fmt='%(asctime)s: %(message)s (%(name)s, %(levelname)s)', datefmt='%I:%M:%S %p') ch.setFormatter(formatter) logger.addHandler(ch) scribe = Scribe(queue) return scribe def file_log(logger, log_filename, level=logging.INFO, queue=None, **kwargs): """Create a file log handler. Return a scribe thread object.""" if queue is None: queue = Queue() logger.setLevel(level) ch = logging.FileHandler(log_filename, **kwargs) ch.setLevel(level) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) scribe = Scribe(queue) return scribe class Scribe(QueueListener): """ Scribe class which logs records as retrieved from a queue to support consistent multi-process logging. :param queue: The multiprocessing queue which the scriber will listen to. """ def __init__(self, queue): super().__init__(queue) def handle(self, record): logging.getLogger(record.name).handle(record) def setup_logging(logger=None, console=False, console_level='INFO', filename=None, file_level='DEBUG', queue=None, file_kwargs=None): """Setup logging for console and/or file logging. Returns a scribe thread object. Defaults to no logging.""" if queue is None: queue = Queue() if logger is None: logger = logging.getLogger() if file_kwargs is None: file_kwargs = {} logger.handlers = [] if console: console_log(logger, level=getattr(logging, console_level)) logger.info('Set up console logging') if filename is not filename: file_log(logger, filename, level=getattr(logging, file_level), **file_kwargs) logger.info('Set up file logging') scribe = Scribe(queue) return scribe class TopicQueueHandler(QueueHandler): def __init__(self, queue, topic='log'): super().__init__(queue) self.topic = topic def prepare(self, record): return self.topic, record PyMeasure-0.5/pymeasure/console.py0000644000175000017500000000412313137471263017557 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import numpy as np log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) log.warning('not implemented yet') class ProgressBar(object): """ ProgressBar keeps track of the progress, predicts the estimated time of arrival (ETA), and formats the bar for display in the console """ def __init__(self): self.data = np.empty() self.progress_percentage = [] self.progress_times = [] def advance(self, progress): """ Appends the progress state and the current time to the data, so that a more accurate prediction for the ETA can be made """ pass def __str__(self): """ Returns a string representation of the progress bar """ pass def display(log, port, level=logging.INFO): """ Displays the log to the console with a progress bar that always remains at the bottom of the screen and refreshes itself """ pass PyMeasure-0.5/pymeasure/thread.py0000644000175000017500000000411113143613470017354 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from threading import Thread, Event log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class StoppableThread(Thread): """ Base class for Threads which require the ability to be stopped by a thread-safe method call """ def __init__(self): super().__init__() self._should_stop = Event() self._should_stop.clear() def join(self, timeout=0): """ Joins the current thread and forces it to stop after the timeout if necessary :param timeout: Timeout duration in seconds """ self._should_stop.wait(timeout) if not self.should_stop(): self.stop() return super().join(0) def stop(self): self._should_stop.set() def should_stop(self): return self._should_stop.is_set() def __repr__(self): return "<%s(should_stop=%s)>" % ( self.__class__.__name__, self.should_stop()) PyMeasure-0.5/pymeasure/process.py0000644000175000017500000000431313160056243017565 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from multiprocessing import get_context log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) context = get_context() # Useful for multiprocessing debugging: # context.log_to_stderr(logging.DEBUG) class StoppableProcess(context.Process): """ Base class for Processes which require the ability to be stopped by a process-safe method call """ def __init__(self): super().__init__() self._should_stop = context.Event() self._should_stop.clear() def join(self, timeout=0): """ Joins the current process and forces it to stop after the timeout if necessary :param timeout: Timeout duration in seconds """ self._should_stop.wait(timeout) if not self.should_stop(): self.stop() return super().join(0) def stop(self): self._should_stop.set() def should_stop(self): return self._should_stop.is_set() def __repr__(self): return "<%s(should_stop=%s)>" % ( self.__class__.__name__, self.should_stop()) PyMeasure-0.5/pymeasure/__init__.py0000644000175000017500000000223113171764440017652 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # __version__ = '0.5' PyMeasure-0.5/pymeasure/adapters/0000755000175000017500000000000013171765525017353 5ustar colincolin00000000000000PyMeasure-0.5/pymeasure/adapters/serial.py0000644000175000017500000000576713137471263021216 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import serial import numpy as np from .adapter import Adapter log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) class SerialAdapter(Adapter): """ Adapter class for using the Python Serial package to allow serial communication to instrument :param port: Serial port :param kwargs: Any valid key-word argument for serial.Serial """ def __init__(self, port, **kwargs): if isinstance(port, serial.Serial): self.connection = port else: self.connection = serial.Serial(port, **kwargs) def __del__(self): """ Ensures the connection is closed upon deletion """ self.connection.close() def write(self, command): """ Writes a command to the instrument :param command: SCPI command string to be sent to the instrument """ self.connection.write(command.encode()) # encode added for Python 3 def read(self): """ Reads until the buffer is empty and returns the resulting ASCII respone :returns: String ASCII response of the instrument. """ return b"\n".join(self.connection.readlines()).decode() def binary_values(self, command, header_bytes=0, dtype=np.float32): """ Returns a numpy array from a query for binary data :param command: SCPI command to be sent to the instrument :param header_bytes: Integer number of bytes to ignore in header :param dtype: The NumPy data type to format the values with :returns: NumPy array of values """ self.connection.write(command.encode()) binary = self.connection.read().decode() header, data = binary[:header_bytes], binary[header_bytes:] return np.fromstring(data, dtype=dtype) def __repr__(self): return "" % self.connection.port PyMeasure-0.5/pymeasure/adapters/prologix.py0000644000175000017500000001211713137471263021565 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import time import serial from .serial import SerialAdapter class PrologixAdapter(SerialAdapter): """ Encapsulates the additional commands necessary to communicate over a Prologix GPIB-USB Adapter, using the SerialAdapter. Each PrologixAdapter is constructed based on a serial port or connection and the GPIB address to be communicated to. Serial connection sharing is achieved by using the :meth:`.gpib` method to spawn new PrologixAdapters for different GPIB addresses. :param port: The Serial port name or a serial.Serial object :param address: Integer GPIB address of the desired instrument :param rw_delay: An optional delay to set between a write and read call for slow to respond instruments. :param kwargs: Key-word arguments if constructing a new serial object :ivar address: Integer GPIB address of the desired instrument To allow user access to the Prologix adapter in Linux, create the file: :code:`/etc/udev/rules.d/51-prologix.rules`, with contents: .. code-block:: bash SUBSYSTEMS=="usb",ATTRS{idVendor}=="0403",ATTRS{idProduct}=="6001",MODE="0666" Then reload the udev rules with: .. code-block:: bash sudo udevadm control --reload-rules sudo udevadm trigger """ def __init__(self, port, address=None, rw_delay=None, **kwargs): super().__init__(port, timeout=0.5, **kwargs) self.address = address self.rw_delay = rw_delay if not isinstance(port, serial.Serial): self.set_defaults() def set_defaults(self): """ Sets up the default behavior of the Prologix-GPIB adapter """ self.write("++auto 0") # Turn off auto read-after-write self.write("++eoi 1") # Append end-of-line to commands self.write("++eos 2") # Append line-feed to commands def ask(self, command): """ Ask the Prologix controller, include a forced delay for some instruments. :param command: SCPI command string to be sent to instrument """ self.write(command) if self.rw_delay is not None: time.sleep(self.rw_delay) return self.read() def write(self, command): """ Writes the command to the GPIB address stored in the :attr:`.address` :param command: SCPI command string to be sent to the instrument """ if self.address is not None: address_command = "++addr %d\n" % self.address self.connection.write(address_command.encode()) command += "\n" self.connection.write(command.encode()) def read(self): """ Reads the response of the instrument until timeout :returns: String ASCII response of the instrument """ self.write("++read") return b"\n".join(self.connection.readlines()).decode() def gpib(self, address, rw_delay=None): """ Returns and PrologixAdapter object that references the GPIB address specified, while sharing the Serial connection with other calls of this function :param address: Integer GPIB address of the desired instrument :param rw_delay: Set a custom Read/Write delay for the instrument :returns: PrologixAdapter for specific GPIB address """ rw_delay = rw_delay or self.rw_delay return PrologixAdapter(self.connection, address, rw_delay=rw_delay) def wait_for_srq(self, timeout=25, delay=0.1): """ Blocks until a SRQ, and leaves the bit high :param timeout: Timeout duration in seconds :param delay: Time delay between checking SRQ in seconds """ while int(self.ask("++srq")) != 1: # TODO: Include timeout! time.sleep(delay) def __repr__(self): if self.address is not None: return "" % ( self.connection.port, self.address) else: return "" % self.connection.port PyMeasure-0.5/pymeasure/adapters/visa.py0000644000175000017500000001365513137471263020674 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging import copy import visa import numpy as np from .adapter import Adapter log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) # noinspection PyPep8Naming,PyUnresolvedReferences class VISAAdapter(Adapter): """ Adapter class for the VISA library using PyVISA to communicate to instruments. Inherit from either class VISAAdapter14 or VISAAdapter15. :param resource: VISA resource name that identifies the address :param kwargs: Any valid key-word arguments for constructing a PyVISA instrument """ def __init__(self, resourceName, **kwargs): # Check PyVisa version version = float(self.version) if version < 1.7: raise NotImplementedError( "PyVisa {} is no longer supported. Please upgrade to version 1.8 or later.".format( version)) if isinstance(resourceName, int): resourceName = "GPIB0::%d::INSTR" % resourceName super(VISAAdapter, self).__init__() self.resource_name = resourceName self.manager = visa.ResourceManager() safeKeywords = ['resource_name', 'timeout', 'term_chars', 'chunk_size', 'lock', 'delay', 'send_end', 'values_format', 'read_termination'] kwargsCopy = copy.deepcopy(kwargs) for key in kwargsCopy: if key not in safeKeywords: kwargs.pop(key) self.connection = self.manager.get_instrument( resourceName, **kwargs ) @property def version(self): """ The string of the PyVISA version in use """ if hasattr(visa, '__version__'): return visa.__version__ else: return '1.4' def __repr__(self): return "" % self.connection.resourceName def write(self, command): """ Writes a command to the instrument :param command: SCPI command string to be sent to the instrument """ self.connection.write(command) def read(self): """ Reads until the buffer is empty and returns the resulting ASCII respone :returns: String ASCII response of the instrument. """ return self.connection.read() def ask(self, command): """ Writes the command to the instrument and returns the resulting ASCII response :param command: SCPI command string to be sent to the instrument :returns: String ASCII response of the instrument """ return self.connection.query(command) def ask_values(self, command): """ Writes a command to the instrument and returns a list of formatted values from the result. The format of the return is configurated by self.config(). :param command: SCPI command to be sent to the instrument :returns: Formatted response of the instrument. """ return self.connection.query_values(command) def binary_values(self, command, header_bytes=0, dtype=np.float32): """ Returns a numpy array from a query for binary data :param command: SCPI command to be sent to the instrument :param header_bytes: Integer number of bytes to ignore in header :param dtype: The NumPy data type to format the values with :returns: NumPy array of values """ self.connection.write(command) binary = self.connection.read_raw() header, data = binary[:header_bytes], binary[header_bytes:] return np.fromstring(data, dtype=dtype) def config(self, is_binary=False, datatype='str', container=np.array, converter='s', separator=',', is_big_endian=False): """ Configurate the format of data transfer to and from the instrument. :param is_binary: If True, data is in binary format, otherwise ASCII. :param datatype: Data type. :param container: Return format. Any callable/type that takes an iterable. :param converter: String converter, used in dealing with ASCII data. :param separator: Delimiter of a series of data in ASCII. :param is_big_endian: Endianness. """ self.connection.values_format.is_binary = is_binary self.connection.values_format.datatype = datatype self.connection.values_format.container = container self.connection.values_format.converter = converter self.connection.values_format.separator = separator self.connection.values_format.is_big_endian = is_big_endian def wait_for_srq(self, timeout=25, delay=0.1): """ Blocks until a SRQ, and leaves the bit high :param timeout: Timeout duration in seconds :param delay: Time delay between checking SRQ in seconds """ self.connection.wait_for_srq(timeout * 1000) PyMeasure-0.5/pymeasure/adapters/__init__.py0000644000175000017500000000312113137471263021454 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import logging from .adapter import Adapter, FakeAdapter log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) try: from pymeasure.adapters.visa import VISAAdapter except ImportError: log.warning("PyVISA library could not be loaded") try: from pymeasure.adapters.serial import SerialAdapter from pymeasure.adapters.prologix import PrologixAdapter except ImportError: log.warning("PySerial library could not be loaded") PyMeasure-0.5/pymeasure/adapters/adapter.py0000644000175000017500000001047213137471263021344 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # import numpy as np from copy import copy class Adapter(object): """ Base class for Adapter child classes, which adapt between the Instrument object and the connection, to allow flexible use of different connection techniques. This class should only be inhereted from. """ def write(self, command): """ Writes a command to the instrument :param command: SCPI command string to be sent to the instrument """ raise NameError("Adapter (sub)class has not implemented writing") def ask(self, command): """ Writes the command to the instrument and returns the resulting ASCII response :param command: SCPI command string to be sent to the instrument :returns: String ASCII response of the instrument """ self.write(command) return self.read() def read(self): """ Reads until the buffer is empty and returns the resulting ASCII respone :returns: String ASCII response of the instrument. """ raise NameError("Adapter (sub)class has not implemented reading") def values(self, command, separator=',', cast=float): """ Writes a command to the instrument and returns a list of formatted values from the result :param command: SCPI command to be sent to the instrument :param separator: A separator character to split the string into a list :param cast: A type to cast the result :returns: A list of the desired type, or strings where the casting fails """ results = str(self.ask(command)).strip() results = results.split(separator) for i, result in enumerate(results): try: results[i] = cast(result) except Exception: pass # Keep as string return results def binary_values(self, command, header_bytes=0, dtype=np.float32): """ Returns a numpy array from a query for binary data :param command: SCPI command to be sent to the instrument :param header_bytes: Integer number of bytes to ignore in header :param dtype: The NumPy data type to format the values with :returns: NumPy array of values """ raise NameError("Adapter (sub)class has not implemented the " "binary_values method") class FakeAdapter(Adapter): """Provides a fake adapter for debugging purposes, which bounces back the command so that arbitrary values testing is possible. .. code-block:: python a = FakeAdapter() assert a.read() == "" a.write("5") assert a.read() == "5" assert a.read() == "" assert a.ask("10") == "10" assert a.values("10") == [10] """ _buffer = "" def read(self): """ Returns the last commands given after the last read call. """ result = copy(self._buffer) # Reset the buffer self._buffer = "" return result def write(self, command): """ Writes the command to a buffer, so that it can be read back. """ self._buffer += command def __repr__(self): return "" PyMeasure-0.5/pymeasure/errors.py0000644000175000017500000000241513137471263017433 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # class Error(Exception): pass class RangeError(Error): pass # TODO should be deprecated someday RangeException = RangeError PyMeasure-0.5/setup.cfg0000644000175000017500000000007713171765525015363 0ustar colincolin00000000000000[aliases] test = pytest [egg_info] tag_build = tag_date = 0 PyMeasure-0.5/MANIFEST.in0000644000175000017500000000037413143613470015266 0ustar colincolin00000000000000include setup.py include README.rst include LICENSE.txt include CHANGES.txt include AUTHORS.txt recursive-include pymeasure * recursive-include tests * recursive-include docs * prune docs/_build recursive-exclude * __pycache__ recursive-exclude * *.pycPyMeasure-0.5/CHANGES.txt0000644000175000017500000001257613171764440015355 0ustar colincolin00000000000000Version 0.5 -- released 10/18/17 ================================ - Threads are used by default, eliminating multiprocessing issues with spawn - Enhanced unit tests for threading - Sphinx Doctests are added to the documentation (@bilderbuchi) - Improvements to documentation (@JuMaD) Version 0.4.6 -- released 8/12/17 ================================= - Reverted multiprocessing start method keyword arguments to fix Unix spawn issues (@ndr37) - Fixes to regressions in Results writing (@feinsteinben) - Fixes to TCP support using cloudpickle (@feinsteinben) - Restructing of unit test framework Version 0.4.5 -- released 7/4/17 ================================ - Recorder and Scribe now leverage QueueListener (@feinsteinben) - PrologixAdapter and SerialAdapter now handle Serial objects as adapters (@feinsteinben) - Optional TCP support now uses cloudpickle for serialization (@feinsteinben) - Significant PEP8 review and bug fixes (@feinsteinben) - Includes docs in the code distribution (@ghisvail) - Continuous integration support for Python 3.6 (@feinsteinben) Version 0.4.4 -- released 6/4/17 ================================ - Fix pip install for non-wheel builds - Update to Agilent E4980 (@dvspirito) - Minor fixes for docs, tests, and formatting (@ghisvail, @feinsteinben) Version 0.4.3 -- released 3/30/17 ================================= - Added Agilent E4980, AMI 430, Agilent 34410A, Thorlabs PM100, and Anritsu MS9710C instruments (@TvBMcMaster, @dvspirito, and @mhdg) - Updates to PyVISA support (@minhhaiphys) - Initial work on resource manager (@dvspirito) - Fixes for Prologix adapter that allow read-write delays (@TvBMcMaster) - Fixes for conda environment on continuous integration Version 0.4.2 -- released 8/23/16 ================================= - New instructions for installing with Anaconda and conda-forge package (thanks @melund!) - Bug-fixes to the Keithley 2000, SR830, and Agilent E4408B - Re-introduced the Newport ESP300 motion controller - Major update to the Keithely 2400, 2000 and Yokogawa 7651 to achieve a common interface - New command-string processing hooks for Instrument property functions - Updated LakeShore 331 temperature controller with new features - Updates to the Agilent 8257D signal generator for better feature exposure Version 0.4.1 -- released 7/31/16 ================================= - Critical fix in setup.py for importing instruments (also added to documentation) Version 0.4 -- released 7/29/16 =============================== - Replaced Instrument add_measurement and add_control with measurement and control functions - Added validators to allow Instrument.control to match restricted ranges - Added mapping to Instrument.control to allow more flexible inputs - Conda is now used to set up the Python environment - macOS testing in continuous integration - Major updates to the documentation Version 0.3 -- released 4/8/16 ============================== - Added IPython (Jupyter) notebook support with significant features - Updated set of example scripts and notebooks - New PyMeasure logo released - Removed support for Python <3.4 - Changed multiprocessing to use spawn for compatibility - Significant work on the documentation - Added initial tests for non-instrument code - Continuous integration setup for Linux and Windows Version 0.2 -- released 12/16/15 ================================ - Python 3 compatibility, removed support for Python 2 - Considerable renaming for better PEP8 compliance - Added MIT License - Major restructuring of the package to break it into smaller modules - Major rewrite of display functionality, introducing new Qt objects for easy extensions - Major rewrite of procedure execution, now using a Worker process which takes advantage of multi-core CPUs - Addition of a number of examples - New methods for listening to Procedures, introducing ZMQ for TCP connectivity - Updates to Keithley2400 and VISAAdapter Version 0.1.6 -- released 4/19/15 ================================= - Renamed the package to PyMeasure from Automate to be more descriptive about its purpose - Addition of VectorParameter to allow vectors to be input for Procedures - Minor fixes for the Results and Danfysik8500 Version 0.1.5 -- release 10/22/14 ================================= - New Manager class for handling Procedures in a queue fashion - New Browser that works in tandem with the Manager to display the queue - Bug fixes for Results loading Version 0.1.4 -- released 8/2/14 ================================ - Integrated Results class into display and file writing - Bug fixes for Listener classes - Bug fixes for SR830 Version 0.1.3 -- released 7/20/14 ================================= - Replaced logging system with Python logging package - Added data management (Results) and bug fixes for Procedures and Parameters - Added pandas v0.14 to requirements for data management - Added data listeners, Qt4 and PyQtGraph helpers Version 0.1.2 -- released 7/18/14 ================================= - Bug fixes to LakeShore 425 - Added new Procedure and Parameter classes for generic experiments - Added version number in package Version 0.1.1 -- released 7/16/14 ================================= - Bug fixes to PrologixAdapter, VISAAdapter, Agilent 8722ES, Agilent 8257D, Stanford SR830, Danfysik8500 - Added Tektronix TDS 2000 with basic functionality - Fixed Danfysik communication to handle errors properly Version 0.1.0 -- released 7/15/14 ================================= - Initial releasePyMeasure-0.5/AUTHORS.txt0000644000175000017500000000031013171764440015411 0ustar colincolin00000000000000Colin Jermain Graham Rowlands Minh-Hai Nguyen Guen Prawiro-Atmodjo Tim van Boxtel Davide Spirito Marcos Guimaraes Ghislain Antony Vaillant Ben Feinstein Neal Reynolds Christoph Buchner Julian DlugoschPyMeasure-0.5/docs/0000755000175000017500000000000013171765525014466 5ustar colincolin00000000000000PyMeasure-0.5/docs/introduction.rst0000644000175000017500000000416713137471263017744 0ustar colincolin00000000000000############ Introduction ############ PyMeasure uses an object-oriented approach for communicating with scientific instruments, which provides an intuitive interface where the low-level SCPI and GPIB commands are hidden from normal use. Users can focus on solving the measurement problems at hand, instead of re-inventing how to communicate with instruments. Instruments with VISA (GPIB, Serial, etc) are supported through the `PyVISA package`_ under the hood. `Prologix GPIB`_ adapters are also supported. Communication protocols can be swapped, so that instrument classes can be used with all supported protocols interchangeably. .. _PyVISA package: http://pyvisa.readthedocs.org/en/master/ .. _Prologix GPIB: http://prologix.biz/ Before using PyMeasure, you may find it helpful to be acquainted with `basic Python programming for the sciences`_ and understand the concept of objects. .. _basic Python programming for the sciences: https://scipy-lectures.github.io/ Instrument ready ================ The package includes a number of :doc:`instruments already defined`. Their definitions are organized based on the manufacturer name of the instrument. For example the class that defines the :doc:`Keithley 2400 SourceMeter` can be imported by calling: .. code-block:: python from pymeasure.instruments.keithley import Keithley2400 The :doc:`Tutorials ` section will go into more detail on :doc:`connecting to an instrument `. If you don't find the instrument you are looking for, but are interested in contributing, see the documentation on :doc:`adding an instrument `. Graphical displays ================== Graphical user interfaces (GUIs) can be easily generated to manage execution of measurement procedures with PyMeasure. This includes live plotting for data, and a queue system for managing large numbers of experiments. These features are explored in the :doc:`Using a graphical interface ` tutorial. .. image:: tutorial/pymeasure-managedwindow-running.png :alt: ManagedWindow Running ExamplePyMeasure-0.5/docs/conf.py0000644000175000017500000002071313171764440015763 0ustar colincolin00000000000000# -*- coding: utf-8 -*- # # PyMeasure documentation build configuration file, created by # sphinx-quickstart on Mon Apr 6 13:06:00 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os sys.path.insert(0, os.path.abspath('..')) # Allow modules to be found # Include Read the Docs formatting on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.doctest' ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'PyMeasure' copyright = u'2013-2017, PyMeasure Developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'PyMeasuredoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'PyMeasure.tex', u'PyMeasure Documentation', u'PyMeasure Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pymeasure', u'PyMeasure Documentation', [u'PyMeasure Developers'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'PyMeasure', u'PyMeasure Documentation', u'PyMeasure Developers', 'PyMeasure', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False PyMeasure-0.5/docs/about/0000755000175000017500000000000013171765525015600 5ustar colincolin00000000000000PyMeasure-0.5/docs/about/authors.rst0000644000175000017500000000122213171764440020007 0ustar colincolin00000000000000Authors ======= PyMeasure was started in 2013 by Colin Jermain and Graham Rowlands at Cornell University, when it became apparent that both were working on similar Python packages for scientific measurements. PyMeasure combined these efforts and continues to gain valuable contributions from other scientists who are interested in advancing measurement software. The following developers have contributed to the PyMeasure package: | Colin Jermain | Graham Rowlands | Minh-Hai Nguyen | Guen Prawiro-Atmodjo | Tim van Boxtel | Davide Spirito | Marcos Guimaraes | Ghislain Antony Vaillant | Ben Feinstein | Neal Reynolds | Christoph Buchner | Julian DlugoschPyMeasure-0.5/docs/about/license.rst0000644000175000017500000000207513137471263017753 0ustar colincolin00000000000000License ======= Copyright (c) 2013-2017 PyMeasure Developers 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.PyMeasure-0.5/docs/dev/0000755000175000017500000000000013171765525015244 5ustar colincolin00000000000000PyMeasure-0.5/docs/dev/reporting_errors.rst0000644000175000017500000000046613137471263021404 0ustar colincolin00000000000000################## Reporting an error ################## Please report all errors to the `Issues section`_ of the PyMeasure GitHub repository. Use the search function to determine if there is an existing or resolved issued before posting. .. _`Issues section`: https://github.com/ralph-group/pymeasure/issuesPyMeasure-0.5/docs/dev/contribute.rst0000644000175000017500000001166013160054362020144 0ustar colincolin00000000000000############ Contributing ############ Contributions to the instrument repository and the main code base are highly encouraged. This section outlines the basic work-flow for new contributors. Using the development version ============================= New features are added to the development version of PyMeasure, hosted on `GitHub`_. We use `Git version control`_ to track and manage changes to the source code. On Windows, we recommend using `GitHub Desktop`_. Make sure you have an appropriate version of Git (or GitHub Desktop) installed and that you have a GitHub account. .. _GitHub: https://github.com/ .. _Git version control: https://git-scm.com/ .. _GitHub Desktop: https://git-scm.com/downloads In order to add your feature, you need to first `fork`_ PyMeasure. This will create a copy of the repository under your GitHub account. .. _fork: https://help.github.com/articles/fork-a-repo/ The instructions below assume that you have set up Anaconda, as described in the :doc:`Quick Start guide <../quick_start>` and describe the terminal commands necessary. If you are using GitHub Desktop, take a look through `their documentation`_ to understand the corresponding steps. .. _their documentation: https://help.github.com/desktop/ Clone your fork of PyMeasure :code:`your-github-username/pymeasure`. In the following terminal commands replace your desired path and GitHub username. .. code-block:: bash cd /path/for/code git clone https://github.com/your-github-username/pymeasure.git If you had already installed PyMeasure using :code:`pip`, make sure to uninstall it before continuing. .. code-block:: bash pip uninstall pymeasure Install PyMeasure in the editable mode. .. code-block:: bash cd /path/for/code/pymeasure pip install -e . This will allow you to edit the files of PyMeasure and see the changes reflected. Make sure to reset your notebook kernel or Python console when doing so. Now you have your own copy of the development version of PyMeasure installed! Working on a new feature ======================== We use branches in Git to allow multiple features to be worked on simultaneously, without causing conflicts. The master branch contains the stable development version. Instead of working on the master branch, you will create your own branch off the master and merge it back into the master when you are finished. Create a new branch for your feature before editing the code. For example, if you want to add the new instrument "Extreme 5000" you will make a new branch "dev/extreme-5000". .. code-block:: bash git branch dev/extreme-5000 You can also `make a new branch`_ on GitHub. If you do so, you will have to fetch these changes before the branch will show up on your local computer. .. code-block:: bash git fetch .. _make a new branch: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/ Once you have created the branch, change your current branch to match the new one. .. code-block:: bash git checkout dev/extreme-5000 Now you are ready to write your new feature and make changes to the code. To ensure consistency, please follow the :doc:`coding standards for PyMeasure `. Use :code:`git status` to check on the files that have been changed. As you go, commit your changes and push them to your fork. .. code-block:: bash git add file-that-changed.py git commit -m "A short description about what changed" git push Making a pull-request ===================== While you are working, its helpful to start a pull-request (PR) on the :code:`master` branch of :code:`ralph-group/pymeasure`. This will allow you to discuss your feature with other contributors. We encourage you to start this pull-request after your first commit. `Start a pull-request`_ on the `PyMeasure GitHub page`_. .. _`Start a pull-request`: https://help.github.com/articles/using-pull-requests/ .. _PyMeasure GitHub page: https://github.com/ralph-group/pymeasure Your pull-request will be merged by the PyMeasure maintainers once it meets the coding standards and passes unit tests. You will notice that your pull-request is automatically checked with the unit tests. Unit testing ============ Unit tests are run each time a new commit is made to a branch. The purpose is to catch changes that break the current functionality, by testing each feature unit. PyMeasure relies on `pytest`_ to preform these tests, which are run on TravisCI and Appveyor for Linux/macOS and Windows respectively. Running the unit tests while you develop is highly encouraged. This will ensure that you have a working contribution when you create a pull request. .. code-block:: bash python setup.py test If your feature can be tested, unit tests are required. This will ensure that your features keep working as new features are added. .. _`pytest`: http://pytest.org/latest/ Now you are familiar with all the pieces of the PyMeasure development work-flow. We look forward to seeing your pull-request! PyMeasure-0.5/docs/dev/coding_standards.rst0000644000175000017500000000275513137471263021310 0ustar colincolin00000000000000################ Coding Standards ################ In order to maintain consistency across the different instruments in the PyMeasure repository, we enforce the following standards. Python style guides =================== Only Python 3 is used in PyMeasure. This prevents the maintaininace overhead of supporting Python 2.7, which will loose official support in the future. The `PEP8 style guide`_ and `PEP257 docstring conventions`_ should be followed. .. _PEP8 style guide: https://www.python.org/dev/peps/pep-0008/ .. _PEP257 docstring conventions: https://www.python.org/dev/peps/pep-0257/ Function and variable names should be lower case with underscores as needed to seperate words. CamelCase should only be used for class names, unless working with Qt, where its use is common. Documentation ============= PyMeasure documents code using reStructuredText and the `Sphinx documentation generator`_. All functions, classes, and methods should be documented in the code using a `docstring`_. .. _Sphinx documentation generator: http://www.sphinx-doc.org/en/stable/ .. _docstring: http://www.sphinx-doc.org/en/stable/ext/example_numpy.html?highlight=docstring Usage of getter and setter functions ==================================== Getter and setter functions are discouraged, since properties provide a more fluid experience. Given the extensive tools avalible for defining properties, detailed in the :ref:`Advanced properties ` section, these types of properties are prefered.PyMeasure-0.5/docs/dev/adding_instruments.rst0000644000175000017500000003400013160054362021660 0ustar colincolin00000000000000################## Adding instruments ################## You can make a significant contribution to PyMeasure by adding a new instrument to the :code:`pymeasure.instruments` package. Even adding an instrument with a few features can help get the ball rolling, since its likely that others are interested in the same instrument. Before getting started, become familiar with the :doc:`contributing work-flow ` for PyMeasure, which steps through the process of adding a new feature (like an instrument) to the development version of the source code. This section will describe how to lay out your instrument code. File structure ============== Your new instrument should be placed in the directory corresponding to the manufacturer of the instrument. For example, if you are going to add an "Extreme 5000" instrument you should add the following files assuming "Extreme" is the manufacturer. Use lowercase for all filenames to distinguish packages from CamelCase Python classes. .. code-block:: none pymeasure/pymeasure/instruments/extreme/ |--> __init__.py |--> extreme5000.py Updating the init file ********************** The :code:`__init__.py` file in the manufacturer directory should import all of the instruments that correspond to the manufacturer, to allow the files to be easily imported. For a new manufacturer, the manufacturer should also be added to :code:`pymeasure/pymeasure/instruments/__init__.py`. In this case, you also need to add the new package to the :code:`pymeasure/setup.py` file in the :code:`packages` argument. Adding documentation ******************** Documentation for each instrument is required, and helps others understand the features you have implemented. Add a new reStructuredText file to the documentation. .. code-block:: none pymeasure/docs/api/instruments/extreme/ |--> index.rst |--> extreme5000.rst Copy an existing instrument documentation file, which will automatically generate the documentation for the instrument. The :code:`index.rst` file should link to the :code:`extreme5000` file. For a new manufacturer, the manufacturer should be also linked in :code:`pymeasure/docs/api/instruments/index.rst`. Instrument file =============== All standard instruments should be child class of :class:`Instrument `. This provides the basic functionality for working with :class:`Adapters `, which perform the actual communication. The most basic instrument, for our "Extreme 5000" example starts like this: .. testcode:: # # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # # from pymeasure.instruments import Instrument .. testcode:: :hide: # Behind the scene, replace Instrument with FakeInstrument to enable # doctesting all this from pymeasure.instruments.instrument import FakeInstrument as Instrument This is a minimal instrument definition: .. testcode:: class Extreme5000(Instrument): """ Represents the imaginary Extreme 5000 instrument. """ def __init__(self, resourceName, **kwargs): super(Extreme5000, self).__init__( resourceName, "Extreme 5000", **kwargs ) Make sure to include the PyMeasure license to each file, and add yourself as an author to the :code:`AUTHORS.txt` file. In principle you are free to write any functions that are necessary for interacting with the instrument. When doing so, make sure to use the :code:`self.ask(command)`, :code:`self.write(command)`, and :code:`self.read()` methods to issue command instead of calling the adapter directly. In practice, we have developed a number of convenience functions for making instruments easy to write and maintain. The following sections detail these conveniences and are highly encouraged. Writing properties ================== In PyMeasure, `Python properties`_ are the preferred method for dealing with variables that are read or set. PyMeasure comes with two convenience functions for making properties for classes. The :func:`Instrument.measurement ` function returns a property that issues a GPIB/SCPI requests when the value is used. For example, if our "Extreme 5000" has the :code:`*IDN?` command we can write the following property to be added above the :code:`def __init__` line in our above example class, or added to the class after the fact as in the code here: .. _Python properties: https://docs.python.org/3/howto/descriptor.html#properties .. testcode:: Extreme5000.id = Instrument.measurement( "*IDN?", """ Reads the instrument identification """ ) .. testcode:: :hide: # We are not mocking this in FakeInstrument, let's override silently Extreme5000.id = 'Extreme 5000 identification from instrument' You will notice that a documentation string is required, and should be descriptive and specific. When we use this property we will get the identification information. .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.id # Reads "*IDN?" 'Extreme 5000 identification from instrument' The :func:`Instrument.control ` function extends this behavior by creating a property that you can read and set. For example, if our "Extreme 5000" has the :code:`:VOLT?` and :code:`:VOLT ` commands that are in Volts, we can write the following property. .. testcode:: Extreme5000.voltage = Instrument.control( ":VOLT?", ":VOLT %g", """ A floating point property that controls the voltage in Volts. This property can be set. """ ) You will notice that we use the `Python string format`_ :code:`%g` to pass through the floating point. .. _Python string format: https://docs.python.org/3/library/string.html#format-specification-mini-language We can use this property to set the voltage to 100 mV, which will execute the command and then request the current voltage. .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.voltage = 0.1 # Executes ":VOLT 0.1" >>> extreme.voltage # Reads ":VOLT?" 0.1 Using both of these functions, you can create a number of properties for basic measurements and controls. The next section details additional features of :func:`Instrument.control ` that allow you to write properties that cover specific ranges, or have to map between a real value to one used in the command. .. _advanced-properties: Advanced properties =================== Many GPIB/SCIP commands are more restrictive than our basic examples above. The :func:`Instrument.control ` function has the ability to encode these restrictions using :mod:`validators `. A validator is a function that takes a value and a set of values, and returns a valid value or raises an exception. There are a number of pre-defined validators in :mod:`pymeasure.instruments.validators` that should cover most situations. We will cover the four basic types here. In the examples below we assume you have imported the validators. .. testcode:: :hide: from pymeasure.instruments.validators import strict_discrete_set, strict_range, truncated_range, truncated_discrete_set In a restricted range ********************* If you have a property with a restricted range, you can use the :func:`strict_range ` and :func:`truncated_range ` functions. For example, if our "Extreme 5000" can only support voltages from -1 V to 1 V, we can modify our previous example to use a strict validator over this range. .. testcode:: Extreme5000.voltage = Instrument.control( ":VOLT?", ":VOLT %g", """ A floating point property that controls the voltage in Volts, from -1 to 1 V. This property can be set. """, validator=strict_range, values=[-1, 1] ) Now our voltage will raise a ValueError if the value is out of the range. .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.voltage = 100 Traceback (most recent call last): ... ValueError: Value of 100 is not in range [-1,1] This is useful if you want to alert the programmer that they are using an invalid value. However, sometimes it can be nicer to truncate the value to be within the range. .. testcode:: Extreme5000.voltage = Instrument.control( ":VOLT?", ":VOLT %g", """ A floating point property that controls the voltage in Volts, from -1 to 1 V. Invalid voltages are truncated. This property can be set. """, validator=truncated_range, values=[-1, 1] ) Now our voltage will not raise an error, and will truncate the value to the range bounds. .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.voltage = 100 # Executes ":VOLT 1" >>> extreme.voltage 1.0 In a discrete set ***************** Often a control property should only take a few discrete values. You can use the :func:`strict_discrete_set ` and :func:`truncated_discrete_set ` functions to handle these situations. The strict version raises an error if the value is not in the set, as in the range examples above. For example, if our "Extreme 5000" has a :code:`:RANG ` command that sets the voltage range that can take values of 10 mV, 100 mV, and 1 V in Volts, then we can write a control as follows. .. testcode:: Extreme5000.voltage = Instrument.control( ":RANG?", ":RANG %g", """ A floating point property that controls the voltage range in Volts. This property can be set. """, validator=truncated_discrete_set, values=[10e-3, 100e-3, 1] ) Now we can set the voltage range, which will automatically truncate to an appropriate value. .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.voltage = 0.08 >>> extreme.voltage 0.1 Using maps ********** Now that you are familiar with the validators, you can additionally use maps to satisfy instruments which require non-physical values. The :code:`map_values` argument of :func:`Instrument.control ` enables this feature. If your set of values is a list, then the command will use the index of the list. For example, if our "Extreme 5000" instead has a :code:`:RANG `, where 0, 1, and 2 correspond to 10 mV, 100 mV, and 1 V, then we can use the following control. .. testcode:: Extreme5000.voltage = Instrument.control( ":RANG?", ":RANG %d", """ A floating point property that controls the voltage range in Volts, which takes values of 10 mV, 100 mV and 1 V. This property can be set. """, validator=truncated_discrete_set, values=[10e-3, 100e-3, 1], map_values=True ) Now the actual GPIB/SCIP command is ":RANG 1" for a value of 100 mV, since the index of 100 mV in the values list is 1. .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.voltage = 100e-3 >>> extreme.read() '1' >>> extreme.voltage = 1 >>> extreme.voltage 1 Dictionaries provide a more flexible method for mapping between real-values and those required by the instrument. If instead the :code:`:RANG ` took 1, 2, and 3 to correspond to 10 mV, 100 mV, and 1 V, then we can replace our previous control with the following. .. testcode:: Extreme5000.voltage = Instrument.control( ":RANG?", ":RANG %d", """ A floating point property that controls the voltage range in Volts, which takes values of 10 mV, 100 mV and 1 V. This property can be set. """, validator=truncated_discrete_set, values={10e-3:1, 100e-3:2, 1:3}, map_values=True ) .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.voltage = 10e-3 >>> extreme.read() '1' >>> extreme.voltage = 100e-3 >>> extreme.voltage 0.1 The dictionary now maps the keys to specific values. The values and keys can be any type, so this can support properties that use strings: .. testcode:: Extreme5000.channel = Instrument.control( ":CHAN?", ":CHAN %d", """ A string property that controls the measurement channel, which can take the values X, Y, or Z. """, validator=strict_discrete_set, values={'X':1, 'Y':2, 'Z':3}, map_values=True ) .. doctest:: >>> extreme = Extreme5000("GPIB::1") >>> extreme.channel = 'X' >>> extreme.read() '1' >>> extreme.channel = 'Y' >>> extreme.channel 'Y' As you have seen, the :func:`Instrument.control ` function can be significantly extended by using validators and maps. PyMeasure-0.5/docs/tutorial/0000755000175000017500000000000013171765525016331 5ustar colincolin00000000000000PyMeasure-0.5/docs/tutorial/connecting.rst0000644000175000017500000000711213160054362021177 0ustar colincolin00000000000000########################### Connecting to an instrument ########################### .. role:: python(code) :language: python After following the :doc:`Quick Start <../quick_start>` section, you now have a working installation of PyMeasure. This section describes connecting to an instrument, using a Keithley 2400 SourceMeter as an example. To follow the tutorial, open a command prompt, IPython terminal, or Jupyter notebook. First import the instrument of interest. :: from pymeasure.instruments.keithley import Keithley2400 Then construct an object by passing the GPIB address. For this example we connect to the instrument over GPIB (using VISA) with an address of 4. See the :ref:`adapters ` section below for more details. :: sourcemeter = Keithley2400("GPIB::4") For instruments with standard SCPI commands, an :code:`id` property will return the results of a :code:`*IDN?` SCPI command, identifying the instrument. :: sourcemeter.id This is equivalent to manually calling the SCPI command. :: sourcemeter.ask("*IDN?") Here the :code:`ask` method writes the SCPI command, reads the result, and returns that result. This is further equivalent to calling the methods below. :: sourcemeter.write("*IDN?") sourcemeter.read() This example illustrates that the top-level methods like :code:`id` are really composed of many lower-level methods. Both can be called depending on the operation that is desired. PyMeasure hides the complexity of these lower-level operations, so you can focus on the bigger picture. .. _adapters: Using adapters ============== PyMeasure supports a number of adapters, which are responsible for communicating with the underlying hardware. In the example above, we passed the string "GPIB::4" when constructing the instrument. By default this constructs a VISAAdapter class to connect to the instrument using VISA. Instead of passing a string, we could equally pass an adapter object. :: from pymeasure.adapters import VISAAdapter adapter = VISAAdapter("GPIB::4") sourcemeter = Keithely2400(adapter) To instead use a Prologix GPIB device connected on :code:`/dev/ttyUSB0` (proper permissions are needed in Linux, see :class:`PrologixAdapter `), the adapter is constructed in a similar way. Unlike the VISA adapter which is specific to each instrument, the Prologix adapter can be shared by many instruments. Therefore, they are addressed separately based on the GPIB address number when passing the adapter into the instrument construction. :: from pymeasure.adapters import PrologixAdapter adapter = PrologixAdapter('/dev/ttyUSB0') sourcemeter = Keithley2400(adapter.gpib(4)) For instruments using serial communication that have particular settings that need to be matched, a custom :class:`Adapter ` sub-class can be made. For example, the LakeShore 425 Gaussmeter connects via USB, but uses particular serial communication settings. Therefore, a :class:`LakeShoreUSBAdapter ` class enables these requirements in the background. :: from pymeasure.instruments.lakeshore import LakeShore425 gaussmeter = LakeShore425('/dev/lakeshore425') Behind the scenes the :code:`/dev/lakeshore425` port is passed to the :class:`LakeShoreUSBAdapter `. The above examples illustrate different methods for communicating with instruments, using adapters to keep instrument code independent from the communication protocols. Next we present the methods for setting up measurements. PyMeasure-0.5/docs/tutorial/graphical.rst0000644000175000017500000002077013160054362021007 0ustar colincolin00000000000000########################### Using a graphical interface ########################### In the previous tutorial we measured the IV characteristic of a sample to show how we can set up a simple experiment in PyMeasure. The real power of PyMeasure comes when we also use the graphical tools that are included to turn our simple example into a full-flegged user interface. Using the Plotter ~~~~~~~~~~~~~~~~~ While it lacks the nice features of the ManagedWindow, the Plotter object is the simplest way of getting live-plotting. The Plotter takes a Results object and plots the data at a regular interval, grabbing the latest data each time from the file. Let's extend our SimpleProcedure with a RandomProcedure, which generates random numbers during our loop. This example does not include instruments to provide a simpler example. :: import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) import random from time import sleep from pymeasure.log import console_log from pymeasure.display import Plotter from pymeasure.experiment import Procedure, Results, Worker from pymeasure.experiment import IntegerParameter, FloatParameter, Parameter class RandomProcedure(Procedure): iterations = IntegerParameter('Loop Iterations') delay = FloatParameter('Delay Time', units='s', default=0.2) seed = Parameter('Random Seed', default='12345') DATA_COLUMNS = ['Iteration', 'Random Number'] def startup(self): log.info("Setting the seed of the random number generator") random.seed(self.seed) def execute(self): log.info("Starting the loop of %d iterations" % self.iterations) for i in range(self.iterations): data = { 'Iteration': i, 'Random Number': random.random() } self.emit('results', data) log.debug("Emitting results: %s" % data) sleep(self.delay) if self.should_stop(): log.warning("Caught the stop flag in the procedure") break if __name__ == "__main__": console_log(log) log.info("Constructing a RandomProcedure") procedure = RandomProcedure() procedure.iterations = 100 data_filename = 'random.csv' log.info("Constructing the Results with a data file: %s" % data_filename) results = Results(procedure, data_filename) log.info("Constructing the Plotter") plotter = Plotter(results) plotter.start() log.info("Started the Plotter") log.info("Constructing the Worker") worker = Worker(results) worker.start() log.info("Started the Worker") log.info("Joining with the worker in at most 1 hr") worker.join(timeout=3600) # wait at most 1 hr (3600 sec) log.info("Finished the measurement") The important addition is the construction of the Plotter from the Results object. :: plotter = Plotter(results) plotter.start() Just like the Worker, the Plotter is started in a different process so that it can be run on a separate CPU for higher performance. The Plotter launches a Qt graphical interface using pyqtgraph which allows the Results data to be viewed based on the columns in the data. .. image:: pymeasure-plotter.png :alt: Results Plotter Example Using the ManagedWindow ~~~~~~~~~~~~~~~~~~~~~~~ The ManagedWindow is the most convenient tool for running measurements with your Procedure. This has the major advantage of accepting the input parameters graphically. From the parameters, a graphical form is automatically generated that allows the inputs to be typed in. With this feature, measurements can be started dynamically, instead of defined in a script. Another major feature of the ManagedWindow is its support for running measurements in a sequential queue. This allows you to set up a number of measurements with different input parameters, and watch them unfold on the live-plot. This is especially useful for long running measurements. The ManagedWindow achieves this through the Manager object, which coordinates which Procedure the Worker should run and keeps track of its status as the Worker progresses. Below we adapt our previous example to use a ManagedWindow. :: import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) import random from time import sleep from pymeasure.log import console_log from pymeasure.display.Qt import QtGui from pymeasure.display.windows import ManagedWindow from pymeasure.experiment import Procedure, Results from pymeasure.experiment import IntegerParameter, FloatParameter, Parameter class RandomProcedure(Procedure): iterations = IntegerParameter('Loop Iterations') delay = FloatParameter('Delay Time', units='s', default=0.2) seed = Parameter('Random Seed', default='12345') DATA_COLUMNS = ['Iteration', 'Random Number'] def startup(self): log.info("Setting the seed of the random number generator") random.seed(self.seed) def execute(self): log.info("Starting the loop of %d iterations" % self.iterations) for i in range(self.iterations): data = { 'Iteration': i, 'Random Number': random.random() } self.emit('results', data) log.debug("Emitting results: %s" % data) sleep(self.delay) if self.should_stop(): log.warning("Caught the stop flag in the procedure") break class MainWindow(ManagedWindow): def __init__(self): super(MainWindow, self).__init__( procedure_class=RandomProcedure, inputs=['iterations', 'delay', 'seed'], displays=['iterations', 'delay', 'seed'], x_axis='Iteration', y_axis='Random Number' ) self.setWindowTitle('GUI Example') def queue(self): filename = tempfile.mktemp() procedure = self.make_procedure() results = Results(procedure, filename) experiment = self.new_experiment(results) self.manager.queue(experiment) if __name__ == "__main__": app = QtGui.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) This results in the following graphical display. .. image:: pymeasure-managedwindow.png :alt: ManagedWindow Example In the code, the MainWindow class is a sub-class of the ManagedWindow class. We overwrite the constructor to provide information about the procedure class and its options. The :python:`inputs` are a list of Parameters class-variable names, which the display will generate graphical fields for. The :python:`displays` is a similar list, which instead defines the parameters to display in the browser window. This browser keeps track of the experiments being run in the sequential queue. The :python:`queue` method establishes how the Procedure object is constructed. We use the :python:`self.make_procedure` method to create a Procedure based on the graphical input fields. Here we are free to modify the procedure before putting it on the queue. In this context, the Manager uses an Experiment object to keep track of the Procedure, Results, and its associated graphical representations in the browser and live-graph. This is then given to the Manager to queue the experiment. .. image:: pymeasure-managedwindow-queued.png :alt: ManagedWindow Queue Example By default the Manager starts a measurement when its procedure is queued. The abort button can be pressed to stop an experiment. In the Procedure, the :python:`self.should_stop` call will catch the abort event and halt the measurement. It is important to check this value, or the Procedure will not be responsive to the abort event. .. image:: pymeasure-managedwindow-resume.png :alt: ManagedWindow Resume Example If you abort a measurement, the resume button must be pressed to continue the next measurement. This allows you to adjust anything, which is presumably why the abort was needed. .. image:: pymeasure-managedwindow-running.png :alt: ManagedWindow Running Example Now that you have learned about the ManagedWindow, you have all of the basics to get up and running quickly with a measurement and produce an easy to use graphical interface with PyMeasure. PyMeasure-0.5/docs/tutorial/pymeasure-managedwindow-resume.png0000644000175000017500000023155013137471263025172 0ustar colincolin00000000000000‰PNG  IHDR]DOÓß pHYs  šœtIMEà%"W†â IDATxÚìÝu\éðïÌöRÒ%ˆ bŸ­ØÝŠÝg×yv·gwœ}gasÖù³¤cÙšç÷Ç.©( ç!~Þ¯{y°Ì<;;;ûìó™ç™g¸¢S xì@~ä@~ä@~ää@~ä@~ä@~@~ä@~ä@~€¢Ž~}ûìæzÿD¼¡Úå8C㛥}@djïdOD¤°tp´¤=ÕÃ>ReK$Ü7>”òdŒ!?ÀeëÖ­DÔµk×/]1,,,00Ð0öÇÛÛ›1vñâÅgÏž={ö¬Aƒ¶¶¶ßS~ày‡@âîú¼OD¦Åšvjé]ÐB$$¾r#0ð¹4]SߘÒ¶À^¾C =\7eë3"‡:½WµÐkt6Ñ›ZÕÒP}3Íûk¿ÏþãQɼÚi_LüêèÊMWcI\°Ù@_Ë+{߉JÔ2âäVŠWmX¯¬ƒ\x~ÕòÓïɤXµ¢‰·o¼ˆY«ç[;ßÃÇ/=ŽdVÅúµ,o+ÑS®ì¥º{ëU¹²^£Vë_#‚F­Ñ‰>¶Ø[ÿ9ãý ?š•ë9¬yËV÷–ì| ~tôtÜôÔÕX"±Gsßò–ÂWÁ‘Ì:¿›Rñê]ä‹ën¬‡µpá ] ÷/\“HˆHˆ¸tÍ}"‰„ˆ(òÞ±£EŠt-©0.–ô¿×ì­$ñïcžœÙ}ÚmPå4¬zzdÝŽñœ•g™"Òw·ï=½´g»d@ÿÚ¸. ½Zµj)Š»wï>zôÈðHÉ’%+V¬¨Ñh¾ÍäØõèzÈQÚ¸·QDD¼SQ[^v~ÚSoµDD$um2 [™Ô*˜Œý tg÷Ó×ÑÖØzÆéE‘æ-‹>Ýý áæ®mDDâB-Zÿd"ú¢~ãË[›‹ubÌóã+7_‹O|ù"F›ßJL+öÕÚåís×\K e™£Û{Dž¿üBŒ>âMŒ¶„ÜøVµû ­ï@oŽ,X~!&þÁíÐò–)q'úîéñDöu}›”2åâm"Ö¼{ëNx5;È3vìØ‘vÚ¡mÛ¶~èܹsÖ Q«Õ*Txýúutt4YYY•/_^­V³Wù[r'Fœ±m­&Zmò_4!m HΔ!.°ô¤+Ûø[šñKL—”¨ÖëåE›·,òtÏC!=´*©Ô ŒõÛ‹·]}š˜ú¬z1cdzT)Ñ&jd6¦D $5‘é“$Ö¦D1¤×¥ÝT¦U©TbS7[îB ‹HÐ[¦„¥ÈçBO­]r*e3UÑ*|»@.±}ûö>Þ©S§/¨Ù3©Ò¾¨ªáüùó†ð@D‘‘‘gΜ©R¥Ê7 „ü;ñf–DqÄÞ= I*ä^{äܺ§;f¬½¥&Žˆ1^Ìé5Z=cŒHЪuDD¼˜géŇ}Ä)¿èTññ’´_8L›¤M>5¦OŒMP r±>òŸ]ÛßY¯Y¥ˆÅ»Óþ×¢ÓgÆ–rý6 #J¹B;Ã…LÐkôDD|êŒRÓŽMÕ6õRæ€âÄö<¾] wû¢ZªOŸ>†Ö®]KD}ûö5üŸõV÷åË—_¾|IDE‹ÕëõAAA/_¾ä8®råÊß&Bàþ¹4?(=Ê»‹‚Ÿéÿ÷ÇëÖ +ÌÇ©S¿¥D&vV Ó²WW•³ÑÿïN$‘…“¹èë«ó˜;GŽGвS¯êæmõ"”ˆ8Æ­| ‹Þ_ºÙ—h–ž!þÙ­7Œˆ¬ X¥~‰ò¹YÓƒwªµ­SÊZL$$¾¾u+Vëj+Ç¡¹AJ[?ƒ¬7ý?\ø‹Ö5ˆˆˆxõêyzzVªT‰ã8½^ÿôéÓàà`OOÏïiþ%\ÿó”Å[µx°jÿƒDÕãÓÛŸ&^Ä ú”jWâZ³²õÃóÚçÇ×Î=ž¼ŽÄ³V+^Hxô玓OßÑ»ÀmëŸUömSÞJ”±¥ÿöèÒ™G?›—ëÞ¿jÄ‘cO4DVÕû´Xsî}Èÿ«ž 9šÓ“höt߆MŽìݳÐ4U?}äçt_ ,µ£"òüÖU·)6*A »×ø)w;y΢Tý2m½™ðÄÙìsÖVMdDœÎªf¿Òrƒ¸¸¸/ó+ÚÏööö>>>/^¼¨R¥Šáš‡5jˆD"777;;;½^ÿÝäŒ_ÈyŒ)ê8Âîú©3—ï> × z‘©mþ‚…K”õ2%Æxûºýúšœ¸|÷E¤šˆ7±+T¦Vã:E•z^Ÿñ.*ÁXŽ*:,•JUµjU­V«Óé¾ÍÆsE§d³ˆI…#jÕª¥T*q(ä|5-–ÊeR‰XÄ1A¯Ói5jµN0üQ&—K%"ž#"&è4ê$µFψx©©™"Í0&A—”zVŠ“˜˜+3œ@bš„$^©sÄ4 q*‰¦&RžHŸ— “(•r1OL¯QëÅr)o(‘ÉÌÌäÆŸõ¼ÌÔL.JþŰL§ÒD^Ù¸êT8YÕ6¼“”#A—¤JTëŸvu"^,“Ë¥âäW£×i’Tjâ@N:zôèô ëì”3ýÿ¦Ó¨t™ÝìéÔªxõGþ(hâc>18Ó&ÄÄ|ì±iVÒ©âbRŠV'Ä¥N ˜úŒê¸ušŒ’æ—m€#N›jÒ¬N$èÔ‰ñj¼å¹[Ž]ÿ€] ™|G¤~Yàûùù>…ÏW¾×¸òø¾@~@~ø±ðØ€üß7+++ìä€,‰ŒŒ4D ä€,Eˆ”ù Sèv@~È*Œ_@~ø²A¿€üðYèvÈÛùEœîÓ¸©á¿Ÿc³K9snXóh>ü“êÖÜæÉÏÕÐoð¯Û¯½×}ë}÷‰ÍƒìÃø%€\HœsEqVufïÍ¿}ðè|ÍïàaÆýë_°÷ì¡e¤‰¡÷O¬Û0e¼håÊ®¼¥y+BPÖÆ/]¥YúWÜ•IØiTt“÷ñ6­î.î€Ü–ˆËM•Ržãå&& 1ÅŽi¿Ö¡Kýá]/46»NüÅ×S©}¼²×¨›u:yÝ:#/PgÐÄÁ ó‹ÞùÿÜÝß{õªŽn"ÕYݦ&Ž˜ì´bÁ=ÝÚz‘[Ÿu«[9¦Û\^éäîéeF^E½,ƒo?ûϻƱ‡§.:ñ8,NG2Û’ÍŒìUÙ†Bö÷ï¢òкoöì¼lÒoà¨esüï„ÄjHléU·ï¨~õœ%1cÛ¯µëY?îÀÞëáæ%ºLYõå–ÙϽä 4Áÿêkìøò‰$C¯°”±—Zã5Õoì÷;u஼Žúªñ½i훑i±ã§´qkC7/]ã;L/RšKõ:qRS¹ñõQ7~_ºfÏ•7jNn®LÒ‰ò!^"戈x™‰ŒW‹y""Nn*ãô½úÝý7ªçë{µÙb¸¨C§Ñ¥ôi·%öEPDLÐäÎ Ïôj­ÝûÁ•8‘ÂTÂÉÝjV¶˜º¸oÿ³ÞÊ—¯V¿FQ+1ŽÀLe}ü’™D‹Ýõ™]$Æ.€ï"?¤â¸]O͘žqb©¡}Í„/,Ó½ï¼-–övRŽHÿîðÜYGx¿)Ú—µ—GŸÚ}çÏsié¬ßßúLZ³¨ª‹‰úú´öó?½¥ÆŸ™žÄŇ­_Ö$å/3ábš’õ‚Ȧմ¹~)—ps"…¹äAÚ‚Í+Œ^½´êé¿.߸¾kÁ¡Ýÿü²i\å|Â3Œ_Jù÷KZJ1Ög`Àw—>Š%¾¼"rno+“ÈÅ,)NeHL—Üo âHÐë3Y›W8ps3Kù]ûöîk*òs›rö "&ìÃdµïï?׸·iUÕÅDdX&KÛ)±ó²n<ŒRø¸$_ƒÊFñ©›Ç›(¨Œ~ÂY”¶%g#Æ¥Ÿˆ ¼¹GõVÕ[u ÙqÃ¥`uå|r„ŸŠ”…ñK–4Ž?—dšè˜è[wn©5jì/%“ÊJ—*man]Ãùé’â5’T:³ÌÔ½ºpò¢Uq‹„{û–Ÿ—Ôœ^Öœ—º·Š:¹í`‰fNQWöoû+Q¨LÄɬ]L¢®ž»ú€Ë§3q/á¬øäÉz±§ðûÁ½Ê2²Wg·oz¨³*—1Xz8Šö;pÒ¹Šåû+»×_Kâ³r‚È®ZÛ*[çL›c5¤m%'.âÑ?§.KÚýÚ.íæ¹´iê8`õ´ß¨G/³¤W·NŸxViì“4¥$Þ\2ù¬k³FÜMãoÝ 9յÿ™Ëúø%K)ÚÄŸÛEõó—Ï«W©.“a"×/¦V«?{¬Õim¬l°7r0?°È3ãÛ/yLDÛ‡wº5vû”ÌÞ_^ý˦wZ“‚µÎé_ÚŒ#ro7¦Ý“9ÛçN’¹ÕmÛ¡Ò«DD²ÂúÔ¾»löCfå‡ÿ6ësùÁ©ñÏ}ïÍÝ<÷—VÅšµjQðåùŒ‹pV5† ¼>cý‚I‡Í ÕmߺأCYzm¼eÕ±ó‡­]µkÞ˜j’Ú©Ò¢›Dfšnó:Μ-^½vëÔñ™¹”¯íë¦àÂÓ"u*[ bÇ‚Q›IdU´á˜±õD83…ñK9™¤š’Å*h4ûêkx¸yœ¿t¾vÚØ\Ñ)Ù,bRሠduiÍ㕽FõÞº¸¦9FþCŽØq,pp§VßìébÏô™ëºîฟþÛ±gê‡ |ûÝzhC½Ï_D³bÇ–5+þ‡õLÚ ÿJÁì_ß–ó—ÏׯS7øÞL²ÎN ˜r©¬_²‘©3ké÷ª?ñ¦±ÕhZ r›ã{U·Ë±c>áÊø†D ýgTTfeq!áÉÉu«·ùçI„–¤6…*ÖnÖ¹sËŠößb›¥DÍ8Ž}²9Eì‹×ÊÌðñ'eÙØ.«ë0âp²ùr»¬_ú^ZiÔÒ¡EÅñ!Ww-^;|œåµí\þƒ£ž%ÜYÛ³ßÖ÷å: š=¢™æíƒËGö®žgYagO·o xŽ1–¾͈#ÿÏø‹ï§[¼Ð°uÓë¤öj|䌽ðÁŸ¸4ó0¢tMnÕýEý§>i¶hYgqÚÙÍ2Nqú7–.épiŸ;µË!ö¸~; Ï\’|k—Ú§[‡¾ÕdÕ¼ÆÉC?:û1ÊlØ¿Öc€ü𥤞·Ä~‡,FÊÚýã>q„[¸.ZTAE‹Òÿsnò¹‡ í\,Hˆ{°{ÁÜu'‚¢™yá†}§iUX¡~~dÉ䕇ïF ›BU:M™Ù2zjóÑS¯¨jJ¤ÙÛ£ÕuvÿÞÝp7lý›݇&õ¯S…ȮӦ­­Ÿ®L·º_šV­öùï3¶—÷ÇâæÎb"¢R?UlЦó“·2‘úáß~·;ޝôÏšÍÿs_´·Ë• Óþ|ø.VK2‡²íFü:¤†½Þá×aÿO½*ð¿Æ9×è?wr‡bb"bšw– Z·íz¸¢pË_ç¨gŸé'Z„$F$0â$%ºŽî䑼½¼‰“ ãRâÒµ£¹të§Ç¥û¥.(vlÔwPl~ ž¥›}Ø¢ÿxqBñ/L`DŒ ¶L0®!0Æ¥ ·ÿcÀ>ü+ûª~ä€o([Ýk :=Iò)EDúÐÓ†ow™µk®kÒí-“fŒXWdW«cgŸ/8rù¯edá.<ŠÒ~²É(rô]>ëzëé¢iÛ'”UŠÄчze\Ròƒ&øÌÑ—ùO¨çœö'±.äJ¤&"íÝ¥K¹M -í!‘¼,ßnb¿Ÿ\ÍÔÏŽ.œ¶"!úüÂQ+U~‹z«æŒ>Y¤Mé·§ÎÜäíË·5¨‘‡‚#!ööÁõëþ¼þF%ÊW¸vA]ªÛëo/4Wã×ÃþÂö“a&ülòûê{m––·}»qøä{uüܯî;ýRï\­ç¸öfgV­;t?>_‰VcF´,¬¤ÌJ›£òílwi÷©§qæ^­† ïäõ~óØåuôp|7"×.‹6±±ÔìBÄ„4343FúèÛ{Woþó¡Iëâ>w¬d/!}Üë×î»þN-µq±ˆzmÖ}ÍŒ:Ö|º·ž#Æ —Ë™ñKFú¸çVm¸oÝpqIÒ¾8ºùŽçȽ~•­x"Ÿ~½÷û¯:÷´rX(sèPµT!{¾PÁ¢Þ‰T7>U$/55•ò"‘Y¾|–JJ|ö$ãêihÇ’séü™ÍʽicãX›ÎýŠ“ ‰y&ªZ=ÿ§¼×yKˆ$‡,žÞÝULTÃ5ôBûÃ'Ÿ÷¬G$.ñËš¹M¬yÒ¹¿>pèn¨¶’©ä‚RDÊ){FŒˆéõ:Îø0Ç‹œê h8zÇö+±kÓ]çNsjØRéÃOFµn9°‘øÉÁMÛçî-ò[g—°?çÏ;çÔeÌ¢r–17ö,]¾È¶à 1ÕÍM»Š×¬Û¾ÖO¶¢†=1Fúçιuê=Zr{ë†Õ£nÚ•jä7ªö›CëþXûWÕÙ ,ßž?÷œS×1‹ÊæK.mºÀ„¤Û;OÔh×kTã°Ów¬:轸£ß¤žFí÷;ͯ€”W˜r,唌1"FŒ%' "Ò…øÏYà/k6lfe»¸[»V,›*žº¨½Å…eswGUí;¡Ÿ³îÍßû·¼Ö3&$óJà &äø"esü’xbpÝDDâÂm¦lZÞœ£ø7·^%\ŸÔ²îTÃ}ϵj‰ƒ¶@ýê£f´÷ ¨Y¥²wfuK~ÑTÿr÷VÏØŽOn‚ÆŸïë3öÙwÜt¨‰äærÞ5BN®œµxÏwz‘‰…,AŸO›Ü(æKH‹»H¶Gh‰ˆKE¯´Rò:•6óV®À—nÄ?—ºYBÒ¥Y}.¥,êÑgíÔ®{7ùkÚúß–yø·UëéõìxÊHìÜqôà–Ž"¢²ïoŒ ¼ÜRwêxxÙþã| +ˆlkù6:2öÒ­ˆšNDÒ’ƒç­hÎi_^gDŒ11&òì3n@u NïrüüÉrÃF´rS;wteP˜¦fÔ‘cáåú÷)¬ ²«Ý®ÑÑ1—nEÕr"NVvØ´~¥Mˆ4ŽMyô,޹ÉebŽ—*MLM¥D,Í(&A "Òö?h_ž9ìØnAË ö""§¾oÚð ’ãÁ{­fu©ã"&rS<ö?þ?b,ùbƽÄ8–þ‚ä€Ü'gÆ/ñÒÊc—s}¸rô¢{ñb…„#"¦×ó¦µçlQ2eV‘Ì"Ÿi™]›k=ù×?ož²{Ëù9û'˜fý¼3Ÿ¯ÊÔ «O¯aelñ“ÄÆÃ–îüﺹ‚”e&mݯ ?:iÜ©ŒÅèCü°ë¹ðnäQG»·Üøás1A ãKIÅñŸÙBÆ2\=ÍR‚qâ]G§\¯Á+ì͈‘̽Mïšgï:“¿õüúŽ"ÆôŒÏÚ×"w{ñŸ!QQÁO£âžÍtEdLc­uD¢àÄ8‘\)6vÿaŒq¼ˆcŒI•žã91F¼L!´z]ìÛ§QqÏæ LWZ‚Þ‰q¼HlX“™+8]’V`|òëÊð&%÷;0¥4û“ŸGË]ÝÍyÃÂ&ù=ò©î¾|.w/f#Jî°ÈP—|Ùyòe€ü¹V_›;{x•)6}Þë.'(´am—B2§bv‰ç‚ÔæµóKRšäŒ Ì¢p]¿Âuý„ú÷iòÛ¹çÚV ‘ð:N#ñÄô}Æ&$Ç“ Ó[á—auu +…qA©k-ÇÍ;×赸…³ÄÔ¥pқߔxJ[óææK*1±Se'%‘2'¤מëówrþÂI ‚>“`!0NlæXÀ£ <íÒ1IDD”¯Òê'Œ1AŽˆôzF"‰HÐ "«†£&´L¹¶ƒËMEI‡ \r c,ùJgAc'AH`G$úKÉMùÇÉk DK^ÏK '/nˆDÆçRòƒ PÚe ë zÀñÄ  ‚1å¤öZdˆZ€ü¹=BPvç_2àÍËZ:ò‰ß‘s nžT¹y璻枮Þ¶‚îÍí3‡n¸ jqÑ©‚m[Vñ4‹»~%TäÒÈÁÄ©¬`ËÆƒ&Å®Ûø\W ]¡Rkw«ÄGÎܲpeºÿçÝÓ­žvNQi¡îãZ6·ËˆWƒÛWó´ ø°»×Þ$öE…u»¶4©({°~Å]­M%Ã_´¡œ-RÅAä?O¼÷äÚŽ¢÷_´ 2íII«“q¤gû×*´«tiïš#Ugµv5\œ|~^rÿµ`×È.Ÿ³‹<æé;2-f!Jy&NmlÁ&\2F…ä 2ô…°äÅSžœ3ùDi†%“/i`ÄÇ‘ ×§ë0†‹ä MIR7ˤ;O¢uEíED,>øiŒÜÉÙVfªzþ:V_Ô†O-XÀµÈðÊéù—$nm¦Ï{ÔmØ„‰ž[—t˜¿B¿xÉ– ýkÉĹL½Mì\M=Â7Ní»2D6%[L™ÞÜY"µÉ F IDAT3òâ¸ßfŒñ·¯Ð¶KÇK.f(±å¨v'ÎpTQ|àœVá[Ò­.J`,*ܾÎcÅÚ½¿Ý£'©m‘ª GLnë&¡çi?.­&üü¿I+' Ûdó“¯_ûBÏN§4Œ£¯m±)8Q–¿fÿ…SêÙðÚ/ÍÂGïƒÆƘ.úå£{œ1ñp¼ÒÙÃ>òȆãBíI­ê[¹Þ¹qS@åñxFºˆ›®(e­{ù×Ö“ª’}ÊY)”êÚMܾtùÖt7Mzûàâ_ÁetS‚‚¡-.PÚäÁH`L04ò —Üv—¹5®k7áã¥1C1NÎÊIó9*bþwéÖc2×)\½R:tc$¨Þ=|ðÞ0¬‹W8ºV«çrrßúC~•lâníÝ”¯ú/E êʛ۽å°i½ú×7„éèÃñP„ë§ ÷ËñKæµ7\ª¦ oYýÿ¿~)ÑqòúŽ“Ó.]dÌo Ƥ/@Z ùÌÍgÒq(Õ^yÅXªuõÿ‘¼t¥Æc>µ5¼EÉÖ~k=!ããEFý˜ò §,ÒeþÞ.)¿w@Dº×D‡&“·tÍŸæ+K»"oÝhÝ?>™"NøøM˜îÁ®Å3Rqï3ö§€£ñÞڸˈ«Ø¡ÍÉ_÷n:_®;#bñ÷-òM’Ú•m=¬_E ^ ׿£Æˆ¶ïÜ·ôt¼@&Ž¥¼;ËX¤±o°dœ)y:$à Æa8åoq$qm>z¬è÷©¥5Ê/c†4bŒÉYDâÖ²}å ­+=iRª×”Ñv2>õEœ]1ïlò+ÊßeÞ¯~œ¸aÇò©jD–^µûŒoí& ·‰\shÙBiÁª5KX¼ŒsÉ7–H}S8"䀔oÆ¢S²YĤ 4À®„ÿʉ3':øvÈÃ/P÷z‡Ÿß‘f;Óç‡/±ë]¥Š–bYÈŸz×6.ÃÀ}XÀÄI—jLÿµ‰ˆq™]À}Ùå™ßËí_¬õŒ÷“Ëð¬ê‡Ç/Óõ[Ô×Kž~û8ŽcwÜ©_§>>nð½ ˜dÐÿ¹TN_úÑ )weþh >EÊ ×ÒÎõJ,íUЂq:".eq.uÝŒ÷ŠþÈ×34ÊÓ'Ž>r×猿qÞGúË¥¹Á´æùÉcÍ= ÙË“^]Þy…+=ÌEÂ2Þ#}ÈðÈÑûÇ}çŸÒü÷žï˜ÍB’§(å>ѦN÷î£Íþ´œ²Öâ>L’[ß죥¥vX°Ï›2Mj? ÷±5Ø'_§‘^ûþÎá}Gât$2+PÉwpGO9¥~‰K¿€ü¹¿ûXø0i°Ì6"«-öLc ÷‰Â¤íÆ/k—n?±Œý.”|8 ?@.†n‡œÅ˜ÓèÙ9ýÎr¸¼¬>!˹¢ùr;Œ_ÊéüÀ#îÃö0—+¶.“ǹܳÿp ?Àw!ã—rˆÀ s&}0¢'Ã\–šÌ\VÛþ,“Ÿt?±kµ³ÌÓ÷Ueä²€ü t;ä¬Ìï€ÆØW´Ø³°XÆKµÓ=Ël1–IcûäVg–lX&›Ì}êÕpb ùr;Œ_ÊYOž?ÆNȦgN`'Àwá_½gòäêA¿”Ú·iðƒØ½o÷¿Z>òäR_ÔíÀp‹/€o‚Ç.€ÜÉ0rÉ$°7>!ã—> ÝÈY…ñKÈ_!ã—> ÝÈéÅ]Þ´íŒ[IÿVùê‹üšú4nêÓ¸©O—µAÍ£Õ]Ž>›½©>oÎlÞvÎM©îÌjÛ´ûÎW:J9 ã—¾-™×  '”à%åÆï\Ó½ô_x ©K‹¡#ûV±áPúw"aü@n’ûî§y{nÓÒ5Gï†kåÎZæWÞJD,éYÀš…[O?ŠÌܪw2È·¨©>dÿþGŠù•s,àÖ{αr×I£ZVriÓ‘Li¢s¼Da¢”ÄŽäI4·u“¹¤¨6ÿ÷±ÅÞŸYµdËñ‘:…s•öÃF·-¦x»¿ÿ•‡Ö}³gçe“~E-›ã'$VCbK¯º}Gõ«Í:ñr"Ñè6M‰lÚ,/Z¿ôV§ÞLô‘×·.Y½ïÚÛ$‰M©Æ½Çô¬æ QݘÙm²ªS/‡À­Gƒb-Šu3±g Šþßï‹Wì¹ú6Ilæ\¬nÿ1=½­TƶŸÜ`éæ!Åe8,‰¾ðþqðmä¶þ‡¤[~™yVÙêץ뗎¬•xð—©þ¯u,öÚòÑ¿Ýréþ²šÇ¾‹ºj“7.iJäÞkØãË£þ¸^½‘ص״±œÅD•ó‡ÿÓçä_ÁÝ<=3§Ä‰MLe<'VZXX˜sBxÀ® ~k;VvÙµíYáèÌ3Ï“:‰:.XÔÓÝPŠKw7bº„ÈpIùªvî~¦«WÐDÂó"S ‘&ÔP¸æÅñCÏów]Û¡š“˜ÈeDָ߫?ôHÄÉ+M\<¢‚)‘Úêþ¡‘žDÇiÞ$˜x–+SÈEI.%¼‰ˆHRu¶ÿ)DÂø%ä‡Ìèc^'YUt31 B[zº+"½ Þ U†…¤Ž%¸AáÚrDD¼q¼’ÄÎËYüÇ›(Q–®sм»ÿFõ|}¯6[ è4¡T‚@DœÔTnìÐGÝø}éš=WÞ¨9¹¹2I'Ê/dríµ:ôI¤¢ —¥ar¦n^VªÿÇ ÎÄñb‰¡8‘"Ÿ’Ó& –åkZ¹i\·g•¼Ë•©R»n%‡#ñ¿€üð/b‚@"¹$Ë-q¦'qñakÆ—5IyHb¦Œ I³DÌ¥¥³~ë3iÍ¢ª.&êëÓÚÏÿ¢ úàÎ#Ä®¾s¶zž¸xýÆéµGwï¶rq7 ŽÅ ã—RþÅÈ r×€{‘EòÈû/â mo]dÐS•UaG»ÂŽü»»¯U†GÕ!·Þ0;OÛô-nõ«[Á‚c1‡Ov>p»{›—~“X¹´®o5tíò½+™‡ž^³ãµ{džl,|«‰'¯Xqlx»ââ§G~û3¡ÔÈ*¶" ‘.üÊ鋞åí´OVø'”YÍ>ÝDª‚:1A¥c‚V•¨‘+Å–®VBÀ™Ó·‹rdïݶÊÖ9ÓæX i[ɉ‹xôϩ˒vê¦Y[béá(ÚwìÀIç*–ï¯ì^-‰¯KDR«–ª£'/Ü7wÄÆ-—ºÕoáî¿uÉ.ç>5ìc®nZwϺÞÂ" ºÿáKÖ½þcê¶h例‹Ùro®=N´ð(`ÆSÂÅñ~“ƒ,Ý8ó/ ÛùáÃü »½nʈäß”5gí>c‚nñš©C×ꤎåZNßÂEÌQ¹ó­^¸uBŸXfâZµÇôÁ>6¼>„ˆXìÿvüºóJêX¥ëÔѵ¬ÒðW?ZÑkôÑ"šíׯùÊ }=«÷î|nÖªñÃÅù›/þ­ÏØùÃÖ®Ú5oÌ5Im‹TiÑÍ&]ŸgUcÈÀë3Ö/˜tجPÝö­‹=:DD$vi4°ÅÕY‹Çœ–{õžß7yGæ÷üKÂ’Õ³†íR‹­K65·[a9©>ò’E–ÅŠKWn›º'RGÊüU;ŽïWBA”ÀôC¤úÇ/ ‚ü:øÅËñ ñ8r-SS·n®ù]ù¼2i*T>_„+:% ›EL*Ñ Aƒo¿éºýýûŸª¿zY;'1©Ù‰3'Ú·iŸ^È‹àoBÞxyzY[Yãmȵ""#=~äìäìæê–7^*€ìhyC.õŽ_zñòE‰b%,Ì-4 €\ËÂÜÂËÓëîý»y'? ò@åƒüyÀ8~)>!ÞÊÒJ­VãÝøÆ._¾ìííÅ…A°²´ÊKC}Pù òùQòƒØ©õzÿÖ8òòv„ iþ%Žãˆˆ1†·àcŒ}éGÏðEå?`åƒþÈ¥~Ìù—Ap=@®ÿèå½Ï)*T>ÈðÝûÇ/qÇÃW8À·÷¥=ÆXëø÷*Ÿ¤»‹;Œ?v×Lo“ÜøÚu!ûúô9ÙpÝòö˜‹Pùdwrs„ œ¿t}n‹rMç\5Þ–ôaÇ×ñðç;ýG¾çÖ÷®Z¡ßѰO|–U7'Ö«Úzã mŽ…ÇŒ„÷LJÕnÐÈðßs1ñ×§7i=ëz"Ë!úìà&]V©cŒÅŸÕ¨öÐï_£Ï…¯ïT½æ¸s‘Éõ ‹¿9«Y¶ëŸhroåÃXìù¡É•O톾]~Ù|9\›­=E,yŒFö©ƒÖv­Ý Q›¥w hƒ·õjÞ÷p˜>;H9·¹£òù·!mC.•ƒÝeúŽð95aÖ¶V;xÉ…˜KË»^°Ï®†¢Œ ªŸ\ˆ2µ=8|%²aS›LÒµ´@»q¿F´åü9ÀÔóùjÏÚë¼}ðè|ÍïàaÂî0"Ʋ7¾€ ¥–"ñì0rœPT™æÎê8˜õå³ò î7ºå¾K×ß­8º”’#Í“Ý ò-VûyHsqåC#NZvàôÞ…E ïn\»mâ4‹Í š;m{ÁPŰœ%Œo{jË™ö³ÛŠH`,¹^ËÆæ1 ß‚ýúÎ?ò4Q`ê ]ZÞfã/=´šü÷û;«FölÙ¼©Oã¦Mº_}9\«Z9xÁ=Ýû}C[û4nÚ{ÿ«—«z¥cŒ©Cήç×¢©Ocß®“¿¡cLˆݰÓƒ{çõnÓÔ§Y÷ {ƒÐS?€uV<´/>!>å‘ÄÄÄCX·aõWœ4)ÕcdĽ ö¿Ð’þí±ù¿GÖÙ³´ —Ýʇ%=?<§k“ê弫VkÖmÌ®gʱʇ1FœÈÜÙݳWéjm‡÷*N/=IdLq~ÉÏ|›û4nêÓ¦ïÄ·£õÌPó Ø}|ÍØ®õ7m>xy`˜–1Æ´aÖOðkÖÔ§I§anÇk!öÞþi:ø4nZ¿ãðÇŸ' Æúo?¸pp;ŸÆ­{Ì?üîʺ±]ê7nÙa®ûñ‡ÏVm]‚¶ý¤J×{alË6KîT?ÝÔ©ùÈ€H}tàè†æïÙ2Ù¯iSŸŽã6ß {|x~¯6M}|Í;gØV"Ò…_ý}l÷–>[w~0(Q`Œ}¾šMÀÇroåƒþø¡#åÌüK"‡†#úýÑeÕìÕÏ"ýÅmWv*$¥Ä‡“gc‹ ®VÉæ¦lØáëQ ê[+ŠöžÐòø Åko—å|aþÚ`ï±³jZEÞ2®ðhõøåªLÚ2£r7ðÜÛh=ÙÏ÷•¹œ#½^ŸrÂxÒŽ ¼}£™ãnõZÂ^>¤¤B$“GÌúu§ÈwÜʉÎêû{æ/ûu›Çº®‘îáúu\-Ÿ®=‹»ŠÄÁ¥š ë\ÔÙD|fÍ‚kJmÝmÖÀ{wzNYЭ T¤< bĘ $ÞßKÓÝÉtZ=IÌd$“:Vèðs"öòøÇG­˜¿¹ôš!‘îñ–?ì;uýµiÂ_ëÖ-Z_¹ÌØâa»&O=¬h5x²·úÅ…ßW>!&Ú°Ó3'm‰ôºx´›îÁù¿ý²Ìz娌H÷d×I÷ƒ~mxsÝÊ%¯:”iÞmR½×{—o_vºÆ²¦öiê1"‘}ýNÕŽÎÚz®õ´úùÆŒ[+0fìH0öS_„.*pwPÛÞãj…^ñû¸!G<«w6–ý½~åªmõ*ü\Œ Lâä­Ÿßè¦ìÁžÕë§l/¶¾›EàÜÏT³Äè±€\\ùäíü BhxhHHH’& ÇPž!—Êœœìmí³sõ¾lZâÖnœßþžÛY7_Ý£¨ò#KhžŸ:íÕ»‚•©¢qé˜ÃW£}Zñ¦¥ûNlpvÄœµöOß*1lO^Ÿ¼Uš° ‰[åJÅ ZpÝKU%""Ïþþ—ûí78—v·äÿ1F¥RÂñ"3ssé^ÙóÀ½ßêeòqDÕ:û;±õÒ«öUñý-hlkø®mÝÉ“˜&."‚/_ÉáÈ…Ç‘¬‚¹\Ìñ23ss)QcĈ±ÄGûOF–ì?§MYKŽ vÚáâÀ#/ú‰ó5œ1­o19QYéÅÃ3o¾IòuRâð†¼ÊðÑ«S»Þ±ã‡£c¢©S«nàù³ÑÑQææujû¤c1ë§ÅÎMÇúííÝþÚ³]QF$véþˈ¶N"¢rNáWúŸúëyMeªYLx ¹ºòÉËù!4<ôMÈ›|&fvù¬påj­æMÈ"r´wüêBr|þ%f&«ŠŒNbdúÁgOó"àL”WÏòÖr÷zÕ-FÏìÐ1 FoïÚMë–´‘PŽU>Œ‘  ü¥C ‘ȽÑÏóz”P2A’^œÚ¸ô÷SbHbbÆ%è]5:½Àq"1'G$3Wp:URÌ˧ñ–¥ ™“±®1T<êР0Þ¡˜ƒñ´½Ô©¨­àÿ4\cˈãEœ “˜HyŽ'&q2¥DÐèÒ_—ÀŒu!g_¯‹÷ž¹Û/4îžru…±‚3<̈ó†æR¥ŒOq‚ “(¤œ^£JY”#â­ 9ˆ÷½{­ý|5 Û+Ÿ¼›BBBl,,­mlÅ Ž¡Nþ\“Ȯټ]®§¼|õÜšI[w·Y¿cxåת9ʘR¾…”+t§ô;¿§—_•);7Õ:vò¯þÙy6?$i’ìõ˜÷ Ö_w.ËÞÁñMø»ì‰½ñ´œÌ£YmË1›Öì7kWÞ,,póÞ7ß:Î"áuš)Óô„ ®‡<éÃé;e2YÝÚõ(“»½æØ)À¯ª|®ÎàæÛ²Š§YÜõ«¡¢ü¤”S•¥ž²gÊ¢'õ|ñóÆY«óÏéc[0_Â_Žþ­wŸÙµ#DçÎŒÝÉ5a”(dë•‘ÌØ°z¯¾–}ìƒSûÏÅéK gS©ÉO;Ö.Ýê1°ž«îÁ¡‹¼Ç7aa©%Gm²”Þ‚æ}MÓŸÀ‘I‰6-†o}IL±CqGõ®½{Îi %=8¾çh„¾Pú 4v^^(KîeÄH~åô·²öú—g6N(=¨’ƒ‹æsÕ,Àw^ù|×ùÁÐÖüZÿÿUSþ¿ -9²µ95~éìâßþçÑo¯-OD¼]ƒ½wû­™}¸ÞšVÆK5¯ÎžxëÔ¤ªCÊç·,Û¬˜nö!ÿß^þ)k¿¦½›DB­'t8ÐuñÒÀ²]¯Ñ´PÛƒ[&õ™ŸD2Ç ~ӯ׶äõïØW¶²9ÒëçÎ"§õ\õ”ˆöŒïsgغYÞ>½ÝX´rr ¬P—Ù“[™¤ß¼õÀÂI›t¤°/VµIMs.Íi;â|úw°tû’XiØ´qàËÄ“ºûv­ö`íâÑGMË œÖʸ‚ÔÓoâݪÍsÆnÖKíj+‘CS |]å#Í_Ñ#|ã´~«HdS²Å”éÍœE¤¡©|(ÍéxF$vªÿó˜çcg,\\`Îa­_.ݵpo[¦a½zv{žRº›¯¥^´L¦e{ýì»dåïK®JìË4ªýSÐaã­ªœ½b톱Ç93÷Ú½éó“ iÓv9¤>uæýi9ÔïXeßìKDŒ1ÞÁ§·ûKvý6ŸË·hQóõŽ7ŸêHù™ˆˆÅÜÜ=s¸Zb_¡ÝØaUóq5úL5 ð}W>Ùm”Í"&ŽhРÁ×­{ùÊeïŠÞÙï€ÜF©TÞܯ.!ëÝ'Μhߦ}sXTe IDATØi§Ïž.V¤˜F£Áñð]»v­|ùòY_^*•Þx¿níºyãå£òÈc•Ïî}»ëשŸÙ_¦Ygg³ÿëþŽ(͹ꄄIß]NËÔÔ4ÛûÕr|þ¥Ü㸴÷€oYé}ÑGO¯×神sT>¨|¾§üa×Wï‘çú‡¥ 9òFd|^NοôHBß¶‚ýâ!¨|àG­|rÅý§SÉ·cºØ§þófŸ+=u¡o~Ã5®,ñéáuk÷^ QIlJ5î9Ô·¤O$ÄÞÞ¿zÍ‘;a¹Sù–ƒú6.lÂé¯ì\¶õôÃ(Á´@•N{ÔÍ/Ë3ñ"ÇóC®^?N·C ¥R™¨J”IeøøÆÊ–-›õÏÇq‰ªD¥2ïÜN•*Ÿï*?pæí³ãÆüùŽ9”áyC¤`‰w~_´õIɾ¸Æ^\¿jé —EªZÄþ½vÞ¡È:&Õ²|{|͆¹¿»-ï_RzfÑòs¢æCf•“?>¸bíƒóý<¤ß}<ý..4O·…¿ô…\]\_¿´0·Ëä¨Rr­$uRLlLר|àǬ|þûù[?øUêÑiѾa‡Çæå«$‹rÎ&<ÇɽìØÍ ðÄüAoõ¶ìåǧtö´Ö\|$ #;O{)Ç‰Í 4‰}òZEžrÌûß¾¹_ê¿Ä󼳓s~çü8xr¹<6Ε*Ÿï)?d‘ ŠQ‘ÔDjhò23SE«tªè$’šH ×Lðr3SEªÔ ±‘Rn¼{/7—3U”JO–9þRqçŠî“œ*ê¿”'Û%€Êò˜\1~éƒë§‰˜áÆë8ž#âEb‘aÎ0ÏsD¼XÄóïû\‚%kñ[Ó¿ÞçEëžMöèŸ=?~ðà¡—ßõÖo÷-{}ÒˆÁC‡Üôì×{J…B8²ûðGö_qõýcº5ŽkØ-õÆ.Î5ËvWàæW޽¢gÛñ‘1ºvK*ݳý¨”Øc@££¿-Ù]*DéÞå+Ž5taLM?Ôí¡çǺä’!i—D›¯yöÞQ½»÷OÚØ¹wÛa{þ_éKœƒî¹µwÓĸº†²eñ–E„ôxþ›%ŸÞÞ:ÍÉ›vú¦Ñù—,ª|WŽìÍû‹v½wÓ¨\W%8JKå¶ÍHžýóìWßþfã!§98< ÐáKB·Af§/Û}Só²'Z]שÆñAH’Ù" !„d 0™,¦òŸ%§Ýi?¼ewþÁÕ÷øÖõ¦ä’RGʱ"E„qéDmpõ/Uü—@~8CɲEbŸ8s¦°´¾ûí‡;†Tü•5,¤à!„΃‹^xî;ÓÕSÞ¿ªc‚íøÒ»®ÿL!Ìq]ÖèíôŸ¶_jý%¯õ-"=)ë%Iªü!„ŠS17¼~ÚóýbÊÛ¢$KH}Kµ!„qú—œ{_ðCV‡1£Z…Iù¡J÷üøÉÏÅ­nïžœd‰—×m9Ô¯^PÙß)²"¸~´ø{ŸhqϨN AB(²¬”]aŽ¿ð²¦ïÎùà#K~›ÛÛ‡{»³Ä4K2/Ø’Ú4BªxSzµÅPÃöýK>ýf{‘"Ž5TÍßgÏgî¾Ìm[ÿ]¿jÑœ&wW››îº,! þâÔîöŸš:÷ç¿3·ÿóÇž{ìÓÌòù¬ñMãå­ 2~Þ°~õ·Ó§ÌÚR~C9SL—!Mó6n,ê0øï7Ia¯ê²ú•É,[¿=óßµKæL{|æ¦"! V><¼ß5o0ÿ’wjþ%k~×O¸õÊv¡ g`ÉßãJéº7œ(„B[v½îÉ'FuNBDõxðŻߙõù´æ–ˆ€¸Ý/k-ûsòà{nýç…Ù/<öYt«a#.o´û×òÕ±wciw½¡­jãvRHÛ[¦?RV&}V¸P!QN€O0Ý TÊPó/ ÙQZRbw*Jùlީϼ;!•Õèü쪭¿vYì7´¼@˜ ð~„Æè_²g-ùèÛ]!„X9ïÕQÝ®N멊pÏ)Rœ¢„WG9NÛÆ|¿É1nýí Ïñ†Ù«ƒü3Tÿ’µîÀ[˜]‰Bܰ҅ÈX ¢¨å.|y(pwþ%ÿ¢ *e¬þ%è5!dH|€Î ê!˜ÉgÜkôôY¿,¹Áù8†pJx *2B¥«†MK õ=ijØë²âìü|ýƒ,ËÙ9ÙYYYťŬ ݰØ’““âL&÷*÷S×Ä€í°\e¨@ÂeT¼«gþ%wdçdïÏÚÉÉfý(±—îÏÚ/„HJHòäuè_ª}•RvÊÓ_7u¢ÄDå…Oò'[/È5’••g±ZYºá°Û-‡-YYYžä†PHÐ÷>G£ó/ù9?—'$&9eY–e¶@RÜû>HRBbÒþœƒžüÓô/·Nþ1¡¶ÖXk5}Wl{Tç0Z~$ÉTXXèùëPÊ×Ò r{µz¾Rè_‚G%HºiT̾ªö¨›¡†‚2¥CÑχ2@~pÕš×ø«5#¹Z<· ;„PEAE ŸUÏ¢(ççù[%Á±Gí¡Å=ž¯\î§+éÕ8/È”¬0x9©´jFÖt_!ƒ»Ö@-˜ÉÍ!*«.((`KR·‡BCCO[¹FAÿ UA Îí܃WN?_r i ÞÝꌺÇö÷õ§Ö˜n7½çú¯Ò*£‚WV„‡ãô/éD­žçãÜž.ûøÓ©óT¶E‘lAH¨½ïó/y°jÊŠ“É$„R²ÿç÷ÞLÿù¿yeÎŽ6·>:¡~îÊ÷f½úF½Wé)åýþÔ37–aj#™<¹ñ±)Š¢‰ ÍOy‡ô/0fœLî€üà›rSI’J÷¯øåP½´GwJ0‹†ã†üòÈÿýž3øŠ¤“o´xçâ?Š.¸ùÚËÚDH¢ÎMWLY¼éDK£Ã»Mù¨[ñ?¯Ýü|~••¶œûwƬ÷¿^Ÿ]lMjqéõwŽé©™¡Å-ß3Ãü²çB/Ã#’¤áùLk{T˜h8?œ~ýƒ(>°ó˜­^£H‹$ ×"ÉôÍÖl»”\q{jç‰Ý{‹#:Õ 1I’IÍã•õÛrì=c]/XV¹žQ»nüðÕùÇûÝ;õ’„’¬~ÿëp¡,E™Ù|¶rkŠþ%µ×%–žºÕý;0ÍÔõPI°¡Ç ^Ùlhõ©æ_ò ¹èD¡P/°¼¡Éà8˜Wªˆ`éä3ŠD@H€ëϦÀ°@¥èxÑyï^í,ÈÊ* Ni×¶Q Q§a‹.Þø¦pçŠ*–‰_þ%øáàÇ—šú»ú8‡sll†Êœ0ßÿÁu£€r&“É$IÂd1›M.Ьˆ²¿(#™$!LfKÙ3$׬D•þ^ˆSž_ÆÓáâFyËžšøÈ˳æ-^{ ¤ŠçÔá¡V³·}0îü€*Kî âõ/ËFØ{h§BËÈÈÐâ2V×üK’dŽ ’J J×criA©%4Üf:¹!X‚#ƒ¤’²gÈ¥¥"¨N°Y’$!I’IBS×OÔñÔ;MW.[µ~ý/-ž¿ôšW¦Žn`å[¦ªÌPýKŽº<KÕåyà<¨š¿tlá|%­ä‡3®0×i]´iW®Ü&È,쇷ãû$V*K-QØŽoÏ*$a?¸5Ç”pYBÀi78³ŽUdaKlÛwLÛ¾cŠÿ}m«·œ¸ªaœÉ£oŸQn:á••ë^„ô/A”_æ§£¬F±n_—Baá÷ªÔÜùáÜ5fåBÜl65ìÝ'á»y³ÕOmn_;çÛœúW_œh–w|tç] ¢ïýàÙ¾‘͇\üÈÇŸ.\÷øÏïþZÒæöޱV³Iq•8ÅYRTXd ¶™Oî‰í{ç¿ðɉ õl'í_ŸYÞ¸A¸E;“¼ÖÒÍ㼞‘¼8+ÀÊ)®ù6lž@~ðßÑG§ÝÿÁÖxÌ£޾2çñûó¥ˆfƒÿ÷ðÈú&QjRd¡ÈÂd2™CÛßüÈM3_{çñû‹,±í.àÞq“PN¬x|ì+›…B¼pËu¢Å¤Ï_èYQÂÄ´nøÖÜç¾<æÁu»~ð– BLœè©Í¼áaÚ¡‰2z+:3$/ "Ñf]î}ðO0r¢JÌ¿äµmÜVÀ}3ÜwʃMn|ïÇËŸÒüʇ_¿òÔ²UŠì9ca¯³¿lx›ÔÞHÕêæ¥(Šër†úRÑ¿œR^TçðZÏRZ˜<ßÃèf&_£!•aâ­¢–¢íašâïù—Äó/¹;’¾këŠ(i„ðøúæ_¼S f”—ôÖûw¥°"œ¹oÐæüKþ¾@Ë{QWä•“’/c€¿V®«s‰ ¡R´ÕR­m„n,½³í֨齓èš*æ_âF jÎH®\#„  ¨å/¹ô ´Á§Ÿe­¥úüÞÞ|û@~Ðn™«þš‚É_¸lTç|(h®ÖWííP([϶¾|¹²ø^³×Õ2?÷/çççû¹ÍF;ICŸ4???88Ø“W I];Mv¦F+…U¸Æ uå@:Cµ¶`©\¡&Ì¿äŽÄøÄÙ’DRhh(ÛnäççÈ>ŸèáëпTYff¦ë‡””-¥ÏÖF®¢¨ Á6 Íñ}bHÍhùÁujy_Ö¾ââbV†nØl¶¤„¤èèh§ÓéɶAr¨LK±A¹åÜõ“wû\¿ôò×g T ¦Òýt‹z}×å^çú¸ …WWDFF†‡ üœœNgLLLbb"—P뉢(v»Ýápxò"Ü?˜_`H€¶òƒÂápxXhB¯è_`è:˜® O6#ŒHß•1± N\6 ÊMŸ~v5ŒÀ¨HU×p«öf FÍ;÷VÁØ&È€Ï0ÿR-rµ×W¿ò¨@oµŽžf4Ny]嬑:[›ðY°q/ó0u)È@5qÿ8Cz½~¤äÌve4§Þ…=C›S<zÇüKþWüÏŒQW<¾º@}ïL>üÝ]W\÷ñn;_”FAÿÕ@­‰êàjÀWü:þ·rÒUÏo*+Bêv2á®1ƪeHD)Ù÷ÓìY.úë@±0‡%·êÒìÍ£:Frň0ìÀá¼Z¯_û˪ýbãQ[ãA÷Nß3Î"9+gOýë9rdJ‹€\¹Ž«üÏÛüÕŒ×ÒÞoŠl:`ÜÝ·hdßþæM÷ox}ÓUsØé¨×ûö§® _4}fúƼèö£'?zUËJ_¹Ò=?ý~¬ÞèçoP×"„hÙ¦{¿+óB(%{—ÍšñÑÿuÕé~ÕÝ÷§¶ “ª~°`Û·3f|úÓ¶äõŽ9D!„(Ýùá w|yÏœ×Dó?o„ô/Á5J5Ç7@ ¢0uã8Na 2 !¤Àä®cþ÷Ü›¯Oñ––»>yqö¶!„Žm³3´½~Ê£·^”÷Ã+ïm,¥»æMž²°äÒ»ž|ù™‰ƒ”½TÎÒg›³§íøWg½þâµu×Ï|læºÿ?¥ßSîèéøiú„;ßÞÓæ†É÷HØüéÌe‡œ•ß9$>DälÚzÄQñˆ-4@Rr×ÎxøÝ=í'¾9ûÃ÷îï~ä³g^[—/WùàñU/=úÎßuÓ¦<ÿÜãc*÷=™LB2q(<nû`Ð’Pù¶Êæ b­†Vй×kÐ]*¹ØÀ™¿÷·>ÝÕû©–ÁBHQ]ÒÆ!—œ8œÓ´kç蟷í)P !,-&M{¢´$ ü¸üÇ-9y¶ù ³ZÜúîøþq&!šüññ²#B8²W,X:äµ›z7¢Ám“þºñ‰ùo» ^KË»Ÿ¹·_”äh´ïë¥ /zô±kˆ|åÿ¾zéßC¥Ã‚N.˜ºÃ&\ñë”é׌ù²c—Ž;v½´G›d›rxÕç+"®~gL·$³ñ©7vùþÙe™ûWõà¡/VY/›6iTÛ !ääà Òç !„h<îãïDZåýKz8¬Ri+ÎN£ó/ù;?ÈE˾r¹BXR†Þ7ýæva’Jñ®Åï¾òÑ¢ kH˜©ÀQß!»†c-®7lŠ ’ìÅ¥y»wDuhqê0ŠãжSòÉe£¶º­ãå™GñB³Õ,„&[H Édv ˜ƒB¬²Ýyꈯ)¢ãÍo~:`ýê?×nX·èÍï½wу¯Þ¸yÑ®÷nõ‘ë à(-•Ûæ¨òÁ-D½Q‚øjx!ýK”Χ†¦T\ÏU+ŒÁÿ×Owºó¹[ëìøà©·¶æ›ƒ,’Âþ_ÆäWWÔ»eÚ¼!-cLû?xçŠ3ÏUùËNY1™Ï<£sÆgîÔ+Gé,ç„Ìaõ;÷¯ß¹ÿ¨[O¬~æægf/ù?§°´¾ûí‡;†T<ÉjÝûvîyK&3çšÜưàÃ]±Æ÷Uµ1‡c®¥J(Bmï|Œ}ÅŽFç_ò÷õ’9,©aJ›!=>dNlS£¬ä8þ_vqÅ?,…$7ˆöb×<^Þ¿åXPt…¨ „*´Å4ˆrÜ’]zÖƒssu. .„áõ­‚ Ã*¹þA k{Ã3·íºí­É¯5xõöÄ&ÑË2®’›8·.ž3g¿£ÉÙ~/¨ÙàÎÖÇÞzís¹_≿œ·$×ÑAsÂÅ#:Îyí¥šMÔÐþÏ3Vš/y²]¸t°ï¨dË·Ï}G ¾°Yb`ÁΟÍËJv£ºñ©ÝçL}jjô©&KG¶þ¾d•5í±¡U<øH¯Þ fö҉״±ì^µà“ޏ²ù—>ºéŽ/"'}4³?ó/?Bú—Õª²çJÇÍ<Ü©Ãà ^§™Ïv¾œ[%€üàÖzC|<ó®Çž›ÚèÕû½ë…Ÿ½_JèŸöÀÜ×¢ûåãb£RÎ|0¨îßµ÷éw_ŸòMxó~CÕÙ»¦ì¥eY(2;™ó¡ ”’5(h¸|‚,Ýš†B— ŸÝ ù!¬Çô…=NþÑÙmÒœ%“„B\÷Üçו?~ó(!„±?^P±º¢ú¼ôc!„nš:ç¦ò¿?®Zåƒ Þ÷ÞÀûÊÿtMÙÿßðé÷7°??/æ_‚ö!œ}„j¿œ'PnÑèüK&ÖÔ!ýK ¿8Dë9 e䨗MkXF5Úi2$ðä­'hÐPÓ˜ª‰#CFùðæ_òO õN IDAT‰F•ƒ .A S€üƒFAÿQ ÕÁŒ0È€¿0ìÞA²@~€пè¤r­Õâ•A0ƒo!D#hœFç_²°æ æ!tÑ¿ä8öï¯KWoÍ)–‚[õèÛ£IøiÁ]ÎßõûO¿ý½/×!Ùb›tîuéq¨‰+ª¹q1 %>·–ÓÍörŒ?@¥ô3ìà8¼æû廂: 9r@kÓÖ¥‹7OMÇ7|¿è_K»á×ÝtãèKã®ønå;[€zöìÌ5i(\l À‡˜ ð&Ýô/9ŽmÝžÓé’¶õu¸¸MèáÍ™¹§ûѽGM‰­›%„F6¼ ixÉ¡œ"™M€êÕ<üJ¼õ¾zäçþ%Y–³s²³²²ŠK‹Yºa °%'''Ä%˜LT}ô/Ù*°FÅ™„–!ýuà„CDT<ÃU7ʹæu{¢»Ô)Ê9XZ/9˜h}“˜VÕ³lJ? £æ‡ìœìýYû#CÂâ#¹FV?Jì¥û³ö !’’Ü~‘èèh]ÌÜ*Û íŠ9ÐRv6P²Ú,rn±C'/p0EuÜ''cÙwÿXjë2´KœQ®LÊÎSQeÂgØÒ@ù!+++6"*&6Îbµ²2tÃa·[[²²²<É®þ¥ŠÿjzH¦“íó²|ÆetrÁÎUäÔï—Ö!pÿßkþܼfåæzƒÛEŸöå\³fMÅÏ;wf3sweHB®eÔ•¦TQ1ÿ’;ŠK‹“œ²,Ë´{«‘â^™%I ‰Iûszø¯ë¢Éd ¶ G©£lAÊŽ§9Ðf©Ôœì<ö÷ï;-­G§ÄDšbzÖ©šþùdµX?à”"3ÔJ½¢òœÃq½®_ÕÙBµÅ¿7É$y¼•Hà¯ä“ÉäùjÕÍüKÖÈ„Ò#‡]D;ó³s•°ÄðÊÁ]±Ù•Š…eŠ ‘œ¥NNÃÂt¿Kfþ%?–þƈâZ -ž¯ÝÌ¿d‰nÖ,üÈÚ•›öÊÞµnŦ¼è)&áÌYõɬY_m-T,QMêÛŽ­_¹1+·¨ðèÎ5kX´N`§ TYÛø¹&œ S{hñãÊÕÉýã̱ö,Xºzá—%RPB«¾ÛFš„p EBBˆÀº— ïiúyÍ·Ÿ®’¥ øæïÑ8˜o†W¤ ‘ÆR@GùÁUaJt¹é3|xôÛz™I!¬Ñ­ú¤¶êsjªˆë>vB÷ò'Ä´ê5²U/m}ª I?Õ9-=,†®X˜ZCSùÁ[ãFH ®K™5ôI=\¹zš §ï¦ÙGûqásM6Œ)­ª ØQ©ûó/y¥úwÿkþë³\³û„9¡ýð›'^Ó©Ò,–Σ+_èùŸc&}ð\ßH©pýó×<½Ö~òeZ><çé![¾{ïí¿o?êŒi9à†;ÆöJ:ÙH®ämzÿ‘'ˆ«ßš>ª®…VítÒ¿µ·ð¢ù˜¢®þ%ÇžzvièÄçîˆ9¼böŒ^ŒœùìIfWí¿áã§gþeR¥+‰--n~tls›BSh½`åØŠ™”Ýó¶Gn¯_´fîë/?c«?ãÚ”!„PŠ23žeYY„hò¢k ¾c~›ah]€Mð_µ¤­÷›‘‘¡Å!ÿÏßZéOŽ¿.Þ5p☋[7iÙóÚ;G'd.\¾ß.„PŠv|ùü‹;=xW÷èÊoÙÞ y‹V-Z´jÑ¢U³ºa¦ãk¾[oêqóÍýÚ4mÖõª;¯o~hé·™%BaÏúñåç×¹õ‘!®8µÝÌ¿ 'þΧœ£–‹N‰À0›ëMYbš7ÈÙr°TØ÷/~eÊ’:w<1¦Mè)oX)þãñ1£†Œ7aê—›re!ç*¡6WB0…5hY°swžì<ºâ§æšÇ<3¾k´‰•î—•ëf„ô/†ÅuÛUìXY&[§5ZãU>™mú˧²&´ndÝ·xþ‹gQÎέ‡J%EG×¾ûø<ÓØÉ·^uÊÈA@ý¡<2奧>ÿ¿!ñÿÎ}â•_HQÍ›…ýuÁòÝ…²\rt÷¶ý…ÎÒ¼-sŸ~óÐeONê“d•¼ú•G-Þ‘ƒa€RºÚ µE)J/æ’bh‡¿ç_’$q²ÑEŠì6þÞ˦NŸvûBˆMM²¹iPÁ¶²œ9qôÌòßzå†;ö¼üê)m{Ä !„hÚ´øgìŒå[‹z]4nRêÁé¯Þ3öU!’S"ŠDd˜²û·='2?¹óêOÊ_`îø÷N}wR[@­¯\·1ÿ’/¸Îï¦*§ã9Œé¬ž# À_»ßü YÌ¿äBÓéÄ—»_wäP¾U¼ðw4Kj4dÊ;—–ºÊ™ÒÿÒ}yψgXßZ©<•lÑÑ69«Ð.ÌÑn|þÃÑGsŽ;CcÍ>|óìzõ“{=ôz›Y!„óàâ秬î2ùñ‘-lÜvBíè_ ê3œèñpB»ù¡ªyÉ›*Š·ôÃþ؞ݒƒÂ­õÂËþ®´8Üb²Å&'F§C6[\ýWΣ[w„4I*»g¯98:1X8ö/X¸-¤Ó¸†Á!AÁ!e/à4G˜¬‘ÉÉђ盟Qn:ὕ[ ;¾ãšŸ9yN[&,•¯ + }Ie÷ŸVJö¯ß˜Tº÷÷/fkïóØÈF•«téä/ä¯~á‰E‰}úwlphÕÇs÷&¿§y$9oX·×¢Üð݇óvºçá ‚Oy©üŸµž‘<[Âô/ùxM³ t˜Àýå‡S¯BïXðâóJ¥°]ñÄC©"MUç Éd«×¾~Þ‚Ï_ø¦ÀßaØC“¯mb“„pXôê“?å‰àäƒ|y\x‹tfI+‘Ôž=Ê#„  €üpö’3²×Ó½Îþ÷ÍnýlëÇ€†Ãþ÷ê°3JÕ vöÍÃgÿ¼õRßüA“WªxÔJ¤E ;€ÎÑ€tÖrˆ%òÃ9¾ â´ñœw—¢™eåáõô/Ah‚ˆ%@­aþ%7kLJy5'=<~¿ô/~NA’Ä…j»¾ahŽ*î?mØ[¤i"#¹½p<Ÿ‰ï'ø'pð‰ŒŒ -¾m‹>–¾q¦R5N¯ýK ŽaÕ~ªé8#¨8?çç燆†²&t–‘òó󃃃=‚þ%ú+@iØ e~î_JŒO<} ??Ÿ5¡'ùùù²$Æ'zò"ô/ÁR!ªw®=ݧäÓ5øÊšË^_° ÝY¢)9îòóøƒ«FÜ—µ¯¸¸˜•¡6›-)!)::Úétºý"ô/Õî† g«é™o ÌW˜ÉN§3&&&11‘)\õDQ»Ýîp8<|ú—^HÅô”„‡ÃóBúðƒïh.½3~êZ°ÿ„.dddhqÂÄšƒ:¹:—BÀùkªXä€þ%îáÔ>Œ@’ýù8ÃtRäÁs °€øD~΋þ%á²¾itþ%òT!ýK€ÎpƒcB^äÃ`,Œp€[}Fùðú—àeµwÚ›á{µý ÔÄÂ"€š#„ ©\ff¦ë‡””–¼œ¬ ?@ë¸Üi4è=¨\‘³4È*ð™46-À§è_‚JÑ¿Ú@ZÜÅüK€÷#„  €ßÑzä¨Ã UŒHÕü®0ÿàEô/iíÈГjA¤*,*@ßMx×OCÕBпFq:£(”ª€ ãP)†È@uÑ¿À@àοDÿT!ýK8·t!ÒX ¨Þ¦µÄÕÛ¦å (Æ R ;€ŠÒ ¨Ì¿xýK~¨*“¤j])HIáG\Ê ?§EAÿ’V¸ÝM /æ^ÿ£$vßc6^€ümaØðEê£NÃI •¯V øâúiEQ²s²í¯Îñ¸¢(®SÝ’$Uœó®xäl<Û_W£FZ4k±ñïÕ)‘d0NxBÄDÇ´lÞræ ç]8 ;ÐŒ4Nºë‘$qbÞ;‹g—‘‘¡Å!_ä‡Â¢Âè¨è’’’ÌÌÌ®]»ê{;øý÷ßcbb¢£¢«Ù¬UXX-˲q¾*ÑQÑÕiÖ¢ɧÒuúoé{}q®š¯àÇH •pE–Fóƒëä½RNß ´â3VsÈB2d.¯æ§¦ jV$€>òCåªZ1@þs#&)FŠÅÕ ~v°ïþú­å c®ícfMJUª˜N—+Ë" ¡ü IQ¹œ¾¨ë3*Š" ©ú ÇPù¡š ÇoýKEÿ¼=yæÐá×v‹aÿ µ#wzí<™Šðûwê\ƒ4yŒù—αI®ªÚét$?Ȳ\£þ%C]ÿ`2™TÝ¿ÜnÜÀ♯}Ùõξ-âC,eïÔ` àn)êE€Ïj9ŸýK®!Å}öœµéÏÞ=vÐÀÁ}ŽºöÁ׿ݖïÉËÕ§Ó鋿¥£óû–³Å_0ü‰öÛµ²ÍUóóúíþq…ç.ÚüÛËWuiTî²oO°»£¬•*¯¿dFF†—„O®„Ûµu9ùتé·=ýKXïëî×*VÎ^÷ݜ银™>íº&6U-P×gTEHÕ]8îDEQL¶>3¾{¦“5wײ7ïŸ22-nÓOw¤Xµ²3ªFDòWÿRè¥oþ¾õÅÓ„L!uÂØqødü¡¢Åß}Å;æÎZn¿ôÑWîÕ³]ËÖz}xÚýíöú沃ÇÁoîxó§»JEQ”ÂÏ¥]ùØšE‘‹÷,™qïuí7jüéÿäÊJIæ‡× ŸôýaW‰Ÿ¿âÁ+R_ßR¬(Šóøº¹OßpåÐ~ƒ/Oûß›K³J<ˆ¨éõ5#„¬ÑMÛvèØ¥ç¨Þ˜ÚCY»`C®¢(Šóøš×¯¿°NÍfKêzÃÛåÊŠ\ðïG.®b³Ùbu=óŸÇ%Fûþ¸k`ç¿7º†·º¥$wéèøØÁ/M»¦M¤Í–pѤowmwg÷D›-ºUÚ¬Í…Š¢(JÉî¯Ü"Êf³E4ôè÷YöÚ\8þé_2…ÔiœXüûÝ4rЕ/íu¬ûßì‰É!t/8‡4!Ré‡ÞqÁ|—$ÉuåƒÃáÝR¼oåo‡£zoVq¶}Ñå¶.]Ì)ËåÝQ®)!Yv_3ý‘÷v·½íõ÷ß{ûÞ‹öìkkr²¢ˆ“WrË®§Ê¥ÿ}9eÊ’ “ßúôçÆÆ¯zé¹oö”º÷NË>£Óé¬þõîýCBT|§½Ô!ÃM²\ºç“±Ã^ÌM›½nëæ•¯\¸æ´§ÿ<¶mÖµ·/lðè÷®ýå³§†„î>T"+Šrr™ÉePáÌ_6õ»„;ÞÿøùÞ{g¥ví=5wÔŒÏÞ°ðá¾?ä”sW?>äæ%ÍžXôÏÖ óǛ߻îÆÏ÷ÙÝxóÕY8~ë_%›_ìßcÒ¯au±fešƒ‹˜ÐïÎ¥ÇÙgꛯ ð¤€‹=ø¬Ê§y þÊ¢|JS·¯°ÝsLÄ5‹³V~0 ¾q¬ÈÙ‘SªEœ~ çáÕó~ ¿ê£»6Œ‹mÐuäõk—ï*V¡œ|jÙ[+ÞñÕ·‡.¼ýÖÁ-c›÷¿æò使¬=âðpüÁWkÐqbëWÏ<·&᪉]ÃDéŽO^^ÝvêwõiV·AûÔGé|蛯·ìÙ¸O®ßkàE­Ztê;æ±·§õ<{'Ž:d΂o¾bä-÷ŒJ47zlþ»÷¤{Ïø6Žmk³JŽÿúÒûo={U§†u›ö?ùÚˆ?>ýãD-}TWç’‚DáÚ³òÿ·léû¥5 ’,IÃf|ólâ¢÷×°»P…Û˜éß©blÀ½‰†Ê~I–eYªô "{¡Ý!+B”Šw=*¹$ksVÑŒ¿êcWHp–Úå6yWÐ(›EV–¡(ŠãÄÛŽäî˜2n¥Ùu-‚½Ôw8ß!Ǻóm¯8©_ýñwò†"gþƒ¾Bk›[Þ_ü\Ï¡äf®Ü™÷ó--ã'º>JiQqX½’¦iƒb®¼³C‡Œ¡ûyÍÈ …R‘êÊOS”5I«¤(Š‚"‚Ìf‹IQE Š29JìÅûÖl>¾û»>uß1¹jQqiÛì<§e®á"RõüKŽãûJê ®wòºktãx±´Hf/1 ¾º\ÅÜËæÈºQ"s[NIŠ‹²w!16IV„PœåùABŠ";Kˉ3ïk\±ÕYCs¶–?U¸ž*Yv:sìðÇŸº²^ùÕÇ’98Ììöœª5½Í…;·Åea²õž±ðé&ž}ߺãf›E‘eÅépšÂ¯øð—i]*>¶98:6üÂ?òÙ?,]òÊÍoLÿnîÚ×#”Ê‹¬"€U¼!+RyԒʆì°Ë–VS9&©<0H±R ß}5Ÿî·ûÇ5퓸fê¬?{Ot}îÂ>zemÂð¦A†Ù+¤‹*î>u®©š>Ÿ[È ¿nÞ¬ÔFIÍ5 šÞ!edhqÂw㮳Îî5öX»vþjù£ïêîÚó;¬úzmQH× â,¥8¯È©(!d»]ŠbŽk'ÿµõX`¯:åeŸ"+NK E”ä;EŠ³Ô©¡H¡uëÛš%ÂÛD–•ÅŠ¢¸ùNËÆ|0þ $KT£Vmztzç³ÌÞCnsAÊÂ{Z6蘔ÿÍßE‘CžüزC‰j;|bÛá'g}Ô¯õßl)¹1ØêÌ?VäTÂLB¶;\]]'‡%ÄÉö®“^–¤ö-oý±7àîö±eoгæKªšã~›ÉÚä–7nÿ¼w×è—b¤c…·Oúo_£—ݬ™¹­ ¡Ò?MŽû€Ê«sþÉåS ¹D@£+oºø—_|Hsmß–1ò¡ ?~úÉ_¥uFka¢Aó¨ãË?[ØjPâ±µßÎ[Y(wQL1Ý®¸pîô©¯DÑ9I:ºýÏåXG<4¼iJà¼ïÒ×éºõ‚¹[œ¡õ…˜rùÀÄI¼ð–¸¦_ÓТýÿ¼ô¿Î“îî.¹µ«‘j:ÿ’gK×yñ“éÓ6]òÀ•÷·øåÕ¾ãî¸ðÍFO™:¾g’ý¿Õ >^Ñü‰qk™ßâæë´Ž<¾bù>Kã«ë…5èÑÒ4ç•©‡õ1oølÚ´íM«ó^£{Ý{uÄÐñ×NÎddë £›–ÌMϾþÓ—„º±ITçiþé_RD÷§Wí½âÛ/­ß[Ô®êåÝ’=yEDZ]ºzkN±œØªGßMÂϼðÈ~lÇšUk·ì=Z,‡]0rô% vOÜÉ¥ ƒü $Q>1’p÷Âb)²ë„W©óáç _zâc×Ò¢ºÞòȨ†VE FÜ5b׌yÓŸ ¬ßsÄÈNû¾B‘".¼çéÛf¿ÿå«g”ˆ€Øf]_mªÄÏ¿3ûùáÍûòñB¡(ŠµÑ•O¶|0û³©?æÉ"´N‡K.¯osó­VŒ?Tÿþn?(׌ Ðä¦?üë’Ñã®kõóüñs¿u>üðŒq+¡zŒ˜0&)%¼eÖ ·_69OX/¼þ÷ÇÖ·Úâ_yéÿÆ>zט9uzÞzÇÍžXTñ&\?T}(»JB"ìâ翟vßS ›™+,ñm.ûP£wî—W…ã·þ%!„ÖøN#'vé•×r^óýò]‘ ™äÜ»zéÒÅáq#ÛEœ’ 䛾Ÿÿ‡£I—K‡Ä §%œÉbÕ”ãtªåÝ"šÁÔ•„pÍß*ܽþA!„)ºÝ÷¶»B(ù¿=uÛá÷ͼ§}hYë}H›Ñ¿?ºü‰Ã‡ !dY ¨ÛóÖÉ=o=¥LQ]o˜Öõ†ò?_6Êõž¤ˆv#&MqÊSÝ~§®ù[ÝX851컜a'ߥ3àµÍG_sý]ç[ßü¿[߬üìvÏuåó§¾Ã€”kßþíÚ·ËxzüSBÑdöîÝe/jmþÐÚƒ•ý!jÄ÷G”ý^ý¡SÒ‡NñdIUsáø­Ia?øó¬§_ÉXñÏžÜÀºßòØc×uˆp·¤wÛº=/¦Ó°¶õÃM"öâ6[36gæ¶éYéõŠw¯Z“×bÈU=i’R—4!Ò}{N7U©º;_Q¸(ô‡ù—Îç¤So\à¡ÀÆ]–Ì[øýŸÖæòá¢ݺ&¨¨èªÑ-„Û×?h™Úç_RòVÞ×£ïœøëî¹åÑÆa¥û×Οܻßþ_~y´­{WPÛ*°FÅ™„–!ýuà„CDžœ  ôЖ}N[ƒõ_}¸çH‰%²Aûž½;&Û(Ô:¨=?ÉétzÖ¿T™)®÷„Û2g¼ÿò3ÅRÜ€É]ºÄ«¨SÜœNgî?m¨m®š‡ø­©`Ͷ'~ýé‰ \S¸Ž»ùº®—÷uÍ=ï_âN¢´Ús ¥ìKV›EÎ-v(" |È9ÇíN“¥N×Ë:Û³Ö/_õÝÒð1ƒ›† Ð5æ_:g UéÎqÞx× ½n{¾×m'ëQµÕÇ5f1àøCužæ¿þ%ÙiMl–pò‚isT‹!ßzrÿÉtrÀE–Oÿü²£Ø!E\Ðá‚F‘&!âzvßýѲ-Ù%MÛNyÚš5k*~îܹ3û\@ŸðuùÆ©@•ù!((¨°¨00 °^½zëׯ¯i­YQnV9‚Qùo«|fE·LEÏŒ$I•>Yäñs•O;·úõë ! ‹ ƒ‚ªÕ휗—lœm®úŸ×çýK΢ü"§ÒjüÈÃî|«áô.L²Éù»–¼ôÐÒv÷=âÞkš¬ÁVá(u(åa¡Äi´Y*mP&s Y±Ù]Ï,!¡ò±b‡"N¥!3pS±šâ*º…ùs ’“’÷g팈ŒÕý2-.)>~âxä:ÕyrRbRÖÁ¬äÄä#lpY³’“ÎûL?ô/X88òŠåe˜xѼ‰•ržuÕK‡F¸óªÖÈ„Ò}‡‹äd«I8ó³s•°á•¿x¦ÐøhËŸûÈqá&¡”æž( ˆÔãNÓ77Ï¢N@ëù!*2J–å”””a™&%&EEFUg¢!×ÂÙ·_qI±Ž-Ж˜˜X…ã‡þ¥°þŸíÊ,¬²OÉœæîw,ºY³ð¿Ö®ÜÕ)ѱ{õ¦¼èN)&áÌYõÙBúŒ»¢ypR»¡ ~_¶&ô¢欵«…µº8™˜j5]0#'€Ê\SŸeH"•é;áÛMù—ÎÆétÆÅÆ%%&™L†˜Õ^–e‡Ãa·ÛY8._÷/™‚6BQzxÛ†ÍûòJåò‰%6¼nb°{«ÈÛq`Ï‚¥«~Y"%´ê;°m¤I§P”ò›[“º ííXºjÑüµRpâý‡u'>ãæ!„Ýn¯fÉh@,œ*ùmþ%ùز»ºö}c‡0[¬¦ò&¢ðA_ïüzP¸›/inÕ'µUŸSSE\÷±ºW<#ªEï+[ôfµ£*tdAÛ0ƒ~@U˜ ð&¿Í¿TðÇë â§ÿó×Ý­‚¹nK'Å1õ7¿ãš`舉E5GáûûÇ™‚¢“Zu¬¯ßðÀ¡ ¨U§5ÐW'¾qÙ1šÂøTÊoýK!]îN}îŽûßy즋’ƒÍe‡ShýfõC‰Û]Æ'… ÇA~€öù­I)=q8kÅ[ã¼UùÑ^ Žÿty«µ¦ ÒèüKœP…ª#„ð}ÿRþŸ3çZûio^‰½’%à à ŠÚF#ÎQ€sˆŽŽöSíe ŒïÔ¿KÝÐK%f*2|—ô™222Ȁ׸:—ü$B:ïµññg¾\¿çȉ¼ürENV PÛ8ûZÀõPu„¾ï_Ê[ùâìß—»²ãÔÊrýü*ƒ0°I ?çä·ù—B{¾½.sº|ꃦàÄ0Ö‰7p‚{¤3UîÒñË%L\7òtÀoó/Ù³~þbÞŽ’S lrÕW5 dµ$ÌsV`\Þ  &4:ÿùªŽÂ÷ýKŽœµK~\U „BqÞ¾vkIÇ1“‡Ê¬Ô²4!(>ÝçL#à6ÛzG~€JùïþqMûñ×J.ÝõÁˆA?4LdðÁ€Õ<¸W=Õ“‘‘¡Å!æ_‚Jùmþ¥Ó4:&î—Öç³N /µ6kâ @x4‚ñ¨:Bß÷/ ¹´¸´¢YI)=ú×'l²4໢Qô9°4äßú—N|{YäËOy(ñŠwßíÌ:¡&àì 3‰ù*å·ù“6dµ IDAT—Âú}²}kAù„d ŒHªb¦þàÏ/)n€1ÿàý!|ß¿d ©Ó¤ €ü-ñCÿRñæw¦¼³¹¸ª¿²µºuÊ­­l¬œ…Ûc8’Äøª»1èŽFç_"?@¥üп$ìß±m[á)9oZ¼jŸ° øùÈ?ÈPw„¾ì_ îòäßWú³#û§Æ]õƒ¹Å¸wÒ_»,œBý¡,"ÀhÜ»S·xƒ¸ÿTÊÏ·}(Ùó탽šöyöðÕé[×}xc›0ö²ä¨—ÿî§nýlBצÃßµÞýÃöU¯¦¦‘€÷itþ%òT!„ç_’s׿}m»Vc¾iøìŠíKžXÇÊj .9ŽwÑ"¥S\ÿ•òÃüK¥Ûf]Õkâ‚ì×½öîÍÄ–Õ++þÊÕªK«(3«µ€+À¨˜ ð&?Ì¿T´å‹o!¶Ì¹sÈœSÿÊÜçë#K‡G°Zü-‚€jp ²†æÎÐä!|Û¿1|©ƒ À¹pýTÊÏó/AÐö ÈÐÿÍ¿TÅ‹MS4<„š‰þ%¨:BÏ¿¤b™™™®RRRXð4 1²¤Îl©Nl-NÅøTŠa‡Ó¤”cQâ*j*6–'ÈÈÈÐâÛfü*å‡ù—N*=¼mÃæ}y¥rùåÔ–Øö·åë ‚ªŽÂ÷ýKò±ewuíûÆa¶XMå}êჾÞùõ pÖ‰±I§±qþD0Ü@çè_‚Jù­©à×ÄOÿ§@vØK+&<À`¨€„¢pkˆšbü*å·þ%SPtR«ŽõƒU5ENll¬«EÒ5QCåvÉš>âÆD©l‘>Á¢´þwÇýïõÊŽ b#ÃÃÊ ^x‚uЦ+U ­\0}ú— ê!|ß¿Úóíu™ÓåÓ‚vpb˜×D†$Òi8;z½FÂøTÊoýK¦àĺ!ÿ}1yì€níÛ´ïvÙu“¿ÜZ71˜ï U2K€ú1¶ )Ì¿x“ßú—Dц§úž¶§Ã„?øäýiãÛí™6hÀs‹Y%FæùØ Gtì= ô/AÕBø¾©pÝ9ï[òã3탄B¤ŽîÝaøëëî{§{0ëãP)¿õ/9Žï+mеaPŶ]”î;æÐË’åT°/Èпõ/ÙR.‰[ûêÜ­å KÅ[?›¹&¶Gc뢔71ÿàý!|ß¿Ðì¶écæôoQgVÏnB ÿû}ùß1w,º­y+µyôåÆF~ÁµìPsŒ?@¥üÖ¿$LÑ}g¬Ë\úÒ5Ýš6hÒuô Kv¬ŸÙ?†¯ À§…Sûº§Ñù—€J¹ú—*þëÛ\ nÔ熇ú°Ê`œäh!B_ö/•ì˜÷Ú¼%UýU`“«î¼ªI ëðª4:ˆÀÖ/ñðƒ#gí’WçñÍ«þ>šÐ¾O×Fa%ûÖ-ûsoòÀ'Nd@¼U¹WZQЮ:ó MÝP)?Ì¿rÑ´ýõ§¹·Ô‰ùñ{Ö/ýfþ‚Eìü/}t`q\´.¯ŸNcCÛŒo“›û À{4:ÿùªŽÂ÷ÛôÅÆ”†5(Ï –:Æ4ÞòíæBVˆÁpÙ"¡@~€†ømþ¥€Ä–Aëg¿×^öçâmóÞXÑ©¾ž.~ 2æ\2àL>ojÒèüK䨔ÿî×îÓ.ûcLúm{|Ù%­’›OÜ{íë÷´æâiÐ µpý4T!„ïû—„9yÔì-;oœ?é_û‹ƒûß0-íŠnulƽ̊É[ý…Aèu“¦v4Žñ¨”ÿî'”¢=«ú}ûQ‡%ÐT’½áëצ<¸lšÚ\5´ÅòåRÆ R~»œ|hÁµíG.Kº¸Kƒ0Kù©÷àÀ<™uÐ~M!®&‰üUGáûþ¥‚õ¯íöéæïÆ$ymtÎqìß_—®ÞšS,'¶êÑ·G“ðª_Z.ÈüéËÅÛBûŒÑ<˜®!xCîØÀíÕ@ÿTÊoýKR`xtºáÞûj8¯ù~ù® ƒGŽÐÚ´uéâM'ªËPŠ÷­^øó^'«ÞÓr_d@#˜ ð&¿Í¿Òi|ÿÍ“Ÿžÿ×¾£¹yùåŠÜ®ëǶnÏ‹étIÛú :\Ü&ôðæÌÜ3„=gýKö5è×'%˜/%e äÀ½!|ß¿”û˳o­Zþ¨öõb"ÂÃÊ ^xÂÍ׳?T`Š 2 !„9,1BÊ;pÂqÊSœÇ7/þþßÈžC»&Ûh[‚Çhn oÇJ?ãú¨”¯/›®Öû½¿2 O 0'†¹÷r²½Ð®˜˯ˬ6‹œ[ìPD@Ù#Jñž_¿ûÓÔõŠK›ùg}¡5kÖTüܹsg¶ÔJð`º^ùå·ù—LÁ ±Žæ.Xw°È©!gI^Ö.óõïN»8ÔÍ—”L'O•Èòi'‡åƒûs s—Ï}gyùCË>š{ôÊÑÅ™+?Oÿ™!U×ïj—_@M}Ì¿x=Bß÷/ɇ¾¼¦sêÏÉã÷¬?ÞìÂäÃþu¤ý„w’ÜŒ#Ö`«p”:ÊBƒì(qšm–“'yÍm‡nZvy…ãÈš…ÿw´Ýå[F›Ùàión6`(ŠB›ÎZܰ N~›©`ã¼Õ^üsÓŠolÑæ¾¯ÖíÚñÁ§£N¬ÕÍ׳F&„”9\$ !„3?;W K ¯ÜͶð¨ á6“ÉD|P# þ@ÇIF¿—u1ÿàM~›I))0×m•` ¬×1zϯ»Š­ GMHùuöú7_ÏݬYø‘µ+7í=”½kÝŠMyÑ-R"L™³ê“Y³¾ÚZÈ¥®=È€—"„ð}ÿR`ýŽ‘›¿ZuDD4oa_ýóÞRgÞÁCÇŽ¹}ÿislÇ=ä¯]øåüEÿ8›õØ6ÒT¶SìÔt$&@ûèØªë R~›)°åøG:vziƒ ¿Ü0ìø%š¾°ç@ÇÛ„¸ÿ’ÖèV}R[õ95UÄu;¡û_ÈøKÆgåà[\ïQŒ?@¥üÖ¿$,õ®ÍعcþM)Ñ=^Z¹ìõ;nœ<ÍWãêrA‚V0 ûU“¦‹- ꬡ òªó/ÞÂ÷ýKBa mÐ訦zh¨¶a@ôsÿ8slÇ= –®^øe‰”ЪïÀ¶‘&!œBQ„P„Š#ïp¾Rœ¿jážò_±¦ 7 ®•­ð,‰Ñ@Ý4:ÿùªŽB÷ÂݪOj«>§¦Š¸îc'twýÜ|䄿¬nøéإ٫ j5(\R{ÛKÐ<ú— Rzš €z¥ñq:'ýF£ó/‘ Rº¹TQ©ê ¯\„͕ܺ¯¶Ý(æØ$(ÜŸ  ªŽBýK€/¤2É D&vVä 6hþ²irãܧ·†˜Á V3F‹š½´Ã’ÓÙd=¨Úë€7п•¢ +kÜ>¢s8j£V®o–Ö¿°ìmTxô`þ%ÀëBпD9¡ÊÓ®i¢ß}üú®ì…P]ûŠ/;jèÞö1þ•bØÐ0_^ÞíÅ´™ªh)»¦ÖæÖdxœ;ÂK˜ ð&ú—t…Zà›T!ä-£ ªŽ‚þ%-Õ1ŒÈë÷H­KÓÂÛãÂhö ÐÆ R ;š,Y>E¼¦—* –üí¢ è›Fç_"?@ÕBпT‰3dî©ðå•Úº,›[› ?@£v€>‹~ê~@ýüž÷ØWó/ÞDÿ`è P+æ_‚ª#„  8G‘­ï»Pq›-õKóí?¤ÔÚ‹“Xòt ::šäø§VSIx¨ýKP)ú—â~gôÊ­«V4:ÿãPu„ô/Ï2$uî©t%•¯–ÜÛrøhð7Æ R ;Ôz Ç ž€o°·΂ù—o¢ Fxþúwi1¼ƒIÂÐú— ê!è_‚.‹‰ê4®Ôê„3@5Ãjjy \5_çÌ'§©ø“¦ùö‹€ZÅüKÐpé¯õBÄu‰B:唋Ú*kt®»M¨ýKP)ú—¨¢²÷±4o¿“êç´Óº×hf|°›ÑæüKä¨:Bú—Ôæ´ZDsåõÜÞ`|6`Bá^垇+€üœÃz–¦£ùX(õ@¥ À]Ì¿xýK:¯r(°À¦UÓ¤ ¨vk'6 ùªŽ‚þ%ÝÔ.iÌOeãsigyÐgµ8¨¶Ù§‘ŸaØôÃ[g”óÍ6ëI8L«ö{ ”„±1+TÊÕ¿Tñ_H­;Ç$’)njôdEÑÿ²â*ü’2& ³¯µ6ç_"?@ÕBпT.33ÓõCJJ Kƒ2«ÆÉ°¦|6e»Ï‚kª"„¤Še‹êl~|…õ¢ *EÿÒiRÊi¦ÕDÂ3òj2BtôצÈW¨æ_¼‰ù—¨ç|ý¡¨SÕïÜ=ôúX‰*ünçjþ¢k¤‹/  ô/AÕBпäÇBM‘΂@UÛ†R ½@*§’ê ½y€S:ùð .›Ös-¥}wu‚ήJ¯\¼ª¡CÝÍ9tç00ú— Rô/Au¥3§¬Î¦¦wT`a€‚ù—€Úˆ‚þ%ÃíJ•³VœµtÆ7±lãEAÞ-ÀfÌgôãP)†Ø:”vÎátdñ½ªÿe©ýï ó/ÞDÿ’¤*Þ¹G,GtŠþ%¨:Bú—PÓâ¬eß$mµ%mÍËä÷‰¤\[“Yé`oÀJôÆ R ;§&IGäI€ÝÈÀÙпDŶ  ºüx³m°'ñdËeþ%ÀëBпäû±Û»fUíÓ 2¨­›Œ²Ê>¶Àn6ô®þÀøTŠaè¼êb€ÞíåzÙ‡0ÿàMô/AÛljZê¦ÐVM¹oï+šo Ù`è_‚ª#„  ðMÚr­õÛ0¡ÎÜ€ÚÁøTŠa …B•×±xX@pÊ ¾ßn·Ñ¿Š  v1îT±“üõ-ÔæüKä¨:Bú—@p ÙÃb— ùFÀ°ƒ*0¥:tQk4:ÿ×OC¥\ýKÿeÀSi®Ã°»o 6ŠB¨ gLøü­GAÿcŽë  ¥J:ËGMè_‚JÑ¿¤Ã=,!Ö£Ïl„ìÀæo`ü*EÿUÐâH‚'¥’JÊ,×çÊðáí,…»g¨h0Άù—¯GAÿtp4¥WÄ¿e(ÓÕ(òŒ€þ%•Öajxà̲Ê€1ÿàMô/i)Wèþ_€êäX°¢üUGAÿ¡€óbØ”eÚPSqœ®SÕêÐÇW’o+@~Nc¨þ%¥${ãòŸÖì©i׋›š÷ý½—m×9´S÷Ôè#ûk™°­ò-ó7οD~€J¹:—Œ$ä¢ÃGíAq‘’BXÂÔóœlûY¡F¢þ©™|@9®€ª#„0@ÿ’RZhW,V×’5È*J JeÖ-Ô¦>ø£•¼¼1øþ‹ €üTÉXó/IRÅP "W}85kÖiL˜0í„R›ÖH›ÄQò „‘æ_’‚­ÂQâtíõG‰C ˆ 8£µ´€Þ7ó/^ÂýK¦àØhkaαR!„ŽÜƒyRxB˜™õÈ@µhþ%k|ëÆûV¯úwÎí¬ØîHn]/˜û? wÌ¿x“qæ_" ¹û ‹â²W~óÅ‚e;míômJ|êÄõPu„†¸œÚ_vu{V9P=Æ Rê_ ?2Rÿ0"æ_¼!Äÿ³w×qQlmÀÏl²»tHJ‰¨ˆ vb·"öÕkw·¢ØÝuíûÚ`__» T,ºk—­™÷DE%u•ß÷ã‡ë—Ù™çœ=3ÏœgfËGýò€Á´üÙðü%€Ò„ú%äEK!ê—?ü¦?ê—àφç/”~ AP¿T6mÚ„ à‹üþ(˜v€?Ïýû÷D‘DòàùK¥ õKP ×¯_#ˆ<"ˆ<"È N!ê—?ü¦Ô!õ¤ª_Êû‰€”î-Y¸…úW¹wï‚€È#ò€ÈãxýÃ×6 ù@qR‚ú%µD \Aý¨)Ô/ (,< @ ¡~ Ô:… ¨_*EŒäã½Kמ|HWpumÜš4v1æSˆJi’Å?¹~3äC|ªDÉѶ¨^·Ik‹F÷ôê•ûoRdl-‹ ›Õ©( ÐeÓdz?Þ:~ê ©ãëã¦ËBä ZýìÖ§ob3ål³Æ=Û;hRD‘zýÒí— Ù”ÐÄ©nÓºvÚßü @1ûº4þùõk÷_%f3m‹j^Ým4Y‘/Óž÷èÌÉ—–]|êè³s¡ð# ñúæ¥/b²h¾¡½GÓNúÜß:˜5…i‡R?Úˆ__>ÿ0Ó²a‡.íëš$Þ9w#JЍ”îÑEš#Ö©âéݾ] W½ÄGA—"²BdQ7ÏÝŒ7ôh×¥ccÉã Ë¯Ä š£,È»ðR–w\CäÆYìÇÿ;óLj^»y‡Ží[Õ6åS„()¨ÙºsçUY//?K£¿ÑPLÒÿ¾™h֨ǀ}ÛU¥_½H§ù2£L¸u`Ë®cwùBW„†N{|1”vhÞ¹KÛZZﯻŸ ø½#‚üÔê—Jû0Ÿõâ#«R=w{³ 掞^ò7ÏãpÞTºã©–SËv]«X™[TªYÏÍ€Nøª ²ø72sºNL+שW™ýñù1æ(õã{jHðÙPÝÞÕtrlˆüOˆzʳÿ½ÒªßÑ»Žƒ•™©yE3.!Š”—nõ-mjÖ«®™ò:.°9p[ÜÀgDÅJµ+;Yéòy"Ggc’“®DäË Ûȳç°!½êä;m.ÂCg¼ MV­ïfk\¡bµºµ2ÂÃ’~ïù¨u AP¿Tz‡›¸4FÓX[U³ÈÕ5)’ãÅ4SVùšBª GUõJq58D&–ѸˆP6¤1OCÒ´«Øë³™d±œáð¸¹p‰,K¦Ds”"yÜÝÓ×3ª¶mã¨Í¦S Lp6 IDATóuyD¾Œ)Òã2¥––CÃöž¬Ôˆ[W/æéu«*–3ìÜŠ«Á¡Ó³åÒ‚š‰[qiX7jãzüøµÃoîèÐJ˦*‹˜ DþçT =ÂÈ¥9+÷¥„¥z­œ!ä÷½ù¨)|\ (•3Z14ÁüuY¡³^]»ø’_³£«!›(UÏ;äÐ ƒæ(åø4éc²$áöám·sÝ=¸;¥mK!"_Ö]]&£9¦5\«Täb¬ß0ñ͑ׯÓäS„ ¡ó"_ps@±’æ„'ÿ åÔîÐÃ"3âñ½‡!7ïY×Ó#ˆüO?¨v„É·ðhä Ö)AýR© s‹£g$ÂPÚÉCÔSç_6êØ¤’¦*¸,¡¡>Wœ"#„¢HÍ ´µxhŽÒ<ñµtõré9,¶@GWÄ!òe£m¢Í$¿KTÅX™•”Eiê‹4tE²¤D M!Ê̸tFËD›Wào¤QJ¥Ÿî` ¸Z:D)SrùŸ9îalçjå»í„ÎNJ”jêó‘?”><©´/•h˜W5§_߸“zëÖŽuU>âRš”I÷Ož~Îq®W]7;)!!!!!1MÊp+Tµå}¼}+4*!&âîÿ"fU+ Yh޲‡È—ý)„¶} 3EøÕë/>ÆG¿¼u-LQÑÅN‹§oo¯ôàÆ³ñq‘ÿ÷,Cß¡’«ÀæÀ·s<˜Ù)ÞÞ¼ó*I,Ɉyq74S·²‘/ÔM&•Ê4Ã(e2©LÉi„aiÙ8‰_üïad\ü‡g7î%Š*;èÿÞ@l£F}J¸Š†;;;ô-(u‰$ïçw¼Ž|]Í©Úw^ðüùóêÕ«ã€ÃÕ«hDx|ûîãÐ(…Yíæªèrp )MŠÄ'·Ã“3b^…†äJ4ªî`¤gfÊMx~çö÷Yú5š4s®À£Ðeu”ÏŽ}ñ"Ñ Z53 Š­…È—õ°Â7´2&±/îÝ{úQfZÇ»‰“>‡b ÍEi/ïÝzðüuŠÐ¡a‹Úæ,R`s „Ž TÁªûâîÍ;ž¾NשָEm3 D¾ìF÷„›?ŽÊ&²Ø°Ç^J­ªY‰¸…a(¾QE=ñ›·î= gÛxy×µÕ,Û9 ¡/*ÙTúæ‰Óë×ÿ% KÔý‚J¸‰³ì“¼½½Ñ¹ tþ¶éàËÁ>]|¾ó‚€€___„þ‹%ß½ ;ðh`‹&-¾õ¯AAAþá%ywÔ/šBý€BþjB< ùÀaÚù@a¡~ ù@ÑR‚ú%ä?„iä……ú%äEK!ê—?ü¦?ê—?-… ¨_@þðC˜v@þPX¨_€ß#þð2^®+@þå#… ¨_€ß‹â݆š\›iO² ‘¿ZÓ¨Æÿ2J´¾|+ɸØUÏäïY2üR„Ô“¾¾>2ø­Ñ’Ä$ ]z+8Oܽë ÈÀ/…ùPS¨_€ß[êßf Þd_÷­\{îÓl"<:±™­ˆ¢(Š Ç¾Î&„ŽÞQGTuhßBJ£ÆŒ‡Yé7þåYQ“MQÇÈ­ÿö0Éç+Iyº¼ßߛ² ‘½;>ÙÛN“¢(Ž‘k¯Õ·ShB踽žšN#Çwp¶63ÔÒµk3ÿz2fäPÎR‚ú%øMé¶9xq†­Fýƒ÷æ8Ó÷g·ð 4™s7U–ºÁåÂ_-gÝBˆ8ä5óIløñ v!³;MxÔ"ð½”Q¦=˜) 3érêg+Éx<ôóö9`0ýf²TòîPÇ÷³ZôØûAA!Y¡ÇRÿ:ðñL‡¿¡[#pÛ €rÓðç?Ú²ë}ËúTÕáŠlÚû-j»wË#1!„cÖ}DÛÊÆ–¶úšÕ¦]~4ÍSW™•.4Ó’&$Xý$y¾{Ï;ÏÅ+û;ëñ4ÌOY?Òðêús1JB×¢Ço ¡tj´®­'Cìù”¨_€?‡,ñu¢ìF?36EQEéw<—™ö6^JáèšëªnE¤q——v±×äjÛÖëåw4<‹a .?R$E¦iÚZiåÁ¹Æ¨ä÷) B[d b«³¹,†fÄ?@ùJ!ê—à7Fåü—«WQ_«Ý©dFE™ñ.<4 µ®ê5ªÉBVvz¶ÚÆ0qvRäÃ3 °aò¯$GßJ;óÍûÌœìBËè[êá(€üÊ7L;ÀoŸ=pfc‚×`oS6È \Cýüîx–­zÖŽšåé<ü”ç’ ;[†NrÑæpõ›l`:zxÍg3—ÉFm¯¯Ã×2óšñ¶Ãü6z1ÞIò¯DœûR·¹çt‹™ë®ÇÓ°ò9aátd€¦àg¡ý‚J¸ŠYöIÞÞÞ%ü*Á—ƒ}ºø|ç¾¾¾ü˜ïÞÝx4°E“ßú×   ÿpƒ’¼;æ@MaÚ@ !5…ú%äEK!ž¿€üà‡0í€ü °P¿€ü h)Aýò€´ò€ÂBýò€¢¥õKÈ~ÓÈ õKÈŠ–BÔ/ ø!L;¨%:ýÞÒVžígÿ—D#¥FöúÀPÏC¼“#(&Ô/¨#IÈÖ'y}–Lk`À"„Fžú|{ÿ&·¾‘åe/öNëYßÃ˵Aça›ï$Ñ„EìåõcúvªïååêÕº§ß±01“wêscÇ”þí<<¼\›N½™õåÊcÿ[3ª«—‡—«Wkß9‡C2ó§-²w'&Ô÷h8ævÖw7šÉŽ<éׯu-¯Úm†,¾ýÍ“teÂŹí]=J¢ !LöÛSK‡¶¬çåêÑÀ{ÐÒÓï²B褳}=¼\?ýi6õ¾„É Ý?wX§– ]=¼¼:]û¿!Šº}öJ/WA§’d…‚W©Ç‚yuß­_|6Z©î]€ƒO¨s AP¿ Fè¸ Žf7_ÞÛA@!²°õ}zíûÀbá•w®žyõ¤U/ܧ¯›e—¼Øú›Ck½…ÑÃ8uzû´æE_Y¿tùXm‡Ç;ñ‰2þÂÜ> Þzü5råcŽ”²á‘=¼Û;aúqÓ±[O·ª˜ugݘ™£VW:9ÓUD!ÊøË‹Glˆ`õƒ­–Fl¸øŠÕˆ5Û]ä·7Μ3Ó¢ÊÖÞ¿: ¦Óî®ëGž»¾ìÇùýÏÍ÷↠ÁKFϯcpˆ!„¥ÙxòÂ>¶2õ¢ˆ|aØqbmmÕ)6ÏadàƒaÑúúä½FrôjVí)£:×ÖgëIwÎ;r'©yëšÖ¹RªßrÒx<äÑ‹D¥“¹ìÅ?kך¿^=Ý‚«b$‘7#Ù5F´¬f("†õ}Zšý{ýeŠÂUÄ¡Óîm±,¶Û²ñÇÍý~%•ô͹s1•¯íQלMìÇùžéìr´O?Knþ1âÐ=“§ß¯»h¶ØoÂKBQ&?}¯çÕ±I%] ¢Û²»×š‘߉;Bů`_ÝÅ)_®#j¿r]Õ_]Í“.tÙvë½´›Aåêy¡ X¢å½Ê][(,0lÂ6nÒßcãŒÀÇ£]¼´)õí¨_5…ú%uC§>½ôÞ i㊼ï¤É¯%úŽVš,Bá[8›ÒžFËEåž3ò¬ [KOÈ"Ù¯ƒ®¥h1ç&µ¬ïåÖ¨óu×㟯N`åi£¸µeÏ89C§„i9cÕ7íÜõ±õª¹&m x’ª ò¸g/åvõí5)BQ&hìêÑ Y¿¹aª‚ªOçûÊìôl"2±óoCæ£Û"ìôwQ‡‚B(­­ªÒ¯¼ÉVën€ùPë‚ ~ @mÈãBc¹•ÜÌyßË1²’³¾–†ê™%Ь¤¬|5ýЍ ÷Ùž³kêPtò»—‰ SÙgA/SE؉ÅK§M0<¸Ë7_iצïò™/úúêpÌÊN+S÷p₦M;÷Î ÕJdSÓ8ëjd&í"Pß«üÈ@M¡~ @Ý(²’²5Mõ¸?zÅfç^bg”4“ï_qèöéëß{L=ÐØ€E’´l–a½¾¾Íªñ q²šúàêˆswâ»W4Ë»v¯L¸²~K¨—ß¡>ZwîØ||ûòu–8=x”6µÝɼõŽõî9îÐ^3ö·¶ˆ•·E„V|¶I„(Ÿ=ŒJŒòïÚÐ?wÑœ6>ovïêøtͮԎkZÉ®nÙ´fýE×y­Ì4,¼[Bqt2ϸÙeû™WÙž5ªUÅ_Z:ýœv¿}«|J˜Œ‡;·GTàï¢I}+9Ø:fº,qb¦’!("UýRÞO@=ü 0Ÿ%Ò‘ì ©ê–:;]J‰¬„ªózÙûc³ÆïáýµunKS!„Å×ä3âqÎí |sm*,YL’›È"­»"èz°±×nJ-ã>]·l}~tÒ U yºdÈJåø-s›UøFòÀꋈ4w‹ˆR’!ãjëò?Ÿs,»¯9Ú\ªZŸìÕÎa³ßôÞ¸¨“iøêQÌÇήkmÀ²žíá"êÒ¦mÍòíÅÓ5³ÑÍù©D—ËT°4×g}H–0¹³”†‘•‘fÊhZ¡¤8lÕâì÷ßÓFmx„Ði÷7›r¥ÊôMsšæÏd˜ôû;v¼¶ýk¾k^‰SA¡È¡LMc„†šluî¸Ôn›P7\ccéë‡ù¾>–eeddJ” -ËÊÈÈ’Ñ„»6Ý[¿áă°ÇÁ[–g»uv7$É—ç™ÿ²Ö¸áu¹Qá¡aa¡á‘ RF£Jçî•âöÍ]wöqÈã+Ý´ìSG% [ßÅÕsÈé$šoÓ¤®îÛ]«ÝJMz}më¶'‚º5¿µÙ¯Noß°÷zìç7aóm[µ1{ýÏê÷B^\ݽò`¬m‡¦æ\òé] ^™Ð¾ÿÉÆµ'BâRÂÎüsà•a“vœèããûNÛtäê½;WÌ9’äÔ¥½ Wöjçà±G5|Æ÷°J{öòc&M‘8¾ái6¼cÞ=tÁ¡P%Y‘âDv6šj}ŠŽùPS¨_P7,]禖ñG¯~\Yuw°4toŸÃ‰„òvHó}>;ŽLqÒt³tÌ‚ù+GœÍâ×ö]0¿e–ò]ä«4:6xþðàÜ•Uèµ+p‚ƒm¿å Òæ­òzT¡U¹Ý”µSܵ)"#4Mš!„Òª=eÝTÎ’m#º¬—³ô«¶÷ϸ|· |™>¼>¼i{P­Õý¾¸|ϳ¸tJ¼ßÆÒYzU;ÏYÔÓŠKò½Ë7öÖÐ{îªÄe+ÖÿÝi>Í5®ÙaކᵴØUjV8u|Íô½Y´ÐºÞÀõÓ»[rHVbØ;…D¶sJ¿¹¿^ÃÿÊæ&äÞ¶¯mÿšïúéy¬ÊŒo„‚O˜Œ§çž±j̵ÕPën@9ú•p³ì“¼½½ñ‰‚_%ør°OŸï¼ À××(1:îäÈŽ›+®=<µ¶Hí¾¡@þv—ï ¯íø¿cp•±'÷øÇvÍ¡5ß%Ã0ßù×À£-š´øÖ¿ù‡”(ÄçÔê—Ô˸ùˆ®üóKö‡«ßWÐ)ÏnÄZwêZù·Lr}Ãæ0§Aª Ô½àsê ß ŽŽƒ¦·ÉÚ5iÑÉ´zmYVصXŸ––Üß1¬²ÈÃ3g\«8bj+S¶ºo+îµN!ž¿ ^XÚµ'Ÿ»9Yý6L«ÁòS ~רòl|6ÜðùMz> ž0í€ü °P¿€ü h)Aýò€´ò€ÂBýò€¢¥õKÈ~ÓÈ õKÈŠ–BÔ/ ø!L; (,Ô/¨!BêœBÒ¨_êÑ£Ã0ˆ'@ÉaþÔ¦?ê—?-… xþò€´ò€ÂBýò€¢¥õKÈ~ÓÈ õKÈŠ–BÔ/¨|ÿ4¨)}}ýÂgG1äP~©ê—ò~~ç•-š´@¸~Ô/Z§¤põK_ß#%X‚%X‚%X‚%X‚%È )Òàë›­±K°K°K°KÊí’2E9ú•p³ì“¼½½q¾ e‘B¦~©ÀßÂ,Á,Á,Á,)ÏK¾%((È?Üóðg*^ý’êõX‚%X‚%X‚%X‚%åy æ œN> ¥óðÇÂ÷Ǩ!ä Ö)Á÷Ç ø!L; (,Ô/ (Z AP¿€üà‡0í€ü °P¿€ü h)Aýò€´ò€ÂBýò€¢¥õKÈ~ÓÈ õKÈŠ–BÔ/ ø!L; (,Ô/¨!BêœB×/1 —#–ˆËCЄ¡©‰©±‘1EQ‚SFÁP®‘?äÐ××/•;ââ’’“œ«9蔇¸%%'……‡BL*˜ 8N@}`¸FþCU¿”÷³Øë‰‰q®æ¬£­#“ÉÊCÜt´uìž>Z˜aÁApŠõáùÀg))qý’X"Ö×Ó—J¥å$h4MëëérÁApŠõáù@ŽÒª_RU2 S®¢WÈšHÁ)^pÔmàÂpü Ôê—TcJ¹VŠ´³‚S¼à¨Õð…áù@N AJ\¿DŠaš¦ËÕ°B ÁApÊ.8êÃõχï5UZ_û@Q]*²_®óm;òJª’.sòèãZØõV–÷—¢®¡ð%:ôÏ"~¶¢CÛi72J:;bcï¶CÎ%»!~IpÒþ›Ø¬Ûêg’2ŒpÞ[”ä½P¿¿_þðeêõK(Åú%RÔ™>&+âܶµ®„$É W׺šG»~:Ø0 É™"ý ×Hþ÷*ã7-ÖÊeÛÐiÏú¦lB!ÊØ£#v[½ço“»“»Ï‘=´ª¹•ÿ]rߌ)˜ü¬ÙÛb¼ #¾°}ïùûoRDPÁ©v³n}º¸”}KþÜ^ ^JqÈcÄ‘öî8xññûLš£cãÞ²Ç`ߺ|\^Aþ¿I AJ\¿D¨¢–EÒÉÿ-¿.¬jÏ1KÝ*PÉoî^8{ñQRëŸwRVЉ`Ѫö ;«I»fTõ[ù~›É˯xö=&L¥EŸæLÉò¯ÏBÁEeyôI¿ÑkC+¶8m”½®2îÅåÇÿ9]{U5RÖº%ë5E €ú Jux•½=0mÌŽ˜jÝÌk%Èxu=`ç¢!á#ÿ™ÛÒTmΚùpüÔT©=‰PEV$ŠFOêÙP"„8ºxz‹%„ÅD2„Èâoo›ºÿðÓ$ Û–fi`Ä!Lö› ­+÷^z™JkZ×ë5jDˈùýæ‹ÇïZä©E²_,ÿkæÇÛW5Ó§¤/× œ5tÇ’zÚŸ>õâç›g¯ ŠˆÏT¾QõvCÇô0¤òNIq’–¢–ø3øâ$õS!ÿ´y•x¢§«1O‘pc÷ê ÿ>M u*Uá§Ófª_~¸¼yÍžó¡É ¹—Ïè‰]´¨Ï2¸ÿ­óßð¿7‰bšÌÜ»ŒœäS]óS(HI‘2“|sË–Çz=V-`¯AB*Û×ðjÃpÞ~ŠW»/) èãÆ{Œnu(à–hÈžå-Œr N¥oç,:ù,:]F8zöMMÜÜœW²^S´à¨Sú@•^þ@'þ·uO„åÀ³{Xò!Ä¡jÍÊüÁãwn{Tw†ș¿'=nÜÓþéÉàð4 «ÆÃ§liÁ#tÚ£ÀuëŽÝù fë;x™8°‰©òáÂþ~’žÿÛs.<]ÇÉgòô.:¥uÛî(˜ªr‰”øFŠ¢”Ê"•ÊS"C‰y–ªÈYÀ°44X Í¢ßu(ªzŸÙÓy¤Ÿ_¹íI­L½·~òú'½ü7¯_2¼Ú‡í3—_Ì´¨mI¿}-¡éì÷7¦+"®…¤*iEê«°,“Ú6&ß›)iŽs»1s—o\½pœgú‰å[î¥+iZumŸÎ÷—"P*•…/ñ/bp>aB¥ü…’!„¡išfC†¦³_Ì™{:»þÈ9Ëæ kiÅ#„04­L½¿jú¶wÎC×oß¶e‚gâÁëî§¾ Ï´v±þë׬X<Ð!rÿ²]/%ùCÁ¦¨1ùIÁQ¦¿~"³ïÒΖ÷i‰ÀÄTHr7¿ÀÝWØ šQ| Øøqë:¸³ƒ(ß.3,m‡æ,_½aÙ¤ÖÜk+W_W”°×-8j”?”àXVÀ0~é…Ò¡}3sNވ˷ñng™õàÊ1Í¢Œü÷º¢NßS7T^]¹øÔ{™ìíQ?¿‹‚Ns6ïߺ°O…[Ëž|/£†–<ÜuFæ5|æ¤þöïW†g—ÚÍ¿|¸Æü¨u AJ^¿TäJž]÷¿›=^1»Ï#;·š®5\=ëÖq0äQ Ãv•Ñ‹f4Õ£ˆ²bÌùkAa R›·Çn*êNæ]U“ëþ##nO=z;{BÍ i÷^§)­%÷Ÿr<Txy=Bìéõ,NËÑIõÙÆ*wîU™0²Œ¤$V-w“3ÿ‹HV¸ñTgà9Û‹S¿TVÁùìMèw;vÙ™©QÍ|[-yuült•¿7ÿÝÔˆEHeî½½W’¢L¼xSÛgc:¦lBŒ:÷¯unñÕH‰k5Á§C®[BhiZR¢]mW½káï3i/BQ‚ú¥² Ž"õ}‚B·ZEMêËß˯)x÷³]«}Ý \9„°{,^Úß–÷Ŷs+¶èS‘0 qr"ÇÍË2ðÐË8YSËßÿ€&à7UjóŠ´‰J]g3aþ‘­cmÊËŒŽÓ– á8š;±‘Eˆ›î›[ӯܲÿp*Þ}ì¼ÖŽBBLš÷êpbäµIÍ+J£ÎÔecji"Ó 959ôuº²²«´ö÷×ù¨©Rüþ¸œ«À…ÆÒ¯;n]ÕöïÝüäÁ©Õ‡¶´œ¹x¤3Ê͡hš¦ák (…D&M ¥=Lùª7àW5!Óu›Už|ü>³FÔ=IÞ­õ>.¿™nò–¶îkÆý|[äq×÷nØvæy‚’-Ô≕Úr…’æä»Ôž{!¹ðÛ_ŒG cì¢B·>¶žaÎýÓÉWW/»©ºüMaEú»×™z.vÚ„¦iBr/ŠgG‡DKÞîâ³Wµ¥LNWÏPäß&ûíÅkö]|™F¸"-*Ki)S(?…‚aT;IÓŸöR¶ÁQíÆïA IDAT$)à×hF5kòÝ—Æ\ûº°BñD|êëmP¦<>¸aÛÑûÑRJC[˜­`™+T³VLîüCÎ MY@}”àXVÀ0N¨Ü£MþÑ‘aÉ=QŒêÄ3®bLÝõ&")ý•_¿lŠB¹Ln”˜©0g(–ê¤Â×Pr‰LYZϘÅó— VŠÏ_*ΰÂÖ¶ukjëÖ´{ÿÄ`¿k÷]ﺠJ^UErdž&¹E;ªaƒ&„04×ÜÕZyàiÄ“°T»®¶Eάe7#ª¿K5q¯ÈÿlS”qV,=Çê6}cW— ü´«‡Î;Eþü/EËÊ68¹çÔ†¡INþ¢Aåä: !„V*ä4aQ$gýª²&šfhÃq¾v¢‹0oèçjjäÛù»£þn™ÿ5OË*úTtฉ7™|¡`T§ÇÅÏÊ08”¦™>;íÍûtŗטhUÏ(x÷éü¾Û >ßQ&ýæú%c›L]·ØÃ\(}´¨ïš¼c]^gaèbåyàwTjù¥i¦ÏN}•©´ãæ ½Êä71r‘‘Cç; ´RASl¥d؆ígÍëZ‘›»¶P‹F’{9‰VM óÀ¥ŽÃ5îµN!HÉ¿?Ž¢hšf ÎŽÿ,Ëû_–¦…™ˆ(¤ŠOÊ7MÊÖ«dŠ ‰’¨Þ!;úY41ªdÀZ»UH¾}ébtE¯J¼Š޲;g/~Ô¬^Y—õÙ›Éb_Dûö\Œø„a”JÕ…ãÜYÉ|)Š¢^bgЇ0_nYÞ¦BÙqR"¢Ätþ_aØF•蘗)|]=Ýœ?:vþI>†Ä‹jvnVECFu5|Šbåç‡:²ÃˆÈ·×ʬ˜˜LeÎ6¸ûô7ºÁ7vTžöNnݲ»™€Å09ó=_= ·Lƒ >Jt,ûj¯ÒБv*ø}Þ‰-Ž:óAàRÏ’÷Ù™-~÷,†mæ`ie)HyM´sGu=mM+ßC ó†e”Ì?ì—=I™vgí¤“úÍ:4r±Õc¥F\Ùs)³RÏZXÙŸ”÷“hÕèàÁ^¸uË…á«p"ƒ·žÍª6ªŽ‹¥prî8ÿºê(;a(+¯Êi‹n“:L8Ÿo Ç R:ðÔ±ÿ jð>^?´/\¡ï’{˜·ÙŒÚ>‰ùüùKùŸÿÃFö…+×ûæ#t#ã´Ð Ç®f(†eàÑÑýÀªÅ+õ†tªeJ%GÜ»z—ÛijœB¯‚­^ÖµãçîжʈK¢¶_ÅD]Ÿ¿Déyöïu~ÊîÙs²|Û{Ù²“#nž=õØvòb'UTàî·ïWP7 ßz+[ׯ„ýoð¿—M=t“îÝó0›jÈ|y» Cðü%('ùCi>‰Ò÷ìßóÜ”=3dôiïnÉÏ|}óоàt—!ýkˆˆ’aˆ"úæå[zŽÚY¡'·ÞäÖQÃÀδ¥É¸K6“^Í*kJ¢ž_»ô¶Ö¸Á"òÙICIŽ\j8\#5UjõK”ªb±ÐŸX–N­]"O^Ú·âd&M(-KOŸéƒÛš±•oò}ø?3kÖ8ïïëÌ•Á-Ü{ÏÜHŸb®™‹ ï|J=G-Â0ŒÀ¶¾=û¶¤¦%ÿ‹ a™4Ú?dõþUþGôZµmcõþVA—ò‹6âÐ4]ø¯8(Zp>»>ß´¯Oð‰¦ëÀq]WmÜ»ê׸fë&5ÂO1 ÃP:îcý‡îÚ~tͬÃRÂ3´¯ÓÚWŸo#¸ÖÆt~»úàò™,£š-[´¨øê³P’ŒÂenÅN³ëì=rtmpCÆNu¼´4g¿Ëñ Ø}#óJßìu=|´|ÏÚùAš¶ ;·sˆ8ó)2%ɰŠuJ Š{,+xï<{‘î=Gö,>!fXZ5½Ç®êîQÅ0 BQ&ßÝî¿/^.´ª?Ðo@u¡lºÎšÃÙ±ëàâó4Ñ4¯Y¿ƒ¥Iüò¤)Å/©øåÃ5åèTÂU̲OòööFïõôàñƒjŽÕd2YùÙe÷<ô¹›‹‚ƒà”QpÊã^öfû˜9¯úl\è¥õk/¶”p¸ ò7(É`þÔÔ/|þÒïî§< Á)×ÁP?o¸Îy>HéÝýÛ×È@M•VýE(¥RY®l¯T* _âà 8Å€å?m¸Îyæ—~ùpüÔ:… ¥ñýq¥Yù;(ê#J§ÁP«áëg ×\ëþëvædå{¸Fþjª´ê—X"æóøåäD¢(±D,§ì‚ >0\#ÈQZõKf¦fQÑQº:º|ò·livjZª¹™9‚ƒà”]pÔ†k䟥¤ÄõKzºz4MÇÄÆH¥Òò4>Ÿojbª§«§T*§Œ‚ >0\#ÈQZõKJ¥ÒÈÐÈÔÄ”Å*ß¶NÓ´B¡Ëå‚SvÁP®‘?ä(µï#D.—ã¬ÁAp0\C©`! Î))ç/òøÃéëë#È EU¹„Dù@aS‚ú%ä?„i5„ç/š*•ç/!’ðGòööFþðe AJ\¿äëë[~"Æ0 EQè9h#Ä ±Ä ±*­ó«j¼Q¿j õKÅ€oF!Vˆ VˆZù”SxþR10 ƒ  +Ä +Ä ­ƒüÊo Aðü¥¢ iA@!Vˆ VˆZù”G˜v(ÎhÂ`¬G!Vˆ VˆZù”K¨_*Îh‚kEh#Ä ±Ä ±Bë €òœBÔ/C£Vm„X!V€X!VhäP.ý.ÓŠºÕïµç£B6¦H³™éW†×i¹øI6úšú¶b… Vê+eô!Ÿz>[#åˆUÉIž,hÒ`ìõL5Üóøcý¶ßúF†Oý7àû@M•Ê÷Ç”°K?þ·oÝîS×Câ³ W×Ú¹QŸþ]ëZ Ôô×™™™§ÎüÛºeÝ"_H¿2°ÅŒGù—Ø ÝÚáâà Fëί¨ŽNö+|¯x†N}'Ž÷qÑÁ•‚bEè”G—¬Ú1<•&ì]›úŒíÍèÓûLû|Í¿u<˺;­åtöŠ“óëËS¿j¹Àhí©Åµeò~ÙÏæwr,•BˆQ÷}G†1ë» ¼¹©N †ÒÏZJòpF»‘!=`ÍýiŸA޶eu¯v=ö®gÎ/ƒc‚øÑÜVÂ2¾ZnÐyÍÄÈ1Ó¤3‚·µ1dý&ã¥iåÑeü´õ+¨Ë™$“ýþü¦eO>ˆ’¶vEg¯6ƒG÷v×g•—O=ò€¼‚”rý’ìMÀ¸^k^Úwº`„£>øòÞÅ#»Wìu©5£º†:&Y™ö%§$ØßË··¶¶Î÷_¯¤¿z4‹[k̲áŽ9{ÇšYqíæëkØóÑ¿~‚Úˆç>qÍhVÚ‡»׬:]t|m{S6Bõe¬QGÇÚØpø’±5 äqÏ®8z.䝿H„¿Ñ¯Ê”FÕIGÕšØÕïÿF|&´Ôß‚gÝmÚœ4kCöÏú Žr é±7ÏÚ<©×‘Û7ö®TêG û¿7ýÓIÆ"~¼xì6á˜å£«jBiYë|˜3Ÿ®®ÅRû~¥¯9™Ñ÷Vm7UïøÖîÕá\’ɼ¿rÄŒ æ½Ç¯lbÍK‹¼öÈ…+QÝÝõ5ð©/ ¸Êjª,ê—”±gý×?µºeë¤nݪըݨûðù¬ô©ÈI½0Ô½í‡ôlÖ ó¶ˆÈã3ºµnêæáåêÕÒgÖáÐ,†ñiÍëÛ¶yFŸ^®Þ/¹—[´¤Ly¸g\—F®;Íü÷­´t¶6+++ `rJ2EQééi÷gdd|ÿW º›Š«_©º‹³³ê³!õfŸßÊàhÅçWmÞž]0¨}/ׯ=Æïy’F«ê²z¬<¶mLþý¢“./вi=W/×&>cvdy»†}žß0¶SíF®gÑiOLéåíêáU«ÕÀ¹'_‹UAM{¼}’—‡—kÃNÝ:5pëÿo-¾3­yýñÇO¬Ú¬n;ÿ§ÙEžÎ ÌY[ÓNýœ’eGž^Ü·u=W¯ºmûN:¨.SöŸÇŠNyxþ)§Áôi½›ºVwqoÖgòÚC‹Ü#æô_)µ¦[/×!—âÃw ïÖÚÃÃËÕ£Aó‹O¿—*>è?úZVæå¡M¼\=:®x|gFóF#n¨ &”чûºûìz+ÿ*&ò? _}}ý$*xåˆVõ½\=švÿÏ­D%!„0’ˆöiUÏÕëQ¯Y{ŸeЪ²Ìz]çï\>¸}W†í& ÉúâJ'‹/Òr(Š+ÔqÓ/Œés8QþdVcO/ׯ35¤ø®;³g’Oc÷'?¼üQK…D_]·`[H6!D™x{ã¸îu=¼\ëw´êr”œ¨äzc—üÝØÃ˵尵÷RiBè”{[Æw¯ëáåZ¯e‡kÿK¤ ÿtrªáѤëøe{Œ¨ôlã‚£ŽùÆèÇ+unTßËÕÃ˳ãÈUÿÅ+äo·ûÔmµ*,ç ‹XÓ©aÏÃÑygy,‘¹Suggg—j6Ú,Ž^¥j.ÎÎ.ÎÕL9O­ð|§Píר};fôðððjÐåµè÷ç—häáåÕeÚ¡HÕŠå1W7 ëÜØÕÃË£ãèµ7•?¼r¬Z»i¿™£k0¡Wò¾whèµûäêáíkyxÕï»ô‚ê˜)»²vd«º^®žm¬–Ó³ å spÉ# ºždÝúèv.Õ]¶¼dçÖUxßhÄÒ™!‡§õjáêáU·ûÌ#ïå§ù”7eðü%:ñö©§ÏÁ+å¿øÎÖµ±ÓeB䉧Jqö1ÈË€£i×rè¼í»wïYÞÏüîê‡ßÉ !„–<:t[§Õ”ÅóƸ%Î^”@Bˆüýî-O­ûù­˜Ö†syåÂ+I%ÿL‹Åâ€ÀýIÉIúúúÿ]¡B…ÔÔÔƒû3³¾W(Zà·É0´2]Ðt'z{ÁÈ5‘µ&í?qüмF ;¦-¹›ÁBäï÷m{nûù~iT¬;`Öºý{vlS=âŸ9›BT÷O(Âvž¦[O]>¹¹¼(6E®Ž±bñõ øÙ¯ïEæ5°xZúµ§­lɶ´-øüÙË+èpt«·½jÛîÿøwá]ð_p!ɤëú…žA½eÇÎ^:¿w¸]Aµ0Ò/c’¢ü“ú•Šäù¦1Ó‚…=–í:¼s¶wVÀØI‡ÞÉé´[K‡,¹o5xmàþ-“]ß®ãw^5¬)¢ŸNªÑÎòé]ôn¯›¸íå·/ŒP:ll¯ÏvšzìÜÙK'¦»H¿5¤¼Û¹ü"åÙw\ogí·/gõòw{ÇOÚ/n6çþ˺ ƒfÛšM!´øÎÆ£²&S–ÍQõÍ®y»Â¤™w–ÏÜ™Ôxþîë§õ°“Äd*‹+J£rç!Í…aǯÄȾ56ªÖC¸ÆµºÏXµýàô[y+Û¼Y[ë„+ç_K !DööÂ…$û®õMŠ8…B‹ïíüÏÀgÁ‚Qî±GÆõ²=«é´ÅSÛó®¯\s3…!â盇ùß±²ñÔ¿k»²Îœw.þýŠ¡JÂÕ² ùö¡!tÓž(·aËk˜vÂíƒL"{µ{üÄ£ÒfÓVn];©£-Ÿ0„¢Œ+h”ûñÁ%ÿ±\ÓX‹Ä=zžwuŒ-Ðâ± <À) \˜òßÜ1«[õ[¾~ý’îúì?àS_†P¿jBÒ¬_’%DÄ3&õ­¿q«[·Í†]ÓkkR„âô×@B”âäX —FÕ…‹žÄÈHBX¢Fó7L®#"„©É{pcÆ¿OÒ[8±¾aÙßÖ\ÂÔÈ>ýﲇQÒ–%,:>~âHbR¢žž¾¯O/MMÍÝ{ØŸ˜˜püÄÑ>½úáj-¾0ºÅ…Üÿ5_¤É—¯HøoçÝ×7gbÒg„×±iç"$ƒáXßøÅ~9{õHGêy‡DfÒ• !œjÓ×ÌncÀRT~wè챋—ümÇ#äôA¿ç±ÒvÉÅ6˜½®K !í‡øô º“èk].kt¾yň–%¿¾ºqÍ-â:ÍM› !,‡I;wt1aÅǃîhuÙ=Ò»*ŸJfÞï¿uíö½;·vNر²J¿MÛþþì¹ʤ;ÿ,Zµçï³Y‘Dβ*Ô†BÇ/bâ ÍúíûÕ§{©o"%†ums ì9†ö¢Ä¨:š6ol™sÆ·p³ Î„ÆÉ< !„•®iՊܽï“ä„ð ³-ßRX|-!»¨-•–(ªìd :]aiÛV5ßL£- ÅârUÛÈê‰X màÙÂ~ù†á#ê7t¯Ý°UëºVBVñϺ1ßÚyô… Wz«d‹tøYJ]9M8¦ ;ÚmÚs>rT%öùK)Õ‡xý~hŠâ¨F^–†&ŸÍR§”†&Ÿ¥”)eñÏßdD_ÜðK5 •É«$Iò³ò‡OãǾ‹ßöѵ´)B~xh ,¡¾¥HÓÞ„e¸WÕû<*r Óï\>ï2,=÷Ñû϶»{ýÆí»wN. X»¶á¼]3Ì hÄÔ¨>‹"V½+ ÿœO=ò(ÊàùK<ÃJFäÔó·bÆF‡Òpº}·<ãþÜ‘_]b<]=iÝc÷éÖ·tÐU<[ÚmЇ%MqøÜ/†lU:cø”IÓ¿X"‰¾3óðyr”WÏÆ±ZÕOÓ!é/¿ƒ” §Æ´€šy‡/ž¶fúá¯÷Kþzïø…—­Çl êâlD½ß޻說Ž|,*ÿaP58+¶±ïªÙäž}Pl¡.·|vìÚˆÅó˜²~\5mMCS.Eù¢Rä«>ÅÂ(tî±ôÛoV`äu…Ë­.»pëî•Í3wvÙ~`üÜ€]Ï^øïÎí]~»¯/>æß@žYR@¬(¾qõFÝ«7ê>hÌ›Ýuݺõf·IùºrêÕ…Ó·}l³8p[c+ÍìÛ“¼ý ·b¶qû¯bRSHýÞýê«ØfikahšpÜBÇãÇCJa[ê[{ûõn¨ú+צ÷Æ.œ¾rûÎùÕÇvý;tÿŽÁ•¸EŽUvÔÓbÖʘ^ÐŽdWe@Ñ'gO?J Xq¤_3”³ý;îPõ¥†ìVï>ÚŒw1½Æw½}’ò§¹c+£dØv÷¬km”w†«©Çú¹ãÕ˰“V¾ÈÌé²ršˆ¡ Ãf} ¤~ØÈ\¾ÂÖ¶ñlcãÙ¦÷˜ÔëÓºMÙt¤çìz#/rÕ× ¹‘+”›Åú£>õe÷?€Z§¤4ë—X†îÞòÛ[¿Î&„%0¶«âàho.úêC L~ù"ݤU¯ºœo¥øtZøÃx¡½½¾Z¥àźÁ3u2¡?0&Ž>ŸfÈ~{?RiQÃü»m£ØyA.ÄR˜–Ê¥aæh”þ"QÕ"tú«çIB낟qLÓŒ†yívC&/ØqpCg½È« ¸íG±’Eþ»õB¶s·FæßÛYÔ£w¤Z¯^fB!ŸžÄÏ6©×Á!%xÃæs™5;Õ.õg1s -ØÑÏbyúy[¥¯ÅgýäñªJÍÎþK»‹‚æŒßÿ*›I¡ l- S^bÈûÜûñUžPð(gT¤ã«"ùUŒ$ïY" []"Ï&Æ4¢À¬ …†¶ʨçÑ2u89/»#~éõDœ¤‚z*‹ú%ŽYûÎöÛš°Mšôm¸eæä™†SúÖ«H%¼¸~ö¯ï¢‚Šà5Ì 3Îí=rqP¾8¹eË{y•¼ƒÀ©w‹ž«&/&Ã;TÓ¿}pöTD}ÿ¹Mu©rر‹ÞFlÓ&=Ü·,™»Þqf';Ùã} ®pš­¬edgÐHïĆ%{t}ìå¯ÿ 8%·*TäëŽõ ^÷­k«:–ü¸›O“4¬¬>,`Û­£We­ŒwãØ[™ðÔ0VŠ˜““‡Ü´÷éÞ¼–ÿ põMVíYµ õÂLØï/_¸íæ¡!gW²`ï?~àLÅÆúñÿ۵jMáØê‹Ÿ¹üDÇ’áZ™»Vb߽ㄨ'ìì?;"V„N ž²ôyþ˜T1dÿþýJžñø[õðJŠg`åÛÖè¯5Köé ª§}nÕŽw•,·5ÒëÝ„3aéÒ3û9sÃ/>œé6»¡ [Fˆ<îÆ™+^&òð“ËezÎiüùýJ´4+S¬`¹83K&q l •'ÏŸ`UŘ6üÑÂ1¬òÖ2Ëyì&϶OåC[ì°ÓÜ4õƦ5OŒÚþSM@ž~ùû½“·¦4èÒ²† yw3$KÇÞV»põKŠ´÷á¡BVFB䣋‡v^ˆq¿£½)SÐØ¸¸­êW¸ÆŽ¦ô?{/ˆêðßmÛð\nè®:s5òêâ¸ÌïF³U5µK}£tÜûµÖá?aƒdX³Ji7Ï'vX4ÃMøsÇ+–¶ëˆ5^ù®˜°Äf×ä„޹‘­« IDATïešnãfv™´xå˜ó6zôh²%+ç÷‰ôÝÉE#'ÒzU;/X3ÈQƒ(~ÿÑ„eÐØËô5Ëwκ=›ð«5ì>Ô¸Àê"~•~óúGÌÚ2meêÙ­S'³B õü*¯[Ï[±zëÄ£©4Ѷöôî]IH•ÏŽ]Œ6bWh¹pu²ÿâýgRºm'¯ì¡Í¢ª^2<~Ö?SÆÊuœÚtô4xÏã"òvF•S Oìžyli6á›Öîé?­¹õƒÐ;æÞ˜E؆Õ;øù·7g«a¬¸m†öŒ8¹fêÖ,†p ªµ·u¬·1›¥3|XÝ kGØnØlåÁ)“ZM[ë7ö¨N•Ö}{ÖxH!\뎻ߘ1ØYAÕQ;6öš<áÆÔuó'Ÿ4®Ý­OO«Õ7¥eWÛðxþ˜4Ñûýï åÖL”û¢æëƒf­Y¨˜¿jb¿Õ ¾¹GU {XsYÄcÒæ‰+çnÕ=•Ñ´i4bõÔÖXôGB“zûøïÅ|‹†CWø5ÿ¼Œ?ûŲ.ªï›ÑªG÷}GÆ:4=(hÚò‘8Vݶïûƒ!…eÐlʤ[ßo©-ãr[Þª÷Š%™óWLÿk—”cT³“߯aN".èŒF¿F þ²­“ö$ʉȪñß ÇÕ.V²;K‡õ"„°4ͫռrYoOS!ä{c#§b§écÏÚ8kÌNÃ]}}ìÞ\ÊÛ»:-í¨Hë.Κe0ÌQšnc·/-Ú´jÈþLÂÖ¯R¯Ý` Þ¯¯¸Ö]ü—¾ì7fúŒÊ»æöÐ@éÔ4À ÿ³þã˜{ú´s~€ùæ('+Jþ ¨6dDãÝG÷Í9œ,#Ddéá»ØhU‹Ј|ƒ*_/ZõX2íÝ”ÕK&Ö­Ú¦sGË·7ÿ¸£IivDG¿ ®b–}’··7ÎwA ùúú–ÒÊÄw¦u˜Hž_T[¤®ûûæí[´»:+›6’<ôï:Qáz®«±B¿*1ÅǾ¾gÚÜÝׂƒX“zeR·ºKOά)D¬ Œ[§xXƒ‚‚üà Jòî˜5UVÏ_ú£ýÚo³‡ŸÛFÙa{îë¹8[в"ήº@5Xi/@¬Ð¯àWÇŠN¼±ïžFãõBÄ þÜÖAþjªLê—þt¿v6~n1ÙÑ·vlØ•*'l=ïQË'ºjRˆúüâX)¢.>Õn2þHçѯÐ:Èà·L!Hi>©„„î‹.\WóÑ„ÁX¯ö#~©µ‘ÀeüÎËã+(ýXq,z¾Þ±*^ð*úî¹ç‹XÁÞ:x~+¨)}}}¡È£ ®¡+Ä +Ä ­ƒüÊ'Uå‰"ù X6B!Vˆb…XÁoÞ:¨_µN!H‰ë—ž‡±~ø£eDÛf¸7xóNaÎÄG–›;BפܫÒýø& §zzßzSGý÷ÚOR,4سkrÂÐÚŠ¯ %ý{º~å-”Іßïe,¹^^Ì`<ÿÖn³‚LíxlfBÞòðü`¶>Ž ;[Õ7ÁÊó;Ng†°5õZÉCƒºØñ8•êtÄóͱúw¼¥j»†;ú¸ìèÍ[¹69Rÿ_õ+ŒçßÒyt/ ¡×ÝŒÌX©*ã0[‡i-,Øð¥“‘T°õ|^dùkÛÙné`÷$¡ß9µŽ[w´ìõGÜïYc@1ÒÄÿÑæ/ñIR$óùüwÕŽšžr ¥o€}|NI~¤¦¥XYÙ×Ì߆èÞ³4{âzE6ßtÜ÷µµý•Ul—ƒ®¼Ü¨œ=QZ þžSéXóAƒÉíE‚¼ü-‘Eÿqyê“æ/©ÎÞ›;®Ð€·¤h¶â ¶pϰXçm-êKJ.N|´e­tÁ\3ˆ‰ß2#ÇfnÀÌ6nåãŸG ÂÝ-¢cöÂC"»û&>]2éi\ûÆ>&\ùÅg·%n¡,Ã~ê`ÉÀT%P8îñ¥”°—³R¬AŸIk—¨Ù”÷‰Zwê×ô«Eÿ5p¯>;ƒ@NòëçÖ¯³Ç·å«÷=O“X†ö®5· fF eá㺲%ÿüéÄ JaH/—ï»húŸ.×YZ- ‘3‘ic’غÍÝfô·OØžX#“Êx⯸´*Êž±SEÖrXØÇ=7/ápñ› PÕvãÄë'»û“\ùÚ¯²ž#ÿ{ÖÎ|ÚÂÃ.´Gh~ôÂÎÎE¤KÉ_yE§It³_ÖSr°¤bPˆ/³Z2ÐÆúí¢ ¢Ò—<«˜wätåÿEÁôñÏ`´ùK|’ð…A.:õ[¢bYˆk OóÜÝäÝφ¶­•T@e—qq1Oe6NB±ä_ì'qó%À÷›FýÒ®¤>jê^ÿQN’—][,#:ó»ët÷^.ƒ\yŠô¼¹Góbõ„?ß•â‚Ãjó~>"©V}âlÚ¦xXÚì'¿±Ä)Ȧ¹dÅfÍ:S”AŠ­ úª"NÉ”žˆÖw÷;zYoý‚X³1)B`Q·öñ®ôödÓ©8øÜh Ù‹È&r€ØÓÏ~/e1©éÈ^Îý="Æðøaöò+%Jw_Ú p>5ßò‹¬-rEÏOe€°°þy‚õýŸâv¼¨9I#Ã>Ý*aQw¿u™LÒ„i†30@k*3Ø©‹„qßiY‹6>Ïœê‰Ï.ªSkÂ׿rØ/v¾ÛíyÔs»€¸bµ½«-Ȭt)iù:/žêÊÚrŸåõ,ç_ÜüžõÓlqêYÖk0ñ¶ÓE÷m¢’òö:ÓY¿ù>ÜÒÒg¸w=mA¤Z“-;º6ú-úNçÚÐin{™Ó*µ÷ï¦/~À737Ì~ +©Í/\u8ëZ9€ÙÖqø®“•¿9N•)œOK¦À]8Íë(÷s9 ŒÝ¹ØÄ+1-® ú®Û÷Õ=¤wâѹìðtŠí†Z–üM-ãÊýš˜2q)?F«Ô ÚrÙ¢}o«zBe¶—ÜKU8êVi" ñ¹'Ùõ°ÉMÌ©)[kY'sí®ý÷J8ÈÏ:Öл—àÄí7â×*í«(Ò”LY%rs?3ˆ¨:Ö–˜~ÕËy`mˆ¥³3›NæDªªû¸HU~Ek÷L©übÙ´{]ïv®2EóøBòãÊw¨Ëíå?טâPÂabñÝ`;úzêÏ<Ǽ16 )ÕDgéк§ê¤&‘ Æ,!Àøò—H’/ðÓ õ¿FåŽliv9=üaÞ„°¨Ám< ¸‚2ý½O¬lœ¤&æÿf-imØÞÌ–:½?ºûš§ãXÀ<[ʰ§sΓ¾®»F;¹fäÌ=õ"ßÉ>´¾€ÀD.ÖþZÅúcé[Óù}û¸t0Áá€6’ç·ÓgŸUÐ>Î3¼IÜ }Pˆ´± 4Å0Œ¬ãÄ+HUffÞ§¥=ÝH0ž_#©:¡ðÂŤÍ/¸ü{‰ÁkžŽºœ=|CrxEJòèÏ/^úsÜWG 4¾îÛJ˜Ôôoï˜ÙÃ×=íöKÞ‰,< ‘D €ÙÔ–¹•GÕ¨ðÅ@³zê_4TåiËS³À²!¬žb /‰¨¸$»D“÷T/¨-âzŠÕsö+\›œ®£1ŽšbõK±Àq¬¾è|â ÇöM˜ÏPmŠÕWà†xå…¿=EAJec¼˜Óá©Kïµwé-{ý2Of½¤‡yþ¤¡Ûæ\-Õ˜ñH¬N[‡ç9sŽeEŠ­fu2‘ˆ6ô6ͼžôņØé¿sÁ}Ý:™`ǽ-TÓ§ü’²%ÞPMGFy‘;ŸJPTÌþq9Y\&µð},±ül½4 u!)ò6!ìP¢Î£6YËó¶"jd¸ ¶Ë Ú5Àb`ï$|Ðnz%ü»º•–,ù)vø/Ç_¶5w3âתÃMIP«˜7fü0 [‹þu‰ØÇ¥¹ à"“ Cœm&/z¢{;= sm_÷·¯MñšQ_(@ÑOµhR Æ‰Ñæ/‘$ŸÏ¨tZŒÑE¦ëCš9GÆfïè÷‚QiôWnÞŸØE~"Íüß®&§Ó1Çéµt©†€‹û5yÕSŠÅµÎMe½Sçÿ¦55´™ãÄçÿNpš„´©çÊ5ØsÖÏÓ½‡3q5€Õý´7eO 6Oqð£U?÷íºvŠUÖ F¬É›vC££ˆ3Éìj?‹˜â±IOGæÆuÚÀÓ²À˜R MGë*毭å}mt»¶æýVÌh¿0;ÑźÎÍçeDzL¹š.ãà~”ŠßM^WP~Ÿâ7o(Ìy’ž]³ÒPèO½þ€a€ަ9 zR‹ Å4ÍqÀŠq>G—–iI%Gz±lÅ{pV$dÅzÒ[*Í-J+°âÇŠÄLJ¯oP{¯tWîÛ\RŠ‹|gzöí/"?UÍ«žÀˆvC¶{ù?ulÒ¸7ì©. Ý—M¤°­ü=šÈðƒe/ŸY‘ ¦?‘¬N+çÒ ÕžðÄ\Ü™ÄùO)°çVíý¤V„Ú¾¹œ÷(iK´Æ÷ ç|€WG¾º¡Lš”¾èFiäa=Û˜TËÁB)ÆXUaXN«a@@Jx¸L]åÚÖ@ë— p Îè^F[Fa‚ªÆM@`b)ÑŒ®ÒÝ8–%,x ^MTm·j)œp’ªÜ²‡y: èR2Kÿ6&÷´m+TÿœørûR:c’W°žG¥N¾¯£áÀþîMRÒ¾½«ÕqoŸ,y)5Á@•±<ïSƒkëŠb7ç¢ý ~@ þ/Œ6‰äñA —‰ÓYI°+ š¶ÞNOriƒ^qýþîÐN´2ñDšQ¬#Ç*)`™ÊA#žÅ¬b¨–{Ùer]l9t²äñò8ÎPÙ’såZŽàcN¶ê`ï•’>,’iÜÄîëÆv“—ϸ§}\ª(o")yà"¯«-Þþþ\#¡¹X¦×ƿ좕…j…ÀÔU„=}ã=%é…O —ŽDT±Y7™þ×8} ˈ¥XîS'±ìK¡ÂG8f`8†f9Žå(#h†3àÀQ gÀ½m¾–z¢Óƒ“bQ“î7Ÿ?¶±#qàuƒ“ [hs6 KyÚÆ«‘õgÕu1yÇËÀ‚Vëôvo¼Xñ]€cJ(p}#©D_P©ñ˜9¡n‡Ä²É%çãTŠ7Ÿà4j†%p’Ç÷¶&ì¼ê\ô¯\+àaɦ‚:r,ç¾VU|ŽƒWvÅqìÕ þ|ô1ìÕ]†}=®L`PƒáXîUÓGà€}´ÝþôÙ£Æh&·÷:bSz;Uy;Zq·ˆ©Ù1_&_ÐÓ$õjüéW[_PšÝ¿Ä_¶”öèäÖ=uÄe¬©#¯–³ç…f/ÿƱÎEË´ÞûJR’KS !r¨åÝÆ[ðc®­ŸFúøÿ%_þŸ$I’äñˆý¡mƒžêÜ&(Öƒ½Rž1üѱ€‡qiWBŸ¾^rM3ä ¦À²\µO]×SÀ¥< €À"œÓë44«ÐsNB`p’r¬BÏ*Õ,)Ä+%XI)5G;hT <¿\Y›ÓOV[ »½Qo,·ÒX°Ô|âÂ^¢e#6yuqim‹:Nô늩u·¶S>ëERœbfj­BEYë´E¯‰#ÃpŸhLšþG-#qwYî«;U–¢#üÜùLqq~Õõ¦ÞV¨†¹­ìùs¬V'6°´|”¾÷‰ZÔÓeZ^Î- ?ÀßΟÇ3zïÂy„˜OpÀy„‰PéƒA}:žÝØÁ±‡º C*›Rük¹‚¥=,çõqú&)3B%ü¢£©.1%ZÇiŠÚ;Li¡ÚšÌÖkaïY^¼úEÍ̾ÑG”ØŒèdq[Å÷püÂL»'NOîÕ¡î® Ã²u‰TUÙL$ÄMøŽa"!!åXµáI*B8 ŸƒEbá•,'3óÐ)5B€ýѯ(¾tÊÈZ- ²?£¬lÅVÀЙ%äðþvâÔâ;yaa6´£¸4.'á= I[û ]ÈK¢•DÝ&Ží°²Å ÿÁ=—‘~@ þQ Æ—¿ô K 5èÊ•1ãQ<€¾þbuizL)°¼P ¢_ŒA?¥ÞsI±¶‡ûOôý³Ï3?BqOؽ§g¨+Í)\| 7‘ªºàtåëöe0Ýí×Ot$9*þYÖÄ‹%zA÷¢øÖªöã’Ü÷mŸSñ/£;|8UÚËiÑ(;K=}˜>åšZ÷‡l'N§ŽÈâ‚ðÂûª8ïc²¼rÍÅÜÕÓ*6Õ)ØäŽkÝ¿îÅ“ ² ÉÈ¿81ç&ƒ[÷´8U,À¼e–²'Wçí,I³>Û,­ù¯ªÅ•þZ’è*ûÆ—‡c€»Xt©;;"÷.ÙM±ñµùtõÇÿÑ’ Ååé&ö³¿r§È*Zq¢(­z:Aõ<{ü16´Ó¦f°Tr’bW1S•¾ÄÎ}Fg÷®zíµû…‰örãw.¯u ä^¿Q'vƬËa_JÙÖÓuÒ0o1cxt/mI´PÄe̵vÙÛëK‚ÍJÌ™u¡¬J œ,ìâÖ S¯=œ_S„iÍž#ò>ë¾æ±juø©Ô#E¼Ì¿¬Ún˜Øbýt7_X<¹!d§÷ØU\òªX–ŽÉ⦴õ$Å@¯»ù[ê–ÌWáWÊ^f`f渮ö«ç­dò†¬'ÙÜ7þÎ½Í KÅ>͘ñvžÒ›¿€RõLïÒU6Š„²üâû2¯(ÿU0ïE³ˆùžŠÎ;#S"þYþ‘i‡ˆˆˆ-ÉžÿlÅÌD¼æžfuì%¼?,*¤.!W™TV¦}ïôöú£3þÒÇMýrõg±7áßÏw9¤+ÿ;Ò‡´¶ß7Êìè¶„“U.Wà›,œRÛæì³ q·ñ_úÝ·Vb¦ÇÓ5¬?)Õq¾µìâòõèI¯+)/ÿE¹C¦@ ˆ÷¹ ñÿ)EDD,M’ýFó#Åhó—Ê´ôù§ŠóOè7úxíÆrÛüÜÛÌå‘>.77›VBá¢ÍM1q±nK*—>§kä`”€‹ùrš*ñÑÚ<0^~@µ„ãË_ú›|®ù„%"–ö«Ç‹(+úC¼‹Í¦ v©Këo]N?ô‘›`d³¦fšöD[C­ÅÃ…$Š’ßcd@úø«íùq5æ÷cO:ýÍ2ôå‹W?ªò¶¼hô’¢¿VG]ÚÿøR 5·LZAˆæª‚Ïà ƒL Ê‚F ¤ˆÇh󗈿O÷ºÂ I%fææR‘YãÊÕºEiH]ax Z‚@ H? QB@Ë_B @(1éT»üb\AŠã]d"èí#JL~@ ¤ˆM; j0áQŽšXmfn!A𻔨雉¥á ‘)éâ/ðOå/E.hŒŒ‰@T#\åÐÈE:¹“#2@ ý€@üe ( QQë [ÎFŸK`šêºÑL̶°W5±Qÿó[%q`ogßÀ·‡z(@úøhPþ¢³ålô…d^ ·½£\\M¿B^‰6*¥È ÏnbUúÏ–LDvn64n„&éâ£Aû/!j0ç˜o{‘HUBWÛ/Az9YÞOTÿãúaƒÁ›—‹ô@ ý€@üe 5/éö×Þßí_Qhp[saf5fA±ŽÀñ>‹ã8À› ÒÄ_M; j6ÍQtõ‡ÃA|’Sð0 @úø+mþ’Z«~ðèvÊó$–eÞy Ç WÏÆ ›o¾Zô[|ñò~îþîæè§DT‰áô4[½¿ À#P?‚@ H? F#!Àøò—¢žÜÓét~Þve&§(oÄ–­ë9š ¹-ç“ö~ðýñèW7Ì[~<¶s¶kïï—§,«"Çw[n½ùÄ¢:k¾7éÌÎŽæÿÍqfŠáôÕ|þÁÀüËó\éåq7ÕÙ}|ŠÏ§>Èû•ë6¡Æ@üÇÁ‘ Ɖ¥¥¥qV,;/ÃÑÖÕÞÞÁÎÎþËTfûS”i»º,!À R€Qÿb=¹ÒK_µ ^› ÿô…pÚçW6NÒ®y_`Ȫh¨n^|\0vö²¥»Ø“ÈŸ«ÔúS_¬ònïƒ_Ù¿uÍÞ¨ÐqúòG1›[ûÊþàè€+Ç®—8ÍÔñq››ixjÿ§‰ezš3М¾ø×S»ÅäRU~D…~ø`U-€à”wBÛùµ}Kù·¥µw`ë/åÐ5Ó¿8ª4fçˆvÁai†—·Xeì¾ÙƒZùµ ·ã¾¢bö‹-¹6µgë ¿Àö!³ö?SVÜ¥ò®­Ù£•_`ó¶Ã–œLÕÖÜeXœ.=|ÑðnMƒü»Yu%÷­vÕvV›ueq‡ÖSn©þ[m[öpmÿ ¿þ{žS ‹þ¡H`{ IDAT‡_`Ðëkð‘†Î¿¶eò°>-ƒ‚ü‚º Zt2AýϯXUÜÅãútmãÐã›Å'Õ[rçÜa}»øùuº÷I £Í? Œ£Í_"žH(æóùïVXMO9Ò7À>>§¤”Ñ$&'ÿ8¾Å"Î:>ùë0]§Q³×בaz# @&¤¼†NÖÓQ¼_?|†ü%ÎsU‹ée/ƒ?½òÊØèbš)Ë=<üYz«ú“×›(OGÿòÍÛ[þ2ýƒï¢3:7[7“¼|ó—}6sÆI]ÙåÕ%ÞKë[ଭÁà“Í?ÐL½*æìCÚÒ”Ž Q·l&E^ô [†ÞŸÅ8½rÕà 3ÖÇ6³y¾GÉ¥UKç,t;º©³EÉÕe¡{ z.ØÖÓ*óز¦®÷<=?@”sjæüpbè’=­Ä1{-Ÿ¹ËûàoAM4•>9lúªë.6îlHÝÛ6oás/ýÔ^uqã‚Íá1¥„M]/½¬_öD†üÛ›&ü´/ªPä¼`õ´Ž6<  oý¼jý±»ÏÕB+Ïfƒ¦†/›xÊsåéå­-q:ëè¸~?Û¬<¼¤­eµ×%ØO?ª+¬-q}iîò³I Œ|P7B÷8ý±ÒrÀ|ÛÚr jyÅžxxã7U£nšätÌiŠ€äcµš’çî•éÆòôãïš¹Nmʱœ¡ª¯ðéôÃ0”Ñg£ð6Ó§”¯\{6VÕ¬ieÖ=Þ?gÞþÏõV_-üþk?3˜¢{?._wàn¶–´i2qÑ·íHÍýÙ½§ëLJ:Fl9‘íÝ@™NÁÆ~,½¾£•æ·°%›ŽßÏÕóm› š±`Bs9Á鞟¯Òu~oDË=8¬ÿáW÷4q'n¨ýgM ñ·ÄÁuÆý‹ãŽßW´|xò!¯ýš‰]J Ã¤kKN>›êç|3<^²d›:|¨;uÔÙÝgR¾ö®+¬ò!íÂ…¼Z£7 hî@€çÔçFœ¼–Û¸3ùçvëØÍʼÝöw4tžZ^eÉlÉï?-]ýËl-ÏÔ©A·ÐÅß¶’WÿÑCæÉy3N»ÎXÝüç±^߯MœëÕ¯oùFtß(t³_e ¢ðÑ¥1c‹O¶*¿ú®nð굕åïaˆøuIj1žµF­_SY€¯EÊå¯.GåQÍLø(02ÐØ ÂH1Úü%’ä ü´Bý¯Q¹#[Ú‡]N˜7!,jp„® LïÁ“ÍcZuiâúoÖRP{ü¶™>„¬ïæSW/žß,ÇNØ}–íöÝš™©ˆeƒ‡­K÷›°zÑ@»g?¯¼Ç°ÚÇGï™uµjÉ䯅G¬ˆ(¯õV!!¶§æ#ço>ðË®“}“Z¸=Nê¸3 æŠý#»´ô êÔgúž¨RVÒtáOƒl ‰‡Ï¿¶©£àÙ–±K¯™ô]´}Ûê©míHx4ÓqÛödû]³rjë²ÓK7E©À¼gÊ”%Ígn?¸ëû‰AXBŽýȹ}D××…=UÓ…Wø1³ÙŒ)­-kBÃe Y=õ9/Mñµï‹¤Ã\¼-õÅ1J½\"“(VOñy.NPð{™†£8 8Ö@±šã£(µâê:¥ÏL™)þ¾b€Gð>ÅUEú’*.üwèØ¢G]ÃsÑ/“F(EÄþXÇAó¿_6Ò=açœÕ¿«8*cß´4–í>ppõâˆã~Œ×°ê;kv¤¹ô7vÈÌM£ ×o~¾tñüµµ­x±;Æ-½ï:fÛ¯goú‚81oÉ…BFýä}®[ý ‹“Sµ–Þ.R@àXߎÍz–«~“ÅØÖs`bçzÖúôØ"uγ<°¯gÇ Ìjy™–Ħ©jbþ§ÎŒ/¹yZ¤m}g"÷i¶þƒv3|°dÕý5óv+Ú.Û{ðð–Ù<´y*¦Ú[‹)¼¼bJ1zûÌ–ò· ˜èÅšùµë7~ë­4À›ë—8J­4&bÜðç~Ũsï=œbÝéKoÑ[  ]™“È$ Œ4ÿ€0RŒ6‰$ù|¾@¥ÓbŒ.2]ÒÌ926sxG︌J£¿róþÄ.òvœÿíj"1‰Bs sèóxõæl\Ð]†Óµ3Žž?ÙjÕ÷£<ø „³‡Åäëû™à’6˶ΠpøQ‘sÏöðUûSoÞßw—á@»gŸŽ8óB)þ=8î›y¯O]fívå©ïäcíä5c؃f*×|¦@©<"õV¡ù#øÀéÕ…413ÐΈDœî…ÞÀ»Õâ"n–ª›‰w(˾„òBêC¹ý8û¢“}Ó~cÄ~vý69Õ~=Èøi÷oýƒ€PÇŸ»Ï5^ê#µÐw¯§[ÿk¼º™¿Hy¿5+Ç×´ñÔÝé»á\R™Yä‘—1GG¶uä¸ÌŸr§Çº£1_ÏÀM—ÚÐΠ³œ'6377çòÖÞ³t𦩽øýÆô<:þltžäìû\·úÁª‹ÕœÀDXaU\d&µBM©‹4œ@*¨¸KˆÍD .Rë•¥:žTTbs!¨jä5nđռi Df:»TÏ‚ÿS»}P 0ÊŒLµÔ»Y€—«\=¶®þRK³cꪼmz8’LÆ]¢C·YëۘʤPøøÄšÍßM5?´wðë :'b÷C¢Ù‚Ffõ~¿b ÏŽé¼,L¦nÚÌìMGc o¸¢ª3®• ÒH? +!Àøò—H_ z™H0•»’ iëíô$—6èµ×ïïíD+ÒœO`€ LÆÃ+ûK)ɾJ£y9àƒ‰êYs³Ké&oõ)¿n\²=<¦ø&f˜’v£V[¦ÅúëÛÖ™à5gRdç%gŸ)Û¸¼“ ã2h‡þµ«J\}Y%ÀÅ–bœÖêKÓ’”²Æ ÞÛÂ0óæ§µúrÁ–ÜFstª1}Å~–ü¥Jtš›kJ$C}êÈ Ç²ËAEhšŠÅÀg©cÂÄø%û9Ó–zén~©õùAªX÷Hî2ý‘$éëØ£[MBçIÈÏ¢ÞÝI{öÝhn})†K÷òÖ®;¯öoüÖ[q³ZµM5)ù¥¹ E’Ú>²Š7u¯+ÓNÓ¸öÛ_”cçñ«Ô>à8ÃãÜÜÍê•"ùE{Øu« ¸ÄR:¥¾rÓ%]¹“¸ˆI‰\ ™J= @0ºr&‘‰&æJ¥­dgôå:Lb)®‰£¿¸ØRú—VF«4¦æüCvû°-H·!ÛÎÔ¹|öú½û7œÜsfì]£kUçèpYÇ•‡ê*ìÀ䜙5åF‹ ë†ú¾µªž4·‘âñeÚ 2džœ?íþWa‹»Øñ€ü¿"L¼|¼¼Ì’.Ž<~-·ß€.¸¶tÒ–Â^ßïà†V>G  Œ“ŠÌ%0¾…ãXðu³:<¯×¹k‘EYÉW#£N- n\Û†ãXÀHVâ°ÿß?lYÒ£±§§%ïB´óL‚†7°âclÅÐ9iíã.(xø¤°b…2;[-qr2y£aáYºË!/&Gûqnââ*.z[̼V°e÷¶üp»ÖȱM²\q&»¦l·Y±~ú³\åíu*ñ ™—UŇR">¿PýBi`8£Sgdc ÞüuùÍ­zŸP±” Xp’b8GpÀÑ ûêm¯æ>ïʇ„ówô¾Ówürdÿ/GöÿrpãWîÊ»gµo½‰.ŽWZù:[:x[©“b‹*|…-O‰QˆÝ\ÍðwE+,ÍðäÞŽDnt>ßR&—W\–&ùÇ»n5,”yÖ+b3Ô€>+:s¨k/±ñu"ò¢stœ:#®@àâc%q¨oÇeÇTdù3¥)IJ3/7“š.`+ujbE“CåGg³v¾ö‚ÚíÃÁ,ËrBÿžcf.ßuhkˆEú'Šj¾€„0±sqwsswsswsu– p…““\ˆ³¯‡AØò¤˜"ž½»%Àyzþø e_l_?¬®¸âaæدhu©–Å   o¬·<®ÕŠŸ&˜£XÕ›d„1K0¾ü%+¹m^A®½#Çó÷´=³¸Ï™;)Ã:¶w³5£i:¯ ×Jnk£ÐÚU¢¸sñN XRüÉ?`ÕI—ÎEò½E…·w¯{d¼¿®ƒ×…¨q™òò¾ã¿qu˜Øð̤¼@Ú`Xë‘;–í°ÐŠ|òóÖxûài¾bÈy­ ,ü{újV~ÿ}±mÍòïžØsÏ@ôz_Ä>ƒzØÛ¼p«ùÄ®Îtêo§nÚ›i»oÉ%Ù×û†ÆD7m_ÑrS÷šÄD³•ñ÷'Gy±àA±¨ûWÎQÊŒWOZGšwùûbÑPRu¦(Z'ìÑ‚ã^¾ À•…Ä;™ôe‚³óÂÇh ÝIE4-ªOðñWÕ¦?ßúmÂù;Ÿ‰ê{Tæà» ƒÝ÷ì:—8¶)0Ÿkpãe]ûqËsÏ +ܤv=û×>¶|—óäŽv¥‘Û7>µêñS=<{+2s¶%2¯]¾×8PHË ï&°4t«v\‡Z²ä;ç/õ^>êã]×È` jµF¥e8Ö V*Õ& _äýEÉØ-[O[|ᦸ¸ú’®ñì¦rž¹ß&ô¢ Û›Oï&Ï8²é¨åÚúRž¤uoŸm;VïõÒ\ðlÏÏ Ö]æxÔÀÍ—@àÞµ»ý‘Ÿ6sûÊWgë¡|÷oÚ;`HØ2pÐù’³Û{XVe7€£5*­Rcà8Z£R* ‘TÄ{í²TƾÙ;JZõíÒÀ2îÄ©Í<ÝMkf ¬}²|ün²]6Þ¦ÅüðXÚeSc3®øÚâ1Ë›ÎYМÌIŠ\ wqµ«Â¯øŠ«sgýæÚ­}#aitø¶p¥÷ÔÖ¶„úɪoæ]w¿¢«EARBFš9»Ù‰‘@úøsŒ6ÉÑÖéEa^bj,ÇqP_±º4=¦0 37µ°·r0ŠŠ ½¿žÜùɪ¹ÃŽš6›=ßõc6}FøÊ «ŠX‹º!Ë7~ã-€7 ™·yñˆô…?΃Ù5ë×§ýOqâÖ/Ô/Ý8{ôNLÖ ïâíc¼EðæL¸M×Ëç®X?ï¬Ä­cÿ>uâ}DõÆmúVnX6fÌ<ÚŒ˜’¶üWÁ€°þ®$ !sžºaãoÍ–µ«þ[0áöYRc´º{ëµ’Num^'ƒ©´óyø,žS,a'j¶Ù¶Ý¯ªµwv0õ6™™ó0Â}–•ëäÂÝ_`4ߤû&±àuqüžÿð¶|H:{[å5¾ñëŸgÛ¢³ËºƒS€úé¾…G²T„•ßï t&\†¬ý^µl휯öèyVú,Ú6ÎGš·Ë7i:~\óÐM“Fî”wXwbÙ”+%+·¯s@„¥W‹ž£…6õ?Þu }üæC<Óq¿¬ÿ®ã³|¤'ÿ0yù²uΫIÿË—u±Ædíæ®NY²l鸃”ȹåØõÓ›šaÁ«gÍ]7gøNÖ¤V§ïÖŒ®[SÝæ{|ýì‚EÛ&Œ,Ç-ê†,\9È…0ËÇr€Um7®ìÚ„.‹žÀìàÎà»ðJXç×{ ² «ÃfüRDÄ¥í¨Sˆkhià͆Xq¢”"äõzÏßêo‚Ñé)elþ¥eã/½|Ÿõà=GBëüѯp½[€ë©#ÛíVhAâÒ|Ìúï9“œ"7>Ÿ-Ëß2áΫΡݎsËj¨«#˜÷¢ˆ¿YÄ|OEçΑ)ŸHBü!1pà@ãúVÇþbÈØïódMhîÏî=V\\é/Až÷é©;ç¡o-»¸|}µþVR^þ‹‚ãÃ>‰Ëääæth×¹ @ü ÿ_€±4Iö·”#2=ÂÈŸ 㜅@ þ&>.æWï<,ÿS®ŸÆÐQ³a¤ ý€0RŒ6éïÒC?.<\HVï,,‰Ã§Ë_B ÒÄ_ÂhÏ«¡ˆ›®¼| ™ás!VgVçù>3 ÖRâSè­N+‘Ÿ ÒÄ_–€ò—5‘~$'bJÌÌÍ¥"A5ý åj]aqÙP¿~=£V§U(¶¶¶ÈOéâ/€¦5˜¯;zp|üqAжÚ>¡"®««ÁCX”šþ—Lð{;{WWä'ôñ@ùKˆŒX@Nêá3©²@ ªè(„QK@ùK@ Æš@)hÚQƒIÎW/8– Õé{º—Y‹id·àÀÞξoõPôñÑ ü%D fû…$‘‰¥ÇÜÉ1t´ÏGy‚ ²s³ q£ÆÈa„ ü%„QK@ùKˆšHZ9Ï^&ÕrÂt9²Æ;0 c0rór‘)Â8Aó#¥¦N;ƒ¿vªn?@çÍÕÄH'x¡çHGã8ïÂq ã§éâ/ò—ÿ‘8ß\%†@ ý€@üu Æ—¿¤Öª<ºò<‰e™w^ÂqÂÃÕ³qÃæ›¯ý_¼¼Ÿ»¿;ÊNA|¨&P;Œ@ ¤ˆ¿ÑN;D=¹§Óéü¼ÿ8lÌ0LNQÞˆ-Z×s4r[Î'íý6ý”ˆ÷ƒašx¿iŒ*%lÐà;CÂwõ¶Aéfâ?jFJEæR…0ªŠeçe8ÚºÚÛ;ØÙÙ¿s™ÊlŠ2mWׂ%8A 0ê_¬'Wzé«–Ákô»$C^ä®Y#zùµÿîŽúŸ¨[|oçÜa}»ùµî;f]DÆû«É©ã,×§Kk¿À  )›nVlwjȽ±aò€6AA~Oß÷¤Œ­Žn^™¿ô¹e•ëV-X¼båg)Xœ œÊ¾uhÍò Wl?òPÁV¾±ü÷Ý›÷Çj1â_ûã¶ôé±Ußôíàä׺ψŇ3¨©|ŸkQ¥1;G´ K3¼²Ÿ2vßìA-ƒüZ…ŒÛq_QñȰ%÷¦öläØ>dÖþgÊŠ»TÞµõ#{´ò lÞvØ’“©Úš» ‹Ó¥‡/Þ­I`÷1«®äþ±Õ®ÚnÀj³®,îÐzÊ-ÕÄ©Ń]¡ƒ»7 ê;uKdá[;O3…W÷ò üæW ê{3ƒü^_ã®”°Êèˇôlëت˘u²Þj÷Ù²‡kûùõßóœBÏo5Í? ŒZB€ñå/O$óùüwk«¦§Hé`ŸSRÊh““ߢt—]þ<ð«o×M¶áé17Á?Q*Uô4žmøeèØÚæºøð5›—ÌuòÝÛϾÊqx¶<)ªÈ©ç¤¡¾ríÓ#k·Í^ìvrSO“¤“çvµ(,P’vzåªéó­oè,¯n#"ŸuþQü~èØc¼U¿ñÞüÌßNœ9xËiRGÛÒG§oj[|;·©þÆÆq¾ÃýL@÷<ò–¶Á°zfä¿75B3o‹Á©m3ê°¡õס[ˆ¢èðŸ·þ:gûÞiMMÑ@Ø»š?aËÐÁû³8Ç ×|¸aÆúئs6Ï÷(¹´j霅nG7u¶(¹º,tOAÏÛzZe[öÃÔõž§çˆrNÍœN ]²§•8fï¢å3wyœà-¨‰¦Ò'‡M_uÝeÂÆ ©{Ûæ-œçè6ĉ÷¦ãUe7Vzm|ð¼û:ü¿2ÉLe›ºë?gÇ<«—7-þn¶ÅÁƒ+lÅ–=Ø2eé}êMÕ‘õ§­_O¸‰›„-¼¼xüæÜγÖÌqWß[6oªÐíÀø:N“°{úüs*¤èù­ fa¤Û´Ã+ø$)à ‚\úk~‡5±÷ÒÔ<Y d&ì|6 ¹sz —]ÆÝyµrhã/›¯SLX;tÑSªàÀˆ¶~A_î»ùCÏVƒ~>¼dXG¿À¶! Ï?Ï»³i|Ï&­»LÜ­â4÷gwl>îçs‡¶ òë<êû/hÐÆþ´éI“e[— ïÔ¨a@`«ŠÎ¢ôÑOÓ¿läצϗ!­8ó‚ ^\ßüm׿A~zîå×rÊ-WzylÓîKŽì[øe» ¿æ}&î‹WsÏ1kVNëß!ȯI»c‡ÔÂ^¤—Pï©a×kÝæ9#»ù´ÿjÚˆÚTÒÝL½.ùLxŽû˜™CÛÔ«ãßkò¢`ÑÝ÷ ªßÄ眀òÄǹ&!ê¹¹x¶ìÙÞ¾ôiT!Ë–d”Jܚ۱̈́ȊA`&÷ذ¦•C˜TÞ­ãBÚúOÚYÄ“{lXÀ;S+8µævl?ùžXeü¡…#Ú5 ò ì2`ÑÉ$ liT¥Ÿw¾ìZAõ9ç_çÛ#Q‘ǧ×zCÿiâNÜPû;1Äß»a§Ñ3:ñ¿¯ Kœ|Èk?eb—†u÷œ2©‰æúÉg*:ïfx¼<仑mêûôŸ:Ê+ïâ™]ìhôi.äÕ: yݺm†Mh“tòÚÛSUÚ̼Ýöwnoé yÆfK~ÿqڗ̓üZté=aÓÍ"¶š›ŠÊ¾|:NÖwöè¼t7”}‘ ™à4ñ¿Ìœó°ùÊíßÂá™×ò­ß°~ý†õë7¬çf†)wÚ«±O½V£æOòÍ?{8A `È<9oÆi׫û9¡lN¤ˆ¿…Ñæ/‘$_ à§êÊÙÒ>ìrzøÃ¼ aQƒÛx$peú{žlÓªK׳–‚Úã·Íô!d}7ŸºzñüÎ`9tÂî³l·ïÖÌìHE,>zϬë¬UK&7.<²`ED!«Kø­Ä„»0£KË ÆmBÆl¾õ‚`òNÏ ÝY8{sØ®•£‚d >•²/tÆ)®Çâ­{·/âk‚½Ìx ·Ü4í;÷û¥ƒ­n]q<ëuÆÑÊô›GNæ»ôìá.|Oà8’Ñ•ë@"—Œ¦DÍ ÍD-˜Ð¹‘dÇäê«¥«óÞg¹ ,³d®VàñMíEšìBŠ p p‚àñ Ç êùo7 þÝ}ÍÉÏT±ª¯·åSðàrŠ0ph›Wánî7°“<ëÊÍœ÷d;hbvŒ[zßu̶_ÏÞôqbÞ’ …ï‰á˜gçOÝ¥î¸âð©³{CÄmœöS¼ŽÎ=9;4,»áÔ5[~\0ÀWZ½÷ƒ¢‹“Sµ–Þ.R@àXߎÍz–«~“ÅØÖs`bçzÖúôØ"uγ<°¯gÇ Ìjy™–Ħ©ØØÏpêÌøB‘›§%@ÚÖw&rŸfë?h7ÃKVÝ_3o·¢í²½o™=ÀC›§ªî¹v¬úÍF—´®WK£CÆéù“Âç®Óøí™@N{sB» ¿ Î_ÌÚó°”V[¢æ¦¢ŠÉÜÔ£ž¥2ÌÁR'›IDAT)µœe /¯˜FŒÞ>³¥©‡jÔs! ŒYB€ñå/‘$ŸÏ¨tZŒÑE¦ëCš9GÆfïè÷‚QiôWnÞŸØEÞ®‘ó¿]MBd"&qBhbna.}>¯Þœ ºËpºvÆÑó'[­ú~””pöТ˜|}?s\ÒfÙÖ™®?*rî™§eúÄ"ÚÀÕî¿|°pzÕ³Cå‡~jzé—'6£NìåJxI£÷º ‹?t,Ëwʺq¬qü·íç +[Ë>[6…64\?6ãA¦n¸³¨ôÝýþô¬;ÎÛ:ÖG ©ªåÝ:˜Wnœòñ‘3Ùݺ¹ó…EMja~9ô{“ošXêrcŒÁ\_=s´?[þ’^MáB!Yñy¤È„Ï¥«(~­Ú2íãôrÖN“œÅZ÷°PÞß“êÜuÿÙñ5¿F2f^mã(üìÁóÛó”"­lÚ: ß| ßÖÛÂó©úU ŒÚ{–Þ4µ³À¡ß˜žGÇŸQ6ªrd5óüžèÚ¡ÇZâÆŒ:¾ýFJþ¾§Ö£öÍø² àk¿ïèjý«9I剋ÌD V¨)u‘†Hw ±™ÔEj½²TÇ“VÆy@ˆÍ… V¨׸GVó¦U€™ èìR= RüOíöA-À(32ÕRïf^®põlغúÛŠthäÉ?vjß­6¡­í؂Ę|=C¨‹î¬´“¿cf[9¡/xcË}ÀŠ5d–"CîÝ_Ö„M\`{fC+ߺ¦E—ö_žÓËWœ›¡bôê²g;–¬Ê Ù¶¡‡#Éd ¸éâob´û/‘<¾@ ô2‘`:+ v%AÓÖÛéI.mÐk#®ßßÚ‰V&¥E1ŸÀ˜Œ‡Wö—R’50•÷Ëo0±S=kîbv±Š¯Ãå-† ìPOàãò]Ô îgÛGçKê4´!ßê‡ËÒ”²€:æŒ00‚$+îâbK¤Q•ŸÆsì³þ—ÆÙ W¶®õ¹wugiUu(¥Áœ` ®þ0ç‚éðmüø€ÙõX2óYèê)]~Ë:µu 2UÏâÏ¥ðЉ¢âóXŒãÃÒ&hp—ç{7->Ɇ ózqi58Þ%óøª÷Ñ ¦›Ü[³æØÝS;X}îÁªö_ªâæ ZºÊqª &M™{ktëã8Çê ”—¢êuÀúœ§Yê¨ùÁíc¥7HlK³K^ˆ½ýìÉšÓªbñÒªÃr¯îòðʻ쫻N¼z”†«É§Xbø+«K³ÜÇÚíÏŸjyÓNžk¶ŽNnÙº©ë®Ýš»ˆ«¹úÂ-ZÍXÒgÖ¢Ù_žœ%¼Eª˜G9E9K¿h½ôåûv÷Àd¯Æí¬ÀÇ»÷¸ó¢ó1ª.m¾]<"gá’Á–œë˜k@fʦ\M+IØ6°í¶—„…ôxþãéEþ"!ý€@üuŒöü8>I’$ÉãûCÛ/<Õ¹MPx´Æ^B_ºõðÈü^ Üd÷¢ÒŒ½»ÄþÈL±„b C³ˆä8–f¹î¸ùöG“æ.žæ.žµù/Ž;ö{IǶUÕÄØÒß·Ž_ÝlQØ_ ˜Ð=xɉnÓ^hø2ó¼­ý¿Š«kWýÖwböÙΚ˜´†Á FKÈ\"àI=:^Ú©"P|qauz­žýlUçò0ÇN.&b¢Ž·är²‚ëbû¹;‹·çH™»%\HÌÓƒëëxÞŸP&R*V’¼ë?á1þÇÍÝ^KRjªþ€{7ä—¶]µgšï«ù B€?ýŽÅˆ3âŽK,% Sê+7]Ò•ë1‰‹˜”ÈũԳDE~ &‘‰&æJ¥­dgôå:Lb)®‰©%¸ØRú—VF«4¦æüCvû°-H·!ÛÎÔ¹|öú½û7œÜsfì]£kUs-ʳí8kO›ñù帥LstÄe='÷/6žèX9õkHÙ=nAÚm+û¸½ùM1¡L.â²”—LÚqöë¢übÚÔ†w{Lðf77§Î+ùé*ÌË䜙5åF‹ ë†úÖÈÅú5ìáA&@³„ãË_„Ç€¯›Õáy½Î]‹,ÊJ¾ujQpãÚ6Ç B#yº1`éÿ+g™-KzT öô”Y¸{[jbïf8Ý‹ôRž­»µ•«¹:%ñÕÖ™„©£=¿(öùË$i>vÄ’Ó——ëàaU×Á’lÙÃãf]÷š³}a{ë7;nœonçhoA=;z¥Ì«k“j˜]ñ9×O ¬kÉ¡(£„%‚À4ùÙZ©³½”÷ú ¸6áÂoÐ2¸ž) #I‚ Ã?ÿúé·%*aåßÑC{÷—/^eް¥Eò<›ºIÁª•¶B ŢñäÞŽDnt>ßR&—W\–&‚’œ®LWQ KS,{kMJ’Þôå;ers±ØÊÕ\š\ò–ŸWãØOæYK¬ˆÍPsú¬è<Ì¡®½ÄÆ×‰È‹ÎÑpꌸ‹•Ä¡¾—S‘åÏ”¦$)ͼÜLjb¸€I\|¬Ô©•­•ÍÚùÚ >h7þ‡Q–:ø÷3sù®C[C,Òosø’Ç—¾dê…s™æM‡;‹^8w#ü¹gÈ`Ö¶ÜÕ< VO¨ÈÖZÖ±~þCîÞÙ‰tžÕ?ü›ecgŒÐÔ‘(|þÓÖó¥–ÁÃ-„z¿Zø©½»NKx çÚ•N»`fM‡w“NXºU;®C-aYòó—Šz¯œ^»\qöÇÃú9)n »¢fZðÝz ñ=üÃŒ¥â©ýüåtγkg¹Î˜ß¥åÉ­Ë–|Y›Š¿¼{o.åUmœŠ5¨Õ•–áXƒZ©T›H$|‘÷m$c·l=mñ…›ââêKºÆ³›Êyæþ}›Ð‹6lo>½›<ãȦ¢–këKy’Ö½}¶íX½×wJsÁ³=?'Xw™ã!„šˆÀ½kwû#?m8æö•¯þÎÖCùîß´w Á°eà ò%g·÷°¬Ên8GkTZ¥ÆÀq´F¥T"©èA*cßì%­úvi` wâÔfžîÕ›aN—yÿa®@lx~kïÖ#†®k×zâÊoÌžtÚ±{¯@wAÞaéNæ×P/~¿÷œ²9ŽoÞÓlþªFbì ý€@ü£mþÒÿÚ¹×ØªÏŽãÏé…KÛÁÚŽk™Eìæ]ƒ„0ÂV©E„Õ ªâ¸ à.¥t×RnA\¹étŒˆJ‚fY½Ìx™qјgÆ6Ýt8X€…â &YðÿÿÃÒ²Ïç/ §=yÒÐóíóëéÓóÆƒ‡þõÒ+»0˜È á‹CrN=ðÂÑJ¥®ï’ß»[QZ<ÑNý§Î©x~Eí”v^S×7Î ¶Ó¯íi˜µâpkþÀÊúõ÷öïBèWµ¦þØ’µ‹füøìu7­~´zX—TjÀ앳߬{lÑüsÝ?UY9¦û–!¤ºŽx ~jí’Mÿ>«hĤñÃ_xü²¿c˜‘ßpÁ¯~²¡vãÑ3]KÊçytmºïùŒ©¼Ás·öÚ¸açŠéÍ!„r?=cå7ç¥Ry£¼ïÙù–=¸§Ç “'¯{öÂænmÈmØ´vúÇCfÁÇo;­O‡ŽÝ¦,®z©nKÝÜN%ŸŸò•Û^ÝBYEVo<·vÝ÷L_Ûr‹•ßsg~ÎͳWÎz³®qaõ¹îƒÆ}¡ü†ïþ½½|M~qÃÄÉ»‡«ÓËPx÷¶UÈñPÍŸÎdtí7êžo-¯žŸqÙ_ëwlÇ–;ŸëÐkè„åk¦ÝÒ)„pöõÝKç=ývȽqheý–™e=¼ßRû•꿨é*?DÝÍG***%i¨©©iâĉiõ”v…d/Ä&„¶YMœ|®fÜýaùÓ Cr“=°eã¤/ýáË{¶ëñ®ï%Í{ëïüÆ¡ÅO­™×Ï¡ý»~ß ’îÏ¿~êð‘·¶Vºª¿„7þùÆè²Ñ—þ·æ¿,;÷à}»7®ÐÙfWöÖ¦¦¦¥/^Íçuÿ@šJÛý¼Úò÷§ÛáÑ\NÇ–ÝšýÐÎ?ï6¢óÁÿ•.ñV-mÈ÷-Ò7©Ós¿t•Úê>váߟvÉdäß>ÿá»æ7,˜ýd¸~Ôº‘Ÿ)éœr*úÒôý—®Q9Ã~ñ»+y`vÉ´]ÏM{ïßv.­}æ·möڟ쌳§ZÎ?>¯ƒ~¸„æSÍþïû˜euU½mTµ“ø ØŽ’¦ ת[nÈ8ôÖñÓ§Nêí?áKÄÑ#Gzöìé(Ò“ûÒÔµº_‚ÂWï(^²{—ó§K»ž|å@«y·Ì¬ÌÞ½z÷-îë(ô$Nˆ`¿Äµè¦ž¹ßŸYêh\“¦ì—ôÄua¹$$ôÄMˆ`¿  ’kýqÙ/èH–Á~ @?@$×úâ²_Ð,!‚ý€~€H®ôÄe¿  YBû%ý‘\;èˆË~ @?@²„öKú"¹vЗý€~€d ì—ôDrí  .û%ýÉ"Ø/èˆäÚ@?@\öKú’%D°_Ðɵ€~€¸ì—ô$Kˆ`¿  ’kýqÙ/èH–Á~ @?@$×úâ²_Ð,!‚ý€~€H®ôÄe¿  YBû%ý‘\;èˆË~ @?@²„öKú"¹vЗý€~€d ì—ôDrí  .û%ýÉ"Ø/èˆäÚ@?@\öKú’%D°_Ðɵ€~€¸ì—ô$Kˆ`¿  ’kýqÙ/èH–Á~ @?@$×úâ²_Ð,!‚ý€~€H®ôÄe¿  YBû%ý‘\;¤¡,G@zº°_ºøç•}ŠŠ ×ï#÷¤uBû%ý‘ì—ôÄåý—ô$Kˆ`¿  ’kýqÙ/èH–Á~ @?@$×úâ²_Ð,!‚ý€~€H®ôÄe¿  YBû%ý‘\;èˆË~ @?@²„öKúh²Þ—ÒÔÔä(@?D[úr¡s€û%@?úЀ~ô ô ýè@?ú@?úÐÀé¿éEeì)‰°jIEND®B`‚PyMeasure-0.5/docs/tutorial/pymeasure-managedwindow-queued.png0000644000175000017500000025401013137471263025156 0ustar colincolin00000000000000‰PNG  IHDR\†ø  pHYs  šœtIMEà"5›lâ IDATxÚìÝu\TYðçÞé¡iDìµ;°°»;Ö®uín×îX{-ÖÂÆ|×\[±QQ@:†©{Þ?fHÁUÁ]Äß÷³†{ÏܹsçÌyÎyι\±)§3b@̈1 fÄ €˜3 fÄ €˜3b@̈1 f@Ì_@œ3¥˜‹‹YF•åÔ¶bN0 HÐêC5çßǽæŠýÇ1ƒÄUÛC[§¸ÇO?ý„7 WizûöñkoyëðŸÅ 61¤Ô«YcAð–ä*¥K—vwwçöØüÚšç“Õ*æ¢T*0äBŒ1…BÑ \©‹=M,ô%dwòAq¦*S¦ c o@®UªT©JæÒ¯Û7»ã –¤7Ä.xr3'“ÿ(fñœ ˆr9¹è+“Œr`Ý$Ä yXÎÄ 8ðEôÎ/_zòYÖ<ÔÛNŒÇc 2|‚êÍ­sWï=§%"Y¾že«T­RÒQúáìòåg#HìÙeL'9‘>âÂÊ¥'Éwï4®«ãƒµó¾M-ƲÖÀ!õìSêû¤Ggíx™þ™”åzhé"ý_#Ž#"âxbøøLÁÁÁoÞ¼ñòòâ8Îп|ù²‹‹‹³³snA04ä}ÌÝýkÿ¸Ÿúˆ:úͳD˜ìýñ†j—ã n–ö‘©cþ˜ˆx""…¥½ƒ…$m÷ˤʖH¸¹(åÉCÌ?–­[·Q×®]¿tǰ°°ÀÀ@CŽ——cìÒ¥KÏŸ?þüyÆ mllr{ÌÀó<Þ~€$ÄÝó?x/žˆL‹7íÔÒ«…HHüðôf`à iºæ½1fHÛêVxú)ühÝ”­Ï‰ìëö\ÍB¯Ñ}Ü,wh>nh5KCõÍ4®ÿ>ûÇI$ól;¤}qñë£+7]‹%q¡f}-¯î9|'$*Qˈ“[,Q­QýröráÃ…UËO “âÕ‹%Þ¹ù2V”¿x}ß:ù8~ùI$³*Ñȯe‰Þ¸™²HOÕ½Û¯ã9‹Âµ|}k8¦ ú˜  ÇN_{š '™•{yï¦u‹["c ò°¯è.±±±)T¨Ð³gÏžâmŒ¶¤ÜøVuú m`Oo,X~1&þáÐ –)!Nô½Ó7ã‰ìêù6)mÊÅ[G¬ xÿáöÝðê¶ö ÏØ±cGÚ¥ƒ¶mÛfø¡sçΟ_ˆZ­®X±â›7o¢££‰ÈÊʪB… jµú[<ÖZÈ…qÆö´N˜ hµÉÑh„´A@rÌ@B–þ‘teK“›ÄtI‰j½^^¬yË¢Ïö<Ò"†V¥”z‘ ~wéà¶kCSŸU/0f`–*%ÚDÌÚ”(¤&2]b’$¿)Q éui•iU*•ØÔÕ†»Ãâ#ô–)Rä‹DD¡§Ö.9•r˜ªh¾] —ؾ}{¦wêÔé jö,ª´/ªêA¸pá‚!` ¢ÈÈÈ3gÎT­Zõ[gý fÈ…x3{K¢8bï…$v«3rnCݳ3ÖÞVGÄ/扈ô­ž1F$hÕ:""^̳ôAÄÇcÁ)¿èTññ’´_2L›¤MîÓ'Æ&¨¹Xù×®íJÔªZÔâýiÿëÑéãÆ–2›¥Ì²Î0ñ‚ zžˆˆO½«Pj„c]­Mý”µ›8±…oÈݾ¨–êӧᇵk×Qß¾} ¿ÆÇÇ~«ûÊ•+¯^½"¢bÅŠéõú   W¯^qW¥J•o6àþ ¹1fPºWp?×'þýÇü­U,”S§~3‰Ll­$¦e¯¯Þ u-o­ þûn$‘…£¹èë«ó˜»GŽ3bYÈ©ƒ× wó2‹zJDœ»O+ï"¢—/fõÅùYÏÿüö[FDV­R¿Dù\óÓÃ÷ªµ©[:¿˜HH|sûv¬ÖÅFŽKrƒ”ö}ŸßÜÿxã/Ú× ""âõë×DäááQ¹reŽãôzý³gÏ‚ƒƒ=<þLKdV©u ;jíuk하·güoyvéÐ8|ÏÉG‘ïÃtªVÑžú_D†/»tGú‘R~[[Ëâ^ $¶*Z×·u)!<Í&’B-†ô´=~êêƒà¨ˆ0"¹UÁR• ›!7 ò$èEllì—î¨ÓélmmœœT*ãòy*•ªZµjZ­V§Ó}ÓcæŠM ÈÎþ“ŠDÔ®][©TâíÈù:Z,•ˤ±ˆ#"b‚^§ÓjÔj`ø£L.—JD±N7Ó&ÄÄdö‡Ø4;éTq1)E«âR—ñK}Fu\Œ:M\’æ—Ì@HŠ‹§dÒìN$èÔ‰ñj¼å¹RÎÌgÀy€,¾#R¿,ð}€˜ #>_…^ã*àû1¾ò,§3ÀwÆÊÊ '1@–"## a‚Ä Y† )ÿb€t0¼€˜àS›€˜àŸÃBnb€Lax ïÅ ,âÄpoŸ¦†ÿ~ŒÍþmÞX̹aÍ»¯zªùøOªÛs›'?W#¿Á¿n¿þA÷oŸ¸Odr“rq•ÃYÕ½·ÀöÁ£ÿò^4¿ƒ»÷ͼPïÙCËJCœX·aÊxÑÊ•\$x?óVØ@Ÿ—›tãµféù¸«/“pÒ2¨ä*ïãeZÃM†S¹!f N,7UJyŽ—›˜(Ä8¦ýZûþ-õ‡w¾ÔXWê:ñ_¥öÉÊ^£nÕíäyûP@PŒ¼`ÝA7* zïÿsw¯Õ«:ºJˆTwgu›š8b²ãŠ÷uthë}D®}Ö­nåîXy¥£›‡§yó´ ¾5üì_ï}bO]tâIXœŽd6¥šÙ«Š5…ìïßÿD•¡õÞîÙyŤ߆AQËæøß ‰ÕØÒ³^ßQýê;IbǶ_kÛ³Aܽ7ÂÍKv™0²Ú«-³7ž{Ål8xÊðÚ6bbê×gV-Ùrüa¤NáTµý°Ñ-$Û§?¼Š»»[¶ï¯×‰"«b ûê]×Asf·)šÏn8ü¾êœå¾![æm:ù(Zç/T±õ¨ñ­ JqfÁÊÊê3g2œxœtò±ª¢‡uÝÒˆ3ŠSi܉ŒV ÍJ(p6à«}Ãù º¨3ÿgÖôç_ƶ±¹½iéáCþþÅÁóúÊÝ'ï_[8»pΟo2M+’¸ö˜;¨ˆÈªéÌÍûvîXÒÄ>ëà†ãEÝ6vðøSŠfÃÇ®œ°jÛ±ØKƯ .3påæMëGWØ9ã·{¶ÝÓžuÈþ©“O*ÛL]·sÜn¶ÿ›?óÏ×:"¯­Üò²€O·.µ¹€éËþ²ë?7ÉL¢Åéú‡S$Æ)€\3¤â¸ÌæD3¦gœXjhS3á Ëtë;oD% …¥­…”#Ò¿?‹½y…}AWW³”ßµï¢?·)o§ b‚À>^íUûáÁ [›VÕœMD†m>ë8%¶ž¶ÂÍGQ oçäy¤L`Ÿzx¼YÁBÊèG!œE+Qr<ĸôËø07w¯ÑʽF«ná#;n¸¬®’OŽ+ðSa}Fn’¥ âŠdšè˜èÛwo«5jœ/%“ÊÊ”.canSˆr"Ð%Å'j&$%$¨tfYo¨{}ñä%« ÷÷-¿ ©5½œ9/u+aurÛÁ’Í£®îßv>Q¨BÄÉò;›D];wí!—OgâVÒIñÉNy±­‡­ðûÁ½Ê²²×g·oz¤³*Ÿ±ñoéî ÚwìÀI§ª–®î^=‰ÿœY"Ûêm«n3mŽÕ¶•¹ˆÇº"i÷k»´‡çܦ©Ã€ÕÓ~£ =Í’^ß>}âyå±CLÒ”’xkÉä³.ÍWt3¿}+\äXϳv³öù¹I–R´ƒÿéIÔ/^½¨Qµ†L†EW¿˜Z­~òü‰V§µ¶²ÆÙÄ Ù "ÏŒo¿ä mÞéöØíS²ÞTøpeõ/›ÞkM Õ8§3ŽÈ­Ý˜vOçlŸ;IæZ¯m‡Ê¯wÉŠtêSçÞ²ÙC™UþÛ¬Š}~î{îæ¹¿ì´*Þ¬U‹B¯.dÜ„³ª9dàëL:lV¸^ûÖÅú¬×Æ[V;ØÚU»æÙ¡&©MѪ-ºYKd¦é¯ãÌÙâÕk·N=+™s…:¾® .Ù„þLì‹÷Ê8!ó'eÙ8îs÷aÄ¡ƒ1äFŸŸ›ô)¼´ò¨¥C‹‰ãC®íZ¼vø8ËkÛ9ÿ—doVÔÝý{wÃ]©õowt˜@Ô¿nU"ÛN›¶¶~¶2Ýî~iZ²Ú¿ÏØ\~Ü‹›;‰‰ˆJÿT©a›ÎOßÉDêG |ûÝé8¾ò_k6ÿí¶ho—«¦ýùè}¬–döåÚøuHM[z³Ã¯ÃþŸzU >à=ŒsªÙîäÅÅDÄ4ï/.´nÛpE‘–¿ÎQß.˳ ™´þ‘Àˆ“”ì:º“{òñò&Ž&$ŒKÙˆK×væÒíŸZ—îw–º¡Ø¡qßA±,x–n53öq+>óâ2:Æ¿01&d82Á¸‡À—þ%d<þÌ€}üWöUã1ˆ¾±l /dÖtz’äSŠˆô¡‡' ß(î2k×\—¤;[&ͱ®è®V7ÇξPhäò_ËÊÂ^ x¥ýd3Qäà»|ÖÖÓEÓ¶O(§‰£õʸ;¥Ä šà3G_åó™Pß)í§M’¿° ‘šˆ´÷–.å60´Œ»DòªB»‰ý~r1S??ºpò”EÍö""]ÈÃ=»Oî ¾¿yÁo£Ö—=ПˆtVnvèÝoü©%‹§/«å5³²iV1céúõ¹4{NdáRسˆ<¥¥¬ ùsÔ˜½² ¦zÛˆ„è G­Tù-ê­š3údÑ6eÞ:s/’·«ÐnÔ Æî Ž„Ø;ׯûóÆ[•(_‘:=u©a§¿³xÐ\_»‹ÛO†Uœð³Éï«ï·YZÁæÝÆá“ï×õs»¶ïô+½SõžãÚ›YµîЃø|%[Ѳˆ’²*mŽÊ·³íåݧžÅ™{¶2¼“ç‡Íc—?ÒÑ£ñÝü‰\º,^ØÄNÄRã"&¤YM™1ÒGßÙ»zóŸ‡&Iò—ðî<¸ce; éãX½vß÷j©µ³EÔ³îkfÔÍϧ{ë9b 1 fÀ)€\(gr“Œôq/Ï­Úð £Å¥LHûòèæ»#÷úU±â‰¼ûõÞï¿êܳ*a¡Ì¾CµÒ…íøÂ…Šyù©n~ªH^jj*åE"³|ù,•”øüiÆÝÓІ? %§2²Zç”/:zÓÆ6Æ<šÎýJ ‰ù&ªV£À§~ÐyIˆ$…†,žÞÝELTÓ%ôbûÃ'_ô¬O$.ùËš¹Mòó¤s{s0àнPmeSÉ'‚JRºæ1"¦×ët:ãÃ/r¬? eàèÛ¯V\âå®M÷œ:Í©iC¤?ÕºåÀÆâ§7mŸ»·èoÃþœ?ïœc—1‹Ê[ÆÜܳtù"›B3¼ÆT·6í*Q«^ûÚ?ÙˆnñÄé_8çÚ©÷hÉ­Vºe[º±ß¨:o­ûcíùj³Z¾;<î9Ç®c•Ë—\Úto Iwvž¨Ù®×(Ÿ°Ów¬:赸£ß¤žGíw;ͯ ”W˜r,完1"FŒ%GD¤ ñŸ³À_ÖlØÌ*¶q·w­X6UmrC˜7n!q(á,Ù¡%"N,qDD¼ÒJÉëTÚ¬[¶c\º ~.õ°„¤Ë³ú\NÙÔ½ÏÚ©5]|z79?mýïGË>úŸUëéõmyÊHìÔqôà–"¢rönŽ ¼ÜRwêxx¹þ㼋(ˆljû6>2öòíˆZŽDÒRƒç­dÎi_Ý`DŒ11&òè3n@ NïrüÂÉòÃF´rS;wteP˜¦VÔ‘cáåû÷.¢ ²­Ó®ñÑ1—oGÕv$NVnØ´~eLˆ4Myü<޹ÊebŽ—*MLM¥D,M†’  iÇ´¯Î vh· eE;‘cßηm xXÙáà}‹V³ºÔu¹*žøÿ›K~§˜ñ,1Ž¥Ÿà€˜ wÈ™Ü$^Zeìòa.VŽ^t?^¬pDÄôz޴ΜÍ#J¥,–*’Yä3-»ks£'Ïÿõ¿ÍSvo¹0gÿÓÏï_æóUša÷é5­Œ­|’X»ÛÐݿߪ›[+HYvÒÖñºð£“ÆÊXŒ>Äÿ× û¸ž ÿèVÉQu´{Ë?2¾”TÿGÈX†Ð,%˜ N\²ëè”ù¼ÂÎŒÉÜÚô®uiö®3ZÏoà bLÏñœ¡M-²v³ÿü,*îù¼AWEÆL£Í‘(82N$WŠ]þÆcD/âcDR¥„çxN`Œ/Sˆ­^ûîYTÜóyÓ•– wd/vãdæ N—¤Ÿüº2¼IÉã L`)Mý¤ðÑr7sÞ°±I÷|ª{¯^Å„ËÝŠ[‹’&2”Ç%OOž€˜ WÉ¡Ü$±¹“»gÙâÓç½é2pòˆÂÖv),s,n›x.Hm^§€$¥ΘÀ,ŠÔó+RÏo@¨Ÿ&¿{¡m¥ oâ4OL¯Ñgl6r< :½±å-pvW×´R7”ºÔövؼs݉^‹[8IL‹%½ù-ùÇ]ך··^Qɉª8*‰„”\›t’^^¡/ÐÉé oè,ú,‚ qb3‡‚î…äi·ˆ‘H""¢¤x•V/ˆ8A`Œˆ ‚ pD¤×3ID‚NY55¡eÊ\ N,7=1F7Là’#/Æ’g+ ‚ ˜ ‚@ã8"AÐ\šHnÊ?IÞK Xò~ÆPÄðpòæ†0Èø|BJÌ ”v[þ‚^'p<1Â`ŒlRG'2„WˆrcØ@Ù]7É€7/7héȧ~ GÎ-´yR•æKíš7zºrxÛŠÖº·wκé:¨ÅƒE§ µmYÕÃ,îÆÕP‘sc{Çrîü-šT?:ºnã ]Át…Jó»Y%8ræ¶… ÓEø¯¸à–n÷´ëJ w×êä°¹]F¼ܾº‡Ňݻþáã@bWÌAX·kÛI“J²—ëWÜÓZW6üEzéÈÙ¢UíµAþó÷Ä{M®ã úðE§ Ë“ä<œŒ$=ß¿.PѰ]åË{ש6«µ‹a†qr?¼&äÁÁ¶±m>'gy̳÷dZÜB”òLœÚØj7,”d ’'dó`É›§<9gò‰Ò [&OQ`ÄÇ‘ ×§g0ÉšÒú—Z»Z&Ý}­+f'"bñÁÏbäŽN62SÕ‹7±úbÖ|jÁæ. f€ïDN¯›$qm3}ÞãnÃ&Lôغ¤ÃüúÅK¶Lè·XK&Neëwhbëb,¿¹Þ IDATê¾qjß• $².ÕbÊôæN©Í˜‘—Æý6cŒ¿]Ŷ]:\r)C‰-Gµ»4qÆ€£Šç´rß’nwQº Å¢òÈíëÜW¬ÝûÛØ1z’Ú­ÖhÄä¶®z‘öÃèÜjÂÏOZ9iØ&ëŸ|ýÚ~~:¥1}}ÈMÁ‰²µú/œRßš×~iÌ dzo2&0ÆtѯßçŒQÇ+Üí"l8.Ô™Ôª•˃‘7Tߘg¤‹¸uñjÁÒùu¯Îo=©*Õ§¼•BÙ¸žíÄíK7‘o-7Ó¤w/.; ›ÒÚߥ6 Œ ††½a0@à’Ûë2WŸz¶2/‚ãBªŒ‘4Ÿƒ"æïË·Ÿ¹NáâiŸ2pÃ#Aõ>èÑÆ”-^áàR½¾óÉ}ëÙûU¶Ž»½wGP¾¿-¤«`vn÷–æõ êßÜ:¦³§ss 3@Iæu6\®“¦ÝnYãÿ›¿~)ÙqòúŽ“Ón]tÌo Ǥ/@Z°ùÌÍgÒq(ÕYyÕXjþ#6ž‘¼ueŸ1Ÿ:Þ¢Të ¿µžññ¢£þ Lù…Sí2o—”ß; "Ý"‰}“É[ºHói•¥Ý‘ÏßxÝ_?3DœùM ˜îá®Å3Rqë3ö§€£ñ^ڸɈ«Ô¡ÍÉ_÷nºP¾;#bñ-òM’Ú–k=¬_% ^ —æ£Æˆ¶ïÜ·ôt¼@&¥½|œd,ÒØ®7&#×1J^ÆÈ0ãÀ4ºöÙD—æ£ÇŠ~ß‘ZZã2aˆ@ŒaFrü!qmÙ¾JÐÖ•¿ž4)ÝkÊh[ŸúŠˆ"ή˜w6ùè2ïׯ?NܰcùÔ?5"KÏ:}Æ·v•Š…ƒÛD®9´l¡Î´PµZ%-^E‰¹ä?¤¾)bøáqŦdgÿIE"6lˆóÿ•gNtðí‡_ îÍ?¿#Ív¦¾Ä®?v•.Vš}Nb~êÔ¸ ‰üú°€‰“.לþk[ã²Êòç¾,ý?ëû«}Ã*Ïx· Ϫ~´qü2]¿E}=åéãˆ1v÷áÝuàãß»€€€éAù¿bGŒ3@n”Ó¹I?:!åîÈ™¶ÚS¤Ü-íº¬ÄÒÎdŒËq)›s©ûf¼gs&7CcÌÐO-p”ÉÝ—3þÆ}|?ç/—æFÏš'=2÷(l'Oz}eçU®Ì0g Ëx¯jŒ1 f€\*GïéöD tÜ{¡c6 I^N”ûD;:Ýÿ¹L›úi#eíÅeŒ$·¸Y¦¥¥L°f>ŠcRÇC¸Ìö`Ÿ|Fz퇻‡÷‰Ó‘Ȭ`eßÁ=ä”vÙ$.ý1 fÈ…aå̺I`\I4“¶oVáÛðÖÞ3Vx'õÏû°ÌÿG,«ƒøÜVz–¡ ÷‰Â¤îíÆ/k—î<±Œã+”<‰1@.ƒá…œÅ˜îòìt³³.ïsŸå\Q€˜r#ä&åtÌÀ#îã60—+Ž.‹Ç¹Üsþp b€\6r“rˆÀ k}”­“a†÷YÍdîsÛû,‹Í¸t?±k©³¬#î«ÊÈe¡ b€40¼³²¾+c_ÑJÿŒÍ2N·N÷<,«ÍX tî“GU4ò8dîS¯†Ëøcˆ3@n„ܤœõôÅœ„l:qæN|¾Å=…3@î ¹I9¡}›ö8 ?ˆÝûv‹b3@nôEà ·Ýø–xœÈ… YI†àg1@æa!7 1@¦0¼€˜àS›€˜àŸÃBnb€Lax1QÜ¥áMÛθô­ÊW?\ä×ÔÛ§©·OSï.kƒ4šÇ«»6}!6{Ër&ÞšÙ¼íœ[*""RÝÕ¶i÷¯u¸Žrr“3|{2ÏA6N(ÉKÊß¹¦{aé7x ©s‹¡#ûVµá:ú6a!7 Èe÷tÓ¼;·i隣÷µr§Š-‡ ó«`%"–ô<`Í­§G f®5:ä[ÌT²¿ÿ#Åý*¼=pûçP¥ë¤Q-‹(¹´áLi¢s¼Da¢”ÄŽäI4·u“¹¤¨>ÿ÷±Å?œYµdËñ‡‘:…SÕöÃF·-®x·¿ÿU†Ö{»gç“~E-›ã7$VCbKÏz}Gõ«Ã:ñJ"Ñè6M‰¬Û,/Z¿ôv§^Mô‘7¶.Y½ïú»$‰uiŸÞczV·—¨nÎì6YÕ©—}àÖ£A±Å;Œ™Ø³¬Eÿýûâ{®½K›9¯×LO/+Õűí§7\ºyH ®I¢/¼§|S¹jœ!éá–_fžU¶úuéú¥#k'üeªÿ‹½¾|ôo·;Ï\»jþ R¯7ü²àt„@D¤ =v*²d‡‘S†5±¸¹aÊö§ê,KæÌ«ŸÛ0ŸÈsȦÛ÷mVRscÉøuÁe®Ü¼iýèª;güv3ž‘îÍ®•\…vý}‹›‰-Š5ê=mÑ²Õ Ç6•.\e×tö„ rE¥É·ïÛµ¼‡krÄ¥{³wò´}ªZã–®\ýk3åÙ¹c¶=Q ª››ŽhªþulOÏà]‹v?Õ$Ü\5ggTõñËV­™5´e¡¤Ð=q|§y›`âQ¾lag%9»—ô"""IµÙþ§p1~6r“3¤¥yœdUÉÕÄ`$¶ôpSD<~&¼ì«9) IJ9r'‚µ剈xc.’ÄÖÓIüÇÛ(ÑgÍ[мðVõb}¯6[ è4¡t‚@DœÔTnìï×GÝü}éš=Wߪ9¹¹2I'* d1Zú4RQÈÓÒp29SWO+ÕßÁñ‚q¼Xb(N¤È§ä´I‚e…Z…Wn×íye¯òe«Ö©WÙYÁá2ür“3ä0&$’K>»õÍô$.1lÍør&)IÌ”q!i¶ˆ¹¼tÖïï¼'­YTÍÙD}cZûù_t@=Âb±‹ïœ­'.ݸyzíÑ]Ç»­\ÜÅU‚ 1CnRÊ¿8!ÿ¡\”C/²(XPùàe¼¡½­‹ z¦²*â`[ÄïÊð¨:äö[fëa“¾•­~};Xp(nÿÉAŽçIÐ DD[O[áí£(…U KqºˆCûáÁ [“VÕœMDDLHcàIÐéÓ—,³+’_õüq”aÍUÿâq¤ÂÙÙ”Ï<º!™}™]_²z¶O¾W—îE ¸ ³¹I¹À:ÎÀ´‘ÏïÝáå†ß$VέX ]»|¯EçÊæ¡§×ìxãÖ±QAk ßêâÉ+VÞ®„øÙ‘ßþL(=²ªHC¤ ¿zú’G[í³€þ FV·K·è© NLP阠U%$jäJ±¥‹•pæô§bÙyµ­ºuδ9VCÚVvä"ÿuꊤ݄ziö–Xº;ˆö;pÒ©ªå‡«»×_Oâë‘Ôª ¥êèɋ̱½ñÈ¥® Z¸ùo]²Ë©OM»˜k›ÖÝÏ_aQ=øø%ëÞü1u[´WÓ:Åm¸·×Ÿ$Z¸4ã)áÒx¿ÉÁ —n„u“ 0¼€˜Á3è2"ù7e­Y»‡Ï˜ [¼fêе:©Cù–ÓÆ·psT~à¼A«nÐ'–™¸Të1}°·5¯!"û÷Ž_w¾UIªv:º¶UºŽ}õã½F!¢Ù~ýš¯ÜÐ×£FïÎçf­?\\ ùâßúŒ?líª]óÆìP“Ô¦hÕݬÓ]pV5‡ ¼1cý‚I‡Í ×kߺøãCDDbçÆ[\›µxÌi¹gïù}“ÏbßÉ¿$,Y=kØ.µ8)ŸQs»‘“*“—,²,^BºrÛÔ=‘:R¨Öq|¿’ ¢&$`¼!Õ˜›$Bð›à—¯^Æ'ÄãȵLML] ººpáóÊbw¨|Pù|®Ø”€ìì?©HDÆ ÿýãÖ…ìïßÿTƒÕËÚ9ŠqýÈNœ9ѾMû<ðB^¿|òÖÓÃ3¿U~¼­¹VDdÄã'\]\óÆ+BåÇ*ŸÝûv7¨Û «¿Lúš;Üý€¹I/_½,Y¼¤…¹…F£ÁkY˜[xzxÞ{p/ïÄ ¨|Pù f€ïÔ˜›Ÿoei¥V«ñîüË®\¹âååõ™ ‚`ei•—ÒxPù òÉË1ƒØ±õzÿÖ¸ÚòvØ@?ÒºIÇc o=À¿Œ1ö¥=Õü8•Æ 7ú1×MAÀTx€\ÿÑË{ŸST>¨|3ÀwéÌMâ8Ž1†¯m€ß—~ôcylœáÛU>I÷wÿaì®™^&¹ñµëBöõés²Ñºåí±ž  òù'<Þ0ȵaåDnÒ¹-Ê7sÍx«@Ò‡\×{ÀŸïõ™|·-làU­b¿£aŸøüªnM¬_­õÆ—ÚÿÚ&Ž Ž«Ó°±á¿!çbâoLoÒzÖD–BôÙÁMº¬R3Æ‹¿0ªq¡'> ¾Gÿüµ½¾SZãÎE&×',þÖ¬f5Û®ªÉ½•c±†&W>uùvùeó•pm¶Î±äü‹ìS­íZ§aã6Kï&ÐoëÕ¼ïá0}vrîrGåó °†Ü(‡Êöá}j¬m­vð” 1——ÿv£PŸ]ìE7T? ¸eš_ôððÕÈFM­³§¥Ûû5ºµ(çûúRûòÕ™µ×iûàÑy/šßÁÝ„ÝeDŒe/w€ ¥–"ñè0rœPL™æçèêûüí?ç[ÛotË}–®¿Witi%Gš§»ä[¬ös—æâʇFœ´ÜÀ齋ˆÞß>¸vÛÄi›4wúÚÆ‚¡Ša9“ú$Œo{jË™ö³|lD$0–\¯eãðR³ U>g€kÁÊÊÊ|hùÐíóþÖ&ÜÙ0û\þžã[”|´úùÑóqå «¯|è-J "Õƒ%¾ÕjMŒˆH÷zO_¯†“Ά‡ŸýmÖ†IDBüƒÝã:5*ïU­¢wëî3½Õ’æÉʦ^Õºþð_>i»úH$3QHxŽ—)•âÐC'^IL¸8ºMSoŸî«ž¨…ø ýó‡¶iÒÔÛǯïü#ϦZÑ¥åàýg6þÒ£a«ÉÿûpwÕÈž-›7õöiÚ¤ÛøÕWµꠕƒÜ×}Ø7´µ·OÓÞû_¿ X½øÐkcLrvõ8¿M½}|»NþýZ„Ž1!:pt£N î×»MSïfÝ'ì JÀˆüÖmX}ðоø„ø”Gýy`݆Õ_ÑÕgRºÇÈš‰{ì©%ý»có¬3²g.»•KzqxN×&5Ê{U«Þ¬Û˜]Ï5”c•cŒ8‘¹“›GaÏ2ÕÛïU‚ž\~šÈ˜>âÂ’Ÿ;ø6÷öiêݦïÄw¢õÌPó Ø}|ÍØ® |š6¼<0LËcÚ°‹ë'ø5kêݤӰ wbµ‡{ÿ´¼}š6è8|Áñ‰‚±„þÛ.ÜÎÛ§uù§‚ß_]7¶KŸ–&ìz/|<( ñhÕÖ9hÛAªt£ñǶl³äáAõ³Mš ˆÔGŽnÔiþž-“ýš6õî8nóݰ'‡ç÷jÓÔÛwмs†c%"]øµßÇvoéíÓºëôƒA‰c쟫Ù|\ ÷V>g€.l œY7IdßhD¿?º¬š½úy¤¿¸íÊN…¥”IÈpòllÑÁÕ+[ß’ ;|#ªaƒüŠb½'´<>hñÚ;F9]œ¿6Økì¬ZV‘·;<^=~ùê“¶Ì(,„Ü <÷.ZO6ÄóÄ}e Α^¯Oéo0vÎ1·kp¤nízÎFGG™›[Ô­ãv=ÄÏïê;5ë··óŒÁW9û~[9ˆr òÑ|xÊìÛW-ínÇ»*V¥qŽV>©=÷ºø×ÿÛº3ȲÖä¢ Æ¸|åÛ´'Ô1 W,g/¸2F"Ïa³'Ö³äHïüîx`À£ð8Ù£!ž½W÷®gÃyH®m;ÁHzÑÿ–©Ï’H‰\ú »ÝwêþÛ}‹Û22ex]KNïúúÏ3Ç*ßÁUJñìÔÁEÂÔMlß%ΤdÛvŽÃ¶zZ³«‚–KþÉø+cD$¶l4eB§b2J”^Üû@5xrÿjf¤ux¶ÿÂõç1ºbŒHìÜý—mEDåïö?uþE-ågT³Xœruå“×b†Ððз!oó™˜Ùæ³ÂE“g¨µš·!o‰ÈÁÎá« Éñu“˜!íUÄÈô£Ï›æeÀ™(ÏžòóÄo\Q<ÑÿfLoKž·¨6xdÍö¿.);aW};QÊÔie±fù)ãÚµ¯\£Z媚Ôö4ÏV¦ÇqisjÓô“ i2nIr?$ñÎüÞí¶Ói4J›xcÄËL$ÆeØ´¡¶­Xä^¸^¤4“&ê͵:½ÀÒvG1´Ñ/ƒÕ–œóø|n…‘A¡jA`$‹ŒÅÉ,ä,F£C¯äù®>©TÚ ~ã€Gcb¢úïgŒ™™™5¬ßX.“§½þAøì¥KäE;ýÜpÿ¨ÿÕÚ¾$³þŠ/®|änõkXŒžÙ¡c@ͪ^^ušÖ+e-¡«|#AøK‡@""‘[ãŸçõ(©d‚ $½<µqéï§ÇÄÄŒKлhtz1âDbNŽHf®àtª¤˜WÏâ-Ë6'c]c¨xÔ¡Aa¼}q{c÷¼Ô±˜àÿ,\cÈãEœ “˜HyŽ'&q2¥DÐèÒÏ3`ƺ³«ßÅkÏÜí}º§Ì–2Vp†‡ñbÞ1.UÊø$'1‰BÊé5ZA ”M9">a{ñ¾7ao´ÿ\ÍäöÊ'oÅ !!!Ö–ù­mÄ ®›N¾OIdÛlÞ.—ÓÇN^¹vnͤ­»Û¬ß1¼¬òë?ÉeŒR¾ …”Y~ :SzßÓSž¼'/3—¼»”ü}É‘>ôäÂyÇø¶Vú–±•ÅœÕooòˆ}Ú¡{CåC©á¥„i¾}"8"°,äå~¾”Ë[&“Õ¯ßèĉc±q±&&¦õë5”Ëå.~A>¿«S8º[ñ÷í-%™íñ•Ÿ¯ê”›j;yþ¯¿6OݽåÂì}ÓkZe£Ë"]å#Œ“”í?µ‡ã³m³7<‰ç¥<¦}µoúŠ+N=flmäiÅ…ì>ê23&%×<Œ8"ƽV ž#c‚ÀˆQjé©OC†n‘ÔÒ´ô—v{ã‰OžòÌ”%}[;ŒÜæÿ¢Vr]•v.³± K_• ŒŒG’\:XòŽéõ‰¤œ^ýOÕ,ÀwPù䩘!I“dgï Çz¹øRþº>+;{‡·áï³óÔ9𛤠>0wcdùC:”zðôÜÈyZn^B‘vƒ—§N‡ºv_>µŽaÁ’ø ¬:|#¦Ž·yìÿ–Ï¿àÞ£¿xךÙþÞ+ÛHÓá%ˆ­jÔå§F]†>ߨ¾ËÑKo‡”õÈFôË‘ ©¹Ié†Ú9ŽéõŒ1F[÷üª¿^hLªØKRß'mÊø<#Ò¼¿ÿ–Š jQÆFFŒéõÌX Ï‘ Ó'Ý1ÞÌÙEõèUœàjÉé"ž•\¤a—ïDÈ µiž¬öíú»Õă³\.ñ³ºú˜>).A#0!)>>Qk&¶(`¡:yúò&v¨ÝÂóȺÙËe=—¶Ô½ü¿S÷ú öH•àHdåf#ìùsÿyiéÛ {ÒYýÄ&±tRFß8ý˧3±3v¿Éܛձ³iÍ~³vÌÂ7ï qõ­ë$Þ¤Y1͈êwÈ“>^jS&“Õ«SŸ²¸ëjŽuõ}Uå“pmÖèWß–U=Ìân\ hl/¥œª|(µkž)‹uœÔóåÏg­.0§M¡| çýŸÞMxrf׎33$×<† P&È Õ/+™±aõ^}m»Ø‡§öŸ‹Ó—κr“Ÿv¬]ºÕ}`}ÝÃC+þ'ò_„…¥–`ÌÈd)£­ÑšfÜ€#“’mZ:ßúŠÜ™ bûê]{÷œÓNzx|ÏÑ}áôh¤0¼P–<Òʈ‘.üêé‹®åìô¯Îl8œPfPe{gÍ?U³ßyåóÝÅ †öåÐâÿ¯šïÿU ’#G›S¹Igÿö·{¿½Þ6<ñ¶ GôÞí·föáúkZ§#j^Ÿ=ñαI5û”oY®YqÝìCþ¿½úSÖ~M{W‰„ZOèp ëâ¥åº_£iáŠ6·Lê3?‰dý¦¯cÉë?±¯lYs¤×ûÈ"§õ\õŒˆöŒïswغY^Þ½ß\´rr ¬p—Ù“[™¤ß¼õÀÂI›t¤°+^­I-s.M÷ñöÞý»?\º}ÉŒ?,‹6jêS0ø 1Ƥn¾]«?\»xôQÓ²§µ2î õð›8B·j󜱛õR»Ÿ|& ÷q±ØLzÑ»è—Iî‰È¡¥K¾®ò‘¨ä¾qZ¿U $².ÕbÊôfN"ÒPŽT>”¦Û‰ü<æÅØ œ3dXëWKw-œÄÛ”mT¿¾ížg”î†h©É´\¯Ÿ}—¬ü}É5‰]ÙÆu~ :,c¼U‘£W¬Ý0öX"gæV§÷/}~2!mÚ¡…Ô§Îzœ!̓"û«î›}™ˆ1ÆÛ{÷ïö`ɮ߿“C…-j½ÙñöSã )?±˜[»gîWKì*¶;¬Z>Ž£ÆÿPÍ|ß•ÏWVŦdgÿIE"6løuû^¹zÅ«’WöÇ ·Q*•†7÷«Â¼µ IDATKøüá…gN´oÓ>œ´ÓgO/Z\£Ñàúø—]¿~½B… Ÿ¿½T*}ðèA½:õòÆËGåÇ*ŸÝûv7¨Û «¿LÊÿGûŸŽ3pDiú¤põ|w!oZ¦¦¦Þܯ–ãë&å~Ç¥½?ü›•Þ}ôôz}^!Gå€Ê'·Ç R²¾ú,ü8órü•fäÈ‘ý|»œ\7é;‘šÿnûÅé¨|à«|þûû@§44“oÆt±ÏüçÍ>WfêB߆yª,ñÙáuk÷^ QI¬Kûôê[Ê‚'bïì_½æÈÝ0ܱBËA}}Š˜pDºð«;—m=ý(J0-XµÓÀõ È8bê·ëWî |Ççó¨ße@·*6ßã°s-¾—¡†ï1ù*ûÇü£å&ÙÛÙ3Æ^¿yŠš ×R(.Î.övö¨|à‡ª|rå|.y‚ã8"}Ì«×IåLxŽ#’9xÚ²[AቂÞém*ÚÉ9Ž#Néä‘_séYt’8(Œl=ì¤G$6/XÈ$öé›D•äy”ܹP>1ÇImŠ:ðþCµœ#î>ý¯½¹_êÌMâyÞÉÑ©€S\<¹\ËáAå€Ê'·Ç ŸIPŨHj"5´Ay™™Œ©¢U:UtIM¤†9¼ÜLÆT‘*uB¬F¤”ïhÃËÍåL•˜ KdRgYr”ÜLª{§a¤ÌF³w–ÈôœäTQ?`nRžl‹*Èþûܤæ@1ÃŒó8ž#âEb‘aΰ‚ÏsD¼XÄóO>]\‘Ù;^ä&ä¹hœòR\„u“3䑦í÷â¿Z:é?„u“r‰ÿrõ ¥RÏeÛð>}_¯4>>^©Tf§+++|83½­ý»Ðwñññxò’øøøw¡ïìmí³Sˆ!+ Á@nð_æ&šƒoBÞ$%%áÈ3är¹ƒƒ•••^¯ÏfØ@ÈMøÁc½^Ÿ?~{{{LƒÎKcZ­V§Óe3žD´€˜ˆH§Óe³q yÖMÈ=xœȵa!7 1@¦0õ9­U«Vá$äa{÷îÅIÄ _ ë& føç°›€˜ S^@Ìð)ÈM€GÛ¶mq1ÀW† „Ü$Ä ™Âðü8°n fÈ1ú÷þ}›÷ÝŒÀý›{üw÷Ž»4¼ýì»Æ£0s*Z±Ak?ßÊ2.çŸJu÷ÿìÝg|U߯ñ3[Ò{o„jè"Jo‘*Å‚•¢7–[Ñ[{Å‚ŠŠ`¤ˆ¢`‡ª@@H•’„PÓ·Ì;Õá¡Oæõù´nç¿{uö®ìâÔ }p‡¡½2kî‡o>Ñ-ëÇ·?MÊëÙ¯Aæ– ' Ba8³iÓŘ!7k«÷ä¼Ý‹†=óÌýq«žÿÏ´Ey=™>u~ÛÜÏv\VDÞ¡ùO½·+ú®·¾]ðéëC´«ÞzwýyY!i4BíäuµId†k¸E¶ Éégoö7m|×&!ÁºŽ¾¯‹9iý‰üâpáÑbÔ]Ãz¶kâØ8¾k¸áÔÑ RX÷/l]{Ò „áôÆÍ› ¾!°ºMrïú¿7& ¾é¦[n¿)@vÇkOŒìÝ­ÿíCš˜OÉ4fïYºÖ<ø±‰½›…Guz×ÏCkå(³û?¯]øpkW¶¥ÚÄôÀy°nÔO§¾·dJ?˜’wâóûG.°Üe`2äv9Å…F˜¾iþsÞ{άõðqÉ5ûšdEÚupãùKן¼¿¡výæË±wwªvd’¤ÕIB!¹y¹h4:MÑß%³ÑlÌ6²S¨»Š,+…7:hCnØì³¯¿\ ËnûpŸÚv雇kWÊpÝÌW*~S îêŠSM/SÖ.üùhž"˜žjdÓ åŠùÊ™ä#‡ÿÙ¸úë·ŸôÙ‰¶÷?20Ô%¤ÇènÆß_~sѦýÉGüµêË×§/L.Z—HÒ,D>¼bÙ¦¿wo[9ëÅ9‡Š¾äMØå–fY{÷æuLhSûC’wܘ¾žÛÞ{áËõ»&ÿ³sí×3g|¸/Oˆœ-Ï íwÇǬ›T»œjÝ$}d¿{¦LÕÞ‹i+pN¬›õ³é<ƒbØõñÓ !$ϰVñw¿ôüÈΡ.BÿîO¿ýè¼9‹g>µ¨@¸·ìvÛ„ }áïh#›xà­ùoMÿ. öÖá·5>ùgQhðëÝD:Ù`Hl]|Ý‚äÙîÁYÏy|8î´å9Bë?àîp½Š, YfKª‹Ø X7 @¤V/®®ÉïÏh~~àÀªø(Ê•Í/=ð¾ïóß<ÞÆŽµsU¯MZ³~͘‘c*ùÅ‹7NõŸØœ±uÑ©î(ï~†9sæL™2…­Õ²e˘j@5ƼŠRÉÿ.Y¾d@ŸýïêÕ«_9hÅ‹ê¦ùä -ÿÛµÇM €u“ÊÆ†R"ªÄ²†A¥ÇVÞ'ΓLiütÐû¦É1ntªÃÄAm ±>§=¤p„™¡.>HäðÙ«‡Ó¡‚é úPVj9ÕºIB6 ŒfE)ú @½Ž–$M¦}hؘãßÌ Îýˆ½»:t4T„sÔ&S×.XyÂ$„[–|µÅ¿ë¸Û;ú©+Ísö>X)µ³Iƒ=Â1¶ÿÊ?K?©$q4 3À^9Um’>jЃܭNŒu“ ~Ô&Aœ«6 P9æî•ì‰ìŒ 3ecƒ`Ý$;:!Ùêd¦†“(gq™¨L/ Ÿón6uõ°åý ²,§g¤§¦¦æòé ‡áæâªÑXŸHùN7Øß™FpO-+q3È •IÏHOIMñóôñ㢲ã(0RRS„á¡á5yj“aìØ_È "555È×?0(X§×ÓÃd4ê2u©©©5É L/ '€ ìë&ÌP™|C~hX¸Y–eY¦'TH±î,(I¡aá)gkòÒÔ&ÁIGŸ =d†2çGÉÍÝ=77·æÏÃð½Ž:Èên­y§P›€”ìtÁÄÈ V/%ª Ôš‹¬*5·L/õZMÈy;NU}Ƕ$„°íZ«’ -¨=¨X§æËwº€-GH\Ϋ_öt3›‡³²é<ƒT80µü+''‡þP«§¼¼¼Jun cƒ 6 ¨úY\p °Û˱Gád 2C#ƒtí%Y»):Ëý µþI˵Ò5œg 6‰c:€5»<Ép¼ÌPj ©Ñh„P R6}þÉÒMÿfiüšõ¿kÊ„®Á¥Þ¢’›¼ê³yËþJÍÓµK¸ï‘Qm}-O çŸýë«és¯LùøéNŽzH”Tþ„µ…u“Æ5ú]ŽöÒËÎÙSÜþÎ&Jf¨fg”É †—¿ûÅŽðqϾÒÒ´gñŸÌkòÒ­aÚ’‰aß·ï}}¬íÄç¦D_Ùòùœ>nðÞ³Ýý¤¬í/Oýpoš¶’¦&_@¬FŠ¢ØÅÍâ×¼Cj“È µ<ÄB’$CÊæ?Î5¸ýÙ„N¡ZÑhÂ-<ûÛ3†…_}—ùÇ×ü•׿;¶õ•Däý{7¿¸fßåî7øt}qA×ü³x#»Üѵ|eÿ²9_ü´;=_ëÞòæ{¦Žïâg7Á‹¯jøž™^àtœê¢&Õƒ`ÿ%3T£yKßÏ òÓŽ_tkÐØO'IB¸· ×ü|8Ý(EM´ùòÉÓù¾"=5’$„kx‹e÷‘ cÏ@WËŽVË‹r÷~õÁ—ú=ñæM¡©¶ïÉÌ•%-[x½unuQ›Ä!Ò>;Öà`Ã#u#€ód†2ä¼Ë¹ŠK×¢b%7oÓÙ,ƒ"<¤«?‘'\<],ÿÖ¸z»*y—ò®û-ÒæœÔÔ\˜öíGº‹ÈF-»ÔÆÁŠo–(§MjñÙ¨M‚Ú‡JÀÐN¸1ƒÌ`«!fÉû4’$4:­ÖrC‚F‘!I’F£)ÚJ%$„F«+ü ɲšPÑ $„TÎý šÀŽ=¹ðå‡NvŽoß6þæž#ÝÙðÕœ˜^Àh¨Ûíß‘6~vgÇÎ ¥—’¤õðs— 9Åò˜lÈ1è¼|Ü4W‡Ÿ:?w© ·ð'dCŽA¸Gzh%I’$Y¢…¦œ{ ]¢‡¿<¯Ù–õ‰»wÿ±`Íëîxïͱ õt¿ªrBIÔ&q‚.ªÛË8DlÓƒ¥ïgÐxD6 ÈÛwâŠÜÖ]+Œ™‡Óä>a®%†¢:ÿÆ Ý.MÍ]$a<{8C:0Ô¥Ô”»*²p k×w|»¾ãóÿ™=åùm‡.i¬©Ñæç,_ Q+k]lÔ&ŒéÁ0ì˜pòÌPj­U­VëÞ¨wŸÐ_–Ì_=º…qç×+3¢ÇõˆpÕÊÇL}dEÀ_¾ÖׯÅ-7z<ûÍÂuþ Q—6}ögAÛ‡ã‚ôZbÎËÍË+0+й /7Oçîᦽz¨3žþá­o/ß0¸g«`)ewržO“†>:ûYµŽ¾Ð­ÖsQ-®µÊô`å€Ó?ƒrp°ÌPö~áÖdüsS.¼÷õŒiÙ’oó„ÿ>3"ÚE# EŠ,4Ö«ÃÏÞÿáìy3¦åé‚ÚßöÔS½ƒu¡\Þ<ã®÷ !„xëÁ»EËÇ¿ÕÓ¯øPìغµë§‹^_~Ñ$<¢º}úÁ6žÔu™1j˜p¨MÀÀ€õ‡NŽ”Êíd·èO¾?àÉktizßç¿ßWôž-F=óѨk‡ª’_Ï÷Wõªøi}ÚŽ~êãÑöÚIŠ¢X¾ÖÍ©6Mj“»Ï`?õ?z©£WtúbûûJÜÍõ¡Š¶Rûk¢ÞÏÀô ê3(a€‰ú쩲ÝD÷Á©2ƒ°ï[ð%~9‹šÜ]ÃÎ¥6 €Ì`UJ‚éÍE¬›»ØÄÕþÞlr±†/ÍåOp”pæ¶²ìûÈ Ž7´µ¶Z:ÉV˜^†žùP=l¹Þ¨‡‡Gvv¶Tcα%ÛÓ'ÍÎÎöðð¨É3Xª’,á½µ|VàÄÀ)™íà €j²å5¦­¯éè'÷OÛë”»~¿÷‚[“ÁO¼0©g°N˜2¶ÌŸõÑO{3d¿˜–.WäHË?ëàïÏ^ºéd¶Æ¯Ù€ >< ‘»ñè'÷OÛ;èžf‰‹~;njÐûá—ïöY=ëÃ¥{³:Œ}á¹1­šõöƒ­N|ûöü#B!LGæ/KkwÏ‹ÏM¼1ë·÷>ß›# '–¼ð⪂›yéÝWÜÐ¥ð©2Ö½6ýëSí&}0磷ïŒÚýáôwe)Ba:¶øÿ”~S_üOOÓ†YS¦Î=ÕöÞžzpá‡ëÏ™K¾­gˆ§ÈØwø¼©ø7/I¹²óýg>;Õá¡Oæõù´nç¿{uö®l¹Ü/%¾óܼýQ·¿øÆë3Æ—¬iÒh„¤!.\·>pÀÅÑ€3\æpPj¨2gŸÞº`áÿÞ/·òBòïrûx!ä‚Ë™Íâ;l:r*Gi"„е||æóý$aj˜öûÆßed¹ý°*µåÄÏ&õÖÑÂå¯oÖŸ”¾yÅ.¯[fßß»…‹ '?¾ç¾çØ;¹MˆºV¾úD?ÉÔøÌOëVÝøÜô;»ˆlåÿ~|çŸs†[Cݯ¶JÔ­S†ýùâ¬;Æ/ës÷¶nJfââ;ãæï®"dô}]~}m}rJfyžû>Q?pæã#Û¹ !Gd®XºD!„K“ ßü:ÍMâ €]’ƒ @fB!çm|fÔF!„ÐÅ yrÖí½%!”ük>{oÁê. ½§·&Çm’-Ó™:˻պû¹KÆ|CÖÉc9þ[ø^;Wb:w$CÑ&¢pÖÁ-ªuˆ¼"ù¼)D¡Õk…BãæéªÑh-—ûµîžzÙh¾vÆTã÷À' ìÞ¶cçß»V²âóÏo|úƒGæäøüþ‘ ,G,“Á ·»’VÒDƒ‘ÝÙÀjµIŒZÊþ å ª{œá¸Øwf\:M}}bä±/_þôp¶Ö]' !Œÿ.{áƒÍ œ¹ä–Vš”…MÝ\ö÷,£}Ù,+mÙaG™Ê*4×Oʸh½£;÷îÜäÄËÛ^}àÕù«Fü×,t­ûLœgñéõ§ç–óà©Oe¡Ñr1ÄjL/`¤ êaÓû$­wx£˜¶·üoÆPÏo¿°üß!òÎìO÷ìrû Ø@IY®øÚ€Æ+¤œŸíÕ»áüïÞù2쎶º“‰+¾=f .\7iÁýÿùÞïñögݤëÇAmÀVt*š•e!„¾Á§g$?2ýõ7ðäScO¼õÍkÓ¤Ðη&$„}s¤â¬ÿÐÿƽ9ëË·¶éÃ:ß6 îЊBÔç¹W.½;{Î#?çH>Mû?üÚ:yKÆêd·–wÝÛ}É/ßÏ\yÉ(„Gd§áÓŸžÐÂM#º?ýö£óæ,žùÔ¢áܲÛm‚\ýcÊ>è5lÆ#§_ùì£öiÑï–Á‘§“ ŸZ–…"sºj“IêjOç˜Ã§°â³¶zquM~FóódDņ놇5ë׌9¦’X¼xñ¸qãìº)æÌ™3eÊ”ú;‘”:ö•< –:8Vò_Õ:K•û[eŸ¼ì—|ÏÅ?_öµÊþJÙ§­â›±âƒTåLS¶ñ+úÈ×mÛJÞÉŸ©èK¶dE[EE›J%[Eå¿RQoVq모ƒ*úh×íµj½zå;N©W©dû©¨å«ÕøÖíォrÛ³ÜΊÃËuÛ­Š“Jž°*;QE¡Z›îuÇŽ×ݧ*z·í5Ù *:B^w³¯Ê¡µòg®hǼî.\I;W²£•ݶ+ßÙ+ß„*†jíµA©ô…–,_2 Ï€ŠþwõêÕ¯ ´âEÕòý @)Ô&Ô‚›màôÈ P#n}æôL d€Ê°n¢&Yš>"y‚Ì\?6j“8M‚­t42P.¦È @e¨Mr(\†ÀÎéh¨66‡¨M2]üçÏuÛgäKa±ÝûvoêS*©ËÙ'¶oغÿÌ“äÔ´s¯›Û»0ÈT}ùþ z Ö5uñj¤’¤öÞQÿ;´æ FŽ3½`ÊLúuã ÷Ž #F h­9¼n;Ëòµ‰áÒß¿®þG×~èÝ÷ß7öæ³›Ù’fd °ë“(ó*°ÉÐ »™ÎÆaj“LÍ ìtS»èÐÐÆ{´õÊ<˜|åšÐ`¼pú‚&¬uóPOW¿FmšùœËÈ“ÙRÀ˜*bËÚ$Y–Ó3ÒSSSó Þšxl IDATùô„Ãpsq‹ˆˆ Õhj”H£6Éxé\ŽÞ?È]#„Zï0_iOÚe“ðs)þ ½”¿9é¯]§ºD{æeœÍójáA–çTÍ©­IP»*ùîpêHzFzJjŠŸ§wˆ÷¹:Ž£!%5Enõ“8Ä*«²1רh]u…GWI煉¯ä›qõ†Ç„>ËÖÿòÍ~¿@Wƒ[—!]‚¹Íˆ± Ø«ý~Ø#8jfHMM òõ Öéõô„Ã0ºL]jjjM2ƒ¥6©øOûZh®^‘å2gu9çxâ_Ñýnïèš²?iÇÁ¤-$´(µg&%%ÿ½sçÎlfd’ºýì¨'3äòCÃÂͲ,Ë”o«‘bݘI’BÃÂS2ÎÖðÕ¢6I£÷Ð “ÁTز©À¬uuÓ••™/îß~\×zlL Ÿ&°gd¯¥‹ÿú+µÕ h—kžˆœb! œ43H’äæîž››[óçaø^Gdu·Ö°S¥6IèýB= g2óä½F˜³Ó¯(Þ-}JîuŠ1Ϩ7–ÖÝÏS2ÌŒôÀ¨€Ž\z@­·ÆZà»­ Ø0Å9̺Iº€æÍ}Îïܲïô¹ô»6ïË hã«æŒÄoçÌùñp®¢óoívq÷–½©Wòr/OJJÓ7lê¡ €š†4¶<§N*j*6ì\ùN7mPÜ ž9ë¶­Z^ ¹‡ÆöÔÎO#„Y(ŠŠB¸FÝ4´§fSÒÊ…‰²äÒ¢ÇÐîM<Ø3ŠÇ•Â9*‹œòœW£ßv˜Ú$!„> ¶ÏèØ>×&‰ànwMéVô±½FÄöb›ˆ™¡¼QeíÁç~;ú¤5ì\GZ7 UÌQ2kÅð—F[;h"8Cf(oį˜®]þÊKëâÞøx\´^!_þûûϿް/9=ÛìÕuøÄG‡ÇzkÌç¶.ütùÖÿfæ(îQî›t{| ¶øIò’œñÌ¢³}_ÿbRsWSÊ’Çý&åêk 9ïž&TŒ«œƒÔ&œj¼ËEê`¿*•ŒÉ tEš"Dx§¢›påK‡Z$ÍÙ’‡¿{q’PŒ—Žÿùã–Ü–ý»†ê„’µï‹—æôÜ´~a×~³´ùô7So>røÏ}¼1ÕÀ£–νNlÔ&A’¸Ô3èG>€×bgÑïuÉŽ®¼Ë’VlΉ¹3>¸03þhò³¿]’ |êõ!‘zSÊo¼·§Ó“okä&._ý=­_ü½O7só÷ÖfÙðíÜ^vúম5Þ,Ù.ëÓ €õ§L.¤)3X†Ý¥ß…ÿ,ý¥b†”5ï|¼¯ÑÝ3Âu…»6ÿÊÛ½Ó¯þò½ÿÎ}æ£{L»Ž\ù÷ŸçÇ®(úÃÏÜž6íÛ—olÒùËÍcüÏî~r˶³š5âèzè\«Q›dãA§` TºydZçT“îS”š@8þ8mf(pYœ$Jü‡éüÖÙ/,¸ÐÿÙwn‹v-~Tr ŠnÓдmÜû¿¹÷¿Ïþ0O¶l—YÛß¾4ò±·îíè]òtÞ¡^R~VÓêGm@` oìP´™¡ª%ï¦óÛ>zvö‘žzë¾ö>åü†’9WÖxi4nÁ‘ Š»œì¦Ñ{‡E»kd“,é4– /ïÌþt< @W [²³|)DvnE˜^qÈ …ãÊRƒoÙ››—[ +²1/'7ÏÓÝCŸ`îÓ3Nxº·ÿùÇÏ !é½#Ã./~mI^ÜÍñ1þ¦´Kç÷í~w ÷’Ï$Ž[%Éøï’gæeÆõïÞ:0ÿŸ_¾üSŽ{úÆ`'éºÎE5k`j“PnŠuÒ[¢)ç€3g†2÷3’ügêÊ Bqúã~ðú‚ÏËP®dÌ.©è§Üº¿ùÕƒíZ꬚÷ë¹l“οUŸ©3ìà­‘Ê%I’t­Z{,X5wã7§ÉÍ“_Ü;PË!SÕy£(6j“¤8ÀÉ3C.Í'~µzb©û|°²O9?{Çÿ:ßQñðTòëûÎ}-ìòÀK]°ó~Rœì*#Ó »OÌ‚ÌP;;”‹–Vïd7mUÃû¨MBÙÜ\£“7gnì43{ õ<|·IZ¨ÑäFß/µI@•â=¾a"[2CuF•Ì38l.bÝ$¤ 38ÈÐÖ^X.ù;O¾¢6 pÆä 3”åáá‘íååE78X.ÊÎÎöðð¨ylÔ&¨ÏÐÂtT@cÃ× KKOËÎΦIvvvZzZXHXMž$ €–å‡|뮥2¥Y¶œg°Œ ϤžÉÏϧ'†››[xhx@@€Ùl¶úI¨MªÛC­àz*Øaë ì 3T‘Ùl ã6hG¢(ŠÑh4™L5|j“È Ba2™j>¸„ãaz¡^Úë¢I¹FÀaÎL}À¶÷3±T% nlÀˆÍêçár2œ!6j“pò­†ôk™cqîNf€1½8ιêÙØl¸¿Ðø 3µŽÚ$pVpî 3× ‚Ú$ÇÃ-tqÖó{cŠ d8*¦FE [A‚ÌT†Ú$pšp UMÕÆAmR‘äädË_bbbh À–cêëd@%øN·Rˆ À†¨M‚Q›»Ä5Qgnz`Ç!36‰ ‚Ú$8çù’“(Ø@¿ƒÌ\Ó €S ‰1|@f¬Fm™¸~lÔ&©> 36Äôµ¬äµV ™€Ú$¨âœŠª4-dÀ†±AP›@ý2P.¦F p®DWo à0¨MòõÐÑPmlÔ&qÚ£Íí—¢8ï&dÅgw³¯Îåb8ßnË<ÔˆéA> Ô£ÎçEIÏHO;›–›—ë êáî*UaG¥q*b©M*þ“¶9×r)‘Q œm³asåØ[e†ôŒôóηkÓ.0 Ðôü…ó‡ŽB„…„Ñ85ij“0˜¨ö`—Áì=(:sY£“g†´³iíÚ´óõñMII9~üxµ~W):ö)ŠRòï¥þ«’ÇE±\Ò–$©øÚvñ#ý³¢ÿº®Æ‡††¶lÞrïþ½U7ŽÁ`p†­Í×Ç·ŠÃô€k†ÇÎ ¹y¹þÉÉÉñññŽÝšÛ·o ð¨b­Qqã8ÉÖ&Ër‡Ú$€A6À.•n-N9¡§«û¶•DÑDâèM\ü«85QÜ8N¶»U©q¨Mp–ÌPr$í ƒãêF#gˆR¥>oU~ÌfÓ Æ“?}º1tü]µXƒ‹Ö¥š¢ÔaŸöqàŽ¶—Îå6hufIHŠ¢ÈE»5-ŸQQIHÕj§Ê Ui›Õ&å˜û‡C†ÞÙ5ƒCt¿ºÆ.œ6D1j$Cu™A’,#i³Ùì$™A–åª×&Õ}”ÊÛùê]/ˆç–Nï衎&Rum’Gû ƒò?œ½<~jß–!žºÂwªqqsá«L8ÙÐ 2Cݲ\M¯AŽ)s×ó¾^¹ùðùáѾ÷˜ûï¹¥™§Úνf³ÙŠÏX«µI¦S‹¾wÁ™è s?¥BˆkOUó¹rw½:æuíKß<ç^ß-i³Ú¤Ü½‹VÜzaL—wK>ÚkÅ¥ ·ùr¸H\¨Zq  3TÿðQX²oÝxZ!„|1qÖäWþðî}÷´ ±Arú®_¾žõø¡ó³fÞÝÔM…™AQ!U¯qj/2¤mÙêìzzCbêÈQ t¢Äª³Õ~ëÓFå© c³Ú$¯›?Ù~øíR?ÏHoŽŒ6À€4çUç–’ýÉ?¶hÎFãÍϽ÷ÄÈží[µîØë®gfNkŸ²ð“õgM¦³?OôÀÂEQ%wïë·šž”£(rþ©µï?q÷ „!ýFNz~é+²RüÕCÿ5Ó2¬ÏÞüô°ÑÊWÅ|i×¢Wî5¤_Âm·ÿ÷“u©JUë~†Zd<»mmZô¸‰ ¡)ë·ž5/V¥ä&ÿòÞ#ÆôûÄÇ[3MŠ¢ÈyÉ¿}ððø¡ý† {èÍeÿdÉŠbLùá¡“?_»ô¥‰#=üÆCÏ%æælž6rH¿„{æ-¨­7YÅÆ±Mm’Æ3²IXþö/§ß?bð¨wN›vÏ·ÕáIeÀàÈ uy(–,w2˜L&Ù*ùg¶lÍôï=´­wñ­ÔRÀ·ur9¼n÷E³,U>?)²l¾”4ëÙÏO¶›üÑŸÏ}âÆÌï^›tÅ,+Џz7¶lùQÙðïò_\ë>ü…OÎ{ý®Äw^ÿù”ÁºwZøÍfsÕïg°4N-1¦mÛÑ«G»›ºœù¿mg …­"çíþ~«kŸG¦O{°]æ3?Øi¼´ã£§>ÚuÇ+Ÿ~ôÖCmN1ýµ&Y‘әşü!:š8fôËOwrsë2ãó¯—.üàZy‹Ulœ€€íßîßýñ?½oì¢OMÍÕzäÿ6¥ßÔu—¸ðÍ8O=Ÿš.Àñ2ƒ(ZNÔ2¤·æÚù…SEpó`}É]Bš‰ŒcEWÞ×И3·-Ùê3æ¿cã5ŒqOgóÎ'òE(W´ð­åûq幞˜Ð*,0¬Eÿ;n‹8ýÇÎó¦^J¯nãÔÓ¹mëSÃ{v qêÙÕÿÔú¿ÎY>‡Ð¸ßøÄË“†Üß}Ä”‰7H{?ºó‡­¦îS¦ lÝ(ºEŸ{þs«çßË3LŠÚбoÎ|îþÛ‡vkäï©—4.žÞ>>Þž®šZ{›UiKU’ ÂCîÎ÷çdÿwýº/¦ßÞÌ]Ò…ßúþϯ…­þbwÇ à¼êoÝ$«-ü%Y–e©ÄƒŠPŒ¹F“¬Q8{`yT(rAêÁÔ¼¿œ4æ˰Ül0Êm³L–pQ¸L‘,+BQÓ圿rìÅ [´–Bj£Áœ™m’ƒ¬¹’W<áa“u“ÌçþZ{&¤g|°FÖ5èÑÅ÷— ÛÎ%Ü*ËŠ,í'„p k¨l IˆX—'s¬ó6´5dQ´©å/VüºÖ/Ê_$É(èíëRü`^úñLáè&ÉŠй(3(B(BQd“¢kõЇOv(^]TÒ{¹f.úQaùQ¡È²Ù¬hƒ†ÎxyT}Ñj=¼µV“«›j/3˜ÏíØpÜ”rü?·SôÐÚ ÞŠP _GXnÓ–4:IÅKT’ “—Ròaùš”UkCU~Ìfë&¹7ë–ô朽R„BÉ=°à½¡C›¹s¬lÊŽ¾. È V]• ¿æL’¬[GðãÆU{Ç>ÒÉÇrÊ0ŸOüigžg|›`«NÉÏÊ3+ŠNÙh”…¢hƒ›Ë{_tíY4ÔSdŬsÕ‰‚+ùfE‘„b6˜!É+*ÚýâáTáÓÖO[tfR¬|§Âò«;ÏP;Ë™ÏïX:¨ÿÏ× !„!yÉknØ™9 gÉu“”ì“λ5Љj¦Ys0%oP°§$DAê¾T|C N-jE¡IR,KAÕff¨JãØlÝ$}Ó?~xqïø€w¥‹¹û;„ÿ{¦ñÓëˆÑs¬k°Ñ±HåßÁç‹þÕÓ÷@×$3—Æ£îïñÇÛoÿOgßVò¹¿_øíCäˆ[[º Ѱ…ÿ¥ß­ŠvqçÊ%[rå.Š&°ë°Ízó=ÿIÃ;‡KŽîØø—~øÿ†6‹q]òËÒ5‘ݼR¶­XtÈì-טÛ…=þå[ŸŠ;ú5óÊKÙ¿iÝ¿´›dÕ6#Y·nRmD†]kOúÜ8±cÓèÂÙ˜à潺1é|÷¡äÿcí_º¦—vÿðù¾€~ï6½­«öõysÿï¡-t'ÖÌû5§ÍÔø@±(1(ŠBç×À7oÍúļÂ]T‹F^šÚ8Œ¨{Ý$!ùv{%ñô°•߯Þ}:Ç%¼}ÿÑ·u w­É3š.þóçºm‡3ò%°Øî}»7õ)ێƋǒw:}!_ön3bìM¡: nPó˜ŒU˜ÇÎ BÅ—Ò­K~ñSÞ{6ò«Å«Þyþ£BÿøŸÙH¯(¢áðG†ŸxɬW]£{ÑéÌ÷B(’ï ½2yþË?˜±¬@¸5O u¾wÊ€7æÍc³O‹þÃÇÆ|³J(Š¢o±W€$ŸUJfmD¿v¾ûñó]›Þýæ‹ =káb{ÇfµIB!ô!F<ÔiD­<—)3é×'ünLn>½mݺ5>Á#Úû^“äËû~ýá/SÓ.7ßì.Ì:vAÂ~6EØW»*3aYNTX{?ƒBM@ûaO´&”ì­/OþØçÉëàUx§®gÛ±3¾[ôƒC‡!dYq‰ê9ñ…ž¯š ÿø{gÆß[ôï#-ïIòm?üñYÃKl­›Íf+§†û¾ú}ßkß¹{»§æ/Bˆ¾_þPvà.\ö›üN¿ÉW÷;Y–Bnù`á-%žÅ'nÂk 'Ô¸Q®ý¼Uù1›Õ& !Œg7Íyå½e›œºâ—ðàôéwwôµvoºxøhV`§[ÛEûhDP¶‡—L¾Ò6ίÄóåŸLLÊjy˘îa@ ÈÀI3CñÒ@µt³¯k“øFKVýºCßBÎÌkØ5>TE­âi“u“ì‚Ú×MR²¶<Ù½ï×!w?öàsM¼ );x¡w¿”?þx®uwA/ËÑû¹k„Bëæ+íI»l~Woç7œ;tÆìÖp÷_:_ ókØ¡gï¸7ÆI¨õm›«q5g!™ÍæšÕ&•¤ î=eròû_¼ûj¾<à….]BtêiMKUÙl®úý Åã$ªØ86«MÊIúx™Ûónx¾e¹Õ Ü[ÿ’ûâ&Ok"’1רh]u…ŸXÒ»éä+ù&E¸µœ“qÉhÖè"ãvò0¦îÞ˜øË:Ÿñ Í<ëatç÷l¨Í#êNÉos«·Úkò½&_¦«ªA«û5µ¶n’¨bãØ®6I6ëÚ‡^½éYëß²¥çÊš|?ƒ¤¹z’±¬f{Íë™òM’o›Žmûi„îÙíä‚õ‡Ò š5q»æÇ’’’ŠÿÞ¹sgŽ\N:X!ࡾÎd5³¹d†êrwwÏÍËuuqmРÁîÝ»«;þ.O—;SQòËýIËzMBˆâzI’Jþ½Äñ­ôßËý±ÊEGG !rórÝÝÝ«Õ8N$IªzãÔ{m’9/;ϬÄN‘9eê§fÝ{C¸›œ}bí;ÿ[×þÉg<­{NÞC/L“R ÌZW7]‰ J£uÕ*Æ<£å'$§—‹|1ߤˆkgcÈ Àe~ºŽœ"Â#RRSü|ý‚‚‚‚‚‚¾Aó ò/]¾Y­Æqsus†­­êcƒÚ¤Ë«ü†m,üÇC7.yèêyèß4ÄךgÕû…zÎdæÉz0g§_Q¼[ú”Üë4^!º)i9r°F(†+— .¾>®v{0¥Þ `Ì€Ì`?Y–ÓΦ8Cƒººº†‡…ûûùWe §"6¨Mòîÿ݉äÜrk4aÞÖî`Í›ûìÙ¹eŸ§0ÓÉmû²:Åøj„9#ñ»ïÿöì3aX ðö-½Vl_ŸäucCmêÎÄsÞ±=BYA 8Uf0›ÍÁAÁáaáS¬:/˲Éd24N §¾k“4aš!„0dùûà™,ƒ\t±\äæa]iƒâõÌY·mÕòÉ=4¶ï v~!ÌBQ„°<¿>¼ëÞ¦u‰«Ø)y„µék|ˆ³G®_êëËÙ63!ŒFc‡‰NˆÆ)—ÍÖM’/®$¾ïÇÇ„V§×8|ÿtü§Á>V>¥> ¶ÏèØ>×&‰ànwMéVüþ-{jÙ›né R|ã,ÔÈR•d õúÂ9}´"dÖÙd4Ë´>08MÕÆQÿßé¦q‹öàò©*0M UTòhÉB 3ÀIج6ɳˣ£_ÿÏ´yÓï¿1ÂC[xÖxE7öbN õ©dDÈæ3d@Øð;ÝÃåÌÔÍŸNðiÉG{­¸´á6_º¥ÆÃGT’÷8+‘à0±AÔmRöŽé§o8=­[˜ÛÕ‰IËŽ'<¡P„z ¨Q}ßú|uœ¤q éÔ¿K”—‹®-£' ¦¸¤r,@fj—ÍÖMòì<©×Þ¯.ß}êüå¬ì"yfº81J. ÞØ ê¿6)kËÛó·o¼8*îÍ’:Ðý \¨³¢)h4uAf   B6[7É«çÜ]ɳäkÔx„yÓ'Çb«•Í G9å-Ôd¨‘ÍÖM2¦nú~ɱ‚ktm:f꘦®t‹ºßy:\¤€Jcƒ¨ÿÚ$SÆÎµ¿'æXFlæ¼Ì£;ÄaˆL‡2 &¶ûN·gþþg‰N|9|ðo˜dp6–){ðàä˜êDÖM‚ÙlݤÒ\ üÇ‚ÝÙô u¤Y8ÒØ^FäŠÂ¶æ ÞØ ê¿6IȆ|Cq!’b¸°çÛ/÷éšù²£\÷tµ¨²CR§Xu6‘ÃÏ¥°e™ŽÊfµI—Wô¶ñš‡Â†}öY'ú5±ÙºIÞý¾=z8§h¢AÒ¸ú†G…xj騗Ìé> 3¥bƒ¨ÿÚ$gdÓæ4>™jgƒÚ¤üƒó^œw0¿¼ÿr‹øâÄX7º5pÝkÌÜ‚ÚÚÒ¸W¨ë#6™P Ô&É9)ÇŽɽæ1ó¥}kÏ·[=ë|™#&À®a/É;… d ¶bƒ¨ÏÚ$./}ÿk‰›Ò7¼5aÌoÚ–æ-=Їa€8æ(8@ÕóöÆ1Ö~ðý P#-CÁ©•O÷jÖçµÌqKïúê¾¶ÞÒ€hMC̨‹í¾ÓMÉ=üÝ”øfC?Ó?úÛÑÄFǸsˆd@­±AÔóºIò•Ýsïl;þçF¯m>ºö•A‘zºÁ:\K«FJ¥€£ð9Ð9îg€Ù`Ý$Ñ9cz=´"½åݳ?{ £8´mKñiýc»Äúó% Ž=j'ç@f€}±ÁºIy‡¾_™&„8ôõÔ[¾¾ö¿´}~:¿n¨/Ýph\C™öD}Ö&ù]gbò¨c Šcnv8(îg€ÙxÝ$ÀŽÆñÎ\(ÌàŒ½2œ™íÖM@iÔ&A½±AÔóºI*–œœlùKLLŒ}¼cË—³Öfÿ 36X7IÝj?*”;æf,È °6X7é*Cæ‘¿žÉ2ÈE—ÉuAztb_q$"pNe/ŠqF 3Àabƒ¨ÿÚ$ùâúGâû~|LhuzMÑÑÄgðOÇìCŸ áGÕ™öÅfµI9}´"dÖ=ÆzpÖ°`Ý$¨‘ÍÖMÒ¸„ÇÆE®bžê ¢þk“<»<:úõÿL›7ýþ#<´…ÉAãÝ<Ú‹| ûEɬØf¾Æ‰".ú dØ;›Õ&)†Ë™©›?4àÓ’öZqiÃm¾t pR\;…Ù¬6){LJ‹ôÓ7œÎ*0–°v(áúqK©§Wá’¹Ã÷2ª¨ø‹Àéuy¨76ˆú¯M’4®!úw‰òr¡ 1Ï5ªïé…bž'õÚ;ãÕå»O¿œ•]$ÏL—8®Ë`ïlV›”µåíùÛ7¾9*®aŸw‘„U—éH‰Î‹Ú$¨76ˆú¯Mòê9wWò,¹T²öó¦Cêkb VÌ3@lV›¤ñ‹òü÷ûîеCÛ]ÞýÂòS^Qaì(5Ä5g2P»lV›$òþ~¹oÂÌS§¼ýå·_ÌœÔþÔÌÁ^ß›O—À Ìœµ>d˜ØµIPolõ_›”»ëãæ'×þþjw!„£Çöè8ô£]OÎëæAŸÀFã'²¨MñbÇNsˆfžjd³Ú$Ó¥3††ñÜ‹pkØ¥¡á¬ñ.' IDATÌE}Ôû™€ ±‚Ìõ³Ym’[ÌMÁ;?Xt¸¨)ÿðw&uoâæ@çNV¨&j“ ÞØ ê¿6É¥ùäYã¿îß2rNÏ®=sÿݾqàVOnÁW¼Pwá“"L2`…€€€úN …4}ßß•|Û’%kö¤ä¹v1}ì¸>M<8’q"À~úØ6@fªÎR›Tügý¾¸äѸϽÿëC/0L½8èh€2œ'6ˆú¬M*8¶dö’cåý—kÓ1SÇ4u¥O"Õ°Áô‚)cçÚßs„ùÒÁÄýB;ô‰oì]pf×ú§#½4è!ÇmkÖ¨8-®w`×lP›äyãÌßÿ¦Ó_ éþô®ïîlè"„¦”ewõþ$8€{  $´T¸i@}a­U¨76|§Û¾ï÷ÆÜ{k⌠‹0¾É¡•séTŒld þÙì;Ý\ÂZ¹ïžÿëicá¿ó,ùx§o§hnf»&I6˜œa:„Ú$¨‘ÍÖMrkÿß™»ŽjõFŽQn9ÿîØ|$ü±ß¶&3Ô"V¶)Õ`û‡M:‚^È pŒØ ê¿6Ih#FÎ?tü¾~X·'%ߣÿ½3oÖ5Ò pfÔ&AlV›$„’wjÛ†íG/˜t®š‚ô¿šýâ3/Î?”OŸT‰,Ôæ F6«M’Ï­¸³Ãˆõá=º4ôÖs=\³dú¤NlœÕèSÀŠ1@ "3%cƒ¨ÿÚ¤œÝßììºðà/ãÃkm ÎtñŸ?×m;œ‘/y„ÅvïÛ½©OùO-ç$oX¾æˆWŸ Ã[xpDÃ,à¨"Ô&AlV›$¹úDFùÔÞ~aÊLúuã ÷Ž #F h­9¼n;ËåÍY(ùg¶­ÚtÚL×2P%–ª$„ÏN“ú|á•öœ¹p%+»HžÕcyÓÅÃG³;ÝÔ.:4´qÇm½2&_)Œ»[{¦a¿>1ì‘P.ÎÈ Plõ_›tå×>MÜøÖÈ }}¼‹$¬ºlåó/ËÑû¹k„Bëæ+e¥]6]ó#æK×üú_Ï!ñõ¶>cA *¨{€"ÜÏ5ªï[Ÿ‹y÷þ|Orn©‰G˜·uO'sŠÖµènjI煉¯ä›áRøˆ’êÏ_vhâ‡ÝÜØCcʦçÕ§>ïëUÉ=Ä%ß· ‚h€ÌÕ²ÙºIÐ Óo‹Vì:›gV„й +õ„öžÏföð²öt¦¹zB“åRWøåܳ)Wr¯l\4ocÑCë,º0jìÁÚ’?—””Tü÷Î;;ݨ€rcƒ¨ÿÚ$ùÜò;:Þrj÷¥æ7DdîØs¾Ã”yá.VF½‡^˜ ¦ÂA¯l*0k]ÝtW/Ši}ÛÝ:¶Yáí¦óI«þïBûÛµ Жz"§Ë †ë ;Çý P#›­›”³wɶÆoïØ·ù›ûZ¶}òÇ]'Ž}9ÀlŠ Ò[ù|z¿POÃùÌ—~b׿}Y-c|5œ‘øíœ9?ÎåØdÏçN-Ý‚ÌØ<6ˆú¯MrŽó;øcâyáÛ¢¥qÛ¦ÓsÖÙs/äYý=ÐÚ ¸A=fï\µü‡ÕÌÍûjç§):Ös¸½€}à~¨‘ÍÖMrm5éÙ¸NCnnø÷÷Þzé¦NÍ>s9•÷U[OëŸRÛgtlŸk“Dp·»¦t+³7†Ü4aÔÆž :€½d8<›­›$t î\vü¦£Y¡áïlYßé»-—›¿oX”–>@M—@f„­j“„BãÕ°…—BDõºgZ/¡äü{ Ý­M¨ž> :czæ:÷3@l²n’’ÿïïï=rçíw?>{cšQ!„ùâönoÝþÑm¹ô Ôû “kÉ™¨„ ÖMR.®?ø‰’/ÿûó½»?•xåòŽw‡4ïú؆˜ÇžéêEŸÀnG]Tµ¿°WÂ>Q›õÆQŸµI¹ûý.Æþ²wQB@ÎÖGÛ}öÉ¿w~v´ß¬m_L½ÁŸÛ€3cžjdƒÚ$Ó¥”‚FýÚûIBxµÔ:{ãWYý±ïûÇ €Ì@@…lñn²QÖè,ù@rñðŒšôñÿnòg;ä0õ?2Ì\76Û¬›$„BçéGåÀ>±`j}dD@…lón¦sÛW~ïá'‰ü½ÿf_Ðü¶|Ù^BèÃ{ÜÒ#œµVÛâ›Ú€Ì”d‹ïtÓºI'æL3§èß;¿Äò7ÏA+S~âK·2 ²Ø ê³6ÉwÈoÙ”N© (÷3@lònlŒÛ=¦ÕЕlN@f€Ã³ÅºI€€A’!3ÀÞbƒ°áºIIÀaúQå%g*™™È °;L/€¡!ç9X-[¶Œˆ2PmÔ&9ݹ“OÔýЙµI`è@{È @¹˜^ê|ϸµ‹ÙB€ÌÔ3j“À@€óìà£G¦Í@f¬Œ ‚Ú$Õâ‚"dÀ¶˜^8z]7 3ÀaP›T‹'ìŒ@]ÒÑPmlÔ&¡VÎß`Pë»EªN†y¨Ó °ýP‹…ŒËáTÛ0›18è‘`w¨Mã 0"qÞŽóíY¥×MâÀ2PõØ ¨Mì}ð 3u„éŽëjíÔ ®µ‡u“@f¬AmàŒ£d€6ï26‰ °nÔµIE’““-‰‰‰¡5¨Kèd þJ"*¨)U]:Ub`_¨M‚Q›{ýØ;†n€­•^7 3U ‚Ú$¨V–&N×ÝM@f€Ã`z£-8ÖM™°µIdàú±AP›ÔniPsLD“€zÆô€:Ü0¾£a€Ì»Fm‡bÝLN¹ãT¯Žˆu“@f¬ ‚Ú$»Pë#.Ãñ¶jÐ#™¨uL/IÛy°nÔï†Yj“ŠÿtòÖ ²œN,“×%O-5y¤Ü“ÖèŠÏgÅÏ3º‚SÝ芟­Ü÷3º‚-ùZ•}ŠêŸŒGWá“^÷sUtš¯naAÉ>½úÏj¾RÏSIïTÞAUy‡ýʲ¥KG× +yõ²­qͳ•WíSî{¶®wF×øçË}é*¾Ÿâ½ *¯U•½»Š/}µkP*SŪÊѦÜbtõ÷¯ë¾Ã*înÕú®rû” ×!-i°Ì_ÑVWƒÛµíSj««â1¼òÏ5º ÛsMŽÏUùÅrÎ)K—޾ýöJ5×<íÒ¥–ÿ}½í°ÏÔê¬U“Z½¸º&¿?£ùù2Æ…­¬Y¿fÌÈ1•üÀâÅ‹ÇgןqΜ9S¦L©í]ÿÚÓOñÊŠª®K^¬âï*Já#]þ,þߊž°Z¡ì+–}õâ_)ù¢åž+ù¼½É꬗j·²o£òN©¨y«þAʶ|EÏ\¶ÝÊ6`¹=[Q›WÒ&ý|æë´j%¿XùVåÝ^÷ç+Ù´®ûº•ìVm'×ݫ҆×Ýò«ò„×}¡r?EE[T%;rµ¶„ŠÞ@å{PE]\ùg¯ÊáôÚg[¶lYñ(¶J[B%[]Õ»¸òcNå»yåg¹Ç·ª ªrܨè#TrL«äÍWô1Ë>XîQ庭T7”J_kÉò%ú ¨èW¯^ýÊ‘@+^”Ú$¨µI€#£Šì ™jĺI`D˸þ®á({ë&ÌXë&1fØ/h@Ú 3åbzÎ5rbô87ÖM™°µI)¨êöÆŽƒúß$œ¯¿È PolÔ&p’ñ"€êbz›ó¶'M vU°‚ÌTµIç‰è*]7‰!>È °‹Ø ¨M0º…úb™P¦Aå_ †"¬›2` j“8=ƒûÈ Àõcƒ 6 Pó¦*ƒ:@fêÓ ÉAf*CmàD˾1†M°*]7‰ƒÈ °‹Ø ¨MÀX`û§)È @¹˜^à0 ΃u“@f¬Am"1€jïãìæuFG@µ±A8Dm’éâ?®Ûv8#_ò‹íÞ·{SŸk’º’wfçŸÛŸÉ¼R »ø7îxs¯Žný$=@E˜g€9Îô‚)3é×'Ü;&Œ1 µæðº5û.Ë×ü€œ›žn isóà¡·ôiívfûož.`€£ùöì³ 3µÎaj“LÍ ìtS»èÐÐÆ{´õÊ<˜|åšÐ  ìtKÂMí›FGF·èÒ½µ—!=%ËÌ ¶°¬›2`}lö_›d¼t.Gïä®B­w˜¯”•vÙTÑO˦|“pñp¡4 Œk¿Aê®Mhm8ù.2`+ŽR›$sŠÖUW$½›N.È7•ÜVrNþ}ÜÒ²±—óî–œÕÈ!€SbÝ$¨÷@C,µIÅÚõg‘4Rñ¼,W8ì2_:°n㙀®£Zû”’’’ŠÿÞ¹sg¶F´ôÀ¾ǨMÒè=ôÂd(šXMf­«›®L표¼qebN«ÁÃÛùkË{"rB}œ€%ŠÂ`£Íî7?öÒœµIP#‡Y7Iïêi8Ÿ™' !„9;ýŠâæS*©Ë9ÇÿøiCF£ÃºE±Ê*ja|ÀÑØ»ù 3À8̺Iº€æÍ}Îïܲïô¹ô»6ïË hã«æŒÄoçÌùñp®" ©›Xs¯s÷îÙ™糌N9Õì/Τ®ÖMrŒŒ• ih¨66øN7mPÜ ž9ë¶­Z^ ¹‡ÆöÔÎO#„Y(ŠŠB1eef+ùÙ‰«NýŠ>æÖ ¢ôª?ˆS”Ù°_8K_s0™°!¸õ¹˜> ¶ÏèØ>×&‰ànwMéfù{‹SZÐã! ®y!8™eË–ñ P9j“ FS›v–Ê1dÔdûὑ€ZŒ Âj“ÔÊ©Œ®d ¦€ÀNDf*Cm•#jw$Áõ{!ênÝ$öÖZÜ¿œ~Wåh¨76j“N``›¤1ëôUXûUÃ<Ôˆé0:ÓmÏÕݤkñjŸÇ9wº:š ºö9—-[ÆN2PmÔ& Ù¡Z‰N„£6 ê ‚Ú$€q@˜g€1½À Ž¿™±¥½¸È @MP›pW×Mºî¨”¼ 2P*6j“È @¹˜^€ªÉ¥ÁzY¹ÅÛ °¬›2` j“vs9€dK£‘ÆAmPê,kÏ š™¨L/À‘‡w\`SUçÒì­È °SÔ&ÃzçquÝ$€ÌT76j“*$I´È €í1½`߸ŽX¶AªÕ&TË@µ'ê†]®›T§[[&™¨ j“0t2pýØ ¨M€° 2P.¦P·'9û}!–[@r™° 6 y΃u“@f¬ ‚Ú$†SØë6c2P.¦ ô¸„¶ãö]®›'Cf€Q›ÀŽd @ýû»™vµIàÀ‰ßNÇ@fêÓ PkùJýËy¾VYµ}Q[oŒ Rd >Q›pžÀ`Ëu“å£jt4TµIE’““-‰‰‰©×‰ó\übè u„y¨Ó ¥Äaäa÷C=Úœ-(ƒu“@f¬Am”ìMõ 6 ê ‚Ú$•Ÿ˜ùàPO§Ø¤ŽŽ€Ó`žjÄô º“€ÊP›d¯n몯–€ê›7]»ÑÖɺIì¨UÔ&A½±AP›v8àx˜g€1½Àð¢z¯Â8€=cÝ$kP›¤¢A?ÃqÀñöÁzÛ¯«ûBpµ¢6 ê ‚Ú$8À€êú Ã÷o¢î1Ï5bzuurej%½ên3lcœÛéY62 6Ô&Ó© pu²n@f€“ÄAmÀ¶1™ªÅôB½žËžk~5šëÙ°€¾æƒ×ÖM™°µIc>Úl0 yÕƒu“ ÞØ ¨MB]Ÿ„øà| Þ­Smðt`-æ FL/€ƒ$"®£¶@¶12œµI€ŠÎ»µx>æÔƦåaÝ$¨µIPolÔ&áºço€­×ÚÁ!¿’Œ.†cažjÄôP ã†,`{³¬›2` j“ vB=¨M‚zcƒ 6 öxÚìq3`ÓeÃ*Å<Ôˆé€Çß ÁÙØÈ P9j“8ƒÁAÞÛyµÚÊY›”u“8þ¨µIPolÔ&€¦ÐÈPæ FL/œgÀͺI 3Ö 6 €ª‡•\Êàd¨M‚zcƒ 6 Û&d@Á<Ô‰é _€ÌT†Ú$Õ+¢^È‘zÓù°nÔÚ$¨76ç¨MR Ò÷nÜtü¢AëÕ¾g¿øîý¯¾áŸ`“œó P#gš^0¤lýmë¹ ®·ŽÖ»qÞß«×Ëåä T6¸d|IK:\/°nÈ €5œ¨6ÉpîàqCd×î±Q!áÍâ{4ÓžÙšÐjOSª ]$@à̱A8Am’œ—yÁèìç" !„Î'Ì[¹|6ËLÿs€J9Om’bÈ5*:½åIﮆƒÌ&`‡£IF¢ Àqq4ÔÈR›Tü§ƒZI*ÎîŠ\þ‰hΜ9¥™2e Û 8ÖM™°>6'¨M’\<ôÂT`¶$ÅT`’\ü\ÊÌÿ‘€ Q›5ržÚ$GP€>7ã¢A!„éÊÙ,É'Ô[Ë&N„u“@f¬áDë&éCZ7q9³-ñŸ”Œ´£m>jŠhÝÀƒïgjBmÔ„S|§›KD·Á7n\¿åçÃFWdû}›y™¸§¸õ¹ˆäÚaà¸ô:P+j“ FNT›pz¬›2`}lNQ›@fªé€ó`Ý$kP›@f®µId \L/€ÊP›p¬›2`}lÔ&Õ9sæÐ€Ì»ÇôPRRR@¯Á»Œu“ØÑÈ €5¨M‚’““iz tè5à\±AP›@fÊÅô€zèh¨¥6©øO¤vo\æ6h{´cÇ^ƒwGfv´’¦L™BfªµIj=p§BmÔˆÚ$2PÖMPj“ ÞØ ¨MªEJÞ™ë6í9}Ť÷kÜ©Oï¡®’=~9/}÷/?Ž9&>@kùdé{7nH:~Ñ õŽjß³_|wÉÞ>¯áÜž?·<}îRžYçÕ¶{ŸøFž‡øhæ‹ÿlÞ¼÷dú…£äÔ¤C÷›;D¸IB˜.þóçºm‡3ò%°Øî}»7õ©ðóªùÒÁß—oJ ¿åž„háÍpê×ù¿œ4_} |À}Ãb\å¬ä­ë¶HË‘]ƒšwí{sl€¾¢Ï«fƋǒw:}!_ön3bìM¡º ö©ò?¯JŒ—ÿ^¶(ñÚóehŸ Ã[¸çÛûGBÉKÙµéÏ¿O\4ÿÆnêé.9Ä‘ßÞhƒ{ÝU“ßï˜×´iSÚµ+ //¯*?™|"¹Ml›J~`ÿþýmÛ¶%1ä&¯ùqKvLï7µ É;¸%éRx«Æ>övÍÀœ‘¸ø›ßÿNÉ•]#Ú´‰t×!„!åÏ×¥…uØ»c”œœ¸-Å·e³@]ž]}^9÷䞣¦†ºtjÓØûÊ?;wõmÕ<ÈÅèÍpvÏ?9‘íââÚ·ˆÐÿ?{wEúÇü™Ùb—înE@JP ;±[ûôÎîÆ8»NìöìÆnÅöüÙ]ˆ ¢" Ý˲1óûcQQWE]àó~ÝëÎwgçù>³ÏÌwžï̾}pëQ®¥›½6›rãàñhíj ª;ân^ŽæVt6ÓPÙ^žzð™Üç_N”+äÚN^u9D^š¦ÈŒ¾ÿ\è×¼QÕÊ.....®Ž¦ÚB:ëá±Ã÷ˆ{ÃÀÝ´»—Hí\­5•íUßNc2Ûw%ÓÌÓ××Ç­’­‰¾Ž&¨éLUíU×|ˆâé˜Û:89»¸¸¸¸¸8;êe¾HàVôr¼*õMcsžž8p]êÖ´M“šfy/^‰Ów©hÀ–áñçyüä±£½ãOœž?ÿ/Uô«Em¨#Ô&•ô +‰{ü†v¬éëdabéâ`%{ñ(1¿^ãðï2 _׆….iRÄ ©¥_ W+óŠÕkVä¼y+fJY{im×&-ëyW²µ´r¬RÓÇIŽÍ—¦iØ7lYßÇÙÞÊÂÆ¹ª¯Oš‘-cåéO£³ }jyؘšÚW©é®•ñ<‹QÙ^V½¿Wo®½”ãÖ´¦å»óÿ²Ò4B8B³wLõ4“ýòI²¨r-Sk·ÕŒ³£"SåªÛ«¾­’¼ºz+Û¹y«:ŽVæVV&Bú ãêöªq‰ Mßõ—‰fæ‹$~Eo+AYhš<36Ynàâb¡Íçk[¹U6P¤%æ(ÊÄðXê gõMj“JŠ";1“Õ2-¸ÜÂÓ3Õ”§%‰™2Ð2&/%M&4ÖãS„ÂÕ1Óf3ßfKKq{Yy¾œðE|ºl5•ç¾}r?Žcëf©AÉ2’ryúFʉ"޶™.•)UÙ^…ú6I–|÷Ä™76›x}˜1(M#„°²˜£ëV­Z½a×ÉÛñy,!Šì·ÙDÇT‡C!´†¡¡ /)=_ªª½ê{ú)MŠ|£Ðß=°qͪÕëwž¸/a¿0<ÊT¶—- cˆ4þöíCs~™hG×ʈ“tûæó aóSãrÖ:t™ùKÜÏê?ËPÒÇ©XÊr\å™ ÅÓà©XÊ”«¬T,c¹|Þ»– yDš+U”Þöæ'<ˆÈÔ©ädÀaÓÊLÓ Êã9&Þ-ZTÔ¢™,±Œå¼k¡x\&K"ËWÕ^u=Ü39Qg?Ñ«ÓÖ×B@É>,–•þ¦B¸ž ›zŠD½n¨)£][Z Ø”26ò#gø^øM·Ÿ€¢hªàÀ2„-[-{ŸB0,[ŠÛËä>»xæ© Jo#Q”¦ñÌkµoç™ÿðG’6íªiò¡ „0º½êH‘— Î’œÜù~ѱ {k7,ýM#„ÐZ–Z„BLL Hü–³O¥6íx…¡º½êù“Kä”®[7{=šã:¯6Ÿ‹L̯ò…ï”êöªûU‡øÛwR «4²à5\”¦¦±ùq·®¼Ô¯Õ¡¹~rä÷ï_¾oÛʇS–F~ä ?œ6Ô&•ØY5_Ä'™ùråõV–/£ø"]6ZÆ#ò|…òÈÀÊóå_Ï)íe%o®9ŸhÕ°muS~ÙjÅÓ64Õ64ÖÎÞq'2ͧšˆGäR9ûîD._ÁhðªÚ«®-ãû¶ëìU0U K¸tð[³]=GmÞëRß´OÏD".›)•ÓZB.“/gÞ·LNñE|>WE{¹êz)žæ8¬,O¦Ü\Š«©ÅgÒ% OÕwŠ'PÑ^^)˜d¸ÍVhî¬Kyä/]Mcr¢¯EHÛ8p,lônûïjŒK½23<–&%¨#Üú\Â8Ú¦ºtöÛ,eµ4#)—«o¬Y¾þ´ÈÈ€'NN—B‘g½Í¦tLµù¥®½¬$îú‘“Ïê¶©ï¨E—©¦j¤,OÆÒ4Exz¦šÒÔ”<†B9‰Y¬¶™_e{9ê{*­«ÿž6Ÿ¦yZº:º,4†yy–ÉML‘òõuø\íB·a0’Ô”| #_U{Õöb$­ebÀ͉KÈe!„•feJùº:BUß)žÊöªyÎÿææ4ƒ*>‚¢FþRÖ4™¤P%ÐÕæ±r–µá9ÀwÂs“J¥aYÙ’y~ùFtBrü“«Wc¹v•Í¥¯¬BšŸ/•3,«Jó¥ –žIeþ›kWŸÄ%'Dßø_´Ü¢²µˆ.eíU¤Þ:|ô×£¦»ž$599999%3Ÿ- M“½¹pèäÕGÏcãß¼xxé̽,Š• ¸\''ÔÛ—Æ&%ÆÜùßÃlgG]Ze{KÛ£ÕËDÓ$/Ã÷»üàyì›——OßH×uu7áÑÚö®ÆâÇÿ»“˜ûðòÍÍŠÎ_êJµmßÜÓY+åú¹[/_Ý»x5IÛÕÓ”¯ò;¥º½j½ó1מG?=ºÈ‘¿t5Ö±µ×?¹|çUF^^fì½1 SgkMAYùKü>¨)åï3|õWðû ߘ4ðô­™Ø{×nÜ{jUD IDAT'·¨Ö¨n%=n©û¥yò•;NÝ‹“éÛÈ{wŸæÛºÙjò´-ÌyÉ®_»ñ2×À³~C>UÊÚ+O¹-*-;áÙ“ˆwRŒÝõK}Ó(JžþòiÄ£Ÿ<‹Ï7p«èk%¤ -2µÔÌ|zóêíGÏÓEÎuW³Ô  GUW–†“ÉzvÿëèUQ—S6šÆJŸG>yô("úMž{½ÀêVBšPck}ñ‹ÛWo>ˆJâØÖpÐúR{Õ÷œGÛÂF;çùë·>KºÔkâg-¢U§T·WåÇ^:õ€ªÒØÏêÃïc”…¦Ñš¶úÒØû׮ܸóð•ÄÔ§QCcÅ)#ÿÏó“~Ÿr™þ#›5Ù)500'¸P²¾ýÖçSçNµ*â»ví FH l`‹¼s=l_Xãú¿ô·ááá3¢ ¿'CÜA ¡6 @} gõMž›€œ@%L/ g( j“3|=m ¨M@Π¦3µIȾž6Ô& gP Ó ÈŠ‚Ú$ä _Oj“3¨„éä EAm”~¬8öi’LV€œÊnÚ@P›¥‹üÕŠ*<ûûBdÏ–Ôõì÷_ö­¯ÐJ²ÏtÐ7ûór.‚ ¿!5d``€lJ5&/%5)¹•=Fo^ÇsÖ@dàwÀ<¨#Ô&@é–q,¸á¬’KÁ«M{ !ù1ûF7tФ(Júΰ°çB˜ø Õ5+÷ïá)¢4<'ÞÉͺ³òk-EQ\cŸ^ë#ó>^IúƒÐž®Š”"}u`l`-Š¢¸ÆÞ]_Kga·úk¹ÙÚÃÎÂH[¯Bó™—Òt g€r6Ô&@)¥×|癉µvFßœâÁÜú»qp˜Ù”Òœ'+¼NÿÑdò-1!„ˆ#R“î¿:0ªBÄßmGÝmö:ŸUdÞž¤6l̹ŒVòn‚!ïÎÔÀ †®¤åç½ÚÝæõ䯷ÆÊ !$÷ÉþŒ?ŽGÅ'¿9Ö:bjÿµÑ¸ 3@™†é(;Äw×lz]}Öüî•uyšö­¦Î©ûvëš»bB×¢Ó Mm ´ÜBÎ= ñ×Sd&Äe‰,´ó“STV6å=Ú¼å•ÿÜ…½<ôù–õÆ-ltaù‰!„gÕyP ŸPºžÍªé$?I”"ö€œÊ2Ô&@Ù!Myž"½ÜÓ‚CQEQmNäd¾LÊ'„põ,õ”÷RòÄsÿ´wÒâé8Ôì:u_T.˪.-’§Ædj9Øj¾y¦Î&TÚët9!„£i¨ÉQ.æðh–aYÄ3@ÙOj“ £ þËÓ·6Ðny$URd¿Šz²«™žò5ÊI#vìÜme¤X’sçØ¬z†4!lᕼÃ5°ÕÉyñ:§ £¾xËØèã‰&€œÊL/@©ÏxB¾"52êM¯J¿`ƒS£&î‰ÊVÈÓo-kïîÑuw¼¢ð‹y鹬ÐÀH—OÉS®¯š¸.V&“ÈÙB+‘LÝ{u³¾2zë£ Y~ÂÅÐa+“úšsp@Îåj“ ´ãÛ4íR-n²¿ÇÀÿQþóNolòdŒ——gPgȾ=Ù43 ô»bˆñúZºm‹€‰/[Ïl®ŸpûU^ᕈ߽ÔgÚɦùêó5lƒZM ßÛÛÓ ð“Q.SÃäý“RGø]N;Ô>¨ˆìÚµ+88€²-òn¥°}aë7þÒ߆‡‡Ïˆ2üŽÅ<¨#L/¨ä  ŽP›€œàëiÁs“3¨„éä EAmr€¯§ µIÈTÂôr€¢ 6 9À×Ó‚Ú$ä *az9@QP›€œàëiAmr•0½PŠ0)gF7ªÙné½lÁøÒç;úû×î·ã• 9@1 6  ôd ——„^²¼h€—6Maó^žY2º[ýÞ~íæ>”Br¯ñó ðþðÏ€3Y§Ë —7ŒëÕÒÏ/À»Áø+¹Ÿ¬9íÚú‰=Ú7­îà]§}¿…á¯ò !ò„³Ë†õh[; ÀÛ¯Q»Ñk/%É !Lêñ}DÃñ·òŠÚêìÇ[CºÔò ð®ÝnÀê멟f;ò·ç–ëѶV@€w@³.S÷GŠÙ‚¿Q¤ÝÞ9³w›Þ~~Ž%2„Eê £º6÷õ h?bùådù»µ°Òä«K:øõ:”XxýL^ì™i ë ¿”£ü¾cçYÓk¼Z>÷x¼BM;™‹ýÔ6m ¨MP{²×—œ×ì¼¶µ-Bd±{‡õY+iügÈ"gC*_`ÅS¾ŒâyŒ è&$„BkÛkBI§§uŸõÒïÁ ‡™ró){Á'«N¹ÿ„ñê4ªE=ɓá˦O´vßÜQ?þîsa@)ƒí43ïl›¿~Ìì ÇÖ×#„ÐZõÆÎîîÀW~ž¡ƒà‹ÍæÜZ4ÒUÀd]_ÐwøÇîNh%”±&:‘Åî1j4aõ$ãÄÓK§Ñß±º«5W|{ZËAáé„çBŸœqn`›I×%„ÐÕ?,¥ ëë¿3h݆&U!gø&ÈJƒüg‡Å:uª$$„6óò² ñ-† wפ>9ëÔst÷ð*|>œ÷øß¥÷ªÎÜ>½¦žêÒS¿Ð9ö´x{üÜÆ˜t±ð½Ø§`ie½'G»]ŽÍR=B%0qr÷r|}«Åû.äV7¤]5šØ¹~rÀÞ멚Øa•Q˼)e\5îœêw÷qŠÂÕøÕžN›ß:»µç}vsú`„aûm}:ó ©09îr·°¯;öuùL9{-$riP÷;>™Ò«¿êÂñ­¿GdÞ"Žiý^~+'†Ýê C©]7£6 Ôj“JÙÛ+WÒìýL•§Ð¹‡nIõR·õnRË; qÛÑ›ngå°yÿ ªàØaܦ[ !Dò<übº6{bL“Z>uÛõ[v)Q®úCXyvÌaûßÚ¶lá ña©,-êô¶³9î­ëZ('3©a½ëyûÕnØsZXdN÷VÈÓ¢Ÿç¸ØjÑ„"°ò0gbÄK?z EQï?(7[ÊÑÖѲøÿN¿Òм>­c€_€_‹¿fˆ•&7=—ÕÐ*O«y&nŽ‚·ãò‹IJÛ³ieæÞùuìgÌ3€ú¦ µIjŸ3z›¯àˆe,!Å›. 4í«˜æ^ˆÉa¼„jwY9¨#Ô&” ¬4+C®í®SpJÉäeæÑÖA=Ú׳áRiÂÐËÓ>ÈnÔÀħ¾ !„WGönàÔãrVÌ”ÐF5{7tâj;þö…A'®'u²¶à||¶jÕvÑŸ7‘gV„þ9ž·y~  ‡"¨Øwņ¦o\<¹ç¼vޝ®«aPÏŠBˆ‹«eö•öë=“øW~ùÃy7“À*öK ?Y?aùk¿ñ;êÒlFnŽBàÜ«YuMBÜÇEžï}úlì€~c¦·75¤Ó~BˆvWšá¸hñ‹__ÄѵУÅ)9 b¬v9j“@¡6  y_ÅC ´¬8U¬, ¢E&&šLv†¤ð 9¥ah$ds³¥D %`Åé¯%CK*7MüYEÅÓ³uò¨ÑjÀˆÚŠÿöÜLg Vclïì]«Ã¨nI'÷>þø I3=:?3ï‹ÕI´¦&‘dç+_ÀH²ò)MCç³×I_ïŸf}®_ãZÎFò7ÿÛ¸ì©~ƒ®ByüÞQ!wœ[Vµ&Ï.ß›ê:¸•=&óáÁWÕ:µ¯ª_èZ¹Ð¥C]ÍþËWÔï`Ÿzrþ)‰Oˆ¯MÄ·§4tÉgñ‘…ÕóÏMë7ó©ï„¿kð⢞Bh‘­u`°ÏúyÓC=¦t²K:¶à°ÄwJCs+y}ýV¼@$}yióŠ0iÓЮŽ|BQHrĹ9ù V!ÉÉÊÑÒiòhÂÊÅ9yÙb)ËÊÅ9ÙÙ¡–P™±¹1w5+Øk©ã5}ä  Ž”µIïÿ€¨mÎàïoðï‰kImÛšs!"ÏA‹¦äÏXÒw=eèÙ~Úª~.B*ßÖ×!sǺ »r|óêg‡öuÖ „ðz†ÎÊœ¾hjÿ}ríŠ-Ç-ç«C)aÂ2,!´¾‹ÁÙýË&®ÈÒº [ÒȘfiowþÊ= ö%d˹†îÍ&¬QM‡bI¥*&G,™°5—ÙÕì³|B'.!Düpëò£ÚÖíõñ‰8¥å3ìŸa³f.t<—gZ-xÖÌ&&4!„e¢PBÙ1Ï2™·§f<õî=&]7…rn9wAÊ´yKûŸÌÙÖ¸$¤‰1Mñ“í“BnJi]‡:Ìîé¯LOroOk<ô¢„B–tl²Ä~Ю°î6œÌsƒšL½O!$¤M qŸrfm E›ýàÄCÚsZ¡'C©Êejø¼²Sj`` ¾/ð»œ:w*¨}P/صkWpp0ð“È^nêÒýxÝõÛ9ñÕnã$f·ë/xÇŽî6j~¥\ñöpßÎÿ:,Ù=ÑSXô+Y–-âoÃö…5®ßøK>#Êð;6÷3€:­ϥϦíÐú9ÛŽ•©_>óöæ­<÷®Vê^Zä_Z±:Òõ¯ÞnBõÜ@ä  ŽðÜ$€RƒÖ­1d¤ÿ‹ÅÃVßÏfÔëL<íÁµÜj]ê«ù¯4fϤ‰­ojÎQÓMÄý  ¾iÁs“JEÖ`ÔpÁé†j¸]¦­Ö„·RÿøñíƒV\Rï.Æ^jÓ ÈŠ‚Ú$ä _Oj“3¨„éä EAmr€¯§ µIÈTÂôr€¢ 6 9À×Ó‚Ú$ä *az9@QP› >¸¨mÚ@J¢6©sçÎ,Ë"žß ó  Ž0½€œ (¨M@Îðõ´à¹IÈTÂôr€¢ 6 9À×Ó‚Ú$ä *az9@QP›€œàëiAm€Àï@ƒ:200øöl!l_"€œÊemÒûñÊÆõ#\?j“@}ÓòmµIŸßó€%X‚%X‚%X‚%X‚%È Œ+ÖNÿù ÓX‚%X‚%X‚%X‚%åvÉÏ@¹L ÿ‘÷OvJ Ä9.üŒ´á[j“T¾ K°K°K°K°¤3€ú¦ ¿é€œ@%L/ g( j“3|=m ¨M@Π¦3µIȾž6Ô& gP Ó ÈŠ‚Ú$ä _Oj“3¨„éä EAmr€¯§ µIÈTÂôr€¢ 6 @}pPÛ´üpm˲‰É‰ oÄyâò4‘PdnfnjlJQ‚ƒàü¤à¨ ×È \300(‘;“SÓR=Ü< ËCÜRÓR#£" !f&f‚ó“‚ >0\#g€rMY›ôþßß½ž„· nº:ºR©´<ÄMWG×ÙÉùÁ£ß2” 8Î÷@}`¸FÎHJ 6Iœ'6Ð7ÈÏÏ/'AcÆ@ßàçgçû‚ >0\#g€r­¤j“”Õ~,Ë–«è}c#‚ƒà|_pÔmàÂpœÊ©’ªMRŽ#åj()Vcçû‚ VÆkä P®ÓòõI¡X–e¦\ %¡çç@}`¸þeðû  ŽJêg(ŠbJ„äé²àƒÏg(˜ŸN w³Þ›^Jßÿ¡¸køöòæW?\кEÈåì #‰^Ù­E¿ÉßÝ¿%8™ÿnØqñüŸá÷ñ#Ÿ…Ú$(}9Ã/<–©Ô&(ÁÚ$RÜY<67úĺ¥;ÎG¤ÊOÏÎͯeÏÞ­íY–Lþ‚ë¤ðgýäý®•K£×õx»í–å­Í9„Bo÷ þkÏâ-šÝÛiŠtøîE ©ÂŸòîÃØ’ˆÉ¯š™ýŽOaóã/íZ¿õä­r"4q­Ö°c÷ö^?¿'í^ ^JpÈcÅ1§·nØyæÞ놫kïÛ¤sßàV\RAÎjœ6®M"TqË™´ÿBG.‹¬ÜeØ?>&TÚ‹§Ÿ¹›ÚÜî׈©:ù+^þ·ÎXRß]ª|W¡w³ïs*¾SçQãÍpöÇr®BÁ~ÿŠ~zpdñ‡§]úĺeŸ!NzŠÄÇçø÷hµEnägÜþØ^SÌਪD‡WéË!Ã6$¸uê=i¸­0ûÙ¥]çô‹üï´&æjsÊü»†kä  ŽJì¹I„*æP’}æŽØyè˜.uô)Bˆ‹— 8Ðl Kˆ4éÚºñÛ÷91ý:ÈÞ„¯^$íïmÊ—'_Þ¼xšɌ®c%Ac¡|C~ì¹ÕK¶œ|’&Z ÝÁU›ú(kûß²+þ÷"EÌ¡…oûÁc‚ܵ>„‚üHòñ“ƒÃ¦]Y³æž~çE³{;iP„ŠNžãXîËñRÑü<»‰?0`À)¿¡õãvﺪÙoKhcãw•¤ù/¦Ì9ü0>KJ¸úN þÝ·‘%ÿÇöšâ@Rªär&念[¢mú¬ü»³ ŸBœ+W©(è;r㺻5&z$¬ús̽z]œ>•©a[oà„ÁM¬ø„ɼ¶lÙþë±bŽs`¿Ñ}ê›+îÌî55¯KÓÿ¶œˆÊÒu ;¡·—nIÝ€û>PV%‘¾±¢(…¢X¥ï”¦‘&I¸™!/XÀÒ4˰„È£6íŽsïþwÈ_~Y'®»ŸÍ(2n.»ü¾U׫—Ïè»~R虫j6ÌËûñy #y}åN–<úbD†‚‘g<‹Ì5«f/d }˜‚áz´6-tåâÙ#ü³†®¹™¥`å5|¦ÐŠA¡P|{É~1ƒóËÂ*dÈ,!,Ã0 KX² #y¾kÊ´£’Zƒ§ÌŸ> ‰-ŸÂ2Œ"ãÖ¢ ë^yô_¾~ÝšQþ);g-»•õñF°|ój‡ÏX¾dÁÜ>Î1ÛçozšW8,a‹“_EÖãS÷¥Ní[:ð?l š™‹È»ÍWÙ|¹ÊÝ€eXù›]+ÿ#>ú¶sÖ,Ôd–ÖqnÜ{JèâóÇ4ã]\¸øB’ü÷šâ@r†8–©ÆÏ>V8·jhÉ}?â ì[ÚäÞ>ÿB̰„(b]’Wï1q\ß:Š çy-•¾Ü7uêaÛ)«·¯ÝÝäjèìï¥ Ë2yw6“ œ4¦—Óë°…aQ’»™áw טgõMÈ×&»Jƒ_¡ÓŸ ï-ø»ûÝ >U¼=½ýkTw6âS,ËN¥¡s&6ЧˆÂ:áäÅðÈä|û—û¯ÈkLXY‹»^ƒ£¯ßwM2ªŠIæÍç™ »¼[¸~µMž^Šû»Æ=LÔvqÕ§?ÚaÅv]+VššJWõ5;ö¿è4¹_yÖ]pQý{j“~Vp>úæÕÆ>í7^j\¥ÐVç=;p<¾ÒŸ«ÿl`LR‘wsëùT–(R®…]Ñ ZÙ¹º9‡ãv½ªž˜{!&ÏÛMøaø×óiD“Ÿ™šR¡š·þŨ×9Œî'¡øÚ¤ŸyÆëd¹ž›µõéûÞÍ˨n¾ÄÛíóÝÀ›KÇ´óÜz9ð?ÙvžuãîÖ„•‹ÓR¸>6a»Ÿ&JØüðý ¸J©›ggƦ(ô<,D…GDŽ®9?'>QÌØ°„ë:dÚ躺!>z/®N8#Î)öH’ïðéÍ\D„˜5êÚúàà‹·SYJ£úøùêj"Õ82öÉó,EECº¤Úû[⌜ÔQ þ¦[ÁÕÞoFÔ±¬r«{7oÝ»ûÈâÝë ›Lš;؃e ÅáR ÃP„t„”ßEú½+Öí»ŸOièˆ$rÚR®œbßÍ3ÌÄü¬à¨8–©Æ õîhSxtdYBÞ€(Vyâ›V2¥n½Ž{šõljÏËŠBX™Tfœ’#·d)ZyÒ@è )YžTQRσÅs“>(Áç&}ÏPÂÑqðiààÓ S¯”SS-Ýv©Ã¬Jï+>(ònLaò® G9T0„–áYzÛ)v<ˆ¾™Q¡ƒƒµ¦=ÿJ´û« 3_kÁG›¢H<½àŸtÇ +;x™2/Œî·çýiñÇ(^Îðsƒóî˜Ò0²¯PÁ¬ gHŒÐ  ò–Â(ä2†Ð)X¿²d‰aXFÎr].í%z?Üó´4 m„ìÕ¾+®Zþ1sK“JT|؈ÑWØB¡`•§Äߟ3üÄàPZœÌ¯³äŸ^Kb”{†êæ ³NO-r7ø¸¡lÖ•åóv¾­?~Ù\?KQþÝ9=–¼?¾½ßYXæ»rŒ<P•XÎ@iYp2^Åå(*ðÞ½Š´ 2M{c –)t&@…œ¡8|JÁrŒZMžÞÁš÷n%‘6'’°äÝ%$F9ýû.5®q?¨oÚ@~ü7Ý(ŠaöÛ1’¤Ø4éûÿ¥µ¬,4‰<_þá±?…¦@9úŽftbD\žò$ñ㉱£!Odçc’víì™xëG ¾µŸ‹ôúñ3o´Ü+êÑ}˜ôíã8âÔªµ—±€°¬B¡¼@ünƱЊ£¸—ÒÙïCØO·ìý¦BKkšsÓ£ãÄLá·°ãŠÆLÂÓtž¾^Á?ºBNá彉HÒ¬Ò®a%}.aYåUsòQ(¾#(¿&8Dä\Ç…µÿ`t¡V+rrÛ¬²ùÌvƒ/4T–ùJfפ¥¯…fÙ‚yÏfûSƒ >~èXöÙ0^©Ž 'òÈ©×ïOqtø±X¡WMþGgŒøÕÃŽ…³­0ýi<Ñy7ªëëhñéB|?,¶¤`žàƒßöÜ$Eæõ¥c4l]×ËAŸÎˆ>¿ålŽc—ª&´äÃ{ÞÿB§å= IDAT›h{¶öãÌ^»æôÀv•¸1§ÖÏuRݦ宮¢ 'ŸWRA“°”m@ÅÌ9×Hõ¶fÜ·„kèh„Ùÿ?¡'ÿÍ¥ÝÛ¢ä^ïÎúÞo6«¶ÏMb?~nRáçö°„ÕphìÍ›±~õ^¦®iæ“Óû/d+!„oâ Ÿ{ñÀ‰ëŒƒ"úì®ñr‡Ïb¢®ÏM¢ôý{u=9nóßSrƒ[8qÒ¢¯?rÏaì\Weꣲù­zªÚ È—ž™Êѳ7ã:u蜹Ÿ^ê­}[îH¨:ì§·¿°ÏM€r’3”äs“(ÿ^]NŒÛ2iVv÷V¾6‚œçWvo;•åÕ¯—§&Q°,‘Ç_9wUßE'÷ÉáµWx5&zV0ob6büդkÊZyq.ž}YuD_MòÑIùÔg¸FÎê¨Äj“(eâ7KiݪÛÇ>»mÁá†PÚ6þAú¶°à(^úÂ8OÖòì3ýÏËvL’ÍŠ¬|»Mê[×€bYž…—=ÿdzMm²¬Ð¡–çZ^Á'B›5ìß+bñöE3öê;7mÑÜöõUU—ì‹7Ê0 óí?AP¼à|4^}¼iŸŸÔ-ï>#:,Z¹uÑMži•fõ=£Ž°,ËRº¾Ãgôß´~ß’É{ò ßÈ©z³`N¡àÙµÖîåâ¡“hã*M76 {öQ(ÈŒ¼?=8<ë¶“çêïÚºwßÒS¹,šºVìÝÄ’óêÝ(¯¢ùÆ–Ž_Ü Tõüûö¹ºeéÌp-‡:íZ:Gû™ɪŠuJ¾÷X¦zo÷÷½[ön™{PÌÒÚVU‡/êägB³¬œBi7ÖÏØ–$ÙÖê3µ·»&¡ì;LžÂݰiçܓ٠Ѳ¬R«µIùô¤-Á‘ø]Ã5å25üGÞ?Ù)500{,¨§Û÷n»¹¸I¥ÒòÓd>ŸÿèÉ#/ÁùIÁ(zé‹õæ<ë¾rv€öï½ÀòƒÃuxxøŒ(Ãïx#æ@ýÆç&•v¿ä¹IN¹€úøuÃuÁ3>Jî^æÒ6\#guTRµI¡ E¹zð¼B¡øö’}ÁùŽà¨QÎðˆë‚O`û¡áw ×È@}ÓR¿éV’eŽ¥Aq'Šà 8ßµ¾~ÅpͳëµlcAÖP.‡kä  ŽJª6I(Šóľ œœüQ%Î …BÁùyÁP®‘3@¹VRµIæqñqzºzò7I¾$#3ÃÒÂÁAp~^pÔ†kä €´¡j“ôõô†Ix›ŸŸ_‚&ÌÍÌõõô ‚ƒàü¤à¨ ×È \+©Ú$…BaldlnfNÓåâ'φ‘Ëå2™ ÁAp~^pÔ†kä P®•Øoº"“Ép&„à 8®áGШmÚ@Jâ¹I€œÊ 9À)«’< g(*m ¨M@Π¦Ôž›ê¨Dž›ŽH@™ˆœ dj“‚ƒƒËOÄX–¥( {ú±B¬±B¬ÊCïüâúmÔ&:BmÒwÀ/ø¢+Ä +Ä ½ƒœÊ<7é;°,‹  +Ä +Ä ½ƒœÊWÚ@ðܤâ`A@!Vˆ Vˆz9”˜^øž„ÅøŽ>B¬+@¬+ôr(7P›ô=#® ¡+Ä +Ä ½ƒœÊ[Ú@P›T,ƒÚSôb…Xb…X¡w3@¹QZ¦äovt¬Õu˹:lL±f*³Î¬Þdî} ö5õí#Ä A@¬Ô-VŠøÝA5ƒÖÆÈ«—wVýÚÃ/å¨aË“ö÷ªÓjí )¾õÃï3€:*‘ßtS•˜ç¿ùoÛ²ÍG.E$IOÏΣnó ^jØÕôÔ999GŽjÖ¤¹®®^±¯:dïÓxâÝÂKìû¯m}¦ï ãe'¸c'ûŠê#¾‘k½àÑ#ƒ¼tq)GU¬“~wç¼EÛÏDe0DÃÄÉ»AÐ࡜]Ý»kµcG°å—f¹7BšLà,8<³º¨<íWMf/=2·ªð§|žäáÌ6ýögB1î´mïvyÇÞQÃN­n ûCéG=•wgbËÁ]vìîmÇûeßA®Ž{@Ë.}ºÕ´ü„c‚øî´¦³?[nØnÉè˜a!ùO­knD—’ñŠÒ²õk?2¤O-u9d%¯O®š¿òðí¸<ÂѱöhÞwh7_º¼|ë‘3@9OH ×&I_ìÑuÉS§öýg r1`RžÞ<³wó‚­^U'ºk¨c›³s×¶´ô´»¶w £[ôëÌgOk¦yU‡ÍèRÐ:ZdaË«0Ó@ÃI€ýë÷PÕG|ßÑK†:Ó™±7v.YÖ‚業Ì9Õ§±’Çí1dEJó†{Ê^<¸ïDÄü~a¿ú©4*Ù·¿êèS3ͪa,`Ÿ”øGðí:†LÉ´3âüªïàg’õ6úÊñÝ«Çt½cùûþkÖŽéXÏÇͳZÝNgîÜ»0Èš›qº¿o‹ÙwÏêÒ°v»uÑ1&vlÖÀÇ/À; IÐä=OrYBÄ×CÕ°nõÄîµý¼ÿœw!ñ]A’"ýΖíëzûÕk;éÐËü’ÙÚÜÜÜ]»¶§¥§Q•••¹3l{vvvÑoQuGÏÀÑÝËÃCùG#êŶ© OÅË?¾:óòø¬¿ZU÷ ð®×yä–û™Œ²æªóÂýë†n“znVï& jzûx×¶áNCH~dhËÚ]ÖíšÞ£‘·_½vSŽ¿L¸²t`˪~uš Ùô0‡%„&ýƆqíxûÕjôWè‰8i¹Ý±UõWׯÉÅÕÝ/°÷ô±~äÎÑ;I¡-ëôØyrÅð¶Õꎺ”Ëd>Ø1®k ·_@Õ¦}¦~.V5óÞú1A~ÞuÚvl[ۧסDF|=¤Q­‘.ìß°FË$ª"ÏäD„¬­AÛž³ŽÇI%1GçöhVÓÛ/ F‹cvªËtüDZbÒïœ|À­=!¤[ow/߆ÝÇ.Ý=Ç7zJ¯%1²gK:V÷ ðîw6)jÓÀŽÍüü¼ýj7ê=÷èë|ù›½†^ÌÍ9׿~€·_›÷®OlTwÐee1„"~Oß M/eŸÅDVö«Ï¯™ÄZ8¨i­o¿­Gþ{5EA!l^ô¡ÙÝ›Öôö ¨ÛuòÖ‡ÙŒ²ä²f‡™Cû¶ªííW§åعŸ\ѤšZ".EñDZš¼¬ÓúïI‘ÝŸ\Ï?À»Þ¤â/ )ÁËŽmTÏ·ÏáØ§_멈ø Ëf­‹B)×VŽèTÃ/À»VÛ¿‹“å€\sXXؼ?ëùx7°ôfC“~sÍÈN5ü¼k6i=hé)Ì·]]=ýêw9ËŽAŽWÎÚ÷F®rl,4Fß[ðW»ºµ¼ýüÛ ^ô_’\ör}P¦‹" Òè%mëtÙÿþÌŽÖ´tu÷ðòððr³×¡¹úŽn^^îÎæÜ7GÌ{%W¶kȶ ;ûùÔîµðbüë“ÿô®ëÐ>dwŒrŲ„ +´«çíà×fèÒË)Š_?^¹T®Ö ç¤¡žì“ ‘¹Eºn>¼x`«ª~µzüsZyÌ”%ž_:¸ioÿæ½—ßÎ,سTrßrpy/?&üRª]¯ C[úy¹{×iÕwÞÆµƒ*ñ¿Ð‰*29{Bº6öö ¨ÑiÒÞײ²ð­GÎåÁOxn“ríÈ®ßvŽ…/²sôì+èq!²”£³w§{ ú+À«U¡Iÿéë7oÞÚÓòÆâ‰{^É!„É»»ûšnÓqs§óIû{vx2C!²×›×<°ë9uAHsî¹…³Ï§þø÷X,ï Ûžš–j``ػן&&&;öçäUø©ò^XFñ£j*“ɸ6k𒘪c¶<°{zÝä !ónd³„Ùëmë9|Ü. ë½'/Û¾eÃêaîÑÿNY¡¼B¹ñ(Ól|èØF²ð™]{,Œñ4j°ùƒusN$(ˆ,fû¨QÇD]î>±o峋ÓBv¿”•Ó»È_á¡(.M(EQ„È-YrC·á€¡íì3NL¶:Æ{äÆí[V÷µ¹1wèÜëY¬"ñȤQkâ|Æ.[»aNo¿óïLî•ÐÕ/lÛè×Ô’¨ˆ|ÎÓÕã—=ñ¹eç¶ Óÿð! )¯öŽ›sÉüÏå»w®X[ómºLcE ô ’ç7cÞŸ)Ð|mƒj!ËûÚpìþZwêäñs jërõÜ[ ]´nóŽg´çŸž1ëtªY‡å³ý…šó÷?{rëÀ ªê\ò?Iº¢,íWJyV 9%ê<Óžæî>f÷+“yõŸ~ónÙö]¶}ÍXï—ˇM=©Öäñަzöš:¡½þµe£×=ýòÅJ·î¬•­ 8®ã÷Ÿ8~öà¯ü/ )¯6†ž¡ü{Œèæ¡óõžâ¬^öjëÈ1ÛÅ gnܾc~QøßÖ<‘B#¾¾rŸ´þ¸ùÓU~±iú¦Èüœë¡“6¦Ö›¹yÇ®å!+ä%ä(Š+J£b»~D‘Î'H¿46*×Cx¦U;M\´~çæ•×ÉÜ9uáU‰eÃvÉçO>Ï'„éËÓ§S:Ô2+æT #¾¹ñ?àY³†ø¾Ý;¢{¿õ¹ BæŽoÅ¿´pÉ•t–ˆ­0ãº]¿•GíZÚ³oÒôÉÌïØ¯XF® <-‡/ž¬Úç3 tΈ:™g,½C¤Ï6½/¿aȵKÇ´q–B‰ªF¹¯\ ˵LµIâÝGÉ﯈q„Ú|ZåN¡raúÓ†-ºgÛ3tùòy}| 8eà[_òP›ê›6’¬M’&G'±fµì¾pëG¯ùŠMªiQ„âúGBâ´·^uÝEsî'H‰ !´fÝ™+ÆV×$„­Â¿}yâ¡ûY áÚ \1ÿO;a=%GÍ¿—ßÄð‹ˆÜ›’š¢¯oÔUKK«s§.;vmOII>pp_÷®=‹qÕŸÚøô»ÿÓl´|oýO_‘ü߯óz½Ãþ¬eÉ!Ĭû €ý!'¢óþ"„k3på'íòèчF’‘”HjúžŠˆÉa*B¸n–üÝÜ–W|µûøþÚsçýYO²ÉÑS½Ío™¶c÷ÛÚ/kï©IˆU«~Á;ƒÃ¯§Û•Ëú›/^b¤iÏ/¬\r•x‡øèP„v³qC{3‘¿ÙÙûºvû̓+ q5éV»Ûo5}µù¶Á[G´qäRIëÑ–ÝW Ny¾3v.®¯O"y4ëóÈ'XD¥òì}}+ÛëQö5ˆøÎÔDÖ¬s ¦t{ÿfj+êGÔ4÷¯æánþիר[¿–“W¤%äÐ\‘®žž—¢Õf€ae9)I<ÿú7>z+oQA‹Os8Úzzú"BòTŽ ‰ŸÄ¤ììW®C<Þv$Ùgôšî~†4qê;©Ïù ½‡^Ô¯´íœ¼Þ?ãÚzkRqð„ˆ‹}·]HlàOÏ~È¢½l¸„Ô¶Iü_ÐÑÓ1œù_8Íæii h𧩝§§K1‰‡¿0¤p,ú¬ùwPAe¤ÝÇ"{JšPÐ9χ=³í·»w=+.!¶“‡_i±p÷£>c¡„5笟 MH¾áÃÝ=xšž™ÿ:WËÅ¿z%;MbçäUç;c%´ñ² —¢ÞÆ«û½kµfåný*Fš™’Ä©QËjïÙ')Ä¿^ ÇUaÇ_ pu¡bNŸKsù£¦Iq¯ÌR¢Z3–ô×"ù¦wwœÐké”öæ4㑺ßÖ'I²ªI›ÊÛ,X‰OˆeÇ~-w<ú(»y=]ê—îWŠì—V­0l²È]“Êð‹‡†Ékæ57¤‰ÜáÍÁðC³…;öÆV¾gD SšÊüËkN$"O8öù(7ÒËü+—ŽîQâÙvÕù쨙͛n÷ ðõõ­Ù°ž—•¨:À=MRµðí¶‹¼V«'uõÂX%…mÙ„yä P*”ð­ÏŸ%æ…îE5^~xwl}DóýÆ-ᦣedn¦Ë£!ŸT|¶O±„° 9óîøùåSy=Ñü0Û³'N_½q~õ¤Íaí×ï9mצzÇOÿwýÚ¦©a›/ÍÝ?£¶:7l]=[-ɵ1S¿mÅÓVŸÅ¤Šˆ*ÝûÕg±û–¥*×Â2 á y߯)ßÚS_jíçÍPî¯<ûn+9Ÿ>zþÚõ“‹÷o:Ôû†¾Ž¼bÇJ÷ X45åF©jHÎeÖøï û¨Þ öö¬n¡‘~¼W› Ê}©Në ‹7Ÿ|Ò&Ës˜¯þ}“ §ïÆVVÁr* \³¬™ñû‹.<-}ú׎WÃl"WŽYø8§`·~ýÐPÐE,#gYçó%õÕNVupù GÇÞ¿¹½ónÃ2.…t·jo—¿Uìü˜EŸ/äÅ,PPš.Sßú’‡û@}ÓR’µI´‘o ³ìÚÚÏ%„ÐBÓ •œ]œ,5?û(Òž>Î2kÚµ±³÷K©<“u'Iääd V9÷w]uà›»š1±ÒD†FFÿhqUŽÇâWw´z´ñ4æS„0Šo¯h]{GÍÔG±”þ»õèËësTÞS¨céXÑÑÆ\WåYßÜÕŒys7V™H°y1wßr,+š›ØèæÆ¼ÈdŠyša¸FžMºž¶l÷†>6IW/¿‘RºN ‚M[¼y÷—œbòÕ0VLö›×™ŠÂ—Ìù¬Tª 4M¹‚B䉞åWè\ÏV‹Cûþöêý+¡xB“›-e”§_ÊK•ìg1‰“—þýêãs)=QÊÃçÙÊ×É“#žæºXš»XÒq÷^)ïª'’ØÛ±¬™‹ÉÇÓ ’—·bVž–E>lâ¼ò7 )ßÒSïhX¸çF=NQö“õìQªÈÞNõóˆ†Õ°¬Ö²ßØYv®h§sដ»Ë¾+iÌ¡µ§%ëZYÕiÜÝWÄ­kW? Mȇ'åsÌj¶vN?µbõ‰œ*m«•øs“¹F.Vœø‡oùï·Ê@[@ÿâñªR•v3þé¤>eäögBò¾ùÐÀѶ2ç§D¼~wO=«Ì TrÆÅ:¾ÊÓž%ä½ÿdZÓÊAÈ$ÄTE' -T-4r0TÄ=Š—þÆò_pÄÿá禠†~Fm×¢ÕÄÞÇ{®î×7©¯.Æœ¼”WãbüÉ ¦kï z¾ãˆwK«ìÛ{–L—W+ø†æF:v™ï"Lþ߯…wLÛl«,$)¥}á˜ÕïQgͤ±“ŒÆõ¨iM%?¾tü"¿ÇUEíÎFÙ'¶î½È:+^³æµ¬Ò·|‚е[{«.‹ÆÎ%[»éˆ_Þ>~$ºÖŒi ô¨r¸c¿8æõ;û®™7m¹Ë¤¶¤÷¶Í:Ïm¸°ªqúúWÌÛ¢ä${þß®q2ÛoŠ|áþ§–=öéдº ñʃT [ÛØyv9tlPQ;ûöDŽuS3¾ÆJžpxl¿+NAUµ×g“n‡-¾BW›\ÕH?ÒŒóúÜék>~2Ž£gûǬë$ýoÓ²«yT3BßÐÁ@|àØ¹ûº6,ÏÖÒÛ‘>°yÃAÍêÜÈãÿnˆ‘Û&åÔ¸ŽI%#Né߯d©Ñ÷ns”𤸆¶Á-ŒÿX2o›þ_5uãO,ÚðªbïPcýnõ¹£þùçञ¼¨s÷äøü]ÇŒ#%D–xùØyç3YÔáù»sü§Ôûøþ#&?7G,gY™8'W*ÔäÚ)ŸÈQº¾=›i š1jEÞ€†Ž™ÑWŽŸJi=g¢è׎W´Ž÷ %£ž/5Ï~ÓØo?4ˆ\Úúó‡-œ»‘in™qïЦc™òê_åt¨øbl«äñün¡T³à¶5]-4r¢ÏnØkÑqj38¯½Š…³Ú¯Ú8m™åŸ>ܘ‹»þ}*3©QÆŽ&È Œú)¿éF +÷Y¹ÓjÃòí›Cö§ÉÇÀ±z½ÁSº¸ ÈåB/Óò1©ý˜¹ ‡Ô°¯Û¹s݈5¹ï'ù¯Ï47…ѯÜnÖ’¿\4ˆ¼ô ´a½k&, Ý8¥ÿz ˜ºÕéÔßTeå RÏé½¢'¯ éG™ûwlÛÖâ߈oúA¥?—-ç/X¼vô¾ †èØùvsQåsÇþŽ>â˜4™½8mÆÜ½öäPzÎ-Æ.ë§CSîCç Lšüï¸á2]׿mü Ÿ&ñ¹ßù ÆÓnž´ÿ ˜Wë2#¤‘Ýí'7Lë»2—pŒÜ[OÑÊ’£†±âY5ïß%i×á%ã׿²„gèÖxÄÚá¦Zwà€£–í½Þ¨áÂãÆ4 Y:uø>ÝJÍztñ|F!<»6£;]ž8sÀqaå!Vv;êòøe3Ç6­Ö±{ÛÅ— ¥]¡šÑÂ1©¯_úïg`d·—ŒúëÝÿi6Z>yÉlùÌE£{.– ,ý:/šÝÙŽG¿1«G/œ¶fH§ V˾î Åã›™ÐÌB›qkýȯÅ«:ýLmôqY¾äñüöÊßt›Ø´s§m{‡;×úWxHèàÞ\ÛŽë7ÿÊB67æjÑ=µfÄ»ž·í¶`^ÎÌþØ”Ï5®ÒvêÊ®B"Vu:càé)˜¿vÌ–Ñ´­÷ç잢o‹•ôú?ºBh-K·}ÎïæoÎ'„56r­ÛN~oòÊÉÃ6yvªðâìûÖUoRбkï¡õ†9JËgøú9šsV-ê·=‡p *ÕlÙ׊ÿ;Æ+ž]ûÿ<í9lÂÄŠ›¦}ë¡Ò­1ffï‰3VNþkéÔÒ÷Ñö‹£œ´89ƒÐ­ß z›÷m›²'MJˆ¦_ðÜý+ i¢¢†•>_(²í9§š¦º¶÷ÅËvèwuösú(ïÎŒ£å3ŽNó!Vد~˜üÍŽààc-wnîaÅE¬ŠÍ8?¦ã,½Oª"B¬à'÷Î÷]T ŸeøoÄ<¨£Ÿõܤ2í·ü’<ü¦>’éË¥# IDATDîÚrKßËÃF37úø¢ÓTí…NBÄ ûüîX1)—·ÝÔ¨·ÜY„XA™ëä  Ž~JmRY÷[f*á7õ+‰¿ºaŦ áè; í­E!Vدà7ÇJw&ìNý‘e"…Ç~…ÞAÎ¥&m %ùܤ$òsú’š ,ÆwµåK¬„^#7ž‰XAÉÇŠkÕeÏ¥.ˆÕ÷Ï:xËÍ`Ä ÊfïàY« Ž „b ¸&„>B¬+@¬+ôr(?”UIHŠ¥B¬+Ä ±‚ÒÙ;¨MõMÈ×&=ŠxT®‚VÞÚ‹>B¬±B¬+@ÎåWIÝúìæê†` g€2ÏM‚2,W"]~ôá±HEª˜Ö×`j[åT5Íå¡PT‰%æžîž\.OȾ!m jôÜ$€³üèÃÑÜê.¹R*G"»’˜.Í—V5Î@d!çMüBˆOD@}àÒ¨#Üú eرHEu'M¡ *IšœÇÑÕÓ»š¨°() ©TŸP¨Ì3€:*³µIT1w ­(‹RÅ´•‘(6]®ü_-¡à™”CÓ¸‚ó~¯g …0 gø?{çÅÑðww¯½s E¢"Šç)"ŠEŒ±ELŒŒŠ½ƒJS° *±wÅÞ»#XE,ôRë»ß(–`0ùñÉr»;óîìÌÛfø2³Pnâ› $„Œkd4 ²ˆ¦PÚð‚P$¼—rëeVI*>ùŽ–fV];÷ˆ¾\výÙ›ÐámÛj W‰øÒ¾˜@½1@ Í€@ü”67)ùá]±Xì`Ëÿ³WX¡Pä—þsϵ£±‹Š9“‘0Í©õ¾"ªòâÔ~mvžÙžùÁiIzÔw“ŸN?ï©ñÏÅŠ‚ƒ£¿?Òg×îIæôoªÍ£8Ç 8@((ƒ¡¼f(_nR^a¶±gdhÈûäPÓ6Ø–¬æÞA“$˜8Agb²¯ªÑ_øÉÅgMºD9Ä&/º3Ão¨‹@à 0:øhzÕÒªÐ6à .+üãä–ÈðeËÃVnÜw1£‚Àey7÷E…†…m>p¿œ¬ÿ%Tÿ±#zwš#¾Ø'ª¯þÌ8ÔÝû Üz³Dþ/ ¾öêTÃøÓed«2ée•Oât÷‰{-}wЬIÛµp´ _àÐËwê–¤òúú’wã½]ü>¾ów?®©?++¼²nü ^ün~+޾Q”831xÜ€n|ãÀÉ— d­BPMVªq¹)ʽ´ÜÃuæÍÚogXþS»"+’âû órä m¾À/è¿ñ¹)@Ê Š3 ”¥ÍM"›Åa0ŸZ8BùÌ=/‡9ñžåWT*êž¿x±Õ¿'z襤Ӝ~žfÆ(¸³:j¦šÍ‰YÅ'ÿqœª{~vç¹â®£§MhË(½w$þàÍ™ã)ÇoˆzN[ì,¹¶!þüS»qª κ}Sdï×Qþõ"rÅŸLœábÊÐ>N]øžc§ Ò—æV+¥WWÌØN›¼zªÍ‡‚Ã8vùÜ™xäé;N‘DÍó”ã·6Ó! 2x”áã_ÃÏ*HуƒwÕûÏX1£kéeaç˹íù<á³'¥r€ºçžˆ….eˆdE^“3&€¬üüî4ãÑKW…Œo›¿(òZ @ž¾ëL]ÏiQsú‘çV,9’#ÿ =’ k¤„ª&ç¯û†åGU¦“µköÜcÔ å± ›—ý`§ŠµºEhií¿<˜:&̲¤ )¥2ŒP”g•ÑMì Ù4Ç' 0Àpœe]¿!uh§AÿoËÓÄñ—k­R¤\tÎ_…AȺ¤MG¤îó#Wtx½sÅη9n²œÝ¿>i;.xÍ´+kî–“M]U—ºyjðõ‘aÛ·mX4Ø‚ -²åÉß¼x%Ò²5UÁ˜Æ ÉÜÇÂâ'¹ ƒŽF¬z½Í¤£ž$3­L˜ÿ¸x „º…µZEÚëššœg¥ls+-€nÐÉ„(x”×Â)aS•j\nM§ÎÔ&E-ÙQî’°wÌ‘–¢ÂZE+¨?˜y¤W‰1®öf%{“qq÷åZ»!½yt «î¯›Yè»6tñÇ‘EfìH'¾ ûà)ags$hqe§JˆÒæ&Ñé ƒY+a ñíL‰ow“Ûi9ã±ëŽl||É@O¡°3ÅQ“NÉÛi‹dV“†u/rI½Aš5I;_™ô`<>u2µT¡ní1jlocV³ûý°Ï Šš¬k›ãŸj{­³ãˆ?{vÏðø¥U‰vêÁ‰ŸW“V@3ñß9ÁŒ”½øÔ‰È”|‰W»¿¼ªº|ש*Á’~®š8€¹øèÖû-±u‘Â7BŠ©úöEâlu6Ë…2aYÅTaÖŸ%8êl– %5•bš û­r@p4X ,¯©a~p ØêLy^¥„•ìx$ëÞ4Q©ÆåÖ¤þ¯¨ÉΪØvw²6ã‚™Ug×o`Ð&Koí¹Tk3µ—þÛŽLœê;ùÈÜÜ7|ËS†,{×¼e÷!ÛÆX²±Š:>m—éí9Úê´ê´Ó›V/ŸÉ2Ý;«= iAÈf@ ¾¥]7‰Nc0™L¾µ*ër±Kéun¶mÈ¥Ñù«I;f÷•×hVÂã§wSû+KÃéôúÖ‚s4¹¸\$£0‚AÃ0†š:]!’S}U]ñ“WR£ï­ÔZAËÃâT)I5œ¥áoÏ’ g1œh¨°BA½?ÛðZH9Ù:ÜÁMWªq¹ýõǬãÜ×**Ößç…‹«³£kÿ=L9­;©Cš}lIØývþñß½Ó*™í&Ånz|ýÒq«Vï›&ûýIåËGÓÜö¼»&u¢kNè…7«½êO´·ÖÊ»ûÓåkyÓÚ[2„lâo™  |¹I :N§ÓhÄîÙn>AÇúõ$¦Öñ¸ò 7ïX:ØÞ\ûnòke"±¿ÔÌÞŽŒIÇUÚõ4®<–¡}Wá¼Ð¢CeûêM¿§«¤‹Ì4ûÔ DQ$à4þÉýH’ˆzk¤9G—ÎúñSÜr/ÿÙñP¤œ¤Zùê›ÿíþ ”ðñ™«ÅmÇϲ2bƒQ»¦[ƒ=vϽc_=˾“Vö­W‹ÏFfZx7¨=]ˆ÷5Uå6¶Ü‹/Ê)/ƒæ)‰3à þü˜&雿®M«¥±éØû¶G5Ñä=ÿuƒz{•Bª ÞkÕ-œ«ÅqäíbIâj Æ5åй:È©‘€B\-ƸÚ¦ªSV+zëLWHªÅWKE•ÉÉ»€BT#¥«i0[¶"Œs´š¨TãrkÚ‚¦›ÿ°é„ÍÅSWï&[tç‰){¶O²hµ3{å%WVN)¼jûHó÷º>ÆÒ5·Ñ5·¶^ï³üpÚôåA{÷ëIUßXæ¿Ó4hë/ΪÙZj†ª¸¨JDBY¿$„¢lSŸ`2YE€¹îþ%ƒO_¹]–ûâòíäcÁ>]ÛéSÉd*IPÇ€”ÿ£®—¬ÊH)áXYiÑmû®š¹çŽŸ«íänÊäÚxZ•œ9r±ØPÐNõJþ&íY® ÷ãóuY)ù„i'HsŽ/õ__õÝæu~8Øß­¡fÌc”¥eÕ¾›e ­/åõ?žTLŠë䀿ýGGŸr ùáR¬¢ô³×Áŧ£  £Ó ‚ h4( oþ9Ð¥&ÑÔŒ,¬»ø®\ý=÷|Ь=/Å€ÑÙ)¬‘’õ¶nCÐì_³ä4Í´¡ði¡¸¥[¤ÚVœò´l! ÉM-ÄŒ:ð¸úvmˆÂÔ|q½Y™ý´„iÚ^—kÔÉÊ{RŸµ¯¨|™Q£nm®¦jÚ^Wøêù€¬(54´ãµð¥Ï0nS•j\nMûÀI’b9zOžº}_¬¯f浇å­U–—^ ›ú´WضN‚©ºJ!IÐpœ­ojÞÖ¼þ0Ñcãtu#SNÊb7T]ÖƒJ¿Z7Iy{$„¢´¹Iº:…%<}cæhepbùÐw^úyö17P—Ëå…%º:J1²ô̸åwÎÝyZ2õŠ)̸pú6Ö]zkÇÚ}ŸÝØ`·–ï¿\ྶ0¦]?“ìàÇšßÏÖ©w´)Þ<8yþw©9-÷ÊÖ˜,«€0s¼e_>yY«‹¶ðñ¾È‹„ç:¾ZÅ•e“Cž;/ZÖƒžŸñ p¦Ž©™.ûÒ*pÍ ˜k#vÀ ãªG‰ §«ä­m‰Ãÿ87IÃÖÉDvøè±-oVé½Ó7+ôÝ ™ •^K̲òcÄ$@ÏÒ€º\X‡ud•牴l XÍ¿ßÜçç3àjf¿µfö*óK< üXÂöã\'Zú™mÛ3å¦ÿn94u¨‹ŒZgçï®Q–r:á®”¬ôm‰” …uµ"EJ…55BU.—Á¶ý®7wJLìqÍïÌËÏE^w]è¬CÓpÖM¼~s9t²l¼ÇvYÓI…ÆuÒ~Ó–È»™=˜wþš®çµÈ’Å$úäضþùOv’;±ûŠÚNìcÔÒu;fÛF+%M5zΊS›i5&7€’×ÕŠjê¤%¯«­©!Ø*lÚû&+ËÞµpKE¯a^ö}ç©PݪmkÈnûs»¢‹FN\rµ­XÍ’ŒôŒ®n«Ü>?^Èïëb£#Ï»µ#ú¹fŸ©íÙßSò"~êš¾·{g=qêá I~ho}´½%²ˆ¿m6€òå&´).-|þ*­>‚0Ì‘#¬Ì|R †i¨iòt”¢ ,ÛŸgô{±Øï Z÷…K;DI“d'†D”‘š|C7L´ep,ØU.àš]=ÚBºžÀ„ oýÂG»‚äÖº?¬ eB ò‚ËQ36—È9–ýfošã¨Fæd¾¬"‹.„ø_x÷8½1;̶a~i–&l˜úóâ›—Ý¢õíÓýÉo­m’ÿX/'xž³Ä»÷ì¹"–~Ǿ¿ú˜³Þ=’ª~tò:Í}aG5 éè盺5võ]LÆõëÁcÍŸšóWO¤› [¹úù¸‹·ûmýwófß^2/QßqøØÑ¦ëoÿËr3´òÅâ°ˆ¹Gè¼^#z[ý~ñkˆão!y=jì¡2€¬Éž»µGl?<¿½J׫g„†¬ 8#¤ë;Ž ñÒôÝG¾\²rê^ÛÄeʺ9ÎꀑOÄòÜÅk‹'U-ú.ˆšÔ –?¯ž_¼)`|5®ÙÁ7(|´iËw3­”H(’¬q¹QUW¼‚ÀBŸ~`t)®ŸVC» ´í홑qs+“×ÔmBX =§åwQ´«¹úϊȪ¢˜€;ï~Åvß’è`ÇØthÍ‘Â9MÛnÀ¢­ŽŸ›}DÓîÔ™{h͹-bP·òœ3·ŸÊQ^0ÛàóÿÏõK­Êûõë‡äˆøwùW çÏŸ5j”’}pSÛ š'§.iá9v.ܑۚ‡èQèÀ_J—ŸYï¢Ò"›w‡E÷'÷k—[!OÉÒé4m.ñðyîa?.úðÈ/È÷p÷P¶R‘ÅÇ~øþ˜÷¾£xÈŠ@ ¾>ÿLS:þüÊ íp!Š3 ”ô3PÎÜ$Ä¿Uu}þ°e?9Í´™¾=z”ñ·Ø/ý·s [˜ÒÌx—]K8Wmao©‡•ü¾kÛk‹{ê!ƒ@|‹ Q ¡ÔÖsk3(´]Í[PÝuõ¥ëHïiþ9ˆ/BQýô䆨-"¶±Ó°ˆ°¡mа‰@ Í€@( (¼Ð¼pœÃ/Þl9ÅeÛ/¾r£‹[— …":0hXµP¬§B ›¡‘XÄR–õÇ€f4xÍ‘Áè¥ šk‚PFê³’@‰]E þ1Ãì¹)/Ë$‰¶ AÉ¥¥oª¾ëÌAbi0ÊËË (B©@q„òš Ðúr“€Ÿ=-)xqôQA©´ØT3©%«ìU& A#x†<3S3$ @6Ñ(7 ÑŠá0éÓµŸ>I@ -”›„PFPn@ „ò€â å5å&!Z#/Š„Ë¥‹Äï¶Uz9ÈGPÀ3äÙÛÙÓhhxB d3  ÊMB´b6ŸÍ`«jIiŠ;ùRO^ȇ‘W]»tEÒ@ åå&!””›„hż®¦ñ´UD+³NIã …T*-(,@¢@ ¥ÅÊk6@ëËM:ô7w·Žö€kj1Ðqœ „„¢ã8rÜ| EQ€!1 ²ˆ¦@¹IˆoD7F[¹5 †!£@ Í€@4E}nRÕ§`B‘ð^Ê­—Y$©øäO8NXšYuíÜ#úrÙõgoB‡·ul‹2OMuÁê„lâÿ0@ùr“’ދŶü?»‡ E~Yá1÷\;k°¨˜3 ÓœÐ{D| ÃPœáó¢Q"d/ãF¹óCâö!ú(• @|« þ¡Œ(íÔç¼Âlc3ÏÈÐ÷É¡¦m°-Yͽƒ&I0q‚ÎÄd_±œTå…Ÿ\|Ö¤Kþ¯{\œâÀ|tŒØ™ÕhµÈšÔýËðvsà÷òš¼öl®äï]þ9o’÷…Œ÷éãÀð§ž.&ÿ•z)os“š ¨I¿¿6bÙò°ð­§RÊIœ \–ws_ThXPØæ÷ËÉ·?¬þcGôî4F|5°?Oh +Š˜8ÌÃ/ppúãò}÷ß(PWù¹¦%«|ÿ£»OÜkéûo4m×ÂÑ.|C/ß©[’ÊÉú³wã½]ü>¾ów?®©?++¼²nü ^ün~+޾Q”831xÜ€n|ãÀÉ— d­BPMVªq¹)ʽ´ÜÃuæÍÚo¸]‘Iñ‹ý†y9ò£g'<¬"@^x9z†ßÐ^ßÓwNÜÍ’W”¦„é ã\ž«ÓÄ ËŽñÑ0Ñãs)ú‚•g@(#J››D46‹Ã`0>-°P>sÏËaN¼gù•Šºç/^lõïÙÂ_¦ê4?!®æ^&}µwqx¡*£?Yzq¹tA¿ùQ‹Ú oÅ…, d™ïño÷¥—²:iͤ™w,ÆNXáoÌ–Qzj­-ŽYã Šò?öz€÷îoËȹ~äÄÞ›m¦{T¦¿!ê9m±³äÚ†øóOíÆ9¨‚8ëöM‘½_Guú× Èo[AÕ¦lœK’ĸÓ7Ý®äåS%¾›3ÅJåÍÝ„ðÍóÂ­Ž„w§å]¯XtêÖµ-Œœ~À÷à•ËϪ >ÛC@Qpè§¡‡ÝìþÑŒ.+¼·bãᤠàÛè¹ËzhúÉç€ë¾}?[ÐD)‹Ï­]™¸Ï%kžˆZµíBF%¥få5iż¡VŒ¬LŽYµýVžDÕ¬“v‰œÕRÚÃfÚä©{ýFìo8W÷ôÈ5¡ãü_|µp0››tnêá¤ò>üûGïÓúDýâÕ™ öFÓ¯œ_qôq ƒÉÄg:¾»Ç÷¶a@‡À §|wœxùs[üìÙB‹IGö0"À*pÔé^)1®e«w’×MUªQ¹yÐÕpß|íNÝýeý«÷xTü±meäowòD4µ6öf/ŸÖK§¥·µ+†Å„uQo í4_^üébr¡¬»U×9ëßí®ÒAãÙ©nçV+@ EéÕ3¶Ó&¯žzÒ?æï ‡gmßÉé£ÊòÐ ”¥ÍM¢ÓL&ãu©ädrÁx^ÜÅÌÄû…qÉcz[¦—P%U’»÷FOîåÕÍìk–’ÙÎÓ¼ö„ö°èc—ω÷ÑÁAž¾ã9`AÔþ5ül¡€=8xW½ÿüˆ3º–Xv¾”üplÍ8¸å¶Æwþ.†¶ƒÜu2Ï^-”Ô=;qWî8ØZ"¤˜jìúW³ì¨U“ñªšlär-\Q|rÉì­ù]çEÇmϯw=%|=yÙY|PpÂoÛÂÇu¨NËͺq1›ÅMZ>\ÀðM =›+ý¤^à [ÁC#hÍr@UNl/ IDATh›é2i4‚ÆPã³ëòJec€F`8NȲ®ß:´Ó 7SÁ?>NMR”Ü»ø’ÅÛ[¿ÁÀÂ5FõÕɽt#ÿ3)2uO¶L]™d6yÓÉû7~GY²âìGMúÃÛŸZ¸]è¶ÿØ©„ÙöO7ÌÚöL,/8ºpv\^çÀ¨˜­ËFÚ©´ì —üÍ‹W"-[S€iÜÉÌ}\ ,~’«0èhĪ×ÛL:êI2ÓÊ„ù ×Ñ@¨[X«U¤½®©ÉyVÊ6·Ò"èLˆ‚Gy-½ry ŠùžéÔ«UÐl¹I¡ g±èõÏ£³UTf­ŒaÑN[ô ³š4¬{‘Kê Ò¬IÚùʤãñᨓ©¥ ukQc{³š]aþx>ƒ¬üõÐw3þÈÓÏ0°Õ‡ÄçE²NÝ &9á”Ügc`?k€ÑðÉÞýO=©éÒØOe9gv¦¶›}h_ ð˜<áhâæk/½»éMØ5÷{ :€æ³]ï´àfF ß)¦êÛ‰³ÕÙ ,Ê„euS…Y–ਲ਼AX&”ÔTŠi*o]@p4X ,¯©a~p ØêLy^¥„•ìx$ëÞ4Q©ÆåÖ¤þ¯¨ÉΪØvw²6ã‚™Ug×o`Ð&Koí¹Tk3µ×;Ã^œê;ùÈÜÜ7|ËS†,{×¼e÷!ÛÆX²±Šz@m—éí9Úê´ê´Ó›V/ŸÉ2Ý;«= Èf@ þ–ÙÊ—›D§1˜L&ßZ•‹‰u¹Ø¥ô:7Û6 äR‰èüÕ¤³ûÊkž+§7ˆÆ 0À™ªL£áoÇH:)UPïFoÿå´é¨GË«”ƒ@òüÀÖß5‡ííÀ¾úûÎÞ(1´øTtî¬ÊåL[þc~Њ1}W0Ml4ê@[ýnØýèòº¼‡Ûn¼ÝLòÒ§¹rc_õ÷ú‡LX«`võã'.€Åüô«ã/^Ι2¦µµóæ²ðú€Qÿ<£(À0‚®/ã••°qùL»³¯Ÿuñ…Hßß4çpÄ˶“–ÍQ½uèwû@ÝæžÙÐØºI.”T$o4x +yòº¦àæ$×Ã8EJ¤2ërQ£LIþ£\aòRŸ>Ë1J&‘r *ó*Š9¶¼V”YÄ;©R ’j8KÃßž%Îb8Ñð)*Ôû³ ¯…”“­ÃÜt¥—Û_Õ:Î}­¢bý}^¸¸:;ºöÐÔӺ“:¤ÙÇ–„Ýoçÿ]Cº(³Ý¤ØíýóR¯_:nÕê}Ód¿?©|ùhšÛžwפNtÍ ½ãfÕ£Wý‰öÖZywº|-oZ{KÒÍ€@|!J»§ƒN§Óé4±{¶›Oб~½‰©u<®üÂÍû–¶7×¾›üZÙ‡Hì¯5³ú‘£1éï‚ ×bW8/øÞê­gŸaîÕÏ`ïÙ«¯,_$á.¡U@Óiú–S?—½‘«éÓnMö‰67SÅ»œ”ËÉw&ËÇτʂÑ9BVU-£0º†‰&|S׺ü†5Ûþ ,UUº¼N4("°5¸LšŠeßI+ûÖ+‡Åg#3-¼‡Ôž.ÄŒûšªr[îÅå”—AsÇèÚmµàìóB ˜½×á¥Eé% ê¤B@ýÌOÛ0EXúoðÞÜ¡«¨ OPŸª}”B«¸Eìœe×àÜ$˜ø£$F´=çjqA\#y»X’¸Z‚qM9t®rj$$ W‹1®6‡©ªÁ”ÕŠÞ~k Iµãj©¨2¹ ywPˆj¤t5 fËÎÑj¢RË­i šnþæ6O]½›tnýÑ'¦ìÙ>É¢ÕÎì•—\Y9=¦tðªí#ÍßëúK×ÜF×ÜÚBz½ÏòÃiÓ—íÝ#¬$U}c™ÿNÓ ­¿8«~dk©ªâ¢*‰t ¥ýfJH}V(ßÄ&“EQ$Ø™ëî_2øô•Ûe¹/.ßN>ìÓµ>E‘L¦’Uq Hù?êzɪŒ”Ž•• @ülïÖ»ê>®ïgð1L<ñrlùõÍe¨-§¡¿çêµ1`^8˜¦"`Y/‡O/gh›¨ 3_W}\2šŽÏýãEÃФÛ’¯n?Ö뇥/Ê0]KÆÿQ/å£9×ZeêYè@YvIÕå‰TLx*´÷?ÀEég¯ƒ‹OG5F§AÐh8PÞük­~T t=-E¿ÿv­¸Áh$+Sö/¥Y9›²èl‚ÖHÉzý¿>nFÓ±5& R‹ZÚ::õ‡–*“ ³è”¸J\R.#)`òÚëս̨½û¥¶Ž‡£k¦!|õ¢BÑð®Z¶wPÛÊ‚Sž–-¤$¹©…˜QWß® Q˜š/ „ÙOK˜¦íu¹F ©¼'õYûŠÊ—5êÖæjª¦íu…¯ž×¯o++JÍ# íx-æêr¹¼°¤@WÇ@)C–ž·üι;O@KÆø}‡f\8}›aË.½µcmоÏîl K¯F©tZ0ÊæCSˆnÜo ÑÖ-iºÃmØ +þãn¡Bæß;½#¿ûÒˆ.hôr–e¿ÞšÇcWý¦1ÂJöêÆþ}ù2SLÍiŒ;;pÕ²ßhSzê Ó/îÐ-h¨®ñ«VDu úÞ¬äôšD±s‡!Uþ¾^*íìMZ|Ä¿¹r“=‡žfÇOì¿`ù½ýÕÙÓ9ÎãLØï./¼–˜eå;ƈI€ž¥u¹°ëÈ*ÏiÙ°šã¹Oög ›øÌ‘81dʼÿ‘ÎÆDé£Äm±g*µ|Æñ5Y üXÂöã\'Zú™mÛ3妘ºó¸*+gÇŠ¦zX°ª^Ü9s¡lHøœvö:å§¶îï2¼Mù­½q—„ †ùàìö¯ž»’8ÜQGžÿøÊ‰³¹K½ÜµŽÆ†þÊý¾ìÙÅ 2ëÓ¨H©PXW+RP¤TXS#TårlÛïzs§ÄÄ×üμü\äq×…Î:4 ÇaÝäÁë7÷˜3@'ûÀÆ{l—5Th\×!í7m‰L°›Ùƒùxç¯éz^‹,YL¢ÿ@Þmë™ÿd'¹»¯¨íÄ>F-]·c¶m´RÒô˜Q£÷è¬8µyVcrÃ(y]­¨¦NJQòºÚš‚­Â¦½o²²ì] ·TôæeoÙwž Õ­Ú¶†%ÿܮ袇‘—\mëÖ_³$#½£«›ð*·ÏòûºØèÈóníˆ~®Ùgj{vã÷”¼ˆŸº¦„ïíÞYOœzxÃE’úÁRd3 M£´¹IÆmŠK Ÿ¿J«O†  sä+3ŸT†ajš<]#¥((ËöçýF,ö;¨Ö}áR³/QÒ$Ù‰áe¤fßÐ mY zº'ÏonŸ,Hçõìm´å\ŸAo£ Ï;¶2ð\5pÛ8ù†þ:Õ½¾Óoìr¶ÝôUþ%K·ÍŸ)So?Ч»öó puþ¼¸EBâ–øm‘á:íŒÌ1tŽXS¶|ÕÆ)ç$ÓÞþzéâÔkiB´ §¥7õfSÇ ƒÞÓ&–nÞrŠä;›1Ô’Ó°š*UýèäušûÂŽj4@ÓÑÏ7ukì께Œë9ÖƒÇü «®~òDL¥ëŒ¸xÃØèý“ãDÀí6eÕ¬®*¦â1oöíÑ!óõ‡mºþvý3ãùá›×MÞS „–uOïIÆ ¦®ßòqÏ—þºt&Ër€ß=³vÐŒ†GÆ*Ö­OX4y ¸F]ڴ廃VJ $ù6‡ k\nTÕ•¯àG°Ð§Ø]Šë§ÕÐh m{{fdÜÜßÊdÀ5u›hßâ{ªFÛÕ\ý‚gEdUQL@à l÷-‰vŒM‡Ö)¬‘Ó´í,Úèø¹ÝuhÚ:sb­9·E êVžsbæöÓAù/Ê f|þÿ¹~©Uy¿~ýÿ‘ÙðÿçÏŸ5j”rÕêÐßT¾†7OFD]ÒÂ!s ì\¸#÷‹~¯(<:ÅwO·ßöOn÷© ¢”•ßÍ‘¯<µÜó-µmï é],õæ‰ËÊ+â}ÑÙùùîŸk5‹¼gÏ>¶­¿6’ø–ùg ÒùóçWfhÿƒ Qœ¡Ô_‚rF¾udyg=7¸ÈìŸ âôý¿Ý×ìÜÉ„+|qfÝE¬×Z+ö7*ÇæœÝEó9˜æîèKöï½ Ûƒ]üÆÈÝÃ’†@ ÿ9h¸B(#J››ôÿ2¼5¬O(}}òpv›mþQŒü¾=vg¥ M›~¿DÍqháÛeýsšsèV®Ù{Á²¡ ÂMÛ®ë]úX²1$@6â[Di÷tk¥pœÃ/Þüâ_3¬§¹3íŸ>‹ÝyÖŽ+³¾iqÓq¹X¦ (J…l†F‰E¬¿\Œ¦ç:»ë|$)@6r“­¼°¢V"–wá¡„üF †òòr$ @6Ñ(¼€hÅüäfºâØK5Jb¯^÷*í_ôàòÌLÍ(Ù D Ü$D+¦w×T{$@´ Pd¡¼f Ü$@ %ÅÊ / Z1/Š„Ë¥‹Äï¶Uz9ÈGPÀ3äÙÛÙÓhhxB d3  ÊMB´b6ŸÍ`«jIiŠ;ùRO^ȇ‘W]»tEÒ@ åå&!”×l”›„h¼®¦ñ´UD+³NIã …T*-(,@¢@ ¥ÅÊHk /‚¿·ùÔp Pch…j1Ðqœ „„¢ã8rÜ| EQ€viC d3 M‚r“߈nŒötk CF@ ›ø2³”/7I(ÞK¹õ2+ƒ$Ÿü Ç K3«®{D_.»þìMèð¶ŽmQæ ¢©.˜@0@ Í€@ü#”6¼üð®X,v°åÿÙ=¬P(òË Œ¹çÚÑXƒEÅœÉH˜æ„^%ˆ…ü¥tù™õ.*ÿÙ3joûÕ‹>lÏn9‚Á0 Å>/%Bö2nô˜;?$n¢RÉÄ· êÿÊH}VR½ñ TË+Ì660ãñŒ yŸjÚÛ’ÕÜ;h’'èLLöËIU^øÉÅgMºä_×ͯN8Œ?]†6/þ7ÞR}nRs5é—â×F,[¾õTJ9‰Ëònî‹ Û|à~9ùö‡ÕìˆÞ&ˆ¯öç då£C‡y8ð®C\¾ïþjCŸkZ²Ê'ñ?ºûĽ–6ȯ&m×ÂÑ.|C/ß©[’Êë?a²ân\ ·«ÀßÇwþîÇ5õge…WÖÔËßÃÍoÅÑW" €g&Ð/p89âR¬UªÉJ5.7 E¹—–{¸Î¼Yû ·+²")~±ß0/G¾ÀÁcô섇U$È /GÏðÚK pà{úΉ»YòáŠÒ”0=aœ‹ÀsušdÙñ#ü÷GÿÏ¥è VVPœ¡¼f(_nAÐØ,ƒÁø´´BùÌ=/‡9ñžåWT*êž¿x±Õ¿gk|-Ìö?…vªÈÛð/ЬqEùû=À{ ÷·eä\?rbïÍ6Ó= *SŽßõœ¶ØYrmCüù§vãTAœuû¦ÈÞ¯£:ýë…@䊷­ jS6Nž°_êúóìX'#¢,5ñ×M“~Îßœ0ËY µÅO¦ÇŒ³;—0¼àýõs×¥9/Š^jYq!bå¢ óƒûiV\™½³Ä{Ù&oÝœC!«×Y_êÄÎ?6oi"1vÅÎ^œ' Á¡ó¶Ûî °…qs"®šlˆï,»»iIÐcë¸Ú´pBÒT¥—›6VyÅßgI’wú¦Û•¼üqª¤Ãws¦X©¼¹›¾y^¸Õ‘ðî´‚¯Ø¿ ifܪ”Ý‘ñsÃ,Ϭuפ9'ÎÞ_üÑqý¡A!ýõ œ¥gBG±²‚z[„2¢lá…t:“Á"úÊ“EQiw_ i4zI" þñÈ&™T^uç^røØ®NÖú_o L_36ø‘¬dÏn|Á÷»n¬öî5ú×ý+ü<øn¾Ag² ïlô÷îÆwõúegj-P—´Ð³ÇÔ_·,Û‹/pè7aÕµb¹,+~DþëÞ…*¤/6 u}(7ëäš•²…O×yõøép ÒWqÞ‚QñÙ2eïôu·¿P²â«§õï!pè>p|LrTåÅ)ÎWØô½»À¡ÇÐ_v=R’ô¨/(^ϬšàÆ8xMÝøG% ”8ëô*?¯ü^ýgìLoy¾©æŒ3@õóªN¾ÍM­\¼ûð*%—’dEv%×ÜR›Å1hgL”çTST§\|ÉóìaÌ"¥‰3ÈsGì/v\¼%jbÿîöœúŒY±yõÑÑðYRQÊbÏ޷뽊‚C~Î#vfÉ@Vx-vª¯›_À÷™¾ñv™‡üœ¾‹UïL¥,öì3ã®Èšgû‚~tï.pà{ >šQGY™¼mÎ÷ÝùÏq!WJZÎÞ{ ›i’ožcñÍW÷ôÈ5¡ã´_|m;÷4·/ãÞá¤ryå½£÷i}fþâÕ¹CWï™Ó»Õ]=ú¸V^x#ñ™Žï‚ñ½;µw8Áºð܉—bÉë³g -&ÍÙ£C‡Þ~£ô3Ž^iñ¡†¦+Õ¨ÜHÀ4Ü7_»s+ƃû»š¬øcë¬ï{ð=½†l¼Ñ"³µ+†Å„uQóGyºòMœ9Ü 65¹Pì®sÖGLÚÇ© ßsì´AúòÂÜj€¢ôêŠÛi“WOµùPp‡gmß©SçN:wêhcÀF ›ø(mnÎ`2¯K%'“ Æ»ðâ.f&Þ/ ˆKÓÛ2½„*©’ܽ÷0zr/¯nf_³”Ìvþ›æµ'´‡E»|îL¼òô§È ¢æyÊ·Œñ[›é<Êðñ¯ág ¤èÁÁ»êýçG¬˜ÑµôÀ²°ó•?rE@‡×;WìL—P‡1SV^Q¼ySd ›!½e®KK#hÍr@UNh›é2i4‚ÆPã³ëòJec€F`8NȲ®ß:´Ó 7SÁ?>6%÷.¾dñÇöÖoP4p ‡Q}ur/ÝÈÿŒÞZ÷dËÔ•If“7<±ãwÄ‘%+Ζ~FoSŸZ¸]è¶ÿØ©„ÙöO7ÌÚöL,/8ºpv\^çÀ¨˜­ËFÚ©´l5FþæÅ+‘–­© À4îdHæ>.?ÉUt4bÕëm&õ$™ieÂüÇ…ÀëhÈ Ô-¬Õ*Ò^×Ôä<+e›[itƒN&DÁ£Ðq{ëÙÒ·=ŽÖИ³íÙÝ™Wͽ—#§_P<ŒÝ3<~©@@¢zpâãçUU%»ÎÔö àªXÉÇÝo‰í¼Ùr“$BÎbÑëŸGg«2¨ÌZâ¶èAf5iX÷"—Ô¤Y“´ó•IÿÆãÃQ'SKêÖ£Æö6f5»ÂüqœAVþú 軳>òzØêCâó"Y§ÆnP“œpJî³1°Ÿ5Àhødïƒþ§žÔtiì§²œ3;SÛÍ>4Н…xLžp4qóµ—^Œ]ô&ìšû½ÀNóÙ®ƒwZpwJ ß)¦êÛ‰³ÕÙ ,Ê„euS…Y–ਲ਼AX&”ÔTŠi*ì·ÊÁÑ`°¼¦†ùÁ €`«3åy•TZ°ã‘¬{ÓD¥—[“ú¿¢&;G¨bÛÝÉÚŒ fV]¿›,½µçR­ÍÔ^ï {qj¨ïä#opsßð-#L²ì]ó–Ý„lcÉÆ*>èµ]¦G´çh«ÓªÓNoZ½|&Ëtï¬ö,¤!›øR”vÝ$:Ád2ùÖª\L¬ËÅ.¥×¹Ù¶yX —JDç¯&í˜ÝW^ó\9½A4ÎTe ;FªÐI©‚zç0zû/§MG=ê\^%¸¹úXnþí\æ/ĹËv“ù:8¼µpÍŽNº¥)¯jª—ЬFÌrÞ1ãâãj‡êÔj“Á¬ê;éµÚÎ4ÿ¬N`^çh±áµŒú²âaøûë4¹¸\$*-Ì–h§ÚÂÃØÍe3àõ¢þy$FQ€a]_0Æ++aãò3˜vg_?ëâ ‘ ¾¿iÎሗm'-›£z7*êÐïöºÍ=³¡±u“yÓ”T$o4x +yòº¦àæ$×Ã8EJ¤2ërQ£LIþ£\aòRŸ>Ë1J&‘r *ó*Š9¶¼V”YÄ;©R ’j8KÃßž%Îb8Ñðí*Ôû³ ¯…”“­ÃÜt¥—Û_Õ:Î}­¢bý}^¸¸:;ºöÐÔӺ“:¤ÙÇ–„ÝoçÿÑ;­’ÙnRìöþy©Ç×/·jõ¾i²ßŸT¾|4ÍmÏ»kR'ºæ„^ˆq³êÑ«þD{k­¼»?]¾–7­½%©AÈf@ ¾¥ÝÓA§ÓétØ=ÛÍ'èX¿Þ‚ÄÔ:W~áæýKÛ›kßM~­ìC$öךYýȈјtŒÐwb¹>áÜ3Æ¥jûΚ84ÄíéFŽÙ‰<}$~ݦO'sw­='Š´Úkb¤œ¢Þ´_R’¿Q<¬~ðUÈIŠ ð–l2`Ölû3°TUéò:NÐh PˆdÀÖà2i*–}'­ì[/Ñⳑ™Þà jObÆ}MU9„-÷â‹rÊË ¹GŠã tí¶Zpöy¡ÌÞëðÒ¢ôPuR! ~fȧm˜",ý·FxoîÐUÔ„'¨OÕ>J¡ÀUÜ"vβkpnLüÑ#Zž‡sµ¸ ®‘¼],I\-Á¸¦:W‡95€Pˆ«ÅW›ÃTÕ`ÊjEoé Iµãj©¨2¹ ywPˆj¤t5 fËÎÑj¢RË­i šnþæ6O]½›tnýÑ'¦ìÙ>É¢ÕÎì•—\Y9=¦tðªí#ÍßëúK×ÜF×ÜÚBz½ÏòÃiÓ—íÝ#¬$U}c™ÿNÓ ­¿8«~dk©ªâ¢*Z–Oy¿$„Òš  |¹IL&‹¢H°3×Ý¿dðé+·Ër_\¾|,اk;}Š"™L% ªâòÔõ’U)%++-=‡ØT\ˆÝr¶¶ËPGõº –¹ ­øÁñCOôÜì5醂žÜ䃧ƒµ“P56d”=;UΨÿ`ªM«­>ɽSüZ`oÎ9ÐL= (Ë® ‚ °º¢<‘Š O…öþ¸(ýìupñé¨FÃÃèt‚   ÛôÇAB×ÑÓRôûo׊²BÈÊ”}çKiVΦ,:› …5R²^ÿ¯LÑtl‰‚Ô"†–¶ŽNý¡¥Ê$è,:%®×ß…”ËH ˜¼özu/3$jï~©¦ ô IDAT­£Ááèši_½¨P4¼«–íÔ¶²à”§e×’’ÜÔB̨«o׆(LÍPÂì§%LÓöº\£N†TÞ“ú¬}EåËŒuks5UÓöºÂWÏë×·•¥æ‘†vôÄ—~ž}Ì ÔåryaI®ŽR †,=3nùswž€–Œñ%ú)̸pú6Ö]zkÇÚ}ŸÝØ€ë †ÙF'³<ÖuQûØ¥«ÙôÐ+ˆN6Ÿ>_š‰K7*áhA§¥,ÌvhwÆŒµ;ÈF•Oì<]%ÿ·W$Ä4½íêÂW­²™â¦^ôû‘w¥ÄàØÔ›+7‰ÐsèivüÄþ –ßÛÑ_=£á<΄ýþáòÂk‰YV¾cŒ˜èYP— ë°Ž¬ò<‘–«ù7žûdÝ$º‰Ïü‰C¦Ì+ñélL”>JÜ{¦RËg_“%q°À%l?Îu¢¥ŸÙ¶=Sn €©; °rv¬hª‡«êÅ3ʆ„Ïig¯S~jëþ.ÃÛ”ßÚwI¨p`˜þÁnÿê¹+9Ãuäù¯œH1›»ÔË]ëhlè¯ÜïÛÉž]Ü‘P ³n1Š” …uµ"EJ…55BU.—Á¶ý®7wJLìqÍïÌËÏE^w]è¬CÓpÖM¼~s9t²l¼ÇvYÓI…ÆuÒ~Ó–È»™=˜wþš®çµÈ’Å$úäضþùOv’;±ûŠÚNìcÔÒu;fÛF+%M5zΊS›i5&7€’×ÕŠjê¤%¯«­©!Ø*lÚû&+ËÞµpKE¯a^ö}ç©PݪmkXøÏíŠ.z9qÉÕ¶þaý5K2ÒK0ºº ¯rûüx!¿¯‹Ž<ïÖŽèçš}¦¶ÿÌf›’ñS×”ð½Ý;ë‰So¸HòC?Xêl¢i”67ÉØ MqiáóWiõÉ4€aŽaeæ“JÀ0LCM“§k¤eÙþ<£ßÈÅ~Õº/\jö%Jš$;1< ¢ŒÔìàºa¢m}¼×vò²Ä2͆uúÓÂ14={Mº iÀjëæ¤z4ÍÙFõsCÆ/^¹ié šQ÷ÞÎOöþënZ\¿ÿ²ç‹ÃÖ-9Å5÷1ÔæÙÉ–ØÔ›M' zO›Xºy_\È)’cì4nÆPKñîRÕN^§¹/ì¨F#4ý|S·Æ®¾‹É¸žc=xL¢ù3À>y"¦ÒuF\¼alôþˆÉqõ±%n·)«fuUÁ0y³o/ˆ™—¨ï8|ìhÓõ·ë/˜Î ß¼nòžZ ´¬{zO2f0uý–{¾ô×¥3Y–ü~왵€f4<2V±n}¢ÉëdÀ5êâ9r &ÇjÚª€¢¥qAóz]† öÔÙ™ÓRÚ”äYô¨±‡Ê²&{îÖ±ýðüö*]g¬ž²6àŒ®ï8*4ÄKÐv_ùrEÈÊ©{el—)ëæ8«cF>Ës¯]4.žTµè» jR6Xþ¼z~Ið¦€ñÕ¸fß ðѦ-ßÌh´RR É·9lXãr£ª®x?€…>ýÀ.èR\?­†FKhÛÛ3#ãæþV&®©Û„°@{NË–iWsõ ž‘UE1 +°Ý·$:Ø16Zs¤°FNÓ¶°hk £Úgúšv§ÎœØCkÎmƒº•眘¹ýtPþ‹ò‚ÙŸÿ®_jUÞ¯_?$G„rþüùQ£F)U‘ÁßS¾†7SÊM]ÒÂ!s ì\¸#÷“¿P•WçÕX¸¤ 5© ï é],õæ‰ËÊ+â}ÑÙùùîÿM”²È{fñìcÛúk#Ù!ˆo™æT=þüÊ íp!Š3 ”¥ÍMúÖ Ënïþƒåcƒ †ÿ€æœÝEó9˜æîèKöï½ Ûƒ]üÆÈÝÃ’†@ ÿ9h¸B(©é¬œ¹Iÿ'Ã[ØT]yþ¥ÕÜgY!¥ì¿àíh$ˆ¿®Ù{Á²¡ ÂMÛ®ë]úX¢}cÙ ˆoÙlå[7©•Âq¿x³±þ¡Í¨ßþ…äó/CÇåb™‚¢(²A$±þrý1šžëüí®ó‘¤¢Aé eDKK ÑZ±ÑÁK+j%âº.<Ô7b0”—— Q „Râ e¤µæ&!ð“›éŠc/Õ(‰½zÝ«L´ÑG4‚gÈ335C¢@ d3 _d6ÊMB´FÚpwMµGr@ D EÆÊÊMB PPœ¡Œ Ü$D+æE‘pÙ¡t‘XâݶJ#Gù x†<{;{ Olâ Ì@¹IˆÖÈæ³lU-)Mq'_êÉ+Bù‚ ò ò k—®H¡< Ü$„2‚r“­˜×Õ4ž¶ŠˆbeÖi i|‚B¡J¥…H¡T 8Bi­¹Iü½Í§¨¶âËÔb ã8A# EÇqä¸ùS³§(@»´!²ˆ/4å&!Z»nŒötkܺÆÑ€@ Èf@ šBià B‘ð^Ê­—Y$©øäO8NXšYuíÜ#úrÙõgoB‡·ul‹2OMuÁê„lâ¡´¹IÉïŠÅb[þŸÝà …"¿¬ðǘ{®5XTÌ™Œ„iNßæë“çí5ê´÷¾?cZó_ÞrÀ0 Å>/%Bö2nô˜;?$n¢RÉÄ· êÿÊk6€òå&åf˜ñxF††¼O5mƒmÉjî4I‚‰t&&ûŠå¤*/üäâ³&]Ò\”¤­ì<ùršñwÞR}nRs5é—â×F,[¾õTJ9‰Ëònî‹ Û|à~9ùö‡ÕìˆÞ&ˆ¯F#3ÈÊG‡"&ópà \‡þ¸|ßý7 Ô†>×´d•Oât÷‰{-m_MÚ®…£]ø‡^¾S·$•×ï?NVÜ ôv8ðûøÎßý¸¦þ¬¬ðʺñƒz9ð{¸ù­8úêíÝw\÷ûðçrÙao‚ * P b‘!Z±ˆµ•Z±Ã nÜ{€ âŠJ]XœhUÜ»jÝ8p"C@†Œ’äî÷GWÛþ¾4ÐçýÊ«¾zdÝ“ÏÝ}žÏç¹; @Ks2fìñ…ÐÛã«¡ Ç åÍ"P ®”ú¸%É?>'Ðw̹šÿp»¢*.§L‹ìâ!ôv ü>&õfŠ¢+GGöîâíí& Ÿ|®äí+JÓâìÔ>ÞA ïJ@ž›ò·›ðÍ£ûŠu¸k*œg@šHck“H’ÉãòÙlöûŽX1fÛã>žæ÷ŸWT*ká”uôò”òK«§Ïšné˜üCË&Þ…5´RêãfHTžŒ ›~Y ÀðüO·+Eùí,Y»oÆsÐzy)5~ÍÄx‡Ýñ˜…7žð¼#g°T]ߺ(eÂ|ûCK uyû§Ä¤½x禽gÅv7%\+nÄš ÷¶H©ª’@ó.ºÊf±8l.I²æ(L¼{驘Éd•ˆ”Ñ)·ûu¶Ê©  ªè‹W®Åp÷t4ý÷„Ù‹̾%/Ùö£¿›ÐûÛ-g†vù~CÚÜÈ 7¡ø¬CÏŠ.®ˆ ýBè2rsV P{yJPçáÖNÐEè탨ÆñhEÉÅäè0_7¡_¯ÉiÙµ4ȲX‹&§1ç úÁBmÏðÀö¶Ö>¡]Í+o]+¥¨ŠÜJ­½!—oÖÚ’,Ï«¦I¨¾~ì±yPgK.IjÌ<ƒ"_BÚ ikwïäêâÙµÿÜ5 {HöÄï|V'¹>-È/ú‚j°WY˜éõÝægrNîï&ô†Zq¡L ÊÂôHÏoRž¨“%×§u}I ”èþŽY?tòv†ô›½ça- Tåµõã¿í$ôv {²¤éÜ{ÝfÄÎkv·{+竽·û´ØcÄÈp§݆LèÆ¾²ër¹¢òÊž«Ì®cF†thç:fÔµ§öÜ®Q͸o>ùg?—¶žßäXtdÿc©ìéáÃEvCbúun×Î/rl„éÃ='›üTCÃ+¥6nzkN_<¿*P𑼚ªøsݸo; ½Ý¾ ù:zÅÙ2ªé–Õµ+¶Ý ¥‰“"½Ý…=ékV“u­H<÷ñˆõîêÙQ4`DOSEQ~µ@YzjîèÌ¡ ‡·y;pßÜÑÕÅ¥ƒ‹K—ömÌxxÌúËih^m‹ÅæpØOKe®þìcž|,'ãjQtòµþ~öÙ%tI•ìÒ•›+‡v ùÂæßü–œÖQ«'¶% û¬Ü{âÈ¡”0#(²7¤zLNœ$ÏŒí¹$Ç-zÑ숷7Ä.RP’¿]Òí>)aîh÷Ò3çg–Rʇ§Ž^›ã6nÓ¶_×±º’0*árµ²òbÜÈ%7ì¯X¿vÙHÿl týâV÷2 ÛNÞsøÐ‰}S;rU]beÉÉø)Òˆ%ñýZ±Zö!Ãæ¦¤¦þš8Ðâʲi鹟êp(òwî.t4wñ´>ú¬ˆÙð@ ¯EÓÃ$™ò€ª¼ 0´1æ0™$“­cnÉ«-(•“$ƒI2™$IÁ`òggÎÖy|å¬Çj¤/¦þñnÊ ,¹rì1W8ÀÏ´¾£ÁÐs‹èf”üìó4£Ú;k‡Ï»l3tõýi+¾!wOŸ{¸ô#ý6勃3ÆnÍOÛ{05ÆõÞòqëïK…{¦Ä$t›¸jÝÌ~ÎZM»£xùè‰ÄÀÉZ‹À±tiAåß.¿¸“¯4koÁUõÛ¬Ú›Èrî–‰Ÿß.óö-ؤ®£NÅݧ"QÞýRž­ƒ À2s±" oÈšöA†7´RêãÖpéLÍåÄé›ÊýcS·§­šÒÏ^RTÓ\ëèÞ:óH)­’÷Ò(Zþòá±­'jœ¿ö3gUuué¨EEáKâzZ¾;“ ÌIêç)ôîÔkØüÃy2,pÕä†i ­Mb±Øl6§F*!”Ò 9²ðNVîæ rº÷BYS+;~öòÈ£€ŽVÿö×$yÚ|ƒäjëéëq@V Àl?uù̯ ŠÖ¹¿ÚÓ%aÁ {6ˆààŽÙwŠe}õ¿Ø¤‰žº#ûÚ…iûo½t)ÝyY»Oêˆàv»˜éWÃÇnû3²Ûo'=WOîëÎhgV´cËf‚¥¥Ía0X}==]"ñ½c²:Í]ÝA‹`ôÓ/ÊÚ—ÅÜ~Îüø[Eu`ôñ@ÛŽHœ=°%ÀÛêÅùoÏù)°¡µ0ç7Á¦ÞhµI2±œÁå²TŸÇâi³éœ9Û®µ¡äFN5Õ¢öQ>eÒS_tyó«îÑìÛ»d•*u#øYr½Ãüî<ƒ¼üéK0õ·ä¾3êiæd Šå.êÞ@t-õ "lÅØ`G6€Eß¡¡¿E¼#ê¨î©ò¼C›³ZǤG CíÉXsúq{Ë-“A[&|kÇpÖ¿¿å·‹MxJ‰_ŠiŽö«’ÁÓå¸\,—ÕÒ-Žj)É×å¸L,UJ™Z¼W’¯Çq¹HÄyë €äér•2 ´šðÀ#Uû²•R·ûÿJQnžXË©“§£l:øþÚTéùmÇkÚ ïò:±—fÅ…Ýý’a¿ö;k¶¾^¾Ý{t¶æ7ºÜ½Óç_m•òÅë^%§õ¤Ý ²ö-›1pÁÂ#äÜ©||k„ÿ¶×¯Éì›wt•¿Cç.ªm .ýtâtÁˆ¶ölìa΀Ð_J@ój“Ø,‹Åb2É­1þa³öûygdÕš GÏ]Ý9£—«­á¥kO5ýI|ºg¦:2LëÃ?Ò”\AÌÏ8þiu蟷eeÜ!á²P3¦äö² +ozMݾ*¤ž"kaßÁù¯ß³¡£0MQ@rH⯮Eè®D£ÝŸ«­ÍRÔ*$“ J¥D<=‡©eßmȼnªÎá‹Ã‹rìBûšÕü^DXv³Öæ“mœÇ•Ó!f}¤xwžeØÊ?(’Í›>|]qv h{j‘ :3äý6L“öQëVöx“î°´tÄÔ48Z©dhù'lç\?¸Ir·&SÙlúy ¤"Ù«‹%I«e„ÀšÏñ!O$£H¥´ZJ ùm=޼Fòj0])«–-mŽd¯ß”QKGÓ´Äà4°RêãÖpͲýaõþ6ÇžºtùȲ=›÷Û¶qˆ]³=³WQrrÞ¨U¥½lìgû¦¯OpmÛÛ:ÚÕé:g×ÝQsfmß&V’®>;3j³õ¬u#½´ßɵtZh3$U ¦n3¤4íÔçz—¦)p¶5N›Þë÷“Êò¸pmïì0÷Ö¦4Mq82©Ê €Rü­]/Uõðz ßÁÁÔ¢­Up#_UÞKKrn“6¶Ê¢ûÅ‘$(Å›I{–±{ÏQ [^Z8sg®\ùòÁÝj³îý»µÑcE©¾Áâ²êWGZ¡v²@–ã™Ò¢ƒ%§ù5óÆ<šcbge¹I’$Q[\ Ѳ2×b¾yC’}ø ø„µ×a2€ X,’$I&“4Áhüs ßT =‚ì%üzúE}£*¯ïÈ,e:xYsY<’‹êTçÎ+UˆiädIf³ ŒTmÉâ²hi•Tõ.”BNÑÀ1okRûø¡Lçõ3 ôø|c=ñ“GÊúߪi:ØñËïæŠiÕ6•UDX´3˜:·$‹²žKhqu[c…K ºàŽªj_Yùø¡H×ÑVGÛº­±øÉÕõmåÅYT gó&¾Q‚†VJ}ܧ(šká:tbÜÆIáú9§o–7×~°¢ôôüáq÷ºÌ_?ÚSOM’®­S$“Áà™ZÛ¶²U=¬Lx –®…µŸAÉë“xºöÙBÚ´µ^7Is÷$¤4¶6ÉØÈ¬¨¤ÐÜÔ’Édz8˜íŸÓ{ÿÅÇ‘A]mÍt EQI¡±‘™F ¹&6‚ò‹G.Þ9ûsú;”øáÑß/°x¥ç7-¹n¶µV‹–ý¼Ö-˜³Êizoûº›[ãN1—x9šäY%oŸl5È•yßúl¹ÀX†¶FÊŒ#G®Y·gЦ†ª/ÁiýÜÑç#—NMu[îÜŠ_œ¹ý€[¨¥èZúò} àX}a%]·1尼修õ¿•(Ú½ú:òâóO8v6W>:˜&ê43 YÖ,›zcÕ&‘&n_ÚìÛŸvÔþ[g֓ÿçéy ´â½ùpEÑéŒgáý-8$˜Ø›Ñ'Šj‰öÜò‰A3nãßxî½ë&±¬Â&}—18vØÄ’¨~^–dé­ŒõI‡*  õ¹27;ÆÞÔûžÌìCë7æ(¬]¯=´¢çÅ$I†Úq«]؉ À ™¿ì弄Å?¦×zmzN\1Q¨Ã…þ‹&åOZ1wä¾]×p?‹[7Ø$ ã€Qƒ3§$Žø™iÝ7yþëë̲l"æŽ9‘8!uåŠi}f/X2ú×Ö¯_?¿{ëÄÀlÙ{ÚèÓ×Í™V>"{ä¬Î­zùGòØÍER¶E—!‹ç1ä˜3ü³2ó1¸tÍŽäØƒßÒsàèÞöüú‚/ºúÖ3Ì€)íu˜$€¾GdxÖº¤…—¹ h@ ù{…a”4¼û¿ZSZ$­LKš¬º6¯à‹a ƹk„VàĘ “WÆNÌ0õè;à{ëeT/“/ˆ_³tè¶ ¿ bÉæGÎø`Ɔc¸ö="üòÙ&¦EßEIÊ¥ËR§]*EÇ ~_éóF,ˆ.ž‘$f™zDÄņ˜0 ¦-z<7vÞðírž•ϰ¥ã½t ‹°„9ùÓ–L˜BiÛu›œ8¤ìY8©döê蟫úíÂgÅoÝô‡ƒÙjWª(êU ¡>ntÕÉèÙ·`JX08Ï:žlPßhICWWÎ¢ä ¿–ÉA`í?hþXW~ÓßW©iWL ïSUÅ«¢ë¯À X›1ÖÍ™½:}ñî"‘‚ièÜc꺱:Ù‡0 ]:ð“ÒY+]‡ ñ«&aý‹æ"œfgþ“×Ïp(Æ8¢ÿ_ÿ/Ó ™™™šµ½Á_ë|ÑÐ8µ—§|=æ‰÷ü¥×ÉŸ$õr;z÷Ú=¼¢öç ]žÝÑÞäf´¬¼"%jÉÁû½ƒd’æ-Ìm¬m0!„9B ÀÚ$ÔŒµ6lîŠq@!Ô„àÌ8ÒDX›„B!¤9pžinÚX›„š£GÅâ™éÙ©,´U• _y æ-Ì]]™L< }±%4Ãn1° ’I’2šÅ`àÀÍÍž¦ïÒ†B˜3 Ô ¬MBÿ‘¾1ÞÓM}vM`Ò€B˜3 ÔUmRý5狉%â+×Ï?~ö¢”ïý‰Á ímÜ;t^y¢ìÌý—q}[y´ÂÊ3¶/IDATÔÐ.˜Ä0B!Ìúih^mÒµ›—¤R©›“ðÃáa¥Rù¼¬èÇUW|Û[êqéU‡¦ŽðlÒ?¢`{DÄï¡;R#-?gGQ÷,#~âòÌÇbVûÑ[7F´ÄKC‚Ày†‡FƒÈ'ßÿâ¿6ÅR2„Ðîÿ&ÒØSŸ Šr-ÍlÌÍ-Z´0ï¡ch¶þšN@;}Šä0H‡ÿ‹ß“®<ú“OØâlY£}"Uv2nÑqè:röœ™Ã;âžås~%UmRcQöñ”% 3çÌ_wðz9Å I’!/8·#1nþ¬ùkv^-§^=±úÏM+·Þ•俆øð„ªòVzÂà>nBo7ßÞ?ÎÙqõ¥ÛÐÇš–¼òNÊaÉOëêã'º»eÊ÷>Bo·.áÃ×^.WÝœª¸”<6Ô×ÛMØ5|ÒÖÛ"ÕRyÑÉ¥?÷ìâ&ìì9wÏ @Ks2fìñ…ÐÛã«¡ Ç åÍ"P ®”ú¸%É?>'Ðw̹šÿp»¢*.§L‹ìâ!ôv ü>&õfŠ¢+GGöîâíí& Ÿ|®äí+JÓâìÔ>ÞA ïJ@ž›ò·›ðÍ£ûŠu¸k* DšHck“H’ÉãòÙlöû_X¬³íqOóûÏ+*•µ=Zõåê'«+ÊzÆpŸ62ÂOÛïgjÔyeùŸ;Òo0ºôrbçÙ½û¹–£‚Ì*¯ï;+ùrÄ4/Ùéå)™÷œºiƒôÙ…s×Èöº¬o D¡|÷¶tÍõC¥Õùþ“äiA–eelX=ä—çkRÇyé`~úþ¶˜½j@ÿ­ù4€¥÷›^]6aé]¯©+gØWM˜7u–ío+‚õ+NÄÆl. ¹:Ô8/=váØ¥ûfxòžï8#ƒ0wsþÔÙq7:mv‚GÉãNYG/Oé ¿´zú¬é–ŽÉ?4õÙDYC+¥>n†Dåɨ°é—¥ Ïÿt»R”ßÎ’µûfü0­——Rã×LŒwØ߉Yxã Ï;rÖAÕõ­‹R&Ì·?´$À€P—·JLÚ‹wÞ™aÚ{VlwS€Á5±báF¬©po‹47mÍ«Mb³X6—$Yó&Þ½ôTÌd²JDÊè”Ûý:[åTÐUôÅ+×â¸{:šþ{ÂìÅfß’—lûÑßMèýí–³ C»|¿!mnd›Ð?|Ö¡gEWD…~!ô ¹9«†¨½<%¨óð k§ è"ôv ´àô‹×]6eÅõ_Çöñsú÷ž¾ÿ™ @–êómRêìŸ{¸ ½¿ì?w®Œ*Í4xo¹ìʸ o7Ÿ˜³·÷õé·dφÑo¿Pr}Z_ôÕ œ²0=Òë»ÍÏä@Uü¹nÜ·…Þn_†|½âlõáP”ž[ÞÕÛM9-õž˜nm¼1ç úÁBmÏðÀö¶Ö>¡]Í+o]+¥¨ŠÜJ­½!—oÖÚ’,Ï«¦I¨¾~ì±yPgK.IjÌ<ƒ"_BÚ ikwïäêâÙµÿÜ5 {HöÄï|V÷‘v /:4<ÜßMè- µâB™”…鑞ߤN;¡ô‰Z7®›âèÜé»òu»/M ÒauŒÝ}èÄÁ¹ž<yÞÖ wZ½ÿÂÕ\Nœ¾©Ü?6u{Úª)ýì%E5U,‘<Úï²ÍÐÕö§­ø†Ü=}îáÒ´勃3ÆnÍOÛ{05ÆõÞòqëïK…{¦Ä$t›¸jÝÌ~ÎZM»£xùè‰ÄÀÉZ‹À±tiAåß.¿¸“¯4koÁUõÛ¬Ú›Èrî–‰Ÿß.óö-ؤ®£NÅݧ"QÞýRž­ƒ À2s±" oÈšöÆG‹Z)õqk¸tæÃÝZs­£{ëÌ#¥´JJ ßJ£hùˇǶž¨qþÚÏœTÕÕ¥£…/‰ëiùîL‚2'©Ÿ§Ð»S¯aóçÉh@š{À ¤±µI,›ÍæÔH%„Rz!GÞÉêÂݼAN÷^(kjeÇÏ^bÐÑêßþš$O›Ïb\m=}=ÈŠ˜í§.Ÿù•!CÑ:÷·C{º$,dÏÜ1ûN±¬¯Cà›4ÑS@wd_»0mÿ­ênm˜VQI‹Ù°€v•Ü¿èús™?€å:}ùÜ`}ÀÛðÑ™áG.”öë­Íf,m]=}-Pˆ˜VQ«ß}¡¯Úîš(7O¬åÔÉÓÑF6|AY”öÞÉ­Ù»r¢wŽ ¶ Ú´õèÙŒ*V›$Ë\.Kõy,ž6›Î©‘³íZJnäTS-jåS&=õE—7?±ê;½+ñ@V©R×10b€Ÿ%·Ñ;ÌïÎ3ÈËŸ¾SKî;£žfN¦ñ Xî¢î D×R*ÂVŒ vdXôú[ÔÁ;¢Žêž*Ï;´9«uLz„Ѐ8tОŒ5§‡°·Ü2´e·v,gýû[~»Ø„›%~)¦9Ú¯~HO—âr±\\VKs´8ª¥$_—â2±LT)ejñ^uH¾Äå"ç­7’§ËQTÊ(ÐjÂTíËVJ}Üìÿ¸[kþ¨Òóێ״Þåub/ÍŠ ºû%Ã6<~íwÖlyî–‰3¯xÇ®ïoÏ#*ÞÚúŒJhË7ÔeVßý}õÂ9c¸ÖÛǵåb/s„þZÚšW›Äb²9ŽÐQ[@HÄñìZ§–7 u2Iæ©Ë›bº)D4s4ˆÉ& `p´9$Ád¼:Fj±¨:%ýzÀèÕ¿ü–íMè#•Š6Éf[G—¥”(h`Áxõd®¥³9qáÙË{Õ¼Pm—ÙÈ«›CbRTØ#_/ßî=:[¿¿¤çéC‘¡»«ùVs†¿ˆ¡šP UŸG4 A²L½û‡|]qv h{j‘jÛ­¤Iû¨u+{¼IwXZ:âôûÝ>Z©dhù'lç\?¸Ir·&SÙlúy ¤"Ù«‹%I«e„ÀšÏñ!O$£H¥´ZJ ùm=޼Fòj0])«–-mŽd¯ß”QKGÓ´Äà4°RêãÖp­n·f×lÏìU”œœ7jUi¯ûÙ¾éë\cÛ6ƶŽvugºÎÙuwÔœYÛ·‰U¤«ÏÎŒÚl=kÝH¯w.™Aê´ÐfHª$ MÝf0H©ª’@óNlàp¸4M€³­qÚô^¿Ÿ¼P–ÿèÄ…k{g‡¹·6¥iŠÃÑIU”âoíz©ª‡×KøŸÑO¤ÅO¯0­\Ì>cTˆ`ñHJ,ª£Tý´WóEs-n ïí>Ü­•7×~°¢ôôüáq÷ºÌ_?ÚSOM’®­S$“Áà™ZÛ¶²U=¬Lx –®…µŸAÉëwçtí³…´ik#¼n’æîI0HcÓмÚ$c#³¢’BsSK&“éá`¶NïýGuµ5ÓU(E%…ÆFfq0äšØÊ/¹x äìÏéïPâ‡G¿Àv╞ߴäºiØÖv<(ûØ“å¹'~?eÔA_tsëÂãìÀe^ºŸ1À6w³cìMݸOàÉÌ>´~cŽÂ@ž»eÊÚŠ.}B\Í ÷â=±®ƒU嶉ËÞYÒÆý›žf‘+g%éìn¥xrfïÙQózš5{¡5Vmiâö¥Í¾ýiGí¿uf=9ü{žž×@+Þ›WÎxæÞß‚C‚‰½}¢¨–hÏ-/´1ã6þçÞ»nË*lÒwƒc‡M,‰êçeI–ÞÊXŸt¨Ò l PŸ+SÓ®]¯=´¢çÅ$I†Úq«]x|Òx$ ~Ã0pbÌ…É+c'f˜zôð½õ² ¤¡«+gQò„_Ëä °ö4lG‹œìw—¸è¶[±â—ÅÝ,]{¿§h5›ÒF뎓f~#—®Ù‘{â[zÝÛžO¾îåÐÕ·œaLi¯Ã$ô="óÖ%-¼DÈAÍ9dã—†½÷‰„–ûèä”I+Ó†&«Úšà‹a ƹk„–šv„–û˜”xAüš¥C·ÕiàøeèK6Ç8rÎÀ36Ìõïùã—Ï60-ú.JR.]–:uèR9,:õûJŸï0bAtñŒäY“”&¿îd´9¯©´)Ùý•ÒËž ÚjøÝÆ]“Új¹^8:.vIô!1ËÔ#".6Ä„`0mÑã¹±ó†o—ó¬|†-ï¥KX„%ÌÉŸ¶dêÀJÛ®ÛäÄ!íx`ÿËÂI%³WGÿ\ÍÐo>+þ{ë¦?ÌV»Ru@Q¯f3 õq£«NF‡Ì¾S‚ÁyÖñä`ƒúFûánÍ•ßô÷UjÚÕÓÂûÅTUñªèú+ðÖfŒusf¯N_¼»H¤`:÷˜ºn¬‡ÎGö!LC—ü¤ôÅGÖJA×!hüª ÁFXÿ¢¹§Ù™ÿäõ3ʃƒƒ1Žè”6ü“ä!333""B³¶7HÿKϧ¡o£|¯ÚËS¾óÄ{>pd'~3ôÞ¨ý)Azxí¿'tyvG{“›Ò²òŠ”p<@ªñ¼ðy`@ ú¿I®O ó"fïúîx»q„ÐÚßë effÎ{hø7^ˆó H£·Íœm@èÿ%…lÄs ›`h>†càšž¶ý¨qgÞ‹—ö< BýÏáá i"­Mú‡kÞ5æ :ñ×0ôý&Ïì=9~ꈠç»Ì§«=Þ7!„0g@ÿM{O·fŠïìÜg>—Ófü3²‚ÅPHåJ𦵨˜3¨!‘J¸Ÿ¼þÓÄwÒFßI)„œ!¬MBÍU#FQELªèhŽùj†òòr333 Ba΀Ppz5c?ù[ÏÝûX‡–¹êÖ>ÉÁû½ƒd’æ-Ìm¬m0!„9B ÀÚ$ÔŒµ6lîŠq@!Ô„àÌ8ÒÜ´°6 !„Bs„Ô200À „Ba΀ÐG©ª’0y@!„œ¡O¥ €µI!„B˜3 ¤N/ „Ba΀Ч`mB!„æ 5œ6Ö&!„Ba΀Z8½€B!„9BŸ‚µI!„B˜3 ÔpÚX›„B!„9BjáôB!„æ`bRÕ&Õÿ÷ï½Ipp0NS „Býs8Ï€47m¬MB!„œ!µ°6 !„Bs„>¯›„B!„9B § €µI!„B˜3 ¤N/ „Ba΀Ч`mB!„æ 5œ6Ö&!„Ba΀Z8½€B!„9BŸ‚µI!„B˜3 ÔpÚX›„B!„9BjáôB!„æ } Ö&!„Ba΀PÃi`mB!„æ ©…Ó !„B˜3 ô)X›„B!„9B § €µI!„B˜3 „B!„4óŸ¿Eff&Æ!„B!ÌÔ›÷ЃˆB!„P3†µI!„B!ÌB!„B˜3 „B!„0g@!„Ba΀B!„œ!„B!„9B!„Bs„B!„æ !„B!ÌB!„Bs„B!„æ !„B!ÌB!„Bçÿù” mIEND®B`‚PyMeasure-0.5/docs/tutorial/pymeasure-managedwindow-running.png0000644000175000017500000023314213137471263025351 0ustar colincolin00000000000000‰PNG  IHDR`i¬’ pHYs  šœtIMEà$üS IDATxÚìÝu\éðïÌöRÒ%!ˆ(ÆÙÝb!6vwœ]çÙÝžÝgq6gÝ™?[±QQ@^¶æùý±K‡ŠwˆŸ÷ë^,3ÏÎÎÎ>û|æyæ®ÈÄ`ø' 2µw²Ž N$"RX:8ZH2žça¨²%î_>”ödŒ!<ÀeÓ¦MDÔ©S§Ï]1222$$Ä0ê§R¥JŒ±óçÏ?yòäÉ“'õë×·µµýnÂÏó8rp;hÿíD"2õño߬R ‘üöѵ§ÒLí|cxÈØüVx ,xõÄMOˆj÷PÅB¯Ñ½ß>wl2zPKCõÍ4o¯l™ñûƒ’y·ØÆGüâð²õ—ãI\ q¿ËK»Þ —¬eÄÉ­ÜŠViP·´ƒ\x{vù’“oÉħj‘ä›×žÅ‹¬}êÔÊwÿØÑ c˜UÑÍÊÚJôÆÅ”…*z«nßx‘ÈY¬PÍ)óKÐÇ…ž=ròòÈ$=ɬ<Ëøú×ö±Ä`&Èþ༉­­m?~üðáCžçu:Ý“'OˆÈÃÃÃÆÆæ[ŸˆAxȽß)‰¯<Ò‘I™¶œEÚ•–x³š{UÒk4Õ'WÖkÔj½ñ;DШ5:ч{4sLáG³2Ý7iÖüÎÂí÷ÔŸ •›ž¸O$ölPÖR¸ö",†YçwWê¢_¼‰yvõM‚õà¦.œ¡³#éî¹+  Ñw¯¼K$‘ÅÜ9r¸páNÅÆÅ’Cÿ÷ÒÍÞJ’ø6îÑ©'ÝûW̰ÁªÇ‡Vo»–ÈYy•*,}sóÎã »¶Júö©…k!2«Y³¦B¡¸}ûöƒ /^¼|ùòæ[?uN^ó€g€¥MxýŽˆˆw*bË«#Ï®]yâµ–ˆˆ¤®úv.•^“±ç!Ë”é¼~æ:úý[Ï8½ (ܤY‘Ç;ï%]ß±™ˆH\°i‹ŸLA_$pLYks‘ NŽ{ztÙ†+‰ÉÏŸÅió‰iùžÃ[¸¼þ}ÖÊ+I¤,ÕuDÏèƒs–œ‹ÓG¿ŠÓ“ŸÂªVïAõèÕ¡¹KÎÅ%Þ»QÖ2-ëÄÞ>y-‘ȾN@£¦\¢Môªà7ooÜŠªjç€ôyƶmÛ2Î3´yófÃ:tÈ~!jµº\¹r/_¾Œ%"++«²e˪Õêaû1U+@®Åˆ36¬u‚ÀA«Mý‹F#dL©á²d–ù‘LeË0l‰éR’Õz½¼H“f…ﺯ1D‡æÅ•z‘ ~}~ÿæË"’ÓŸU/0fìr–*%ÚdÌÆ”(‰¤&2]rŠÄÚ”(ŽôºŒ›Ê´*•JlênË‹c‰ÑIzË´¤óô-QĉU O¤m¦*V…oÈ%¶nÝúÁÇÛ·oÿ5ûGª´ÏªêA8{ö¬!9QLLÌ©S§*W®ü/ŒBxȵx3K¢boô¨5lV}ÝãmSWÝPGÄ/扈ô­ž1F$hÕ:""^̳ÌiâýÞá´_tªÄDIÆo¦MѦžÓ'Ç'©¹Xó÷Ž­!¯‰¬ŠÖ¨\ØâÍÉ +±™ cK»`›¥]’åâ &è5z"">ý6EéQǦJ˺i3>qb {ß.»}V-Õ³gOëV­"¢^½z~MLLÌ~«ûâŋϟ?'¢"EŠèõúÐÐÐçÏŸsW±bÅopŸ€Ü”že=DaOôÉÿû}Ÿu‹å äãÔé_Q";+ EjÙ‹K·"ÜËØhÃþw+†ˆÈÂÉ\ôåÕyÜ­C‡B£fYø‰ý— v®döîYqž~Í} ‰Þ^8÷±oÐl=C⓯Y¹Y¥ ‰ò¹[Ó½7¡µ­]ÂZL$$¿¼q#^ëj+Ç¡¹AZC?‹ì·ûß_ø³Ö5ˆŽŽ~ñâyyyU¨Pã8½^ÿøñã°°0//¯ïf¶%\óó”E›7½·|ï½dÕÓ[ž$^Ä ú´jWâZ£¢õý³ÑÚ§GWÍ:šºŽÄ«f)+^HzðǶãßÑ›ÍkžT hYÖJ”µ™ÿúð¢i‡?›—éÒ§Jô¡#4DVÕz¶í[yæmø© Ë^í :šÓ£XöxÏÚõŽìÍ“ˆ U?}àçL_ ,½‹"æì¦å7)þ]’@$ö¨þS>îfê*œE‰z¥þÜt=éQÐâg¬­$š˜èUÞ%=L18ä 9^æ´Ÿííí}}}Ÿ={V¹reÃuÕ«W‰Dîîîvvvz½þû¶óSþÔn¨ÝÕ§.Þ~‘¨ô"SÛü +ímJŒñöuz÷2>vñö³5obW°TM¿ÚE”z^Ÿýæ]’±UldT².ý…L—C¤Ñ³¤[>Ö™•oQÍÑžZTº¾êbÜ«SA×½;¶mµëøý˜7‘º²•+jOüå[/Ó¤,'”Ò~ÛØÈž $¶*\; Eq!*Ã"’Mv³;zâÒݰwÑ‘Dr+·â šaØäI†~ŒøøøÏ]Q§ÓÙÙÙ9;;«TÆI÷T*U•*U´Z­N§û֛͙üõ¥Œ/]³fM¥R‰ã çkj±T.“JÄ"Žˆˆ zN«Q«u‚á2¹\*ñ1A§Q§¨5zFÄKMÍF/ êÄ„”ôóQœÄÄ\™åÓ$¥ðJ…˜#¦IJPéH¬05‘òDú”„$D©”‹ybzZ/–KyC‰Lff&7þ¬çe¦frQê/† `šÄ•&æÒºå'¢ÈªÖà!õ¤ ºU²ZÇøŒ«ñb™\.§¾½N“¢RërÒáǧ„ZÙº9Öó€äð0F¥ûØM˜N­JTà‚&1îó}3mR\܇þŸa%*!.­huRBú,€éϨNˆSg(~ùÐ) ñq⌑&ÃêD‚Nœ¨Æ[[åä5Ø›ð‘ïˆô/ |_ < <ÀGñùÊv]ß?»àûfee…€ððÏbbb ù)áàŸóCÚ¿€ððaèp@xÈ [@xøŒü@¶€ððièpÈÃáEâëçoøïçø¯¿o‹;3¸I—å4ïÿIucV“Ôçj8à×­WÞêþíÝ÷‰Íƒ¯‡aK¹Š8GKã¬jÏØë€ûΟÓÖÓŒûæÛ_ ÇŒA¥¤Éw­^;qŒhÙ²¶®¼«y+?Pö†-]}¡YôgÂ¥g)ØiY”w—÷¬dZÍC†]¹*<'–›*¥<ÇËMLbŠ Ùf•CŸfúƒ;BžilÊw÷K€—RûpY÷á×k·÷¾q 84NîV»ÿ¸ ò‹ÞýÜ%¨ÒŠåíÜ%Dª[Ó;OJ:ÁiéÜ;:º3¨Å"÷ž«W4w̴żÒÉÃËÛŒ¼‹x[†]rúï7~ñ'Í?ö02AG2ÛâMúë^цÂ÷öés¬â :¯vm¿hÒ{mÿw‹gÝ ×ØÒ»N¯á½ë:KâBFµYe×­^¾ÝW£Ì‹u;¬Êó3ÖyιÕ0qHM[11õ‹SËnôì2vx'·G›G sBÑxȘ’‚—o¾«"uá˜Õa%û-Û°~͈ÊÑÛ§þvÛ®K¦Í³ ß;iÂqeËI«·¯ÙÙî¯9Óþx¡#"!ùò²ÏòûuîX“ ž²øoûÓ׬X0±KEed¬ÇàÇeØÒœÑ®Vf $‡0SH 8Y->ƒ‹Îàk‰¿ùäk8cZŸ¢r¢²²óL¾öJÝÚ–ˆÄEž:²V>ލœå“ £Nÿý¶q•¬-’›($/33·°øè z¦K|yõ÷¥'â<Ú—w0w èèML“Í—¯èxðχ1ú²b"‘C»¹ó»y qéâNL—%)[ÅmûÎû‘ZßüDbˆSÆwò‘Q²ìÜÎ;ªÁ“ûW5#­Ó£ßÿ¼ò$^ëø¿ç,Wµ«è("²kÕ­Üái§Â¨W†ÍSß_s ²âÐiþE•DŽõ;6Û×÷ÌÕèú®ÄÉJZ9¥ªGª›s£Ûfå|ÜmywW¯²upþs~ ì [šÐÀ»ë‚.¿ÄN€ÜH$‘º7x…¥Œ=×/¢æxc§‡Ü±ˆwéå»/:/ܙܯŸˆˆL}šŽ™ØÒE¬Ù°heÐÍH½Hi.MÖ[èFDÄIMåÆgÔ¿»¶eÑÊ]—^©9¹¹2E'ÊoX„x‰˜#"âe&2^-扈8¹©ŒÓkôê7w_©ž®éÞr£áBF#”HÒgÜ–øg¡Ñq¡:œ7<Óªµvo“WâD S GD$w¯QÑbÒ‚^}NW*W¶lÕzÕ‹X‰q~Tö‡-™I´Ø]ÿ°‹ÄØEûÃC:ŽûÐÔŒé'–×LøÌ2=zÍZÞBaiog!åˆôoΚ~ˆœ¸¶Mi{yìÉA]¶¿÷|qMßòÚwüÊùU\LÔW'·™óé-5þÌô$.:xå˜Ò&i‘˜™ç3”¬D6Í'Ï L»f›)Ì%÷2l^nÄŠEUNþyñÚÕsìüû—õ£+æãp~˜aØRÚ¿ŸXÒRŠ ¯þv|_ááƒXòóá"ç6¶2‰\ÌRT†øÀt©="޽þ#kó 7ww³´ßµ¯o¿¤Â?·,c¯ b‚ÀÞŸ,VûöîSGËæU\LD†e²µ;o;áÚýw _—Ô‹N™À(1}óx3·ÊØûáœEI+Qj0b\æÉ˜À›{VkîY­yç¨àaíÖ^SWÌ'Çqø©ü@Ù¶d)AËøŸÂƒL{ãÖ µF½ñ¹dRYÉ%-Ì-°+á!G£€.%1Y#0!%)I¥3ûø‚º玟·*j‘tgÏ’³’SJ›óR¢VïŽoÞ_¬±Ó»K{7ÿ™,T$âdÖ.&ï.Ÿ¹|˧3ñ(æ¬øäiz±—°eÿîe)Ù‹Ó[×ß×Y•Éš,=E{Žì;î\Ùòí¥k®¤ðÙ¹ò@dWµUåM3'Ï´ت‚ýàï%­mqó\Zú;ö]1ù7êZßÛ,åœǞT5Ð$C)É×N8íÚ¸a9ÓÄ×£DNuìp‰ïÇeØ’¥ âÚEõÓçO«U®&“aÎÖϦV«>y¨Õim¬l°7á!DzCÌ©1m>$¢­CÚßµuâÇÞ^\ñËú7Z“µúÍìSÒŒ#òh=²õ£™[g—¹×iն‹íDD²Bí{Öº½xÆÀfe‡ü6ýŸÂƒ“ßϽîÌÚ0ë—íV>›7-ðülÖE8«êû]ºfîøƒfë´iáóà@¶^oYeÔœÁ«–ï˜=r›š¤¶…+7íl#‘™fÚ¼vÓfˆW¬Ú4é`¼@f.ek¸+¸¨ …HJ»Eo›;|C2‰¬Š49ªžƒáGaØRN†©¦¸O9F£Ñ`_} OwϳÎÖª^ »~d\‘‰Á__ÊøBÑõë×ÏîÒš‡Ëºí±iA sŒö‡±íHÈ€öÍÿµ§‹?ÝÏw–ëêý£úo‡œ©ïÏ è}wеuÿù™¥Ûö5«Qþ?¬j2žeø&³o¾-g/ž­W»>nð½ žjýeëb®Ƚ²?lÉF¦þX3¿{½q×MFS·Š-‡Žé^Í.Çû¤KcŒÍ šZ^™Å…¤GÇW¯ØzèïGÑZ’Ú,_«q‡ÍÊÛÿc×,%j&Äqì“mélbŸ½V†?)ûŠM಻#g: WËþ°¥O᥆/TDœ~yÇ‚UCF[î[ÕÚå?8ðYÒ­UÝzoz[¦}ÿC ši^ß»xh÷ŠÙ–å¶wsÿÒÏ#"Æ2·¡qÄÿÓuÁÝL‹¼zJíôþŒœ«Þû—a‚F”©½­º;¿Ï¤Gç/né,Î8—YÖ9 ÒÿÆ2Å.ãs§w6ÄŸÝ{[¡i Soá’FûxÓà7-Ÿí—:2ðƒs½£mûf}%ŸEêÕoó~ìzÈf~ ìÝ$î¹…k¡"ETħ þï3ÎÜOjíbAB½sg­>ËÌ 5è5ydóB õÓC ',;x;FPجÜ~â´f±“š xpiS"}øî®Í¯½sKÃý®õ¯¶u’DÔ§ve"»öë7µx¼,Óêš´Ú§[¦n +3ú÷MœÅDD%~*_¿e‡G¯e"õý¹½o¶Sáï•þç1wÇKc'ÿqÿM¼–d¥[ýu`u;z¹-°íÞŸºW Ût%’s®ÞgÖ„¶>b"bš7ç÷_½ùj”¢P³_ç ­kÿѵ ˆŒH`ÄIŠuÑÞ3u{y'Æ¥-ÄejDs™ÖO/ŽËô;K_PìØ°Wÿøü<Ë4{¿9ÿáâ²$ã_˜Àˆ ²l™`\C`ŒËü²nÿ‡6€½ÿWöE=4ÿ–¯êpøP»PÐéI’O)"ÒG?d¸ãô³\Snn?uèêÂ;š_5ãlaK~-%‹ºw>øÁ;í'Û‹"Ç€%Ó¯¶˜"š¼uli¥H{ {ÖÕ)-rh³BJúXi3Uì.ì<ñ8ÁÜ»ùÀ!í½ßnµä¾ŽîéDäÚqÁ¼Fö"–\ˆ˜a2fÆH{s÷Š ü/"Eb]ԷÀvì%¤O¸¿oŪ=Wߨ¥6.ï^šuY9µ¶5Ÿé­çˆ1„„ÈÍrfØ’‘>áÙ™åkïZ7XPÜ„´Ïo¸å5lw`E+žÈ·w½AËÏ<®ÁÚV)QО/X H%?"ÕµOÉKMM¥¼Hd–/Ÿ¥’’Ÿ<ʺzÚ¨‡ä\2ÿǦIå X¿®¥qˆM‡ÞEIÐĽU©–ÿ÷“÷Þê*Iˆ$.˜ÒÅULTÝ5â\›ƒÇŸv«K$.öËÊY¬yÒy¼Ü|àv„¶‚©äéÒãCÚÉzFŒˆéõ:Îø0Ç‹œêöm2bÛÖKå}¶cýmçö3«ÛR0é£N„¼kѬ_Cñ£ýë·ÎÚ]ø·.‘Ì™}Æ©ãÈùe,ã®íZ´d¾m©¾cªëëw­Q§MÍŸlE× ­ybŒôO÷qoßc„俦µ+†_·+Ñ0px­WVÿ¾êÏ*3ê[¾>8gÖ§N#ç—ΗZÚ_ )7·«Þºûp¿È“ë¶-ß_iA»ÀñÝî ßë9jr ›”W˜r,í6“Œ1"FŒ¥Æ"Ò…Íœ$kŸ6µEÌ—8u‘l‹Ö'–Š8""^i¥äu*íÇ›¸c\¦Qþ\úf )¦÷¼¶¨gÏU“ª»úõhôçä5[—ºÿ—U‹)uíxÁHìÜnÄ€fŽ"¢Òo¯ ¹ÖLwâhTé>£} )ˆlk4<4êÂèNDÒâæŒ*oÎiŸ_eDŒ11&òê9ºo5 Nï~ôìñ2ƒ‡6wS;sxYh¤¦Æ»CG¢Êôã[HAdW«uÃÃ#/ÜxWÓ‰8YéÁ“{—4!ÒX<82ñÁ“æ.—‰9^ª415•± ƒ—ˆHÈØó }~êH˜cë¹ÍÊÙ‹ˆœzu¸Þsð½ ŽûïX4ŸÞ±¶‹˜È]ñ0èèÿˆ±ÔwŠ÷ãXæ‹ r™œ¶ÄK+ŽZ2Øõþ²óï$ŠŽˆ˜^ϛ֚¹ahñ´¹VE2‹|¦¥vl¨uøøŸÿµaâÎggîkšý3Î|¾Ê“²¬>¥º•±¹OO[ºõ¿Wê&6 R–¿i]¢.êðøÑ'²£úuì®Û¼ß;—w’¿;Ü¥Ùº÷Ÿ‹ _J:Žÿ‡-d,ËåÒ,-U'.ÖiDÚ5¼ÂÞŒÉQšaÉÔË1Æq$èõ™zŒÉ"uCÓb€ÔÆÝ2åÖ£X]{K {'wr¶•™ªž¾Œ×±áÓ p}Â|orz¶%‰{Ë)³tp‡3ÆŽW:{ÚÇZ{T¨5¾y=+×»ÃÖ­®8¦!ÏH}ýÜ%·Öºçn:®*Þ³Œ•BÙ°ŽÝ¸­‹ÖS@ Ó”×÷ÎÿVªog¥!%âeŒŒÆC ßÐ= p© w™»_»±.Ò‡qVÆHšÏQ÷¿ 7’¹NáêíÖ•Ã#Aõ&ôþ½·†Ñ\¼Âѵj]—ã{Öp¬`“pc÷¶Ð|Õ~)\@WÖìÌÎMëºé_^?©s ÷‡A.˜@x€\.†-™×Z{¡V†¼eµ_‚®ýbø¥X» kÚMȸtá‘¿Õ™¹©[“iÛ›L3þ6°Ý ""ªµì’±TëjC×ý94ué ~#?µ5¼Eñck16ëã…‡ÿ’ö §,ÜqÎîŽi¿·ïKDº—D‡F6vÊŸá3+˸"oÝpõß ?"NøðÍ ˜îÞŽSÓñè9ê§àÉ•7òW¾mËã¿î^¶LFÄ"µ+Ýbpïò¼@®M†mݾgÑÉDLKTòs–±cß8NÉ8ûQêäG†«ŒéÁp²ß8ÐHâÚdÄ(Ñ–mé¥5Ì/cц(bÌ©ADâÞ¬MÅÐMË~=nR¢ûÄv2>ýEŸ^:ûtê+Êßqö¯ ¼vÛ’IhD–ÞµzŽiá. m´ŒYy`ñ~öoXño—åYÕ÷×Y¬ë=¿—·<óöq1ÆnÝ»U¯v=|Üà{<%ÔúËÖEÏä^9=léG'¤ÝwùƒÍ÷4iwUË8­+±Œ—= ÆÉ‡¸´Å¹ôu³Þ úwWcÌÐ"Ï8úÀ}³þƽ§èÏ—áÒš§ÇÜ7÷*h/Oyqqû%®ä` Ëzlô: <@n—£7‰ûÎ?¨ùÛí>Ûî+ I”ûDƒ:Óÿ¹¶ù3ÎeÊÞ[‹û@*Imz³––ÞUÁþ!Õ¼hÒ{H¸­Á>ù:ôÚ··î9” #‘™[…€í¼ä”q²%.ó6 <äæü@93Û'"ý@#øc­â÷ó6¾S—ú¦õÏë°¥€÷cûØFd·¹þÑ Ã}¢p#©gë1‹[gÚO,k ¥^ñ €ð[¡Ã!g1&|Í ô¯9ñÎr¸¼ì>!˹¢ár5 [ÊéðÀ#îýÆ0—+¶î#s¹gÿá@x€ÜžÖrˆÀ 3$½7'ËU\¶ÚË\vþì#‹q™~b9ÖdgÜ•‘Ë2 ÂÀ‡ Ã!g}ü6gŒ}As=‹e½6;Óó°-Æ>ÒRç>¹Õ‹5ì#›Ì}êÕpYb ñár5 [ÊYž>ÄNøJÇNÃN€ïÂ7º7Âäöü@¶”Ú´lƒðƒØ¹gç7*ár¯Ïêp`¸À7Æc@®e°dHØÿÖ> Ù‚aKŸ‘Ö> $œâßjê”oU¾úÞü@_?_?ߎ«B5š+:5q6þëfõL¾>­I«™×UDD¤º5½•—í/t8šr†- <ü‹dÞý×®[Œ—”³}e—‚ÒoðR—¦ƒ†õªl%ÂÑômòaØ@î+o§y}fý¢•‡oGiåÎåš XÖJD,åIðÊy›N>ˆÌÜ«µØ? ˆ©>|oŸ>‡|˾:|ã-çX±ÓøáÍ )¹ŒéH¦4Qˆ9^¢0QJBFõŠ!šÕ¢Ñ,RT³e”ÏÛSËnŠ×{ûô9VqPW»¶_4é½¶ÿ»Å3ƒn…ÇkHlé]§×ðÞµ¸ƒƒÆ]L&ÑҟȦå¼1¢5‹n´_WÉÍDsuÓÂ{®¼N‘Ø”ðë1²[U‰êÚ´ÎTí»;„l:oáÓvä¸n¥,(ö[,ÝuùuŠØÌÙ§NŸ‘Ý*Y©Îj31¬þ¢ ‹Êpd}æMâà[Ë…=)÷6þ2í´²ù¯‹Ö,V3yÿ/“‚^êXü•%#~»áÒaÚªåsú±ö—¹'£""]Ä‘1ÅÚ›8¸‘ŵµ·>R´dμʘYõ󉼮߶uÏúÁÅ4WŽYV²ß² ë׌¨½}êo×é^îX•mÝ'ÀÇLlQ¤AÉó¯˜7Ê_2oaÈ;{ÿcËÊå'¬ÛºgÇ’®î©L÷r÷„É{T5F/Z¶âׯÊÓ³Fn~¨&"T×ÖÒTðë¨nÞa;æï|¤Iº¶|æöwUÇ,^¾rú fR"’ôDÄñ<ñ¸ %†-ä*¹¯çAõ`ϱ˜}ç¶*cÉ‘gÇ!íÎ÷:xôyUÏ=çuUÇð+nJäÑ}ðËÿU­ ‘صûäQmÅDóGýÝóøŸa½¼>2<‰›˜ÊxN¬´°°0焨àç,Wµ«è("²kÕ­Üái§ž¦´'9´›;¿›‡¡—.îÄtI1Q’²UܶI«[ÀDÂó"S ‘&ÂP¸æÙÑOówZÕ¶ª“˜Èeh¯ËíWÝìGÄÉ+Œ[0´œ)‘Úêîa÷Å&h^%™x•)UÐEI.žÅ*‘¤ÊŒ 8$ßË„aK¤{–bUÞÝÄ0öHlé塈~ð:Rx-8TqV’:w⎅FiËñÆaJ;ogñï¯Þ鈲umƒæÍÝWª§kº·Üh(@§Ñ%’"⤦rc€þݵ-‹VîºôJÍÉÍ•):Q~á#[«#Å( x[v)gêîm¥ú_X¢àL/–Š)ò)9mŠ`Y¶FÁeëGw~R¡R™R•kÕ©à¢àp0¾Ö¾&$’K²Ý gz¼rLi“´‡$fÊ„ð KÄ]X4}Ëkßñ+çWq1Q_ÜfÎgmÐ{p†!v ˜¹É+äØù«×N®:¼ãhçe :ºKp8fa¶”ö/vÀ+×°Y¸¹Écî>K44¼u1¡UV…í 9òon¿TU‡ßxÅì¼l37·Õ/n„ Ž>Ÿìvàxž½@D$±ó¶^ݧ°Jci"Î=´oï>Õx4j^ÅÅDDÄ„´^ž>sÉ2ûBÖª'Þ¦le‰OÄ(\\LùÇ’9”¬×©ÿ˜…+føå{~þö;ãGòaØ@î z˜6æÉ훼Üð›ÄÊ¥E=«A«–ì¶èPÁ<âäÊm/=Ú5p³±¨*ž°té‘!­‹Šúí¤Ã*ÛŠ4Dº¨K'Ï{•µÓ>^”TvXUûLs¦ êä$•Ž ZUR²F®[ºZ Á§NÞt.‘}¥V•7Íœ<Ój`« N\ôƒ¿O\”´['ÃÚKOGÑž#ûŽ;W¶|{içš+)|"’Z¹Yª?w×ÜY;·\ê^¯©GЦ…;œ{V·»¼~õëºó +èîû/Y÷ò÷I›c+ù×ò±å^]y˜láéfÆSÒù1Âê/Z׳- Ãá!KxÐÝ\=qhêoÊÓw™:V·`å¤A«tRÇ2Í&iê"æ¨L¿ÙýWÌÛ4¶g<3q­ÒuÊ_^ND,þÛ~ÝþJ%u¬ÜiÒˆšV™Nõ«,í>âpÍìÝdÙÚ^^Õzt83}ù˜!âüMüÖsÔœÁ«–ï˜=r›š¤¶…+7íl“©7ƒ³ª>°ßÕ©kæŽ?hV°N›>‰]ökzyú‚‘'åÞ=æôJÝ—ù&ü’´pÅôÁ;Ôbëâ~Ãgu.$'Õ^²ÈÒ§¨tÙæI»bt¤Ì_¥Ý˜ÞÅDILH@DºpØ’ a/Þ=–˜”ˆ ×251uwswÍïÊç•)òPù òÉ&®ÈÄà¯/e|¡èúõëÿû[¯ ßۧωz+·vãxú‘;u¬MË6yà…< {ö*ü•·—·µ•5ÞV€\+:&úÁÃÎNÎî®îyã¡òÈc•ÏÎ=;ëÕ®÷±¿O ýÂ;ÚÜ{ý€Ã–ž=V̧˜…¹…F£ÁkY˜[x{yß¾{;ï„T>¨|à{÷[JLJ´²´R«Õx÷þe/^¬T©R6ÁÊÒ*/ðAå€Êç‡b§k‚Zà°ËÛù~¤Ù–8Ž#"ÆÞz€cìs?z†,*ø¡*ô<@îõcζ$‚€ëærýG/ï}NQù òAx€ïÛ8l‰ã8ƾ¿þ}ŸûÑcŒå±ž‡oWù¤Ü^ÐvÌÛQ;¦U2ɯ]¾§gÏã V/iƒÉW•O6ðxÏ —çʉaKWg5-ã?ó²ñÞƒ¤<2 ¶oß?Þè?ð%7¯^¥*åzŽüÄYu}\Ý*-Ö=Óæø÷7qÌHx{tp­ú ÿ <—xuJ£Ó¯&³¯!ÄžШãÒP5cŒ±Ä³ÃÖtì­Àà pôÏßßkÚW«1úLLj}¯Oo\½ÕšGšÜ[ù0vPjåS«A@Ç_6\ŒÒ~Õž"–:4ãë©CWuªU¿aËE·’ hÃ6woÒë`¤þk6rnrGåóí dC¥z õ=1vúææÛûzË…¸ K~»Z çŽ¢¬ ªŸ{gj-ºwðRL›¤k©[ëѿưåüÙ¿ô3ùjMßí¼uÀˆ¿}çÏiëiÂn1"ƾnXJ/EâÕvØh¡ˆ2ýÓpò/ûËgçë;pD³=}­¹]~D %GšG;çîç›®ô”æâʇFœ´t¿)= ‰’ÞÜØ¿jó¸Éæ6qþÒ&ƒ¡Ša93*J#Þ4þÄÆSm¦ûÙŠH`,µ^ûŠÍcµy¬òAÏü ÝVVV†ñµºeÕ!ƒÊDlý{˜6éæÚg¬»iî&yo9õ“Ã&”é?¸®ò^Ðåw©î. ¨RclHŒ@Dº»zUª?þtTÔéߦ¯½›B$$ÞÝ9º}ƒ2•ª”ómÑeÚ‘WZÒ<\æ_©J§ƒo¿à[(ãÉ?ÉLžãeJ¥8bß q““Îhéïë×eùCµºwΠ–ü}ý{Í9ô8Y`êÐ¥› Ø{jÝ/]ë7Ÿð×Û[ˇukÖÄß×Ï¿Qç1+.FiÕ¡Ë̽£{»gP _?ÿ{_<^±àÀ cL~zÅèÀ¦þ¾~&l¹­cLˆ Ñ ý¼ý»g÷héïÛ¸ËØÝ¡I裀Àêµ+öØ“˜”˜öHrrò?ö­^»â Nþ™”è:¬zòî¹{ŸiIÿúÈœ-1µ†u+iÂ}måÃRžœÙ©Qµ2•ªTmÜyäŽ'ʱʇ1FœÈÜÙë wɪ­†t/J/MŒ%ôÙºÞ€Ö¾~-ºÎ9öæÒêQëù5k;vÇÝDáýn‰WóV.¡›Ueê7H<7ªYË…w ª¯oßdXpŒ>6dDƒösvmœèïïÛnô†[‘ÎéÞÒß7 ÿì3†m%"]Ôå-£º4óõkÑiÊþÐd1öÏÕl>.{+ô<À›(gf[94Úû÷ŽËg¬x$nµ¬}A)} ;?_x@Õ 6×eƒ^}W¿žµ¢H±ÍŽö_°êfÙáÎçæ¬ «4jz «˜Æ¬³ä^åñ§Âo‡œy«'[âyâ¾0—s¤×ëÓÎ@O×1·o8môî ùKWˆdòØàé¿nŒ^6ÎY}wלſnö\݉#ÒÝ_³š«éÛ©[QW‘8¬DãÁŠ8›hÂN­œ;we‰u#:Oïw§ßv¯‰s;Š’Ç¡Œ„仯O?ãÚ}Ü‚òæ‘gÖ,?Y±rvS3tïN¯»è×uÐX“Ç{­_ôG…­œE8,!OJ;ñlogqèð~ ü•J¥ZrèÈÁØØw¶¶vÏêõúl}}óV5õ,¸bþ‰ -.¬¼[´ßî*–üWW>º{FÏL`ºð C¯Gø³{»V¬™¸ÕgMg‹YÿPͺ‰ÑW¹¸òÉÃáA„ˆ¨ˆðððM  u«4‘¡Ñ÷ŠŠ°à x”¨BDD^}‚.öùÒ¯o.ã”m©ÿcŒ$J¥„ãE&fææ Ò½8´ëžGïMKå㈪vš·"˜C›Ê%<íyÏE*6ÌÑÊ'ý\¾.ñÅ_›¶‡ZÖ˜PXÁ—¯LË6D‚:.úmÁr¥-CBÃwÆHä=xƸ:–é]^ ¾• Ûw8ܻNJuly"/Éåͧ£é"Î]7õ[Ø¥F!)‘kÏÁ7zMÚ{£—#Qá‡Ô¶äôî/þ8u¤Âè1mÝ¥”ÈNìŸ?RÝÈN‘õ]âLеjí4xóGÕ;)˜qk±ÔŸŒ¿2FDbËǶ/"£dé¹ÝwU&ô©bFZÇÇ{Ï^y§+ˆÄ.]~ÚÊIDTÆ)êRŸ>­¡ÌF5‹¹m!WW>y6 S[–u1^ûÀçó( ˆ P #‘Xd,Nf!gqγAž?ù'•JëÕm|ìp\\ìþ ½Œ133³úuÊeòŒÇ¿ ÙžðD^¸ýÏõ÷ÿ«ú 6$:qñÙ•Ü£n5‹ÓÚ¶ ®^¹R¥ZþuŠÛH(Ç*ÆHP…üÒ6„ˆHäÑðçÙ]‹)™ )ÏN¬[´åă8’˜˜qIzWN/0FœHÌ ‚ÀÉÌœN•÷üq¢eÉ‚æd¬k :"4’wðq0ž°—:±‚Gilq¼ˆc)ÏñÄA N¦”]æk˜±.äìëv¬´kÖÖs~]Ò®¨²Vp†‡ñbÞ0ª\ª”ñ)"Nb…”Ók´‚@i‹rD¼uAñž—‘/µÿ\ÍäöÊ'†‡ððp Kk[±D‚(ÏÐiµâ·âðð內³7‰Ó<Ú:}·Î¯_Ó«Ëf¯¼RalyóÌ=Íó“'Þh§7/7Ýøˆäàõ¸:µ-9"‘T&"JNLPg>Ë$²k<{‡ëÉ#Ç/^>³rü¦-×lRJùåi޲†‡´ïD!í’@À)+šÓÍ[žº&/3—¼>ŸúÅÉ‘>âø¼ÙGøVc—”´“ÅÞ{wjg~Æ^}C-D鹄Ò2E†¯aH ŽˆL# yùÌ_Úá-“ÉêÖmpìØ‘ø„xÓºuêËåò,¿ Ù?ùÇ)œ<­ø;–’­ñ•Ÿ¯òÄíëk9þçßo˜´sãÙ{¦T·úŠs™*A`œ¤TŸI]ož±öa"/å™ 0íó=S–^tî:uSo+.|çá˜q¬PjÍÈ#bLÐkâ92(Œ¥—žþ4d8?’^B†&¿@ã2.oÜñ©×G3e±€ŽÃ6=­‘ZWe¼ðÙX…e®ÊFÆ-I- ‚@,uEŽˆôzDRN¯þ§jà;¨|òfxHѤØ;8ê1ËA.>¦¿ì,–½ƒã«¨7_óÔ9:lI¶oÖº˜Zs¶-~÷Ñ™a³×6Û<¤¨"ãÏNœŒpï²dR-Ã4'‰Wçõ]~ðj\-_óø¿–Ì9ëÙµxÇÊA¾ËZæÏp LÛüÔ ãO :z²®MÇÃç_ ,åõ1˜#AÒ‡-eê…ç8¦×3ÆIì<­U?Õ˜Tt¤¿OÚ´®{F¤ysçêß´¤­ŒÓë™±ž#A§OíÕ'"b¼™‹«üÝýç ‚»%G¤‹~øXeYÞFÂ^e(/Ã5‰øP@^­ë2Þr™¼®oƒsþ¬T¡²Riòþ‘/B}}Qå#œE¡:m ÕiÛ7"¨—ÿ’§êêV ʙʇqbS{—>‡Ž~=ô×…ÓÜfNkî¦yy7Ò¤Ô@_oK11¡g4}tPÆš‚W:ØŠß=|•,¸™sÆŠƒÛ´‚î„«Ë{I‰˜êÅH‘»¥(C §MÍXvÆ7*mâWÞ¡n» {fïø["ˆ1Ɖe¼&Q¥7lŠN£g飙RKL«S‰G†²ô#Ò„ßy)Ø7vvz÷OÕ,À÷^ù|·áã8¹B‘œœüõå ÿÞ /~[¿òMÉÁaKúˆà+Ÿ•9½r>_±ïÐ “fîn²®“§$=\œ8éÖ¬A¹B.†O…`Ùü§¥3ƒþWÞtõ”ãÖÝ7unÏ+Ï´[>ãXµÅ¾©à·ÇÆN»]:À¯¼«4òÂÍh¹[a‘æáŠ€N[¬Æí_÷ÑÙ³uòéS’4R“µfb‹üªã'/Ü5qdbÇšM½­ž±DÖµa KÝ›¸ãÜs€WZ?G"+[a×{ÏÊKH_ݽ%Tgõ˜ÄÒY{õÏ+÷Y>‰½ñ„œÌ³q-Ë‘ëWî5k]Ö,2dÃîp÷€ÚÎ"áe†Ù3ô ¢‡<éý™:e2YZué#÷sͱ“_Tù$]ž>"Ø= Ye/³„«—#Dù:H)§*J?YÏ”EÚïöìçuÓWäŸÙÓ¶@¾¤?÷þKï!<<µc[¸Îƒ;RkÃàP&È Ô-%™ºvÅn}Mûø{'öžIЗΦB£Ÿ¶­Z´É³_]WݽKÿUSÔ„E¦—`¬ÉÒú Þ›â5COG&ÅZ6s²é9y2AÄEÕ;vï:£)˜rïè®ÃÑú‚™7ÐØmax¡,µï•#]Ô¥“çÜKÛ럟Z{0©dÿ .šªf¾óÊçû ††æÐôÿ¯ÚñÿUbÉ‘­Í©aK§üö?ÏÞ»}my"âíêí±3p匃uW67^»¨yqúØk§FUÒ>¼eéÆ>º‚~{þ‡¬ÍÊ6î µÛv_§‹BJw2¾FÓ‚ål÷oßsN ÉËNS˒׿b_ØÄæH¯7ž5‹ ™Ümùc"Ú5¦ç­Á«§WòíÞðÚüeBd;ΘÐbäxý†Mûæ_¯#…½O•F5̹ 'ìˆwðíÓåÞ¢­ §þnY¸¿Ÿ[ØEbŒI=:U½·jÁˆÃ¦¥úMnn\Aê8n¨nù†™£6è¥ö?ùâç,bñ8Ÿˆóm€4©§$rh“/«|¤ùË{F­›Ü{y‰lŠ78¥±³ˆ4”#•e8ψÄNõ~ùtÔÔy ÜfÜâù¢óÆó¶¥Ô­k·ë1eºÃZúUÊdZºûÏ —mYxYb_ªa­ŸB ÄoUmظإ«ÖŽ:’Ì™yÔêñKÏŸLH›±³!ý©?ÞóáA‘C½v•÷̸@Äã|ût¾»pÇosȱlÓ¦5^n{õ©ž‡´Ÿ‰ˆXÜõÓöF©%öåZ\%ÇQèf¾ïÊçË›£E&})ã Eׯ_ÿËÖ½xéb¥ò•¾¾çr¥Rixs¿¸„ìw8;udªTž IDAT¬MË6y`§<}Ò§°F£Áñð/»råJÙ²e³¿¼T*½{ÿnZuòÆËGåÇ*Ÿ{vÖ«]ïc žjýeœ z8¢ g©“’’p}w!8#SSÓ,oîËñÙ–r?Žã2ÞçþÍJï³>zz½>/õ™£ò@åóÝ„‡,ö¾xwü8×<äø+ý`NÈ‘7âëÇäåälK߉ô‘ðïV°Ÿ=r•üx•On¹ÃtZ‹3õžbLÿ8höŒ3%'Í Èo¸¨•%?>¸zÕîKá*‰M ¿nƒŠ[ðDBüͽ+Vº©‘;•mÖ¿—_!ŽHuiûâM'ï¿LÝ*·ï×µN~YžÉ9rmîúq:Ò(•ÊdU²L*ÃW8À¿¬téÒÙÿÜq—¬JV*óÎ=Qù òù~Â÷~xÐ>Ù6zäo‘C)ž7ä –|kËüMŠ÷××5þüšå‹–ºÌ[Å"þ¯U³ÄÔî;¾¦åë£+×ÎÚ⾤OqyÄ©ùKΈš œ^FþpÿÒUóö{Î ô”~÷Áô»¸²<ÓbØÒgruq}öÜÂÜB.“£>ȵRÔ)qñqn®n¨|àG«|rÅT­ïý*õl?OÛȃc†6ÌÅDD)OŽ]RëÑ¡~q Žœ»ß<7ñØ­¸Ê?Ý9yG\qdûjE”TؾåóËO„v.êxõÌ“|ug·(ï!!¯.!ƒözѲ`AÙw}¸|ã²¾~›´aKöŒ±/_DDF Šȵ …«‹«ƒ½*øÑ*Ÿ\|Í—Ú%Áq‘>îù‹‹2Î&<ÇɽíØõШäü¡¯õ¶åìåǧtö²Öœ›"$;/{)ljÍÝ ˜Ä?z©"/9¦ƒýoßÜÏõ[âyÞÙÉ9¿s~<¹\ރʕÏw²IPÅ©Hj"54Fy™™Œ©bU:Ul IM¤†ë$x¹™Œ©bTê¤xH)7Þ"‡—›Ë™êJO–9þjq‡Š*ê¶”'%€ÊòŒÜ2lé½ ¦‰˜áÆk8ž#âEb‘aÎ0ïÏsD¼XÄó`|‡ÂJŒYúÂÆWFt8 <ü#i¡^ëŽöz¯Ujâ0fI@–Gù|e:LÜÔ!K‘:Vï½°zï¼ôVî^þCMÖvìØO;ämÛ¶ýAÃaXü.˜†,;ê;ÜEå¢Ù–q@Þð_Í­œ[®yøÞôyòéràÐüºíŰ%€\%×õ<@^ H˜m á!¯µq¿ÿÕ„Kÿ!̶{ü÷Ó—*•ÊÄÄDî«ýïÖ÷õJ•Jå×”`ee…(ÂC:;‡×¯ñfä%‰‰‰¯#^;Ø9|M!†KH¹Ä?lÉÐ.|þ2%%ïGž!—Ëí­¬¬ôzýWæ°%„½^ommíàà€k¦óƘV«Õét_,2Ñét_ÙÊ„< ³-ä*#?†- <|:²ÖòªV­Za' <ä|~ [@xø4t8äU˜m ááß Ô«I¯Ía¸£ÜÖr•ÿúÓ ç‡´™q˸-fÎ…ËÕkPÁQÆåüS©nÍ u:ñ½Ç-ý¦ô1~ªæç]ó}­8¹.?†- <qÒÒý¦ô(È%D=¹|âÀ¦Éý/w›?+À]–ÓÏ#÷l7{^C-#RÝY<~«¢ç„žÞ2"Njíb>l´PÄÉ!—±²²BlÈ=rÁ°%Ndžß³·O™ªþ}&,YÞÕíÞú…ÃuDLýâäÂaøùû¶ìýë®» ,ÃZÉ·—ëÖ¼‰¿¯Ÿ£ÎcVþõV§{±µwãÀ•Õ†4OVwmÖ7(BŸö áÇ ¿¤Ô¿ßþ²³bÿÒçï~ss×ñ¯Ny÷­Ûûîúø‘—–VÇMì©]GÞüð¤×_|âÖ¾_<ýÚ’ÒŒþƒšå/œ·©\D¤|ë?îi=âÄTkÃîZ²ü“EIgß}÷ÝófÞýíSKNºq Cí‹_ó÷}š”¬žrÇsËš_üä‡ï½öØëÌ'Ÿ»KÅb «Ïý’„¶%ÂCÝ"šŸ!yëwl_ôÉ‚ø nÓ瘴Ԗ}—÷r/™»±´:eDµ?÷â³ûwn‘–ܪwŸÌòÍëv+ý†´ÚýËìMå"å[æ/ØÓöŒ’ú•È>w=~Í'Ÿ<<ûä$KÆ…Þ:úÔ¾ƒ³GãÞ²6¿¢ðÏé³ÝgÜ|õ©m3R›vyñèÕ³ViÝïñ¯ftÝqáœQ¾Ä„`Vì¶eÓësíX•S²ñ­+F¿çY‰à*/W;U÷ IÅŽ§¼ðúW+vº­QqaÅîx—ªÙÒûœÑjÊô¹›®ha»`_ÇKz48;ˆ¢XmŠˆˆf±Ø,U_+î wEþêM…ÛßvÎמ;¥–•»Zï)Ñ$–åþài[ªþ/@x8XÙ¶¿wHÆÀTëz±wÓëww®þ+{ltÑw""âÞþÓ}c¹`âÛçuOØ;çÆK?±¦žxz«×§Ï[wŠý§‚ã®ê–àMM¯(JÍ?ˆˆˆæÖ¬-/}êñAÉUÝPŠ-:žv%¿æ ¶%wÑ–_g|—ÛmÌèŽ1äQ@x8šòÍßøciÇëúfeÚÒÔe«÷DjYùwšª©Ež/+¶ýµU:Ü<ºGz¤ˆ¦ªZåbkÚ §·}óýwÞ³vº®kœ¯ë/[r»LëŒÕya޶ñJõ¢Êó—šp¨È™ýÑWëJD$•#tJškîý[ׯ]óÏòE?¼ÿä-×¼¹±Ó7žž–v’£oÅ÷=1õǿ֯ûû·™ï<6á£õU»ÙÓÚ¦©kf8ücùâ¯'Mœ¼ºêªq–ä^ÃÛ¬XQÒmØñ¾ï%Rb»ŸwZôâçxgîòuëÿY:ûý§î{qe‰HÑ»Gºðv[ò­ÚmÉÞdÐ¥ã®>·K Yì¶¥ƒ™­|Ù+wŽ%:ãØÞ—ù#?»-èƒrìļ¿•ûÚí:ýôÓuñ€´ý ¼òùøû?¸åøH¯ÁÕ¿miÖÜYç>ï?ðÉ'Ÿ\pÁºÄî¼_¦~žÛãº֡$¡ur#æj‚žHë<“‰ÊÐë.'!êü5ªªîÈÛ‘››[Z^Êñ0ˆ°ˆ¬¬¬ôÔt‹¥ñ•‹Äéë]$[`YVè7º¼4+¡ðRo¾ð°#oGNnNBtlZ3›GYEyNnŽˆd¦gzs;´-ù§pÓ*?ì¬wQåÔDDñj1«n_©/ƒ;ø=Ýj÷ų¡rssSâ“SRmv;ÇÃ4\¶|[nn®7á  @5RÀw:ª½ÛR#nÁ!â}¦’›áÂCiyizF¦[UUUåôÓ!­qOEIÏÈÌÉÛîÍ?MÛ|ö²Î§qþLU`Lj‡P—G­¡÷ŠsÒ!E‰ˆŒ,..öþv¨ãýt€}X½?(´-Á«úcºH¶ÿÿJã-^§Ü4k59Ñ¡âçA™=‡ž¡ÐÃV­ŠôžXÇûƒËEâLez=>d÷Õ Ý– W‹ð9Z½R©âíôBÀ^j8¦0#Ì<(•ªçOEEE=hô„CLLL­ƒëe~Ú–à«’Cÿ„ûá£,>þ‡éOòƒnyúÑb½jðíYz“:XóppÙè^—ÐYóàóGZgNðÉðræ¶%³Ôìþ|nç‰ï¯"Þ˜½ûGÙmi:E^°Ÿ°µŸf ŸðP«â´X,"ZYÎo½:ýÇÿ , m_¥¿tåã…u–Ùêþ¿œ“ßþrùŽRkLf‡S.½aL¯Ã$ #öeyyŸ™pªHY½¨W.³LŒ(Š·.õ÷|0jx¨½æAJ·mØѬU‚MQDÂR;dZ¾Z³£Bɪ¾µ{ߦ-¥ñ=šD[E$<³}š¶|m^EÿäpÏ V–­‡®Å+Þ}áó½ƒn}âäô²Ü¿ý3¿XU­œ;¸ EÛ’Þ‹’êêÊLêÁ6òõN‡ˆˆŸ6Qr2àTÃ"øä´aµá! Ô’}ÅZX³ðª>¦ˆØ0×ö‚rM¢”?Q"aÑaž?[Âcõ’½%G½>µ»(7·8ªu—έšDJ“–zùâiÂ*êÞmKÂ;Ÿ>žÔÎ`lcíÉ Õýþ(ˆùôG8ÙB*p‚ðàZ³æš‹¢ˆÅfµz-X4UEQ,KÕ3Q±("«­ò'ÏDU‹,ED©c̓%¹ÛI­Þùè¡ñ›zöîÒ©÷)ý{6‰¤ð×s`Â!´ÿ U•7|W÷Pôøé‰FÚA(„7^@ÌjU™ŠbJˆTÊ‹Ê5Ï÷Ôò¢r[L\„å@j‹JˆTÊŠ+B-/*—È&QVEQDQOưԱ`:¬ù9½ÑváÜEË—ÿôÞ¬Ïç\øÜç·°sè*0ÔDÛ¥‰ùâMè¶ßñ½ÜIäÒÓŽK4ò” Yó`‰jÒ6©dåÆýj§H«Tä¯Ù¦¦ ̯Q“Ú[µˆØ».·DZ…)R±}Mž%ýôô°Z8´ˆÕT‰Èè|ژΧ)ýç¥q÷/^½ï¼–©¯žz¡rq ŸÜÆå¡m ÓE²}WnÕ¿?ÇП;•ÆL…˜¢ªpŠbÈHB+,7@x8jY³ ·Z­‘-O˜þÍ´)?4w´¯Xúþ×yÍ/8)+ܪþûÞ 7ÎHºõGOKh?üĨ{>øhNâ°¦{|óç²N×uO±[-𻤏¤¤Ì­iî²’â[dT„õÀËpÅ–ÏŸüpß gô?6UÉY¾¾$î˜q6ãìçê§+Äù< ùp«V&Pß2Q6xšä‚ãk¯yˆcÆÜ;n÷sïßw{¡ßnØÿîÕ<Ì"åMM‹Åbézå=W¼øÒ÷Ý^bKérÖwœšj³ˆ¶oÁ}?·JDDž¼êépË'OöO¨®_Ã’;.üµ©}¶Ç%QMûžçUÇG[øˆÇŸaÃ˨CÛRH7”# Kݳճ+—÷ÓGt×À”¯ø'˜39ÂC'xDó!·=?䶃¾Öæò·¾¿¼ê'¢ÛŸ{÷Ëç\³* ýŸŸ9àð7×ÉqÇ+†í¬Õ4Ís¸:Ai[ •jXq8T»¡¶¨çû~p·Í5ͦ½!÷$$’…@8ñU á§BW˜A¿iÇÓ¯T±4–éwM­~€ŠAˆ×k’’’xŠê°nfm«G… ¢ÐOäêÜ©p ^Ö<· 7ñ?çáÍ‚i/.mKº’]F~ NGû¨Þ4)RîCS z_ÏÂV°áÁ¿ÙA‘ÐØ¶(»-!â„C—÷ªÅ4GÒ×Õ3ôœ§5óÔlΡQ„ƒÖ¸F¬ —‚… xûÆl²ÃúºýžšõpÇ+‹ç5¯ºÆü5QQQ………>é°3ÙH´°°0**Ê›[ð4, ‹xÅDPê`ž !µZ`:“~X^„C‡‡Œ´Œm;¶r0̤°°pÛŽmi^ÞmK5­¯bŒRV§°0 ¦"ôþ•Ÿ}(ømKž•·æn---åx˜FDDDfzfRR’ÛíöæÜ 6ÔÔºuk¡o$Óv‡¥ê3Qv;Á§¡§(÷×=7Çå&B;¨?<¸Ýîää䌌 ÖL›‰¦i.—Ë›a·%ðf“ DÄåryYe¬h[‚ê.#l¥ cgZV67.…Â\Ïœ…!€n±N:ÐoµZ啞C«¼Ì¿ã0Âc÷ÑÜKåroêW0ú¹“ÕžÓgRuí2³š <ÁnK~äi©ohÙRoÓu¿+‹÷‡ƒfªmSF—Œ‘f:!ÉZ„ÀËü ´-…ž#}nÍ–¾*û “ŒÊ¹ ÅÆ@·X'@¤Ü=M¼‡@­týaæúEÛLeüðv¨›­]õ¸ºÃЦ3‡ïªOeɱâ'3oòƒÐ¶ô7n6·Ðå3ÇÅ¿%;Ý8@x€±0á ÷P!TW¡|øCf±lmÙ|2Šz$ü†ýUCOB HXóýâ"q€¾ò‚CDÄá4]=gâ€7ýà?ñ!гÍ<”þýüè³ï[\¤¿{¦æsãÙ—|°©‚“®ùAh[ Ì{´Þ¦ø˜Íd'˜æÿåš`…àÁžy(XxËy¯¬¬¢›ö>îÆ1'¤èe>D+Û:oÊäwøs[©Xc³:ö|ñ•£»'Ðë L8˜!9øû½œ+È" b§¢Ÿ5ñÁÿL„à1ç¢ÎÐWx%¬ûø‡¯lg-ÚþçŒ7>˜ðH”gF6ÑC|ЊþxíîÇ~Ì<÷ÚOj¶óŸsfþ¸pÛÈî áœ7AÛ’ißûuùVäY\ç~©žpÐm„œŠ°m‘ÏO<˜ G™¦üõë9É ³‡k\ÓÖíÚFHÛöǸ—ýòô/ÿl³{Á‹½¼`C~±*‘Y'œ{ÃçuŠw­{õŠÛÿ:s\×e}ºbwÄ1gÜúÀ5ýSmâÊ[8eÒË_®ÈSZwÛ¯6ñÔþ«¾xþ¥é?n*´$´2ö¦ë†´Œ¬X÷ê·¯ziÛES¿Ûàjvêu]÷䧯(Hêzþ÷žwltç[ùæy¿îivþãWij‘c;õtn¡;LD+Û2wòóï}ÿÏnWd“¾çÝt»£c¬R÷7‹Ö~ýüóÍû¯("«C³=.é%"R¾áÝË®ÿ,áæ÷_’Äóû¨ùAh[ IÁÜJhúÁ_ûmfÃØK|p|Ç‘G†zÈ\ù€9è«Çív‹=*Ò*"JxVï1ÿ{ìÕ—'=}Õ±?|zÊÚ2q­âÜÖùÒ‰÷^}bÁwϽµ¢HÊ7N{`â̲Sn|ðÙGÆŸÑ"¬ò¦òæ<:áýͯyaòËO_Ôtù‹^\V ‰ˆ¸þýäÿ´A7L¼¾¿kÞ¤q7¼¾¹ÓeÜvNúª^œ»Ó]óÞX£Ó¢%oåš]®êïDÄ„)Úþ¥Ïßýææ®ã_òî[·÷Ýõñ#/-+TëüæÞEÏÜûÆ_M³'>þØ}cj¶;Y,¢Xx<v[‚±ëcQ qу#ä4§(ì©UG¶¤mdZ ##Ž`Ãég·%wá–_Þûhmâ©%¢$öÊ#¢–íËÏkÛ»gÒk7iLjˆ­Ã-OÝ?8IW‹mßÏÿ~u^AÄç3s;\ýæ5ƒS-"íÃ~û`î.׎3–Å éŠSÛ‡‰´¸ö–?/¿ÿóן&b;ö¦Gn”¨¸ZmýrÎÌïpa«0)Ôþï‹gþÙY~fzä±iz渳ž8éÂ1ŸuïÕ½{÷Þ§ô딡å/údAüoŒé“iIs\ÞëÛGç®Ïɯë›;?]d?ý©[FwŽQ³ògLŸ&""aÇŒýàÛ±œ|GEÛàÃ$ÃE¬©5ÀáA-™÷¹óEDl­GÜ6éÊ.±ŠˆVºq֛Ͻ÷Ã?{Åk)r5w©žOÇlažûlLˆT*JË 6ý[”Ø­}üÁs(®kó,YÇgUÎCD4=.M±~—+MD¬v«ˆˆ%":Üb±z&¬‘ÑvµÂ}ðp–øîW¾úÑå‹_úDz^ñÖ['ÞùÂM«rJ6¾uÅè÷<ï®òrµóþmu~sõ6i6ºU$§™WùAh[Bˆ0×çÙ¡5eQ½ÑËšüN ëqÃcW7ù÷‡^[Sh´)"RñŸó4»ê©iÃM¶ä|4þ†‡þž§ìWݪf±ÖqŸÚß8ô½fÞPói5¶yÏÁÍ{}õ¾Å\ùÈ”™£þçÛq7½~w÷èê²Û·¼^Ç77¿¦ŠÅʧLÆ„|S×鬜s:ô¾úkÇ P¾ûòí)ä_¿=£J"‚ë@6t2¬yP¬±™-[w~×}#£ç?ýÀgÿ•‰”lýkGt¯ì¡“ÃU=üYe‰ÉL·ïY›S¬UEMDÄžÖ.UÍ]™[îùféæ¿vZ3ŽInPVríýoGiõ?¬Dgµˆ—ŠRImŸ¦æ¬Þ™T-1:2½ŽoF$·Hto_½£ü°éGæiX?¾K¬a@ˆ–§À¡g'F£èg̓Ûù²G®Ýxík¼Ôâ…ë2Ú$ÍuÎ\¤¶q¯™õþû9®6‡û½ÈvÃzÚ'¼öÒ'ê Œ}}?mö~W7kúIçtÿ¥gÞiwË-+þþôù…Ö“ì§loÀ=*[ýÊu“å´s†Ð.#¼h§妟y{«¦i޾ï?ñÐI78NÈRv­ùuö"{ö„u|óž§¶˜òñ3ïd\ØÉ¶iÑŒÿu¥Vî¶ôÞךpË{/f·¥£ç¡m A©³Cx+$þ´à5Û»òÝ0î“r>Aá!àìÍFÜyßú'<öD«n»ãüO~ðèíJzÏ3‡ Ëø`íáCGïñw]ðĤwž\lÏèyÖî«¿ÐDÄš2ðÞ‡÷>ûÒä¿*RâÚ ¾îÑë{Ä* .¾¬ß´o>}êë½"QMzœ3áαí#,ÒïΧozcò'OÝ1µLÂR;ô=klJxbëCF“‹s IDAT¿ÙôìûnÜòð›/Oü*®ý ág4Ù²¤ò¦UU4•W˜£¡m h@5Sï%NQÄA."H„’é^?¿H\ú"{(†‡Ø~“fö;ðGKBŸ[ÞŸ}‹ˆˆ\òØ'—T}ÿÊÑ""’2þƒÕÇ*qà3ß‘°nW<ñþUqÍØªTqÜ良>èŸ k{à,)Ã_œ1¼ò‘ÝïþU­ûfMè<üêÎï>ô4‰l9ø¦'ßtðwëüf‹¡·½5ô¶ª?]Xùÿc.ûèÛËx1?*v[‚ÁÞ? þ‰@=‡èpNƇ(P>~:’¦€Ð`a óü ´-€ù²íæáð-ÖIX}>ƒt*!òQ¥ÓÁ á \š @C3*)ì¶Ü« à K ÿð›››Ÿ˜œ’j³Û9¦áª¨°åÛrss½ ž¶¥êÿz@Ë•VªzȺ9µhâßòšÊîžó×’ßW-Y¸ªÙ°.IµžŸK–,©þºgÏžÁ,à ýÙ¼çPhµŽB±3J1ê©´¼4=#Ó­ªªJ‹·iÛ BQÒ32sò¶{ù¯›¢mÉb²‹«ÜU9ª«Ìm °Õ(VÝ{þúuƒí¸ó[''X’û7i3ý“ß~Ë=vhó°ƒn(¸€Àaw#ÂÃáÏ %"2²¸¸ØûÛ¡Ž÷Ójôaõò ˜¥mIì éÑå[óKÔ,»EÜ…;ök±âj>÷´Š’ ­z°¬‘ ÑŠ»ÜÍp çE4qðŽ€èb·%ÅB#„Aãœiv[²%µk·ké•[vîØ¸lÁÊ‚¤­ã-âÎ[ôáäÉ_¬)Öl‰mšGìY¾pEîþ’âÝ–,Ùfoq\z/Pí(H~V5Á/ˆ… õžX‚xpMr‘8kJ÷¡ý‹æ,žùY™™Þñ´¡,"nÑ´ÊÖûð¦'ìoùqÉ×-R•È´ö'ìwLÏ Ÿ` ,f žòR¡³ÍœÉëß6MÛ’ˆØ“:ttxp¤Hí{ñ¸¾U?ÜqÀ¨Ž8gBÅâÙKƒO#„_Í<„Κ=R/®™v[Bí×hƒ¼@r_)§CÎÆþ®&"ÿu $d×5iœ—#ÊtWú»÷þùùËS¾_²iŸ5½ëÈ+Ç_أƆ•îÝ _¾ëñ“oyç±Ó”âå_øðÒŠ7sìÝï?Ü/ºhõ7o½>ã×u»ÝÉǹìú‹dh× V¾}σ3ä‚×&njãÐ;“´-8¢ÿÒ´ÐÜJwmK®mß=ôè ˈñ]Ÿœ¿`ÊóO>ðâ£ggZ=…ÿ<ü⟢ÔX:lëpå½·KL³(mÏ‚ï{oGÿkï¹®yÉ’©/?ûHDóç/j&"¢•¬w>þÜÜ"«Dr•µï±W¿Í„C8YtäÓE²…zœ~\Ì›SðCµÄø•E‡¸æ1vmûyֺġãÇœt\›cû_tÃùéëgÎÏ©­äßÏzE;oì›Tó^ÛâZ´ïбC‡Ž:tl×4Ö²wÉ7Ë-ý®¼rP§¶ízŸwÃ¥íwÎùz}™ˆHEî÷Ï>6«ÉÕ÷ ÷dè=í˜f·%ƒê˃>øTKö•Hxl„ç~Ù’Û·Ë[½½\*rf=7qv“ëïÓ)æ û¬•þvߘÑÃG÷Äg+÷«¢–î/ÖÂc"<ñÀÛ¢}BцMª{÷‚WšjóÈ5½“,÷ ÜFæ¡m ¨|_{{V'Ÿ úÂʘ„Ø1mЯó‡9g`²ðp0{úq­ì[g}þÛöRÍ]’·aÍÎrwYÉî¥oÞ7ÍrñWŸ˜xМAXówÜ3ñ™§ŸxüÃÓþ™zÿs?ïRÛ·‹ÙýóŒù›ŠUµl÷¦µ9Åîòâ‚ÕS~uçéÞ20Ó®øôù?^yƒ ˜§æ!€¸jU¬¦5xM0kˆaº¸Â´èoQú\sëéOLzêºïE$ºE[‹jmY´öï{¶¿8þü«~ë¹Ë®ßüì —·îÜ/EDDÚ¶m!_üüü5%N{‹cû¤n¾ø‘°¬Öñ%’«múeó¾õÞpÁ‡U70õšË·<ñæ-#8ü~pÝ–‚Y+ðÀ۪Nx¨]eÚÒNÿlßKví,TKgþ廉v™­†O|ã”rO-Sþßô{ŸÝ|Σw mn¯Q›*IIjnq…X“º]þø»çïÎÛëŽI±þ~÷•Sš5Ïp×ËÊTqoŸõøÄޏoT‡./¡w´-™¤„`]¬¡«ê½a·SŸ êj‹Wl1)Y1Rºî½ïrRú÷ÉŠŒ³7‹«ü»òÒ8›%"%+#!LÜ.Õjót^¹w¯ÙXÝ&³òª¼Ö¨¤Œ(qå̘¹6ºÇØ–QÑ‘QÑ•7à¶&„Yì YYIáŠ÷ç^¨\\Âw·˜pLlI¤ÿÄ4‚á¡ö¦µ²œå+v„G–oùõÓ)_W œ0ªUXÍ]9ð …‹Ÿ¼ÿ‡Œƒ»·۹胩[²FÞÜ>RQ\ù,ÛbÖ¶ÿñͻӶ÷¸ùî㣺¥êˆ~HÞ0mKãwÞ3ëb¤J ÿðpðš)ýwÆÓÿQ®Ä¶8ñìûïrôH°Ô6KD³®Í f|òäWEî°´ngÞõÀEm"÷¶^xp^Deu;ãÎgÇöK³)‡Ö³ áAïÁ£*?mK„‡ÃÔ› v8ü߇·»úãž/ÃZžù¿Î<¤Nìr÷Ç_Ý}ø‡ÜÌñêwÆü,P ±îF&=cR¾8øïpå#ÂÃážRkæG}=1ÌXy¹æ¶%™ <Ô.0©ãõ¼šîðúþÒ¶„Æ” šˆˆÃÉH€1Ê)Æ€ðРò’™Ó$v[‚>£…ƒ×àÈX>@·áA5®Qx&B'hѶŸÝ†‡¨¨¨Â˜˜†ÉRaaaTT”÷ùAh[àYŸm¬=#Ž\}2ßÀ˜,A¿iÛvl+,,ä`˜Iaaá¶Û2Ò2¼¹‘¤¤$F>¨8uÎÁ5bCáD `T˜NÓQhkP.%Ä¢á‚?óà)·æn---åx˜FDDDfzfRR’ÛínôжäßwÊ ÈT† n·;999##ƒ5Óf¢iZEE…Ëåòòvh[x…J“…q¹\ÞW™0&pXÌœ€¢úy9B‰…!€ny–„Å@ÕÅ+|yƒ\Úl§%,ÂÈ´-P'EaÞ„à&˜¤Âƒ÷˜ZÙ‰ðmKAx„¶%ÀdX(`Ù ÂBZèM@£Ñ¶Ã`Ö¯3 6†:ÏBÛR•õë×{¾hݺ5£s8EØ @Àp‘¸Z h9ÀQ±àþÍ©mKÐ/Ú–Àøà <:ÉBÛ€ £ÝÐ9&À¨˜‹@€Ñ¶}ÕB,ÿ qõœ|ph  Çç&‘ü ´-!µæÒ©‰3„+F‡ópãÂ;:ÌE#„èAát„t ¿Ÿ]LàáðÚ–£óLJÌ ™bB×y€ÞóƒÐ¶„#csw¥NáYz½mÌ<@¿˜p¥tÒÂôŒ¶%GÅ <òƒÐ¶d˜"®±}Ïì²wèX:‚󻨯é!ùO‡ 6Þ0&B¡Ò Φð>ò…Ò ƒÃ':?:   ЂiMÓväíØ¶}[qIq( kTdTfFfzjºRó›Á9OÛRõyºšíÕŸP)ÓaÖ§!ÏnÃ%%.a½…‡y;víÞÕùøÎÉIÉ¡0¬»vïZ½vµˆd¤e08Þ mKB…§àÎf D¶mßÖùøÎñqñååå¡0¬ñqñÚuXñ׊úÔÇÕƒ“““³aÆýCZÕGš¦ÕüºÖ_áûš¦yfE©ž ¨þÎáþx¸¿:ªV­Z¥§§×sp˜p`Ù|ÜnFŠÂGò¾Fª¸¤8)1©¬¬,D†UUդĤz¶!UÎúõë{÷îmî‘ùõ×_“““ë98´-™5|Áá4l_O˜#%YÑšeÄðàùdZ ±ÃVÏÏã«§æìYU?ÆzmKа LäྚPР[=8¡0Dõ?‚6áP±éË׿§¹¨O²•×ù txPDÑ4MUÕ Š( s‹ç1Ösp‚Ö¶Tò÷ë¼8bäE}’y‰0ÚÛ6“Ò€±ž³tpþU——]ƒ¿Î.%*ãZUrýÛ–<ƒãv»ý6D%K¹ø¹wú„nQ:õœà´-Eu;´ôÅ—>ë}ÃiÒ¢m•÷ÔÆ…Qt͘UˆSø¨@x¨‹×=9®üe_¼ñþ× Öì*“Ȭ.§žwÅ¥ÃÛF›ã×3ùࣶ%׿O®»ì½­ÍǾþæùMm""56ejàm/{ä¼Ç¬~pw÷HÜ3·Û]ÿ»´¶¥âSXõËîóz=[ó»fìwV·™MjìËÚà ñ±ãðᡞƒ´¶¥˜S^ýuÍÓµ&,ÑMbyÕ¬.L€Y¨ ÃÓÖï•Ò§Nž_qʽÏÝ:º—cë6à⻟º½KÎG¯ÎÝîrmÿꆡW~´±\Ó4M+^ñXö¹–išZºyöó·^2t؈A£¯¹úßûU­lý»޼åÛ|O [¸àγ/¯.Õ4ͽwÙÔ‡/;wÄ ageÿïÕ9¹eš×´æÁ‡*¶/ž½­ùWKÏ™ûËöŠê]œ´âõ߳ŵüõ7~©ÈÈŠ¦i Àd‹8èÿ†Ù±Èð ð (ž†þF+ݺð—üÄSGvŠ­^Z¬$xV°5s–ïq«jUßOU릪î½K&ÝóÖ¦Î×¾üö[¯ßzbþǾ´d¿[Õ49°:Yõü¨Zþßg'ÎŽ<ç×>z㱋Ó=óØW›Ë½¹¿ªÛí®ÿšÏà¸\.Õ*¶-ž—“5à¤Î'÷KÚú‹·—WŽŠZ²üÓ_ÂÞ8áö«:çñÔ óò+öþþò/ÿÙô‡_{ùÉñÇoy{Â3³ó\ª¦j®­Ÿ¼ú“ô8÷êóÝÙ#"¢×}o½?ý£.iaóòÎyc=')))HÏ‹²UOîwËϱ'ö²çæ[£J¿7è†9{yÁ„O_(ñy±BPƒԸŽA#?Mß½y¤¶Kµ×üfXÚ1)’÷o^¹&šÔ¾T‚;ñ´_âÎûßù½[¦¦´è=êÒžî¥ó7–jšh~´ò®•þûÅ×;O¸îêaÇf$g´|áYY[~ZºËåå}nèàx‚·\;ÏÍÍìß+-²iÿ>‰›çþ¶Óó8Äyâ­]3â”ÞýF»úeÅ÷ç.ýüW¿qãN?®eóö/½þÌè?>[”çÒD¬éç?ñÔ½WdìÛ21Ú®X¢cãâb£Ã->›©Ï°x–‚"Š—>?¹ðsç¼=!»m¤bË<óù¯ÍøáíåE¼b(ÁÒ ³ÛR寪ªª*5¾©‰VQ\áR5‘ÊùÏwESËrWå–ü÷Î5ç}à©ÏÝåj§—'eTÞUÕDÓ4×¾ÿÖîÚÿïı ­žkV”W¤æºÔÅ‹;ÜàÝ–|²›­{ço³·¦õïjQmÍNêÿͼÅ;‡•®ªš(žñ‘ðŒ¶ÉÚüÍ[ÖlWÓûd†{¾iK?.Cf¯ÝYÞU%,:\90FÕcë‹ i€Ý–\{·–5ÖìÀZ{Ò1i2§Då2á! ¼ Ö„¦‰²~m^Ù©ñaÕß,Ù±!_¢“#UÑÜUáAÑDÓT—f;vü‹·u­ÞœT±Ç„ç­©úQñü¨hªêvkÖ”‘÷=tn3{ÕZ£b­Þ¥µƒãu…îÞùû¼ ®œ ×gPõ­Ù¿ç «‰VùïˆgÙ²b±)R• <™I­Œ`ZÍo«Å­€ž AÛm)²íÀŒ%OLþýÔñšˆˆVü÷{Ï-MÙ62”^œŠ°î&>½ƒeºH6¾®§Y‡ó…χëÞì×cËèÝ;é‹ù3Wœc8Ï˾{×¢/—–D÷>>5ÌnÓJ JÜšfQ+*TÑ4kjÛTõÏ5{Â4©ªù4UsÛÂmR¶¿Ô­iŠhîr·&¢)1M›GîY“+q*/)¬išâÕþB yÐ4MQo·4rïú}î–”Á·ÞsF¦]D¤|ý´G_œ·4Hÿš»-i…›þÞѲuÓ¶–Y«rJ†¦F+"e¹+s%õ„d›¬«MD4QͳG’oÎÏäC}'h»-ÙÛ\õÊuŸœÚ;é™deOñ_]3ÿÛÚêιW¶¶óŠ¡ƒªOq©ös8}·—ׯŒRšBx¨ÚP¨ñ7ÖêÜ+Núéé§ïRÆ\tÚ±ÉêÎ?¾ÿèÃ?Ë›Œ:³C„H‹ö‰{ç<³ã{–~=ma±ÚK³$÷9û„©“žx.ñšszf*»×ý>ÿ7û9wlÛ:|Ú7Óg5铳xÆÔÕî˜æ¢…·>khÆ-ï<ùš\8¨mLIÎ_?Îù¯ç-7õküKF#v[ò><¸w-›½)îÄ«»µi^9?“:¤ëÌ_²«_–hÅ~šý›­MÔÞ埿µ2iгíRÓÏêc}ì×ÿoü¨ö¶³Þø¶èøz'[*ª¢ƒ¦‰ˆ-¡Y|ɬ¹‹þ‰ÉÔlMÛ·ŒñjŒç1ê}·%Qâû>¼hËÙ_úÃò-Ea™];Îê“îÍ-ºöüóóœÅkòJ•¨ŒŽýNë×&îÐq¬Øóï’EKWoÙ]ªÆ?êü“Óm¼BA¿)މ)}"”0GxE¼¾š’Ð{Üs÷4y÷“™ÏÜÿA…ˆˆ$ö¾êžÑ-íš&-ιñœÏO›ôHxóþçŒê±õSM‰?áæ‡¯òög/Üç,“°”v½‡]dl~Ù¸!¿1åñqíŸs~ëfЦiöVçÞ÷€í)?ñ}*1Mº|Vó¯®j§ªjý¯óP=8^^ c×òycº_ÕÔ^};‘­Oéñäìå{.E©Øôý«7hqmýï^Gë0‘.Wƒ´¶%±§õ5¾Ç(ŸÜ–+É·ó7&œ8lT¦{Ëâ9sfÅ¥ŽêP|P÷­üöóß\mz2<5Rܶ8ö…tS‹Óhè–o§†èÓQxñìFêÝmX’ºœ}k—³E+üå¡k_‰»íÅ›»ÆT.ëîtþ}oŸ_õƒ#GˆˆªjaMû_ý@ÿ«ªÑ$±÷eOõ¾¬êϧöTúJ|—sn™tNíúß›ÇÛˆÁñrˆ’O{äÓÓ¾™ÈÎwLùHDä´w>?4ÞHx‹A×>3èÚêojªª¤ á£á5n%®ûØG?ë‹A©º…zNÐÚ–D¤bû“~ιàïÍûÛvvÕ„ —t‹ol=ïÚ³f]Ar3;7³HÊIÖ8W­ßß©{BÛ+Ý´hIA‡áçõË 7*Ô9´`vä ‡ðàýnK ?¦w˲i3¿ýÝÞ^Í/iѧwºî*®Fì¶äÓ!ҩꇩëÝ–´‚…·õ;íý´Kn¾êÞcbËs–~þÀ©ƒr~úéÞÎ[2]±wg‘=1%Ò""b͈WþܶÏ% Öþ—ï\½ÕÑbùïnÞUfKhѵÿ©Ý³"¨ ø NtDq»ÝšÏf‚,©§Ž»výóo?ûH©’:ä^½Òt×îv»ë¿æ¡zp4³O–yº¡ê98Ak[*ZòŠ3âþŸçݼg·Ö±W^Òû¬Á/,¹ùí“£“˜*Š+4k¸­ò+ö›º¿Ô¥IXÕ¨Ey{+Ü[“Þ§÷ˆªÈ]>Ñ7sâÆ k}ð -Y²¤úëž={÷4p:ÄáäåÂà (o×<Ôºãé®}|ÀµJR½lC·jmĥ匛ê98Ák[RÝöŒvéVH[;tˆþÚ›ë<(–S-žÍpú÷\¥.%þønÇ·J°ˆ¤öï»é½¹«w”µ=&â 3t`8\ŠiÀ–°N:Q ˜ô+Ó¼€þÂCdddqIqxX¸ËPE).)ŽŒŒlÐà4kÖlùòå -Ä«‡´Î¹‹š[çOzvyòÜçê;_ó뚪Ö×uþØ‘5oÞ\Dê?8o[r—–¸µŽ×ŒÊwÃk-']vBf„Z¸qö3wÍérÛÝÑ»M‹=Ê.®r—V•ÊÜÖð[1³XíZEI…ç'[tL˜º§Ô¥‰ð–†@ä7˜_6™+¤<õ [å†nxÈÊÌÊÉÍIˆOˆ…a--+Ý»oo“¬& œ”””””§ZÚ–öÍ–pöüÊ?Œ?qÚøe_ôÌÐñ¹U{BztùÖü5ËnwáŽýZl‡¸šÏ=KLZ’í÷œmEjjœE´òýûÊÃâãÂyŤ2 $ÃCbB¢ªªÛ¶o+++ …a ÏÌÈLLH¬Ï¶B Îá¡m)vðÇ××Ùžd‰ÊˆmìÓ,©]»¸?—.\™Ø#õiñÊ‚¤­ã-âÎ[ôñ§D{vû¨Ì.bfü:wỈ-¬¹KíŒíxR:û.@Àx6:s*â ?ÐAxp»Ý©)©™™KHì^¯ªªË媨¨`p¼œ@·-Y¢2Z#""åùkÿXµµ \­z±¥Ä5͈jÜ!²¦tÚ¿hÎ♟•)‘éOÚ9Á"âMñܾ=³ÏˆS]sýðùR%*ãøÁgöN#;€ "RQQQÏz118u ÚnKêž¹7ö>í•Åj³[ªz‡âÎør×gÄ5ò&íI::<8R¤ö½x\ßêŸHìpê¹N5ÛA¤qÈ'ƒÆJqÞt®µ˜W±…~y–<)" ÿpÑo/ÏH›ôw‘êª(¯–߸䀣WúSŸÍdNöœPoÕ‹€Âà¿ü ¿Hœ%2)³c÷æQ¦]°ìŸÍ+œŽ†Í30)ÓªÕ4_ŸxÌVH&}aÌÇÆ@·‚Ö¶Ýë&Çc×ßþÆ„+NÌŠ²V¾ŸXbš·kCܘ2;1%ÂŒ.h‰ÓÊ÷åç.xíš!¯Õüî€{çÏa@¯„$ðmK…¿¿8Õ>aÞ–ÛûfD˜jP¬<]øýK82®›†`£ úèuÒb‚%<­Çà^McÂl5Xyµ†>5zõËN ,ÑOExê)h»-E÷¼fÀŠûùlùæ]û «”¸9$Ô£=ç |ÛRÁ§§ü:ÿ‰s»·HIˆ‹­2læ>‚‰maqpRg6@ÐÄ ý ÚnK1ý__¶~’Z+hGeÄrLÝ*&»àý,2©Ùœ”U ,¥áF´Ý–*rütÚ¿e3¼Íy7œ×&œÃb6¦)==e´Ž¡W ³ß%ÂÜü o[rå-ýý¢"ÑÜ%ùë–®)ë>æ*„,áç([„Ê0}È ì Ex/x‰;ñ©ï®ñçòïœsÆw-3˜vÔ³tLŠÓЯ í¶T[XËcRzoy!Ç$ä9(ô}|XS’@x@ˆç |Û’¨å¥”ìÏýõÃwVڲ♨H@xô)hû¾>=ò€¨ø&'ÞºêÔïèÅ1¡nàðB`¥¥B¿‚¶ÛRì ×­)ªZ­XÂã3›¦E[9"k—t¾ IDAT‚fºH6£€ð-?HàÛ–,ÑMÚ´cðýÄ<;Š@xô#»-•®zcâ«Jëú«ˆŽWO¼ºc‡~äÔD„Í qL> <G„¶%µ(çßµk‹úž{ïÊY‹¶JÄ™Cï!<ºÀÚÂÂPw~@¶-EõzðÓoküÙµcÞ“cÏûÎÚaìÓ_:=ŽrX´!@Ð4n‘.×qC£°Ûô+È—w(ÛüõÚ|4ÿ‚ék–½{y§X^b`È3B‚w‘8­xÍÇãz·ù¦ý¦ïÖ-zÁÑ:’à@x€îóƒx·%uÿò×/êÒqÌW-]°nöÃC›Ø9 @@xV2èqÊhºÈtŽÐ(tF™k _AØm©|íä󌟱£Ã%/½ye7Y½xaõ_Y;öê˜ÈÅáС ì¶T²úÓ¯·‰Èê÷oþþÁeøå®9#ã9,€*¬96EMc0~@¶-Åœãâ•%ÐXÙ €Q°æúäÝ–@x€Qo·%Ô¶%è=?H€w[Ò±õë×{¾hݺ5£¡g4b…Ó_8™Í¦ AØmIßÈ µK™ljPþá‘ ì¶t@yþÚ?Vm-(W«ÖOÛRºžÔ5…g <ºÍø¶%uÏÜ{ŸöÊ¿bµÙ-U{ðÅñå†/ψ㘄6E¶ãÂQOa¢á¸ µ-ýöòŒ´IÿySÇ(¶ïÀä<×à’õÃnKЯ í¶d‰LÊìØ½9É€ðCå |ÛRt¯›[î»ýÿ[²òïUUVo.TM2ªNMœôÞˆ8œŒ‚az~€3 Þ¡m ú´¶%­|_~î‚×®òZÍwÞYñ¡2øl6 ÅÌô+hmK…¿¿8Õ>aÞ–‚²ŠfŒç˜ ÈNqð‘PK6C3Ð{~À·-)–ð´ƒ{5 ãp„wL…{j˜y€~z¡ZtÏk¬¸ï‘Ï–oÞµ¯ °J‰›C_c@x|#hmK Ÿžòëü'ÎíÞ"%!.¶Ê°™û8& )‹YŽÌ [ûf ‘h[‚Þóƒ¾m)¦ÿëËÖOªµ·’%*#–  ¤1óý ZÛ’%*£iôŸ>pñ>];uísú%|¶9¦iFO—zãÓh@@­mIJþxè´aOmî6îéw>|û©kºl~êŒ!­(å™è mKÐ{~À·-/{å=÷m³¿¤k¤ˆˆ8ΜÔmäËËn{£oÇ„.f _Ak[ríÝZÞ¢wËÈêoD´èÕ¢|ëÇP… ] ZÛRDë“S—¾0uMUŸRéš_\’Ò ÌÉLkTXo£àW´-AïùAß¶ÖîÚIcÞÜ¡Éäþ}ZEÿ÷ëü¿’¯ÿáÚö\30Ÿé ` IIIŽ •,I§=¿lýYÓ¦Íú3§$¼Ï¨ ç_0ð˜(.ç(>Ñá¨?OÛRõû+Q­^v×@Žá†Éȶ¥²§½4íß²ºþ*¼Íy7œ×&œc‘M[@x@( „ƒ+oéìï‰{ïªEíNï:°w«Ø²­Ëæþ¾%kèƒCÇsL Cà=M…~äÆ`·%èWv[Š>ñ©ïþyÞÔ«š$úà¿ÍËç|õùŒ~ÛðßôóÃKS“X0}v¶AEĶêAÈf  ×ü A¸HÜÊOW´¾ìÌUaÁÖdȘcV½ª˜€.í"qaÇF.Ÿòí–ŠÊ?—®öÊÒøÍYðXLk^&ÂŒ+x‰ëò¿§NÿmL˦ûvúɳÚßrÑË7GxÓ ù hLCïùAß¶$Ö¬ÑSVo¸üóÏçü™S5ø²§²ÏîÓ$‚Ï"À+Ó)ÜÃcæú´¶%­dóây¿®Ûí²…[ÊvüñåKïž8eu)ÇÀQ_>¸ª uºèŒåÉ¥Ì<@¿‚v‘8u猋ºŽš›yR¯±¶ªù†¨ðµñ7éÚóÏÏs¯É+U¢2:ö;­_›¸ºƒ»Z´~Þg³ÖÆ {N{.iÁú  Õ3U80q~À·--ÿ`iŸV}3&ÓGSs®ü%ßÎߘpâ°Q™î-‹çÌ™—:ªKü!7®•n]<óÇ-nŽ;Ð'Ú– _Ak[RÂã’š4óٳõgͺ‚ä'wnžžÞªÛIbòW­ßÈ4FEÞòïfom1h`ë¨Ð{^òÁ³Ç“Y+‹"ÂÐPAÛm)ºÇ5ƒW=ððçnݽ¿ °JI£g*öî,²'¦DZDD¬±ñJÁ¶}®ƒ~ĽwÕ¬oÿIè?¢w ³ëÁé` „±¡*á8\~À·-íÿéÑ×Írt×fÉñq±U†ÍÜ׸›S+Š+4kxÕê ÅaSËJ]:NµÒÍ?ó»¥÷ˆSZEñ”„/»¡™©$0+Ö<@¿½NºZì©oý¹¾¸V_‘%*#¶ñ7©X|H¢ªµ*;µx{Îþâýó§¾1¿ê[sß›ºûÜóOLµÖü¹%K–TݳgOÎ@x*m·%KTzŠë»©3–m/qk"¢¹Ë r7Z/}ó©“bssö(»¸Ê«¦TW™Ûa;0ãjï|æùm+»¢\»–Ìü¿Ý]Îzl’µÖ êD „ 2?HàÛ–ÔŸ]ØÓñcV÷´ÍË÷¶;!+ÿ÷?wu÷FfX#oÏž]¾5¿DͲ[Ä]¸c¿Û!®æsÏ—Qùµ«"Âb±G'ÄEZ9þ€àÑ4–àP4XC¿‚¶ÛRÑŠi‹[=ýûÊ\Þ¡Óm_,Ûøï;CÜ®&)öÆfô¤víâv-]¸rËΗ-XYÔ¡u¼EÜy‹>œ<ù‹5Åì· À¦30oŒáÚ„ਂ¶Û’VVdmÚ1ÝÞ¬{ÒæŸ7–Ú[Ž×úç)Ë‹{ƒÖ”îCû·(\:ó³ÏøÛÝî´¡,•¯†Â«!àm zÏø¶¥ðæÝV}±h×ГÚw¨xáÇ-å½c¶ïܳ»Ä‹+LÛ“:ttxp¤Hí{ñ¸¾‡<'ÓN{ G‚„Fàˆ˜y€~­m)üØkîéþåˆSû¯Ãegî½§GÛv.[rü¹¢Í6ž¥Æìª õÄÌô+h»-‰­ÙEÎ '¯+HOÊ|fáÜ/Ü׿œËÏnÊ f@'²ë×ߟ­ïeÙ¬R€. èÀϽ°2›ðø0?HàÛ–DD,1-ÚLjˆ4péíD+úïïǧÛ9&ú+¿ø¨€q+u¡Q CÛô+(mKZéß?wãEÙ—ÜòÒüm""âÞóë ÙÇu¹iq1Ç!N¶Ž—ßòk6ƒkÀ£€` ÂnKÚžÙ×ö>ãÖÏ×ïûï«[OíwÇ¢ýû~vD»>7Ïk}óÝ}b8& ¤Ñ¶½ç dÛRñ_S¿—ó¿Y1uXRÑ/7uyÏm,}sÝ I‹ß¾á„D–<h0ÏìkŘÆÄÌô+mK®½9e-uIPDbŽz\áüw nþiå§7“€ºÐà 6®«@x‚%‰S+T‹Í”°¨è¦×¼r×ɉ ª8b [¿¼ƒˆˆ¸vþúõ§Q Š”®ø¯p·å»Ïœ+l""öÌ“†Ÿ”ÉV­ÔWo <Œ‹ÄY#”“¯>orÕŸ¿eÌ4ÏWÑC¿ÎùnD|ȇ³òBÔA}$ `°ü l[Šñ]aè5Pr5p ×{n`ç¤Îù!D±æú”‹ÄüŽœÈŠœûÂBD0v[`j¬c¦D>êéA°! àˆh[‚Þóƒq·%FŒÔ3¾E1M• ÔÀÌô‹ ÂP/´-׿0™MM0&I@x@ˆç¡m‰Â—ÂÄ0ÂpdL8Ðeœ¥¤@xô‡¶%ÂЀü ´-€Þ8´ÆwÑJ`”/íØ¯!ŒQ@ Aˆ¡m zÏBÛR•õë×{¾hݺ5£áMPq²êÁ@x€É°Nº2.Ú– _´-B =B /áð2?mKh ®ƒæËÁtúàp„Ê¡; v'<AÄ„(ÄPƒ£Cxê…¶%•°¢ ‹†áæËBÛÜ´É„‰—øì€‰°Ûô‹Ý–((ÊIUfôI4©÷hâä¨/f _´-H!‡É:ÂàM~Ú–ôV2*lg„€&œoì³ dжý¢m‰šòœª5Ô’2s:»Þ¹Ž¦,&ÀÌôŒ¶%HÐf ÷ü ´-‰‰r ™è@À˜iõ³ÃÉbnC•eÙÜ1Î_ m‰‚ŒJ0pë Z@ÀóƒÐ¶@xŽŒ xÏø=n¾:ÿ}džíÿÏã³ëúŸ¦ïlæùÓ‘÷)fcÂH´-ŒêÇ…‹ŠáhP~Ú–¢èPHΫ _L8ÀÏ5¸¹^ôëÝÃs¸ÏòÉ$ Œ¶%µÞ­×ïR$yó©õ™7K @x„¶%Ô¬ðÉÎfÂÌŽ Ê2@xê…¶%À÷Ø9¡€©òE•ðè*?mK `„˜ °Môƒ¹j™ª c®€¿Ñ¶d˜¢çíŒød œÙ:?9Eõ}C!ý’ðAx€iòƒÐ¶ðm8@x€ù0áÁljb›ø”!€nyÚ–ªÿkèÇâÚóÏÏs¯É+U¢2:ö;­_›¸ƒ‚»V²uéÏ¿®Ùš¿¿L KlÕí”ݲ"½Üy.²êšHOG‡"NГśéŒ|‘DDQ cæzÏb‚¶%Wþ’oçoŒì6lÔ¨!ÇYÖÌ™µrŸzШÅ;v¸ÒŽ?匑ñõ×ï~ÞRÆÑà¯Â8a”›5f _&˜p¨Ì{Ö¬+HîqfçæqI9©Óçªõû;uO8Ý­É=†«ü:#r÷ºO×縛‡[9 À(E*Ÿy#40óý2ÍnK{wÙS"-""ÖØŒx¥`Û>×á~Zu•º$,*Œw!ÐE*Ç„(?ˆáÛ–ÔŠâ Ín«LŠ=¦–•ºê~ÉÒŠ6ý±¡<­C«žœæpîªzDÊÑ2Ðð‹z‹J!¶%è—iÚ–DD±˜ÏVÕþø÷þ=gþÖ¤>çWGvX²dIõ×={öä  ÙúÖLƒÈGx|È,»-YìQvq•WM5¨®2·5<ÂvȧÂjáúù_/*:öŒs:'Ö¹ÚÁ0!›—cƒÐÁ6Jº+#œ (+qðÑ×4´TÖ0Ðy~ãï¶dOH.ß•_¢Šˆ¸ wì×b3âjwµhÃO_ÎËkyúÙ}›Fð>¯¸,@ã˜æ"q¶¤víâv-]¸rËΗ-XYÔ¡u¼EÜy‹>œ<ù‹5Åš”ç.ø|ÖÆ„žýÚGæçååååí*¨©÷º¨0DUÃ@·Ìs‘8kJ÷¡ý‹æ,žùY™™Þñ´¡,"nÑ4MD4WA~¡VZ¸hææª_±·>sì¦vÎÀ ´!ᡖĉ±'uèè8ðàH‘Ú÷âq}=_·5®=‡Ûßü´¹çfC1‹l¿fO߈‡%[÷'+mÂàfÚm ¥6 ÷4Ôk _¦¹Hjáò¬ðW1Ç©eÐMáÂà«ü ¦h[˨?˜4ƒð†Ôˆ>ë*y7;áM'W‰fvÅx¥LHU3ÆÍ™ÙoKv w`V´-…‡èâýÌá¤î„æÍ™$`úü ´-…h˜Ðh©-›ûÊzSTÕTð <þÀ„ƒ™£ð›ò°Öœ>Ê6fº0VjuøsE5éþ?„(Âà[´-™«®äMà™ ë<@ïùAh[2M‘ÃT#[d:£P×°èÿîqà(ý¡{Ì<@¿˜p€#"ÁßЉÅ¨àý7ª ,áEÛ#…: <AÏBÛªž· äjc­ÃÎfv áÄ„p ø¦Ñ@>å‚ýŒ£y„ ¡h[‚ÿ qjqÝ– ÷ü ´-…v•/""4³‡*M…£¯oÙý‡4¿Ý8Ÿ'„]RR±¡“œõÉH†íDw(âô®8c«_ <Gâi[ªþ/èjZ €ú e*„è=?mK0S$£ 5ëŒÐiÊâÊ}T¨ßfÁ‚iè뤃PÚ:9áàÃiŠ#ßæC¨“„ ì¶)Dú¥^ç"né L †Yж½ç¡mÉ(§xÛƒB-=š¦÷‡éÐDQè3!.Vþ­SiÀíúÃÙ:~¤Ù~â‡jvbÛ4¬“öûÛ(/æ¦{¬<¦|ÄiÄÚ½ÎÝAÕ@_h[‚~ѶdøŠÈŸ×bcñWÖX¶¯ïIý?ðvhýCµþ„è??mKzS«1\mQã3÷ȼg†ü°¶ªö:_yX6èmKÐ/Ú–Ì̯‹ NqIÁÁÃÉ)À„˜y€~Ѷˆ§Aý þ(°?ÕîæÃ\ô|¶352˜y€ÞóƒÐ¶džÙ†`làÃ¥Ù‚VL4v±¯Ïº§ê¼6YvK.Ê)pbpÔ@`ж„Ö•4„ýøÀûêK;â+€—ó3õ¼6E$€yÚ–ªÿË€¬_¿ÞóEëÖ­ýV˜‡p]Ø­è±VÛLKSLÙ¹á8b~Ú–ªø13˜ ¦Òq…ëpŠÓ¡ƒ¢³¡][»¢Å!W9hÐÑt8ëýè~¸6ëüDc‚Ð)LC¿X'Äb·öçâf­ü×{@MiˆÃdþ'sð¶|å)€c·%й@?¨Ã?.‡¸F#.¶p$ÙGl03Ç~/:|!õüEÏ) <Þç¡mIŸUü–D=wm„àçÊ:yÈTØÏ£ bÍô‹uÒf.¤ÌñÂ]ÏAðG«½NÞ}½ëJ÷ÁK{rèÈ’˜y€~Ѷ¸Bˆ*¨žµ2V@hcæzÏBÛRÈE-TþQS=©×MA^ÔA-ð¤ã163Ð/&x)ÄÑ«pÎ9Ds:¡{Ì<«‰—Wcy^õ²ð|93Ð/.¤ÊÎѶä?[©Ì¥Lw&°Û’nÎò!9hLCïùAh[ ü:ȆçFÜaÎ%„˜»-é‚Cã³Iø\ãw[H¹@PѶý¢m >–íynì;7ÐÐàÍyeÖ½%+€êü ´-ñNÌû:@‰ ªsÆGh[‚~Ѷd—Wó¾Âúðjðã{3½A%¼™3Ð/Ú–àM)ÏÃÔõ=7ÖnK:ÿ¾Î»g‚:Éó¸œÜfâ’qá&ÈBÛLPµ;´€Ö@0Péž)€¡Ð¶ý¢mIïëÞ×a!¶}½LµFÃ3 ¾ßm‰žf _´-)T¸ w˜ç²q¤ Àš'h=Þ!h1Û[~è¦a{€ðx›$Ú–´òâ Íf÷|Š®Ø#íR^T®rü)LëWÑq+$ÝV“”¹<1jBk·%E©n"ÔÔºßK'Ož\ë;ãÆã<Ñ[]~Ø_il9Ë6©äŽPߨI%< ”…NÛ’eW™Ûó’§¹Ê\JXBØ!+’ˆ 0OyÉnK`L,˜†Þóƒ„@Û’%*%É^œ·§\DD\û·(qé±VŽ? <õB»-ÙÓŽ;&lëâEÿääm[÷Û‚u®¬ãšEq˜»-áð1OÃRh¤ˆ°¬¾gœ˜ºcáWŸÎ˜»!¢ËÓÚÆ€Þ°æzω%<½ëétåcæúBmK„À¡Ô¶„v[ÂÃÿ³w×qQ¤ÀŸÙdwéîAJA‘P±±[õ<Ï®³»»»õì³°ÏÆŽ³[,º{sæ÷Ç¢"?Pðù¼_¾vò)ñ ôÔf!ò?ûüöÝgQñÙ2¶i£íÕ)"O ¿qéΫ$1%4®á×ÄÏ^ó›øÎc]’zãÚƒ×Éb†£iîâÛ¨®:‹ òz¤'<>}â•eç@/]v~#”¾‡¡³ÞܺtóE\Í×wðnÒ †.·’Æ3 º0áPîCMî›Ëçe[ú·ïÜÎÏ8ùîÙ›1D¥|‡Ir\®VuŸ€vm›{è$?¹™Ã"¹uöV¢¾wÛÎÙä= ¹ü:—AsTYÒã³^I ‡6DþgœÂ~ü÷èéç³:ÍÚwh×²Ž Ÿ"DžüàÌÕhA­V:5wf½ºtþyýæ€ï$ùpýÔ­dÓ†ÝûöëÝÖ™~q>äE&ÈWEÒí}›w½—$/º2ô0tFèù‹á´c³NÛÔÖxíìƒ$ye’P]([*ï1^óâ#Ë®^]SC3'_sYThNšÊ·KըѢm#êVfævµêyêÑIÒåDš%5óö«anhRÍ«^5öÇй4š£Ü÷ô°ógµ¸håmˆüOˆzÚó_kÔïàåhejbfaªÅ%Džö*2Kϳ¾›¥‘‘M­z®êÉao2éb›ç°ßø¬˜x‰fµVÚ|žÈØÉ͈¤Åe*ù Ã6ðé1dPO?½"gÎeèa謷áIBçúž¶F†.~u ²"^¦TÖìɨzþ@P¶T~cMB£n¤©¬Väj‰ä©‰¹4SQÉš\"'›(”/oh†As”ó.Iù˜š—tçÐÖ;‹îíߕ֦…‘¯èC]*¥9&îÕ-¸„éú'G~ó&ÃQ@>E˜º0òÅ7|WÆœôôßpNöÝͳ#ŸÜvë¾µQ=‚ÈÿôAµ´=L‘…•¼<€ªçeKåÖÇñ„<’!‘+/±22‰Œâ ¹¸ÚZþg²âwN^I0oÚÑˈ§ <—È% åPÁÈ%rЧÍc£9ÊñØÚ™äßÇHg†=mݪU-#V$"_ÁX<.¥K„p !l5-5"Í“³õ¸D.-¸±”–Kl¾—_ÜÿNâw§·p3Õá¯Kƒ=çn…»·Ó@äê Zê†Ëph‰œ.l9År+é#¯pì€êÂ}Ò匭a¤ÅÊŠÏTVºJÓs8:"tå9ÄÜ=yî~ÃíÔ•Áe õu¹¹IiRB!òÌø,JÓHƒ‡æ(ϱŒ¯¡­S@KÈa±ZÚ"ž‘¯hMcM&õ]²2ÆŠœ”J]W¤¦m$’¦$çÑ„¢ÈNÈd4Œ5yÅ~؈á÷õ4 ‰äÓ] WCK(¤ "ÿ3û2ôí\"·šÐâ”d‰š¾.É@9ÃÓ–Êû"‰š™³ýææ½È¸¤ØðÛ·?p¬ùˆKyR¤<8q*”ãVÏU[œ’”””””œ!a¸†Î¶¼wn‡Ç$ÅEÞû7Rnêl!d¡9*"_ñgšî¦òˆ«7^|LŒ}uûÚK¹EM{ ž®ƒƒfÊÛÏ?$&D?ú÷y–®£«ØæÀ· |g.0µ7¿½u÷uJn^VÜ‹{áÙÚÕìµøˆ|ækR‰D*§F!•J¤ ¦L= Kæ†Aî‹E'$~x~ó~²¨š£ne-ÿa4üíǷ⯗goo Ê]^^^áß%xýÆ¥†K +„††ºººb´áêXОܹ÷$|ö&SË¥Qó:¦jˆ|ÅõîI·öï;ÿ$FL¤ñ/Ÿ<~%±r±qKßÃP| ܨ‡·ï?‹HdÛøøÙªWììÏ‹ðv6vß§Y!?¾ÓRpdAù*ý}Òç/ŸìX  BHà¹RRâ]×ÁG‚›7nþ­Ÿ†„„ÌÐû¾×EÙ¨.”-¨$ êùÁÓ–<” HJeKHÊ?”- y(&<” Ê–<”! ([@òP2L8 y(”- y(Cþ@P¶€ä d˜p@òP*([€ÊÉýð*Q¦ @òU  ([€ÊEþn}-®Íä§bBd¯W7tt=뇶Wd#Y»è÷¿™ƒ À„ƒ€ÊÒÕÕEÚ•—œ’G—ßFnãvmå:ª!²ðĄ́.”-@å–~:¨éü(ñ juf?Iô‘qMmEE©Yø ~#&„ŽÝî%rÜÛ]H©¹O}”“ùhÃ>êlŠ¢8ž}¶½Ìû|#iÏ–ýÞãK1!ÒwÇ&Ø«SÅ1ðè¹êNMð·zácÚ»Y›êkhÛ·žw#•F3’¨JùAÙTRÚ­÷_œj«Väý™nôƒ̓‚gÞK—f‡¯¯yáÓäBHnØqjÚÓøˆccíÃftû¸yð{ £Èx8M½)·R%yïvx?½y÷¿?È !$'ühúg"b“>žn6kð–HÜ*H jÀ„ü:roÞùÞkþÒßœµ¸"›v³6Œÿ{óã\BÇ´Û°6ÕŒ,muÕ]&_~2ÙG[‘“)4Õ$%[ô”ºk÷;ŸE+ú¸éðÔÌM\7\ÿ꺳q B×¼û°s¡´Ü[ÕÑL O"ö€äª”-À¯Cšü&YzówS6EQEév8›ñ6QBáh›i+ï@¤ä ——tvPçjÚÖë9ëHDÃ_u$O‰ÎP·µÒÈĹFކTêû49!„-Ò±•‹Ù\C3 bH  åeKP‰QùÿåêXèj´=™Ê()²ÞE„h¥­\G¹’4lE×Ág\6¼Ì§D?:=¿‘‹¦èF pt­4³£Þgç§Òø°xF×RA$P…aÂ*}êÀð)/#>¦sk Ò=?vꡈ,…<íÁÚήn=Æ*Š®¬ÈKËaºúZ·¯kÜìº:<5«Àãæ³B÷µÂÄT<ÊiVÈoeºCJ@@¢ ÿ•ó—Ïv,a…!Pðk`J¼£)øHpóÆÍ¿õÓ¹zß÷º˜yÕ… •‚äTÊ–<”! xÚ’€’aÂÉ@© l É@ò‚²%$%Ä’€RAÙ’€2äeKHJ† $¥‚²%$eÈÊ–<” *‰Î¼¿¤¥O»×Sh£ÜHßììÓ`оw2$ßeKª(/lËü¼ßOn G²ÃöÎÒ±eCo_¯6f}•ÃYS‘tqv;ï'•YvgËè¶þ¾ÞM:MÜó,뛩ñ`y ¯Gàη2Bˆøù’6Þ¾Ÿþô ŽQœ;㽋.ôr1³„fÄÑ'fýÞª¶·oÖƒ]Œýò N½³mjïÎ-½¼}=ü;ZòNRø£¼g7õu#»pmYÜå•}Û4ððökÔ{ÎÑ7y aÒ/ þl'½}=w¾•¤ÝÝ6µwçu¼}=šö»ëI­Ü›·'— nQÏ×ûAÀ€%§Þ‰³ë>Žß»u‹ÎÄ*T·ñ98þAÅó‚²%B'\XDÜlY/G!òŒˆ'™vFüVÃŽ>¿qÉ’©º.ûþtà)3€{ëFͽ+#Tþ/¦\š7vgbÛÚ¼?4oÉè•ǧ{iP_æç¾Ü1núél6Qÿ´²þcå亚!„°„f,"#„âºY6ÔE@!„¥a#úö^K"·Œ[tÅjØêm5ew6L›9ͼú–^E΄eÉOÃéšÝÆ®¦-?±l휩®»ºš²Ò/í0í®˜–WáºòÇ&L?ÁþmÎÎÂÐ]³æOØî´o˜ƒ×Ä][² Nû¥oöM]§Á£Sž=—8w7ØA=õή…',t8²Ð‡ûbÃèYÿzÎݵÈ_/éüâ3Æh9ìäÀÓk»©¯Gã®C×ßHç/dò®kìëáÐeâÎé%ÜÁä¼OØ8è² !„kìfÉŽ}úQRìªò¬èëÁGã­Ú¶±U+~kÒ˜gqÄÔÅ„G!l-»êši/¢²‹¼¼$âছÚ]†Ö×eŠ*Ìâ 1%Ò±Ù:.5õSC¶xš.'²„ç¯döõÔ)B¡4Ü[:ÓO®D‰UôĄ̀zþ@P¶ 2d áñ\;O³¢¹tjPÀ¼DÃkôúÑ>Z$ýöòáÛØC7Mh¤Ï–$®•“œËðÕùÊsi¶PK@r’s„p SÐM£ÅuÚ°ª9WñîÓö¹f­&®l¨©§N’Y¶vÒhíý»zZòm»/XÖ]OW ½½{Ù–?gÿ³ª…añÆéÜÔ†¯¡–ÏhñåÓ%4Qÿ|}YôŽÀ ¿ÞÃfÓÖ®!øV ²ÒÅuAþi4[¨­FrRrD_¹1:ùúúƒ©>SºVû,Á¢“þÝ{1ÛqH#6á¸_96òåƒZí¶±eçZNÞØÕ*s”Ȧ–QÎÕèlº¦@¯ò#yÕ…²%U#ÏI«›èp‹.céúÏÚoûææÎEƒÆ°w-÷zþ(&9fnÿ¹kÌlµy,!›Ã*¸BA3ŸošÉЏ•örCP£ ‹¶tjóvóñYu\êû+Ô°¾º3$ä~rwKCÏÆ†Ê…NvÌã€YgB³[4ÖüæžS,vá$-ÿòÅóÏŒÍ;®ÜíùñåÅõËúOâîZ`ÈþæÆ Ïì ¦èÆ$¯‚7ßÖé¼§^Ñsé»cÓ<¨6t[3!²÷'WïLï°ú@KéµàÍ[CV¯»è1§¥)‡BØZ¦Ú¬Üäl1@òPʲ¥Â¿Õðe1>[캫YõêZçú¾œØ¾Ûê#Í$ÊjéëCfDõÚ°°£mÖ6!yŸ%¡ a¢gŠ)‘ž°ÈÉ9K¯ÙÂý5ÅÊòEÌ?G]­·jÅo®ü¢/ÅÕ6Rg…gä)·R°?jzúæC–”þFM>K¨+"’,I~i‘"/KÊÕÔæµ.ÅÕ¶rжr¨Æ{pnÈ¡ûiÍZë³=®†6_–—o´B’)¦Dºùï„N¾ºîpZÝIÝŠì¶<ñòÜë’Ú-ÞÞ݆GÉ{±í¯‡f£føY뱬gx×u´awÏÆ“œùªßöH@Õó‚²%Õ9wé©å&dÊ ùúDWž“žG³Øl¾¶©vþ2Iž6—%0´4Ó¥»Z°O?w1&ç]X"ߪ†÷³ÄĪàöi9[ÏâëXX諱hÍâæ—eF„&sLmu9ŒBưó—Ê“^Df«;š‹¾u©žYÕ0Èyð*UáiÆ&²øçi“6¦ßÝY@¿>µ7$Í¡sPý¢ûÁ·mÙÚ4ø¯U‡lþp•ÜZ¿?Þv@3.‘¾\Ôc¯þœS[ä»,Ò-ÀßÍ”•xÿв;ŸÙ:,Bynv^V®”aä¹ÙYYlº€câ߾ƆMKw¹Žòã?Û¹õ¥a‹)öj„:éÊÚ#é^“‚ nµfrž,0íŠíÐ-u#^&Bqµ,­Z{ò§nXsÜjhcƒ´ëí{­ßx¬½ZþoD?NÙÛ¨«èc<€êBÙ€ªai»5±Lm>Œˆ y€*JY¶Tøw k6oÜá¨h([UÏHéÊ–¾¾/K°K°K°K°K<@UQ¦£ÿ뻫±K°K°K°Kªì’ B9Í ùñ­LwH ÀÉ.TDþPš²¥b K°K°K°K°¤*/ù–¹z˜y€_Ð÷•-)×Ç,Á,Á,Á,©ÊK0óUqÚq(G˜y€_¾$@¥ yUϾ$É@É0á€ä TP¶€ä  ùAÙ’€’aÂÉ@© l É@ò‚²%$%Ä’€RAÙ’€2äeKHJ† $¥‚²%$eÈÊ–<” HJeK*…ƒ€Šçä‡Ë–†IHJˆ‹ËÍË­ A „&Æ&FFE!8N@u »Fò@!ºººår·CBRBJjŠ›‹›ž®^Uˆ[JjÊˈ—„cCc烓œ’äèà ­¥]‚“ž‘þ&:ª”ÁPË<RP¶Tø÷wo'.>ÎÍÅMKSK*•V…¸iij9:8> }Vš>Á)98U's „hkiÛÙØ¾Œˆ@ò• Æ2$ŸòòÃeK¹y¹º:º‰¤Š¦i]ÝRNÝ"8%§êd…ùC™ô€_ Æ2$„”_Ù’²"a˜*½RÖA"8?¾ZÕ €ªu\Ë<@UW^eKÊ¥Jõ)ez³ü};Æ2$åS¶DŠaš¦«TŸB ÁùñàTÁ]Õ|×PÙ;.Œe? ¾çTWy}½EQt¹¿ZÔfø•t]ád±Çú¶ê»ó­´ðeÝBé+sèŸ%÷ùòöm&ßÌúјÐâÈ ½Ú :›ôÝ ñŸ”-Éân:÷,UQ™Â—ø‘×BÙT¾äá'ŽeªeK_*Dz%RÖ >&'òìÖ5û®„¥ÈWÛÚÅ»íï}ÛÛ0 ÉŸý WHÑתàý®K#·ú°ãîuíMØ„BñG†8ä¹jwã{ºÍ”Ž:¸²™UôU ^Œ)˜¨r’"'."üÕû„L C8# ûêvúøHü„¡³\6•}áïíû/>yŸMs´lê¶è>0ÈÏœk+H 2ää‡Ë–UÖRH:õú²1k_:÷¹ÄÓJºwáÌÅÇ)­­Æy|ÑóìÏs‡²ÝÆPÚÉLê»ëD•¿Uä·™ÂäŠçÐ}ì$ÚIôyOÎüXòõY(˜ïßP™‚ó=蜷w¯?KÓ°®QÛU‡Ïä¦ÄDE…½5ô«,Çà•U®÷/wÌÎ…>DübÙÓ>öÛ¶²©.%yµºßÔ˜ÁÛ×Óüô‘Ï Ý4ceHdb¶œð \ÛÓÏ[Ÿ"gÊä{2–²ÞóðÉÃyͧBö1dÓÊÜq>FKßþ];wý¿Qɹ4˜Öí<|| «ú§PÉB*øžFú"Y­Z}o'!„hkë›XæäVÖ§•Y_<}ù>ULsDÆÕÜkÚëò©/î<~Ÿž'£ [ gíZËÉXä¼¹v僱»yvdd×¹I=KAÁ)2"ï=ˆNɑ҄âëXÔ¨ål¡Î.§!ÙTºÜ*¿äN¾¾ew¤e¿ 3º[ò!ÄѹV5þÀ1;¶>ö›ê·±ÿø'z8<;q>"CͪÑÐ)Ã[˜óñ8xíÚ£w?ä²uëרDñhAŸYy=ú]ß}6"S«Fà„)}kj•× ¸ç Ê‚%òÃ7?P¥P”©<žé‹HÜ“—éòü KMÅÐ !òˆc\›1y€wæ¹[ŸfÑŠôûë&¬{jÞsî¦u‹‡º|Ø6mÙÅló:–ôÛ§±y4-~ëQ¦<òZXº‚–§¿~™c\ÇFÀy1ÍÑsk;rö² «ŒöÉ<¾lóýLM+¯êÓEþQ …¢ôeýe Î' C£}"W0„04MÓ aÃдøÍ™³O‰ëŸ¹tÎV‹¥ ííuyEv‡bñt¬jxÕoà_ßÓšóôIl^9]qÃ=Pù’‡˾$2_\z¡pl×ÔŒS8$ñmÚZæ<¼•K3„(¢ÿ¹!÷ê=uâ@ÅÕ‹N¾—Jß™5뢠ãÌM{·,øÍðö²'ÞKi†¡óí<-õ:m|‡÷Á+‚#ÄåvÃCéDzr‡™PõüüxÙR™Ë~xöÝú7}²|Æoí=ky¸{øøy9êó(†a»úˆ…S›èPDawîZÈË$‰ÍÛ£·ä~S†8«bÝgxäIGîˆÇÖ2̸ÿ&Ca÷àǻ᫑¹>5bž'h8ÕÐa}¶3‚jzV#Œ4+%…U»®ñé#Såž<åéwþeöï)[ª¨à|ö"ô»ý:ï(ºÔ V‘½Î{}ìLlõþ›ú71`R{ÿï+) Q$ß ¾¥¸¡»— ›ƒN}jŸ]t5:ÏÃEðiÐöìH-ÉHI¶¯ã¡s-â}6­õE(~ l©âÐÒì<š¯¯Áûf—Έã#ãy « )BöNFÿ‘yí IDATï~Ì”êÙ9j†–Šó(#catlº˜6`B «ÕósÒúrV¥aå¨A-ç± L4"#Óòh tPU•Û̃<ãC²BÛÍTXt¸`kY›ð²criK†pjü9{\C-ŠOí¨ÛS®Ü‹qøp2±î¨9­œ„„7ëÙþøðkSšYJÍkÒÒ‘µÕ ‘ꄜþ&SQMU^ï÷¿ 5’P]åø%qÊ4½ô¿ÂÒõ½Ö¹Ý“ûž<}xrÕÁmz-¦-îÆ0„bs(š¦)BøšJž'•$GÄÓFÞ&|å pŒœÉňTí¦Õ…'ž¼Ïv¹ŸçÞ«•ÎÇe×¢3õÂÞÒÖ½M¹Ÿï‹,áÆßë·žMR°…¼\…¦L® 9E.²L<”á ¶ìO[úŽŽ‹feÔfʨzúù7L§^]µô–rº„Â0òÌwo²ujÚkš¦ )˜9Ç†Åæ½Ý>(ðoåvRíš%/ºŒøíÅí«÷\|•A¸" *Ga)•+>…‚a”o’¦¿ãªËõ´¥Â×ÏMÍ‘g†]>ó’R†QA3ú2†Î ú"*%¡8<¶œáå ›Ç)f¤a$IOB_'ä((#§)õòF0ó•ÎŒe_wÒ ¡ F›¢CÃR0QŒrâU7¢¼‰ŠLÉ|=ë÷›le¿.“Ê ’³åf ÅRž4BøZJ–'U”×ãdñ´%€b”ãÓ–¾§OakÚz6±õlÒ­OòùYÃÖì¹Ñe~õºŠt. M juòÏ ! Í5ó°Vì{ùôeº}[ ‘ké­H×wéÆu-øŸíŠ"áÂò%gY]§lèRÓŸquÜ C…çÇŸÿ£lÉCÅGÙ¿2„RÓ·±·7ÎOÂÔ¨üD‡!„Ð ¹Œ&,Šäo_YÍDÓ -g8NC׌«),ì÷¹êjEvBöîÈÜõ·Íþ˜·»Eu]*6xô¸[L‘P04Ãäwìß—}zœ¸{Úü¬ßÚÕµäg¿¹upÏùÌšƒú¸‹ˆ‚aˆ<öÖåÛ:Nš9á'¶ÜâúMu׳7ia¾¡ lޱµ“@CÛÝ9åáˇw^«éZÙÙjfÅ–¸c|S7×Äû/žÜyËÓ2·³ÓM*ÏA Òeß;–‡kÑiÆBí}»ï^t<—ai˜× µ²›·!‹aä„¢H½·mîžD™Ðª~¿Y}]E„²é2}&gûÎý‹ÎeÑDݬVýö–j$ùË“¦¿Œ¢ cY¹ÛiVÈoeºCJ@@]PMŸ_EN‘)ŠÊÍËÎG$eeeihT¡/nÎÊÊ Eè| rÁX†ä€ò+[251‰ÑÖÒVã«U…¸‰%âôŒt3S3çƒcllüþÃK ‹*’?dee½ÿðÁØØT.Ë<|ÊÈ—-éhëÐ4'‘HªBÐø|¾‰±‰Ž¶ŽB¡@p~<8QoߊÅâª555c#ãR@u`,Cò@Hù•-) }c«J|¥:MÓr¹\&“!8NÅ@u »Fò@H9~I!2™ §D‚€î~ !ÏHy^ß³v×Éa‰bÂÕ¶vkØ:°O?KŠ>Ê:;;ûäéZµh­¥¥]æë™Wú5Ÿú¸è›Á[Ú_¸Þ`í¹å®8Èþ %µO¿F£ qckjáÊNq±"tÚãý‹WN5C&ÃG°üÖët»}û‚̾5¤åÜ›Üb {ù‰y^ªt\µ˜o°æä¢Ú‚ y=ñóyM'„bÐmÏá!̺®}#FžßÔDëºÒÏZ*ïÑÔ¶ÃÃzì;Ø×šûÓ>ƒMKWß¶=úõªgƯ€1!÷ñì–CB²¾Z®×iõ¸è‘“%SÏom­Ïª$ý¥nåÝyÌä~õ Uåd’¿?·qé†cò[ÓÂÍ·õÀ½ê겪ʧÉ©²%iÔÑ=W¿rèƒ:’ÌøÈ[gnßóæðmzÙ•û¨ æÐã_¥ !¹OÚ*¹l„³!”šµÖ‡™óhW –ÊWÊþʉ“{ÿÀÊ-£'éÛÒÍBN'™ì+†M½`ÖkÌŠÆÖ¼Œèg_¸Ó­®®>õå ·@uUDÙ’"þÌÜuÏloÞ2¾k#O÷: » ·ÿðŠ@ Nú…ÁuÛ,8~p~¦ :mŒ>6µk«&žÞ¾¾-§ Ïaɽ;¹™ß­›¦þÖÀÛ×# ÿâ« µJŠ´G»Gwnèáݨã´ÞJÊgosrrØ›š–JQTffÆþà½YYY%ÿJq·Oquí\kº¹)ÿ¸ÙëSQ{f­8+ÿüzÍÛ3ó´óòöõhÔ}Ìî§´²«ûŠ£[G}_tÊåù}[4©çáíëÑ8päöGé4!’—ËÚ6è±õÀœÞÍ<¼ušyæmÜ­5CÛÖööoñçÎçÙ !„Ði÷¶OìÔÄ×û~³ËÎÆH«ì]\q´,œj¸zô3Á›<:õ(1lY[ÿÞûÏ­Õ±Nñ7rèŒgû&ö ððö­Ý²ßìor•AÍx²m| ¯·¯‡Ç®xöù'ν;¹Yý1ÇŽ¯ÜÔ¯íÜgââ"Og‡ço­IÇß矉‘Š£O-êݪž‡·¯_›Þã÷«ÊLýç±¢Ó{Æi0er¯&®5ë6ýmšƒ ëFÎì³:ZözuW/o_A—#víÚÊÛÛ×ûA³¾‹N½—È?îë3âZNöåÁ}=¼;,rwj³†Ãn*ë$±‡z× ÜùVöULd¿ÀqõõÅ“˜ó+†µ¬ïëáݤý˜¿n'+!„É‹ügÁo-ëyxû6ì9ýïçY´²³^—y;– l×ÀÃÛ¿í„ýa9_\ãdñEêBEq…ê"næ…‘¿J–=ÞÈÇףѴ{¹ßêR‚ÖžÞ=>°QÝ~'>¼ú-{uíü­abBˆ"ùΆÑÝü¼}=êw°òrŒŒ(;äz#ƒƒ÷oäíëÑbÈšûé4!tÚýÍcºùyûzÔkÑ~ØšëÉté?ƒ5j¸{7î2féî}Ãìžo˜䣼ؾ±Hýdù€N ëûzxûút¾òz¢\öv[ _Ë•/óiäêŽþ=Åžâ±Df5\Ýjº¹Õt±Ñdqtì\jº¹Õtsu4á|<¹|nð;¹ò}ý¹gûÔîÞÞ¾ ú¬¸ûþÜ’¾ ½}};O>­Ü°,îêú!yxûzw±æf²âç÷WNÎušü>m„;~õeNICCÏ]'V mWÛÛ·~ï%”c¦,áÊšá-ý|=|Z÷]÷0#ÿÈ*®—+ÍàRHr#źϔm½kºzø·¸xÇ–aÕyßhÄbÒÙa‡&÷lîáíë×mÚá÷²_áS䪔 xÚ|çä3ŽÏÀNvE/»³µmìµÙ„Yò©Ó܇ ðÕã¨Û·xG«åÄEsFz&ÏX’DBˆìý®ÍϬŸµ|rkÎå ®¤üø:77÷@ðÞ”Ô]]½¾}ú¦§§ïÞ›SRqh±_ÃЊBtq³œtúùÃWG׿÷ø±ƒs&mŸ¼ø^C‘½ß³5Ôöó÷¥fá×wúÚ½»·oéù×ÌaÊ{&ä/wœ¢[MZ6¡™,d^ÏÞ+¢=†-dòlë³q "‹Þ;vìiaÏÏÙ0ÄøÚìÉßʪè]â×úP‡E(6EQ„ÈBW¯¾§ÕtȈN6ég§ŒÜí1fÇÞÝ›ZÞ[4bÑÝLF‘prÚØÍ1žÖnÙ¾°¯÷§©y:çÖ²MQV‡ jiFЉ|ö«M“Ö†»Ù½Ïö9x’¸äw‡'.¼aÒÝÁýÛ– m ŠO“©b¬X|=¾øÍýèÂSOC·Îäu-ÙÖ¶ž?wæòòZm×v#VnݵﯹyæÎ¿bÜeÝ ÞÒ£g.û{¨}q%0’/c’¦ø•Ž+¥¼Ð#'Ÿv_ºóÐŽ9F?øNFgÜ^2hñ«k‚÷nžàñvÝÈYç”Ýš<öØ©÷>3—Mé¬sgí¸­¯¾}U„Òj8C;]vIGÏž¹t|JMÉ·º”w;–]¤|zîå¦ùÿ[Š—¿yÙ»¿ÇŒß›ÛtÞŽ½û–v†Ì²9\L!tîÝ G¤'.3Ì9jçœ/%Ùw—MÛ‘ÒhÞ®}ÖMînŸ—­(s¬(µj5¾wÍÃl"}½k̸#’¦“WlY3¾ƒ-Ÿ0„¢H(®—ûÿƒKѱ\ÝHƒ$<M*¼4ÆhðXÅpŠb¦]Ÿ=rå«ß—­[·¸_]]ö/ð©¯([UÏHy–-I“"ãúÖ߸½­ÝzýÎ)uÔ)B©ñG?B¹©ñj5º >“CBX¢†óÖOðÂÔâ=¼9õŸ§™Í áX]¿´¿5—0îâSÿ,}#i¡÷ƒ…ÆÇŽNNIÖÑÑ ì©®®Þ½[}ö&'';~ä·ž¿—á:{aDó ÿ'j¶îpã/×Hº¾ãŠvßàþõÍØ„ÿ6Ì÷è䳑yáXÝðÅûróíÝZœž˜@êùè‹Î¦«B8.SVÏh­Ç’W{wðÌÑ‹÷·ç‘,rjÿ¬ÐxIÛÔ}ãÌXÛÙ]Dˆy»AAûƒBî&YWÉÒœo^+¢¥©o®nX}›xLöÔ¤ÂËqüŽíÙDþqß»w pæb7vÚƒN£÷>aôn×CÝ?þÝÁŽKHuõÐÝoåŸûðëÎÝ¿ª±Eˆ8tþב3HáÚÔ­ël£MÙØºù‘ÜG³ãî~nöF,{'ŸV*+M¯¡£ë [4 uˆ‹¯——_ÃÆõ´9Bu›Åjikks!ê†ØF–œÈõil»cGh¼¼½:Åfkhkë É+¶oHø"&¿Îqõé‚Ä‹='“<ÇmþÍ[ENëw%ðð?Q«ï¹,o´dbG Bª Ÿvmàž« M|áÚü¹rnK! ,þ ‹ÅéhkkQt‰ot)lÓ~›ÿ–_4i=ĮĖ’Æå7ΛÁ¯­ìÛÈœCˆÕôQ·Ú¬8ÚoêDbôxß•g}ÖÌìl¢ÝRŽù;EF¯4ûòí.…ÅײËÚR⸗ɢj5ô”g,,M[g½Ü{Ñ´%¡X\®rYBKžGëù4wX¶~h‡Èúþuëø·låg%d}ÿ)—<î[oDI{a•Å+Ø"-~ŽB[Fމû»ÏEÿiÇ>w)ÍuwÙo€¦(޲çe©©óÙ,eJ©©óY ©Bš•{c ÿa–òê¿D*«ž’ÇŸ•<|ê¯8gmQ[“"äÿ „%Ô²äy’Œ¨—Ùzuu>Jñ½\’ܤäÁåóC†¥SwÄÞ3mïݸyçÞÝK¬Yã?gçT³b1=¦Ø…ÏcˆU/;á¯ó©GòUN÷‹þšE•O>qü”/–ˆD¢æ>ÏŒŠâêØ8¹8šÉ|õU¤`8î“Ì÷V/»xšê™‡¾~_²7YpÙzäÆÎnÔûm½z_újØcQEÇ@eϬ`ØFA+7üaSpêA±…Úܪy`ÓF,ž÷Äu£]4ÕõMŒµ¸!ä‹‘¯Ž)†F!§ Òo¿X±‘×. ¶ºtöÂí{W6MÛÜyÛ¾1³ìltæÂõ»wvÎ ÞucÑѹ Tá %ÅÄŠâ¹6ìæÚ°Û€‘Q»þè²eË­®ã‹ÊéWLÙú±õ¢à­¬ÔÅwÆÌ*݆ÙFí¾ŠI-!U¹«¯bWš¥Ån…¡iÂpKÿߥ”¶¥¾õn¿~Êã•kÓkÃ?ŽN]¹s÷ܪ£;ÿ¼wû@;n™c%ŽyGL[q"Š{#ÙÇ”éOì‰SŽP}—þÝËT-íLŸÛ•Ç’{ûU»Î…7å]ÌtYWç‡>IEúÓ‚¾•Q0lû¡›×¶2(¼úÂU×aýÜþj¤åË ãW¼ÈÎ?,¤ÿhÈo"†–3 ›ýõ@IýßF.npù [ÓÆ§µOë^#ÓoLî:qãá3Š9yÑ+¿^È^® Ø,Ö/õ©¯¸çT= åY¶ÄÒ¯à(»³åØ1!,‘}uG'3ÑWŸEê«™Æ-{6wÔæ|+¹§3"% tU*ÿ®ë<“Æô‡ÐT¡ž¾~þ]uN±sî»Çq¾½;¸ð(BhEé:.––(%ô¥S°}}]MAU}šP±7 jšÙU³³4Ñ*öôŒgRØþøøƒ2£`ò¢dzͪ™ZjåDGeÐeŽ<‹¦9úî-~7{íÁíý,oßü(¥´š ›½j×Á)NÙ÷®FKT0VtÖÇ÷Š¢×LxŒTª ,¡å B‘'<{-±ïÔÈJMSx‹U¸!WÀ¦s²¤´òdßI¶æY­>ž&¯“ÿQ͉8ú&ÏIô:ìqäÊÞ•°÷ößlîØõyCšÚ©eDÞ:s>¹ý©žÂŸÛ_±4=†­û:hùØÅ6;'”~h:uôá\±hÝÚ,ýÉ?;OgȽ¾ÕËiR±eØWñ‹¥½–Q­‚:Ö«aª–yiûΦ]gÙ[Óˆ‹;³pAó›;f¯5ëïɉ¾và¯W2C¿_l4Aò¿º ù’8JàÜoÃ~óíëöîš|4UFغv^†ÏìQƒOnYMÝsô´Îã­yNͦa÷î Ã6çäÿ>‘¼;±pØ¢dZǹÓü՜Ԉ¼òw%,½Fs7OY½lÇÌÁÛÄ„oäâßm°Q±EEüê¿Ïé9}óäA”‰O׎Mÿ +Õ+ð«÷_»Ž·|Õ–qGÒi¢iíÐËNHUÍû;ÚˆmØbÁªÔ¹‹–÷9”Mi;¶™°f‚·&‹r±xhâô¿&Ž’iÕhÝÁGïU"SŠÈÛTKÓ?¾kÚÑ%bÂ7©ÓcîäfÖÃoŸ=pCaë»¶Ÿ5·[cÅ5o=¸Gâ«'mÉaWÏ¥ùè-£ŒØ,­¡CüÆ®Ñw›~Óû'Žo9yͬQG´ª·êÝÃýE0!„p­;Œëvsê¼!gÎnßÐsÂØ›“ÖΛp¨N×ßzX­ºI( û:úNJƤ±Nå¿ç–=\=v@Áÿ‰š­ ™¾z|ÞÊq¿¯’óͼ»¯\ÐÝšË"Þã7[1{óŸÝÒu›†ÃVMjeÈ¢?B˜ôÛÆìxŸË7÷¼|V³ÏK÷Å/–vV~IÜÔ–Ý»í9<ʱñˆ!“— ï˱êºmרÿÓ¥°ôšN»ä–Ú<º å­z-_œ=où”?vJ8µ:ÎÚ0¤†€äwR£ëîÎ_ºeüîdY5ê¿`´»°t±’Þ]2¤'!„¥næâ7pÅÒ^>&r‡¾{— @û¨K…ïΫ…=mÝÙM½º9JÝsÔ¶…¢…WÚ›MغÕëµhÎû/ú+®uç¹K^ý>rÊÔj;g—vh ´üÆÏë;uî†é×9f>më†îc¾ÙËIË’<\ k´ëÈž™‡R¥„ˆ,½ƒÍì,`‘b‘¯Wýë…B«î‹'¿›¸jñ¸CÚέ;u°|{ë—MÊçtšòã[™î€“]PA!!!AAA崱ܻ“Û# Î-¬#RÕ÷õ6ÊÖÚöìuxTG‡Ï•õîÆ=@BA !¸×R\KÑâVܥŦ¸»[ð‹;ñdcë»W¾?<øJèf;ïsŸ§åîæîÜsÏ9¿™33è¹2óŒÔOÿ0•Z|~¡¯Ù ùÕ?†Ê8Ø·ï…·ö r$‘­¾¶èæ´^KÍ?;§®Ù QÁOçÿë] Y/ùÿ~< —ŠZmɨù·6«GüÏH{xïc‹:µœEÊ„‹k¯bM×x­_!þm[1ù¡ûñ[lª.D¶BãÓAâa¸THÚ’±óo b"þgÄj²îïܼ»H„Eõ q«¦úŠ1d+äWˆÙVTæµ#/L[N6 -ü =$•O?À·\mé"l°üê¯JXTÑ|uÿÍž‘ Îä]7&#[!¾½­HÇ~ÇîôC¶úÿŒçÔwÈV£}:h©V„ábii‰ŒðÕU ê%BÏÙ Ù l…l…žˆÿ ¥ KHE|•`ajôŒÐ3B¶B¶B¶B¶BTÚ§ƒÒ–†®à§-EFGþ§Œö_»_ôŒ­ÈVÈVÈV$ˆo¶ÚÒÈãZdL@ FFè<$ˆw@«-!Œ n&ê„"®LÒG?D"`ª™ëRä-…Œ@ †C×`@«-!ß 7u’Â$ÀÛÚQ*DÖø€ìBõ“Ä|7“¢ØB²@ ñ€@|hÀaÄ$qý½­^z!ê]ÿާ“å³x2@ ñ€@|)Æš¶´öè´¯úþ¤W"g0>djÜÖœŸ†”Ã'0ñdjÜœ,@ H< _£¥-!Œ=Åê)´ â'ŒC#Ë Ä×`°R×Ð*ÕŸ‹Á‡û³°€gj¬ïåºäÈqµŽ²‘<zË壣Y-…6`ú„qÐ @ ñ€@|›¶ÔØ&ÓÏ…okÙ ˆ>¢iÚ6/ë¯Ô’N5í“eº§I6Òÿúw¸ÐdxO·nBY0~cJ¸îï¿Ímä>ÝOdšƒÛc6gsl­§Y-yø¤²ªèÖO8a\õêW^Œ‰úÊgÀ3[7Å5wIJ4$ü„áé0¼´¥*fÅÎvõ¥+ÿp‘͵~Gˆ²W€Ù£L,_Áýon1 -·NrˆÞµ!ûÛG93ó{¸óø,••½âBnlù÷Š{6ul£Ú}¡à¥J÷’c±åWwòô‹Q/ôå–Iìæ8Óv#õn¡>µÐÈk=Íê*T<0JEø¯7wå×_}È®lÕ"½*|ñÃÃ{ò‹h®SÏš?-ww°tÖ¡§;–§¦€Y}÷Þkû9ã@k^,¾·{WB`¸¬Ñ€"´Å§Û….l3¤)«`ñð±e[¦¹×~÷L^æ¸'–[리J|P®ûqMæOòðº5ð¶Fÿß«?9BQ¿þíbÞR—Þ>Îõîì:Ô‹/¢t¤.ºYRÀ`¤_3·¦v$“›½ôlN¤0[oÇùAVµL°’Ù¶Óigsc}!ùVÒ)ÝÚÙ‘¬Bq:äåÆ(íÞR¾Ýý]¶u%—¯NÕþWý #ýš8¨mâiAZÍíÐÔ¡Šb³õv˜ÜØ¢¶ WŒÑ©ñ¹›/f‡–¼õ ¡íºavÏb{]PjþàQ5FHÞþDîý˜ÞWU:!ñ€@|-›¶Äåp|!—ËýPê(©‰{úÛÇdæCRr¢••½q>‚ß§¯{碌qke\ÓÑÜW©{•—³8F¸:Yá™»ÃÕz ¸»O¥`éô'Ô &µð²s6…æçþzäõš¶¤8ÿ`öè<ÞDO1¥&0y»ïÀ‚¶4®%*¼<îɦÕây³Í 2fÓ´L›ÙþÓý™èåO·à;œu·ˆˆÜwïÚÑ'îù¢ñÏ£[Õó6aK.¿¸+r›ÀÐLEGJ:ºyCŒ,mv–†].t ´idéQé3Îä§rVq]-§ŠNDh;z=­7ÿ@¬Z¢°¨Qíx{jk‚é$ ¼o5ŒÇùœúR€¨Ó/1˜ØtXçÞUyZ÷ôqÆÒk…r÷ª{[ð p>5×ròc­-²EÌM¢€°°Þ>Ö:ìÏèm¯Œ'WDO³ºŠ›,èè»&Ž{ksE³:(ÕÓCÅfƒÚ¶q@Øsrú‚õ/Ó&yàÇ3ò«Wû“¹”û…Î÷;¼ iç] ´·sµ¢x<‰•&19GãI*®­.ñ^ZÓ‚ÐUülæOL˜f r/Òßú€‰—í¬vš_âäz/Ï3AÚaX·&–î¤îÁÍäy¡J%ÎiÒÆµî_÷rY×:N³[I¼E˜Z®»Ÿ²ð1×Ì|ô û¾®uNÞŠÃé7JX̶ºÃ¯m­üÌq}±üÈÅ”à= ¸Km§9m¤ÞB67SÅ#!×Ћ‰»Ùø¯÷ˆ=ߜ㉺V'ž\È8›¢g@½®Šå:?SËèßú¦ttâ %(6]µhÕÕª&_žá)õTä ¿SGALHVÐx»N6Yq™F˜©Å³–´5WïÜŸû …œôcu¼ºxóNÜ}'x-×nQ²|UáÄåá7÷3ýˆòm‘éÐ.Î}«ñ •‘&Ûp23TQÙ;EÊó+J½û`bÙePî5¼Zºò8‰ª§—ž–}CYb/Ý^gŠC! „‰Å¯ýí¨›IÛëzŒ|§c@U¤ŠH× ¹N•CD" \?€á¥-q8\›œ§=ž5¬‰}ðÕ”³³Ç‡÷o^56—Í-Ö>xøÌÊÆIlbþo–’RïI‹eô§÷Gt\õ|ô˜G öîë[ˆè¤”_îëèWƒ×<ï°7ûD:î_W$ÀlªIÜŠ Bò*vÑQŒV_ÁÅ€¾ì‡Ô%Ié`YF«gtqãï; £*^º%ævmËÆî¦º[÷÷Lê{F÷±c‰±É×c§<;Çš¢9ýð•>]]«W.&1ã pV«QQŒLË:ñq€À9ŸedZF®d8|¼L•“‹b<ÂA¥ CòÊÜ ãóqJMiÙÏÙíó/.û24îÇ,Ëö^¦~µ»4‘nß³Ëx‚ M-f t”>MýàÝ|$&?O•Ÿ§J!ÌÏw³ö¾š¼lk”°ÌÔdãžd)ã®–È߯³r4 («ÞAOa¸XZè .Ÿeðq³:<§Ë…¡ùé ×CÃO-èV¯š Ë2®$·²,`äÿõ–ãaS&1G_îô5œK °7ñ[(Ó3$ÁÃtÊó±tÍÆƒõ—#þn¹ M‘*Ÿ'ð—i k±D§IUвROÃåxUûþ¼äð¢WF×kW:aºbØ×?ÄG'VöD¥eu4«•Ǫ4<‰)mQ‹ÐÅ)J(Íê´™Š†ãàD½smÁÙìHËÀZÃ2β £Âp`˜Š+ù7ÞaÃXµêÖƒŒ%cžSŠÜÍ]Ëí:£õ±¬#W§ÔËe‡\£)[G^eOY§êdé%-*‚98qÙBe¶VU¶¯³üER‘¥‰“ÓÙZ̲,³Ÿ ªò©ø<ãì8WÉ”ùMËiàcæÍê9ô— µc"[ËvÕ˜8=§a§:Åy?e2P^v·.¯ð¾J: È:ò’,c2°™XŸ©&þYQ‰¯•{^ælÙߺÜü9ÖúØe\)Èšÿ$έƒ–¶-IÍ{U›™+–$_²5PÌ·‘?¸¼FɨµÀRzU‘–á\<~ä][sÛWR§¹»^kÒMjŧ°nbóݲK{ˆ&u™„•EÕ,ª;Qo ¦ÔÜÙª÷^+ãzÆ™g¦TËzk:_OÖw¤i¶‚z£©oj‘»ËRÍ©ðâD áëÎ¥ rÊ/7õø®L1Èmyë´íQj@PÛÒòIÊžgJAg—ÉÙ™wT\?;?’½`ðÞ…“„KðpÀI„O(´´N§<ìoíØI™›*–L¬‰?=W"côO—Ý~ŽO Qðhcª‰KŒÐ°ªØüØV+6'05Û{”¬|eœI7Ú¼‚B›!m­Sï*¸U0SïŽÖê÷l]cg nÉš¸KŠòìÆ&àã&\ Ç0Ÿ³ŒR÷ÎðÁïÓËÁ".ïZºŽ•˜yò¨D£P_û•ž+ž8¬J“ÜŒ…/ôV¶B+ ©´BÎàÞv¤‚{ÙzÂÂl`aQtfì'òx¶öëÚq>-Œ5ê;¶ÄŠÆþ—WFâøvú /méa:O­Ó”È#K{¢H€ž~BeQJd0,¼Rò"^ñ A<€^¹ûŠlu'÷?ý©°ó/Ó¾@nÉïØÙcŠ+ÊÌ[x +N_~ÁjJÖìK¥;Ú¯çÈaõ1/ÒÇ]., šWÕVÕžf}j±œÒÿҚÇ“Ä]œ ·ã1úçS&ÞPj>Jrb5Êt6Ï Sáp?Ža^a¹äªËY+'—.¡“»ÁW»ÿÔ…”ô³ë‘šsy\æm·îlÝw’‡xIú,fN®ÌÞQ ¢ÚfÝ·XZsß‹-:Wç*ùÙ‡Ä1À],:Óœ’uŸfÍ&ÚøØT\ùñoze]AIЉýÌ¡"`eéùËNäg3å$(^fŒ9ÆLié´¡!Œ>!^¶³€ÎMOYdç>-Ƚ½V}#,/Î^jøÎåÙºÆv€Ôó\ þÄŽÈ5™ôÓ+‰[:»Žä%¤uO$/ŠÐ1²èÔÙÖ®Ó»zþH0éq™3.—@aÞ¼“¼ùí܃›bŠÜ‚Õ‡3cŒµ+˜Rí>’*íî°æ'’Q*ÏžJ:’Ï`€Áë´Ëòí† -ÖNuó€…ê@FJ§…o.ËP‘éìÄUû‰1Ðjnÿ•´)Íâárüj]1ÏÓ ÌÌ×T{ó¾NX—þ,ƒýÙϹ«9A2ú¨ç©ãBÞOOz÷ È/´=ÚK†s 8§`ý¾´kòÿÀF?•ÌkAÈ?¿Ê\YPP²&âÛòMBBB6%x|Û‚™ ÈFfÕíEäG³)šÍR†Æ«?9ª½öè´¯ú¹I?®ü.ö&üzù,…änÇJþ‰îáXÛïnvtKìÉr§(pMæO¬fsþÅØè/[fˆ+þõ—ªV—"§ÆPFÖ˜iXŸ*vÑ9Zô¦—‹•˜Ìy•kÎÇ)âS„Ϋ÷ÿEJ!!!‹ã%ÿߢ‘„áb°iKÅjêâsÙÅç2ôŒ>¯VOj›“u÷ã¼W’ãí"rs³iÊ×ÊÿÒ”ëùâ—”QvCñ¸¸K §)MÉC C‰„¡ë0¼´¥È÷IøWÂaq¯šdTHqþGÁ>.4›Øß¥¥½s5åÐ.9‚q60ƒ¤ägj#µ‰ó9(Dþ„qe‰â«0ØMâŒúѱgmÿá5´% W>)÷¦$Ä¢ü¯»«¿²ÿé#5·DjŽFʃKb:N"e>#Äb°iKÄ?§c þ¥øB3ss±€‡¬ñ%JM®¬¨G þÙH4'@ x@ ¾F?€Ñ¥-!À™´­Vr9:7QŒñ!tõæñE&H< Ä—FÌÙð‚.õ­Ö2·¡zøC •Ôí¸¢³ó)‰âKùViK¡óê!c"•W)ÔuOhëˆL@ H< _§¥-!Œ¥F·é|Ä…XZ¦ª¬Ë I„Lc{E}å·_‰{;ûÚ>µI5RÄñe ´%„³é|Ä¥2ÀËÞQ*¬¤·]¨OÌ×i3ê[}Û+‘‘•õê¢aC@âø2ÐjK#æB,íïe/ðÒ ©J{O'˰8å74Mëtº¬ì,$‰âëô_ÚÒ1ìë¾ß ­uo„ÈT¸­_:Ô IDAT9?­+3¯@Càø·Ï¼bY0ä&ÄñÅ „q£§X=U¹•¡žf€ *d«; Cê@ x@ ¾ƒM[Rª•ŸÜM|Ï0ôá8QÕÕ£^F¯çÿS°´—»Ÿ»9z”ˆrÑѬ–b*÷-P$š@â0 ý†—¶þìF£ñõ ø¸Ã•¦éÌüì!›6«éhÎg7]Œßó‹ÿôáQ¯®¯›³ôxTæûÆÚašú4j1hÑÉ$µñN½b5)g îP? ЯãÈײ>®µË·0êôk [7›xGñߪ»˜âÇ«{úöÞýRšˆß;ù¾=úɤ©œ›& êÞ$0Ð7°C¿'cUì§üŠQDX8º{ûæ¾þ~^x2NÉ0…a;fêÙÎ/ зu¿){ž3€0ÐÈÂp1Ø´%‚ |!—Ëý°ÀJjâÄžþö1™…E´*.!á1ÿApúñ ?kÚŸ¹¶ºÓò9ú¼ØDçÀIƒ:Û >ŠO‹‡ï¶Äz¬h<µøuä§•_Q`BÑÅY‡¿HiZkÂZù鈽??´½ãï'Ñ>ü5"5¨ášéœ»ÝnïÝg3k´ˆÐ_]Y赸–þqiu46ò@ÑåDõŠÈó)KS*ül¤²IC1ò¢O ‹Ý4°ÿþtÀ1ð+(¯›¶6ªÁ¬s«^Y±xÖ|·£‚, ¯/™²;·ó¼-­ÒŽ-ù}ÒZÓsý™§¦Ï=K \´»©0rÏ‚¥ÓwzëÅ3FSi‚§®¸é2výŽ:ú[æÌŸãè<Àéè¨|»I°¢cºÍ Óàÿ­áeV»kêÜ Þyÿ0סkg60Åp¡ƒ®|KúXð‹+7ëæ¦ßWM4­~f²7‘QŽ_U+ŽVR¥ÛøÞÖLÊ•­¿ÿ>Û²æÁq®²Ú?Lå!.x°gùÖéË=N,oh‚&B!ñ€@|^?€á¥-q9—Oœ§ÓÿŠ“-éáÚØÃ<«P3a׋-ªÄçê3ŠÙÑ᫇7ö÷´ù÷ZÄØÕ<×Ãó!-T»¢þÑ9ÏºŽ©~{ÇéxÊ5hÚšQæg/ßû¤Dê7tåòÁ>buØÌ®“‹z´üëàõ$…™wï™K§4,\÷îEÆÿœ²fõø\%B§&f,âkއnÜ™ÕiÍ‘‰>¢×õº"têÛÒhXÙ>p%xϾòg+Ååõó6ž,"ljxju`ýºÒåÜÝ0öÏ}áynóVNncC•wgûеÇî¿Tò­<ö›Ô£`ɸSËO/mf‰SéGG÷Ún³ü𢖕^”èiVGW|.¿šÈõµ¹KÎÇÇÒÒ~ÍÓ”§rË>sm«I1¨âuâñ­¿u;¨R0§‰<«Ò€sáA±f©½sßÌuR–auåÝBʼnš¦?ÖçÃñæS'–,_}>JѰAYôÂ?Ý?kÎþ[/µVþCçÿö“¯tþƒ?–®9p?Cͱ©×cÜ‚_Z:pTa3»NÕŽ™â²éD†WmUhŠÖ÷ò_P{ñÍmMU/Úp<,K˵­ßoÚ¼±¤«yy±\×5p¸Õ9>:ëà Þ‡ßœSEŸ¸¥ô›1®‡Ÿ%®ÓÂ.>&kðøäc²Õªqíꈠ¶Ãø!‹N¾˜äë|ûlŒ´ÇþaÍ«s¡Æ¤áç{ì:“ø“W ¾j‡äK—²«ŒØÐ§‘“ú^ròFVïÁΜ¿·[›Væ-·Þº§ze’å™ðK­Š Ì ”)õÊ|ËóJÏB3(ó•Zy‘† ÊBBhÎ¥LIƒÔèzÕ»VB`Æ£2Š´ ˆñ¿µÛg…-OMSнú{ºŠÀÕ£N³Ê¯³Ô‘Û&­Èî±e]'GúN“èÐaÆÚæ¦1ä==±j㯓Ìíéÿvè†Ê Ùõ˜h8¯®¦ÿ´_1yçG-‰ÿI›'54{×ј¼»®)ªnjƒÄÄé0¼´%Éåñxž&"Lc%®ŪZx9=Ë¢tZuÈͰ]SÚRò8ƒ4'Fr pž ÀH¼¬±s˜7Ù3¯»z0¡SMkörFUÿ½Æ#ñÜúE[ÏF×Ä “SnzšQ«q§Þƒz¶p&Åÿut³É”9"Îw®¶¤Œ:ÿª;»–ÃEõºx©×œQúÕ{﫸Y•j¦ªÄœ¢¬Ø|Q5oIi‡›º×¨¦3΀‘BîÇ®¨ÏL–gÝÑì8À2ZÞ3/óÓ®[9Á⵩Xšaßœ%ñ²³Ì›³N¼ àhš5æ­*1üU€¡öKíö÷½7Òm=VmÓ-¡I³~ÍÚwhä"¬ÜÒ‹•Ç?J.ŒÝÒ·Å–×§‚{tzùÇé~5›”i#ïªÂ¸£Cå÷q¶#XUÌŽY›Ò~=ØB‚Cñ§ý ·l¶àKVRèî#'{6ô{­>t©§æ,{\mÌŽPÈŠÄñ9 v“8.‡ÃápH’Ø?¥E·ù§‚šžPÙ‹¨+w™Û¥¶›äAx²¡·•Øß„hošHŒäqÞýL—´oò²®¶†ô¬e…¥í0è:ÎóX•LU:òŒ ­­EÌË" ûV<° ŰcŸ)SÙ·¡Ü… P)J´ÆÄè(¶4íç»´û—Òîç‹» ÆÑê%¤¦ÖªõZ=@ëåjà¹14£ÅÝL{œ7íÀùÜV:Ò[ó4’¶ÅåqÇæåyy Eš–=--…C…íóðáȃ*îì½b¥|zó†¯Ï¼¼­ªWíýeÀIn9QÚg‡¥Y¢ê˜?6v°z£„8Üü=‡?ﺕ\d)\[¶Ä’¦D‹‰\„‘Tir-@К &’y&æ<½B]Ö½NkK4˜ÈRhŒý¾¸ÐRÚ×VZ-×qLÍyøçìöy[pÜl9Sýêù›Â.¯;¹ų̂;GT©ÌëÎá’6ËÕÑ”ÚÎ<3câ­ÆëÖ ôyo=ÇÜFŒÇ«KýI—vrîä½Ü¡Á ÛÙ‘Àù¿"L<}<=Íâ/;~#«×•{cñøMy]~ÛÙÇ Ív0 _@&@,¥ K`x“x<>Ë2àãfuxN— 7BóÓ®‡†ŸZЭ^5–ex<™VˆcÀüËù0ÅñOr…–ä;Q§>Í6 Ô­¶`J;Í9ÖÞî¼ÜÇÏòJgMÈ32”"''“wêÒÒ] Ù‘™ê/âpWaþ³¨ú­ô`)~°é÷»U†ªŸñDz3Ʋ²fé„éïr¨äw×(„ý$žV¥?ªxs¹yÊWrÍêh25³¨ðîŸ(KnoÖzOŠ =ÃÎÑÓ¬Ž%X`)šyóµ7#Á‡Ú!öâ=­ÏÔm{ìß{dÿރ뇺ËïŸS¿÷%ª *Fnåãléàe¥ŒÊ/õ¦$1R&ts5Ã?T¬80 @J½‰¬ˆ®¥D*-=,MDÒ/wÝJÐY(ñ¨"”E¥*YmzD6æPÃ^dããDdGdjXejt.ÏÅÛJäPˎ͈,Íì§‹ãåfžn&Æ1`"o+eR\i•£Ï‰È`ì|ìyŸµÛç#Y†aù~GN_ºóÐæ)·žÉ*ù¤ÂÄÎÅÝÍÍÝÍÍÝÍÕYÂÃyNNR>μíaJâ#óI{wK@—vzî˜uÅ?l];¨†°ôeæ~Þ¯(e‘šÁ  òn-½4ºé²?'ø›£pÕ *d„ë0¼´%+©mvn–½#I’~¶gv?s/qP›Vn¶fEeçfYIm ¢Uä[»Šd÷.ß‹K=÷KÒeü• ¡\/AÞÝ]kžØtÛ_C€ÁÛ‹(ñªùÕ}Çÿb«ÓQgÿø#Mï âÚƒ:YÛ¶d›íئœgÛ7ÇØw›ì#„Ì·‚À¯³jùo¿UÕÂ,çþ‰ÝtD—OAèݯ“í ó7›kïL%ýuê¶Ýèé¶û]‘ü´opLp«ßÖå!M6t4†Ü%Š) ¾+ùå܇‚ŽC œÕ—Ê2²¦¸º8ûêo‚Å™ü ¿Sc–}ý)[|67ÆÉ|˜M¬'þ4R­ëÈ‘EP‚ZSlêûÍyPÇ^¼§ò×¶VÕ²¼{W~7÷Ý;/ÄjtÁÓs!÷undú?6½ô»ÌMl×¹wµ£ÁKw:OhcWºuýs«NÖÀ‹÷b!3g["íÆÕõø”¤îàⱋ§lVn]…_œpïâ•ü®K‡¹ëŒN©T)Ô4Ëè”r¹ÒD$â ¼~h.µiói‹Üd—W^ÑÔ›Ù@Jšûõ¬O-X·µÑÔÒÔ# š¬®%&EͺzoÙ¶rÏÄF¼»·ÇZ·›UÕ—Zž{ûŽöGþ\wÌm¨öÞæC9î?·rà€.vSß~¤‹ÎoídYžÝp–R)Ôr•Že)•B.'bùÖeõ©ûfn+lÚ³]m[H½­4óp75ÎXýlé˜]œ–š{™<<ðûSq» õÌØ‚ G.‰k0k^#Nf| à<©‹«]9~Å•]Ÿ=ã/×­êºð‹"În9+÷šÔÌ–P>[ñóœ›îc–µ·ÈÍÀ8fÎnvB¤"x@ þƒM[r´uz•——Ųlé[ÔÓO¨,J‰, ÃÌM-ì­ ¢ |¯Ÿ&=[1{ÐQÓ†3çº~I´¦M=»|ìŠ|Æ¢F¥ëöâÀ;™³qá”ù̉Ù5ìÕ½»ýŸÑÂÚc×Î×.^?sÄLR»ç­#½ðî>K¸MûyKâf/[;ç¼È­MïîÕcÎ}º ‚š£7üË×-¹[fU›™˜¼ô¯OpoWzÌî{jàºõ5\Ò²ò/¸„cØwɈQk¬U‹ú8Õ°y›l ¦â mÒ³3dûN1„ áFÛÚvï|ªTßÛF×Ü`fNb„û +× y»~À(®IÇ BÞÛ2ãxîóð¾vˆ?Wá9¦ÞÛÇNÚ6rYsðrR]P>ß7ÿHº‚°òðÛ‚¾Î—«S,Y=kèn-iU·û‚-£½ zÿú& ÆŒn4eÃøa;¤­×œX2qÇrÑò­kGPaéÙ¸óG¾M­/w]ƒB³±ïÀcù/G¶Ù/é½óø oq½ ¿OXºdÍØ‹JŽ_ߥKÚYã’–³W&.Z²xôA½À¹É¨µS˜aÝV,LŸ½fÖàŒI•¶¿®QÃX·ÕæVýé÷¹ ¶ŒV‚[Ôè1y?€X†¬|»±Å7ƶ[ðfv ŸùׂƒÞ.&@Hj׿­ ž¶7_"—×Mª-4Ò@ÒÖß‹ >°ìD‘žÖì:wó?ŒJMI,fr®,såõ÷¬ûï>2¥úÇ~…kÝü]OÙº`—L "—F#×þÚÏ™Ãʲbr˜âœMcï½iZn»°ÄßH­X¹À¼„üó«Ìõ!k"*H?üÒ·o_ú«c_/öú>ɪ°™]§Â²ËËýDÈó*ž³ûT±‹ÎÑVê»°“9¯rª—ÉÌÊlݲ5røþ¿)$$dq¼äÿŒÈèÃ% süø‡ð¸¸[¹Ó¯ÜŠœ0¡ÍdÂà@âa¸lÚÒ?¥‹.x$ÎçTîä+‡ŠK[B Ä—c°›Ä)Â˯ÞAfø^H Öèø•yäKb:ÎZLT„xPkÔ|ù @ ñ€@|~”¶„0FzÕˆ,437 x•ôJ”š¼‚â¾ß~£Z£–Éd¶¶¶ÈO‰âKA#æ§6UYH8þ47Q]ißPÛÞUW•ŸŸ”ò¯L„½½«‹+ò@âøRPÚˆò8ã;yï„,@ ˆÊÚlaèúPÚ@ „a€F† p@1JnÓùˆ ±´LU±8!ÓØ^QßFY™vbÁÞξ¶Om’D@ ñ€@|(m aÄl:q) ð²w”V쎩مêðÄ|6£¾UQe1AYP¯n=ä*ÄñúPÚ¹Kû{Ù ¼ôBª‚Šãéd§¬Dâ¦iN—•…Ä@ ñ€@|)Æ:àp ¾nßÜ^€6•3Bd*ÜÖœŸVáÊÀLÄ+Ð8^™&¹±, hƒi@âørPÚ¸ÑS¬žúÊPO³P ÷Æ0¤‰â+õ^Ú’R­|øänâËx†¡?øljª®õê4Úx=ÿ¯˜‚¥½ÜýÜÍÑsD”‹Žfµó=~ˆ Tá#‰„ñb°áÏh4_¯€»rišÎÌϲéa³šŽæ|vÓÅø=¿ø£G‰(=Íj¿ËȃŽ@#ø }†KiÂR©Š0¨‚ed§:ÚºÚÛ;ØÙÙp˜Jlÿ 7mYÂ!x8Ááaú±œlÑ•¡Mº­ŽÕþóà3;tçŒ!}[ýzOù- Ç<Ø1{PÏöþ¾ÍzŽ\’úéb²Ê˜ Gwo×Ì7 0°ÇÄ wóJ' è²n­›Ð§y` oëþS÷=+f*™‡ëiVGUø¡•? ߨ5q}£y —-\³çLx.…A°%ñWü±|Ѳy¿ýyä^¦#‚ÀiÙÃc[/Z¶pí¡Û™zœ ‚ÀÕq‡Öì~P„ßìƒI%7 ô-=¶í>é;¹ßjƈâæè@ßaòcªAY}QäŽ!-»'ëÞ¼vò¨}3û5 ômÚcô¶0Yéý2…‚'unèЪnjý/ä¥gõÙ7ÖëÔÔ7 Q‹A‹N&©wê«I9»`p‡ú~G®¸–õq­]¾Ý€Q§_[غÙÄ;ŠÿH³LËîœÒ¿cƒ€ÀÀž“6…æ½÷Òy×vñ øùœŒåƒio^Ø€@߀Ñ×JyÄá…:·ð hÚnäšKéïÕûLñãÕ½}{ï~©„!ƒF†®ÀðÒ–‚ð…\.÷ÃÒ*©‰{úÛÇdѪ¸„„?Æ46‚Ö"÷êÂK_ ýeÍR‹¹ñ¾Iàœÿ<†©óã”QÕÌ51gWm\4ÛÉgO/ûrûÆ™’øð|§ÎãúHÕϬÞ2s¡ÛÉ Mâ·M˜sÚaø‚àQòéå+¦Îµ>¼.HZ‰zDôß%m‰)~‘­ð´hÕûçÎDî‹ëgÏŸ0qÛNš{eï‰Ç–-z¬Ê{õøô™ÃÇ-~\[¤Œ¹’íõóÜ–‚Ç;6œ{Vgl#)¡Ï û+Û£Co+ñ}G(ú#m€sL]?Þ‹Td=:¼6xÒ¯§‚túíÏ{èü%Œ‰ñô§éb7 ì¿?p |$+¯›¶6ªÁ¬s«^Y±xÖ|·£‚, ¯/™²;·ó¼-­ÒŽ-ù}ÒZÓsý™§¦Ï=K \´»©0rÏ‚¥ÓwzëÅ#D›ApŸËi½*êA²’$9¹rzìŽ}9§²Å콇áËÖó÷´ù÷ZÄØÕ<×çÒÂ7 ðÇ}·ïÜ´ßö˵ñ hÑcþÅ—Ù÷6Œé\? Y»q»#,€*lf›F£·o›=°i@ oÐðßn½¢@õç†gõ—l^48(°nÿ€ÚV¥-EÑ“?§þØ0 зy÷{4­7äÌ+@ÿêæÆ_Ú7 ô hÛµß&ï(Ø¢«£t\tdßü[ú6ê>n_Œ’àyŒ\µ|rïÖ¾õ[ö5  ö*¥Pÿ‰2v]Ölœ5¬C Ÿ«¡“‡TÓÇßOÓjΜÍt9}`óšÕýºLXÐMpÿðÜJÕs¬§Y]Å”EÿF–Ij6ö¨æÝ¸} WLž«&÷IxMÛîÍ|\<tìíÏI¸— ÄØ¢ÔÒÁÃ^È“¸»‰ŠR YSÅ_y*hÞÊ]DÿòÈiæìáåUïÕà9ãk³1·b•ê'³Û4ZÚ÷KgÔ ÷î—zUØÌ6'9òÛð¾íFoxTÄP{5鳿äö =›û´è>çÌK-PiçV/>’JAù M Y9¨]#߀Æ-{÷oÐaÁsµa;·ú/GÂCO­òŽWEŸ¸¥ôûe\?¯:mGLkË}x”k70ó–[oÝ»»©µèù€Lá£?&ÿØ( зq»®c7Ü®ô[úŒ«§£%=gŽh]׫v»Ñs‡Ûǹ”¦V³wú¬Ç–Ïkõ^ÿ i^ŧVZµêÔªU§¦›&»wôÞzʤ.õ¼k6>w¼OÎùñj]ÚÉ9ÓN»N[ÙˉÄÿ‹Á¦-q8\›œ§=ž5¬‰}ðÕ”³³Ç‡÷o^56—Í-Ö>xølãȦíê»þ›¥äU³eº7!é¹ñÔõËwt“â@Åî:ÏtøuÕô6ú%ý­Iñ»rA_»Û—_ʦõÓ£ÌÚÏX±hB½¼#ó–…ä1š¤¿ MØKÓÚ5 ¬×¼ÇÈw^QtöéYSväÌܼsùð@Iim¯OÜ7eÚ)¶ÓÂÍ{¶Îàc‚½NtÐË.oºmÚsöo‹û[?Þ¼ìxúÛe–’§Ü>r2Ç¥s'wþ'Êï‘´¦D"©ˆ U…J–o&(­ÄøÎu #2K[™<\G1Zýw:×—$Þ¿ŸcV§‰‹׫´À5pH‚$ m È2ŠYI`€á$A–þ³¯Bo¾òjß@Ê# ò;»T+ËP4pÄ¿ 4UØ–º–3V.[#y÷¢Ýe|ú´ýÛ#Ý/X=³#ycͲ›2æs¥ŠØ:zÁ ³>Ëvþ¹~V—*¼Ê¹z2U¤¶ôrã<ÇZvLú‹,å«ÈtÚ¶¦:×´Ö¦Då+3_dƒ}M;.aVÅÓ´0*YÁa;Ã*Óbòn–Ƕ–3‘õ{eEت9»d-–ì9xxÓÌ>UÕÙ º’ÛŠQ¾[ér¬kVáåDdjA—zzîø³Î³×Ž¬gú^TɪomèôÌ݋`Ô…J–g*(ÖÁM«Ö´”Ç'•0tÞÕeƒ‰[§7‘"éP@iKC×`xiK—Ëå)4jŒÖ„¦h{4tJÜÆ+ú­Pi¯Ý×NÚ²®ó¿]LB`"äàßÄܜڲæ¬õó:JpªZêÑ‹'›®ømxU.Èáü¡‘9Ú^渨ù’ÍÓýEl]nxèì3Ï‹ýµqù”Ž­Ö{i;*öôŠßgN‘ú³Á•½Ïl†×Å•à)ŽØ}è€&æÐ±tŸ‰kF·¶Æªâm½˜WVÑXvß´aJm@CÞÍcÓ¦i;‹@Ÿ²«wß?_‚u›9›Gy Tå•¡¤Ckó²0’•?=r&áCw.?¿~lÝÞCêÿ\ßR“+£uæÚJÔQ4«ûNKµÒÔ‹¡Sn˜€À£Û˜nÞ&$kãagoÞMñ ª*Ödd)JÌà<ÛªÖÌ¥¤\ª ?)YeYÏZ}ü‰I›±’¤sÛŽ=ÌÒ‰\šôîß¡šèût;aŸT´üå­­;¢%íÖúˆ>Ù!Ž /ß17Ð@+‰8ú󋏯Hç1[Vwå[[sþÌÊ'™ÚvÕþö¯JdûÎÎY4¨™à¦9ùÇãÊú(Yž ¿Ôª¸ÀLJ™R¯ÌW±<1¯ô,!4€2_©•iHqY„МJ™’©Ñõ82ªw­„ÀŒGeiãk·Ï Zžš¦{5ô÷t«Gf•ßV‡ºÜc§öÝi>¥™“™£¥ eþ½Õãwc¶Mo!%´¹ïô_¹÷Y¶ªÄR Ëº¿wUð¸y¶gÖ5õ©ašeÿ¥n³º¸‘IQ© Z«,~±mÑŠì[ÖuräЩ(îAâø'ìjK’Ëãñ2·Km7ɃðdCo+±¿ ÑÊšHŒäñø"«*T•å+ð$¦XlЦ˜z…Y†bØÏ.®ùþOsÌ]<Ì]<ªq_}ìQa›啃0E6YÑpAðXß½Û¢&¿ÊUq%æÙ›{®aW©&tê(¶4¡¨¢ÑR8Æwt­âàêlšõxÕE-‚lœš YЫ HÃ1ËN/ù=ÕÍŠG’¸}³!¿–ö‘2…a럚w˜êª;¦µnãaÎyÔ°<ö$[C¸ó¿‡J+gäçÌØ4Á9vË´5Q RÀyóö3Á-V~؆ÿ½Ã–ý­£Ù·áuå ýD–"ÐȵeK,iJ´˜ÈEÈI…&×2DiZ &’y&æ<½B]Ö½NkK4˜ÈRhŒ%¸ÐRÚ×VZ-×qLÍyøçìöy[pÜl9Sýêù›Â.¯;¹ų̂;GTáTnk‘¶mfìn>&7§·”¨Ž ¯éäþÃúmÊ}u‰»FÏK°eyw·wïãK¤6]®cp©ÿømçÊÏ) LmÈ»#»mtss Z~ÈWSj^:óÌŒ‰·¯[3ÐÇ(gçÍkƒL€0pý†—¶ÄãñY–7«Ãsº\¸šŸžp=4üÔ‚nõªÙ°,Ããñ äÇ€ùÿ–óaŠãŸä =<$î^–ª¨ûé:Vó*¥ˆ´u·¶r5W&ÆÐo"7 Lí¹ùQ/_'F³_œÎjKJ´@Xùe°$)~¼môŒ›ž³¶ÎoeM¼LšÛ9Ú[è_½VìÙ¾~åJªø>¦u4ûf‡itJKœÒÉÏ$ßÔÚÖÚœyyû‰Ò¹AuKλӕ©´ëWd¾]¤\Œà’A’püûL˜./k‰4u¨âY·Çâß…ÌŸ| QG@0J¹Ž)œß|4‡´p•@vtveŸ/LJ<ªeQ©J@›‘9Ô°Ùø8Ù™V™Ësñ¶9Ô²c3"K3ûé¢Äx¹™§›‰1F ˜ÈÅÛJ™TV›és"2;{ÞgíÆý|%ʰ|¿Î#§/Ýyhs‹”[ÏdF1iã˜Ø89XáÉçO¦Ù´iæ(2·wssswsswsss0çàkgK.Ðo{F¨¼¨…ØÕ±,á‘IœlùÙWŽF‰;T™Ø¹¸—]ÁÕYÂÃyNNR>ŠO ¹2A&@,›¶d%µÍÎͲ·q$IÒÏÃöÌÂîgî%jÓÊÍÖŒ¢¨ìÜ,+©­AÔñ|kW‘ìÞå{‘`ù¿öî48Êú€ãø?»¹6 g#` hFRµEm°R¤ å¨J[+j±‚£¥¢œ "JQ ¢½,"­ ©Ö:U¼ÇŠcD¼ŠÕIp I_Ø:¶#ë.ÝÐÏç…/˜IØyÜç»ÿ_ög'3;¨«zí¯ÍîÛñäÒ¹/¶üË®±Üèï”®\tÃüNמSøÆò™kcæönݱ͙…Þ9mQ£á'Ön|ôž{ÞÛby%gÜÜYKÃÀv•ëV-{¸²ö O0¬}kùÕs^ï^vF÷¢Èöç—Ïy&³ï =›E>û5Ä7Ý=füм §Œ(®Ü´¡2„)hש]ÞÞן}¹2/gïÆ5KîXÝtÄ’sÛ4¬OFkëþu[$ÕX?~ÝæNù¥7mÌÞüäʵ5ÅÃ{¶ÈŽFªÞY¿yoNVõ[Ϭ~èéFgNüjËO?еn׋¾ÔbPyI^4’QÒ.ûéw*ë{ç~ðöîFŠgnîà?óiÜsìmWoyËÕ7u¸gòWŠz–FV.[òP~ïÌ «/ÙR[|x_G³Þ»VÏžsk·õoºóŇ—=¯”ö﮺xUUõÞšõuñª={ªåçgÇ:ŸÿµüË~¾à¡fçwصfö÷\Þ§EfÓ^CO©2ïÎ~×|³ÅÖûo.vÚ-Ý 2óÏ8¯Ë g/ë6¾_ÎË÷ܵ¡å€‰sñ(§äœs‹î_¹}2wkm]5sì¬uͺ™~Û%sC%£çL¯¼ñÖ)—­¨mÔé[nŸÐ§qFF—+nºbÛä_L¹î@ËžC† hy×–2šô»vúÅ“n¼ógOf¶í7jpßWî=èFšu>¹ùŸœ?iÁ‡ñH“Žg»½üìã"¡ú3_CÕÎ [kkâK'Œ^úï¯ï1õÏ Ï¨|añøÛ6Ôevéé⫆wÏk`»’HFFôˆoa¢‘Výbë–íX5Ö;}c­»{üEŠr¢á@õÆßÏ»k}f“§ ž8âÌÿã§ ÷mªXSÙûÒ¾-²¢!t9ÿ‚//ZtÓMÑý™§^pii,z”î]›¬öC§Þ¼qô¸‰“:Ý;ïüŸ\½öºùÓ~²ªU¯aŽ*ž·ö0_Ã6¯Ÿúú¤³®]‘Utúð¯ðô#9Ñ4³}ôêü‘.ßBo^zö/ ‡/y`B—‚“ÇÝiRrƈ[fŒîÛ,rÐÏ®Šû”Tþú®‰¿Ý{ »Mïa3æŒùrn¡ö•S¯Z³;äß{Èô».ïßÊÓ•¢ŒÎS*¾øw™|®²²2W“4TQQ1räÈ´zIËCj·ÃŽÒs!«Ÿ-?ïš0cÍÌ^ù©}áþM‹F}÷© V-9¯Õ§þ"©Y7ýÜï¸aõ¼Ó ŽÂkhxºN|¡[i›õÛŽÆÃe+ÈÜöþö.j`õÝ÷Þ=«ÿYéöªêÞ_yÁwV~ë7KG¹íþ÷íÓÕŠŠŠ©¯Úïèäô•¶³%8,r²#yÙGã4– !dFØøéòé~í¶Ç—­Ù]Ú£cËŒíOß·øÒï}µ¥rþO‰Ò:¦Ós¶ô õþçBÈÉŒäfPNV$„ºß=Tv¯ÿým Ö„k×{è¬ß>Þ_ž€x€ôì‡~O[:Fåõ™ùȇò…YÇ,vÌÿj¬Ç¤ÇþrÔ^CÃS 5ûâ¹Gþä!;3#·,ˆ6¬x¨ÙW“›.O- ™mݲb?#ÄiÍl‰cذ“òW¼òA“¦M bGöy滫öíøGå…=ÒkÖì«ÙµkWëÖ­½OÄ$ëX-Aáâ³;և׸ëöM5G8Âcõç´wÌݹyKƒ¹8ÑÌhQ›¢öÅí½OĤÖÁl‰cQ^NÖ•»\9Е !ñ/ø‘¾š7oî"ˆø|–T€x€¤ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8ˆHŠÙ€x€ú!˜-ˆHÌ€x€¤˜-ˆH¡‚Ù€x€Ä8¤•L—€´õñlé“ÿÚ7)++sppX8y Ýû!˜-ˆHÌl @<@RŽJ€MI*•JBȦ¼ŒsÇÏæXì9xsÓÎ=iVØnŠlÞ¼yóæÍøÃìììÇ'%%;v¬(ÀÅÇÇ'%%>|8;;»2V•~8¶ä^·N N&ÇN,Ö¼\³â­‘„MYÉç’Òr ,ŠPé¼Â#Â|ô’ Ò’2sMV!T:ƒOPÕ*ÊùƒGRÌÂ-¬~T°NÎ9±ÿx¶"yVoPËçBžRpîð‰E!gß»G}HݨPñ²¥^i`Ô-¼^T Ö”q*þL¶UÎN˵z.zƒlÌ8î|zŽÉ*$»w@hD°—%ñ’O ®æ™qê²upWú¦JñX­.°J€%5)[„Ô ÖIì/€óðòò HIIIJJR©T‹%%%EàååUŸH?ÛSY.j&êÔBÅ’s.þdRvÊÃÏÏÛ]1夜<‘T [²Ï;“–k’µOwµ%?3Ëtƒ]bjwOwUñOžžÞ^zÉZ®¥I*—¾(±Y­­¼%ótü锓U­×k%KAVÒ‰£‰¹â²OÔ\öŠJ\ù›–®†)õlbfaqáp&’$EEE……… !‹\xxxTTTÑЪÍÑÀ– 8¯X¬²BèÃ}µ’PLY‰é!ÜB"«øi„ÙÍt,±Ð˜‘Yài͵ !´Uk„{i$Ù”›§HâFޤóÍÌ<‘£¨<ÃjV÷R !ç½ÒÒ®ò÷Šb)HO+Bµ››¶ÌûsFR–UµOzÕ¼Õ¦´£‡Îæ›Ò’ÑÕ/ùD!|B³.zE1¥¼Ò7- *¾Fr 4è5Z7 p€=¹dü´ô¿íÚµ+×rjÔ¨‘™™YPP „0 Õ«W¯¼u&ð)ÙZ|œÚ§zpO$„lÌ1 !DaÒÑCI¥o´˜d­·›*-W6§ߟªÖyz‡©DaÛÀ:+-íJ îM(ù3Cp°‡J˜.d8c®Q!Ü| I­‡—^äc¾Y¹îí7•«|SkIGœÎ+ Пy¯@eƯRå_6¨ÿd9>>¾(À !òóóãããëÔ©C?àQ¿ºêÌ¡3¹Š5ë|J¡W˜»J¡(B¡¬b(É/’Zï®s¯VÓšx>%+×dµšr3s ¥zµ‹“’"Ê7Ó@Òú^aiu‚ôW©;%I£÷ô  ñu»Nß_yÖãÊßÔMùì€=+ |E‰°ùOQ”£G¦¦¦ !BCCeYNNN.Q­¤G†`cZ¿ªU2ŸÉ‘ “O÷®î¡Ò{èEv¡0…!ÜO/ ¡Xò33Ͳє§øFÔ ¬ªXòÎÇM6 cžIñ”TBa)4ÉBg5[¯žÚ$!”¢q[•Š1+ç K»R†s ¯uõl§÷Ô‹ìBQ•gñõQ[rsÌBIoÐJ—|âåë ]å›*<à¨üøu{åää¸àààš5k¥º”””ÔÔÔÐÐPooo2»'éüªVÉ:t:[6¦œJôŠŠðôóK9‘aÉ9{h_’^§’F³¢©£+<{âÔY½»A«2囄*Ow•NÑ«„Q¶fߟ-)V媟â¦ÙF%ïtü‘4½»¸oÖÙã—-­ü-_Iëê“t2Ëšuòà!Úb2+BèB¼4’ùâO¬RÅç’u¨qåoZ××ÝprÞÞÞõêÕKKK«]»vQ¯[Q÷[@@@e8Á¼T•’â´¾Uªx«„æ´Ó 9Vµw•¨a~J6 f•ÞÃÏßS«÷ ðrW[ ssóLBçP¥v¤¯FR{FT)ºfLÒx]-†©ôAU‚=u’PL¹ùF«¤»âÒ*°òŸÈ:‘ž:µl2™›wHõZažêË>ñòu’æŠß” €Ãh×®]…;öüýýË›J’T§NJºÁ¯BªÿÎÊrýÁô»µMš4aØÐž={žÛ`¾ñ÷ÓàxÈpd8á@† À 2d8áÈp À @†d8áÈp ÃàZ4Á­$ËrRJRBBB¡©ÒÀ¸éÜÂÃÃC‚BT*[ö‘án©¤”¤s ç|=¼‚}ý) \Ñl:—pNF†sT >~A­–ÒÀXÌfMª&!! çÀ M…!¡aVY–e™Ò¸EQ\ô›KRHhع”ó¶]*îoDÉÍÝ=??Ÿ¢¸v)QgqP½;ÙÙßæ_Ÿ w6$gqàÙóÉpµ •€C Ã9àd'Àe3îŠ ÿýdÈ›y£ãÞkip° n::oä{Yÿ|±¡»Í>ª2–IC Àͺöh‡?œ:ÂÙx÷¶õsr6¿Û¥èßý†¼úé÷[W?`•¬u/ÞÿØŒ£¦Ì%ñ_=Ö¥tù1±]bú¼³;uÓ˜Ø./­NwœAvéÖäí» ëÞUÓ=gÓ«sÜ,ݼÊX¦$‰Ü}s^è×ó…_ÎY¥JY ’ýïãŸìß7¶÷£C?Y|(·ìFU Žý2꡾~yÄXüŠ9ygÜÇ£Ÿ~°wߨG'ìd¢À•TB?œ¤k>üý§jK9)Ç·¯^òÍ{ÏmâÓOúV×Ûjù!±o½|w ºè³´>‘>úþ¯¼&×÷pˆÆÛ•ÆR-©{¶dÕèå!•þú¦[¢•±L¥àXÜGŸ®ÍS ÛL̸lJÆ–éã¥vyáýÎþ ˦Í|nÍÙ#šmYSŸÆÿž"•~kÚ¦©/O;Ó¬ßà·T›¤H7ð€c4b)¸êÞïNí]¥VT]7Q7ºE»®Ý~~mØÜÉKÛNêã{hÆ[Ÿ®ŠOα}P£û‡½òdóŒYÏOÚoû_è½HˆêOMtzæŒMÇSóeá~G߯>ÜÈ粎B•{XTtt˜ºô…ÂWÍü,otÛ¾Þ$gî^øùÔEÛÎä«ýëß;tÔS¬»ÆWðÈ“¡ë¿ùãH¶OtÿWß|¢™*ߥkÕ&P$ü2tèŠf·Œÿé·}yAmžûBí]Sÿ÷ýÖdCý^yÿɾ*ÅxfíŒÉóWL·¸G´}øÅÑý¢½$ëùßF>:Ó8dæƒ"57\yÉû6'WéV_w|öó“ZÄÁQ Qíñÿ=yè­ò{?ä¹ö‡Í ÚZ=^Søëä/V3‡ÞñÄ#c«ZÿðÌûÙ÷=èûÏo›OåyÕé9|ÔSm57µLmö¦±ƒfûöïdZ»|W‚Ù¿qŸ^ïßÈ[%„9aÅÿ>\ñÌ-¾¹NaMXüÊÐwLý|@u­œ¼âµ§~ª3qÆÓuõJúÚ7žXXïõn»ÇÿyµßF:mPMqÉ…P²ö¬øOÝöÍ!¢ ¢~èco˜ºâà“MZzkÚ–©ïÆ©¾þèš·æoöøæhüÊäWZy_(ÓÂýŸz?û¥¯ßoaš´ìÕg–·>¹O.û8]¦’Ÿy/ékÞ7¸ƒáè'›…6¨éý#ßÿìË©¿Ò6ç׉3w˜j<þÉsQjÿØñóý°`rL°!¼õÀ—?übÚgŸ®⻉óJÍ.꽑­ÈWAµœþåÝqú¼;û‡9 Þ:qüïg,B¹`×Üe¦öÏ¿=扺§ütáQ“P._«¢ñ;K¯¿¥µzfìèžÞ;f½òÌ„#û;¤îÙ_¿XtÒ¬dïœüúìÓM‡1oîW£Û¦ýðÁç»r!„J%„JºV¿ì—Jö ¡íúºUôþ³uÔ~=ÞùrÁü¹{«…µ`ÏÏÛ|cGÒ4eù{/¿±° Ýsc†uÕþ3gîÎ,Ea-Ü¿l·g‡¡c^z¢QÚïŸNß&ßô2…%{ë²=Ÿ{õ•¾UÅMúrO®bMß4ý½ê<ÛÚ¿x—QDG{§ï?‘§%ûÀæ“ÖŒÝÛÍB˜ö××nÕè¿mªº|B˜S'Zƒë†è…Brˆ 4‰O·(9{ç¼;+¹û›£»„–>ÝÂxzöLyý‡ƒêÛèëó·§Z©öÜ çpóRÝ"†Š­ÇR,†&}ÕŠ)'-MÕºMØÒ ñéâw­¤Ò{yûøè„­(„lÌJM©Óº¥ÿú#§ó”zúK¾±|jÎcÌ)ù_õá_Ró’O4ýeIr›—ÇÇ60vï ~¶ngÚ½‘Br»ãÍÏ^nå)„ÑÿÀ’WÍ‘£ê^¶VÖ–!4Uoô#U5J³‚eKfEŒûLcwQèµíç±Î¤Æÿ¸ÉgÀ¬mÂÔB÷{¢Õãמ(lÑ(4vÊêØò’¿î¤_»gýÕBíæá®‘TzOooB¨Ü[¿üÞSÍ=„)pßo[õyçÅÁ*¹^ÆŠå¿M³4BíÞfÔC›¸ ¡4ÐíÝ1iÕÁÜŽí½EÅ—Ù&P·ßx,J'„\˸kè×kïÝ÷ÝÉ÷~ðv§0­|®dÍõáÍk«·ìI0uÔÝt¾f·†Éÿü›:°ªt䘱J¯HßpóÕrzÁû—/PÈBç¡/ u*7o½Rž{æ×Ÿîi1ê㪻‰¬ ÅvöxºÕ,ª÷Õ+ØzlåŒ/'|à7uR7ªšªà*ná “9iý¼)_þö_²UmðÖå[},—t¢)…'VÍþtþʃ™Bëá¥Ê³DZdå²ác)ôþq£:¥ªÜÃÕ'/ yÙ'¤e÷èæ¢, ˜æàÔ<9RH*¶è5µ»¯A2𕫝•J«BIëᦖÔjI!Tz½°š‰ÎœøêÉ>ó‹ÖÍb2Éón¤¨èäTöUp|ÓÏVƒƒµ’$„Tzµ—$„$„$©µjI’„ÊÍS§RiÔ’$I*7O½$[dQôfUñ» áu•uçsdÉÇtË”„’ºx¡jï5½ O&Æ'žÎ:ö݈ß•¬õ‚gŸ8óñ—ƒZG,ÏÈ[NuóÏ›‹÷ev Ø›Ð¢Ž—Ê£ÆU[K:ùÇ•øyg!„ªäÃ%EV„æ¤]G²O|»ÿâ’÷~ý¡ÄW&µ0J~­úôj_W'DT•áû¶]÷oz—È¢†NÑŠ‹™{*œ±ZùΘ¸?I„v ÌXùɇËTÞ™ópó·Ì5/<öÃ¥ïdܸ)›ª>=aá}õTç¾>bÓ‹À-¤Vݺe¯‡»¬kË*«|ï“‘%#o’ÚÝ[{ð¢¥e9ëùë­•(NAÅŠc¡Uh¼øåëÍ=J¡õò¨ÈV4žÚ|P׬g}IÞ¥óŠß%I’$$I%•&1•Jˆ2³J#Š,K½V’L¶Y¦TüMUßö£¦Ýa’‹KlÕGïlm5î­Þõ ^žýæþwüHê¯6EFë«~¹ößcµÎj£ú†è$µoý«ý6,¸ækÓ/_`úwé\¾Y‘$•Š)×(Üýý÷ÿ|jAÑ{•œmÿûSÄÈOoîõïQ˜U¨­§Î/ÔS:–U¨H’Ji†‚çb臫Óéß­/Œ~®­oÒܳ¢ÞÈ>-BÜ…Pd¹øA’Z²Õ*„g÷%y´z¥{t€NKé;ÊMåU­†!óP‚äÓÔ¿8é)Š"^±o0qß•Öêz…R7XÞu(ýKÕ’{¯)²r#9áÒ±pÓ¹¿ÿ³6©/I–’P¬rE¾µ’{|oš{š¾Û-SX3ÍõoQ5 $BWò’ÚW§Òú†‡ûë%Ö´¶fÝêßÏkZ½¨õVß4÷ÕYÙýªê…Ú«ÿVíáqùÕªzáê5Ï›b‚Ü…’.>M_¥v€g§gÉ—Ì:æ¦Òz…FDdmß‚»ÌMkj…bL9“­ŠôÕjô9?פƒ$„Õ,s„ÓT`?\üi¹Žp=œbÍ>{숛*/ýôÞ ¿ý°>©Ñ³“ï Ñ§Ô –¿[·ÞÐLæ¯ïç²ø·BÒTõÈØ¾nûAÉ·@UÝ?oCÜÒ-rmëáUß|sÎR»b+àÕ76lèÌ÷>ß[׫ðÌž5«Žß1fÄ•úÉ´ÁWZ«ëQµï×ö›ßûØD¿;Â¥´ÃÛVoÑ>ôö K_~ô ãc3§=z•y©—Ü[Ä’¼c{~ݧkºw¹VñÈØ±~çaÉ×b¥ÝHeD%Q<**I’óo\»S_[ŸöÏO³÷vŸZ×Ýz“Ë”$kæ5÷XªHç6~ûíÙZ½^MáœzQG¡z«*™S¶©î®“4ÍîôþâÛÓ‘ƒkz©$I\ç·WX Ê·iÆÖOç|Ózh'¿³¿ÏýWßúížeOèRñA IúZ1=«­ü~ò¼C;ú^úÅv·Žãšùº™GJ+-\éÑL}tí‚…§-‚~8 …f'!Îþ3œi×ô1Ã…’GhýÖƒß}»OËá1#ŸÙÿɼOÆþàÝóÁ^5NmB}Ô#OwÜ7õ£K¼ZŽüptÿ3¾?Z iÙ3&&ôÛ#\]­ã?ÒÌœõÍ»K³eáUµeǾÕÝ¥”+¥±+¯Õõ¨üÚ™øâ¬?NxuQè‚êµí5$P+„e!dåz»oÉNlMݽ5½f¿º%/¸E |òž±Ó&ŒüݳåÈOz—Í[—Ýå­èua:·jÚ›ÓÒeŸº=Ƽ;0ÊMN¼¹e !$©0þ·IKNæª÷ûòéÒ°$ùy×i.ÎÔm[M/IBÚ²Uð·9 i‹–sÍß^i’ûÆžúlÊ”×›Ý"îôÎÐæÞMó-³Êºj½ýjÎg_}úúÏ]Ÿ{ï¹æ^*IÜõÜÐí㿞2þÏ &±}¬òÕ?Œ¥œ3ÂJõßYY®?˜~·¶I“&l‰ŠÙùïÎM[X‹å´c^ø³ûç“:ûWh«ìþè‰÷Äk ^oráiZ7»L%kÃë¿®ýÙ¬§¢t4ÔTB3ßµGÓà²ÔjuQ¸Æ{öìÙóÜó/“›ŸÞÚt".ôÃ)9× íÐÈWUÁÈ"•­P\9Þì2…d³Ç:M8àÒÓ¶ ‘ánõ¼ð£ï]Ÿüx—_ Ë8‹ƒí ›põ^X¸×Ëà6ªÂ -ÞX¸ÄÖ+èÛaÂòl( ¾R2äÒµ{b8ü2œ2 ¹¹¹ž¥7<i€ ÈÍÍ5 ¶]¦Šb½•BƒC“sss) \'À%&%†‡Úv±ôÃÝRþþþBˆ³ g ) \››[XH˜¿¿ñÍÅÈpŽÈjµ„††2š€‹PÅl6[,Û.– w«Y,›oEàj¸€ 2Èpd8á@† À 2d8áÈp àòh(‚K(Š’”’”x>1¿ ŸÒ‚ÁÝ"I©ˆÈp—JJIJKOkܰq€¥8„´ô´CG !BƒC©ˆ¸HED†»TâùÄÆ ûxû˜L&Jp>Þ>õ¢êý·ï?§ÉpTD®Üò òýýüF#EÜF[¶l¹óÎ;oðͲ,ûûù;Ó°#@ED†+·¢alEQ( à6R¥¼‡¡Ó\ GEP‘á*H–eY–)ÀCç;f©ˆ*"2\¹›¿Š¢Pu·½ù[®ÃPQ'뇫¼Š¨pßgý_Oóãø;=ìñ»[=ýôŸÝgO{8œ3¨ˆ®åÖÝnÏ´Á]ù|w^qŸ¤5uík}úŽ^™l½ìÆC3Љí:juê5Ê­`ï‡ýbûáŒÅæU§”brêŠ;ÞÛ£è߈uY¹;ß¿¯÷‡;ó•›!gþõü}ƒ¦1*Š¢(¹GõèøÂªTYp“$qýªóûa={}°9£dlDÉÛ;yP¯'¿?i²ßŠHQ²7¾PRuìÞwÐØy[RÌ7URB)#ºyÆ#³w¼·GŸ){óŠ^0ŸþöÉûŸYšl½™¶[AÀþ*"Çë‡kü谻ן×cÖcµõJöö¯¿ÚùèìÎÁêKßh:ñ×¶LuüŸ»2:w ¸JYèªözᕬHµíÛ¿r·oÇã"¾~ô¶.ŸNì_ËCÙ«¡(77¾¡È²¸°mþ¯¼&×7(²Ìu/ÀÍ5o¤æ|ð¹˜ß_ýýÁæÏE»KÂtbñ˥³ãŠHÈŠt͇¿ÿT”:ïüžÅ³¾}ó=Ÿy“î¨hý]TÝ(¶ª•eE*ÏìÕó×>üaLZÈŠRRÇÝÄê) %É+"Ç뇓|[}ªIê¢é¿3çï_0e“ßÀbª\^™N­Ù’Û䉧ïq_±'CBžõDl¯ñ[2!„åÜ’Q1¼)=móWS¾?lBÉ;¼äýaý»ÄÄvíûøˆÉk-Ât|î#1±Ï­J¯@.*Ûüj½‡»V%©ôƒ&é×ÞÜ’Ÿ·itŸØ.1͈7ʹG~™øBŸûb»Ä xfâ²cù²b<2}ÐÏÿ²öë±ßûอ©{g¼òÄ÷Çv‰‰½oÈë3·¤˜G¾x~Ò~Kê¢zw‰‰}ê—3§VÎülÉ‹¢(Æ„¿f¾6 Wl—˜¾ƒÇ}·=Í¢(ræúÑÝùß⸠Oõ‰íÒó±7âŽäÑc3{ÎÌÅKåæå–¾’ŸŸ¿ä÷_gÏ™Y毡Á€áwüöŲ3aMZ;íçÌöÃ42H7[)ÆÓ~þüÀû»ÄÄÆñLÂf‘¢(BR{GÔ¬S»nÓöý^z²ˆÿûh¾¢XÓ6NÙ¿ïý]bb»ôyæÍÿeZ•¢ZhØÂ_ŽÜ-&öþç§­O6+Š¢˜“7}õÆ€ž±]î{äÅ9ÿe×$röþ_ÞÖ¿KLl·/MZq"_.^ÂÐïÿïù‡ºÄô~|âêÓçÿ™=fP·˜ú¿ñã\ùòN3mûU=òíÏG .êEËÝ4æ>“½h<6÷‘û_Y™nÍ\?ºû#š?n@ll—¯ÍÛ›¿tâ“}b»ô}nº¢uBXR¶7決Äôüþâ#ù²¢(ׯró8\à‘ãõà ¡î4tðÒçæM™*c¥æþûÖÐ]þ&ã©u›rj?yG󀽺7Wý—Ññ÷¨GFöXóÚÌoö7¶mú·g[Žx£­OÆâªöèüñsâ[¿üùë5”ó‡þþ;)Ë*„J%$UÅŠQV«µ4w7XYÒcük{žœ¬=mD#wµÞ-så‡oÿ îûÚoFü4qêÛßÖš=XÂrè«ÙÒ=]?Ñ R­9ݸç‹Öð0^ûå¤I_6þzô‡ïþCw& ©¡S»kQ„¢Èrþyo}¸.òÉ7?kí¼î«ÏÞzÏýË ½¼daÉøëë-1¿ð†Ç±_¦ÌòûŸõ‹PÓ6‚Ó+í† IJNZöÇï1Ýc ƒÑX¸lùÒÌÌŒ  à²íc«ÕzC5§äÛö©Gë>;Æúæ÷mŸ¤îcsZûJ7]Y–¾?u[ä°G5Ô§Æÿó×ÑL‹¨¦±UE$+e†‹Ù*´^z!˲¢ kÕäãõBÜrã—}:}⼦_ލ¡a‰ŸÿsÈ#ƒßŽÍÛ0{ö§_µi6¦AòãÞ]êþàóãî 4žÜôÝG…"Ëæä5ãßšŸÞå…ÏFW·üuâçc§|1ª¡"„åèÖ|ü¹·»ïžýÅäáÛC›Ý?ä­®gã¦}?uÍÝScCÊÔA²¢¡éöHû?>üf]ï÷ºùÊŠR¼¶²¢w¨÷× KÆú…Gú=õÚ=IK§÷ÚˆeuîøâeëW_Ìø¶k«‘ÑŠ¬X~[–8`ÀèXåàO3¿zçû识ø¬ÿä:Un5 =wpŠÈ3œÚª½F<¸lä¢~÷Nå~…w˜N¯ÿ;»ö#Íü<Ü:7Õ¾ÿç¿Y:ùJ½ÔqóÛŸ¹qý§¿jﯲf”üEʱ MÕæÍëEzK‘Õ¢[ !„¨9äÛ?†Tž:ÿÙ IDATtC*;—X¹Ð=ª5´’Jíáåíí.,g–ýt°æ³3{5ó•„hÿè€å«¾ùûÌÃm¡ª5ôÓI1AEu\ïGêÅ”“–¦jyGè²MñéJ+o7¤Ò{xy{ë„(T¡EÉ?üËŸé†~ܧ¹Ÿ$j |¡ÿæáËVžêÞWo÷Þ{&ÚMˆæºÍKÇï>WØ7ÜÀqÅBtêØuùŠ¥™Y™ËW.ëtOçõÿÊÌÌðööéÔ±KÙ9ÿ7ÞüÕ„uÑû·áŸ¾¾[4xZ§µ *"sÚ‰9èVÑÕƒTÕ#ë´ìlÓŠèBÏ–%÷ÌÖo~8â×a\=wE‘|[ôyXÙ˜•–Z»Us¿õGNçÊÕE¨ë¾øÑ›ý$a­š¸býÊC)9ú_ÿH¨ûÔ̧:©„¨£Ýþí_iа$múm·gÌäÇ:D鄈|úÅ=ϼûËžg¢ƒ¡®7â—:ùIÖêg~_»üŽ×^ï_]'r•Õ‹?=”l¼/ØýÒ­$y4ì÷Pø‹ß.9z÷`w¥xm!„RòSñE¡ñëþÎÔ׋|ݦ¸ÏÚÎK˜ÃŽý²qÇñ,K}EMÕÇÆ¾Ü/\-D‹ð”†®Þp¢ƒáª\nƇ©ˆ0Ã×BˆÂÌl£"<.¿8óצ¬ZšúIBªÛ¹™ú£ÿeu¼ÛW’¼[?9¬ÍÓŸÌIl8rV‡ µ¥S!Ü£º5UMüàé§[´iݼU§.mk{ÝÔ±$Ie¯Ã(Óv”Ë\¥! ö'äÿ7ñ©‡?-zŸÅd2åZE¨ôÚâéÅæ¤ßNÿjÙ¾«Úà¥Ë·z›-VY)Û¤.îå3gžï~öÌпîlÕ²eûnw×÷¿¹Êõ¢ŠHQ„\°~lÿõB¡®Ùcä„ÇY– O®þzÊw«g ­‡—”g4Y¬²¢I­‘dY–„Ð{»K–‚¬SÇrýšÖöÅõNQ%dL:’¬ -î¾Ò…×’;–b R„¤RK²,KŠÖC§’TB‘eYHzƒV6Y.¾NM)®¥®ƒîüé“ï7ÅÛȽâ%(‰K3\iu$—^©+ Ù"K†;ÇL|¢®[É_ªôÞÚÄÍ%u–$¬IþoÂrU¿7¾èÛ4XŸµnÔ³q%£ e‡Š6º¸Ei´+SÊBÈBBÈÜ÷®Òö-ÝÕõz}×®ÝW­Zž“íááÙµó½nnn—²,ßxóWr­î§:䫽Ò_T "’¼[ž9¥Ýš [víüqÒ’…ÛÆÎ}­ïMÔäUD²¬HÚfCß}<üØ·͉ÏUéTŠ,+æS‹ÞŸ¾%âñ¾é^×_JXøÒ¨¿•âÌ’ZH’Š"[ͲPI¢x²¬E\Xú…EMÖ K(“¼d!+RÙ÷|ÉÅаoï°W¾ýíD‡’z«ìÜ„âêìâjMVDñš”,]‘eY(%( !¬VY¨u’Õx½*p°ŠÈ±2œåÜŸÿÑ~ÜSÔ?|âïqÓÄLöÂá(„0ŸÙ°1¥jÿ_mï¯B(yÿÍ=wÕžìvw{çîøzú¶ê†¨?eåݟĆ—-`ƒNýtê÷ô©O _óOâ“jjo¦î”eùÂXêEÃ’¤X­Š¢(B\+ `Û “G›Pí…51—Ž!(B˜Îï?'¢žëÕ4H/ÅjUŠ—¢’„l±– /!„¢òªé–qèTŽ\ÝOÂ’¬À¯u V9Wfye.渂+Tewu7½[×.Ý7ý½áÎ;Ú —²,Û¨æ¬XE$«¼kÝõ`­»’²ò•sþ>mlãë&lS)BHϪ5¢k¿üZâËoO_íãñV3=ìÑlD—º~¡XŠÆ . Y–­5T†Ð MFü¹|¹š·T\‰(BÑÖ’ÛŸ`l]G'„Rpf²:¸ºŸºÌÊÞç£ì²Ën¨Ò;•¨B»¼cÑ„·ieµ"EÒèU¦ÜkѪXLVåÂkÉKëW¡_º¢\øAQ„0%ì?+‡ôŒϸ^• 8SEdgΚò×äoN7}þVÞjU‹Ç†µxvÂÔßîüPõÒ¸e>·aCj•˜ŽMkO™W|cΙºb_v3Ïïþ·Þà´‡úJn›7u]›ñw—”Wú_L>Ô$¶s³*ú”í2ܪÖöטŽÏòùŸ}_š?µ«¿TîšóBóW±æä™dE.ÌÍÍ7{i|ªøü¹æïaŠ&ìž^u—Íþhšþñý,ço]½?âéçë”öÚIBí_3Hþé÷_6º5ÖÛ÷Ý‹EV´~†ÌvR|-!ÅMR}­žý^ûå/^µôJ^?/.¡zßNjùl™[”é丂ӻüÖz½¾sÇ®â*wB·Yó·BQþîÉïüÙ³G«šž¹{v§¨Ã;k…­*"q¡ëJ1ÔøÖ'G~ýáÌ*?TÃ7oïlµÖ”ã×þ¸ ÁRS)î†+©…”¢.5Y_£k3ísfÆYï É>¸ú—u9ÖÆ²,Þq_“³¦|Skx×HËÁ%Ó·ªï|½‡’|a ÅW“(¥½f—Ý“¤L¿š$<öy ü¥oN‰ZŠ,ËšÐaÆã~Zgª]xpÅO¤Yk_¼‚ÅxE_T)•P„",)ÿ¬ÙT½yˆõÔÚ9Kóš>wGhUÓõª\À‰*"{Ëp›¾üj_ÁsîP !T‡>úë³ßLù³Ã¤˜âKŠÍ ›×%‡tiRºN’oãnu­SV¬úêì*}¯ITÕjÄ}#\þÜ—³·4éWüšÍü—/üø•iF¡iúà˜ÚûJÖôÒ ¡"Í_«µ¸Ý˜¾þ½'fBüôúÓ{_œýá]žì±ëÓ/Æ­××ôѸޯ¾e÷ͯÿ{k®E¸‡D·»¯ƒ·T¦É*T¡]†>vpÊ÷“?øÙ¯^÷ؘj§·EQt5ûnpÖg£ÿðl6ü½‹ÿ@WgÀ›/[fÌûxÌ<«.¤IÌ/ÅD¨•l…~8¸t ¸­DM«XE¤ o^-mÁ¤Qóò…Ú¿~÷WÇt U “°IE$ÊtK)Bh»|õĘþ÷YµG¼ØûÔ”ÿ÷–*¨Y÷®]ƒ:&.ºAî…‰³ù“#ûNþâ»ÉÛµ!Íztlrd©,Eå×+ofNŸ5gÌò|É«fǧÆ>ÝÄC˜Ëv½•틸Z?\™Õ¡Ý¶]ôÑßB(Š¢ í2tÈÉ?~>Q„µìÕ«ÃÙç®ÕWú³B(Y»Žÿ%Ũ iõИÛùJ’èq*pžŠèFë‰úï¬,×L¿[Û¤I'ÞZ»þÝ]/Úd2±ã·ÑŽ;Z¶lyãï×éthÞ´9­ˆöìÙóÜ󿟧Ñ]–j%©ìýáÜ®¶o¹C«ÕêdÏK¥"¨ˆÈpåvaÀí«:Ë=„AEÀ•*"2Ü¥ÜÝÝó òõ:=µ'p5oÞüÆAI’ò òÝÝÝ©ˆ¸NED†»TxXø¹„s¾>¾nz7Jp…ÆÂ̬̈ð*"®S‘á.åçë'ËrâùD£ÑHiA¯×‡…†ùùú9͈*@ED†+7«Õ¦R©( À!Ȳl±XÌf³Ó|#*"€Šˆ Wf³Ù™N¨ˆ8Zxd8á@† Ãàv¸uóRW®\Iq§wï½÷:U†B 0€í œ[zzú-øÆR€ 2Èpd8á@† À çpf̘A!pqqq@†d82Àáõë×BÈp Ã*óR2Èp Ãá΀y©ιXóÎüýý¬Ÿä*” Ã9ó¹Õóf}³tO¶•²à옗 ᜇ6¢ËcÞéÛÄ“Bd8á\ž†"(—3f\òʰaÃ(‡y©ε؀=`,\óR2œ‘-&£ÑlU”’(`ŸK½Àœ°zþï',B±yáÜÍ~m<ÔÌ— » IB¡Ð®á.£­Òýi®vŽ€n&ÀqõƶÀ¼T€ Ü’ìâÊñ…è Ã- Á¼T€ à)šÐ@ŸÜmp@†àöçr¢9Èp@eÕ° @d8]÷‡ƒí0/ À à˜Ùa^*@†d886žõ~9†Sd8pMÌKÈpnºâ"á8—qÓ˜— á@†p#˜ÙpëQÚÈp@9N™Œ‚§’pΤ ”óR2€Û„J€¶ÈpìúÔrí¸ÆéåǼT€ àö¡+Èp8ÆÑd8À.0)\­Ú§9J†\ ]q(?æ¥d8€›C+” ¸(ºâPNÌKÈphËÈpÀÑÏtƒE ÷ “Ro¾ôÀ> € À10pØ— áØºâà²û3÷£Ê"Ã኎• (`§J»â¨qM6/•ý¸Œ†"(—ZµjQp€WtÂã.8ÒpôÃ*ÛIc\Ù9ç(Rv ÃÁ1RHr2@’+Nr¥ÿàœ|^*»·ã6ÝÙv7Œëá®KÿK¯'ˆb¸½[™Z¨üè‡\,É•þãLéÚi^êwϰ'ÕиpžsˆÓge¸úáÀ ÛîKƒ!€ã ÃÁ1P«ÂÎwNÈp`ïœ1±9À¼Ô[ÖOF.œÝÃÈp€ÛËÁž— gÅU‰d82@ƒ µ@†àf8è´j2À¶œðy©tÝ 2P Mm7‡y©°—úœ®S2œÇ9àpgèŠ]f·§v®od8àøxîpmN8/ ÃpÛp;@†"ÅÕ8ê¼TFÙ8²@†À©ŽäÚ'û[8X@†à§F°—f48}³„Þ2áÎÇNç¥ÒtÈpd8pR®úô$ž—zK÷1\±Lèp%Ã@t8Ýâöm…ÛÒ7Ã4ápΈLƒѸÎWµdܸfëá”BÉÝ®s»ÚÞ—X9÷Ķ¿þÞw6Û"¹ÖnyÏÝ ƒt±œ“C>/UQН_¼¤¯Ž‹«à’\¦Î’ºãu'Ü›ÅôîÝ­êðšU{³ä‹\濬<¨irÿà'ŸèwðùMË6'šÙAÀ)û@†s˜—q8>' Å]#CBj4kßÈ3õÀ±ì‹Bœ9ýLº*´ATˆ‡Nï[½aocrJÌØ.W²5æ¥d8Ç`ÎLÎÓúº«„Bíê#å$fYʾCëWÅÏzæŸ]§s­B)H9_àY5ÜÀå‚€ë¸b@¤·¨¤cÃꦹÈõp²9߬¨õšâ*ZÒºiäìB‹".\ð¦òkÓ)%ní²o÷ùèMn­b[]^:;vì(ý¹eË–ì@€“œKèá£la'®vÕ#\5à !„¤ºP‘Èòe»†œw|Ë?)‘]j¦?·oÇö;6¨ÓÄÿ’"·Ñ,À¸È`¡JkÐ ‹ÉRœd‹ÑªÖ»iÊ4­û¶×4hY+ ²q‡^½[ÎýóO‚‰=€s²»y©4ä2Üi}C6Å\`VJ;êÔî¾’Õd¥2±7\Ø€Ke8T”wÚÎÍ{Ï$'صio޽Z>*aMÙòÝŒ¿ÎW4~µ#Ý2voþ/!» ?ýøŽ‰Új Btì!œóR‡Ï6®òEÕÍ»wÈ[³ué"£äݹ{c_•V¡(B(B¡¯r×ýTëwüþýYr®Ûþþv5 t÷.‚˨wK1 2œÓÓúGwêÝéâdÔvа¶%oˆ¾§wô=ìÀîq4ì@Qÿ"@†\í›—ÊT$ rh(‚r){pQ È+6y¥_Iñ^÷¯ú¹Æ¶è÷ÐC¥'¿[ðéö\ªöJXÃ~W9Ư½gö£N¸‘WÊ–ç ”X9Jõâ÷\yï½Þr.ÿ«~6\ëäãr}/Ù7´ž·“[óHõßYY®?˜~·¶I“&ø¤•+W0À¡ÜŒ3† F­œ=ñ†¯Jv‘ë—oñ×´çR½ävÿ¶]ɲË,í(*ûWüP§Ø Kƒé­Ø©n¤Ä*\ Tl]þ†ü“Ëw›¿ŸúÍñ±ôôô üÕž={žÛ`¾ñ÷3– ÀŽG»neUÎÄFÐzt¬-âòôd8à´ € p¦;n;&R€ 8Û9›:BØÛóRË•§Ëæ3‚¸‹·\»J'ÃÁÅ*àæÑÇöá€ÓímÁóRiì €³$xК"À pL5À-ØÁÈp¸ ûš— € 7… qávë–ÎK%d8…ày®\ìpÊyd8ÐþlyNef%2À™°v4/•J Ã9I ¡‡ ¸½¹0ëî-TÔ Ã(_¼l„ç¥V&5$.t°Ñ`TRžváS € ”Ed8æàÒn帉 \jãðÏKår(á(8jŒ³Û$Çu÷ ÃÁá“(UÈv‰y©°÷PУA†@ d8T"NŸh$Ð,Èp ¡ f€Ýmn®±!ÃÁaÔk®ÏÀmR‰óR+p\S €X~Š \Ž;VôC­Zµ( p*ŠRühÛF„JZ,\ýpåS«EÀ¡1/ ס¯ì¢@†lv΀ ”“ü´ÒÉpàXp“þy©·¸N«Ô‘w÷o²ƒÀU‘áÀqöTsÒp5ÌKÈp€ã4U¢K®Q=2ÎC†s€â¥\»d8š„öR×¹H½á@l`[·a^*;@†ƒ4¯]óë3R Ðz„mC?õ*4µ”żT€ €–be–*­ Ãá6£cDC Ãó"d8€ áy©€tɃ]‹ ¨<ÌK…“pá+­Ép¸}Í)PRVàlÎ|jÉçó·¦YÙBÎyæ‡ˆÚŒß Wnû¿7uW.œ”}Æ#zX]m‹“álÏÐdH÷Âï?_´ãTZnAa “Ìs²ƒÀmRÁy©¬Ëˆ5™œý0Ç&»ìøz¸“gä¾¼vÍœ±Õq—4a='ÿ6>tåœÝyl2¸@=EWåÕP2œGA {Ïp–̳ƈfUÝJ_Ðú× ÙLL@J¶.†»Ù$A¢¥¨ÉpWæ^§SèŽglÏ’!„Pò÷ÏÿtgHÇ:îl2”ÿÀæ´ p:ö:/U[ûééÏýرµÿ¤)#_Ó°“gkŒYûT--› ö‡6"óR2\e}Ú¾¿åÌ¿ÿ¼r÷™<]X“®ýzµ Ó³Áœ„¢{pÊ '„Úཇ·èm£¥Y2n\³õpJ¡dn×¹]mïË’ÍGwlÙyèLz¡ìÕ°wÿ»B4ì"•ã@EÙñýÖÌç×O}®×]MjW«Ñ ]ŸÑóvgÝÌ„KêŽ?ÖpoÓ»w·ªÃkVí½lqrÖÞ?~YŸàQÿîûèÕóžºÞÜŒ*W©:Ðfr”N×Þ±×~&%gó¨v¿ <òé7kz™Îíüe\Ç.ç6lx³qÅf5X2Çç´èÙ8Ò[%Û7:wàXv£æ¾ebZá©-;rêÝ÷p»P.ºàô¸Ž„êðÑÍåÙk†ËÛ1=Îíí½Ý°èö"CžܺW×);Fιˣ"Ë3g&çiýÝUB¡ö õ‘ö$fY„¯®ô¦äCg­nÕvÿ:÷tšQã[­i‡ŽÍÃÝØà’6¶"$IH½hÀíe·Ã…²UraƒÚ¯^=ÜŠÞN6盵^SÉ$­›F6ZÊÔ?r^J¦ÙjÕD´º÷ÁÞ=Zø%ÿ³lÍÑ<*(N*..ŽB¸|í­ñ»ÏpÖ‚ÜÜ\%úÙÞ©ãFÌÜšX¨aÍ=±ò“×Ö4y¼…ÇMŒª ½Ã²|é¾ [ -’OT³†5ƒCª5éÐ6ÜzöP’ñÒ…ì(Ãñª‰¢¦35]¥Ö/ŒA€Ðsíª€31`#ö7–šµ4Æ÷uÅÿ~çÂá~eÐn™Ô=Ö§"QUkÐ ‹©¤ãM¶­j½›¦ÌÙV¥Ö«s¹è’ÆÃS'gZ!.:%·lÙòÖF+€'à0Ϋë'Žå_qÌTeõªàRµ¾!¦³©r¸V%¬¹IÙŠW=ï²_^åì¯Ù~.1OòV Å”eÒùxëéSÜŽ¬IЄãe8•!´zM!„¦Ô#ÿ8›c’KöcM w•PC…†5þQQÞ{vnÞë×"ÔrjëÞÿµ|Tš²å‡Ÿÿõè4了°&õ<o[»ÃóÎjê„[’½¢Û‡h©TÎa(nsoP¹ç¥Òƒukp t8p†+"g¬}¡uçéG…Z£U•ìÍÞ=–_ÒûB T6ïÞ!oÍÖ¥‹Œ’{Htçî}UBX…¢QT%iÃÚÄv´¬Ù²ò—’!´aמ­ƒ¹ÉˆÓÖYá*EÞ?Ó¶Ï‹Ñ[µG´þÑúEwº8Ùµ4¬mé;üêuì[¯#{àøŽlw=YRv®^±%¯h7²¤Æïp\¬Ì&ÀyÈÊ7/•~€ w£<bc™ÿ›N|ý`åÕC]ªζçžÒ*˜kqŽoüköÊQz «;0hÃüݹl2À5ؼ±AëåbÌKÝÞ3W6šJNSúžï¾Þ«©ãÃ=~—b“^ºŸò¶vè€$ÃU\Öï÷ú>°î¢—B˜=»…Mê#ÜîPŽ5€ wU^]¾‹?œWÒ'©ô>aU‚=Ôl0ŒntÅ ÃÝ *ˆÚQl6NröæöÍ ày©ÎÖ ÌzgÖÂ+ýÊ-ú™wž‰vc«ÝôiŒ[-ÀY]c8ÏÞvxº8[†“óÎ=r$ÿ¢×¬™{Wm9+Üzvƒ 6GW@†³)C«wþ£Ìÿ-I}2äáåêzCfýôù½Þl2ÀµÑ‘̶ „°÷ûÃOÿ>æž:Ƨøéð®¹O4òbàà$˜ÉkóF…p­;AÚm†Sòÿ0¬uûgk_\¿eJ¿Zîä7œ0Àž3œœ½ûËG›Dü­úøMñ«ßï¡¥6UŽ‹á¨©»d×ÙŽÌxøžá‹“ê þ|öSÍÄ¡­›K¥ö‹níÇMâ@ À 2œÝ­QÁ¡ŸOBúfÄ}ß\ü+u§%ikî÷a«Q-Ú+gºÄ› ÕóR¯,d¸òñ¹…3gSpölݸœ‹Š"°÷ªp[ªßkÔÀ›Ñâpó`lµÂ ×á`w‡% ÄQ°ß2¶¹Žc ÃàÆiìxÝL©Gþ=p6Ç$—Œ k›¶o¨a«ÀÍb^*@†«rÆÚZwž~T¨5ZUI/½w%Ç—ôà‘©€ g§ë•÷Ï´ÅÁŸíßób´ë,à˜·Å†Š8Ó&áŠNåîÝ<ÒîܱcÇŠ~¨U«{õ#@ì#›R Ã]Ì£Õ‹ý>|~ô¬K'SH IDAT±OÞnP'9•gdT¤çm†At8w:‡½ŽRÈp壘²R6Í|¶Û̲¯Þ³8ó¯^Nó¬­[Y3R NPcpƒ“Åuƒk ³Øë½Er·O] û×™£¹ŒÕ<,l"..ŽBš½öÃI*}p‹®­ªxêØF4VØ}eà–³×~8–ÏÞóß[,Ú}:-+'·D•-`¿ýp9›'ÎÛ¶.£oó˾ê\×ÃÀuÑÅÅ–,Ãyvør×±Ïä‹_TB½\o1è p€WÒ`/;PQö:–ª2„Vñ8ùó¸AÝÚ4mԴͽƒÇ-:íY%ÔÀó]Q–n%Õ’Ìöá.Sðï{c&œn6lâ×ßÍ™ðl“Óztûð¿B¶ØóRGg¯c©ù»¦Ï·ŽZ½âƒ¦îB!úõïêßìþi»FÍjk`«á2Œ5@eàz;f¯ýp–̳¦j­«»—¾àV­U5ÓÙ › —Õ/\Ò°'ÃÝ6nµî Ú9eÁá’ÁÓÂÃ?LÝØ®¦;& ao·c©º¨¡Ÿ ü¦k½ˆÚÔðÈ?¹mݾ€çW­Ë-¸ŽÒy3•Щ`Ëy©Üæ×ö%á*ŸÊ¿óä]Çz-\¸jϹ}›ÞcûèTÓ@%aãÆ W9àÄïR”­I†»5ûš¡F§Ç_ëÄFÈ÷°¹¸¸8wÅÙÿ. j02\å2]øù£Æ+ýJ_ûá×ÖÓº€ó«³¿ gIÙ¹zÅ–7+öV]\†ç¥‚ª˜ W9t¡õÝwÏû㌹øÿ…GNßéÓ"RÏŽY&ÀÙÏjÐjç[ppÑ6àέÉËîýg`õ*;t¹÷®èðºÃÏ<:md2à:MÚ•‰ç¥ŽÎnï-¢ï3ïÐñ'~ùeÍžs…†®Oxè6n.Ú½ür™Ê¼ó'°*»]3¥àôÖ¿¶Å§[4z•1éß%Ÿ¿óú;ó²Éè¢pr¹nŒpþ‹.ìµNN^ühÓÞkÃÚ·ªæ¥)Ù}ŽÌ1fÓcŒ qçHd8[ÊÛýíÎ6ßX60LÅFºôç;sjÇ-ǼTÀÑÙkB’ôÞþU¼ pW™þŠBÀ칡*s¸J†óhñl×ãÞÿeÏÙôìœÜV¶XŽy*bÀAóSeb^*»–ó·“½…o¯c©ÙÆÏܲ.»OÓOʾzÏâÌ¿zù¸jtãª5€6(LÀÞ3œWǯöË¿dƒÊêE@yp;*2Ü-¥2„Z–/X¼ë|UB(VcN õc³'´÷d«pº3ÄJM±ÎxpÙï½E=Ò²ßúðæÁ§wgFÝžº}OZÓa³Âtì‰È^6À¼T¸ÐAê¤ìuNCÞ ·Ö˜¸}ï¦oŸ¨×hÔ¯»Nýº›Õ¨e°Û §óÔU¢C´úªÍýOo·‘G…\êŽ?Öð½3¦w˜õÌÖ5kVyõnâsYŠS Ïn]ºþ ÷¡s¢áZ’«ëlk‡>ñ€ãe8¡©úhÜñ»âsBüÃ&m^Ûâ‡ÍYµ|â*êŠF¸ŒÃñ9-z6ŽôV‰ÀöÇ8–ݨ¹ïÅ!Μ²{ùê³Õºt2ýµ<•á*@åY­®§BT¹ç±Ñ÷%ïäþ$·†!šÖ`ÎLÎÓúº«„Bíê#íI̲ß2ó\­™VýqзíÃs7³g°/¶ž^ǼTÀÑÙ㜥ðäŠO_xô¡Á/}¾.Ñ\”¯2¶My¨A“·æWl‘²9߬¨õšâ PÒºidc¡E)ó™§7.Û®j{w i¸¤Ú^ݲ-Â3ú@…f÷ì¯NÉX=´uùº6Ýjny¥ã’ãÿûŽfvÿ˜Q+”NoÿØæ&nð+©.l=Y¾¤f’óÏŸËÎÏ^·`Öº’—ÖÎ_Þ·ÿA ßîØ±£ôç–-[r\À%B'rgGWœ§Õá’ì/Ãåï[°Bô_öß‚ÿ¼¿_ltÿ£þÝ9;¾Ëg[猸é´­°˜J:Þd‹ÑªÖ»i.ìZjŸÆ=û×)žÊ`IÛ±ôÏô&½º×÷¿ôÉmp­–+õ/àLGÖíšÎeí@žÓp+X2Ï«?ÜÄW³a÷¹Sçæ¼µaï;wùÝÔ§Ö7ÄÃt6µ@ת„57)[ñªç]ö˫ݼýÜJVÁì¦Ri=|½ÝÕŽT'ÒcTy-i ¸ÁƒE0[įý’ͲJSž$Á£Ê³Ó_»É'„ÐøGEy§íܼ÷LrÒ‰]›öæø×«å£Ö”-ß͘ñëá|Ž7ÀUZ®v]ÔEMJÀd»_AŸ_[¬¤:°y÷yk¶.]d”ÜC¢;woì«Â*EêJÐÔ¾¯Lªp.\ Gëd¸J`IÞöûÏ_Iþw27]µ|QÜ!„Іµ¿¯}XE™ªõîÔ/ºÓÅÉ.¨í am/+”ໆ<˾”œl¸*pñ¦)׫ánŒÚM:1㙇g”üûKýäÑý÷sËc}ØjÀíˆqTßÎ…y©ÎÖ|b—çržà àöáš<¶&÷³pcA_p«' ÃćnÓÈpp’È64n$þÞH1Þ’¢æb8€ —?'á–ÚÙî2œó¸öÈW³Âæ;œB\\…áàö¡¡Byd8¸&¨VvÙ Ãà<d8@ågÓkôÆÙ´£Žy©p Ñû€ §ÂàPÌKÈpgÁ¤T€ 2na7RJ®ÜQÁ°5á`_geÿoï¾³(>ŽÏ>5½7RIB Eºô¢€"Ær"¯ŠÊÝÙ»žÀééyvi‚Šw*¢‚ˆ@@z DBK$”Ò{{ÚîûG A áy6ßÏ?†Ççy²;3;û›ÙM«ÁºT€ Ø%fË Ù‡úLÀ“á€ü}}±.•£‰c™ à|€ ˆï ø¶ý>ÇÂmd8p@¢µ7¹ß™óh™v‰u©.à08 ÄÅc"÷ñ\9Ö¥d8ø}I ÔhQ Ú3š E‘á€ëÖY0øáà*3wªÙ÷@…u©¸†8üÉpÀ) ­!Rd8üÓ8ϵ¦½c]*®í©d8À!cñï?¯3=@ÅÃ\K:ŠàФ§§×ýMidSê ¸H[ÎÝø´®¥‚; ·F¬K%c]ñ!ÉmÓd8­î4@ÖZÃH›±=p­±.•2p&Zß1þ{^@憽Ç8Wƒ[MAÐrcJ“$Žèë‹g‹@kĺT4ϬiŒc®e1 ™ºòÆ¡y wåœ6€ëxøƒ 4'¦÷ÿ…u©h†qWÓK«D:2`¿ãBný&a¸|’×3©æKŸ¢î¡~™d8á´VLê´€fš/g]*@†­t02€Öq&à´a7X— á@†à ˜]2GÅ‚€1Èp8Áà™:u*ëˆS ×ã¤ÂÓááÀù ™]+v9ÝźT€ û>m„9 ÃÁNŸD·kš‰›7£p7€ €ñÀ‘2àà½ýcË;Š…¿— áÀ°áÒpkÆ=‚5Z¸›d8\’Ž"ÀÕ°ó«0\ úLµcÀõ;gp¿ ¥€ Ðz´¢k©Ö’”›ö¤ÔJ.AŽØÎã‚«Ôdر7-«°Ü$¼#{ Ú#؉Q.°K­fÎZ¸íÖSÎ=ÆO™2:V“¶iÃá2ù‚7ÈÕyyÖ€.ƒÇÝÃj}{M_ÿssñ‰oÒ³+láF-m‚'i& êv§µÌÃYJó«ôÞ~Î!„кyJçʬ—z·l­µ ƒ‹K©€Ëqôõ%ü•<2œÝ“-ÕEkÔÕe’ÞI'›j­o·JÕ™ƒ怘H7V|ûÔŠÖ4Hšó%Y¾ä¸ÃVztÓÖ,Ÿ~·Åz\$Âíß¿¿ñçn¸ü¡ I’Ä4á.E£wÑ «¹aâM¶šlZ£“î7“ßreúÖ«:›ÜÍû¢w‘ۀ]„›V²Ÿz¯@WsQa,„¶Ê¼rÅ=ÈãWV®ÊØþý–‚¶c& uÔ§Š4Ë<>2œÐùtèàQt`×áÌü¼SI;WøÄD{j„­ ñ‹… W¦U+œ³ó» §¼nØÑ¹²°     ¨Â {Øg¶i-;ªõë9vHÕ¦=k¾5IÎGŒíæ¥Â&EE¡X+ +•ÚÊÄ5g>¢žxïèP=ᮽOçáS;¿0Ùù¸çáu?wœòpGõãáa€*ðô HÆ ÃÀïÇd×ÏŠ+(€ F„.{0*d8 ¥1© à ¢üá[ §NJ)d8á×HÃ0ëR2ü<^¸61’ À ‡ÀÔd8€½a]*@†p LgÈp¸²Ü—źT€ 2~h^\t¦q©ž–Šœ®¸äIKŒ(pd8€`]*@†\?\eÈpGòÇ.‰². À ®×ÖÉpÀéÇðà 4=|[K[EÀ.²A¤e#2ëRU;ò!À‘áêŒËPez£f[®¥â²=S# d¨ëRUuÈpÔáÀ‰Ã-8€Æ?!CWO†ŽàÐêq?œzÇå £„4!IÐÕã`ä:J´F¬Ke`2È. ÓÞì è.ߨhrt à µ@MŽu©tptÜweÒÓÓë~ˆŽŽn-=Eݳ¿é2ÇÅäM ô“-VôÆ Ã]ÖÝ8hV»Ì žs?AêµTüŽ!&Ýð;ϲ×ëH¹L>ãàU_‡ !˜‡Àù©y¶¼1*ÙçßÿÍÆ°.ptÌÃáwc4Õ'Îë8=ƒ–¬ë_=4ŸJƒá@Ç¡†Ò#ɵÚ0׆߽.¶1¬Ku°>„~¿ÁµT eO®Ä8?m Íy8ü!>®´ éˆÑ’x@†ÐüIŽ3+€ ‡ ¢5MÅ]Ó9 ®õAú¿0ëR9®A†¸.X4 •µgñ?¦…X— áК4žÔtÉàWô"ÌÈp€ÃÄ8žÎ Z χÃÕž!Ô½wd8 ªöÖzY— 8:®¥ᎀu©d8áÈp5`]*@†pí±. à¥ñwøcøË.¸˜‡ ÃëR2Èp€ku©d8áÈp5`]*àèxÆïyŠ)ïÐÖ-û3JÌZ÷и!#û„9K” °GÌÃ52gï^·;߯ßÄ[' ‹¬9˜°ùd5Þ V¬KÈpª‰pùÇ2Ì!ýv hÓ¾ÏíµYG2 q€ g×äšÂb‹³¿—ABG»R–[a£`€=â~¸zŠ¹Ú¢è úº;à$½³^˜«Ìò¯ß¶páÂ_½òðÃSz€ wýHRã´¤"+½ŽÚŒ‰máÂ…ä?× ëRGǵԆüfpÑ «ÉV—Ü«É*\ ®Äþýû)*T(¨P2\Ë„‹Ÿ¾º Ä,„ÂZž[!yºk)˜fžžN!P¡°7ëR©PRáœ> 6ʵ'1%»à܉Ÿwž°dž¹ð|8`—¸®‘!xÀ¸þ[7ïZfѸ…ÄÑÞì“ÔivÂ}`þ`}\\ÜUü¦„„„øøx‡.¬ß.J­ÐåW%_Åw&''ÏÚnùýïg®Ù*  Åp?××R[œR“µoÓ¶äÌr«Þ+²×ðaÝêZ;¡XÊÒ·­ÜR3ìÞ‰†º—äŠôÝ›v=W%ý:ô1¸³Þá+ñÀ޽iY…å&ÙàÙcðÐÁN’ +×V’²sç¡3yÅUÉÉ/ªûÀÁ݃$!¬%);6íI+¨•\‚:1°‡Z†ƒ¶Òcë¿Ý–ÝfÂôñá6]¹ôÀò¯~.=ÿ‚[Ümwðטòmݲ?£Ä¬u2²O˜³ º%¹&çpâÞC¹•mð°;oŽq“ÔÖt岃+–%^xãUàð{'wt®U݉F©ÉNÚ¶ãੳ0xGv4¤gˆ³$U6]2œÝ¶ÂêôÍë“*;¿¥£kÙÑ-ÛÖíòjTÍþÕ¦ÿðù†,«Rh“^æÈ†ŸR¤£¦´Õç%ý´mÝ~ÿiýýºéÉÕyyÖ€.ƒ{ûj2“vì]·ÃïîQáõU®¥4»Üµ]ŸÚÊ3ûwìY¿;ðžáÁRáþµ[Oyõ?¥-sϦM<ü§Äyª ÅÉUé[×ì)RTÜt…Brë5'vlNØìsçøöþl%Å”µó»ug}âzê멳)nFI«Ú𮯭ÃÈ)A–ú&«˜Ïí]¿O«×õõEJeÚÆµI¶žãïîê/ò’Ö­]·Ùë®ñÑÚlõ5Ý+j¤ª–m†µÙG³4Ñ7öíÒ©ÿ€PKÆ‘<“ŠvÐ)zâßs;ÃùcH®8Rà;¨WT`@X—½ý+ާY{7µ¾½&Œ×.<$¼cï±næ¼ì ›+×)räÄá½b"CƒÃcnèÛVo.­°(Ö’´¾½u  ŒìqcW·Âcéå² ά=kvTvwcHýßMVcÓBIïáT/ÐÏM/ÌùÇ2Ì!ýv hÓ¾ÏíµYG2«ÇÞI[Éá'ÝMÓ'&"¸MHX°§^6]­‹o`Ce¸–eäÚ÷ 5ª°/²–eX}:u v7ÜC»ÄúØŠó*mjlºd8{îW*òÊ·@º‘¼Þ+ÐÕZœ_-«|Ÿs+„G ‡V!4N¾¾Æšü“zŽ2ÙZkƒ¤æÊU¬U¹)ÉÙÚˆ.!N’¥4¿Jïíç¬B­{§Tq®ÌÑ£¥à—u?e…ç§—TÞtå’=_}´pá’ÏWn?^jB®),¶8û{Õ¼tAîJYn…ͱ÷±âÔ‰R>kÓ²% .þ÷wÛŽ—Ù„PeÓmdÎ9p Ð·W¯65öEZÏP?mþ}饡˜Š²+Áa6Ý+õÔ>š«ÍŠÎ¨“ê‡ÃN:a®6˪Ó6SEch8/jôÎza®²(B¨bº[©:s0ÃÐ;ÒM£”«´rÍg×~öã›6 çM7µwÓÈåÕEÛ°ŸBÒ;éäòZ«" [£råñMkS¼†Lîl”,ªnº’sÛcô.NS~Úž›Öè¼îè+U[]ÃŽJuûivì3¾µ<¯B±¹»Ç ¹¹¿¦ôDâÖMk ÞScU×t›4â²Ô=ÇE‡ =4¢ÂÆ-fô˜¼oÖnü*m¯¯«EÓyÌÄP£R¨¾¦K†³ûNTÒHõ=†" ¥•í²PdE=ûl+=ºik–O¿Ûb=4¢ÖÊÕ·t딸ҜÃ;׬“¦ôv¢I… ÙñkÔV‘}®º¼vý¿S_úñÓoÞÚ]}MW2úFDú !„ð÷w.;ûMFFÙ ÑBHRãé]û)›Í²®M\ÏŽaz!}†f|“ž^㬶¦ÛÈ”s ©È·Ç¨`ƒJO4Š){ÿîÓÞƒn›à]šôsrò®äˆ›{iÕ×tÉpvÝ\ ¢Ìd­)“E2¸èÕ}E[ktÖÉ&kýÐH¶š¬’ÁE¯†qoeúÖ«:›ÜÍ[«æÊ•ôî¾î¾þîU'–%¥÷êí¢V³Ui¬Q›Öè¤säÕû÷rG÷ú&j9·cÕåÆ)â=ÊÏ©³é6T¬“»Q²š,Âà¢V“­®F«É*¼ ŽÝr5½d«5Ù„Ð !´NžNÂ\cÕúª­é6tFe©‰'”vbêÖg¨°/’+Oì9fŽžãï£óîµê‹í‰§: S_ӽ†N¬jÙ<ãè©©È-¯»^o.ͯÒyû»ª<ù7¹GA®-*49ùù8ü2w¹*cû÷[ ÚŽ™4 ÔIj•«Xj,ŠF# ½W «¹¨°FB[e^¹âäáØÃA‹§w#wƒF£wóô0êUØtùüD…¥ä\…âæëªsñóÑW”˜…BXËs+$@w­cרG‡R|¦°nŸlUEU’›«“úš®BSÖ¾¤bŸ½‚ê=ÑXj›\#•ŒžîzÅjÎêkºd8ûø†Ä†Èé»~>q® '%11S×66Ȩ¦=”-&“ÉlSê~°ÈBhÜ#;ûWÝ™t*/?óð®}…®íc|¼Û4çìünÃ)¯vt®,,(((((ª°õU®%kë÷ë¤gædeÞñÓÁrö}t:Ÿ<Šì:œ™Ÿw*içá Ÿ˜hO5v%*lºÖÂ}«VnÚŸz:+óäÍ[Ò•ð¸H7> 6ʵ'1%»à܉Ÿwž°dž9øã4â‚­Ç·î8š•Ÿ“–¸-ÕÖ½»A•MW.=¶ç¤ˆî×ÉK£ÞÆ#"Ò£:eWÒ™Òšš²Ìƒ?Ÿ²Æ„¹Õ×t¯pŽÄè=Wô Ú   «øMééé]»v%Äé½Ãüå̃{~>˜’m î=jhG/ŠZ\ÍÉÕK¿Ý}²TV*2ýòK¶w—N¾£˜wuÆÄ}‡Žçk#Œ忨%Å”“´ïTYYÖñ”cõŽW…tkç맲ʕ$kÉé´cGN9™còé2tLßPgи†¸–¥íKššQ¤kÛwô°NÞ:Ih݃Ûè ŽìÝ“tìt•OÜð‘Ýý6Éè(rîÛw0%ËܦϘá}t’›®)sdžCRÑý®¨óD£q Žð6g&ïÙýsÒá3µ½Fìæ¯—ì·éÖÔÔ\ŧòòòÖž¹‚ER§Ù Wô æÖÇÅÅ]Å–%$$ÄÇÇ U+..¾ŠO%''ÏÚn¹‚hKA82d8áÈp À @†d82Èp`”êÌ´|‹=| áà2¬gæ÷ÐG>Ÿ\+„åäûCãfn¯øCß×äK*~ºÍ;èþ]U2€§£´rMaQÜ|_âÜí©¬q¢d´8æá´¥?Æ|-£vG|ûÞsÕ Ó©oŸå*I’SØG—§× !ç|ÚÇ5ö¡?ŹHNq/&U•'-¸¯˜›V’$¯éŸ¤Ö\ø%%‡Þº÷þ…©µB˜Ï¬|fL;7I’tþ=ïzoO‰,„œ÷y·Î~â–nmƒýܽÚMxuG±L5 ÃÀñšðÕO/F9 úêľWºÉûÿ6:~yÐ+?—š+Sæwßxߨ—÷W !Dõ±UÒKɹÇW>ÙîØß&?ùËèågMŠ­ìÀK®Ë}zsé_Ò0W“4{Ì´e¾/ì.6ÕœùzÒÙ—Gßñy¦U!ªR¾+½oíñœ‚¬o96û¡Å'¸®Zõ/}v¶Ïkÿº'ÖSïyóìׇæ~þÑ/ÕB]ðí³njåãÖåù͇žïïe+;—]îìn*(¼è•Øš#ÿþÏ™þo¼3½›·Á)dسóþì·uÞºs6!„>ôŽYcB BòŒßÛ£ %ÏLÙhÜ U2¦šwݬ½·ñ%§‘ù&!„Î+Ä«®k”¬y›ß|äï_&æ9…Çvï(ªýÅ/…Z‹N•¹EE¸×Šõ1Ò×gK¬B­«¯«¶îe­^£ÈŠBÙhÌÃhU¤úÿê½Ã|Ü'þP¬Ô±Uœ9žòßñ^uï©{“ùØ;SZÛeAjumÑ©¤_æ«Biú%£aŸÊŒ³•õ Ïœ{,Wñ ÷f € Í’àôÎ[Qêñ¬R}™ñ>ž|qÅñ ›µdÿ‡·vív××9¶¦o¶Õ”T)Î>~žÉZ¸wá‹gZ,µV¥É—˜ë'Õœ»N¿;,ñù§>?Rj1ÛöÖ£ <8¦–@†¤;{X èIDAT€f`wgïì—ûw{d§ÔÿŸ—ŽMyº»‡Nï3|¾ö/ß®x ò‚™3çîÏÌÿ‹ÿ'ƒ<îÁ^<}Ë«¼Ï8SÓôKªÞÚkÎúeSÏÍéëmpŠ˜¶*tvÂ73"˜†pM¥f'\ÑæÖÇÅÅ]ÅoJHHˆ§Ä€º_ŧ’““gm¿‚¥ëÌÃ82d8áÈp À @†d82ÈpàÚ“Ë÷½9®ÿÍÛ^$SÍÆœ¾ì¡þƒg.;c±ŸmÒQ-¨Gͱů­6ܳäùÁ¾ó4rÙþwüë—âÁリÞV/Ì9[üsѪ}gË]"‡ßûôËwu÷ü픎­øÀ× æ/ßr0·ÆÐãÅïçOÔ!Ìçv}ñÞ’•ÛR‹Ì®ƒç­~c€«B©9½iñ¼Ï¾OÌ(µݾdÙs]u¹›½þÙ¦¤“yUÂ+fôý{frŒ‹téVjOýðÆìEkÒJ%ß®·>þÊ“#ƒõMþ·5kYümóÒ/øH윕§=xÿ²‚¦/ê½÷Íßßñ𖚦¯úNYòý#ÒÊ·?üfOÊ™³Î/vÂŒ§žšÜÑUºhQX/¶ñ†è;^›{ø®WÞX;tÞ-ÁZ2hNrÞÆùßÖŽzëîçÆ|Tºô©—¬Ô 7!„µ©‹}iUÈý³÷sÍXõúO½ðß÷Æø]âäò½o?øØîè{îŸûH¨³E ð„¶üsîyít¿ûþüΣ:“iBKæ7þßâÚÑ÷?ÿnŒ¯d2†ê…0ç$¥êúÜ=ûÏm 9[æ½ùÖc1ß?ÑÙx©­6XüÔ["f½ÿIwËž/½òRhÇÅw‡(Ú€1ÿXÒ¥J©ß¡ªäEO.1º:GÞõÖG#Mõ¯ÊÅ;ßxaµ§Ñ5ö‘yKã-õ¯ZÏ­™3w¯‡Q”9X=é¯÷tOmXøæ›/útYö—ðŒ‹…ëÅ7^ã;üч¾šöñ§‡F½ÔÃ… š5gÃò£~“žêíÑ0ée>ûÝKO¯jûô¿~üÐ:!„¨=ñýê쨙ïß34X+º<:;u×ÿýwGþ¨ÉAMBœùôŠ77=÷ù?n j2ãTstÉoxõ˹7z¯R¶ëÃOsnzgùc]]ÏO´9÷xòÞRÝ¿;;%m˜ùËÑB[çKL_™2Ö­;ýàw ÑŠÇÿ8ý»Í9Óî oœŠ“ ¾í»ú6îãß;æ>æÝ¾žÎ®^]Û4lrÚ¢WOOþ{¬‹›¡S\XýÆUøq[q§û'¶u i÷¯·ëßÚ»9ᇹéÅ–ZÓÅ‹âR¯ >½ß‚—ük÷Òõ¯mî‡@%äÒC›ÎúŽf¨û·­`ã?[¬}pá3ƒüò“­º¤Jqòt® Ná=BDÖ‘SÓ¯±älßxÆÉuú èwÓ¯­Ë4 Q›ž°­Ä]Y÷ôØAz 2óÃyV!ªŽ}¿ßìUôÅŒ±ƒz=ù©Ï”ÊBIjÈ8Š¥ªÂ¬u÷v¹dâPªÎ¦8GvðÑ !„>¨[¸6'9Ët‰7Wþ²ôãífLïÖ$2 ¹hûü¯‹û?t[{Éö‡ùë•1O>?ce«ÊIüú¿'FßÞÉùREqÉ—ÜãÆÅÊ·dÔÚEu“áP K^J®>ºWˆA!”š#‹ãÜ”æÞª?ŸwœÂnˆ–Žýç«}ùÙZ‘}4µÈf®j¸ YÇœ}øœl6‡L|î£Oç½4ºzÎã Sk+ϤZÍJûi¯}´dÞ_{å~ùü“+2k ÓÎT›ÍN=zcñgoNJYü—Ù?4YLaÍNXº_ÛrÏKN\ÉÕÅUŠÑÝ©þ ZgO£µ¬ÔtÑÖìÕ 6ˆÑO nz!Ñt|ù¢ÝÞ·>2È·I¬Q*~Yúññè÷ŹÕ³\°æÞ#n›õµËýo>ÞßSó?‹â7/¹Fö¬J?UiËE¸– €JX«ŠjÝÚxëë3Ìñ}%© â‡-høÿ‹§Ütú£•ÏÍ}æÐ“ÿzlì„>1ík…³§sÓ€¥Xª*mÆîñÓÇ÷q¢kô³©[flÜtz¢w­ÆïÆ?Åìb¢sÄs¶ÎZ·7¯[Y&lÚŸn®¢ã Ý5fîšC£FxJB¥:哿í÷ܲa¾—Ÿ4’4ÚÆÉ/Ù*+—R‘´ô“Ñ3þÞÝ­é$\á¶W”ô{þöMo¸³f­ž·AŒy»IÚÓø ™ýUDNú®ÏÞ˜ù„ößÜvÓeŠâ¢¯õ öÒTVÚ„ÿõŸ#à& Dã;êõ¯º×ÖÍÙ²¿ö±­7¾÷Î=]œœ&ÍývüyùÕ_¯só§Ýw,¶MÓô#é]\´–²r‹"„$„Þ+ÜKª*®Õ…•ê’êú(£oˆ‡”Z\«w3*ÕEõ¯j\\åÓ¥µŠð”„ùìw/?ñÃ}‹çŒms¹¸¡qñq¦Š†‰7[M…YïáeümF²f­š·AõöÍ!M¿®6õ«öxMùbÈ“på–~r2zÆ«=Ü›¤=­{HÇ®!;z_?ã›Í9S§GD]ª(~ïÆ_O\K@%t®¾NÕyåÖ†ÄÒ&"*22*22*²m¸¯Qcô ósÒ!„ÆàÕ&4ØÛrèëŸÊ:Ž»áÂU©†Ð¸6rú®´ªº4Sp¢PòoçïÕɧúhb¦Y!”Ú¼S¥º ¨€ ÎQÆüý lB!WdeU¹†…¹k„ù쪗y¯ì¶…ïþ)Öåò÷ÿK®ý«ÒÓŠmBaÉ=œ%·éü›E¬JùþO?Mš~_Ϧ±L.Øúá·%}gNëxÁ$\æªù•Qßz±üe­*­‘5ZMÝ×\¤(.½ñ¶òÜ2ÅÅÏÍ..Â<*¡Œ 4%&åX´×_ú]rù‰½‡Ê\Œ•ië?]°ÖëŽO'´Ñ Q}à•q³vôzï‡wú¹†‰ïõÉ?ç¾Õí•ÛÛæÿøöêÚ¾¯Œlãê5åöè•‹ç|Øþéq¾+^ßå<ö>Þº?Ý0cÑ«‹‚f Öüx~Jð¤'º:o~yæ«i}_øÛ@}öñ!„ÆèÑÖßh:¹æË„’·Æ j@ŒQã&/_òÞŠÈûºšvÏÿ*7ê!zaNç—~s×,¼ÉW#,™+ço#ßžvÁ$ܱeíõšüùЦ)T)Û÷éÒ“‘Ó_íÙ°zT.Úôò³ÛÚŽÑ#©ôðê«+:=>$HwÑ¢‹7ϹèÆKB(U§~ÉsméfS`d8TBãÕmDxþ·[3le¸t†+Ý¿ä±÷Se½oçá3—<>­[ÝT“¢ÈÂfB¡m3ñ· çüóƒ‡Ö›\"†>òþócý5BDÝûÖkesßýзV÷öŸýàÙ¾’q³Þ}Åô÷÷ŸðÉ7îÖ9 gvr¶9u²LÎÝðê#~iÀ]Ÿ-²múŠ…Ÿ$ÜðÞ½¿šÆ2´û¿7ŸÍŸ½`ÖŒrwì”W^¿3B/„YȲPêîSÊö}¼4=ê¾ó±L!äüM~WÚç™;:95ù6KæÊù?‰‘ÿj’ö4®‘}Ú®\¾pöÒ¢á1pæ»ÏÝ®Âz‘¢°T\|ãcŒB©8´î°&nN”“]T·ÔivÂ}`þ`}\\ÜUü¦„„„øøx0®9oõŸ'- û`Ås½]%{Û8ËéϦůðÉò§.ý¸_{fË]ýàK¢ÞÿúÅ8çË¿³¸¸ø*¾?99yÖö+ø[^Ü€jhGͺ͸þŸ_¯µ»m“KïÊm;ù¶öà„\²cþ¢ÔÎÌèâl/•M{@=œ;=ð„ªÏž~}{±ýÍûªÔm¹1ÓÆ†ë±XͧV¼ôâ¶°YÏk£µ—mâ~8ÔDãÑû™u»Ÿ±¿ süÖƒµT ‘Óæïšfg5Mcp¼´Ná@†€ 2Èpd8á@†h%Zî漏3¦¸¸˜øã˜‡ À 2d8áÈp À @†d82Èp À àÈtWñ™ääd À‘2ܬíJ àúâZ*d8áÈp À @†d82Èp Ãáà8þ Q?ð ¬·IEND®B`‚PyMeasure-0.5/docs/tutorial/procedure.rst0000644000175000017500000004043213160054362021042 0ustar colincolin00000000000000#################### Making a measurement #################### .. role:: python(code) :language: python This tutorial will walk you through using PyMeasure to acquire a current-voltage (IV) characteristic using a Keithley 2400. Even if you don't have access to this instrument, this tutorial will explain the method for making measurements with PyMeasure. First we describe using a simple script to make the measurement. From there, we show how :mod:`Procedure ` objects greatly simplify the workflow, which leads to making the measurement with a graphical interface. Using scripts ============= Scripts are a quick way to get up and running with a measurement in PyMeasure. For our IV characteristic measurement, we perform the following steps: 1) Import the necessary packages 2) Set the input parameters to define the measurement 3) Connect to the Keithley 2400 4) Set up the instrument for the IV characteristic 5) Allocate arrays to store the resulting measurements 6) Loop through the current points, measure the voltage, and record 7) Save the final data to a CSV file 8) Shutdown the instrument These steps are expressed in code as follows. :: # Import necessary packages from pymeasure.instruments.keithley import Keithley2400 import numpy as np import pandas as pd from time import sleep # Set the input parameters data_points = 50 averages = 50 max_current = 0.01 min_current = -max_current # Connect and configure the instrument sourcemeter = Keithley2400("GPIB::4") sourcemeter.reset() sourcemeter.use_front_terminals() sourcemeter.measure_voltage() sourcemeter.config_current_source() sleep(0.1) # wait here to give the instrument time to react sourcemeter.set_buffer(averages) # Allocate arrays to store the measurement results currents = np.linspace(min_current, max_current, num=data_points) voltages = np.zeros_like(currents) voltage_stds = np.zeros_like(currents) # Loop through each current point, measure and record the voltage for i in range(data_points): sourcemeter.current = currents[i] sourcemeter.reset_buffer() sleep(0.1) sourcemeter.start_buffer() sourcemeter.wait_for_buffer() # Record the average and standard deviation voltages[i] = sourcemeter.means voltage_stds[i] = sourcemeter.standard_devs # Save the data columns in a CSV file data = pd.DataFrame({ 'Current (A)': currents, 'Voltage (V)': voltages, 'Voltage Std (V)': voltage_stds, }) data.to_csv('example.csv') sourcemeter.shutdown() Running this example script will execute the measurement and save the data to a CSV file. While this may be sufficient for very basic measurements, this example illustrates a number of issues that PyMeasure solves. The issues with the script example include: * The progress of the measurement is not transparent * Input parameters are not associated with the data that is saved * Data is not plotted during the execution (nor at all in this case) * Data is only saved upon successful completion, which is otherwise lost * Canceling a running measurement causes the system to end in a undetermined state * Exceptions also end the system in an undetermined state The :class:`Procedure ` class allows us to solve all of these issues. The next section introduces the :class:`Procedure ` class and shows how to modify our script example to take advantage of these features. Using Procedures ================ The Procedure object bundles the sequence of steps in an experiment with the parameters required for a its successful execution. This simple structure comes with huge benefits, since a number of convenient tools for making the measurement use this common interface. Let's start with a simple example of a procedure which loops over a certain number of iterations. We make the SimpleProcedure object as a sub-class of Procedure, since SimpleProcedure *is a* Procedure. :: from time import sleep from pymeasure.experiment import Procedure from pymeasure.experiment import IntegerParameter class SimpleProcedure(Procedure): # a Parameter that defines the number of loop iterations iterations = IntegerParameter('Loop Iterations') # a list defining the order and appearance of columns in our data file DATA_COLUMNS = ['Iteration'] def execute(self): """ Loops over each iteration and emits the current iteration, before waiting for 0.01 sec, and then checking if the procedure should stop """ for i in range(self.iterations): self.emit('results', {'Iteration': i}) sleep(0.01) if self.should_stop(): break At the top of the SimpleProcedure class we define the required Parameters. In this case, :python:`iterations` is a IntegerParameter that defines the number of loops to perform. Inside our Procedure class we reference the value in the iterations Parameter by the class variable where the Parameter is stored (:python:`self.iterations`). PyMeasure swaps out the Parameters with their values behind the scene, which makes accessing the values of parameters very convenient. We define the data columns that will be recorded in a list stored in :python:`DATA_COLUMNS`. This sets the order by which columns are stored in the file. In this example, we will store the Iteration number for each loop iteration. The :python:`execute` methods defines the main body of the procedure. Our example method consists of a loop over the number of iterations, in which we emit the data to be recorded (the Iteration number). The data is broadcast to any number of listeners by using the :code:`emit` method, which takes a topic as the first argument. Data with the :python:`'results'` topic and the proper data columns will be recorded to a file. The sleep function in our example provides two very useful features. The first is to delay the execution of the next lines of code by the time argument in units of seconds. The seconds is that during this delay time, the CPU is free to perform other code. Successful measurements often require the intelligent use of sleep to deal with instrument delays and ensure that the CPU is not hogged by a single script. After our delay, we check to see if the Procedure should stop by calling :python:`self.should_stop()`. By checking this flag, the Procedure will react to a user canceling the procedure execution. This covers the basic requirements of a Procedure object. Now let's construct our SimpleProcedure object with 100 iterations. :: procedure = SimpleProcedure() procedure.iterations = 100 Next we will show how to run the procedure. Running Procedures ~~~~~~~~~~~~~~~~~~ A Procedure is run by a Worker object. The Worker executes the Procedure in a separate process, which has a speed advantage on computers with multiple processors and allows other scripts to execute asynchronously with the procedure (e.g. a graphical user interface). In addition to performing the measurement, the Worker spawns a Recorder object, which listens for the :python:`'results'` topic in data emitted by the Procedure, and writes those lines to a data file. The Results object provides a convenient abstraction to keep track of where the data should be stored, the data in an accessible form, and the Procedure that pertains to those results. We first construct a Results object for our Procedure. :: from pymeasure.experiment import Results data_filename = 'example.csv' results = Results(procedure, data_filename) Constructing the Results object for our Procedure creates the file using the :python:`data_filename`, and stores the Parameters for the Procedure. This allows the Procedure and Results objects to be reconstructed later simply by loading the file using :python:`Results.load(data_filename)`. The Parameters in the file are easily readable. We now construct a Worker with the Results object, since it contains our Procedure. :: from pymeasure.experiment import Worker worker = Worker(results) The Worker publishes data and other run-time information through specific queues, but can also publish this information over the local network on a specific TCP port (using the optional :python:`port` argument. Using TCP communication allows great flexibility for sharing information with Listener objects. Queues are used as the standard communication method because they preserve the data order, which is of critical importance to storing data accurately and reacting to the measurement status in order. Now we are ready to start the worker. :: worker.start() The Worker process will be launched in a separate process, which allows us to perform other tasks while it is running. When writing a script that should block (wait for the Worker to finish), we need to join the Worker back into the main process. :: worker.join(timeout=3600) # wait at most 1 hr (3600 sec) Let's put all the pieces together. Our SimpleProcedure can be run in a script by the following. :: from time import sleep from pymeasure.experiment import Procedure, Results, Worker from pymeasure.experiment import IntegerParameter class SimpleProcedure(Procedure): # a Parameter that defines the number of loop iterations iterations = IntegerParameter('Loop Iterations') # a list defining the order and appearance of columns in our data file DATA_COLUMNS = ['Iteration'] def execute(self): """ Loops over each iteration and emits the current iteration, before waiting for 0.01 sec, and then checking if the procedure should stop """ for i in range(self.iterations): self.emit('results', {'Iteration': i}) sleep(0.01) if self.should_stop(): break if __name__ == "__main__": procedure = SimpleProcedure() procedure.iterations = 100 data_filename = 'example.csv' results = Results(procedure, data_filename) worker = Worker(results) worker.start() worker.join(timeout=3600) # wait at most 1 hr (3600 sec) Here we have included an if statement to only run the script if the __name__ is __main__. This precaution allows us to import the SimpleProcedure object without running the execution. Using Logs ~~~~~~~~~~ Logs keep track of important details in the execution of a procedure. We describe the use of the Python logging module with PyMeasure, which makes it easy to document the execution of a procedure and provides useful insight when diagnosing issues or bugs. Let's extend our SimpleProcedure with logging. :: import logging log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) from time import sleep from pymeasure.log import console_log from pymeasure.experiment import Procedure, Results, Worker from pymeasure.experiment import IntegerParameter class SimpleProcedure(Procedure): iterations = IntegerParameter('Loop Iterations') DATA_COLUMNS = ['Iteration'] def execute(self): log.info("Starting the loop of %d iterations" % self.iterations) for i in range(self.iterations): data = {'Iteration': i} self.emit('results', data) log.debug("Emitting results: %s" % data) sleep(0.01) if self.should_stop(): log.warning("Caught the stop flag in the procedure") break if __name__ == "__main__": console_log(log) log.info("Constructing a SimpleProcedure") procedure = SimpleProcedure() procedure.iterations = 100 data_filename = 'example.csv' log.info("Constructing the Results with a data file: %s" % data_filename) results = Results(procedure, data_filename) log.info("Constructing the Worker") worker = Worker(results) worker.start() log.info("Started the Worker") log.info("Joining with the worker in at most 1 hr") worker.join(timeout=3600) # wait at most 1 hr (3600 sec) log.info("Finished the measurement") First, we have imported the Python logging module and grabbed the logger using the :python:`__name__` argument. This gives us logging information specific to the current file. Conversely, we could use the :python:`''` argument to get all logs, including those of pymeasure. We use the :python:`console_log` function to conveniently output the log to the console. Further details on how to use the logger are addressed in the Python logging documentation. Modifying our script ~~~~~~~~~~~~~~~~~~~~ Now that you have a background on how to use the different features of the Procedure class, and how they are run, we will revisit our IV characteristic measurement using Procedures. Below we present the modified version of our example script, now as a IVProcedure class. :: # Import necessary packages from pymeasure.instruments.keithley import Keithley2400 from pymeasure.experiment import Procedure from pymeasure.experiment import IntegerParameter, FloatParameter from time import sleep class IVProcedure(Procedure): data_points = IntegerParameter('Data points', default=50) averages = IntegerParameter('Averages', default=50) max_current = FloatParameter('Maximum Current', unit='A', default=0.01) min_current = FloatParameter('Minimum Current', unit='A', default=-0.01) DATA_COLUMNS = ['Current (A)', 'Voltage (V)', 'Voltage Std (V)'] def startup(self): log.info("Connecting and configuring the instrument") self.sourcemeter = Keithley2400("GPIB::4") self.sourcemeter.reset() self.sourcemeter.use_front_terminals() self.sourcemeter.measure_voltage() self.sourcemeter.config_current_source() sleep(0.1) # wait here to give the instrument time to react self.sourcemeter.set_buffer(averages) def execute(self): currents = np.linspace( self.min_current, self.max_current, num=self.data_points ) # Loop through each current point, measure and record the voltage for current in currents: log.info("Setting the current to %g A" % current) self.sourcemeter.current = current self.sourcemeter.reset_buffer() sleep(0.1) self.sourcemeter.start_buffer() log.info("Waiting for the buffer to fill with measurements") self.sourcemeter.wait_for_buffer() self.emit('results', { 'Current (A)': current, 'Voltage (V)': self.sourcemeter.means, 'Voltage Std (V)': self.sourcemeter.standard_devs }) sleep(0.01) if self.should_stop(): log.info("User aborted the procedure") break def shutdown(self): self.sourcemeter.shutdown() log.info("Finished measuring") if __name__ == "__main__": console_log(log) log.info("Constructing an IVProcedure") procedure = IVProcedure() procedure.data_points = 100 procedure.averages = 50 procedure.max_current = -0.01 procedure.min_current = 0.01 data_filename = 'example.csv' log.info("Constructing the Results with a data file: %s" % data_filename) results = Results(procedure, data_filename) log.info("Constructing the Worker") worker = Worker(results) worker.start() log.info("Started the Worker") log.info("Joining with the worker in at most 1 hr") worker.join(timeout=3600) # wait at most 1 hr (3600 sec) log.info("Finished the measurement") At this point, you are familiar with how to construct a Procedure sub-class. The next section shows how to put these procedures to work in a graphical environment, where will have live-plotting of the data and the ability to easily queue up a number of experiments in sequence. All of these features come from using the Procedure object. PyMeasure-0.5/docs/tutorial/index.rst0000644000175000017500000000026613137471263020171 0ustar colincolin00000000000000######### Tutorials ######### The following sections provide instructions for getting started with PyMeasure. .. toctree:: :maxdepth: 2 connecting procedure graphical PyMeasure-0.5/docs/tutorial/pymeasure-managedwindow.png0000644000175000017500000015223013137471263023671 0ustar colincolin00000000000000‰PNG  IHDRW:šŠ¦ pHYs  šœtIMEà ë– IDATxÚìÝuX[ðwf{—¥AZD°±P,ÄÆÀîŽk‹^»»»ÅVÄÀƼög+6"J7ËÆÌùþØ/*WWxÏ}¼°Ìœ={ÞsÞs†r !„B!”§!„B!„¡B!„BC„B!„† !„B! B!„B* „B!„0T@!„Ba¨€B!„ÂP!„B!„¡B!„BC„B!„PqÃýá=+Ú¥V¥äf\J„áB!„B: ]ÉDg(®|IýH¹þžPg¯ì×y¹9W®\ß„B!„tŠïÇgîüoIxœ©¤â¯úò3úµj!‘HX–Åw!„B!R©R%'''êÀÑíªœï<3¸:‹ÅbŒB!„ÒA„‘HÔ´ZÅkñ·^g8üp9ß=ÏÀȪT©BÁ÷!„B!U±bÅšüŸ)á»GŒ€QG*xöB!„ÒeÖ’_*hŠeY B!„ÒqBÎO­Uú#Óš1T@!„B¨ØûÁPOB¡ïÂÄ]Y³ò\5>ÒÛœ‹'!„Šg¨€C !ôß`e‘.‡Ý~òösª¥J»T­]§v+~Ü¥5k.ÅץDŽnÎB&þ꺕çbvê6©§Õ³‹}Ê)ƨÁÐ-²+øÌ[çí}Ÿû™ÄÕúikËÿ…¯EP4üA¡BŠˆˆˆŒŒôðð (JÝ¿qㆭ­­Ž† ,˪!„PÑa’ÙxèIZÎ#ò¤ÈG—ÅØõ¯ ´ºÚ¥(u;›h?ÀÑ3·2NŽŠOYXò´{uH>U6Gý⎟ì'#C„PɲsçNèÙ³ç÷î¦Îèñðð „\¿~ýíÛ·oß¾mÖ¬™©©©Ž† 4M㻎BEˆM}|ìIè¹ùvkëá`Èa3â^ß {ÇÏժׄ Úm‘‹ßˆ2/6ÍØùÀ¢QÿឆŒBõukܲõ¤‘žFêê›(âîîžèe&\:ŽèìÆýxjݶ;)Àuh5ÔÏèöGQ‰J”PjWÞ³y“jB6îêú5â@âV×5ãÑý÷)c·&~^¥^œ=sãU‘–oîßÖÝ”Çh6—­í"{òðceX¦Ÿ_=«Ü/I¿zúÂWÑé ¤NÕ½}¹aZB¨û^SSS‡7oÞ¼zõЦi•Jõöí[ptt411ùÝ.* „N|ƒ¤½¹ûZ’ê]üjYs”™2%ÐúµÚ9{0 …BöÍ…\Îh¾1X…\¡âä·ÙçàÁêõ«÷Õºm»§+ö=—¿àñžž>U®\ÏŠ"Ífáÿ‹´3—òÒâ’__ÜÁ~Xm­–½9¹iïý4Jê\µÿË£§onØÃ2Ø ç0 „Pn 6‰DOžYYZèÕ0®½ÍçC 旅¸jŸñâC¯¹–ÌÄJVVjžBê5hdS øtrÉškÉiÏE»eG6IO.ÜO0oìײ’•f¿1ôKÜÃDZuÍ,0V@{÷îÕ^ h×®]êºwï^øBäry5"##“’’@*•º»»Ëåò_óp±T„Ò(M3ZŲ„e•ʬ¿(¬vÛ?+T€<‘ÉýH®²5¿i% Uf†œa„®­Û–{sà…B(´«(fX¬üóõc»î¼ŒÎÈyV†%D3œÌó” ‰@:ð%UF&ÏX •ö¡¥L&ãêÙ›R×’IZ|:c”%¼‹ˆ>¿qÅùìÔ%ÉðÛ!¤#öìÙ“ïãݺuûŽš½€*í»ª:–e¯^½ªŽ !!áâÅ‹uêÔù59>* „. õ-ŒR|y•YÆÑkìÂfª7{çl|( €šK0 %C`•rÍ¥IîØáë‘ßì_T²´4žöw Qf*³:¼˜Œ”t9+ä2 ·‚ö„}–oP§œá— Áw“r‡„°${Z5°,Èž8gRa@çÜ('°1ñìÐ${&ŠkhNã· BH·}W-5`Àõ7n€ªMKK+|«ûæÍ›>|WWW†aÂÃÃ?|ø@QTíÚµA´€÷U@!ÄNˆ·LÆÿ5nß¼†C)Jžó…Ä‘˜Iy£$o?޶¯n¢ŒøßãC+Î?)›üøäÉpM¶+‰:ìN™^ú‰ï£€ròiç]–wãZAß—…z†´·?ÚIs¾p8¥ìáùHŽVš6ªdÌ`3">LQÚš ñR@é‚ìf}…oå½ñwí«ÿñãGpvv®U«EQ üyó&""ÂÙÙYGW@¹ !TôÄåÛµy¾þÈó Ù« {^]šC±LvµË³mPÛøÅÕxå»3žÉÚ‡çܰª”fÓ_žØ{îÍø¶kóÛÚ~Ü¥œ¼úϧVÎ=¥ùÙ zïÁžñ'O¿VHë èÈ9x9.êbðçne, àuysxË6Kòåm´VÕùüœëKä ?$\ݹþ¤$¦³\Çú•KQ²v¡ +5­zeçƒô×Á«æ_6–ò ñ©*iƒAUõp)n„.HMM-ò2 ýlnnîííýþýû:uê¨ç'Ô¯_ŸÃáØÛÛ›™™1 £‹¡& !„PÑ#D\¹ë³{ç/Þ|ò&:aŽžii‡²ª¹è!´yãA BÏÞ|ò>A@KÌÊTmèÓÈŲ&3þKbº¦YRLl†*gjA®i Ù’þ8äÌ%€~Íöõ,Í¡½Çƒ7“?] ~àÒ£K‹Øç^$|‰Q¹×©­<ÿO|žï¸\ýEyº²暘R?°À•–kä×¾¢„ÕÚ„çÐfD_³3ço?‹HŒJí*Ö*£ H¡bI=F‘’’ò½;ªT*333kkk™L³žL&óôôT*•*•ê9å:#ô»v˜Z6¾aÆb±ßu„*úJ™Ë ø<.‡ ,£R)r¹ŠUÿQ òyšªòL¹‚!4_O_¤•‡ÄÊÓR3súš(žÄ@œ§gˆ(Ò3i±ˆKQ¤§ÊTÀéIø4“™š®â‰ÅB. „QÈ®O«K$}}¡æg†èé 9Y¿¨€(ÒReŠ„Û[ן©×¨Ñͬø°ªLY†\EhíÝh®@(äs³^ £RdÊä*ŒB¨(:ujv¸ñïþ#£ ' „Є¨2UA7Q *¹,MžÏYEZò7Ö×&Êôääüþ¢µ“J–šœ]´<=5g¾œg”§&˵­_ò;635%™«ÀhíÀªäir|ËBH‡ýà\ܶ2$J$ļ;v…©Õ{jÀà†ì¥¥ NDæ›;ijï³pXYŽÔwîöÃûö®hiQpCÑ(šži•ÖÍ^¸jÁØ:©Go¸›FT‘AëÂ(÷NƒýÜô¹†®ÍûÏZ¶jÃÒ‰¾¼°¥+ÂbYõ¡^ÞÿÊ©÷äq=í^ïš8<༨Õè€áµÒC×ïz&’roEÀ¦ˆ*C×mß¶y|ø}sV?1ëëðL¢ŽÌœ~NÜaæ¦}[ô2ûgñÜUÀfÜY·ã}iŸ^=R¡³WÝ2ï>oó†å3z×Ç$©ðr+X៷µê‹0Nȇ¾ˆç`%]u§†#„B¨p‹²¬R-æÏ\^à.¸~bÖýOòN¦À-ÿל ^¥(€FooL¼t+®•g>{s„¢ú††v¾UZä½CkÏ';v«ia`ã×È"5>ž®YÛ2äÊ«Æ À±èºdY_Gu!6½í¨Òbyîžvûö¿ˆQz—൘=µ§›2×ö?•š5¬®>(­^ºr÷mŠÒòA× ý7v­mÉ0ëØ·Æ©¹#` ÖáÉ_l>S{Ì\ßòbËf=Úrù^|3[ Õ&ήkHìÑ’XÖ´m 7{SÚÞÖÙ½1^lÿ-@á¦77ÂÓõ Áw"ñ$ „BH·BàðxêQ Zd$ ”š©Î­»ZºZP·#¨{}:«³/è¹µ ˜ÑÁ†«ŒÛ¾20øQ Ãð3CK(¾žPóŒLâýÝ+Üþ$§„âL§´z y\ €H´œKPB=Å(ù—gŸdï6÷ë°C=C¥P°•ÒícIyŸ>½ûuõó¥\i—ÎÚÅéñ(¡}ƒÚ†3—|É£†»{ݦõ]¥\¼Þ Tø$}žO׿œ".ž"„BéZ¨ƒ¢ò›æLC(._Ý”&ìw–é8pјš†"#s3C>À| Y8ï$í?cKçjæÂ¤ #{ïûêù’o¬œ·û³÷ÔÀež6ù½YûH5?¸åGT“dÿ…§/a¯k•̰“v³úgϬ¦8"Þsí‚ jŒß°Òó•›÷ï-9¾ÿÖßÛ&Õ.Eá%—?uRö¿ßØÒˆ‹Pý ^;w]ZÞ0ýéá5Wy fW3 ùŽå¥‰çv«ÐÊ*ñö‘]W2ØÚ”ÀØF’xçòçT)•ı‚µè›]ð\3g3v÷±ƒa⪂—öl{¡’VÏÛæ7r²ä>}ôœu£¸Ûû7ßͤ 3c€cV·c f-ŽèXËŠŠyëüM^§i´Ϧƒ¯å ³VCŸf.ú™^8û¶ÖÄ­R2¬˜~ɶU‹ŽziÄr¬›áDÜ‚>ɈÍß;E<ù»ïêÕ©'઩ßM.—¿zûJ©RšHMðl „ÂPá#…„‹W¼€=£»=œ¸gFÁ›²q77ü½í‹Râà5tÁà*ú€c§ ^/سpªÀ¾qÇ.µ>î”í6Àëɪù#Žë»^=ïßB+Ÿ¿>]¸}áßû¤n­Úµqøp5ï&”´þˆ¡÷æl^25D¿LãÎíÝ^/Ôk£<'.µq}Т {åÀ7-W§M/ž@/×áu;Ÿ»aãΙ!),èÛ¸{ùÙ‹¨X­BøVÕìâ÷.·=8R׿&6µààõV L@*ÊP¯¨èVC¡P(x®~„“½ÓÕW½ê{á©@!TÂQ®3B¿k‡©eã›5kVØ­¯ÖõÞçò˜¥ŠÄÞÓaûµûeO—ri¨÷BÛMÇ&Uþ½ÉcòKü=y|K“Ÿð²vïѶ jþÆZE»Oá?)˜üçÇrõæÕ¦šâÇ !„П.44tv¸ñò Pø$¼ F}¿¦Shˆzvµ;Œ èWϬȮðôÛÍ's–Ï©).Ìælúës›6ì9yëu¼ø&ejzµêÞ½mMó_‘…fÄ“–Š"ßl9ù <ÈÿIÉOUØ}PدBa¨€tHá¾…æ×·r¤+7-êNÐò£'ÝØÉæ7\ã$ýñƾƒvÆUï6lþ˜2úŠÏÏož<¸a‘Q}}íA¬@S@€Ü-f´[}–?˵y™Q›f7ʫȧžýêO”Ö2 WëZölÙà™¯[-[ÕÁ𫽾XÞ•rþFr5”ösç $¤\›4hoÙ¹+²n™’Mùfç¨ñ[®_ä“•ã—ïúk@   ÿÙ8B!„¡Â7ð‡î:†g2Z€ÂÝ‚í׳¡mYWW¸º•an]ž~ùEz'C`SŸï_²pÓÙð$bP¶ùÀYÚ•Éß\1}]È“VdR¦N·sÛ&Íl=&iFÈZO=&ê`Ÿv‡íßÝ[}ïhæÓÞÞ#ÃÒ7ª`ÖmÛÎöoÖåÚÝ_««|·{ÎΈê“-omͨT¹f³Ý_pä/–ø zÔ5 Ö­Àíÿs\v°ÇíɳN¼ø’¢EµNc¦¨o‘{ý»©Ü¯vÄÑà»1”uýÁ §wqãQ|¹¶jئ]÷bEeÛN[<¦‰yŸ_–eóiô–Å«Ðs|7§¬ã¥%V`YBeoDåj2S¹öÏ)ŽÊõ;ÉÙkÙbà°”Ò†4ɵ.ùºñžqyâÍ_K°,aó«Ùƒ%„Êýò~@¾þ+ù¡Ñ„BC„þ?5˜_+U1À+%æ0Ñ!SGoåö˜´Ð6óÑŽ©sÆl*ÔîþÄùWÆ®™VUûüzèËDå7[‡K¿5ó͙µgr51‡›t¼_ÞÝ!;TPD\<õ¡”Ïä&ÖÚ/žq[9(Ÿ¬\I5ó2²Š÷Á½Ó”A•mõåoO->c™ûñù Š:ß·÷ô.ܧۗ¬·¹êÑÁ z¶n»eÿÁKüÒίX>{U¹µô ÉÕ‹Oiµé)Ž¡m—²Âì²*êĸ }–Ìô6å°IW—Ž['ó_Ö_¶`ü¹rª|>ñImîÞiܰN" Ø”GÇ6o:qSª¬WŸa=ê™3–[¨ðïc~mϹ˜“ÿ’ìÞð´ÃJwÓÏ[GOÚÈßñÎá ëº}'uÖ¿¸~Óñgi¥*´›0¦mY1TÚ™_w³ûÏ¿I5pi7bt7—¸í×¼PÁ‹€^Á¶=–/miÎ!9a aµ–C&˜¤G7l?ñ¿èLžqyïîûÖ2ç“úâ膇ï}‘óMl #õ{ÎidLçzë) C„BC¤;Š&IƒI}yý–gÆÍ—W”€òý©íÇô¯-¥¼õ?¼þò›Ú1ÑÄ¢‹g¥2ætWÙýoIóõôø4‡£_ª”‘2޾λ»eì«h°®Rº …Jérã·mí I–é>¨<°Šä¸Žg½Ò‡.8À»¬ÀÌ«S‹Sn¦böj§a)½ªAÛ½N»rëŸí3ö︺àÈd½Â÷&Ó¥êÌ̳ûìúRMãx&N¦ðøŸä­MD ®:uçÖ4Uì©©“Îç-†‰ ž6ù0Õwé¡^5­„‰§z·Ýúõs–ÍKÉAÑÿr„„ä™ÔL²c ¸zŽÏž[A‹Ìõ€À±Cÿ×ç],Ý~qSK! !@Sê¦4ÇÄÑœ{"*11âMbêÛEÃns4—BiŸÁZŠ#s5üš!Í¡!|1¦hŠ%„-qY%£Jùü&1õí¢¡¹JKg¬Es¸êÝ(ˆRe*YBg½®¦Ì–=ʬÜÌ2.‡Ë ¼Jó²[ß„°Ä°lcÿ²ý‡Dh¹úò;e;‡LU°4FÁäm-R4°*FÓàf©<»ËëKEš ù¶ ½-·ïÛt¶ßò6Ö<=›²å€1x üº£ZñéÁ¨0¥[m+1›P“Kæû»ï˜Òݬ¿ó¶Ë,ËC°„âê[Ú99µ·f‡Ç€Ì4™’a9Ë@X–e)`‡U±ióq“ÛfÏà¸B=Î+MPCX*+à"$k2˲¬¦ëŸ°,˲ÀŠ`YæëÒ8B=úUÖ^,K²öÓD ꇳ6WG?šçc³C–ímÕû²ŒŠ¥h êYVÐäŒE䉪B!„¡Ò¡h~v$5Ú Ú°•c_û/»ÐaûÔÚ­»W Z4~¶xtÇ&ªO.¿o?¬Í³eç:¶­ã¬Ÿzïv4Ǧ……Īš}tÇÖc’šÜ§6m}§²ËU(ߨQšqôä҆¶D¼öªc®Ýµðä—é=©Ý¹Q {Œù8¼s]gCH‹yr7îëH€gîjÉn ÚuNRSð>tóÚ'J“Zê¿(£¯Ÿ¼T®Ž…2Ö’ iÎk2Ž4+e-H¤žM ‰Ôùš”!žmëñ9»÷æ”Ö¢´€Ä«Mt‘vðìÛv®¾sÝ´s’JýfŒ7Ð9¯ þÒÚE—²^Q鋦µøkxÆ–½kfžPpŒ\¼´·çsÙ.Ã;$_µT¥çàÙ ‚á‡D.•uÆœ7…ÀP!„R-ºÎý®¦–oÖ¬ž8ô»œ½x¶‹_—büU‘{ýýO¶Ú—;TøA‡‚*¹V"…IºÏ¹ñ•'IŸ‰ 2õFýÙÓZšqUP?õ}©ýßí?¬ã4·dËó¬ò[V©-è"Ì}|„ÇÏ7mÔ?n!„þt¡¡¡³ÃxwU@:¡¨J:6ûÆù6Ö³eß³L{aU Ú““YÍ‚@TöæTξy﬜ϽËQ·¿s äs伿Q_ßuùûiÝŽYñîÜéÎeÌ…™oî»MUeÃ#yï(# !„† H·é-ØþðÏd鮯výÉB²Ö¥¾Ñ|Îõ*ß¾öj¢ä«½¨|b¬†6É·´œaò/1ÌWáKÎè•ß䛯SƒQÆ=9|2U}»Z~û: A{$*÷1"„B*à)@º-@Ѭ€„4KæÓä-¨ üuÓÝÄ{ÎZﬢþ}’_›ÿë ‚t…mœ±Pß(\ƒïÔ)`U§\ç‰äM¬yÙ!„ÂPéL(Z„°?Ó9þ3꤈Ë+ì’¢+ !„B* ‚ HE*B€úºéKéÄÑð8¥;ç/!„BC¤[Ñ`Ra‰zÕ¢¯RròÌ^  Õ:¦ ÛÌ'lFåú‰YhP?T†ŽE,!„† aRQ+ø&b„ü@ã¼›åAëyHA›‘ÚåÔ7º  †pÈÔ·^ •÷'B0X@!„0T@:ŠÖëw¯ð$ü¤³ÏâI@!ôGøïî„¡Ò¡h0©(tîÐOB!TBì?¼ÿ¿+C¤¾k0à]²B!„þ{4ž¤ Ô©Gê˜ÏB!„† åŠB!„0T@H& „Ba¨€P>0 !„BC„ ŒB!„0T@H& „B•àP!õúhߎsfþWåËŸ/ó÷õöñõöñõî±1\¡x¹¡góñWS~n]ÍŒs[w\ð@²Çó:úöÞ÷Q…NQÃ$„B¡*ü×.öl\æUØØ» ÿ?x ¾M›‘cÖ‘rðÂùo¢À$„B!ñ»oÁ¦ø|yÛÊÀSOb•BëmGŒòw—r€d¾ \ºóÂË$Vß¾^×Ãü\õ˜¨#ƒŸtówÿt:ôaeY»çÔqmËŠ)í°G –ˆ¸ÍIļ԰‰Ã‚¶o¹DuïžèwqýŠgž'¨DÖu:ßÑMôùÈàÁgklüéÀ¾›’A[†%®Zü8*E\#—ÆÇ ò¢BFN¹™0¾ƒ/€I‡¥œÍ+vÛêa'aîí\±áðÝÏ™<“J>ý'ô­kÁ“ÝŸÛkº¬[?‹°§ÂS ݺL˜Ò·ª!$ýo÷òµî|Îäê[»5<¡¯‡TvmbçÍVnQ^€!ÀwÞ‚ !„Bý¿wT!óùŽ¿ç^·›¶róʱ 3Žý=38RERú¡M÷¹×/Vñã–¿—\ˆgTѧÏ'Tè2vƨ–†÷·ÌØóZ^`É”gÀÂf¥8.#¶íÝsxÛ¨ Š{+6ETºnû¶ÍãëÄú~Udк0ʽÓ`?7}®¡kóþ³–­Ú°t¢//l銰Dsßù“Ý…¢šÓ·î9´¦}Vd¥Š<8}ÖaYƒI+×m˜ÖJ|iá„]¯ä¬ìþ¶“ŠºÃ§Mìë´lÿkEúýõ ö%Ö Xµ>pÞȶ™Ñé P4 4NÉ H!„Bºæ·Ž*È^>›PiÈ’ŽÕ(pê1ºëõ!g>Ôu:|]UwêpŸŠzŽýF½º9îÐØzÕ¸¶ýfMìbͨ]:öÖ€sW"z9;hDq%zšâŠ (664èš¡ÿÆ®µ-9fûÖ85÷â»Ìn‹®K–õuT—bÓÛˆ*=!–çîi·oÿ‹U ¦9z†††"E´ºpÅû3ÇߕK]+.€Í˜wº¿ð @ kMY>¦†€\úìøØç¯“RŸÒ%ÎÕ«–±ƒSàyÎ>WßWÑ`B!„† À$ˆÈ”Ö´—¨³ˆ¸FÎŽ¢ø—ŸcØÏ¬…§µH½ß²¢u6%ª 5'AñåÙ'Ù»Íý:ìP R(ØJé,P|=¡¦wŸI¼¿{eàÛŸä”Ð@œ©â”f ˜-~ rp1RŸ=JÏÞE*û_Dk Íå©‹ãˆJ‰)e&käޠ̺m“z½­åQ½j¯ÆµlD^w_Á$„B! ŠaYày…nt¸åGT“d?ÄÓ§Fim‘|cå¼ÝŸ½§.ó´‘ÈïÍê¼ø»è«G(uÈÀµõ[°Ó9ììõ{÷/l<t¦×ºå=ìyxåå¡N@ÊþOB!„Ðo÷;Óå9†vv„gïÓÔÍlUBø™´¬¥YYKúË“H™úQyÔÃOÄÌÙ4wãZþñakéfñÍ!ЦeXž™‹ûéE¢HšÍHÂÍh(ãž½S8¶lçi#á6{DVÅä.Y`^ÖXööe¢zÑT’öîe‚ÈÆFÎ?¨E•¦=‡¬Ø0ß§Ô‡ëOY¼î ˆB!„tƯU Ê„·OÑBõo<©Mû¦Ò‘×4ì^Ë úBàÞHÇ®ÍíL ýêr§¯]{zt§òÜ7'WŸH¯4¶Ž)G Š½}ẳ»™òMèÚàt÷±uÍs­ZÊÊ3Òe*Â*eé ¡˜kd+eC/^xdíJ¹GÇ:;ÌZ ѱ–ÿòÖù›¼N“kíÍ3r²ä>}ôœu£¸Ûû7ßͤ_jg$;uîÚ3k–k¡9r¾}Ó6ŽÁ;WY¨ož|gÛ¦§ÆM––Á³¯_²*òÐÌ]I¾^n¦Ô§»¯2 ìôiH¿à?=¢ÙÊ­Ãp$5L@!„*á¡‚êѦc²~7˜·ôœÉªå3GnTñ-«·ÐƆKAõ¡‹†mXºsò€"±õì3{¸· ÍDIùßÞiû>Éø–uzÎßPš«_þrm¿ñ§’`¾ÿ Öë¶ t®×¿ûåyëFsK·^¾zÀÄÅ£6®Z4a¯ø¦åê´ée’k¤‚’Ö1ôÞœÍK¦†è—iܹ½ÛËã\›CÛÜ™·|¡KÿųN[i¿é§¯Ø0oTœk\ÑgÜÂ^e… Ëç%sŒÜÊó×íšy AâÒž]U¤–Gr”À$–e#"#ÞxŸ–ž†B:KO¢gogo[Ú–..ËÖaåƒV>…G¹Îý®¦–oÖ¬Ù¯?PUÔ‘ÁƒÏ7ݰª“¯ž’ììų;t./ä}ÄûOQŸ\œ]Œ¥Æø¶"¤³ââ_¾zimemok_<^V>³ÊgÿáýM5-诡¡¡³ÃüÃŽÍn¤J`Òûï+¸U040T(x ¤³ ]œ]ž<{R|B¬|ÂÊCôg) HiéiR#©\.Çw¡_ìæÍ›…ܘeY©‘´8åê`åƒV>Å0TàZµßÜ/²â-@IZ‰¢( „à[Ð/FùÞžú‹•B¨¤U>8ª€tBÉ\‰eYg·#¤ó½â÷9ÅÊ!¬|0T@’˜€DQ!¿­úõ¾÷£G)f£ ÿ]å“ùdy—€¸‰As=$ºøÚUQ‡ 8×|ӚθD Âʧph|ÛîD P H÷¶©î»àŽæÎ~ÀÄœÞÈ{ȉ/L>_iK›zxÖt*æ[Ùƒ)M<Ûo}¯,òok ˆwf”W³êÿF\NN»7»eûy÷2ÈÏ`“. oÙcm¸œBHÚÕq-¼Fžc BèPðïßÖ›»Õk0érBV}BÒÌkU¿ãæ× Ý­|I¹:2«òñjî×ãïí7c•?u¦€d%YüäaÇ^á·/Ì—µÿø¶‡‡¬Üü¤æøJb ¯÷/9F·ÙàïÄ×áÊX¿ÚÐÙýËrÒ¿<<¶q×”Y†Û—´¶þÑÖºŠ!E“ßIJ„­—r~ÇÅÎó|L9À’U¯ýÄáÌ¿BŬòÁQT"†¤R©:føÙkÚ¨îè‘Õ£÷,:¡L´eþeã¾íìx_m'{êJjõa£šˆŸßId@öl…ŸgƒÉa ,¨>èÑlê¥ØØK«çmy– À¦=Û?©[óêž5¼Û÷ž{ú“¯Öùzxö ‰ûïíŽ=à$"Mѱ˜}tä”›éׯwðõöé½þ•œM ?²xd‡–¾Þ>þŸ|“ÁyøÚm‡¹¸õï>ÍÚMÿ'îñú±}Û¶öõöñmÙ+`ÃÍX¥<|Ýð%OUq‡G¶÷öñíäã‡Ð ËTBäQ—6Lòoãëíã×súî;ñ*Bؤ°ñÍ»-=vpQÿ¾Þ­zO>žŽã¨Ø´eñã‡ÓÒÓ²ÉÈÈ8~âè¦-~ cOR©ÏØú—y¯æóéÅ»¼Æö­"¡~¶ò!™ïBôlY¯º‡gÝV½&½U@‘U>„ 8ÖŽÎe\ªÔí8º_yxuãu!LüÕuñkííãëÝaà”½’¢®y†ì?8±gSßÖÃׄÅ( !DsmódÿV¾Þ-»Úò(ES{°)OÌÒÅÛÇ·i×ÑKμË`5% ÞsléðNÞ>íû,>ñåö¦‰=šú´í29èYûõϹ]G›ð]‡Âe¹ÆÒ®MlÛaÅ3õƒò7ÛºµšÀ$…oÞmñÓý}}½»NÚþ8æUÈâ~|½ý†-º¬>VPÅÞÙ=±w[oŸö=g Ï` !ÿ^ͦãÇén僣 ¨¤D P4+ q,št¨ÇúùÞ&s;®ëV†ùD ç.¥”^·–ÉÁ¨{‰Íš‹\ûOn{fØòÜÇY_[¼1Âcâ¼Ò„‡š^nXó¼ÎÔsʰQOÂ.NbÀh¨ ¸)`&»wAÓGXÚ¼ÅÜIû­ Ç¯QQÄ“BçMÛÇñ›´nеüÙÅ«¦írÚÔ“P½Ø¼‰jèݳoy[7¢R«QÝ]­%Šˆ‹K–VÚ:¾×¼¡O‡îsž±¤—Ÿ#â½ '@Ëf<Û>uÞeÛ~S–×4ˆ¹¼yùÔY¢ÀEmôYP%^ÚzÓ§ÏÈÉ’7GVn[y¢ÖòŽÖ¼,Q±”Ý©lnf}òÔ Ÿæ¾b±X.Ï,ÑÄ$*%<}°,Kø–5ºüÕ§œ¹0íÕÉeko¯8¨^í8dÞ­ç4ßô+›6-Û\»êÄò1AÓg†ˆÚ Ÿîa"m÷º×@XVsaîÔ Þ#—·W=?ºxõß«Œ×«@T¯ƒÎ9ö6­ùƒMëV ½cQµu¯©M"®Ù³êBýU¾æZõKǼi·º§æí¼Ü~VÓR,!š£e Ñ hF4/B•¶?¼cÿI £CÖîž4â¤s½®£&’6¯[¿«I¿ÜKTQÁ'?ûû÷%ÏlØž<ðÜqІ6UKQu»ûŸ>»óÆÇÎuÐNƒ—-ñ1U­¶ïæ D‘O»×²8yíU©a äR´@¢o`ÀÈ$’ñòȹ„Šƒt¨fDCב]®=ú¡¹·Tó9³º ªñ¯‡Ì}ð)ÓÏJŒ—7*®Ô½F^MNŸ IJN:z²QÃÆaW/%%%6òòÖ^аð{\k߉þ»Ï~›²´³¹%§*EÜëhbѹN%'sÚÉÁµv‹"­|rúéUiÿÙ¹/ܨÁôr"B¨RÕ;t`åÉñqejT3 Hcí ŽË¨ùSQÀØ|>ú"6UpôT”Kÿ ý›Òμ;».ÅPE_ ~ ç³¢wƒ²|Û£œyäá@73œr#fŒndD1öO\<]kR@{>¤‘óÇ–½ˆ‘·4å}—(I…ެFí:þº~OÑ-’õ“æWB€kÔ|Æän®Èà_;øL6|ú`O}PZ¾9rõîÛd•+àÚôþ{LG+@u«ØÛƒÏ_y×@\ˆjW—E:]ù“P!:6úSÔ§R}³RR¼VŠ ¹Rñ)êXš[þp!E¾Q§´Ê’2 è}õ1S¼½˜èÒ×ݘÊ­E î”àûÉM½hÚÐsøØú§­‰ª:9¨‰9 {6´ØµU zƤNkÕó¬U§yˆ.?•¿GQ”v¾¬V¯«•M ²¨§Q÷ï¼L½J¡›¦©Z áiÖQSF_ݵvóÉ'± G¬ÏÏ` ”*†%Ú…š1 eÒû¹‘»fÎ]ÊÑA”-gY.GSœÀPH’*ìCCžcÏç7mÒ"ôì©ää¤cÁG!úúúÍš´ „Ú×?˲…^„DX®Û_ÍŽŒû§þÈμüº)¾»ò:6©g8~n—®¡õëxxxù6®hƒ"«|Vöw—0Žc‹¿õ© &,Ëf¾?¿uåîó/“'ѧÒ[…Ša ŠÃ¥X–¥"J%ËLþð&ͨJÐÔ5êŠGC[¸Yh:ãùV®¦lð›X…)ŠæP,ËR„'áÓ „eY b«PåžC@4u!eÞ¤‡Ç…{®ùôΞ Áæ­àÔ ¹´:œ/ЙŠeY <ŸbJ–…ìM)Ú¸Œ÷pdL¤òß«Y„t½ò)¡BTT”‰¡‘±‰)—ÇÃË¥ØP)•Ü8nTTÔÏ„ P´·`S¼Þ3ï Êgh›{ëÞ­5¹¦AîšâÃ…ó_QóÚÕ˜§y„ò ¹q## €Ãp2ÒRå¹{8f­Ù^8}îæËSwîï°yïèªâÿS7TÈþd³'î±ÀªXJì1qq_aÖž´À€÷ùzÖ×$Lô¹¥‹NÓ'¯ó«b&H¾ä^²—·AÊ?k_uê3˜8?Ø{]‡ÒZÝ[,פró•›÷ùvkç§®QÕù'‚^ X–ÍI@Ê5žNQ„a!xfNƲ[ï’Ú¼œ÷I™=O_ž~‚²ÃÚT1! C4¥Ð°*&k|€Ðú6¶ÂÄRY{# @ÿę̂¦ |Ò*Okæ ~(Pq­ë´/o¡@ØÄ»ùµW,K–mÜ¥lã.C¢ƒú® {'¯/AÑT>€âê™Û8¸•3éó˜i+æÚ-˜ÛÎNù,FRu„·‹ˆJ=ꙓç£]SÐb Snâ«O¬¥©8®IS6øi”¼¦3€È>>á˜Ùq´JÐ^¸T»lí7*{éUÚ¢I×Z‡ÝⱄP\­H“1êCQ)’“—”Ubv D“ãIr~ @õ4’5oem•øoÕ,Bzåó‡„ E E¢ŒŒŒŸ/[íÿÑôÃoëO¾)E˜€ÄD‡Î|_c¼:¥8tí!cjùÏ\p°õÖžN¼œPâü…»¶Ík”µQX£v•×.þ_rM½M³Ï÷ÛÙ«-¾Üuýü³õVyg}\ãÎNžû¤šŸOM[~ÌGñB»r&Å« ~=wK§ÛZàz‡…êØ#Lfjº‚%lfZZ†RŸkXÚPvîÂgKµlØÆåä¦ùk}ZT2R}yùÏù§Ö†;gAPÀ‘:š²N¹*¬Äÿtõàîp•´2a ÏÈZœtïÊݤ”Jb®él8µò2š°-ðˆ~'wý˜°í£ìýYsØH­õ µÆ7°ZGÅÒ×ke ‚Æ^M €{£YÇÞU>éwæµ÷k[ÇY?õÞhNé|(ªÊr:â‰ØµëÔ¾ïÿÚ:oCéLJ¥_9zêÆ‘}u1ho”Ê‘h²juš'aMªòælÙpihžòüü‘Ë©L%–¥Ljµ¬¼wãÊNC›Øªž_ûÇ# ¼„Ää” I»$Ùc_-²ª5J@¤B‡¶V£w~'²,×¢¼¥<èàËŠ2™ÏÏ8Ï”É}€š! õ %Y㪨bo_¸f_ÍœùpqKHz•aµ,lÿVÍ"ô‡W>J¨ nV–„†þïjµÿ®ø¤H޶¨.-_ý?§A½Mi Íšé¿ß?p~H“Àvš†Š—Î~¶jéi‘}õÓFÕZ¹©æ^ýá„ s`g{ÚOîr´çò•aÕzj^£^™¦ÇvL°8–5ügxÑLËùÁ5 £éK›Õwý80àñ¨Mó<¼ûµ¸¿lÝô0A™ó§·Ÿ0•Ù¾óèÒ©ÛT 2wólÙÀ€ÒêŒÚÂ{pïç+÷¬˜sȨ\s_»ˆ›@á;úõ¬û|ãòñ§ôªÕN³ßÙÊÕúí &ngøæ•}&ö±æ”|ú ±/ awLVD-Bòc•¿tM§Ø­³­OŽIÅ63f·²æ€Фò­NvÀµjúׄwç,]n·`ĨöV-J›VmÞ¤‰Ù7ëþe9s‰A¯Z¿¿üV¬Û½âϼj ¯Êá!,BKë’´vã–‰§3(}G¯þ¨,¥ö@BÎS<ª õ Ç¢i×:‡çß „ÐÞƒ{=[´z1Xº·iÓ rï§o*dÿ @’ìŸ{$VÎ3¯Ñiâ(ÏR-þ¥šEèÏ®|~ªEê:#ô»v˜Z6¾Y³f?öd7oßô¨éñó£ H׈Åbõ›ûÃ%~0áìų;t.'íÂ¥ nåÜ ^?ýbwïÞuww/üö|>ÿÙ‹g½—•BŬòÙxÓFM úkhhèìpã>æ_;ª@hõ@§§§ãEóǸÚôôôò¼¹?IY IDAT¬ÈW@Ò}EißW!ô++½ïúè1 SœÆÃ±òA+ ò¤[ýð‹/9sŠü•æÉñó¹tE¹Ò"'!ôk+ØïÎÀÊ!T"+Ÿßp·æìöeÖ»ˆ*åMð¢ù—«Ì\êWZ=õ”d¼ Ù´ñàí(Ϥ’Oß‘~ i6åÑ‘ 'Ç(„Vîm‡ ô)+)æ1C‘‡ :e•œÁ„lb±8C–!à ð ¡_¬Zµj…ÿÜQ•!Ë‹‹Ï ±òA+] ¨¯CåÛ½“&œøB,ªÒ´:z w/ÛùºâÀ)ClS®o^¿r­Í²Éž†)ÿl\t<¡Ñ© >Ÿ ܲp·ýšÁ‹e°@ù#æç:BL@úN¶6¶"> B¬=ÒY™òÌä”d;[;¬|B%°òùÕ‹¥~õ+ߩ۲Ã]bBÆžR¯™oÏÞ–Uèß½YEC ¬û=º6ãìãä:•Ÿ^xÊ­=¡[=W1”3ï~ûúúóá½*U“ËãO̰úùc.i Hæ„‘£c¢±BDHg‰D"[[ s ¬|B%°òѹ TÖpEQLò‡™†Õ­%4E,]ÌȃðØŒÒáŸÓæBŠ¢€[;+®¿IbªëqñbÒ‰Páç†J`MÓÖVÖ¥­KãŃŽ+f‰:Xù „•ކ …ÄÊ’eÀ—ðÕMOZ / ²$™J–” | _=¿ê ˆ,AÆü²—€w„È÷œUQ%0©X6ABXù „Г߀ôÕ´f¢þƒf®ES4‡ËQo@©×¢i €ærh𦰀¢èœBÐ6”ÀB!„0TÈ¿M©€”“À—Qò ¡( €U¤+@d-æ‰DTT†’P @é K%< þèð [ L@B!„ÂPA«eù¹ P”fZ3×ÈÁN˜ô*J| ”_^ÆÒæÍÌ%¦å,9—^Æ([˜qÈ¢^Çó­ËqYRPɹ™C‘¼¹ß«Ä& !„Ba¨ iNj·¹9°Š Y¦\ÉV%ÏÌ”Ó"1_âÒÒC¥“Â6]•WVÍ„oT¥EEfùö=562útbÇCAÍ¿ËpèbºXêo N¾÷‹p±TÀ$„B¡’*ä7WAñzçÈ‘! ‘“»5j½$phY½*ý'÷[µzãÔñ2®Iå6&x™ri0®;òïˆ+WM9®Z×ì>cpuCÎ3þrÝ­™ÂB!„0T(Jü²·žøUTââ°Æ/Ï£t©êÝgììž7ä(~Ô÷ý.Q .aRPP~°B!TltéÒ¥D„ êtö|¦5£<'ê]\ˆ?w¼˜€„B!¤k~ç¨*Nᮀ„B!„¡ÂŸÝ¢ýSü®E~#\ !„BH§üÒû—‰Åâ´´4ê§•„7æÏz¥iiib±øgJJ¥øiD!„*¹¡‚…™ÅçèÏiiixÞ‹“´´´ÏÑŸ-Ì,~¦uêÆ !„Bºã—& ©[‘Q‘™™™xê‹ ¡Phin)•J†ùÉh0 !„B¨d† ÃXàÌæâ„¢T*U*ÕO†‘$ „B•ÜPT*ÕO¶)Q±„+ !„BéOÒhJP{cWà¡gißy„Bé(.ž¤ p0AÛúõë¿~pÈ!xfB¿ÞÁƒ;vìˆç! úm0 „BéL@B:-®€„B!„¡BÚJÖíX•B.W2„dý€B!„0T@(%êlʨó;¶î8þ$Ä_ß¿mëáGÉ,^!]…*Ép®Ò¡hJF¯tó8!„B:GN(Y H!ôç8xð ž„0TÐ9Ì—à­îŠÀûµ•%* !„Bèð R¯î<ÿ±æiõ­ËÕhÚÞ߯–¥€*ú§’=^ê?ñRÚWùÌöqêÅ_–yK)|÷u.Z\ !„B¨$† @ñ« Ý¿ •ûöÎùã;g »ÓwÙB?{AQ?Щ뢥-”@ötÕÔ=¢Ó¸(¾±aÔØI¬«ãƒ·S@!„Ò5¿6‰â”v*ëâV½®ïàékÖ÷±{¾mEH” €È?^X1¶gs_x–ª½zdÆ“õcû¶kíëíãÛ²W@à?q*ÕÇ=ƒZù¾’«7P¼ÝÔ§íàh&ûyÄ–e]ÝÊ»¹•/g£GsJÙ—+ïæVÞÍÕÙœuvÃòã‘ ÈîÏíÔjÊ¡}óùøø¶¹áÆ—È‹kF·óñmÙoîñ¨¢ol›Ð·½·o‹ÞS6ßN`Ò¯Môõö_ýTŽOQÂ$„ÒM¸B*ü”À±eÏâ×§nÄ(Sî­ØQeèºíÛ6¯¿oÎêûiÙÁži•ÖÍ^¸jÁØ:©Go¸›iÑÀÛ&îú¥ EdXX¢“¯‡)çûŽ€•=º)mЯZlÈ´áã÷ÊêŽü{DsÞ?›î$½Ø>aÙ}Û wïØ0Ï—²péÅx(š§ƒÿ'Ñ`B!„† Bë ûæÿìÝw`SUÿÇñs3Ú$Ý;”QfÙ*KÙS6*‚7âïq=î½Ü(*‚¬( Šð0e´ÈÊꢴ¥¥3͸÷÷Gh Ø*¶¥¹4ï×ZnÓ4='Mï'ç{¾7çDòüõ“šÒ³ExX³žWßÒñeÕKU¦0µ¹ê†qý:5  ižØ3Òzüà)ÉÜghóSW³ aM_³¾ ÕˆËBþíÏ"{>úÊ´—_~å5—kÌ×½ôÀĽ‡\3ª…#ý@ž­dçw+#î»c@+sXL×17 õÙ¿|©"|ú¼òóНÿ¯½7OžúÄb¨O¦†ë*Øsöf–ùôÖ‰óœ;ìV«Ü©´ªšHØrÖÎ}çãŸwthMþ^eŽ»¬è"zŽh>÷»UÇnmª]µþtüÝþuR’¤ÕIB!|½4¦òcÉasØòö+9‘òàøÅÎ%WXíqåŠðc›ÃÅà,@ªú/àÙQ¡"ûÏa¦Mºö÷~üX‚OÕ§ô~>¥K…B8N,{íå_4“ŸýlRB„¡på=7}+„Ú°^ÚüÝêƒWè/n{×ÀºœÁK’äú!„ŠCÑ6»iÆ+ƒC*ëš$O…G5- Tç¾Öã¿}µÖ?ºwTd›p9s1¸J®òäÝ–½'C´0±[„QB–•3›´á— kuzÍœy«K:Žìâ_ßïõëBZGjsöçzU=¨@_/’ÂÅB€gGÅQ”‘v ußöäe_¼vÿ´OŽt¼õža^á}¯îmûíùW¿Y»'íàŸ,™óò“_§UvÒ‡· —S%­Ý±=eñÌggí¯¼&›&¤Ç•­Šwí*ï:²CýWI~ “ù¤¼õÌœUÛ¦íÛºâ‹O½»»\ˆÒ |Ýt@ª_t@u¢àɶI±nûà‘»„’¹]âÏ==±{„—"¨Ï#¯ß;{ÖüS!¼ÂÚö;5Tæk´Q#ï»ãÏ׿¾öä·Áñ£Çm~l]eVJÐB:ÖdTüŸL‚äÓéö™O˜ÞûñC K…60.qè‘z!*Y²ÌSçb¤A€jHíž]ö¯¾à©ÖùÆ SÅcWŠÖ?wÛÛOy#3y‰»ðÝÌËW-Ÿ4qÒßÜ`þüù“'O¾¤GcÖ¬YÓ§OçY@ ’’’XXÜ|Ϋ(óÙ  8´¦Ï.[¶ì…!µþÖ—pñ½|ê…;¼ûoINh(@P›K7*سÿi¯ßå#â Ìb£I ‚$ÕÐ]ºy: ªþ˨€JÐ *4œÜœÌ¬Ì@¿ð@ÞBn<*lÖ̬L!DdDd]î‡$Ï YYY¡A!¡a:½ž¡o4ì6›.O—••U—¨Àb¨SRR Q¡!X¬–s¤C–eYfèUHQ”Ú|™$E˜#3sOÔå[S€àÑQA’$ƒÑXVVV÷ûá¬ý"MP­§µî“B€çFçi¥'œè»ë¬Ý]ù¤î–ŵiÐf©’ $¨=ŸÔNÝ'—K°€:±Qðd »ª 9uþ«´´” PƒZ/&øúúž7¹uL ‚$ÏŒ ç½ñ\ëªÏÙ«Pï?iµ© ^&¢Ž«  €:Ñ *4¨ªóKF#„R‘¹öÓ¿[{´XØjÈ Ó§ö ;ç1ÉE»ÍûvÝÞ#'K¦¨ÄÑ7ß9ª­ŸÇÔ1Õ{TPmÊ¢€gGé/QÁztᛟmŽœüø mí;ç¿óá{æÏ6kÏ~‰£èà![«¡7]ÛÌtzçO³Ìü¬ÙÛ÷wñiÜaAQ”Kbÿ÷9¤z•––æü ..ŽÑ?*œwî+I’5sýï'›\óøÈnZÑlê•¿?þ¿M¹#ÇEž}X^±W?úЙ¯kp<ù‰{ó]}].ᦔúmÖÇ?lH/•Œ¡­z^uÿ­WDè.íY¹+¬êø˜YL8 xXT8¯‚°d.04i¨“$!¼ÂÚFj~NͱIQújOA[I…d 2i]OK­‡çÏü:­ëô×îkªœ<øÇyŲd¦ÓRÃG…úè€D¨ ¢‚»Èå§Ë¯&Þ•I?/û‰b«"LÕœvʧ¶þœ\Öbr÷P­ëa[þÑB]tçέbü¤˜&m.Ú©0W„¨nLêñÞ(@P•†½®‚³%F£‘$¡Ñiµ'EVÄ™OœÏ~bå;ÿÙôº»†GêÏù„©ÕÀŽÒÖ7ï¿ÿ•¾øõ£eBsq.jlàr  NIII à±ÜÙI’´¦@£d-µ*Îc²µÔªóõ7hÎ?ëtä'øÂW§=üêèª%ˆÊûÑ„ yô½˜äµë·íÜôíÌþêÛ·´7rZ ÄW xtTøË^)ºUpùî#ErG£VØòR³åðfïsÏ@í§6}øô¬C=xå¦Îš¿Þ§PôÁñýÆÇ÷óñïþïþ5[ONíÐL¯æÓbÕªõµØD}\Š›$Ï ç5KÕjµÆfFü²`î²Ø«Ûض~±87vrß(o­|hÞÝ÷, ~`ÎK½÷}úø››bo|h`pÁñ£B_tlxÕ²œ¿öÕ÷S;Žìß%Ú;oûþBCLË/æÒ>­¿H×_«÷8TÍRYLðè¨PµWÁùOF# -¦<1ýÔ[_<õP‰Ðzä›ë¥V" EÆv2-W)Ê÷ÔÖÊ{1ôyuÞC]Œ•÷é×¼kȲïÞxô£ áÞyüCÿé¤Õ0± -êl(@u¢@TpçÙ¦!vèƒo}ðœƒ^-oùô·[œ|ûç9L-ÆÜ7cÌ}jVEq^…Í£ž‹ ¨JÃv@é€TÛfDû4ºê”.¢Î{è€êD$À“¹a¯Â¥{úÞˆ¿S]¶5×qr)@ðè¨PµªÀ¸7¾8D$¢Â¥}F{©pW$wa1@mt¯‚Éd*))©—ÊøFïÒúIKJJL&S]îÁYz$Ø´*C$€¨Ð@Ìáæìœì’’ƽ1)))ÉÎÉ6‡›ëx? ¨Jƒ 9ß0ÎÈʰX, }£a0"#"ƒƒƒG]ž„P¡¤¤$¢BCp8!!!f³™Í‰¢(6›Ín·×åNè€àÑQAa·ÛëxN‰ÆŠ$UÑ0Pv3€jÐ Ô‰ QPEZ W,&€:%%%1Qp' ˆ @iAP€@T\±˜@TªA¨¢Â%ÉòçÛÇ=•Rª¾G&çýrϸ¿×·‰WÑñ+—¬Ý=&!Л§HÃp Uý—ð´¨ õ‰kÝÊ ZµiáØ¶ñõ‡ÊÆDûžZÿîóï¯?œW& cÔeWÝýð¤ŽöƒÞúОÑÓ»lûúû]§ -F<ðÌ´~a:aÏÝ0wæû?íÊ•ãÚzÉÑÎ3ýâ½?¾ýÞwk•h[ zïÿ mf´üðÖ‡v ¿©Uò7KÛ› ø¿çoô_6óÝïvw¹ö™'&µó‘Î>0ëñÕ› š\ûÊmCctBˆv{¾ªÄá%„R‘¾jÖÛó~ÛwÊnŒî=éÞ‡®Ž÷“ª?Xz`ñÛo½úh©!ªm“»è!„ÖßßüŸ…÷}ñþÐ`‰§Û?¥A€j¸­ÀÆáp½É¨BHÞQ‰Sþûò‡ïÏ|ýövG¾z}î !„ös“²;Ýôìwô*^úÖ§»J…õÈ‚gž]RqÅ=Ͻùâ]#šz¹«Ü•/=ùÅñNÓÞ™õþë×Çl÷Éw·+Ba?4ÿÊໟýO?ûê™ÓïþøxÇ›Ÿyp|ÄÞ¯ß]uÒáúh´>á>"wwj¾½êˆÁ×KRж¾ýØ'Ç»ÜõáÜÏ?}¨wþ·/¾·­D®ö`aòOÌÞsͳ¯¼üÔ×Â%FHRÂ?a73€Ú¸¥úÇQ’¾qÞׂ<ßÎ$„Ôãš)Bȧór[%v^{àx©ÒB¡k{ÿŒ§‡KÂÞ4û·5¿íÏ-6ü°$«íŸL¦¢×_®Êž³~Ñ6ß+ß»u@/!šÞyÿÎ[žþa×…е»÷ÅIöæ?­\Òë‰'¯kî%J”ÿýøÆ¾“ÖÑƳÃ3zú¸uÏμnÊ„ ‰WôéePò’篘<{JÏH­áWßÒã×—V¥eæUwðä÷Éúa3îŸØÉ(„•·è»B!¼ZLýòש<ÏþH NlTˆ E._óØUk„B7êÁ™·uö“„P,G–òÖ¼eû …ÞÇOSjµËŠóÁy9žÖh”lkñ±C¥A]Ûœ»b?y WÕ!ễ!¦}¸¼(-ß.„ÐêµB¡1øxk4Zç›ûZ£^¶9”sîDpÛ‡_Ýž²yëŽmË>\ôé§½yç^óÞÌò#ŸÞ:qžsUÀnµÊв«=¸?[4™ØÜÈ3ªNiAP€à¡QAòêv÷ËwDšóüG©%Z£NBØŽ&=óÎú&·ÏXpe»Mæ×wݽþ¯_ç<É—²¢ÑJ©æùËå/÷àš.$©úz ­_l÷!±Ý‡L¼ãtÊ‹·½8wÉ„ÿ:„®ý½?–àSu#½>ýãjÿH-uFµÖ˜ìûÖ­LI͵H&s|ŸA}ZúŸWç'—Ù´z㞌"»dmÙ½ÿ¼xîP): ž¬a÷*HZ¿Èfq¯|ô©1>k^fáÑ !Ê3öäøô¸fx|ˆ—$„,+JÕ72B_p ³L© ŠBèÃ[‡ÉY»³¬Îƒ–ã{NjÍ-BþU²ͱT}cÉ'ªi€°YDX›p9s1¸J1¢šƒ†¦AŽûs¬5fü½Æs 6{Þ–_×1v9aÂÐöšÔ•ËwŸ–Ï …;~]¶O×yÌ·Þríá'Öÿ²!›Kp •§â~n~ñÎv‡?æ½? õæ–Á¥[“–$oß¾~þ›/~‘i¯ñ댭Gv×oýè½ùkSVÿüé3¯¬(² !´}Ç'”üúÆœ5{ݹtÖÛ´—Oèìÿ¯Þ¥­ØÿÁÿM{xÖ¢ÕÛöîûsó/Ÿ½½ +bà æ1}¯îmûíùW¿Y»'íàŸ,™óò“_ ¨æà±à^šæ,zcÎoë7¬øú­g?8tæ§°žwÃÈÑwÿïááBÒ‚¸ô ì©‹Cº]Þ)6"¢y×¾}óö¦“l§ÒOiÌí[Gøxy6ëÐÊ¿âdn¹ÌüÕq×E ôMF=òTÚ=O¾üjów|øÚ#¯}ùÒCRD÷Ñ#Gš¿å¹'Ê}›D™¸ÖP©Ý³ËþÕ<Õ:ذa .RZøÇ̰|ÕòI'ýÍ æÏŸ?yòd7ýrÑÎï¿Þnžpý:!„-kÅ?õ½q|“Ë2—\œºÖü<ç>eGIN‘âgöw]1Plå6¥jÙAk ô‘V»YQø›´ .ý$]pëÖþù[7ìN?™sdÛúÝÅÁmã4‘›üÕ¬Y?¦–)º –±†‚íve•—:¼eK¶¾iû/æ¨ïĆ!€4žK°iC†÷+]™²da…dŒˆ4¼S F‡P”3×Ûð޹|L?ÍÚ-‹¿N–%cx›¾cú´0q6@TªUÕû¨d}püÀ«ãž Âzß0½wå BâûOˆïϬ¸p©fÀ“Q€¥Ñ(: €zÓX: @cC$€¨¸Y£é€Ðh4è^Y–srs²²²,V Cßh¼ QQQaM’'Hžrrs2³2}üÂyç¸ñ¨°Y3³2…‘‘µ¾“ÆÓ €¨P YYY¡A!¡a:½ž¡o4ì6›.O—••U—¨Ð˜: @cB$€¨Ð@,VK„9Ò!˲,3ô*¤(µºh°$E˜#3sOÔñ»S€ * º­Y’$ƒÑ(IRÝï§Ñs˳¡vU£ÑÔ}ZÙÍ êD$À“5ô%ØÜx|iqË(ÕrU¡>-H$AHh´ù¤î“K€çFçÉ$« 4gÔé«YLðè¨P_« ž6œµ@—ÐOZÇÉ¥ Ô‰HQ¡aÏ)Ï9ýUìE¾ðÜÊ„W>˜«Bȧw|ÿé«w§å”8L1=Çßqïøx?ã䯝?Z¸ñÏ£y¥Š1¦ûÈ[¦]“¢­º“ò´Ÿz웃^þlZko{æ‚ûïý2óì÷3cöM-¼˜mu£ Àƒ£ÂùH¶´y÷ß»(["²[å~g¹pªµÍÈÛ¯oîS¸}á‡_¿:«Å¬‡t'ÿõø™¶ 8ºþ¡õ»sm Í;ÞñtÇ3·híhå½[²K"P'ùÉï>—¤òØõ+Ÿšwöž5†ðíÚFé˜áK$ÛP€ 6 {]… zçÙåœÓQQR!™‚ŒU•FB±^÷ㆲ¶CzFè„R¼û³çfŸþÄCƒÍç^ÿÙ‘þåÝcÇO۬ɲ*L´J&÷Ò‚  @5Ôü¶»|jË¢õ¥q×'†‰ ©ïßùøÒB©É°‡_­·gþðÊ[;»=øê¸fqúì×io~¤•!ÈO[|`õW¿ó¼wÌ;·µô®ë©0›.&<:*8϶Ï?ç>óÏó¯ÍfÍ\þÆ»›Ý8cd¤îÌqïæS^x}@vê²9oý÷ãÇ޿ɾí@ÑÑ}O_»¨òkR»&û¡¯žïÕ¢ûeέã‚NlpCʉ©­š±­¹&·Ö(@ub£@Tpë9eU€pù„=ã{ÏÌ;5äñ7ÆÆzW• ¡±-CcãšÚS&¿½ôÀÍÿ½ÿ½wËe!„Jñ¦7Ÿü.ú¾×nîêçú t~¾’¥¸‚Eõ£ Às£Â–³ÛóSÞü½Ømºú IDAT—=üÚ-ý«ù ÅrºLÖøj4†°è&•ÇN§4z?st˜Q#ÛeI§‘„B)ÏØ“#†×ýçôœ‹9\ÔÉ­ ‹  Nt@ˆ —Î;ç–­eeåe²"ÛÊKËÊ}Œ&½åÏ™‘Ütê#‚òÎBÒûE›OÏiAy‰qAöìÍßÍ=ÐçÆ6F×{’Μ®J’íè‚Çfç% éÓ>IJï—9ëä„Gz…éXT¸Øq¨nL€GG…¿ìU°¦ÍûÏÝ‹O !Dú£“óæì»ÂNÊUŠrç>±¥òV†>¯~~{§¶úyKfÿz²Ä® j7ðî·wñÓHçÜ÷™ï é‚Ûµ7Í[òñš/+„‹+î|ñÎ!Z‚‚ªcFeZ xfTø ¯Öw|¾ìŽó|gñÀjn{ݣݯ«ù¬T ôƃœ‡ô¸í¹·]âS—Z K‹ œåìl0þ#véŒU÷*P€êÄF€¨Ðp§“œµ«9$Ôi)£Î—$UqÃÕš¥K“'<ê28uï€Äo#¨PRRƒx,Ý¥ø =§o©çkQ€àÑQÁd2•””øúú2î,•””˜L¦º§A€j4h’9Üœ“]RR¸7&%%%Ù9Ùæps]î„$µiÐUçé`FV†Åbaè ƒÁìp8j}' €:Ñ *4‡Ãb6›é—Ú˜(Šb³Ùìv{ï‡$Ï B»Ý^÷sJ4>,&€:%%%±°x, C5p– 6-TJ«ÄPwÑ1POZ UŠ‹‹c€{±ªU`1€¨Tƒ$P'6*D@iAP€@T\±˜ê”””Ä DÀ(@ *5¦AQpÅbQ¨H Nt@ˆ €*Ò‚  €¨¸b1Ô‰HQp3 ˆ @iAP€@T\±˜@TªA¨¢ Š´ (@ *®XLu¢@TÜŒ$¢PcZ W,&¨®ÞïQQ”œÜœìÙeåež0‚&£)Ò!IƒSëÁq Uý—ßLP : D…ú”“›“*¿S‡N!Á!ž0‚ù§ò÷Ø/„0‡›/dpòòsÛ¶nè ƒSxº0íÈá  yTÈ>‘Ý©C§ÿ€ÌÌÌÇÿ«¯U¥ê×ÏûÔßWÅù¶$IUïdW©éŸ5}ê5oÞ<""¢më¶»ö캳áìÙž“„qÍ[ì?pà‡ÅP§¤¤$¢B½)+/  ®¨¨HKKKLLlÜ÷iÓ¦à à ,(*+/óœœP•.dp(@hüQÁù–¼R©q_ÕÏx ¾^ј\àOM€ª\”HU'ÐJc'έ•B­¹­’íØOïÍKÉw0繫 BRE®Ô¸‡Ïù3*Š" éÇŸdòS»­©üÏŸywÔ˜ë{†ðjÕ`£@T¨×óBIrž@;‰ ²,«©Éž»yÅfÑ}hP:FIÕH¦ÎS‡[Þ}oaâ݃چûèÎ5¿B£:˜tëMW¶òQÛ{ò‡ÃÝÕGrÉÁßWí+ñm׿+ß:žÝÚs·,ß"%éÖð!Ãm»™Ëv}³lïÆS“z¼éz´ÿ¢ÂÕcx}: D…ú%)߯Ãi´\<óÎ~÷pãCSãCåœm¿|1óþýù3gÜØÒ Â¨ (Ê…Õ{֑˲3JuFmIFNYœ¯¯:ß ¿€ŸÚmH¾W|¸)õõóV¿4>Ñ~¼8OWÿ§–ν ub9ôͬ5¶+žxë‰ý:·kßµÿ Íx¨sæ×®:a·Ÿøùîá·}}Ī(Š¢”ízùš«žÜRª(²åøŠ·¸qøÈQƒ'N{ú»?‹d¥"íóëÆÜÿkžól¾dý#ã®~¿EQGá¶o^¸ùªQƒG޽濮̪¨ûþfwíUËr2JýZuhf*É8QæÌì§m_·ôç%?ÿ¶~Ï ‹"„ö¢c;_öËÏ?/Yºf[ZMB.M[½d;ôC›W-]²vëïÉ'ì¶ì¿.ùùç:êñ)q!7sO’Æ'º…Ù²iΓ·NqÕéöíÏÞh3GùP~ˆ õ$ɹKÁn·ËµbÉØ°1/hÀ˜Ž~U»£¥à^c»y¥®Ü^àåÊò¦Ê 'E–…[f>þé±Nw¾ÿÙ§?Ð+ïÛ—ÞÛRäEœÝ`-;o*[.|öÙÆñÏ|ôõì—oO~ãåŸ[k÷HÏüŒ‡ÃM{”òœŒRŸè¨°¨HCIzNYå›ãŠ=7í„.¦SbBûPËá­;3-²5g÷Æ]y¾mzöЧcHñ¾äí™Î!—Ü•%…·lߺÕeÝõºˆƒ‡Þ¯m€¶Ÿÿx·u@{_Òçþu~½z賲ʴ&ËÒéƒï^YHS+àñ.JMºóöZïU°:^ ÂZ‡é]¿Ü+¼E¨Ø(×ÚITµ(âÌ y) 6úOúðÚÄH­anê¾ôÕ5G,㡜½é™‡f9ôãâ“—Ý÷üÈv&!ÌC®»è?k·æ‰×Öú'uÛv¹üDz©OL„QgŒŽô>œžSÞ¼…F!é"»^Ö1L'„ÖäýoëñSeÒÑl%²GÇf!z!üã;žØ–].„djÕ·O»­ž«‘$IïååÕà{ÜV€T¶õíY%ÿ]µõ¹f«Gû¡.rôÛ?¿´ñò϶—à[Ë»´ì[·2%5×"™Ìñ}õiéÿ×Dn+8´%yëþôSٯÄk/Ðñb@بêSU$gZ¨Í °|æ²,¹T„b+³ÙeEˆ3kΣB‘+²öf•3mÒ—Îx‡Õ&w,¶;3Å™–­²¬EQì§È/:ôìÔ ZI!›Õ–Wb—Ckó~Õò†[VKNz‰)ÚlÒ_´ÙëHFŽ¥y Ó™oU9Ã>‘Y\RXª˜"}ÎÌ·Ö'ÄGdZ”p!$­—îâVÛ¨º’½0£"zd“³{`ôÁ-ÂÅÊòZ÷î²çmùuÍ‘À^#'D:ÒSV®\î6¡sÀ9#,ŸÞýëØ[ö¸âÊ0£pèü©vžDe Qçµørm`LH;[1 À«ê`yÎá<ábdEÅQ!¡(²]ѵ»ëÝ»˜ªNOõ¾Þ¹©•7Λ E–E:æ©ç¯j¢¯¼©Öä§­u_W÷]>B)ÏÉ(’KŠVÿº¿òPzNyóæ^Õœ¬kÕ~=·u@2¶hÞòê¬ÍîR„B)ûsÞ[[#Æ´2Ö6)¤,é6ºS¬¿F„ö혚´7­¨cB K°KÞRÜöÊI}Ìz^¨¢B}r®*(Š"IRí*stæÄÄà×,Ùuí=Ýü§¸Žü䟶–û$vóÒyëKq¹CQtBÈ6›,EÖ*LÞ™ZàÝ?ºò O‘‡Î['*Š,E‘„â°:!É7&ÖXš%ü;ž)9R¥–T8F÷¬*(å9Ç‹M»õhîÜ‚+ŸNÝ´#ýdy³˜sne+Ê·èüý}¤ãù¥öæ&½ÂQš_"Œ‘†¿<I\„jª ù©ÝV€¤oyûÿ7@bð!RAÙž.‘G3š?²ê¶¸ÚžÅÛ O–êƒB!„Ðú™¤Ù§í"ðl€³žÜŸá04ÝþãçÇó+tM»ôex1*; Õ:*¯æWÝÚ÷÷×_Tšrý v!òÉ¿}ýÕNkô„Ñm B4mT¸æÛ%ñ#Ì[/ØP&÷P4!=Ç]öÍÌWß š6¾{¤têàæ5èÇ?:¦Uœ÷‚_¾[ÝÛ73eÑ7û¾±BñŽ;Ü|ÿœ×>× nå[ž¹gíÊ£Ýï¿··mÎÕœ?£[: )–Üôb¯ÈÎáþg"©YØž”ô“–h!ì§3ÒOhu–“÷äb¯2šZFJ›ví>Ú¥eˆ¦è讣¶ÐÎf£tîrˆÆàçm?–žuJï+4¾Aþ^R}=%.äfî)@R@ï’ÓÇ-þ~ÙöôR¯ÈÎC®Û3Ò»¶÷&ÛÊlŠÖ»òRn’Þ “‹,vET¥\š[hshtщú™lYÛ×$ÿ²ÒÊHõ]4.©—¨ÚÐ\Ûw¨¥ÀÄéo=ýùü%o<ý¥M!DPâíOl¦WÑtü=ã¼½`æ‹Þ±ýÆOè–ñ½ŠpÙ}/Ü9÷³…ï<•T!¼B['Žœ¬5ÆÞ<}è+³ç¾²Þ¿Íñׯ}¹D(Š¢o~ÕSÏèæÌýöÕߊeáÝõò±±†Z>ÔªU…¿®‚bÉI/Ò‡wð;»[ªÝœžSÑF!ݱ±Ø*¼‚š&ôl¨•DxÇÞ÷ìØŸ¼Æ*ô~‘m{uŠ1JJé¹QÁ·i‡9ÛvlÌж»¢w}E… ù©ÝV€$„BÞmÂ]Ý&Ô×o€æì2Š,ŸÿÄ’í»Сk‡æ!Âúõ>6oÕþœŠV-νdÈ–-[ª>îÞ½;/U QD!œÍREm÷*8ÏZƒ;{ ó8¡”l|þÎü|÷¾.¾’óþ|:^ûÔg×VÞpÌ(ç ™WL¿;žéwÇ9'e"(ñæ‰7Wþ{ØDçc’:¿æøsnZëGêl–Úà'›õÕìüÞ#¯BˆfÃÇT7×þͺ\ѬË9÷â7`TœË¿½#:\>¢ƒžˆn+@BØN¬õÂ[Iëÿ<^ä“0òö'Ÿ¼±k@-wkô&½°[íJe.¨ph½ :—°¤Ñzk[¹Íy Içãë%XìÊy‰Šx@%بêõ¶²R=í÷õn‘جbÁ’_7ëÛÈyåM{&F¨h/hÕé¦ë*\TÝI)Þð`ŸA_„ßxßíO´ð³fnýᙃ3ÿý‰NµÛجŒð±fä•ËQzp”ä)~mý]Í4¾áÁºÍ™Ù¥r˜¿F(Ö¢ÓV¯oÊ€'D!9Žº ¹Ò„ ˜~gÚÛŸ½ù¢E úLá*ê@ïܨàp8ÜuµæK#*ˆ º›{ J·|dxzÝê§;8 €¦ÞvcâØ!ïl¹ï³Ë}jõܺµÿέvu3Û¥ì.î ŽÜäo¿ßá3pê¸6¦ÈÎm}mZµÅ·WSmÖÖä“~ñ}#è…@­è€ê™s¯B½]›LÑÿÎWúßyöì\U#XëËGÀ•û d‡ÞÜ:âì>fmPÛ¶>‹k]mhÂð~¥+S–,¬Œñƒ†w Ôág¯¨ì9j€}eò²¶J&s‡!£ÃI À#¢‚Ñh,+/óöònÒ¤ÉöíÛÿíiwU¨v]Âõ³ÕÞÒÙyI¸½H’äúqÕ]ýõãjoö÷bcc…eåeFã•«ø˜|Š‹‹ýüü<çV\\ìcº ÷ç¼ÉQ^RîPâ§MÈ›~÷GÍfÞ|Y¤A.9²âGWv~ð1ŸÚ߯>8~àÕñÏ a½o˜Þ»êAm\Õv¯?ÀâBTdTfVf`@`hhhhhh£AK…¥ðtatTô…ÜØl6OOmÒÄCÒBqqññôt³Ùü·tCÒé%#Ç­9ó»z-¸ëì§Lúä7† àõêUP`,ËÙ'²+**wÓš‚«^u=Ê^¢ÔÁm|û}¼-m¦|îAÉìÇœ¢ në€dËZûý‚Cçôn9éîI-½™ @TÔ‘Dà Ùs·®ø-¹T!„â(Ï;¸5µ"aÊ3£d&pß%ØzÍømË¿­G挱´™™%B‘””ÄÂà±è€Up[¤óy55%ì÷yÛK˜àéXU€ŠÒ‚hø$![-Öªj#ÅzjçWsvëZ𛈠ÔÀmH§ ·æœCæqŸ|ÒÍäÎÑ u^óȹèïzý#Žp„#iø#¼"q„#õˆškü¤vÏ.ûW_ðTëüaÆqj‹‹”þ13,_µ|ÒÄIsƒùóçOž<ùB¿«\šy8³´rYAÒxDÆ„ûhÝ;³fÍš>}:O „Påo>»`á‚¡‡ÖôÙeË–½p ¤ÖßšU¨…{ 4>Ñ-[3øD¨” ,{g?;{¯¥ºOâïxöŽxÓt@ˆ €»¹álriæ¡ÊÎ9æ(ܽ<9CFœ¨ˆ €jÒ‚hÈ$Sç¾ÿÕåßöœÕ¯M´TÛvêìïÞæÏ„OÇu  n¾œBÅñÅôo5ð¥¼Éߥnûü–Ž~Sˆ ÔÀ}—`SÊR¿žØjÌ'ú{—L~çê8#1Îb£@TT‘Dw@’‹¶|}çø)?7{iýÁ/ Ö3 •Ø«UpC$ëY“úßµ(§íï}r[W±?eCÕ§´Añ=⃴L Ð *îæ†Håû¿_œ-„ØÿÅÝW~qî§´Ê_9&€iD@iA4dRÀ˜•v…a¨ {  nÔÉ}‡ €'£ *J ¢; ©XZZšóƒ¸¸8F¸« PÎW‰¡à^III à±XU€*¸¡ÒYÖ¼;öf[åÊ]κÐ.}»„òˈ €JÒ‚hø$¹`Õ=‰ƒ>8$´:½¦ò:Íþ#~:üÓæwsÇb‚BˆÒ?Þ_>óÏ÷Æ›$¦À{  n뀤1GÆ'Ä’ zt@<« PQZ _€äÓãÞ«_þÏC³Ÿ¼µW”I{&0h|c[Çú’£Qp;· )ÖÓyYë?š6ô#×£ý®À´€HJJbaðX¼q Up[RÉæw¿Ñ?¹:½¸ÂæbÅrðx¬*@EiA4|’¤ñï6¤GŒ¯3pV  n»›O÷iýw=õâÂíÇóO—T*w0%€¨À@ ÜV€T¼áõ¹›Ö¼zUBÓÐ@¿J#—œfN@: ž$¨(-ˆ†/@òí÷ñ¶´™òy ÚdöcB€§cUªà¶$ÉãsôûgnÚ³KÇ.=‡ÝøÌÂã¾1f¿ „"))‰Aˆ €;¹­I”ïx~ÐÈÇ»N}ÎWŸÍ˜ÖùøŒC_ÞeaJ€Ç£ *J ¢á ʶ}0ÏñàŠß^ìbBqõµC‚»ŽyÛƒ³{›˜àÑXU€*¸­É^˜amšØÌXuÀдGSkF9D@ÜV€dˆ»¾îˆåú^§Ç͘»½t`ßZÞ¥¤‘ª6)˲¢œ›%ÊNd•­ùföšÊC«æ}sêªk{…i]o·eË–ª»wïÎ3àÑiA4|’wlBàÞ“ó‡÷mÓÖöÎÚtk¢ï‰“§ÊkyµfÞ¤vkå2‚l¯ph½ º³ý´F_ÛêÌnf{þ–%ÿ;ÕyìðvÁÚóîˆxˆ €nì€äÝnÚã ÝF]ÑtÇï7.¼¼[«O¼Žg'|Þѧ–÷§Œð±fä•ËQzp”ä)~mý]Í´ÿ Ùí6ƒF£÷ ô7jy ¢P· ]“ë“_~°8"8ò «º}»átËñ·Œ‹©í¹».¸ukÿ[7ìêf¶KÙ]Ü-.@#¹Éß~¿ÃgàÔq\BÀ¥…HQPEZ _€$„ߦm|…"¦ÿMõJéÑ?s "j·³Yš0¼_éÊ”% +$cDü á5B8„¢¡0Ë€¨ü;n)@R,G—}øÖW)…a—ßòðý#õB8 6½Ǥ§O½s|e­¯Ö¬ŽxuüÀsDXï¦÷þ˯_øåS§1ùT-))‰…ÀcÑ®ªà,= Ù5U)Xqgâˆ~H;}ôçôy8¹èôæ7Gµîyßê¸ûëéËœOǪT”DC •íùæ7qí/»¾\ºñÞŽcpÇÖOž™òÙÝ—±Í€U¨‚.Áf/̬h6¸s $„o‡áíKÖ|^|ß¿œ@T€z¸¡IÈ6Y£sÆÉËä3íƒG/â7ÎÁF€¨¨"-÷t@B¡ ˆ¤Àåüˆ!€¸çlö“›o ”„e×Ñ’S𥠓vé„BÙ÷ʾ‘z¦è€wsÇ%Ø´éȬ;&ͪü÷æû§,p~ä3|qæÒQL *êH ¢! F--ášh5b¯TÁ @T€ú¹£àŸ±Q *ªH Â@T€ ±˜ê”””Ä DÀ(@ *5¦AQpÅbQ¨H Nt@ˆ €*Ò‚  €¨¸b1Ô‰HQp3 ˆ @iAP€@T\±˜@TªA¨¢ Š´ (@ *®XLu¢@TÜŒ$¢PcZ W,&€jP€êD$€¨¨"- ˆ €+@è€7£+¯ IDAT €¨Ô˜Hª¡c ÁÁÁ„WiiiÎâââ @T€çr Uý—!!P : žŒ$¨(- ˆ €+v3€:Ñ *nF$¢PcZ W,&€jP€êD$€¨¨"- ˆ €+@è€7£ €¨Ô˜HDÀ‹ D  €:Ñ *ªH ‚$¢àŠÅP': DÀÍ(@ *5¦AQpÅbQ¨H Nt@ˆ €*Ò‚  €¨¸b1Ô‰HQp3 ˆ @iAP€@T\±˜@TªA¨¢ Š´ (@ *®XLu¢@TÜŒ$¢PcZ W,&€jP€êD$€¨¨"- ˆ €+@è€x2C5p Uý÷’þYìûÖ­LI͵H&s|ŸA}ZúŸ“È•òŒ­ë6¥fäUÈ^AÍ»^Ñ¿k”Aâ)T‡U¨(-ˆFP€dÏÛòëš#Æ®#'LÚ^“ºrùîÓò97Ërrìá®1æÊí ›–®K¯`ö€ ±ªUh‹ g’BAêÁân£;ÅúkDhߎ©I{ÓŠ:&ž åÚnWŽ<ó±Ùxêà÷i™ÅŽXo-O 2¬*@M$[áÉR}P¨Q#„Z?s€Tœ}Ú^Ó­e»Å.¼L^ÔP-: žŒU¨(-ˆK¾I¶•Ù­·î̹¿¤7èä"‹]Õ¥¥ôØŽÃÖðÍ}ÿÙ·lÙRõq÷îÝyz¢í­·A÷—%¹$mÍâäÒv#Ft b— *ÿÄ¥ßIácÍÏ+—…ÂQ’S¤ø™ýÏ[¼“KÿþÓêÜfÃÆõŽ¡M* *§Ñ\‚MܺµþÖ »ÓOæÙ¶~wqpÛ¸pä&5kÖ©eаf­ÿaù‘Àî}ÚKòrssssó‹m O ¾†jÐx.Á¦ MÞ¯teÊ’…’1"~ÐðN!BQ„P„н8¯D±”$/9^ù%ú¸ÑS‡ÆèyP#6*D@iA4‚K° ¡ŽxuüÀsDXï¦÷v~ÜfÂô6L7P?   ¦ ™¤¤$ *îÔh.Á@Tê?-ˆFQ€@Tê ‹ D  €:Ñ *ªH ‚$¢àŠÅP': DÀÍ(@ *5¦AQpÅbQ¨H Nt@ˆ €*Ò‚  €¨¸b1Ô‰HQp3 ˆ @iAP€@T\±˜@TªA¨¢ Š´ (@ *®XLu¢@TÜŒ$¢PcZ W,&€jP€êD$€¨¨"- ˆ €+@è€7£ €¨Ô˜HDÀ‹ D  €:Ñ *ªH ‚$¢àŠÅP': DÀÍ(@PCõ¤AR¥´´4çqqqŒ *Às\‘€ÛQ€U  Ô‰HQPEZ W,&€:Ñ *nFQ¨1- ˆ €+ˆ @5(@u¢@TT‘HDÀ‹  Nt@ˆ €›Q€@TjL ‚$¢àŠÅ¢P @è€U¤AQpÅb¨¢àf €Ó‚  €¨¸b1€¨Tƒ$P': D@iAP€@T\±˜êD$€¨¸HD Æ´ (@ *®XL *Õ  Ô‰HQPEZ W,&€:Ñ *nFQ¨1- ˆ €+ˆ @5(@u¢@TT‘„g )9;—ÍÿlÖ¬Y³¿Z¼)½\©æ6³fÍâ)€'Pó}¢TÁ“¬™—n<ÚsôÄqš—ïX¶êP™Â3à±eËyñ4µî€Ä¼ðû‚F€¨Uð $ëɽ‡­Ñ=ûÄÇ„G¶JìÛJ›±'½qg…´´4žáÌ ˜æÌ Q¨SZP€$—ç²Ã½$!„Ðù›ý”Ó'ŠÌ? *Õñœ$ÅZfSt^zI!„¤7ê…µÔ*óª£c Î¤ªÿ6òŸV’ª2º"+5±³Y6oÞÌ 0/ž¦Ö/GÌ ¿/ *õ–„ I^&½°W8œA±WØ%¯@¯s—÷¦OŸÎó¸HPÏ)@Ò˜Bƒõe¹V!„ö¢Å’„Ÿ–§ *Õñ Húðö-¼2R’÷eæfücýA{Tû&&‰§P  ¢´ <âl^Q½GôZ³jÃÏ©6ot硃ZùV%{Á¾u+SRs-’ÉßgPŸ–þ¤ù†§TäìZ³zËá«Ö/¦s¿Á‰MŒçd9¹$mÓïÛeç—X…!´UWtÕ3lnŸ—*ŽÂ½¿-\›yåM#c½75Ì‹R‘»ÿ”í2O[EH⤉݂XI½Øþù¯‰½ uãšMûN”ÊsÛËôiÈ9¡[Èùûþ÷ÓæQ·ÜDTjä»™+IÞ]†MîRÍK{Þ–_× ì5rB¤#=eåÊåþa:˜óy1WŒ\~pݪe«‚§Œlåºðã(ÊÊÕÄtÐ+H[”¶é÷u¿z‡Oé΋©»çåÌÝÒ´5KRò¹¬¡ŠæÅ–»ùçE{½Û÷˜ì­ÈÞ&^Ô.~PøÇ¿&öœ”%«ŽEºjxsSé¡ß¯úÅzíe!d¸†Ž E»~üvÃIYƒj#¿°P*@ú›×ö‚ÔƒÅ!Ý.ïѼkߎ¾y{ÓŠh£Úàg>ÿx<}Ôåc†÷êÐ"&ºi|¯>-½J³rʘ'÷Ï‹B±d¤,YWÒaDßh=U}*™¹äàúÝJ÷±Wönß<:2*&:Ä›¹qÿ_¹,'«ÄÛ¾EˆQo mÙ±©wIv¡‘køÓpÿN§M¿eh¤ŠÏlj PQZQ€T#[áÉR}P¨Q#„Z?s€Tœ}ÚÎ3£aýËkäÉÖ ‡ÆÛ¨ç¥TóbËݾtEFìÐáC ª™¥°=C3(ÊÀÀ¹{^ä’+ÝØoüeQÞoŽªç÷ÅQ’S$; -z_™èUzlóêuK4SÆx3xîýk¢è5ªwþ÷Íݬ±^>¶S oy€¨Õò¬K°ÕLÒHU¯ärÍWgÃEž† ºFž°Üº<¹0¶ßÕ-haåþyqgf—Y~›·¿êÐ/s¾ï3eB'z¸õ÷E±UØ¥àÝâ›…¡ýJ³u¾-&ŠVîýk"îß°½<~äµmGvnÞr0%å`Ôà6~ü¶€¨5§áÑH½I/ìVû™WtÙ^áÐztœ„6ðß× ¸FžB8 ÷þï—ÚncøãªŽyч]6áÚ.gÞζe¯[´Né;a@œ/“ãæßIç¥Sle6Y5Bh þFÉZnåmwÿ5±ØöG¦ŸA±A&)hp“Hïo¥ìèwy§…øËó‰!€xònæ³ç:>Öü¼rY窽âgöçe»¡_/äyŽÂ}ÿ[”lé8úÊ„pÞUɼèLAUü¼4½o€¿7ãÜ=/Z?s€túøÉ E!ä²S¥ŠOˆ}vÜý×D±•Û”ªu­O A8¬"ˆ P+: !tÁ­[ûçoݰ;ýdΑmëw·£UjÃÿ­þy¶Ìsf}ò[ºU(å‡Wþ¸67¦WϦš¢¼ÜÜÜܼ‚R¶Ÿ»}^ Îy‘LM»´Ðe¬[½óxΉ´Ík¶…wЧ%§›þšœ}hë݉”ûN–XJslÞqÊÔ¢mï|4<Åa«¨¨°©¹â˜·,¡¢´ <»’І& ïWº2eÉ É?h8»ÌÜ¡¦kä)²PdE¥¢ ߢ”\ûÓÁÊ/ñí|ÕäÞa¼œºu^ Òy‘ŒÍú]Ù{íš-¿ü`Õø5é:r`G^ØÜ÷פr^$Ÿ–ƒF•­[·é‡/×*ZߨvƒÆöŠf¯yÓ‹v-ü&¥@ÕQj÷ì²õOµÎ6l“‹úuá»™—¯Z>i⤿¹Áüùó'OžÌ€ÆAùÛe‡  8´¦Ï.[¶ì…!µþÖD{¨HjCT€ŠÒ‚ðð$¢pˆ @5(@ *5¦AQpÅbQ¨HD Æ´ (@ *®XL *Õ  €¨Ô˜HDÀ‹ D  ¸ô)eé©'mj¸ * Ñ¥A€K‹ýØ]õÍÛiÂvèþ§ý^\§ûs¹“âW™oÛPÊ pC5&$¸¤Éåyùårý݉±Óƒó>Õ·50²Ü‡U¨H.m…¿LüÒa˺ɭz<·Ë"*Ž,|pp I’ MúÝ» Í"„œ5'ѧý7v6I†ÎOl+-Úöáͽþ¿½û¢zÛ8~f[’Mo¤)”@(B ¡ƒ ЍôfATªJ‘"M@AzPªtD üÄö‚€4©!”@€„@Bҷ̼$à» ÈB¾Ÿ‹KeÜlvž™=;÷œgf˺¨%IÒøÖî¹46÷Î'I;:ýµ7?ÍÂpñëá­*¸H’¤ñì1{oš,„œüE—*oy1"$ÐÇÕ£B›~»!³ðä¦A€Ç”G›uÿ]αñº3Œ‹|ðL· þãö§²N-¨¹óõÖcä!DÎÉ-Ò˜?¯Æ}ý^…“´{ïð3òsÆÁ1ÎÛ•~ǓܚNÈ=4¾U—µÞ£öÜÈϽ¸ñ¥„±Ïtýâ’I!²O}•þú¶¸Äë—¿ñäø¾KÎp‰¢žDL&xrä^¼"¡Þ¤i¯Tu×:‡¾0þãfW¿X|8G¡ ì<àùŠ~Aå¼\ªÜu,fdsFÒ•›ú@×üë)VÛ—r¯\u±Á”™=#!«0H®ž¼ªxyrWD”L&x샂ÖIgN»œ®­Õ§›×Ž÷FoŠË4›ÒÌëP=¢ÇÆD³åƒÍ¹iÙŠ“—»N2¥ìûtôç—ŒÆ<“bñ$†Â)§ê=_.ûûÈ¡_O7æ'ý2}àÂëѽ[¨)8¢J <îtAÏv¯{elƒˆþÿ'5˜ºsyëSÃjºi´^-¨ßÙ¼é­Ð;æœj_ðŽïÒÆî®Ñ£/¼øQϤƒs-Ÿ$çÖCkOؾ¶SÒ„úž:Çà.[ÊŒù²W0“  )||ÌýÀذÔV­ZQ8—»vtéÐåo°~ýúnݺQ(ðdPþöJ¤ ›7<Óâ[ÿ7&&fbœ÷¿þÕÌ*À.0™`oˆ ° 4 ›iAp$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ ·ÜSKzÔyê½-‰FjñέíÛ IŸµí½ÔDØ(æ£×øãWeSþwoôÉ;öÉów¼Î®˜MI?Îøj»&ÑÑ‘QO·ºä·k¦Â‡›o\÷Q¯—ZFFEGõû>Y~à/gÆ~=íÝ—žjÕ¨ëŠxƒÐ•ï:éÆçOÙ–h¶ë½BÃö“ H %cï’5W"Í­ë¦Âtùëác·ª_ùpEýñ•ã' _¾v@¸Ã]?£òk7î£gýÔB¡r,¤ÕŠÓ?•_+äÿ7eÔVw)÷â¡Ë¥ZöŸ0¨´.ùÿ–NŸ;BWmËØ:z!„Prb—û}–Z¸¼Œ¬³‡Í:QÔ¼±ÒvL™8j\èÆ¹­¼UBa¾¶kÊ€g!ýúäŸY2tÊOÁæ,­iÜ»p̸1e*-y¹ìݽRÈë³FÖw“„B¥/í«2;|Î)úÕqo‡8gZ=mé°É¶Ílá%nî›Ñ{Оò¯¼ùaÿ2NF¥”Û­ß¿/>7vñ€þ›ÝÚõûZy7³ÉÍG#„Py·Øw]—Ï—}zL-=Qø;^^^„Š‹|cïê=š§f7óS !LI¿n=åÓ~u¯f•u¢êà7¿k¿ü›³o„Wu¼ë`[X©FDåÑd@¥ê…ÿi8½è£øÀv«êÝuã?mZ¸´Šfßæw®æÊB¯†„¯Æ Û2lZÃÏûþ „"çäæŸ³ëŽx§}]/•¶o{¿/÷¥>ýœ¯JÎøcÁ€iW;Mrdðùï“Âù~H*ß{n׆¥Õ"lp·ï{~µ+±ËkAÚ»‚ŽkPµˆ/‹ÜQ{èìÚ…ÿYÕãÔw/ï¾tÓ,\.múd§ÿû_L~Ñ_}ÇÏßï‹oeŽ™µN~}ŬWÊÝù Ô~-zF-½áÈ»5£Ý$;Ý1h@‚]  €b”·ë”®îs• No®MÕtB¡v/_É-íÄù¬{ÐÍñ ºÖ‹ŠnðBßÉ?$äßÙ¢$§þº`ã};VÔýµ,ïúÑ-ëhv¬ë¡æë;'Z¢îýéðÆ>·ÂM7ΜËõ vQ !„C™ˆùÒÑDƒ’ûÅ ±MžúJ˜þŸ^•ì„S×BüÔB¡õR'þy9ÿžÇ™MxªAtd‹Nýü–l²|㸫̪þb³@­1ñ×÷Mèõü[“~¸dâ^|^ÚþïbÕggôxºNTãæ/Ûp:»°T’kg«ÊG~:Ÿg¿;³ °£´ h@ ˜RÎ\2–íZ8m`ÌLÏÓ¸8&ªõŽ";5Û,|þ:LW{7~wJ½·»ææ‰ï~2acðÚ!UnO;äÇmX´Ç³ÃêÆÞ·~${÷{Ͻ³Û¨«òÚ‚ÙOû©r/<%©ýÂÙÏ—Ñš/ÞÙ7²WÇ‚sì*'w'‘šy5fìÐoƒß_üv-WUÎ?®‹œcùBíäî`ºœž/ —¿^½¶ôs#f5sóv×ož>ïýÁëVöÒ !òŽMjßgó Uhûu Ö‰ì+Ç’dC@é¶ï/~Gs1fÞÄ ƒ]B>o¹ë~_|vÞÕc‰FCiïÎèï’úÛ瓦œ¼it”«$„äZË/ûçø,¹¦“ž¾'*À.Ѐ@ñ1g§ä:øøZœ±—TêÛ1›•»¯j*—°†M þ³J%¯Ë{_ÿñçËoW©P0… §ü2oSZÔÈÎa]ÞàTkزUÝþØ0s@_eégíãþ8Ÿ»°[ó…·°¤ýóæâ-$µZ*<ÌW̲"„)ùàá”ÄŒ÷Ûn½ýdƒZu¼ñ‹.jë#©n?‡Mò=¯^íU­qaKT• úÓ{ûÅü‘Ò5(@-„CÅÞ –={ùØ–Ùc_›úɺaÙYf‡šÝz>WÏYˆêåGÄþÔkç§Üï‹æÜŒ\UÅŽ=Û7ò”„¨ôþÕ]—w潨H'!„Ú=ÐC•“’e¾DÀ¶‚¤Ûÿ¤ wú†Y!ŒW]–ªÞùZdãí /ä›qÇS4å¼îx>%'=[VkT’®LùÜîÓwg5\?“"ùV(åuß/>Ð%°FY)aÏ©‚ÄSÏ^S|Ã| /à0ß¼š¡è}\Ôv»g0«»ÀdÅx@èS±¬fí ùÏz; !4M_¬²pÑ´•Õ5t8ºâóØR­GUpæÄµ¯wœnìûÕªWÎ/í7ãZTÛ5KåûrÎN9jR³‚»¦æ\»xŸG»/šÝ¾°!gÿäa\›·®WÎ-÷ÜŽÏV_)ÝõùrÖ_ˆSxÇfÎ}ç/ØâÙ14uû´yµGÖ÷±yf;ïìwkbÒÂ:tkìoqHëPîÙ6>›½)ôõêù{¬»Zî­–¥µÂ;¿[÷5>~÷éóú#“ú/×¶x¾Y¸Ûýk>9ìÒznmwéùÖfG=Ó¸²éòÿ-ŸwÚ³e¿*NZçVÝj/úáôˆqC®}?ck^ýqO¨ïÿÅk¼¿ÒÔaøŒ×:¿U#ï—y«ÃßlW® *(Ùñ‡“+„ºØï¹{¢ì H#}Åá¹ó¶Î­]ÓI¡)ýÒ” —FÏõÚRÙµü3ïOï]ÕI³f!dY¡ñލ©_°iÆöEyÂ=ìé¡ó‡µòQ !äk?Îû*½Þð®áÝXUW¦n%ãªÕ×]Ë’ëtþdTï*Ž6^‡äR{à''}4sÀ¶l­_Ýn“>j]ÊvR8·éÓ¥1uf¿vב»®ÂŸŒ¸6~á€^7UžUÛû¸{°Vƒe¡ÈŠBã_/\^²fòæt£Ú§Ú‹c¼W×U’KEV×-Ü4csR¦Iã]ý¹Q‹×u“„h;eFÊ„©sûnÏ×7ë?gdk[רxñžF.8uÂÒ÷^ÍÒÔï1ûƒÎ!ó/JæÑŽ©jL(çh¿;†>>æ~`lXj«V­xG¡¸ìص£K‡.ó€õë×wëÖBð”ŒßFw•?|Ë´–ÞIºñŠ.ݶD/Ý0´ŠÃãXróÕ­½»~VnÎÆÑ5œþaãÜ{Y¹… ›7<Óâ[ÿ7&&fbœ÷¿~‘\«»À×)Pœ$÷¨>¯”Ù?oÑÁLùñxÅrÚ±ÝWCÚu¬øXæ!§ý¶`Ql•·zUs²ç—IT€]à+Ø(^ÚàŽã^qÞ2r·‰¦ÇáõfÇþrµr—Öw óãÁ¿iÌè_ÊxÿÙµ]¿P®U€¥Á(6Ž•{¯ù£÷ãòj]›Lÿ¶ÉãZj]h—»»</”YØ&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@°7JûI ¢(ºvíª( õø˜U€]`2€¨XAQ°™w@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ì¦oY[ IDATßÖ »àååuÿ!aÃæ T €¨€¡ éö?ÿæ‘Ï´x†r<4 ÁŽÒ‚¸¿¤{¯g` KX–°„%,a QO¦Ú×z%,a KX–°¤Ä.yx¤ðñ1ôcÃR[µjÅ¡-FZ¸Ÿ$«?Å–°„%,a KXR’—Ø31ΛY<öþ]RÁãY–°„%,a KJòfðäO)P€"Ĭž|€½!*ÀŽÒ‚à+؈ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XA€½ÑPØOZÿ¹IQ”äëÉIW“rrsJBÑôNúÿ?_?I’(ÅyHÅûÁpMT@IäååU$W)$_ONI½^9,ÌÃÝ£$Ô-=#ý\üy!„)ŠCqRqÀ~$_ON½‘Q-ÂÛË»$¬oêÔØ¸Øb®‰ °  H·ÿù¯Ÿ'éjRÉ9ÚBx¸{”-w?#Å¡8ÿ®8`?’®&ET‹pws7 %a}ÝÝÜ+‡U>zü(Q¤…"h@ÊÉÍ)9G{·ùîs–âPœW°9¹9^ž^ùùù%d}eYöòô*Æáš¨»PT H%³ñú>ךâPÖÀ“1p)ŠÂpMT@ RT Hàɦ(J‰Š Å»²DØQZÿ¹I%òÜðý­5Å¡8¬5€'`àRE–åŠq¸æ{`ŠêëŠl†Îœq|Ƕÿ»ò(.™Rrâþî§Ó™òíÿxHký(§/Í7þÜþݾdã­‰3Nìüþׄ<å!ïE[cÒžï¶½a~ˆ¾ý+þËï¢ Àã$I.yh@BIWœ HŠ)ãâÉ£q—Óòd¡rpõö ©âÆ6±$gœøé×ëåZ6 ÕK…Çòçùéœo£–UõÉ»cþk<Ó¨¬cÉ<ì4g'Å:|3_'O¿²*•÷a—€‡úÑ]t=9JNüÎ/–­ûß‘„,YãZ¿u×ÞÝ–qàL Qö—Än@zð :%/ñðî£i^•j4ðu’òo&'\¼œ’üxEé!ç~¨=ÂjE*žZ©DGξ°ï×£i®!UêT÷tPrR¯œ?òB©†ÞOÖžö4påµ † kG\–T­s¯1ƒ‚2Ïþ¶~ùÇ}âÞþlBë»9FV¥‡k¢ìB‘ÝéßLæŒK×Mžµ*•vB/Ÿ€`“IQ‰L!97ùäžÓçRòÔîÁ5ëU t’„0ݼxâHìåô|EëV£z9×ôƒ;˜"ŸŠò× ó#;÷fUmѰ¬£dN;ú¿½YÕ[Fêþúm¦'öNHÏ5ÊBíäR½V¸œ‹/Öv|9;áØŸÆHÿRz!ç^=uäØùÔ\ÅÁÝSe.…%μ|âÏØ„y²ÆÙ¿bš¼t–/DÉOúsÿ±Ä›y&Ehœý*DÔªè­-ÊÏ”‡W%?éø‰ÇŠ£Â=5Bááá”-T™{ؽ«o¶²ˆìs¿ütÉ¿F™¬3g’µU[6 r’nï£göˆOÍ6ÈBrð,[¥VÕ².êGY°§¤ ]TS~]²êLÐ ?è¤BˆÊUkUtè=dù燎ŽHúôÍaGšw;ºuG\†cpóþ£Þn]F'äŒÃæÍûjߥµWåV}†¾Ñ"À|hrÏñ¹ÝßðûuÕq7Ý«t>ªWM÷¢êòçZ °Iü狼™OÒ9iENJzþíAGÒh ÞJúÉ3ÙÞ•ëÖ­êo¸øç‰£P ÉÇöMq©Õ¬yÃêÞ™§~?|ÅèêçªÜLÉ6 !g^½nÓ¯¤…P 7ÓŒz?÷»zUŽ>¡5¢5m]#ÀèøucTïµã+ЬXº{À½yfÿþ æÀõ£Tr+<–U ×þüýD¦Oµ&O·lwàØ=ë¬vö «Õ¤iã語7cŦeÿÃ,ŽñFBªÙ³|ˆ‡Å9Iëìò׋շ±ÈYgŽ&J¥*T½3MI*gp•z›4m\;DuåÏ#‰¹Ê#-ØQT$³Ù\4W˜ožøñ„¹ò O•ÖÜZ¤8„¶j”}ð§ó9²"„9þ›ßLõ^=¢wSóÏ3§|›`0\Ø<~üÿœÚ[´fÉäWJý>}òÖƒ¬(rî¡ß¢ûÖ3,aÃÌ qyEv¡‚ÙlæZ ˆ˜Ú=¬jÙë‡öÅ\w÷+åëãèïéT$ÏZ ë”u„âšpåbZžìvó\’P·zˆ·V·*5Ò¯î>wÕT«”“!9น®¥¨üKëÓÓ^Y)¹:/¯»æ 4å+{E6äåJ~þúøÄô<¹”úñØ@JæÉ]ß´\âTÊâ/æôóñYžU[T r’„ðT'Ÿ¾”'„’wõÌU]X³JþzI§ á~^¾i*åý×Ð#9”ª&„b6äå ¿RŽWÒ3ŠÃãPÙ•+;ø¸êlà6WÿÞÝÀW%„¤¯Ø¨a¸ûÝ;„Ê5¸²«²)/WåàzæLZ®\Ö•!@IUd³ ¦ŒK)fˆ@½åµj÷]VbrޤM•w& mæ. QÛãüï£~Ú%ìÒ·×êúð¹p½þO÷xqËÛ¿L}º¬ë½?m`! ž'¿~êÜMsEoUQ­o1V›¨»PŒ_Á¦r ¬Õ»ܵääëׯÅ9{Â1¸~Ã!„$© ŸNí –d³,ç¥g+úç·ÚÙÛY\N78yjΧd}³’M>á!G%fnd*îáw÷‰È9‰§þ5W‘4:µIÑÉÛÿÍ*èCëF–.Ì>JÞ•C’,2Cf†ÉÑ×ó® ÁäœÙ¦›'wm‹•„B‘Ͳâc¼s¥M7/žü36!-_¨´:ɨ¸ÈE9&ëí¡l¬¾­Ý@Rë4V>W”üëqGŽŸMÎ6KÆ$K.Ê£-ØÛw@*ŠAZ’ŠrçÓɲ¢!üKHŠ,Ë’:¿J~Ò„+çϤÞ<;þµÝê‚qÝh0ú¦d™J+’J­‘ žÇÁÝI2æÌEuCWî€÷W°©ÜýƒÜýƒÂªå&ìÙuäTbÅF^V¨¬¾SÕ®~nJljFÊ|Šn_éPRºOF¾>ÀõÎ7˜’“pè`¼T±~‹ ¾zµáòÿíŒ{œFgÞÝÓãöÒ4Ò]'=„bm(“•WD³:¥n—BRi-»²ä›çþø3É¥jô3¡^Ž"+îç_“—Ѝt.NR~F¦QñTK6?‡îY}1á÷Ø ÃÕ#â²ËÖnÑ8ÀEc¾öÇŽC J´"‹ ’K —:ýâ•,s…¿:GÍ7Î'C}YQ ƒ„$„l6É’Z'™µÏ c?ìXöÖG™¤Ö»ªc…Røº„(8ߥÈ?W4ë[œŸuìp°Ÿ´ þûW°=pì6çfæÿõ”t..Z¡˜­Ÿ×V9º;K9©Ù¦ÂÍNÍNŽ*›ŸS^RBB¶k »FíèeNº¥õñ¼«ýÈœ“š%¼Ê—/¥×HBÝ|¢=|¯‚¤uvRå¥g™nà–ÌÉS¯d§åk ÿ8hïvLY©9ÚR僽U¢(‹ò(Š£õ,ã¥J;{.ÝdqdoÊɾ=mbuõ•Û ”Ü7e×Ðr./Ê1«à±S0«  ¡¯Ô4\ûíŽíErΙ˜ï/9ÕlT0áû×ò‹Ç’Ô•ƒ‚ƒœÒN' 7Ï‚?n.:•R*,/åS„RT˜UŠïHŠáêá_Î;—/ëëæ(Ò.L0º‡ûëUV¯¬ÕùVö=v¡foÕÍ G/}jø;I*ÙËGs">Ã;ÒS+„äè‘¿ÿªð¯à|WW;yè•Øóg¯h|ÕYWNŸL“}‹dÜ´‡/$Öºû©ö;zF)£7ÜHˆ»dP|…œ+øÇ=k8 ³¢Sâž]¿{†»eŸÚºd¶áèÞZû^6u‘èñTE—Ü+ÇùñBÁ½ Ž)nE†[)šs;Å{$¢ìB‘5 =ð ü+‡Ý<›{àœA’ÎÕ¿Rýˆò.*9Ãê£u¥ªGW?~$ö÷Ÿ BëP¹AD'IµK)u|^`Á}k´e<¥«æR®w¿½$çàUSÆÜ{ÖÑ+¸|9·ÌÄ¢8JqŒ®TõÚ‰=xUÒ— òM?WPà€Z kœè«…W3 ý7Î/8îì+ 'G»ïyNwüÔñ=\ÇÄÄLŒû÷ß Ê¬ìB1Þé `×*Pœ'¸8`WW‘]Öü÷dY…×*ïXɵ @‘5 •ÌÞk»¸Vâ<¹Å»¸Ìfó£øªÂß ï×!Ìf3×*ÅõlàqR”×*ü mHÏyË ÃBq¯o1þv¢ìBQ5 9ë333]]KÐWÙfff:ë)ÅyxÅûáä䔓›ã s(ö“ý†$I9¹9NNNÅõø^Ø…‚Ö£‚Ìð_žÇßß?áÒ¥ÌÌÌ’s´—pé’¿¿?Å¡8¯8`?ÓÒÓòòó¤’!/?/-=-0 °¸ άì(-ˆÿÜ€äéá)Ëòù òòòJBÑýýü==<Íf3Å¡8©8`? †ë¤«Iùùù%a}üŠq¸&*À.U’Ùlöõñ ðP©JÄŒ™,Ë&“Éh4RŠóðŠöƒáš¨€’¨È¾‚M£ÑÈÅ¡8ÀpÿŽk`GiAp$¢`é?^Í ¢žLEu$ð¦AQ°Äd€½áH° Er¤˜˜* žH­Zµ"* D§ñŸºuëVr*¦(Š$Iì9l#jE­@­¨UIØ:ÅÒ¤Mì Hÿß³Ë6¢VÔ ÔŠZ±uˆ xòq¤AQŠÀ6¢VÔ ÔŠZ±uˆ (iAp¤!Ë2E`Q+jjE­Ø:D<á˜Lø7‡Â°Î6¢VÔ ÔŠZ±uˆ xÒÑ€ôoᨵµ¢Vl¢JHZ4 =E¦¯”mD­¨¨µbëð¤{\&L—×vjÜcÕe“=¼˜šŽ¼ùSÿz­§ü™Ç¾f¿ÛˆZQjeoµ2'nìҨ˒x#µúïrÿœÔ¢É ß²ìpͯ}Õ³é KÎx×[Ã÷*À.ÉW°Y‹áù—]=oå·¿¼–'´!ÍÚtéÙ±a“Þ::++ëÛï¿y®uww>Çpó§7ž}ØrIhß%/þ¯÷ßyÛgTg'+·t>Ušw:¤KMwNÙX«•Ó¯›:kÍÿâÒeáX*,²e—·ßm¥^ÿÊËß¿°vm·Ò¶>½²÷l=J=cëGõô%i¿j=Éwî·Sê8=”ß—w죗ú|•.„·óê/û)ó;õЏcQK÷ÿ0”Þ±¥rnûöÉîk7ö Ñ>²÷ Æ-¨ztÛîo¼Ü¨´ÃCøLÈ9<áÙ~1™÷,÷n?ghüÀ‘ù£w|ÞÆGõ˜ŒW’KpT‡!#ßh\Ê^Ž•¼„íŸN[¸õà•\¡v+ݦ÷»/×÷R•”w=Q%3-ˆ"n@2œ_?¸ÇœÓaúNî%§œþã_®œñEÍ:£«;ÚcNÈÎZ·~õ´kׯéÑíe77÷¿¼Y¾ç.Ë*mÓú‡®J¬­ð‘—c˜ûWñ°¶tõ‡Îy·²*ãÒþusæõåüõÜÔ”êîZ™®lü΂”¦ý§ªámL>öË–Í?œ|ýi2¯ýê¡r¬:lóWu†vï0ñ›I }”SEþ+t!FŽËñQ?ª÷à;•ÅÍ«gölÛ¸hXÝo/]ørù"ÿTp {óÓÏÚ!rŽLô¹~àôw«: !9ú†¸_÷‘\ÝUe÷ûUÁx®ÉJücý¬%ƒß÷üzIç²öpä¨d˜9`ôÎÒ/™Ù"D—`Û—;ºÒ¹¾—#ïú"ÇÙ,Ø…‡Ñ€d¾ºmâü£¡}/Ö©yíj5ê6ëÜÿ£u_ÎìRV“¾³oýç'oÙ8©ûSMÚ~&þëÑžkY;*:2ºu—±›Ne+BäìùtÃ~Ÿ/ýJ“¨èÈVoNý9ùVב9íЪÁšEF5o7æ› ùEój³³³×¯_s#í†$I7of¬Û°&33óïÄÚENZ¯òÕkFDü‰¨à#_=~æŽDÓçb.l›ôÖ õ¢¢#›w²êÏ ¹ ±ªë̯>h¹^rê®I½Z·lÙ¢ËÀe‡Òe!òc§·mÒýóõ¾útdTóöã¶]HÚ3·Û:QM[¿³âX–"„rÚþe#Ú·ŒŽŒjüô[Ó¸b(±;¶µm¤q ¯R=ªU¯‡G‰CߺvrzÛ¦¯®Û¾`P»ºÍÞû-[Î8ºvDV‘QÑuž}cÂÖs9EÍ8²tX—è¨èȦí:µkR»ç7Érξ‘O7òõ–™}ŸjØvâÑ×(2*ºáó¯[g/sîwÖJN;´ý¨¦É¨‘/·Œ¬^³þS¯ Ÿ»ñãúgÆõœo<;§S½¨èÈ>?^‹[Ñ¿ÓsQQÑ‘QMžî5廄|Óåµ=ßý%;kWßÑ‘Q/Í8²oôÓÍì.èx0'nzµ~—Œ÷ÔÄøìW÷ž*¹²cæ€gGGFµ|qÈg¿§˜…BÉ=óÍäWžmÝ¬ÇØ/ŽeÊ}•:~´|zïšDF5m;|ÝÉì»Î_ªœ]ôIÒê]œµ7w|eSŠñϱÍDG6³?ÇÖÒmÞ÷«†ui^ÿ­—NÿÓ–:™øó¼IŸŸÌB˜Sö.ܹaTtdãvoÍÚuÅ( äF7l˜úfó¨èÈÖýæþ‘. !§ý±xHç†QÑ‘Z¿8`î¯)òý¿«T©Õ¢ãi«Ö(lá¤Í—MVÇF‹1úÈŒ·Ú7kÝॷgýzÍd¼°´KÃggÅ~ÎÌi×´û¦ÄÛt*çÒUªGÔŒˆ¨Y-ÔM¥ñ,_­fDD͈ê•4—¿1qÃESÁz½³zÙè®QQÑMzÎü%1aû'½šEEGw¹1¾à‰I?/è×¾ydTtÔKïÎÝb~ôãUxÕº-_ón åÔϱÙ÷ÑÐcåÖÙý_¨ÝøÕOv|f“šûö³ £#´é5ÿ`Fážem”»Ÿ—Ûòãc~K é9êݶQ5«G6}¡÷ÔåKTÒÙØˆVÊY'7ìñLdTtÃÎc¾L0> ïz¢ž`áHrÊÞojôn_Þò”ºÚ#´‚‡ZaLùnòÆ´ˆ.ÞŠöÖ¸ThÝ÷Ã¥+W®šþZéý³GoºhB9÷ðƽîÏŽ˜òáÀÚ×7|09æº,„Æ„•‹†¼6~ÆÈ6š]3'ÿ”úßß¾999ë7¬I½‘êååݫ盥J•JOO_·aMVöß5uZýBE6ß&[›¯”Ó÷Nz{N|ak¶|½ñÃf×—œº?SBV~¼ÜëåX¶a¯±óÖ¬Z¶h`õ3ŸûôdÁµ¦ØåßÉϽ?}øÓƘz¼:3>rÀ´ñÝŽ~þñIfaŒ_óÞ{ßë{ÌÜøÃæ…ýü™0rãc ݱÿöKs$I£’Z’$!ŒÇçÌÙïþT¿wÛ‡¦ÿ0jà¢øÈ!Ë׬ZÔ;hÿ”w§ì»©˜“¿óÞâ+µ‡Ï[²ìã^QM²ËÙ{¦/:Ü®_ŸgK +•Ï:½èýy§"†¬Z·zÙ‡¯×I)¿ññooÎ߸néôþMœ¯¦í±V*Oo‡¼sÄß>@Pé\½êŽœß;HòÖç;¶oÛ5£‰»Æ£ú ïÎú|åÚÏ&vÐíœ8igªÇù“895šöÕ¶·Ñ¿‚µf–ü»k’f~’ö«¹Ç?8r‡¾ë´›–Ð*{ý a/åŒß?é3õ@pï¹Ö,yaþÀñÛ †5Sâ×ߥÖè9nú¨ž{ç ýü´ís ’{³I _ðRWyÿ«¶ý¸eTÍ|[CÊÅåÓÿ'5xuðËnÿ¼¥t…Oo¼øÅakržúhùšµÓ:êc>è·øTžBÈ9ûn6´1íÃUϯøpEl~Ö¾éc–§6ÿhåÚõóGv­›”e~àZIŽÛ÷yZûõOI[ccÁó­_Σg-]·ráM3ÖŸù{^é§ž¹þÓösùBa¸°sgjXÇÆþ81"çü±üWï.“&½Sÿê—ƒ_é³4»åÈ)ï¿ ûmæœ=iŠÈ9¾¨ßÄ}!}~ûÍú¹Õ›Ç|øÃu¹8ö+E6™…ÖE¯ÂöGéOW]©Ýoúǃ›fl™8÷`–0œ]9dèæü§FÎ\2wØKå„"„ædk£Ü?¸X~–»ø¹ŠäÃǯß>¦vrÕ©¬~À™­.LûuÂÀYG‚_›>þÔ7ê{©Ÿ€wýÃBì(-ˆ¢l@2\?sMñobã²µG›+FÕu‘„¢ÊëoaιqÕ±f³êúÿL2ˆRB¨œ›}´`x=g!”Zºƒ»GóçÍg* ¡ ê¿`Ú›!Z¡ÔÈûî›i‡®ä·öþ Â_où2%5ÅÓÓ«[—...];w_»~MJÊõ¯·l~¥ÇkpŽAÎÙùî3;oýÍùéù_¶¸û×]þ“G¯ o6.­Âÿ•Ñ_üáLî[Bh‚ú/¼k½"¢_}C9/ýZ²hÔÀ{ÇÉø,¹¢BSmÔœÚx«L/nÜöU“)S߬ ™â»uã_Ío{cíÆ«M>˜×¡†³e^èÓm]·˜})ÝBJd“Íó@²áƹŸÎù]Dެí&ªÊÖ/ë௦Ëëzísí°òíVU„(ÿÞ˜í¯9ø®ßÅ•½^ÿbðKåµBTr9¾jãžÂ#‡ú×Íná) ‘w|Ò½•O ŒKÕ†Ö¯_5ÔC -ÑP䟬øwmQÁOU!4¼ÁsvZ+·zý70å­61ÕD׫װY‹Æa½‹“Z¥Ñ»{xxh„./õ+'cVÊ5mƒå–/?~Õô|J­võððÔ ‘kulH¾«&OÎ~õ×釫¿½^{èâW¢¼U"¬÷˜7~êòå7ç[TZ½ËÔü“í"]…¨øö¨“¿ô^ýsrËBhCß™5±gFˆ&AÉÿ×å»ñý*WÖÙ8ºÖº¸:¨TZgOwINÞjcHQ¾±ø³…í!ýÊÿí–2$nœs[7œ î³±Wó2!‚ÇÚóüÌÇß&„äÔèã¥c£]…È÷>¶ñ­£§Ó2ò²]ÂÔ«â,BÂj6ý—µr ª(~‹»šhulìsk­«¾Ü§ª )×Ô —ùòÇS)¢Aóç˺aÛù~UÂ¥ø»n„¿Þ¨Ôƒž•ô'ÎÒÀEäû^ûÓÑžsÇuPÉ©_mþâÔ5ck+¿3½4wp«J:!JwêÓvcÿïŽg¶iî.=ÒýÊœyáçO—žôn=«º³’·Í†±‹§¶ñV S¹Ë[b¾9žœé´öËKUmü¼ŸJˆªºÝ‹¸.„)i׆{G¹!5þáÃ¥Sà_×iƒ;¾×õÇ÷>jóìšúÑõë×oôTóšeœ„µ¸Ó—®Y[xuõ/Úé©B.smêÌ*`ÏŠøjæ{b¸Ååeúgæo}_•ÖÙY[0ØÊÇ6|¡šxôRÎÙyZ,’„B1åäÚYfam½”ܳßÎùðÓ­ÇÓ„ÎÕ]Ê4…ͅϬSKB•ƒ«ƒZÒ¨ Oé¸heƒÙœqîäõôSƒÛìRKÇÄùFÿkY²D…Â8·ãí–;„BøÔë5wÂs~ê8!ÔŽnŽ*!„0&¼ª*S³lá\˜SpMy]Ü•‹§õáuï=G.iô®º‚J[­¼b#TïIDAT¹ÒóuUãßïÜ%ªIÃúÑ­Ú4 +÷Lc÷¡uéÓ4:ªA‹¶-«ûhí²VºÎÓ¾jzü÷_öþ±ï÷åï-›YéµO?óŽËýÍ©û>ûxÖªÿKÈS9¹;çUÁ÷uáŸ>ü®šTvS=öûÕ]Gvéçãs}–+l†×øTsN9y%IN”K7*<Þr(S»Œôý©dC!„PÖ@Pµ¬ö‹„T£ºûy-¶‡•ƒ«^ý [*/)6Źb•[¹ªÞ9ûã3ä !©´Ú‚רÒ{:«L¹²wƒg¦/èÿÒ™ÆMë×múìs ƒõª€eJ²µ"Œ‰;NžµñÐU³ÚÙÝ!Ûìa”…& éK>]µ=þòêí?¦Uïõà—)K’¦`äU9º8¨Uã©äèâ 2̆kÇÏg&þֻ闪‚3ûùc¥Ô\E<ª¨ð×x¥ ë0~é»uÜ$!þñ£A¨ô^z•)7?ã|l–wýªžwVÅú(wÝð÷.wî2*Ïúï®ÙÖvÿo»÷îß·uÚú¹s›~¸bti+1ýŠÕ…Ç®ˆà—Ë럜w=QO¸‡p$Oy_ñíñ 9J¨»äX¹ïÒ•]Œ™&¼}ωƒÜ£³‡Í;RÔÚù­+{˜Ž}Òé­KV2‡Y–4Ú»Fg•T4Ãõˆa£îZâììü7ó wæ KZÏÐðjUÿšä¸yúžáƬhjŒ\?)Êåö'•ÎÍ忦{×Ëxî‹!“w… ü4¦C„¯”°ôåW¼çCN%Y~âŒÃfEí×mÖÂ×CohHj½‡¶dîØV¶‘J5bþàjn.>þîZIqW«Ç=û”"„b6É·>6mÿ2«•÷ÐOÛüã;ßÿÓ¢1+7tXºvÈ„õ+šoÛùë¾½+ÆoXùÛ”¯&6±‡»†X©•äàW½YçêÍ:¿5ðüÊ×;.Y²§Ó0‹]9ýçÉ£>¿Üfʆϛ»äíÖjüý=±Úï…{jRK/=ÞûÕ=µ»Ÿ¥VŸE‘e¡qÒÞw=þyH¹ß-ekmï]‚ýUúòÂo*ïüî§½û¶ÏþjÅ7}×,ë]^ûÀµÊ»r4I>막³¶"Y_„Ä­ŒÚ,õšñåkõÓ¶õ|iYÁ¾ÔôÅ ³Wn?õ”î7k ¬ïùŸÞIãé­±U1+ê ýÏ{Î÷ö¹­‹§êÑŽWƒb›y"«p·0üóGCá&Rd“¢¨Õ÷~PJÿ¸‘­}¸ÜCíÚ Mhƒ6/Lÿmd§Ÿ~Ùý+{£.~Ö½ µñ3Ì’Z¥z¢Þõ ×*ÀŽÒ‚(Ê$•OýV•{—|}.O•“_…J•ÃÃJ;߳˛oœ>qÓÿÙÏTöÐØ îrFÜ¡kú°0/»ÊÖÿêƒ. Š¿|éø ½·Oá/Õa8çâá$×èW_ªá«“„Í÷7L©ÜCË;§¿$yÞz~/7§’z‡«— º•._±|P€»Õƒ1]@ùòáKùAÉ?|U]ºb@© ÷ìøóòW^%ËŸ­_:aÞÆeo]û}÷eƒäÖ²Û€ ³Wnžµÿçø|;¬•œy9!Ãl™ütŠÁ`*•Mf!„0%=›_¡c·æÁ.j!”Û—æH·!„¤uRËÙ™¹à¨«àĤrOM®˜ÿýêÎC(råô)ÇÎe<ÎtýäélïðÒá¥UWŽ\,¸P^ä]:xIñ/uçäAÞ…ñæ25JÿímÓ$õí"ßÇr?[êÇÀpßì¸)[D¾yöxª>4Äú …eYq,]·mŸá“–­[ÐÞ3þç#V®û§Zâ¿Y²3/¢S³2¥ÿnE W_Õzôˆ Ô«„øë÷jÿF/VNÛ±`ÑYµÚÕ-òk|Â˨]ÕyÝ~U^®ªG<^UªÕ~â'cÆ Ys6OˆÜûþhP»– Ð¥œL¸u™¼R ¬r¾ôùjºq6)÷öoV9—)ç!ŒyÂÏÊFt ´¶Ð§œ·ùÊñDC±‡?ÔOü"Ú 9B…=x HšÀF÷ÚöÚ¢>½¯õíÙ2ÜW›rþ÷DYøÞ5–¹‡–Ó_YûmdÛ2™7ÍÙ’fª[øÆÌŽÛñýn]¸Óõÿ[>óßK««:‰”Ç}àPû·xµéâ1ÃÇøŒxµQYéú‰ß¶ý¢{õck 뎕}2øâË_”Êæ[/N0VºŸßàTååeºÏ>Eô±š[Î…ƒÛ¾=Óxâ„–R ܱ|©Zt­¿xê„ùácÚU0Y=é'ÍS3ëøVðnæ¹eÁÔU]ÂŒç~]¿îŠ1ø¾*ßpPƒóNÔîøl½ ‡ä=GSƒƒ/Mí·¾\§—¢+ºfÜŸ¬.û¬¿ÎkeJÚ:¼Ïž°.Ÿ®ê©\;¸aöUݱu|rðÔ–ez/ÌjŸê/ŽŸøBiµÖJ[¦Mßî×Öoóþ’lEh½«=3xÉ V~j•{ÿ~ ß›ûn¯¥>OÍ\7bس#玴ٽÒs¯v¯qbƒBhC^Úy÷èúmsªúβ…=†¿·ûýy ßêW·Ó+݃gï’k…º>_[Ö¤…çã­‚l<8ç½·nýÍùéù1cçL6}4kèk³M¥£ºÎšÜ5D«Qà 9añ;Ó—Ðff¿ÿ\)•|Y¡¤X:dyBŽC™¦}gŒúΖû¼Ó:|Ûèg»v^ýå Ê-Þ}+fäô·{i‚;-]9膕÷S#†ýþ÷[jñà[[>øåS³>š1êõùßZíÆ/ìWÅIäX;~ñªQÃaÚ’a«RŒÂ9¸ù›“×Ðß_­ û>é×C¡r)]­aï™Ó^n BüÝØ¨)ÛnÔ #cޏܧFÇn]*œÿñöÚÕk]AŠéáò†9É¥ö ¥;üé¬>k²„Ú«R£¶½ËèŠc¼Ò†t˜øÉé׎]qÅ„ûýhÜû¨×è‰ Çþª)Ý KÛúÇ×*6G9ÃD§j}4_¹yõ¸M7 B8Eu›2±oU'•°²¼+Ý»PÜuêÈ‹#fOºÉ£j›ö/]ØóÄ}šÙ^>>æ~`lXj«V­8´…Љ‰éÖ­[=Yξ‘/“·\×Ù^×÷ü…óåBʱÝíÙÃÙF¹‡&vjšøÝ„H=µb¿úÏL—×vëö}Ûu+_-£¡VLIÿiX§IŸlSKO­ð·Î¿;—31Îû_ÿjf`ÖžhÅø5ïxäÛ(/výªž5#‚œ³Ïl›µSj23̉Z±_¡¸k%§ì^ý‡cóù•õÔ OèÖ!*À.<”¤']1NGâ‘o#%/ñ÷e V¤…Ú³r«w¦t‘¨ûйV¦+ÿÛpÔ­Å'"¹³_±uˆ °÷´ ŠòHÿ‘¾þÇ;³óCaX·ûÁ½È¶‘SÍ!Ëw ¡V(úZiÊtßô[wjõïŠW¶Ûª?ºQ+<É[‡›¥Â.xyyQ„88Ä6¢VÔ ÔŠZ±uˆ xâ´‘Ècp#h¶ÛˆZQ+jE­ð8o`GiAüç¤ã'—¨¢•´õeQ+P+jE­@T@‰STW3W«Rb `h@ *6Ó‚°£; ;ÀdQ°‚$¢`3-ˆ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ DÀ ˆ €Í´ h@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ˆ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ DÀ ˆ €Í´ h@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ˆ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ DÀ ˆ €Í´ h@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ˆ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ DÀ ˆ €Í´ h@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ˆ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&+h@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ DÀ ˆ €Í´ h@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ˆ €%&ˆ €4 ›iAЀ@T,1™@T¬  €¨ØL ‚$¢`‰É¢` HDÀfZ4 KL&Ø %€=(h@ºýÏ÷$­ZµbR ¨0«;J ‚$¢`‰$¢`w@ *6Ó‚  €¨Xb2€¨XAQ°™ HDÀ“ DÀ ˆ €Í´ h@ *–˜L *VЀ@Tl¦AQ°ÄdQ°‚$¢`3-ˆ ì™æ_üLLL …ˆ w˜çMÕ€' Hˆ ˆ ˆ ˆ ˆ ˆ ˆ ˆ ˆ ˆ ˆ ˆ ˆ @T@T@TðŸý?}È]à†{›IEND®B`‚PyMeasure-0.5/docs/index.rst0000644000175000017500000000452213137471263016325 0ustar colincolin00000000000000.. PyMeasure documentation master file, created by sphinx-quickstart on Mon Apr 6 13:06:00 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. ############################ PyMeasure scientific package ############################ .. image:: images/PyMeasure.png :alt: PyMeasure Scientific package PyMeasure makes scientific measurements easy to set up and run. The package contains a repository of instrument classes and a system for running experiment procedures, which provides graphical interfaces for graphing live data and managing queues of experiments. Both parts of the package are independent, and when combined provide all the necessary requirements for advanced measurements with only limited coding. Installing Python and PyMeasure are demonstrated in the :doc:`Quick Start guide `. From there, checkout the existing :doc:`instruments that are avalible for use `. PyMeasure is currently under active development, so please report any issues you experience on our `Issues page`_. .. image:: https://ci.appveyor.com/api/projects/status/hcj2n2a7l97wfbb8/branch/master?svg=true :target: https://ci.appveyor.com/project/cjermain/pymeasure .. image:: https://travis-ci.org/ralph-group/pymeasure.svg?branch=master :target: https://travis-ci.org/ralph-group/pymeasure .. image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat :target: http://pymeasure.readthedocs.org/en/latest/ .. _Issues page: https://github.com/ralph-group/pymeasure/issues The main documentation for the site is organized into a couple sections: * :ref:`learning-docs` * :ref:`api-docs` * :ref:`about-docs` Information about development is also available: * :ref:`dev-docs` .. _learning-docs: .. toctree:: :maxdepth: 2 :caption: Learning PyMeasure introduction quick_start tutorial/index .. _api-docs: .. toctree:: :maxdepth: 1 :caption: API References api/adapters api/experiment/index api/display/index api/instruments/index .. _dev-docs: .. toctree:: :maxdepth: 2 :caption: Getting involved dev/contribute dev/reporting_errors dev/adding_instruments dev/coding_standards .. _about-docs: .. toctree:: :maxdepth: 2 :caption: About PyMeasure about/authors about/license PyMeasure-0.5/docs/quick_start.rst0000644000175000017500000000366513160054362017547 0ustar colincolin00000000000000########### Quick start ########### This section provides instructions for getting up and running quickly with PyMeasure. Setting up Python ================= The easiest way to install the necessary Python environment for PyMeasure is through the `Anaconda distribution`_, which includes 720 scientific packages. The advantage of using this approach over just relying on the :code:`pip` installer is that it Anaconda correctly installs the required Qt libraries. Download and install the appropriate Python 3.5 version of `Anaconda`_ for your operating system. .. _Anaconda distribution: https://www.continuum.io/why-anaconda .. _Anaconda: https://www.continuum.io/downloads Installing PyMeasure ==================== Install with conda ------------------ If you have the `Anaconda distribution`_ you can use the conda package mangager to easily install PyMeasure and all required dependencies. Open a terminal and type the following commands (on Windows look for the `Anaconda Prompt` in the Start Menu): .. code-block:: bash conda config --add channels conda-forge conda install pymeasure This will install PyMeasure and all the required dependencies. Install with ``pip`` -------------------- PyMeasure can also be installed with :code:`pip`. .. code-block:: bash pip install pymeasure Depending on your operating system, using this method may require additional work to install the required dependencies, which include the Qt libaries. Checking the version -------------------- Now that you have Python and PyMeasure installed, open up a "Jupyter Notebook" to test which version you have installed. Execute the following code into a notebook cell. .. code-block:: python import pymeasure pymeasure.__version__ You should see the version of PyMeasure printed out. At this point you have PyMeasure installed, and you are ready to start using it! Are you ready to :doc:`connect to an instrument <./tutorial/connecting>`? PyMeasure-0.5/docs/Makefile0000644000175000017500000001516612643513756016137 0ustar colincolin00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyMeasure.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyMeasure.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/PyMeasure" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyMeasure" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." PyMeasure-0.5/docs/images/0000755000175000017500000000000013171765525015733 5ustar colincolin00000000000000PyMeasure-0.5/docs/images/PyMeasure.svg0000644000175000017500000003057513137471263020373 0ustar colincolin00000000000000 image/svg+xmlMeasure PyPyMeasure-0.5/docs/images/PyMeasure logo.png0000644000175000017500000002714713137471263021302 0ustar colincolin00000000000000‰PNG  IHDR\r¨fsBIT|dˆ pHYs ½ ½´½btEXtSoftwarewww.inkscape.org›î< IDATxœíÝw˜]eÀñï{ιuzï“™tRi ]AQAš  +‚®.ˆ]˲vwWt‘Õµ`YA”¢T !½g’LÊ$“éõ–sÞýã¦O»wæÜ¹÷Îü>Ïã2÷œ÷¼ïû;oA!„B!„B!„B!„B!„B!„B!„B!„B!„B‘B*UÖZ«Ý,Ÿ¯lçd­T=P¬ D+e¤*OBŒ;íôkÔAP{•¦!jKëÕMãõøqzÙtí¨[\”÷ó…H{Z¯@©_ùŒþ_”ª »“ù¨q ûô’Ûñ|GÇs¼ž+D;úëÕÆîŸ(u­ŒŒKØYþvm8ŠÇãyBL(Z-‰˜\;Uµßí¤“ÞÞÞm¿r§6œ¿"…_ˆÑQú<£WìÒKOw=i·<Önû•/iø·d>CˆI¤OÆyµêÌn%˜´°+úêåJé?2µ !&‘]CáVs )…s—~¹R)þ'Yé 1‰Õzµ~À­Ä’R@•c|t~2Òb²Óš÷6êe绑–ë`¯^6ÔÜNWq [}Wk=æ&¼ëÀv¸ ç"¹g쎾:æZ€«@k­êj7ÓB ÁÐc.k®€F–ÍÕPæfšBˆ¡¨wŒ5w›¶éúD!Äf5ë—sÆ’€«ÀPzš›é !†¥Bx¦Ž%wûdèOˆñÕ…c¹ÝåQåu7=!Äp´Òþ±Ü?afêmX½•î®ÞTgCˆŒ2!€ã8ÜõÑ{¸ï_žê¬‘Q&Dxø—O²æÍMüü?þ—m›v¥:;BdŒŒ=Ý}|ÿ«±µ‘p„o}ñÇ)Α™#ãÀ¿ýKšö4ùû_ÿ´„—ž{=…9"sdtؾy?ýÁoüü_>õ¢‘h r$DfÉèðí/ý„H82àç[7îä7<–‚ ‘Y26¼ø×Wyêчüü;_þ)ÍM-ã—!!2PF€h$Ê×ïúѰ×twöðƒ¯=8N9"3edøåOaë†#^÷»ÿ~œÕ+6$?CBd¨Œ -Ííüðÿ×µŽãðµO߇Ö:ɹ"3e\øÁW £­+îë__ºš¿üßóIÌ‘™+£ÀúU[øíƒJø¾{>ûôöô%!GBd¶Œ Zk¾ü©`ÛNÂ÷îmÜÏ 2_@ˆÉ.cÀ“|×_^5êûïÿî¯Ù½sÜN]"#X©Î@<úzûùÆ]ÿ>¦4úûB|ûK÷óŸ}Ã¥\‰tÑÑÝžæ4lbws[[Ñhºzº9ÜÿðûÉòøT–”QWYK}U5>¯/µ™O±ŒÜû[ö6Žý$¤?ýþnüø•œyÞÉ.äJ¤BKG;+7®aý¶ÍllØÊ††-4·n—RŠŠ’2NŸ³sN=ƒE N#?'×å§7WÏl´_y¸ÅÍ4÷ìÚÏsÞO_È•ôæ2“'^û†‘1­ŸI­­³ƒëWñÚš•¼¶î-¶ïÞyü°®ï0 <`øTl_*ÊRG¿átTƒ§Oãô‚Ó«Ñ¡£i†ÁÂYs¹ö—ñ®EàõxÆõwÍ{j¬EOöö´¯|çVøÖ®ÜÌÿþÏ\wÓe®¥)Üc;Ë×¼ÁsË—²bÃj¶7î8®À~…™g`æ(Œl3 0GÿÓ°Û¢mv›ÃÊ kX¹a ßûÅý\}ñ¥|à=WRœ?¦m÷ÒZZ×^{é-®¾ðV×'ò—°dãÿ’“—íjºbôö5à×O<ÌS/?Os[둟ÁC>_aæ¨d6Ù5Øí‘}6v‹FkÈ ¹íº›¸þÝW`™ixàÕkiÇáÒ3>ÂÚ•›ÝHn€ßõA¾üÝÛ’’¶ˆ_{W'<òþðôŸEÂ(F¾§ÔÀ(40RT ×!ïݣюfÆ”©|åææÔ9 R“¡¡Œ1¤mCøá_>™´ÂÈöai`ý¶Í\y×ÍüêÏr"xkMgz Ì·°ÊRWø”|õ·Y˜ù[vnç¿z'¿øÓ&ÔÔò´ ]Ý|ûK÷'õ‘p„{>;¶¡E1zÏ-™¾|;Zš±J ²N÷à­31Òlcy# ,°ðͶÐJóƒ_ý”Ïßwý¡þTgÍi~ò½ßpð@[ÒŸóìKyùùIŽ8Þúí[øì}ÿJ8Æ;ÅÄ?ÛJû%<¥“-Œ€âÉ—Ÿç†/ßNKG{ª³5fi†Úæ+Y¾rû÷eû°qÔÙÝÍ÷ÞM8Æ[gâbºÜ•ù­/ »7L=ÞÒjÀ– ;ؽ«‰ùo›×õÝ=4liô³y§ÌBñ½ZþöøK|àæËñú2`âGÛ¶{'/ŒóoôÒ…þ ­x)þ9}«#¬Þ¼/üè›Üû™¯¡T†TeŽ‘V`ÆIu<ùÚ/â¾~É3Ëùà%w úÙãË~†Ç+:üüÑßQ>-VHJŠ ÷'¾²3màŸë¡ï­ϼº„‡ž|„.½:Õ¹JXÚ5ÄÄÔÞÕÉÓK_ bfìS”›yoË)øf[(Cqï¯`óŽm©ÎRÂ$ˆqñø‹Å›%·8VðósÀ“VõÏÑ1sÞ)&áH„ùÉ÷°ÌªÕHãâ™WÿNÕÌ£~Š ª8п6J×ßBtý-Dÿú(SÎ=Õ±µ k·nâwO?šº|Ž‚‘t-í¬Ú¼žªYÇ÷úçg@ÐÐõlˆÞ7"Dö9Dö9ô¾¡ûÅ0ž¨À7ÓB)ÅOþðKº{»SšåDHI÷ò›¯’[ªæ_à sFœ#5©n´‰ìX­ýÜ>òw#Ka•ttwñóÇ~?žY "é^ZùÚqÕÿÃLòÓ|A¦Ý6t›Þn;~M€gЉ2=õ(]=™Q ’Êѱõý'Vÿïf€k¢ì}´ ? 3gè"bäŸwà V¹AOo/¿yòcÍê¸ ’jcÃ6Œœn|Yƒô’QìÀ5ÚÅxý¢´?ÜOçS!:‹ýét Ÿ˜gЉY00ïV‰·z`PóTÅŠÔÃÏü9#F$ˆ¤zyåkTÎzÊ¯×«È &–fh]`ÙŠì±é}=‚>föa´Ù¡ëï¡akÊ„ÜwùðÍ41rFŽÂ’EÎEÞAKPX…Zò÷¯$–É ’êõuoP9}øÿâüľ†Ñ6‡ÐÖh\UøÃBÛíA¯·[4vûðÑDùY‹¼ä_é'ÿJ?Á3<(ïÐM«<öû<ö¨÷é7DÒô‡úÙÚ²o`øëš cÿ‹69„wÚ#^~Ä0—&Z›‰Yd <±ÚOº JIóúúUx#¯øÉ B Þ½þŽy‹‡wÙDöÄWz·ÍOdf‚5‘(Vqlvà3¯¾äjÚn“ ’æÕÕoâ‹s˜¯(/¾Z€vb è à±<„·G‰9ø¦[ø¦ß¡ü}ž•„UÉfQì÷yþµeî'î" "i–¾õ:ìø¾byCŒ  c×çò©+nF)ƒÐ{Øñzdí%÷=>‚gxÈ>×Kþ~¬²ä³À@™Šåkß$j§ïºg ")š[[ØÖ¸ƒ@oöž>Í–Æ8{ôÕ<¦ÅÂú9|ì=7Ð¿ÞÆé9 «ÄÀ’…wª‰ò%o‚R`æ)zûzY³yCÒž3VDR¼ºöM´Ör‡ÿŠõôiÞÚ¢ Gã ‡çXVl¯‡Å'Æ{Ïz'ÚÖô­‹wÒOªî[x}ýèµM6 ")^]õ¾œ¡ß²‰~àHo¾×<ºÙË•g_ʹóÏB‡4ýk¢Çõ§’qhtcõ¦õ)ÎÉÐ$×i­Y¶z(ðû/Ü=ý£(ü²óyŽn#¬”â¦w^Ï©s±{5ýk#`§¾&`d+”¡Xµe]Úž% @¸n{ãš[[ ¾Úo´…8R°ý'ëm·]ö¦VLÁîÔô­·]ßO”2ÀÈ‚¶ÎNöhJmf† @¸nÙê7–_;Rø#£|#jø<'ø¼>>{õ­Ô”Ta·9ôoŒ$4[0ŒìXÜÒØÚŒ A€pÝË+—(9þë5æÂè#`ð“D²üA>ímT•cÔô¥¸OÀÆþ ¶6îH]&†!@¸*±býj”¥³Ô×ÂÇïÐSsƒÙ|áý·SWVÝîз2‚Ó“šª€‘ûs{ãΔ<$„«^[÷¡p3|‡Ìô…`ÕgÌ…8ÒxýÃ^–Ÿ•ËÝ×ÝÁi3âôiúÞŒÞaz)ñhþØ¿Áîý{Ç÷Áq’ \µôÍרL8¿WÓ‚•›mB.ö¤‘,ÿÈkˆ}^·_þQ®»àŠØ´á]6}+¢DŒ_ï ò)”‚Ýû¥PLKW½€U`à8ÊÕÂiÏg²âº^)Å{NßúÈ—XX?§Ï¡c”Þ×#Dö;Iï$´»4ÚP4··‰Žß‘wñ’ \³gÿ>ö4bd)”_±mƒ×ãît[}hè0ÞpXI^w]ý >{̬ͭšŠÓ§ m:ö8hç h Ñý½oFè{+6'!àõÓÚ‘~‰N€£DºxaÅRÌÂX¡/+4(Èu .®C/ÑœÀñË só²éìyíýüº“˜_wëwmæ±eO³±q ¡mQÂ;V™§ÒÀŽ.hé~Û:¼É>’Ϫâ .>õ<Î>étÌhú½o%×üõ•%XE&^K1­Rá8îÖ±u8–Þ‰ ¸¬ìÜ,ö6î+9µ3™S;“¦¶fž}s /®YFxo˜È^#[á­2±J‘.×m‰`·ÇšÓÃ9 ÏàÂ…gSWVsäÒ¶–ŠË ú}“M€pEs[ «6®EùfŽbFµÂ²~/ô‡]zPDcAßñ£†aP^UB¨?LKs[ÜÉ•”pÃ;®âªsÞÃKk—óô/p°£•þMQTƒÂSaà©4Q'œ3«#i²‰îspúcA©²_sUÎTfÞr yY9žÕÝÕK4jcYés,ºáŠ%o¼Š£5žB“¢õf;×î £‹ö³ôýazé¢ÐZÓÙÞEaq~â¿{’H®xnùËøÊ3koëæ\:¤ÑŠs |¦ŒØ3•RÔM¯fë†twõ&ü Ë´8{Îéœ=çt¶ìiàoo¼ÀŠ-«ˆì±‰ì±cM[{ð¶Y'sñ)çqRE=Üø5üvhå´/ÿ”¥ÿùìAö9ëêè‘ &–®žn^]óÊSgYøO˜¥›çÒé?úP3¢(wpLÀ0 ¦Î¬eÓºBý¡Q?oFU=3ªêiëjçÙ•/ñÂê—1”Á Îæ'ŸCAN¬ ;ÀºÛ®æ”{~·³‡üM;9çÖï²ôþÏ=!tuöŒ:?É @ŒÙ“KŸ'‰P4Ǥ¶l`¯Yv@a`qþ}¨­]”7°#íÄU‡–Çbúì)l^·Hdl‹ rò¹æ¼Ë¸bñ% s`±ÙöBºë*(\½•þÂ\²w6±ø“ßcÙ~†hðhE8&Žàñz¤‘ é7.!2Î#Ï>R0o‘5H¯¹R=Ê¡µcéÃ`€iìXóù½L?©ÎµN7å´ð¶ýê·£M“»‚u·_CpßAÎÿÈ¿bõôwÝhš&É"@ŒÉæÛX¿m3õ§Yä³ûÍ}¨6_œ3r à°@ÐOýŒ #ù_õæ3çâX&(ÅŽ÷]À³|›–“grúÝ?=î¾!RÒcòç%ÏàÏVÌ:sø*mnÖ¡Þ³18¼ß_á €†9tÏÉ˦nz5 [“º3O$;Hø˜ãŽ£A?o}ñÃáã§÷÷¾_ÂmR£ŽDøóßÿÆü ½‡Æü‡–ï¶ßð{5J)Ê K|6Ò>¿0—)Óª ºmÿ¢y~æœÐÞïëíOj!51jÿý¯XE”OùX;ýgÔ/?[£CšÒÜâ» ÖþLaq>EÖF¢ÑŽKÀ¶ëß…áTàH8‚m;˜ÃÔZÆKês 2RÔ¶yðO¿aÞƒïÌ3˜Ü1Ôì@CMiå€Ï†jÿ&'/›Yó¦ÌáÀÂQê©.¥»¶|ÄëÆ2<é& bTž^öä$0LÇ߉ò²GœžØ[µ¦d`0ìå÷ù½Ìž?)ÓªS6-7ìæé1 ¦µæþô{ê$Ö‚Œûø¯ÁžyhþLUQÅ€Ï<žÑµd‹Jò™=:E%®õ Du;ŽMÃ!·GŒôˆ„=ùòó4õ60£<Þ#}c²ü`š`¢ùm÷Æzï«K€DkÇòú¦‘­Þ8ø,>ÓGù) ÖQì¯:ò¹Rм‚²sÛÏ Y$ˆ„ôõ÷qïoþ‹)''^è"¶ÆkB_¢@ÇšÓCyAـݨÆûü^¦L«¢¢º”M-´6·ª£Ðcø¨NeoÏVz颯»‹k”M€Áx}ª§”SYSJG[öµÐÓØÌ½šìÙ´†öÒí"l÷³×ÞÀûÞvƒkyt“ôˆ¸½²z¿}êQjç[Ä;±®§_³r“ö½£+üvGìÆÙ5Óý<y†aPP”ǬyS™=…Åùq÷˜†ÉܪÓðZ±&Rk÷VmÍõ<ºAj".Í­-|áGß4u'üµqMC“¦q¿óVÜvG,YÕCkƒ f¨›^MEu)ÍM-´4·cÒ‘áñz()+¤¸´Ëc±»k+ûZé ÷òÚÆ%hGsòô3“š×DI# …C|ú_¥¥½Ê·Yøÿ [»4›w9£Ÿôs§ÝÁ4 fTÕú¹×ÿ\„±ðù½T×UPY[F[K'ö¤¯·Ÿ¼‚\JË É9aÁà ßÍã¯þÇq° ‹×6½„a,˜zú¸ä7İlÇás÷ÝÃÊë0sõ ‡þÊDlغۡ©Å½ùöN¯Æ‰ÀÔòjüCâçN5Ã0(*ɧ¨$Ÿp(‚×7øó‹rKÉòeŽ„¸òœ±dÍ_YºîY€´ Ä¢v”Ïýðž[þ2fÀ ð4…¹ƒ¿ý›Z5ÛvòÄßamÿÏôs¥oê¾ÆCþÃÊ ªÙ¼gA6—œ~Mm{xá­¿̧®|ðßi…ožIuÅÀÂ߆Í-ÉYeg·ÇÒ]3mÐÏ=^OÒøŒÅŒê¹¬ßõÖ‘¿—TqÝ…ÃÍdˆ$ صow|ÿ«lÞ± # Ì·°‚ŠŠ¢£m¬C{ñÐØ­¯ÇËœÚYƒ^ãMáÛ?eUø=Ç™*æ0‹Œ§ôÈ…H Zkzò~ø›c|f™`)ÊŠ‡Ýuõ¦]6ÉÞØÆní¼»`êIx‡8|¼:GËPŠó¼+ÕÙ’@ÞF~ð«ŸòâŠe(Þoyä`Œê…íÀö½{šÇ>´û`ì!§L¸Æþ0߉;¦¡©ƒ×^Ò€Iî@ËAøãCüß3&jÛ˜A…w–…yÌ*¿ülE( «·ÙŒ×*V­!zÐÆ2MN±`ÈëÁá Ó0I5ìiäwO=Ê#Ï>A(FY ß4 «Ò8²±gÀ9AE_H³zÛø¼õsZtæOŸ3ìQàÆFÀ$ÒÞÕÉßW,ã/KžåÕ5o¢µFYŠÀT‹Âé&9¹±Ÿ€ ?v®ýÎ&Ͷq,ù‡DÄztÚ׆‘M€t&`‚Û×|€ç^‰ç—/eÅúUxüùå&3϶(¬1ÈÎ3ðù?³¥CÓ°/I]üÃÐáXû?èósê0íÿ 'ïˆÄH˜`öhâÍMkYµqon\ËÞ®Êê¹µœæ!˜g ºwÿ‰úC°a‡3®ÕþÃ"ûm´Öœ3ï¬!{ÿAªÿnÁš[[ضg'›wlç­MkY¹i-Í­-˜Eõl“)‹-f–&^E¶mXµÕ!’й*"û”R¼}áÙÃ^J `¬$¤¹þP?û6³s_#Ûwï¢aÏ.¶íÞÁöÝt÷vwm0O1ï"/Õ³L<£œ¯5¬ßéÐJÁ«ˆ4;è~ÍÜ)³¨,~sͬœôØU'“IH‘Þþ>Ú»:éèꢹý Í­-ìoi¦©¥™æÖöµà@k3Ý݃ޯ …‘e`fAÅ “êi…ñU£Is°=5… ‘]±jÇ?œqñ°—š¦I0Kšc%ÀeŽvhؽ‹5[6а·‘¶Î:º;iïŽöŽ®NÚ»:‰DãØÖTA…ò*Œ#¨ðg+*K *‹Áçqg.|s»fG :ý³[œ^ÍÔŠ)Ì­~òLVv ­×d .8ØÞÊÓË^`ÉŠå¬Þ²îÞnrŠ ªf™„z5}Ý¡nèëÖ„z5˜±B¬,À£0¼ åëàПÊÊø/ÌQT+Šó l‡?¢ž~͆©+üh툽ý¯X4òÔÙtÙT3ÓI¥P8ÄS/?Ï/=Çòµ+qcù æ¿ÓË”“Ì!«ãŽ£ G¡¨&Žõ¸‡¢špÂèhB¡£'é™”**‹ r†ž3jQÖlKÞ¢ž¸ò°ßÁéÑ̬šÊÉÓæx}v2þ!&! êîíæwO?Îožø?ZÚÛP>…§Ä pªÉœÙÙ#|7 Cá÷ß§àÈ‹ìøha;ЊÃÛ¶ÂkAwŸC$ªð{ÁïMìDœálÜáÞî=£bkB;m”R\wáûF¼Ü0 ‚ÙÉ9Ùg²‘§P$̃<įžx„žÞØ)V‰§ÂÀWd0­Ê ²Ø½:yw¯fS£&v´¼¦µëð'G;è Cáõh‚^…× ^¼±àô*|^Fìܾϡ9IkùãÚe£Cš3gŸÊôŠº¯ÏÊŒËqß“€8,}ëu¾þ_÷²÷@ÊTx+M<5ʧ(ÌU̬5¸4#5‘mµGÓŠÕŽ:>@®-ø<‡jý©èêÓìjJmá·»4‘F‡ ?À o¿:®{ò r’œ«ÉCÀ0zûûøþ/ÂÃÏü­5V¡wº‰áWxL˜YkPZàÞ[ßímµGÓÛ±Ó¨S[Ð¥!¼5vŒÖ•g_J^V|;¯ 7™¹šT$ aûž]Üù½a[ãN Â;ÍÂS«v–ä+fÖ*¼ƒôÒFoH³i§CûàCþVx§Ýö»è”sãºÇðÉ IÄÓK_à+?þ}¡>¬Bß, å¯W1»FQ”çNÁwtlµÝ®ýšŽ”ŸpœNM¤ÑÆçññO—~CÅצ/(ÊKrÎ& 'øÑC?ãgümlWœ:oMlWœÊbÅ´*·¡iëŠm¦ÙÛŸ†Uó$ÓèßEkøðÅ×P^P÷½…ÅùIÌÙä#àG;|óÁÿà÷O?†áUøf›˜ù~/̪5†Ü;Q‘(lÝãîÞù™Dkè_Á iΞs:çÌÿ¤œ¬ì Tÿ]&€Øþ÷_¸ïßxzÙ‹(ŸÂ¿À *ªJõî½õ“µw~& oŽbwh¦WÔqÓ%HèÞ¢yû»mÒ­5_ú÷o)üùV–A^–Æ0`çþ£sËP6ΰLŽ›Ã£àÈî¹ÇÚߪ9˜âñöT ï´‰ìwÈ æpÛå7áI`klË2)”àºI~ôЃ<ùòó±Â¿Ð (L3XÛ`ÓÖubÜx,Â6á±N¿¾òcæ$tAQžLþI‚Iý/úð3æÁG‹² 0Ï (f×dù4Ñhªs7qD 6^—»®úx\³ýNTR^ä~ÆÄä ËV½Î=?ûÊPøçz0²3ª Ê Ýý©ÎÝ¡!´=J¨!ŠeZüó7yÆßpò seÿ¿$™”M€æÖ>ß¿a;6þÙfž¢¶,ÖéÐÓ'Uý1Óп9Jt¿ƒßççŽ+naNíÌQ%UVÿ0¡H̤ ¶ãp׿A[gž«Ä ¢È`ZÕÑž¼©Œ‰î×ô­âtk s øÜշޏ½×PrósÈ’•I3éÀÏû=o®_‘mà­³(ÉWÌšr|ß~w_Š27Øí¡6NXSSZÅWÜBIÞèÚïJ)ªjË\Ρ8Ö¤ ë·oáÇ¿ÿE¬Ý?Ë$?G1§îø¡=­™”³óÆJ;±ýü"6ZÃùóñ¡‹®ÁcrwRbãþ²õwrMšàh‡}à^¢vßT‹ÜâØp߉#KýaRº3N&²»4¡Í6NƒßëãÃ︖³ç1¦4MÓ¤¢ºÔ¥Š¡LšðȳO°fËFŒlEîTƒ…ÓŸá×-oÿ¸é–Ç0Ç ØIDATÈ¡·þ^­5ó¦Ìæ¦K®§8·pÌiWÖ”âñ޾ö â3)@[g÷=ô3Ps’‡…3L¼CL)ï‘öÿˆ´†èn‡pc… ÏÏõ¼óæ/re§Þ유Œû“Iþów?§£«‹@Á©§š 7¤Ü#5€¡E5‘&MxOl /Ë4yû©çrùâKÈ ¸³K¯aÔÔWº’–Ù„{›÷óÈsObù§]ìc¤%© ¤Cšð^›è>Žj”Rœ1ë®9÷2ÊXÊêº éøG>üôá_a;QθÂGþ;I92p„ŽBtlñŽÓû7ñz¼\pêb.9íBŠóÆÞÎ?Qaq>Å¥‰­c3¡@ÓÁüeÉ3̇—ÒŠ‘g=÷ö“’ÓpÓ…¶5v«Ænqˆ¶ÆÞö9yœ;÷,.:õ<ò³’³ŸÏ®")i‹¡MèðßþŽêùš)óâû5'[û_kpº4N‡&Úæàthô¡è1-N5ŸsçŸÅ¼ºÙqoÙ5–e2ã¤:,·6^q›° »·‡G_xгÞÿ¯8‘×èè^Ý«qz5N—ƒÓÅ‘ÌfáÔ¹,œ:—ùu³ ø’?W)EÝô¼>òK… þòÒ³„"!²òâÿgÜà¨ÆqØlÐQÖ'¬ÑáX׃,oÎ d3½ªžYÕÓ˜]=úòÚq?psÊ´*ró³Çõ™â¨ yöI²ò Œj•½iTÐpºœ~Ó§Ñý>ôó0±B'顼´”òübÊ Ê¨,,cjå* S7Ï>ö毖]~Sl€­»¨˜éG¡/œÄ ÀéÓØív»ÆîŠø¡dùƒdeñ{ýø¼¼/Y¾ ^ËKaN>yÙ9f—KQv>ùÙyiu”¶RŠšúJ)üi`€¼Úø@ĆU[ì$çf 'ö‡È~ûÈPÛa¥ùÅÔ—ÕPU\IYA eùÅdç“ÌÂJ`/½tcuÓ«É/”Ó}ÒAæ~“F ”"§däžëhÞÚbkûßîÔDöØØöºå°`ê\æM™ÍœÚdù'Þñצi2mV-Ù¹îÌc7a€‘§È _íÚ°j«3n…ßîÒ„lìöØrÃì@óç/æÌÙ§RWV3>™H‘ìœ õ3jdOš™°À*V 7£4jÃê­½ÉïøÓ5ĶÇBCq^!—q1çÌ;sLëå3EYe •5¥iÕ!b&lÈ®01‡hØN¬ðwô$¿ð»CNN ‹«ÏùÎ[°3‘በåñXÔN­’ã¼ÓØ„ YCåå8šµÛtò ¿†ð›pc¬sñÜygrýï#Û¥Us鮸´€ª)˜CEa‘&lúÇѬٮipà‡»b‡_F°Û5o€›ÞugÎ>5©ÏL ŸªÚr™Ü“!&lø4ÇžÙåhb…¿3É…?¤é[ÅéÓ̪™Î­—~˜‚œ‰¤•Ï¦TÆö3Ì„ ÇŽh ëlZ;“ûL‚þ5±Âÿ¶ øäeÉè1ûxøü>Ê*Š(,É—£»2Єývfjh ëšÛ“û<‚¾Õœ>Íi3òÉ÷~dBwô³”U“_˜+½ûlÂˈþ»šÛ“ÜágkúÖÅÞü§Ï\È­—MÌÂïóû(.-  (OVïM6h`ó.‡¦–ä÷ö÷oŠMåU3OüÃ?N¨ÂïóûÈËÏ&¯ ‡ìÜ,yÛO06lÞ¥iêHþ8x‡Mô CyA)w\ñ±Œoó{<Áì 9¹ArósäPÎ .³¿­ÃØ×ê ’ü"¶Û»mü^Ÿºâ£dù3ë ;¯Ï‹?àÃð‘• +;(UûIf€dÓÑXÕ ½äƒT§ÏVÖ–e¢ Ó4ðx,<^–ÇÂëý·Ï+øÒk/$ŒRÿæ:¤9oþ"Μuʸ<Óã±ý²x½–ÇÂã‰ýiy, CI¡ ‘0 уöAMaN>¸ðФ=Çð‘›—Mn~Á,?–Gþïî’oT¢¢šðV¥·¼û‚>w×í‚~ŠJò)(Ê“¥³"é$$(´ÃÁ kÎs§Ìr%M¥TìPŒ²B²F:ºHIH€Ý¥‰ìµÉdqýùc¯ú+¥(*ɧ¼ªTzßEJHH@h{loí+¿{ÌËzýS¦UË_¤”€8EÆNΩ.©ä¢SÎu:J)J+Š©¨.‘{‘râ 5„b{\wþå£>&Ë㱨ŸYKvÎÄÛðSd& qˆîvpú4 êç° ~ΨÒð|L›5Ÿßërî„= #ÐQMxw…âšs/UÙ9A¦Îš"‡_Š´#ÐDtÏ9)eÕ ß/…_¤3© C÷k"{<–‡kÏ»<áûƒY¦Í®“1EÚ’oæ0ÂÚÑ\¸ðl rÛëÎãõ0mV­~‘ÖäÛ9§Gm²Éòyßâ÷$t¯aL›U+SyEÚ“0„ЭáÒ3.JxM}%Á,™à#ÒŸ€A8»Å¡0'ŸwžvAB÷åQT2ñ·ƒ€Ažòû¾ÅïÆkÆ_÷ú¼ÔN­JV¶„p€D[ìNMyA çÌ;3¡{kë+¥Ó/E‘Tg!eäÛz, áí±)¿×œwyB»û—ÈqXêê oåþïþzR Ljìqpz5óëOâô™ ã¾Ï㱨¬-ObÎD2u´uò­/ÞÏÅ'ßÀóO-KuvÆ•€CtÂQ eðþóߛн5e2ÓoضiÿxÙg¸õº/³{gSª³3.$Ùa£#pþÂÅÔ–Ä?å7˜ ¸´ ‰9ãIkÍŸ~Ž ç^Ç÷¿ú3z{úR¥¤’À¡~öÅ&ý\uÎ¥ Ý[SŸ>Û ÷ô÷…øÑ=?ç¬ú÷ñßÿþlÛIu–’bÒí@hc­á† ¯"7G^^A®ìè3Áµµtðµ;ïãªó?Î[¯­Ouv\7é@¤ÑÆéÓÌ›2›³ç÷}†aPSW‘Äœ‰tòÆ+k¹lÑGùÈ{?CcÃÞTgÇ5“:8½šH£ƒÇôpãEW'toqY¡lä9 =ûÄRÞ>ÿ|ë‹÷ÓÝÕ›êìŒÙ¤ ÚþQ´£¹|ñ»¨(,‹û^Ó4)¯*IbîD:ëï qÿwÍsÞÏC?{ ÇÉÜþIÂÛcGzÏ2‹ËÎ|gB÷–W•ȰŸ`ÿÞƒ|៾Ãe‹nfŲ5©ÎΨLÊ=xtÿ½ûƄμ÷x,JÊ “˜;‘iV¯ØÀ•ç}œO¼ÿnöìÚŸêì$dâîdk4 ¶i›¢àÀ^CŽ?‹h$w²Õ¥ØQÛÍÜŠÒÚ44ù¿çyîÉeüÓg>È'?ÿ¡ŒØ6þW_íWnq3Íá,yf9¼äŽñzœq«¨.ås÷üWÝpIB5Ì„iÞSc-zj´·OÊ&€ɶo÷îüÇoð¡K?Í– ;R!I"‰^üë«\´àƒÜñáoppkª³3€!’ÌqvíØË¦–Tge€‰Û (D¨¬)ãîï|’Ë®½(¹}£$@ˆ$ÈÎÍâ3_¿…â*,Oú³ôÍYæŸ:›‡ž¾/ÕÙî37“}»¸’–a\ÿÑ÷òé¯~”ÒŠbWÒL¦ŒEyœwqbûö q¢`–ß•tN?{_ÿáÌÛlWÒ„HÕSʹû»·séU¦e;8„%ŸßË­Ÿ»‘O|öAwjãM€£pÕ ïæs÷|œÊšøW‘¦# B$`ÎÂ|íÞ;XtÁ©©ÎŠ+$‡œ¼lîüÊM|ä¶kÒzX/Qç7" <^·ÜqŸüü‡&äÁ/„™çÌ×y§ÌLuV’F€'¨ª-ãîïÞÎe×¼#ÕYI: B’“—Í]_»9í§ïºirü–B Ã0MnºýZîøÊMå¥:;ãJ€˜ôþë¿ÉŒ“êR”p{?€Ëé ‘t]ø5c:¼ÐÕ  ÃÍô„ÃS–Ñ6–ûÝ­h½ÙÕô„ÃÑþ­cIÀÕà˜úM7ÓB kS¹ZØ3–\ 5,^§ ³NF"céçÆš‚»}Ji~ëfšBˆÁÆØËšë»[†~äØ!’JóZ%‹^k2®€Jµx#h©‘L¦þœRj̇š%å\m8_ÕžŒ´…˜ì”âñµøïn¤•”P«ÎÙ« õ^ œŒô…˜ÄÖ÷)u£[‰%íd ZuæKýéd¥/Ää£Ú þr†:«Ó­“z4X­¹øÇ > Äþ¶b0»µa¿³J³ÉÍDÇeãÝ‘åo׆ó ýOJ"Ýhµ$bríTu–ëslÆmó&½*+âôÞÜ äŒ×s…È`[ ͧ«¬ENÖÆýƒýJéð)×åãý|!ÒžÖ+Pü2j„¬Wö'óQ);ÆDk­v³|¾²“µRõ@1hoªò#DªhT¨½JÓ5¥õꌦTçI!„B!„B!„B!„B!„B!„B!„B!„B!„B1.þ MZì©6‹IEND®B`‚PyMeasure-0.5/docs/images/PyMeasure.png0000644000175000017500000002066313137471263020355 0ustar colincolin00000000000000‰PNG  IHDRQP ]FSsBIT|dˆ pHYs[[æÍÎËtEXtSoftwarewww.inkscape.org›î< IDATxœíy˜U¹¸ßSÝÓ³÷Ìd²™L˜¬ÈæE B!,áJ€H¼ÀA@QAYP¼Q$n ²„Å{A%à Â5‚ JØAY‚‰$L–I&™d2ûÚ]ç÷Ç×®®®îéžîž%œ÷yòdj?§«ê«ï|ÛƒÁ`0 ƒÁ`0 ƒÁ`0 ƒÁ`0 ùE ¶ÃæUÇÙ>u¹ÒÌö†6 lÓJ¿¦ÃÖmS _9Òí1 £“”BtSø¥o€¾n°ýö|ôµu¾O|w¤[a0FI…ãæUÇiKý9Õ>*4 êüsŸéf †Ñ…•lƒö©+14†Å•#݃Á0úH*DÑ|lÛ1úÑ<ÒM0 £äBJ;ø½·Þ§³£;‡ÍÕ”t Ãè#•M‰Öš+Îû7çî\¶Ç`0ÆC¢Þ÷$ ë6³âgbúÆ\¶É`0Æ C¢m\»x9Ý]½ôõöóµ/_—ëv Ø`HBôæïÜM__?áp˜·ßXóO¾˜Ó† ÃX iÓ¦ð‹íx8SÖ¯ÝȉsΧ»³'nýä)YõþñøsßÊ hnÙÉ3o¼À o¿ÂööfBá0¶£,‹B_€q%•,:jÇÎ9Keô Õùæä«Ýƒal’±=sþ¥¼ô×7 ‡ÃqëKËKX|Í\xågsßÊAÛ6w?¼‚gßz]ö.Zü»°ÊÊ«w6øÛLèÏ’Ó/â“–îeŒ5 d$DW=÷:gÍ¿ Û¶=).-╆‡©Ìm+añí×òJëk„ƒ¡ŒŽ+ÙYÆ©]Àe§]ÎîFˆ¦ÇÇ÷ð6°-GçF®á¤x>Gç72"£±÷¿Þ^Ç)g@Ow+yžÒ²Ž?ù|>ÿ|í=Ž>!mí.kyñq¬73 ÝÕ<ôΣT–”sî‚3òкµË+BÛÏi•f˜¦èQŠ] [5جµª±£ý–áý%yn'QÈÝ,ÎÑù?9Ÿ“íÀ¤ß`ȈŒ‡óQš›™3u!õ3§ð·5¿ÏKãÒᢻϣüvVoÈP¦hm·ñU**Zª¸ó²˜V35ÕYi¢u7Wj£_ʱm[¨ ~–† ~íëSßÛø?m»†Ú¦<ñ2ñBT#Zè ìyDf¼Ìq­3BÔ0b 9Nt4°³uþñ-TW$±¦ o}˜ö'ûèzi€¶ ­üà×·å§‘9@¡, ¹_ Æk¸"\¤×ÕÜTñß#ݾAPH Åcsp®Ù$ PƒaDÓBtåOSVÆï‡Š2Ç vŽÛ·ûõÚíݽÞ?Á¢`²E`ªå‡ Ù²½i[Ÿ š ¥ôµ7s5TÎ'gçàÃïµ4aL Ñ—6üe·=b|0¦ŠjÀ^m¡ûcû†šmÂ-¢¾ ¢ü„BÓ|´•µrßÊ?SË3C+4ÚÓj!šÞÍ5˃g s³2AŸг<¢†Qǘ¢Zk:Ã;cËÎa8°~_ª·ŽG‡dKùüB*?S„oœw—­bÅ[¬Îc‹“ò¦O©ÎVHÍ´mu¨Vj>èó•Ö×kÅ ZlŠ^vE[Áíµ?)¯îƧ‰Bìë§fq޹À¬Ü4Ç`È#Ÿë75`÷°q›f]£CŒÚ,-çôÃOáæ?ßAÇÞí(¨²Ô†Ó–žVú c$“¦oã’¶õéìZ{Û¸):ú‘JÔÈ,­©R}êràÛ¹odÎ8x ‹c †QǘÕD}ñi¬’~64iÖ5Ætµ­).(b¯q¹ô¸/R¹± ÖIΣSu²iëæ|59k/kÙ¼eIûÙ(~…Òq©R(­8w¤Ú–&ÿ LÂqÀ™¸Ãh`Ì ÑW×þƒ°~‹‡©0Åâiª_ËUŸº˜-3)l*´'ÜËš†´”ÂÅOèhW<‚F)Ø»þÇ¥£m2Á>Çß~`ÑÎqPM,$¯/žÙRTåñüQ|‘ëåñ¥‘køòx‘¤¨Ìò¥@Ù {¥`Lç;º:Ù9°“ª"…§rÒ“‚)-+¡¯·ê`WüUÖmÝÀ_}”íÛéß*RÖš|TôVðñ}ã#53‡½?™²aq÷ÖšåÁ7•Í!îHßpÀW7ùÆŠZ˧¯N8Pë]K:.Ìôz“n¨˜æ÷ë¹×+­Þؼ¤í‡ƒ¾8ű|6pg†MpåÝç̆rD°ŸDlFÛÐ< <Ô8–Ÿ^ä˜:–WÑ!â àr`!ò›‚|T ü§œŠ:dTs:°±ìº^`=Ò¿ßÿä<»“BôéWÿFpF5ãë·$n„‹¨*«¤¸¤)õ“yÿ½Âá03&ïÍ×^LKÇ.zõqÞoø€Ê¢J.ÚYÆ kÛyõŒcéëêó;¼(Í:­8H¹´Œ°­÷òYíOÛÿSiÊQN¯¾òÕݼ{ÓíýXœ¡5§í>—BaÙèçÒ8üOÀ§!9À‘Èÿ!ÍË—"BN#šhjÙ Q øð#`œÇv?P |1òïà “re•ðuRÇ·–#‚ãD`)ð%ॠ¯ó à¼5[ ˜Œ3‘¬¯”Ùdœ,q,ÿ†Ì…èW§`”+\ˆž†Ü›(¯#BôëÀ÷€€kÿBà\ Ž^”×F®ï¥™ûEþ]Øî¯&&¼“2&‡ó¿öõÓ~(.LÜ^* ¢,ˆeY”–S?s Ê1úW^ÅÇžÍõÿý-®þô%TÚ²˜¹â)zº{ …r‘X“oT—òÒŒ”*Û¼„¥¹…ÆçøÒvævS[é3½û< E8¶ÿÆáíˆö³»…ˆÖ—. A½+Ö Ž÷¢ шî"õÐÝ©çÑäÒÍ> ©ð{2K8xødšû€‡Ó<£8Ÿg_ªïR¦éJ‘݈}Œ½x6ÉúÿVW’¾Ìû,òáÜw°Çœ]׸²Ù[±"D…‡ÇÝò,)CYÒ½Šªr¦ÏžŠeyw×.ð³îÌX{Þz»{óÓø\¢tµö¼ªÀ‡}ÚõÅUø5œ³ÿ²„/yRjn­šªàc8®¥5!­yfÛ×:·§qŠpŸk]&žv÷¾+HÔD2a 2tžYvÚ„BÀVdX×é8&ºÝÜöd!šÞ³ˆVã>G”V oûn¢ñ¥ã„»89ò·óeEοØájGt¿Ã¿Z0®A´PH=ñsëŽA>~S<Žï6›"ãÚg²ëXOÆœ½såí”WÅ4Å`IâoZâ+ÁR~L†TT•3{ÿi+¼mÈ[>:;âðîëCz­Q÷Ï 7l\Òù.ð¢NŒ+­Ü,?1Ý˨Pø3îÇV)üJëtC•üÀÿC4Ò¨ 9Ñcbß#rl¢u ÕQ@Ì û¹Ö7— /M bw "äýŽë«Èµ†· €ÈöӈݛhŸ{›çÄ‘QLC† '’hƒ«D†Ÿ©8ú;ôvÄ.Z LôeBäÿkg­†íü!®åwSÃx¤Ÿ§?El¶N¦#ÏKÔîý^ELDUˆii*ò{ŸÙæÜwê 9>“8ßUÀ:äc}fæ¸w•šèº°ìÞëøüçòd˽Ô¹‹šÉŠ M:¥ÕÝš&G†ò…|¾ÌºgeZ”4(æ„ u‹ç?­×Y¨çên '+ùè¹?|a”ê÷a_ˆJ|Hš®ê\ÛÁ¤•OÁ¡5·”Yµ?)¯F©£TC̓ w(8½ôÑä<‚Ô€Äún&Ì'ÞvØŽ Ñ3å9×ò¡Y´)ë\Ë©ŠM¿O¼‰á*ľ»§ñ6ðXûʵü™ié ¿ç_‰·ºíéÀj¢­íìlm¡¥­•Õ kxëƒ÷ØÑÝŒ¯zu]M·w§t4·ÂÖƒDÊ[*9qÔþ-.Ég6݈葖azŸFŸñÆ{£¸C鸀g@‡•VŸ¾åuˆîg¡Û)¥À§ô ˆ€¼ ÿD<ó ñˆ.@‚çL||ÞÐNÅ<Çß y!ÓÕjœ¼åZΕÀªG*TÍBê 8I5aÙ}ĆB¼ð/"¦‹Ÿ#vò=ÈL‹œG¼†ž‰Ô‰û~{VË»íîíáù7^â‰7ž¤§d==azºlzBaºúlºì^ª'AíT˜\iážÅ¸£[³ºaðßO÷iêËë(+. <˜Ê©9ÖÑZrçõj­}çm¹²õÍT{´µÿ~ ¼pØ)”…Ö糌o{ÍפP§i©øg¤uRYûŸ³,ÑrñáMg“øçÒ¡2 s¾TCÔn«k¹ ›ÊD "¡#{ç¡$—Ò¤Ž‰ý=p)ñ”*ÄÁ´1׬`覅±H‰f\ÝoO³[Þ„h(æžÇà‘WVâß»™™(J\ºTwŸæ½Ùšm»m]6…EaÀïS¼ûM’ÉEã(k rÎɱŒ¯²`in;4ÂhÐ(l¥ñiTŠ›·´wü”eƒk Ëè­]Î=À¥O»U[[^yt#­q™ãn$šï ZVèß¾~aÖ1… ™'–t‹“áõöûˆUlŠ¿³¢{?,» ïÉ/{O%ƒ{Þ-$@ÿ³ˆæèôêg“ßÄ;>MÌvmã>ˆ7ûF$œçîÈ~£u‚Ã\áU€çe†ö;»*ž±y¢Ûv6sÉ-Ki©ldŸ#5¥ñÏ^Ø–êKÍéZçîé<2﫯¥€ONŸK°Dé…JJ³-¢>zÐСЯixņǶ.îø»—ý3á°}—ϲ®p7¤,û\\ér%¡ÐÉÚõ)O‘v€}*‘,™ã‘¾ 3úEdû'‰/Xñ"ã8T$Véɪj‹d6]þ„8%µ?gÉVD«½ ùÝ ñ¶§#áOw µ%Oíi¼ “ç"ð[“ä^ç\ˆþóßïrõÏ¿OÉþ»8¸ÎÂï‹ ­°f£Mw_nJCꘞÆÂCc¦¤ªêaõ®g‡æU_¿rÛÁ°Š”nXÜš˜'¾ÄcçAhºªsuíÍÁ4Õ0#ÿŸQsgÍÅ[.ܲ»ª>M¡ÂDm¢´¢©±½óo™_Ù“ÄÇ?~–˜uO¼—­šÏÌ hN²m/$¨ý×z§YÁFB ^BlÅÿFL ³aâ¤[€ìhè˜óZ Ù9?Bl¦W“y5­±@¾î·"Ɉ#§BtKsÿó‹ë(=p;4[Zò†Ö£¨Ù1™¯.<÷*¥& G>š×´ÇJóSGì^!ß±«»saUsgM‰êì8å kÒ¶RêþÎuÿGÄ^W„<œóís;" ¢/ÿð»,¯åô»)?—-Û‰Ïz‰2 ÖöÈ*c’%õ02pgMÈ¢=Gþí‡hÀŸ#~x¨AÄnzðUö$ÞoMbìíPyÆke΄h_WÜv ÁƒvRR¬hÜ‘ß"ä¾v5µ,Yx…˜–]9.H p´×S*:Ú× ÞŠfÜîÈ7tH£Î&ªñuvR®ÐeiÛÊ&ÄÈM"D¢ÿQ;è;H®r”§ˆOÅ ]ˆ söé*ò—îXˆØ Ýt+b‡ý9 9D,]V#ñ¸KO#Ù6óÛ£ÂôKˆ-úkynÏpâv¢)àh²KNIÎâDpï­èY›™VcÑÕ›GÖn)áˆâ¹\½èÒ8jYµSG[a÷ÑûËèWZýË9µˆòkÍüh¨Rœ¤š‰Ø^×0ÜBù³$ås%¸Ý/ÖGrt^/ÎFÊæ9y)òò/@„­ÿ$'|Ud½Óq%ñÞý±ŽW$B¾ìÑ@Ž„èÖæí¬éy™õòëJ§fv†è(i*gÚŽ\yäW8óÈEX®x¨šº‰F æ§èøNßVúT4JkÆyå5dšŒ•ˆ–}¡!>µ“Üi‹îÀÑ9:¯—¹–_G>#fô2â€ú&±¨‡èxä+IŽq›o²)A8\¬CF:NMn^>/˜!úÇ®gïöMøÉ•&jwk vRÑPžmáÂ9çñµ…3eBM¾Õª˜8y¼ÇY N6}½m°*¾Dž+­ÎšrkÕJ©8U^ i;[»¤H¸“Óñá îý#éMa‘qü­Í,ˆrKüýo»éH “ÂïˆÿÝ“}TÜ…YÆBÜ`É,söïÓù¼`Ö6QÛ¶é*nض¡7Ðc vÆê´„ ñ…|¨ |J}%ÔW×ñ‘™³™U;_’‚ÊÁŠ2ꦹk1’¡á' ‡ƒ åŽÓvèbP6‘«[)ýN$u4¬@‚Å#—‹{ð³õÊ;Yéø;Zé ÀÍ9¼Äæüq’M¦U¾¸Ÿø ý5È=wkžnç[¦UðKðŽÛÌ7&~ú˜£ù³Òž7)²¢]½=ø*äù×Þk°wÇ~z¡ d{)•v³&Ïàc;€ ÕT”ñY™×Ú­¨ 2mÖ”¤Uë ‰ÚÛÿ4 ¶âÔÆ4 ¥.@Å"P(¥‡T±)]^F<峈 [‰×³å]äÅšYÖˆÓå>”Ï/M-Óä„ìDf™öÁ­k¼´Ýyü3‘>¦ã¤)@4^ÏÊGyæ^¤ÀL±çê&$¬.盬%Owo7¥¥"@ÿµÑ¦¹5yýÍfµÍbéñ‹¹æ´+9óˆSÙgÊLÆ•We,@-Ëbê´fì“|Úƒ7‘š£÷ĕȓIè|îÒw!¥~C~YAb&Ðä¾LÛ÷+$(û^2Ÿ²¸qÒxá3šîô±EHØÑ2Ò{ÑÒþ7I]RЋyŽ¿52i ×5ßq-º¾©s¿{Jñ#A;2Ÿó¹:™f$SjI’3%kéccH}S²"! 7³¨þ$–œtÕÁ¡OëmY“j&pÀÁû0~lŃŽ*leß…Ž»ÿV\”Æ^iZÜÚ禬 ñÎåP>Êß\s'ŸB<çéÆfÎAEàõ´‰uöç¢4Î;7rÞètÖé”û&Rm½‰_\Fz„ÙÀÅŽ6*â't²>òÏÙŸo’zN¦‰Hzi&óhåƒåÄ—­ÓÀw4]ïóç¢9ËS픽 §lÖnÔ)«,¶qƧrÌGù2eå%ÔÕOfÿƒfQ;uRÜüI†ÌÙº¸ó=Ðσ#ÜÉ©…*, ¿†¦¬C´ÄŸEþÝHî‚£Ý|‰ÄzÇ q•—à-L}ˆ{IAÝ/²ßû†ë‚g"i–^¶Áøѿ“ºn¨›càlãµÈ ÿâcm£XHžý_XÔCF4`/4‰Î¿9H¬ƒ\û– ûÿ sÍ8´#!sÄ×aXBlŽ%¯NtŠîUÈó_…hÔGxì äÀ&Öš-;’'²¨V‹9ãfî>©K/ø ðøñû}ø üPTRDqIQƵA ƒ£Q?UÉ<³Š°í³Ýš[¾®i*Úíí)â…Z5p;¢q¼ƒä•ÛÈ 4ïi@®B²®Ü9èßG @{q/@2ˆÞEì—¥ˆ*1ÌDñ`ïåKˆðNRr3#ëÃÈÇ¢±_ú¡çŽHPÈÜRî ޜ܂h®Nûâ|ÄŒ°Øéë¾$jx!äƒèŽ›.^E>*÷«' Añ "h× Ï…©¦åÕ)©çy]$ûŒ¥T°fBûDÎ:aQÂ&ŸÏGUuªê JËKŒ]sðt0!Å—•湦Ë:“å…eÞBlšÑôH§ò«uÅkˆÕ‰Î{ÙmßG4²»\çö¤mG¦µø{d9ÙÛÕƒLž÷D‹¬ð¸Ö ’Ûc£û>î¹ØhéÑQ‰³M{‘8EI”mÈÇä0FNˆ‚8›¡YNüïTNêéW¢l@úïÁ‹á|òMÅMe|é˜s‚â«'T±ÿA³˜:½–òв·U#Wš¬a½(ý[ít0i)¾¬•¾?Å¡c ÈËs-‰Dî'ڹ܅Ä}Ö#Ŭ“MÖ÷sd²³h^ü`Ž¢mÈ ¬G!ZfºÏÄoay#ñ5MÝábnºMútÒË›_œCbÈS²küøR ±€Îð-è΀ój¿ó^mBìÔ³9ž÷%oõDu¯fzi=µãc*˲¨Ÿ9…Êq©Šuï™tù}k‹Âáùîõ>=²™Öì§´LÜD«¯5Ôjà¿"¾´žÛÛ V_éh°Ù:½èFÂ`îFlg'!BÌ=”ëGìˆO /aºÚù½ˆíðR$¥Õ­ö#ó·À/‰%„qÔ^7Øõv"¹ï7 Aå‹O´»l[Þ> á3-…÷"Œ#µQ÷wmol¿‹xGÕËÄß«tâfŸ ¾ß/gØV/6#Ž¢‘ÇI$ÚuA„þSȽûiÜNúµÚ~±äSÂÒÔØÌœ© ñ·˜´4ÑQY¶©‚o´„`qlÛôÙSDz ÕùæîQ9¥õ?.Ýk À·Ù9%r$ì鱯ÅíyÍò¥(dN£Iˆ0ÛFúBs0Æ!Cßh\g#ù-\ô£éC²ŠSC¥éOIäÜÑ™\Çä7šˆhç ¡ºW~4Ñ.ÅÁ“Œ “§LËt$äó]äŽ EãÓè¡Lâ¶' ûYSÎÝÂðBÞI~óõ[CõlèG†ì›²9I^Œ‘Á•,úø‚ÝË%¥ÅìU›M™ÄÜñ§VÒ¶+¥$Ç6ÓXU¡—àx4ض´wd2³¢Áð¡&çBÔßà¤çSX3ÉÔM«AÄtÄüòÿÀaõŸæWw‹ÂŸ¿ä4®¸æŠKñ ¨¥A èkëüsŸéV †ÑÇ UA6¬:Îö©Ë•Ö‡‚ÊtzÙ±LJ¿®ÃÖmS _9Ò1 ƒÁ`0 ƒÁ`0 ƒÁ`0 ƒÁ`0>œü†n†q_VƒBIEND®B`‚PyMeasure-0.5/docs/images/PyMeasure logo.svg0000644000175000017500000002467213137471263021315 0ustar colincolin00000000000000 image/svg+xmlPyMeasure-0.5/docs/make.bat0000644000175000017500000001506312643513757016101 0ustar colincolin00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyMeasure.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyMeasure.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end PyMeasure-0.5/docs/api/0000755000175000017500000000000013171765525015237 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/experiment/0000755000175000017500000000000013171765525017417 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/experiment/procedure.rst0000644000175000017500000000015613137471263022136 0ustar colincolin00000000000000############### Procedure class ############### .. automodule:: pymeasure.experiment.procedure :members: PyMeasure-0.5/docs/api/experiment/index.rst0000644000175000017500000000040212656766245021263 0ustar colincolin00000000000000#################### pymeasure.experiment #################### This section contains specific documentation on the classes and methods of the package. .. toctree:: :maxdepth: 2 experiment listeners procedure parameters workers resultsPyMeasure-0.5/docs/api/experiment/parameters.rst0000644000175000017500000000040413137471263022305 0ustar colincolin00000000000000################# Parameter classes ################# The parameter classes are used to define input variables for a :class:`.Procedure`. They each inherit from the :class:`.Parameter` base class. .. automodule:: pymeasure.experiment.parameters :members:PyMeasure-0.5/docs/api/experiment/listeners.rst0000644000175000017500000000022613137471263022154 0ustar colincolin00000000000000############## Listener class ############## .. automodule:: pymeasure.experiment.listeners :members: :undoc-members: :show-inheritance: PyMeasure-0.5/docs/api/experiment/results.rst0000644000175000017500000000014513137471263021645 0ustar colincolin00000000000000############# Results class ############# .. automodule:: pymeasure.experiment.results :members:PyMeasure-0.5/docs/api/experiment/experiment.rst0000644000175000017500000000040413137471263022322 0ustar colincolin00000000000000################ Experiment class ################ The Experiment class is intended for use in the Jupyter notebook environment. .. automodule:: pymeasure.experiment.experiment :members: :undoc-members: :inherited-members: :show-inheritance: PyMeasure-0.5/docs/api/experiment/workers.rst0000644000175000017500000000021613137471263021637 0ustar colincolin00000000000000############ Worker class ############ .. automodule:: pymeasure.experiment.workers :members: :undoc-members: :show-inheritance: PyMeasure-0.5/docs/api/instruments/0000755000175000017500000000000013171765525017632 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/keithley/0000755000175000017500000000000013171765525021450 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/keithley/keithley2400.rst0000644000175000017500000000043313137471263024321 0ustar colincolin00000000000000######################### Keithley 2400 SourceMeter ######################### .. autoclass:: pymeasure.instruments.keithley.Keithley2400 :members: :show-inheritance: :inherited-members: :exclude-members: ask, control, clear, measurement, read, setting, values, writePyMeasure-0.5/docs/api/instruments/keithley/index.rst0000644000175000017500000000054213137471263023305 0ustar colincolin00000000000000.. module:: pymeasure.instruments.keithley ######## Keithley ######## This section contains specific documentation on the Keithley instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 keithley2000 keithley2400PyMeasure-0.5/docs/api/instruments/keithley/keithley2000.rst0000644000175000017500000000043013137471263024312 0ustar colincolin00000000000000######################## Keithley 2000 Multimeter ######################## .. autoclass:: pymeasure.instruments.keithley.Keithley2000 :members: :show-inheritance: :inherited-members: :exclude-members: ask, control, clear, measurement, read, setting, values, writePyMeasure-0.5/docs/api/instruments/fwbell/0000755000175000017500000000000013171765525021105 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/fwbell/index.rst0000644000175000017500000000052213137471263022740 0ustar colincolin00000000000000.. module:: pymeasure.instruments.fwbell ######### F.W. Bell ######### This section contains specific documentation on the F.W. Bell instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 fwbell5080PyMeasure-0.5/docs/api/instruments/fwbell/fwbell5080.rst0000644000175000017500000000030513137471263023420 0ustar colincolin00000000000000################################## F.W. Bell 5080 Handheld Gaussmeter ################################## .. autoclass:: pymeasure.instruments.fwbell.FWBell5080 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/resources.rst0000644000175000017500000000033213137471263022367 0ustar colincolin00000000000000####################### Resource Manager ####################### The list_resources function provides an interface to check connected instruments interactively. .. autofunction:: pymeasure.instruments.list_resources PyMeasure-0.5/docs/api/instruments/yokogawa/0000755000175000017500000000000013171765525021453 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/yokogawa/yokogawa7651.rst0000644000175000017500000000030613137471263024343 0ustar colincolin00000000000000################################# Yokogawa 7651 Programmable Supply ################################# .. autoclass:: pymeasure.instruments.yokogawa.Yokogawa7651 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/yokogawa/index.rst0000644000175000017500000000052213137471263023306 0ustar colincolin00000000000000.. module:: pymeasure.instruments.yokogawa ######## Yokogawa ######## This section contains specific documentation on the Yokogawa instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 yokogawa7651PyMeasure-0.5/docs/api/instruments/comedi.rst0000644000175000017500000000043413137471263021620 0ustar colincolin00000000000000####################### Comedi data acquisition ####################### The Comedi libraries provide a convenient method for interacting with data acquisition cards, but are restricted to Linux compatible operating systems. .. automodule:: pymeasure.instruments.comedi :members: PyMeasure-0.5/docs/api/instruments/ami/0000755000175000017500000000000013171765525020400 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/ami/index.rst0000644000175000017500000000046313137471263022237 0ustar colincolin00000000000000.. module:: pymeasure.instruments.ami ### AMI ### This section contains specific documentation on the AMI instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 ami430PyMeasure-0.5/docs/api/instruments/ami/ami430.rst0000644000175000017500000000022413137471263022120 0ustar colincolin00000000000000#################### AMI 430 Power Supply #################### .. autoclass:: pymeasure.instruments.ami.AMI430 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/hp/0000755000175000017500000000000013171765525020241 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/hp/index.rst0000644000175000017500000000054413137471263022100 0ustar colincolin00000000000000.. module:: pymeasure.instruments.hp ############### Hewlett Packard ############### This section contains specific documentation on the Hewlett Packard instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 hp33120APyMeasure-0.5/docs/api/instruments/hp/hp33120A.rst0000644000175000017500000000031313137471263022064 0ustar colincolin00000000000000###################################### HP 33120A Arbitrary Waveform Generator ###################################### .. autoclass:: pymeasure.instruments.hp.HP33120A :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/parker/0000755000175000017500000000000013171765525021116 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/parker/parkerGV6.rst0000644000175000017500000000030113137471263023444 0ustar colincolin00000000000000################################# Parker GV6 Servo Motor Controller ################################# .. autoclass:: pymeasure.instruments.parker.ParkerGV6 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/parker/index.rst0000644000175000017500000000050513137471263022752 0ustar colincolin00000000000000.. module:: pymeasure.instruments.parker ###### Parker ###### This section contains specific documentation on the Parker instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 parkerGV6PyMeasure-0.5/docs/api/instruments/signalrecovery/0000755000175000017500000000000013171765525022666 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/signalrecovery/dsp7265.rst0000644000175000017500000000026213137471263024525 0ustar colincolin00000000000000########################## DSP 7265 Lock-in Amplifier ########################## .. autoclass:: pymeasure.instruments.signalrecovery.DSP7265 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/signalrecovery/index.rst0000644000175000017500000000055713137471263024531 0ustar colincolin00000000000000.. module:: pymeasure.instruments.signalrecovery ############### Signal Recovery ############### This section contains specific documentation on the Signal Recovery instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 dsp7265PyMeasure-0.5/docs/api/instruments/thorlabs/0000755000175000017500000000000013171765525021450 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/thorlabs/thorlabspm100usb.rst0000644000175000017500000000027313137471263025305 0ustar colincolin00000000000000############################ Thorlabs PM100USB Powermeter ############################ .. autoclass:: pymeasure.instruments.thorlabs.ThorlabsPM100USB :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/thorlabs/index.rst0000644000175000017500000000052613137471263023307 0ustar colincolin00000000000000.. module:: pymeasure.instruments.thorlabs ######## Thorlabs ######## This section contains specific documentation on the Thorlabs instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 thorlabspm100usbPyMeasure-0.5/docs/api/instruments/tektronix/0000755000175000017500000000000013171765525021661 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/tektronix/tds2000.rst0000644000175000017500000000023313137471263023500 0ustar colincolin00000000000000#################### TDS2000 Oscilloscope #################### .. autoclass:: pymeasure.instruments.tektronix.TDS2000 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/tektronix/index.rst0000644000175000017500000000052213137471263023514 0ustar colincolin00000000000000.. module:: pymeasure.instruments.tektronix ######### Tektronix ######### This section contains specific documentation on the Tektronix instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 tds2000PyMeasure-0.5/docs/api/instruments/anritsu/0000755000175000017500000000000013171765525021317 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/anritsu/anritsuMS9710C.rst0000644000175000017500000000034213137471263024414 0ustar colincolin00000000000000########################################## Anritsu MS9710C Optical Spectrum Analyzer ########################################## .. autoclass:: pymeasure.instruments.anritsu.AnritsuMS9710C :members: :show-inheritance: PyMeasure-0.5/docs/api/instruments/anritsu/anritsuMG3692C.rst0000644000175000017500000000030413137471263024401 0ustar colincolin00000000000000################################ Anritsu MG3692C Signal Generator ################################ .. autoclass:: pymeasure.instruments.anritsu.AnritsuMG3692C :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/anritsu/index.rst0000644000175000017500000000054213137471263023154 0ustar colincolin00000000000000.. module:: pymeasure.instruments.anritsu ####### Anritsu ####### This section contains specific documentation on the Anritsu instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 anritsuMG3692C anritsuMS9710C PyMeasure-0.5/docs/api/instruments/index.rst0000644000175000017500000000106313137471263021466 0ustar colincolin00000000000000.. module:: pymeasure.instruments ##################### pymeasure.instruments ##################### This section contains documentation on the instrument classes. .. toctree:: :maxdepth: 2 instruments validators comedi resources Instruments by manufacturer: .. toctree:: :maxdepth: 2 agilent/index ami/index anritsu/index danfysik/index fwbell/index hp/index keithley/index lakeshore/index newport/index parker/index signalrecovery/index srs/index tektronix/index thorlabs/index yokogawa/indexPyMeasure-0.5/docs/api/instruments/srs/0000755000175000017500000000000013171765525020441 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/srs/sr830.rst0000644000175000017500000000023413137471263022044 0ustar colincolin00000000000000####################### SR830 Lock-in Amplifier ####################### .. autoclass:: pymeasure.instruments.srs.SR830 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/srs/index.rst0000644000175000017500000000062013137471263022273 0ustar colincolin00000000000000.. module:: pymeasure.instruments.srs ######################### Stanford Research Systems ######################### This section contains specific documentation on the Stanford Research Systems (SRS) instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 sr830PyMeasure-0.5/docs/api/instruments/newport/0000755000175000017500000000000013171765525021330 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/newport/esp300.rst0000644000175000017500000000072113137471263023067 0ustar colincolin00000000000000######################### ESP 300 Motion Controller ######################### .. autoclass:: pymeasure.instruments.newport.ESP300 :members: :show-inheritance: .. autoclass:: pymeasure.instruments.newport.esp300.Axis :members: :show-inheritance: .. autoclass:: pymeasure.instruments.newport.esp300.AxisError :members: :show-inheritance: .. autoclass:: pymeasure.instruments.newport.esp300.GeneralError :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/newport/index.rst0000644000175000017500000000050713137471263023166 0ustar colincolin00000000000000.. module:: pymeasure.instruments.newport ####### Newport ####### This section contains specific documentation on the Newport instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 esp300PyMeasure-0.5/docs/api/instruments/danfysik/0000755000175000017500000000000013171765525021442 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/danfysik/adapters.rst0000644000175000017500000000025313137471263023772 0ustar colincolin00000000000000####################### Danfysik Serial Adapter ####################### .. autoclass:: pymeasure.instruments.danfysik.DanfysikAdapter :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/danfysik/index.rst0000644000175000017500000000053613137471263023302 0ustar colincolin00000000000000.. module:: pymeasure.instruments.danfysik ######## Danfysik ######## This section contains specific documentation on the Danfysik instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 adapters danfysik8500PyMeasure-0.5/docs/api/instruments/danfysik/danfysik8500.rst0000644000175000017500000000026113137471263024313 0ustar colincolin00000000000000########################## Danfysik 8500 Power Supply ########################## .. autoclass:: pymeasure.instruments.danfysik.Danfysik8500 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/validators.rst0000644000175000017500000000072713160054362022526 0ustar colincolin00000000000000.. module:: pymeasure.instruments.validators ################### Validator functions ################### Validators are used in conjunction with the :func:`Instrument.control ` function to allow properties with complex restrictions for valid values. They are described in more detail in the :ref:`Advanced properties ` section. .. automodule:: pymeasure.instruments.validators :members: :noindex: PyMeasure-0.5/docs/api/instruments/agilent/0000755000175000017500000000000013171765525021255 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/agilent/agilent8257D.rst0000644000175000017500000000027413137471263024062 0ustar colincolin00000000000000############################## Agilent 8257D Signal Generator ############################## .. autoclass:: pymeasure.instruments.agilent.Agilent8257D :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/agilent/agilentE4408B.rst0000644000175000017500000000030313137471263024150 0ustar colincolin00000000000000################################ Agilent E4408B Spectrum Analyzer ################################ .. autoclass:: pymeasure.instruments.agilent.AgilentE4408B :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/agilent/index.rst0000644000175000017500000000062013137471263023107 0ustar colincolin00000000000000.. module:: pymeasure.instruments.agilent ####### Agilent ####### This section contains specific documentation on the Agilent instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 agilent8257D agilent8722ES agilentE4408B agilentE4980 agilent34410APyMeasure-0.5/docs/api/instruments/agilent/agilent34410A.rst0000644000175000017500000000027413137471263024125 0ustar colincolin00000000000000################################ Agilent 34410A Multimeter ################################ .. autoclass:: pymeasure.instruments.agilent.Agilent34410A :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/agilent/agilent8722ES.rst0000644000175000017500000000032513137471263024200 0ustar colincolin00000000000000###################################### Agilent 8722ES Vector Network Analyzer ###################################### .. autoclass:: pymeasure.instruments.agilent.Agilent8722ES :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/agilent/agilentE4980.rst0000644000175000017500000000026513137471263024062 0ustar colincolin00000000000000############################## Agilent E4980 LCR Meter ############################## .. autoclass:: pymeasure.instruments.agilent.AgilentE4980 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/lakeshore/0000755000175000017500000000000013171765525021607 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/instruments/lakeshore/lakeshore331.rst0000644000175000017500000000032313137471263024536 0ustar colincolin00000000000000##################################### Lake Shore 331 Temperature Controller ##################################### .. autoclass:: pymeasure.instruments.lakeshore.LakeShore331 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/lakeshore/adapters.rst0000644000175000017500000000032113137471263024133 0ustar colincolin00000000000000################### Lake Shore Adapters ################### .. autoclass:: pymeasure.instruments.lakeshore.LakeShoreUSBAdapter :members: :undoc-members: :inherited-members: :show-inheritance: PyMeasure-0.5/docs/api/instruments/lakeshore/index.rst0000644000175000017500000000064313137471263023446 0ustar colincolin00000000000000.. module:: pymeasure.instruments.lakeshore ##################### Lake Shore Cryogenics ##################### This section contains specific documentation on the Lake Shore Cryogenics instruments that are implemented. If you are interested in an instrument not included, please consider :doc:`adding the instrument `. .. toctree:: :maxdepth: 2 adapters lakeshore331 lakeshore425PyMeasure-0.5/docs/api/instruments/lakeshore/lakeshore425.rst0000644000175000017500000000025713137471263024550 0ustar colincolin00000000000000######################### Lake Shore 425 Gaussmeter ######################### .. autoclass:: pymeasure.instruments.lakeshore.LakeShore425 :members: :show-inheritance:PyMeasure-0.5/docs/api/instruments/instruments.rst0000644000175000017500000000031013137471263022744 0ustar colincolin00000000000000################## Instrument classes ################## .. autoclass:: pymeasure.instruments.Instrument :members: .. autoclass:: pymeasure.instruments.Mock :members: :show-inheritance: PyMeasure-0.5/docs/api/display/0000755000175000017500000000000013171765525016704 5ustar colincolin00000000000000PyMeasure-0.5/docs/api/display/log.rst0000644000175000017500000000016013137471263020207 0ustar colincolin00000000000000########### Log classes ########### .. automodule:: pymeasure.display.log :members: :show-inheritance: PyMeasure-0.5/docs/api/display/browser.rst0000644000175000017500000000020013137471263021104 0ustar colincolin00000000000000############### Browser classes ############### .. automodule:: pymeasure.display.browser :members: :show-inheritance: PyMeasure-0.5/docs/api/display/curves.rst0000644000175000017500000000017413137471263020742 0ustar colincolin00000000000000############## Curves classes ############## .. automodule:: pymeasure.display.curves :members: :show-inheritance: PyMeasure-0.5/docs/api/display/manager.rst0000644000175000017500000000020013137471263021033 0ustar colincolin00000000000000############### Manager classes ############### .. automodule:: pymeasure.display.manager :members: :show-inheritance: PyMeasure-0.5/docs/api/display/windows.rst0000644000175000017500000000020013137471263021113 0ustar colincolin00000000000000############### Windows classes ############### .. automodule:: pymeasure.display.windows :members: :show-inheritance: PyMeasure-0.5/docs/api/display/plotter.rst0000644000175000017500000000017213137471263021122 0ustar colincolin00000000000000############# Plotter class ############# .. automodule:: pymeasure.display.plotter :members: :show-inheritance: PyMeasure-0.5/docs/api/display/index.rst0000644000175000017500000000043412656762621020547 0ustar colincolin00000000000000################# pymeasure.display ################# This section contains specific documentation on the classes and methods of the package. .. toctree:: :maxdepth: 2 browser curves inputs listeners log manager plotter Qt thread widgets windowsPyMeasure-0.5/docs/api/display/listeners.rst0000644000175000017500000000021013137471263021432 0ustar colincolin00000000000000################# Listeners classes ################# .. automodule:: pymeasure.display.listeners :members: :show-inheritance: PyMeasure-0.5/docs/api/display/thread.rst0000644000175000017500000000017413137471263020702 0ustar colincolin00000000000000############## Thread classes ############## .. automodule:: pymeasure.display.thread :members: :show-inheritance: PyMeasure-0.5/docs/api/display/widgets.rst0000644000175000017500000000017513137471263021102 0ustar colincolin00000000000000############## Widget classes ############## .. automodule:: pymeasure.display.widgets :members: :show-inheritance: PyMeasure-0.5/docs/api/display/inputs.rst0000644000175000017500000000017413137471263020755 0ustar colincolin00000000000000############## Inputs classes ############## .. automodule:: pymeasure.display.inputs :members: :show-inheritance: PyMeasure-0.5/docs/api/display/Qt.rst0000644000175000017500000000032013137471263020010 0ustar colincolin00000000000000########## Qt classes ########## All Qt imports should reference :code:`pymeasure.display.Qt`, for consistant importing from either PySide or PyQt4. .. automethod:: pymeasure.display.Qt.fromUi :noindex:PyMeasure-0.5/docs/api/adapters.rst0000644000175000017500000000256413137471263017576 0ustar colincolin00000000000000################## pymeasure.adapters ################## The adapter classes allow the instruments to be independent of the communication method used. Adapters for specific instruments should be grouped in an :code:`adapters.py` file in the corresponding manufacturer's folder of :mod:`pymeasure.instruments `. For example, the adapter for communicating with LakeShore instruments over USB, :class:`LakeShoreUSBAdapter `, is found in :mod:`pymeasure.instruments.lakeshore.adapters`. ================== Adapter base class ================== .. autoclass:: pymeasure.adapters.Adapter :members: :undoc-members: ============ Fake adapter ============ .. autoclass:: pymeasure.adapters.FakeAdapter :members: :undoc-members: :inherited-members: :show-inheritance: ============== Serial adapter ============== .. autoclass:: pymeasure.adapters.SerialAdapter :members: :undoc-members: :inherited-members: :show-inheritance: ================ Prologix adapter ================ .. autoclass:: pymeasure.adapters.PrologixAdapter :members: :undoc-members: :inherited-members: :show-inheritance: ============ VISA adapter ============ .. autoclass:: pymeasure.adapters.VISAAdapter :members: :undoc-members: :inherited-members: :show-inheritance: PyMeasure-0.5/LICENSE.txt0000644000175000017500000000205513137471263015356 0ustar colincolin00000000000000Copyright (c) 2013-2017 PyMeasure Developers 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. PyMeasure-0.5/README.rst0000644000175000017500000000431213143623160015210 0ustar colincolin00000000000000.. image:: https://raw.githubusercontent.com/ralph-group/pymeasure/master/docs/images/PyMeasure.png :alt: PyMeasure Scientific package PyMeasure scientific package ############################ PyMeasure makes scientific measurements easy to set up and run. The package contains a repository of instrument classes and a system for running experiment procedures, which provides graphical interfaces for graphing live data and managing queues of experiments. Both parts of the package are independent, and when combined provide all the necessary requirements for advanced measurements with only limited coding. PyMeasure is currently under active development, so please report any issues you experience to our `Issues page`_. .. _Issues page: https://github.com/ralph-group/pymeasure/issues PyMeasure runs on Python 3.4, 3.5, and 3.6, and is tested with continous-integration on Linux, macOS, and Windows. .. image:: https://ci.appveyor.com/api/projects/status/hcj2n2a7l97wfbb8/branch/master?svg=true :target: https://ci.appveyor.com/project/cjermain/pymeasure .. image:: https://travis-ci.org/ralph-group/pymeasure.svg?branch=master :target: https://travis-ci.org/ralph-group/pymeasure .. image:: http://readthedocs.org/projects/pymeasure/badge/?version=latest :target: http://pymeasure.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://zenodo.org/badge/23569/ralph-group/pymeasure.svg :target: https://zenodo.org/badge/latestdoi/23569/ralph-group/pymeasure .. image:: https://anaconda.org/conda-forge/pymeasure/badges/version.svg :target: https://anaconda.org/conda-forge/pymeasure .. image:: https://anaconda.org/conda-forge/pymeasure/badges/downloads.svg :target: https://anaconda.org/conda-forge/pymeasure Quick start =========== Check out `the documentation`_ for the `quick start guide`_, that covers the installation of Python and PyMeasure. There are a number of examples in the `examples`_ directory that can help you get up and running. .. _the documentation: http://pymeasure.readthedocs.org/en/latest/ .. _quick start guide: http://pymeasure.readthedocs.io/en/latest/quick_start.html .. _examples: https://github.com/ralph-group/pymeasure/tree/master/examples PyMeasure-0.5/setup.py0000644000175000017500000000505513171764440015250 0ustar colincolin00000000000000# # This file is part of the PyMeasure package. # # Copyright (c) 2013-2017 PyMeasure Developers # # 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. # from setuptools import setup, find_packages setup( name='PyMeasure', version='0.5', author='PyMeasure Developers', packages=find_packages(), scripts=[], url='https://github.com/ralph-group/pymeasure', download_url='https://github.com/ralph-group/pymeasure/tarball/v0.5', license='MIT License', description='Scientific measurement library for instruments, experiments, and live-plotting', long_description=open('README.rst').read() + "\n\n" + open('CHANGES.txt').read(), install_requires=[ "numpy >= 1.6.1", "pandas >= 0.14", "pyvisa >= 1.8", "pyserial >= 2.7", "pyqtgraph >= 0.9.10" ], extras_require={ 'matplotlib': ['matplotlib >= 2.0.2'], 'tcp': [ 'zmq >= 16.0.2', 'cloudpickle >= 0.3.1' ] }, setup_requires=[ 'pytest-runner' ], tests_require=[ 'pytest >= 2.9.1' ], classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python :: 3 :: Only", "Topic :: Scientific/Engineering", ], keywords="measure instrument experiment control automate graph plot" )